Subversion Repositories f9daq

Rev

Blame | Last modification | View Log | RSS feed

  1. //****************************************************************************
  2. // Copyright (C) 2000-2006  ARW Elektronik Germany
  3. //
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. //
  19. // This product is not authorized for use as critical component in
  20. // life support systems without the express written approval of
  21. // ARW Elektronik Germany.
  22. //  
  23. // Please announce changes and hints to ARW Elektronik
  24. //
  25. // Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
  26. //
  27. //****************************************************************************
  28.  
  29. //****************************************************************************
  30. //
  31. // pcicc32.c -- the driver module for the PCICC32 PCI to CAMAC Interface
  32. //              Thanks to A.Rubini's Book and Dirk Muelhlenberg and H.J.Mathes
  33. //              for their arwvme driver
  34. //
  35. // $Log: main.c,v $
  36. // Revision 1.29  2006/06/04 12:26:07  klaus
  37. // release_20060604; Version 6.9; pci_{en|dis}able_device() added; remap_page_range reorganized
  38. //
  39. // Revision 1.28  2006/03/28 21:49:49  klaus
  40. // release_20060328; Version 6.8; Support for AMD64 and Kernel 2.6.15
  41. //
  42. // Revision 1.27  2005/10/08 13:21:52  klaus
  43. // release 6.7, removed "requested io-memory still claimed" bug at incomplete termination
  44. //
  45. // Revision 1.26  2005/10/07 16:57:10  klaus
  46. // fixed a bug with request_irq with IRQs greater than 127
  47. //
  48. // Revision 1.25  2005/03/11 13:23:26  klaus
  49. // simple corrections for to use with kernels 2.4.21
  50. //
  51. // Revision 1.24  2004/08/12 19:59:19  klaus
  52. // conversion to kernel-version 2.6, released version 6.0
  53. //
  54. // Revision 1.23  2004/01/16 18:42:26  klaus
  55. // converted remap_page_range call for kernels >= 2.4.20, contributed by Roberto Bertoni,
  56. // release of version 5.3
  57. //
  58. // Revision 1.22  2003/12/04 20:34:49  klaus
  59. // minor change: restore the hardware set intCSR content at release of path, release of version 5.2
  60. //
  61. // Revision 1.21  2003/06/19 08:23:38  klaus
  62. // re-compiled with RH-7.2 (kernel 2.4.10)
  63. //
  64. // Revision 1.20  2003/05/12 20:59:50  klaus
  65. // another improvement at previous place
  66. //
  67. // Revision 1.19  2003/05/12 20:38:28  klaus
  68. // improved debug messages from test of connection
  69. //
  70. //****************************************************************************
  71.  
  72. #define VERSION_HI 6
  73. #define VERSION_LO 9
  74.  
  75. /*--- INCLUDES ---------------------------------------------------------------------------*/
  76. #include "common.h"  /* must be the first include */
  77.  
  78. //#include <linux/config.h>
  79.  
  80. #include <linux/sched.h>
  81. #include <linux/proc_fs.h>
  82. #include <linux/pci.h>
  83. #include <asm/types.h>
  84. #include <linux/delay.h>
  85. #include <linux/wait.h>
  86. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
  87. #include <linux/interrupt.h>
  88. #endif
  89.  
  90. #include "list.h"
  91. #include "askpci.h"
  92. #include "plxbug.h"
  93. #include "plx9050.h"
  94. #include "fops.h"
  95. #include "pcicc32.h"
  96.  
  97. /*--- DEFINES -----------------------------------------------------------------------------*/
  98. #define MAJOR_NO                       0                         /* use dynamic assignment */
  99. #define DEVICE_NAME                    "pcicc32"
  100.  
  101. #if (VERSION_HI < 3)
  102. #define ARWVME_DEVICE_ID               0x9050
  103. #define ARWVME_VENDOR_ID               0x10B5
  104. #define ARWVME_SUBSYSTEM_ID            0x1167
  105. #define ARWVME_SUBSYSTEM_VENDOR_ID     0x9050
  106. #else
  107. #define ARWVME_DEVICE_ID               0x2258
  108. #define ARWVME_VENDOR_ID               0x10B5
  109. #define ARWVME_SUBSYSTEM_ID            0x2258
  110. #define ARWVME_SUBSYSTEM_VENDOR_ID     0x9050
  111. #endif
  112.  
  113. #define MAKE_CC32_ADR(N, A, F) (u16)((N << 10) + (A << 6) + ((F & 0xf) << 2))
  114.  
  115. /*--- TYPEDEFS ----------------------------------------------------------------------------*/
  116. extern struct file_operations pcicc32_fops;
  117.  
  118. /*--- DRIVER GLOBALS ----------------------------------------------------------------------*/
  119. static List *pci_device_header          = NULL;  /* list of all PCIADAs found */
  120.        List *pcicc32_work_device_header = NULL;  /* list of working device interfaces */
  121. static unsigned int nMajor;
  122.  
  123. /*--- FUNCTIONS ---------------------------------------------------------------------------*/
  124. static int my_interrupt(u16 intCSR)
  125. {
  126.   int result = NOT_MY_INTERRUPT;
  127.  
  128.   if (intCSR & 0x0040)  // it is global enabled
  129.   {    
  130.     if ((intCSR & 0x0028) == 0x0028) // it is a enabled PCIADA interrupt
  131.       result = PCIADA_INTERRUPT;
  132.     else
  133.       if ((intCSR & 0x0005) == 0x0005) // it is a enabled CC32 interrupt
  134.         result = CC32_INTERRUPT;  
  135.   }
  136.  
  137.   return result;
  138. }
  139.  
  140. static irqreturn_t cc32_irqhandler(int irq, void *dev_id, struct pt_regs *regs)
  141. {
  142.   CC32_DESCRIPTOR *wd = (CC32_DESCRIPTOR *)dev_id;
  143.  
  144.   if (wd)
  145.   {
  146.     // evaluate the reason of the interrupt - if it is mine
  147.     u16 intCSR          = readw(wd->pLCR + PLX9050_INTCSR);
  148.           int which_interrupt = my_interrupt(intCSR);
  149.          
  150.           if (which_interrupt)
  151.           {
  152.             writew(intCSR & ~0x40, wd->pLCR + PLX9050_INTCSR);       /* disable global interrupts */
  153.             wd->wIrqStatus = (u16)which_interrupt;
  154.             wd->dwInterruptCount++;
  155.             wake_up_interruptible(&wd->event_queue);                 /* stop blocking if any */
  156.           }  
  157.          
  158.           return IRQ_RETVAL(1);
  159.   }
  160.  
  161.   return IRQ_RETVAL(0);
  162. }
  163.  
  164. static int request_io_memory(PCIConfigHeader *pPch)
  165. {
  166. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
  167.         if (check_mem_region(pPch->desc[0].base_address, pPch->desc[0].size))
  168.                 return -EBUSY;
  169.        
  170.         if (check_mem_region(pPch->desc[3].base_address, pPch->desc[3].size))
  171.                 return -EBUSY;
  172.        
  173.         request_mem_region(pPch->desc[0].base_address, pPch->desc[0].size, DEVICE_NAME);
  174.         request_mem_region(pPch->desc[3].base_address, pPch->desc[3].size, DEVICE_NAME);
  175. #endif
  176.        
  177.         return 0;
  178. }
  179.  
  180. static void release_io_memory(PCIConfigHeader *pPch)
  181. {
  182. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
  183.         release_mem_region(pPch->desc[0].base_address, pPch->desc[0].size);
  184.         release_mem_region(pPch->desc[3].base_address, pPch->desc[3].size);
  185. #endif
  186. }
  187.  
  188. static int translate_addresses(CC32_DESCRIPTOR *pWd, PCIConfigHeader *pPch)    /* differs from PCIVME */
  189. {
  190.         if (pPch->desc[0].type == PCI_BASE_ADDRESS_MEM_TYPE_1M)  /* LCR ISA base addresses */
  191.                 pWd->pLCR = bus_to_virt(pPch->desc[0].base_address);
  192.         else
  193.                 pWd->pLCR = ioremap(pPch->desc[0].base_address, pPch->desc[0].size);
  194.  
  195.         if (pPch->desc[3].type == PCI_BASE_ADDRESS_MEM_TYPE_1M)  /* User ISA base addresses */
  196.                 pWd->pUsr = bus_to_virt(pPch->desc[3].base_address);
  197.         else
  198.                 pWd->pUsr = ioremap(pPch->desc[3].base_address, pPch->desc[3].size);
  199.        
  200.         return 0;
  201. }
  202.  
  203. static void un_translate_addresses(CC32_DESCRIPTOR *pWd, PCIConfigHeader *pPch)
  204. {
  205.         if (pPch->desc[0].type != PCI_BASE_ADDRESS_MEM_TYPE_1M)  /* no LCR ISA base addresses */
  206.                 iounmap((void *)pWd->pLCR);
  207.         if (pPch->desc[3].type != PCI_BASE_ADDRESS_MEM_TYPE_1M)  
  208.                 iounmap((void *)pWd->pUsr);
  209. }
  210.  
  211. static void soft_init(CC32_DESCRIPTOR *pd)
  212. {
  213.         if (pd)
  214.         {
  215.     init_waitqueue_head(&pd->event_queue);
  216.    
  217.                 pd->pLCR = pd->pUsr = 0;
  218.                 pd->pPch = (PCIConfigHeader *)NULL;
  219.                 pd->cModuleNumber = 255;
  220.                 pd->cFPGAVersion = 255;
  221.                 pd->bConnected = 0;
  222.                 pd->wInitStep = 0;    
  223.           pd->wIrq = 0xFFFF;          
  224.           pd->dwInterruptCount = 0;
  225.           pd->wIrqStatus = 0;  
  226.         }
  227. }
  228.  
  229. int test_connection(CC32_DESCRIPTOR *wd)
  230. {
  231.         u16 intCSR_store;
  232.         u16 cntrl_store;
  233.         int i = 1000;
  234.         u32 access_store;
  235.         int error = 0;
  236.         __u32 dwData;
  237.         void *dwAdr = wd->pUsr + MAKE_CC32_ADR(26,0,0);
  238.        
  239.         cntrl_store  = readw(wd->pLCR + PLX9050_CNTRL);  /* read CONTROL register */
  240.         intCSR_store = readw(wd->pLCR + PLX9050_INTCSR); /* read interrupt + CSR register */
  241.        
  242.         writew(0, wd->pLCR + PLX9050_INTCSR); /* disable interrupts */
  243.         writew(cntrl_store | 0x0180, wd->pLCR + PLX9050_CNTRL); /* enable access */
  244.        
  245.         access_store = readl(dwAdr);
  246.         while (i--)
  247.         {
  248.                 writel(0x55555555, dwAdr);
  249.                 dwData = readl(dwAdr) & 0x00FFFFFF;
  250.                 if (0x00555555 != dwData)
  251.                 {
  252.                         DPRINTK(KERN_DEBUG "pcicc32 : write 0x55555555, read 0x%08x\n", dwData);
  253.                         error = 1;
  254.                         break;
  255.                 }
  256.                 writel(0xAAAAAAAA, dwAdr);
  257.                 dwData = readl(dwAdr) & 0x00FFFFFF;
  258.                 if (0x00AAAAAA != dwData)
  259.                 {
  260.                         DPRINTK(KERN_DEBUG "pcicc32 : write 0xAAAAAAAA, read 0x%08x\n", dwData);
  261.                         error = 1;
  262.                         break;
  263.                 }
  264.                 writel(0xFFFFFFFF, dwAdr);
  265.                 dwData = readl(dwAdr) & 0x00FFFFFF;
  266.                 if (0x00FFFFFF != dwData)
  267.                 {
  268.                         DPRINTK(KERN_DEBUG "pcicc32 : write 0xFFFFFFFF, read 0x%08x\n", dwData);
  269.                         error = 1;
  270.                         break;
  271.                 }
  272.                 writel(0x00000000, dwAdr);
  273.                 dwData = readl(dwAdr) & 0x00FFFFFF;
  274.                 if (0x00000000 != dwData)
  275.                 {
  276.                         DPRINTK(KERN_DEBUG "pcicc32 : write 0x00000000, read 0x%08x\n", dwData);
  277.                         error = 1;
  278.                         break;
  279.                 }
  280.         }
  281.        
  282.         writew(cntrl_store & ~0x0100, wd->pLCR + PLX9050_CNTRL); /* clear potential interrupt */
  283.        
  284.         /* restore all contents */
  285.         writel(access_store, dwAdr);
  286.         writew(cntrl_store, wd->pLCR + PLX9050_CNTRL);
  287.         writew(intCSR_store, wd->pLCR + PLX9050_INTCSR);
  288.        
  289.         return error;  
  290. }
  291.  
  292. int get_module_info(CC32_DESCRIPTOR *wd, u8 *cModuleNumber, u8 *cFPGAVersion)
  293. {
  294.         u16 intCSR_store;
  295.         u16 cntrl_store;
  296.         int found = 0;
  297.         u16 data;
  298.        
  299.         cntrl_store  = readw(wd->pLCR + PLX9050_CNTRL);  /* read CONTROL register */
  300.         intCSR_store = readw(wd->pLCR + PLX9050_INTCSR); /* read interrupt + CSR register */
  301.        
  302.         DPRINTK(KERN_DEBUG "pcicc32 : get_module_info(), cntrl=0x%04x, intCSR=0x%04x\n", cntrl_store, intCSR_store);
  303.        
  304.         if (cntrl_store & 0x0800) /* a CC32 is connected */
  305.         {
  306.                 u16 bla = cntrl_store | 0x0180;
  307.                
  308.                 writew(0, wd->pLCR + PLX9050_INTCSR); /* disable interrupts */
  309.                 writew(bla, wd->pLCR + PLX9050_CNTRL); /* enable access */
  310.                
  311.                 data = readw((wd->pUsr + MAKE_CC32_ADR(0,0,0)));
  312.                  
  313.                 DPRINTK(KERN_DEBUG "pcicc32 : CC32 status=0x%04x\n", data);
  314.        
  315.                 if ((data & 0xF000) != 0x8000)
  316.                 {
  317.                         *cModuleNumber = *cFPGAVersion = 255;
  318.                         printk(KERN_ERR "pcicc32 : Wrong module type connected @ index %d (0x%04x)!\n", wd->wIndex, data);
  319.                 }
  320.                 else
  321.                 {
  322.                         found = 1;
  323.                         *cModuleNumber = (data >> 4) & 0xF;
  324.                         *cFPGAVersion  = (data >> 8) & 0xF;
  325.                 }
  326.        
  327.                 writew(cntrl_store & ~0x0080, wd->pLCR + PLX9050_CNTRL); /* clear potential interrupt */
  328.        
  329.                 /* restore all contents */
  330.                 writew(cntrl_store, wd->pLCR + PLX9050_CNTRL);
  331.                 writew(intCSR_store, wd->pLCR + PLX9050_INTCSR);
  332.         }
  333.        
  334.         return found;  
  335. }
  336.  
  337. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19)
  338. static int pcicc32_read_proc(char *buf, char **start, off_t offset, int len)
  339. #else
  340. static int pcicc32_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *data)
  341. #endif
  342. {
  343.   int i;
  344.   int pos = 0;
  345.   CC32_DESCRIPTOR *pd;
  346.   PCIConfigHeader *ch;
  347.   Node            *n;
  348.   u16                   cntrl;
  349.   char            *cause = "None";
  350.  
  351.         DPRINTK(KERN_DEBUG "pcicc32 : pcicc32_read_proc()\n");
  352.        
  353.   pos += sprintf(buf + pos, "\npcicc32 information. Version %d.%d of %s from Klaus Hitschler.\n", VERSION_HI, VERSION_LO, __DATE__);
  354.  
  355.   i = getNumOfNodesInList(pcicc32_work_device_header);
  356.   pos += sprintf(buf + pos, " ---------------------\n");
  357.   pos += sprintf(buf + pos, " Interfaces found : %d\n", i);
  358.  
  359.   n = getFirstNode(pcicc32_work_device_header);
  360.   while (i--)
  361.   {
  362.         pd = (CC32_DESCRIPTOR *)getContent(n);
  363.         ch = pd->pPch;
  364.           cntrl = readw(pd->pLCR + PLX9050_CNTRL);
  365.           pos += sprintf(buf + pos, " --- %d ---------------\n", i + 1);
  366.           pos += sprintf(buf + pos, " LCR  phys/virt/size : 0x%08x/0x%p/%d\n", ch->desc[0].base_address, pd->pLCR, ch->desc[0].size);
  367.           pos += sprintf(buf + pos, " User phys/virt/size : 0x%08x/0x%p/%d\n", ch->desc[3].base_address, pd->pUsr, ch->desc[3].size);
  368.           pos += sprintf(buf + pos, " Irq                 : %d\n", pd->wIrq);
  369.          
  370.           if (pd->bConnected)
  371.           {
  372.                   pos += sprintf(buf + pos, " CC32 is or was      : (software) connected.\n");
  373.                 pos += sprintf(buf + pos, " Module-Number       : %d\n", pd->cModuleNumber);
  374.                 pos += sprintf(buf + pos, " FPGA-Version        : %d\n", pd->cFPGAVersion);
  375.                 }
  376.                 else
  377.                   pos += sprintf(buf + pos, " CC32 is or was      : not (software) connected.\n");             
  378.                
  379.           if (!((cntrl & 0x0800) && (!(cntrl & 0x0600))))
  380.                 pos += sprintf(buf + pos, " CC32 is             : powered off or cable disconnected.\n");
  381.                
  382.           pos += sprintf(buf + pos, " IrqCount            : %d\n", pd->dwInterruptCount);
  383.           if (pd->wIrqStatus & PCIADA_INTERRUPT)
  384.             cause = "Timeout";
  385.           else
  386.             if (pd->wIrqStatus & CC32_INTERRUPT)
  387.               cause = "LAM";     
  388.           pos += sprintf(buf + pos, " Pending IrqStatus   : %s\n", cause);
  389.          
  390.         n = getNextNode(n);
  391.   }
  392.  
  393.   pos += sprintf(buf + pos, "\n");
  394.  
  395.   #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,19)
  396.   *eof = 1;
  397.   #endif
  398.  
  399.   return pos;
  400. }
  401.  
  402. #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)
  403. //----------------------------------------------------------------------------
  404. // replacement for kernel 2.4.x function
  405. // original from sysdep.h out from the book
  406. // "Linux Device Drivers" by Alessandro Rubini and Jonathan Corbet, published by O'Reilly & Associates.
  407. static struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode,
  408.                                         struct proc_dir_entry *base, read_proc_t *read_proc, void * data)
  409. {
  410.   struct proc_dir_entry *res = create_proc_entry(name, mode, base);
  411.   if (res)
  412.   {
  413.     res->read_proc=read_proc;
  414.     res->data=data;
  415.   }
  416.   return res;
  417. }
  418. #endif
  419.  
  420.  
  421. int init_module(void)
  422. {
  423.   int i = 0;
  424.   PCIConfigHeader *ch;
  425.   CC32_DESCRIPTOR *wd;
  426.   Node            *n;
  427.   int             result = 0;
  428.  
  429.   printk(KERN_INFO "pcicc32 : init_module\n");
  430.  
  431.   /* create list of PCIADAs and work devices */
  432.   pci_device_header          = newList();
  433.   pcicc32_work_device_header = newList();
  434.  
  435.   /* search for all PCIADA modules */
  436.   while ((ch = GetPCIConfigHeader(ARWVME_VENDOR_ID, ARWVME_DEVICE_ID, i)))
  437.   {
  438.     if (ch->subsystem_id != ARWVME_SUBSYSTEM_ID)
  439.     {
  440.       printk(KERN_ERR "pcicc32 : found 0x%x, 0x%x , but wrong subsystem id 0x%04x!\n",
  441.               ARWVME_VENDOR_ID, ARWVME_DEVICE_ID, ch->subsystem_id);
  442.       i++;
  443.       kfree_s(ch, sizeof(*ch));        // FREE(ch);
  444.       continue;
  445.     }
  446.  
  447.     if (ch->subsystem_vendor_id != ARWVME_SUBSYSTEM_VENDOR_ID)
  448.     {
  449.       printk(KERN_ERR "pcicc32 : found 0x%x, 0x%x , but wrong subsystem vendor Id 0x%04x!\n",
  450.              ARWVME_VENDOR_ID, ARWVME_DEVICE_ID, ch->subsystem_vendor_id);
  451.       i++;
  452.       kfree_s(ch, sizeof(*ch));        // FREE(ch);
  453.       continue;
  454.     }
  455.  
  456.     /* test if it is configured as PCICC32 or a PCIVME interface card */
  457.     if (ch->desc[3].size != 0x8000)  
  458.     {
  459.       printk(KERN_ERR "pcicc32 : found, but wrong memory window size 0x%08x!\n", ch->desc[3].size);
  460.       i++;
  461.       kfree_s(ch, sizeof(*ch));        // FREE(ch);
  462.       continue;
  463.     }
  464.  
  465.     printk(KERN_INFO "pcicc32 : found %d. PCIADA card\n", i + 1);
  466.     addTail(pci_device_header, ch);  /* add this header to list */
  467.     i++;
  468.   }
  469.  
  470.   /* fix the PLX bug in all PCIADAs */
  471.   i = getNumOfNodesInList(pci_device_header);
  472.   n = getFirstNode(pci_device_header);
  473.   while (i--)
  474.   {
  475.         ch = (PCIConfigHeader *)getContent(n);
  476.         PLX9050BugFix(ch);
  477.         n = getNextNode(n);
  478.   }
  479.  
  480.   /* create work_devices and translate the access addresses */
  481.   i = getNumOfNodesInList(pci_device_header);
  482.   n = getFirstNode(pci_device_header);
  483.   while (i--)
  484.   {
  485.         wd = (CC32_DESCRIPTOR *)kmalloc(sizeof(CC32_DESCRIPTOR), GFP_ATOMIC);
  486.           soft_init(wd);
  487.           ch  = (PCIConfigHeader *)getContent(n);
  488.           wd->pPch = ch;
  489.           wd->wIndex = i;
  490.        
  491.           if (!request_io_memory(ch))
  492.           {
  493.             // successful request_io_memory
  494.             wd->wInitStep = 1;
  495.            
  496.                   if (translate_addresses(wd, ch))
  497.                   {
  498.                           printk(KERN_ERR "pcicc32 : translation of addresses failed!\n");
  499.         release_io_memory(ch);
  500.                           kfree_s(wd, sizeof(*wd));       // FREE(wd);
  501.                   }
  502.                   else
  503.                   {
  504.                     // successful translate_addresses
  505.                     wd->wInitStep = 2;
  506.  
  507.         if (request_irq(wd->pPch->PCI_dev->irq, cc32_irqhandler, IRQF_DISABLED| IRQF_SHARED, DEVICE_NAME, wd))
  508.               {
  509.                                   printk(KERN_ERR "pcicc32 : can't get irq @ %d\n", wd->pPch->PCI_dev->irq);
  510.           un_translate_addresses(wd, ch);
  511.           release_io_memory(ch);
  512.                             kfree_s(wd, sizeof(*wd));       // FREE(wd);
  513.               }
  514.               else
  515.               {  
  516.                 // successful request_irq
  517.                       wd->wInitStep = 3;
  518.                       wd->wIrq = wd->pPch->PCI_dev->irq;
  519.                
  520.                             addTail(pcicc32_work_device_header, wd);  /* add this device to list */
  521.                             wd->bConnected =  get_module_info(wd, &wd->cModuleNumber, &wd->cFPGAVersion);
  522.                             if (wd->bConnected && test_connection(wd))
  523.                             {
  524.                                     printk(KERN_ERR "pcicc32 : connection test failed!\n");
  525.                                     wd->bConnected = 0;
  526.                             }
  527.                           }
  528.                   }
  529.           }
  530.           else
  531.           {
  532.                   printk(KERN_ERR "pcicc32 : requested io-memory still claimed!\n");
  533.                         kfree_s(wd, sizeof(*wd));       // FREE(wd);
  534.                 }
  535.        
  536.         n = getNextNode(n);
  537.   }
  538.  
  539.   nMajor = MAJOR_NO;
  540.   result = register_chrdev(nMajor, DEVICE_NAME, &pcicc32_fops);
  541.   if (result < 0)
  542.   {
  543.         printk(KERN_ERR "pcicc32: Can't install driver (%d)\n", result);
  544.        
  545.     /* untranslate translated addresses */
  546.     i = getNumOfNodesInList(pcicc32_work_device_header);
  547.     n = getFirstNode(pcicc32_work_device_header);
  548.     while (i--)
  549.     {
  550.             wd  = (CC32_DESCRIPTOR *)getContent(n);
  551.             ch = wd->pPch;
  552.             un_translate_addresses(wd, ch);
  553.             n = getNextNode(n);
  554.     }
  555.  
  556.     /* delete my lists */
  557.     deleteList(pcicc32_work_device_header, (void (*)(void *))NULL);  
  558.     deleteList(pci_device_header, (void (*)(void *))NULL);      
  559.  
  560.         return result;
  561.   }
  562.   else
  563.   {
  564.         if (nMajor == 0) nMajor = result;
  565.     printk(KERN_INFO "pcicc32 : major #%d assigned.\n", nMajor);
  566.   }
  567.    
  568.   /* register the proc device */
  569.  
  570.   return proc_create_data(DEVICE_NAME, 0, NULL, &pcicc32_fops, NULL) ? 0 : -ENODEV;
  571. }
  572.  
  573. void cleanup_module(void)
  574. {
  575.   int i;
  576.   PCIConfigHeader *ch;
  577.   CC32_DESCRIPTOR *wd;
  578.   Node            *n;
  579.  
  580.   unregister_chrdev(nMajor, DEVICE_NAME);
  581.  
  582.   DPRINTK(KERN_DEBUG "pcicc32 : cleanup_module()\n");
  583.                
  584.   /* unregister the proc device */
  585.   #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19)
  586.   proc_unregister(&proc_root, pcimod_proc_entry.low_ino);
  587.   #else
  588.   remove_proc_entry(DEVICE_NAME, NULL);
  589.   #endif
  590.    
  591.   /* untranslate translated addresses */
  592.   i = getNumOfNodesInList(pcicc32_work_device_header);
  593.   n = getFirstNode(pcicc32_work_device_header);
  594.   while (i--)
  595.   {
  596.           wd  = (CC32_DESCRIPTOR *)getContent(n);
  597.         ch = wd->pPch;
  598.           switch (wd->wInitStep)
  599.           {
  600.             case 3:  writew(readw(wd->pLCR + PLX9050_INTCSR) & ~0x40, wd->pLCR + PLX9050_INTCSR);  // disable global interrupts
  601.                free_irq(wd->wIrq, wd);  
  602.             case 2:  un_translate_addresses(wd, ch);
  603.             case 1:  release_io_memory(ch);
  604.             default: wd->wInitStep = 0;
  605.           }
  606.    
  607.     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
  608.     if ((ch) && (ch->PCI_dev))
  609.                   pci_disable_device(ch->PCI_dev);
  610.     #endif             
  611.      
  612.         n = getNextNode(n);
  613.   }
  614.  
  615.   /* delete my lists */
  616.   deleteList(pcicc32_work_device_header, (void (*)(void *))NULL);  
  617.   deleteList(pci_device_header, (void (*)(void *))NULL);
  618.  
  619.   printk(KERN_INFO "pcicc32 : cleanup_module successful.\n");
  620.  
  621.   return;
  622. }
  623.  
  624.