Subversion Repositories f9daq

Rev

Rev 9 | Rev 43 | Go to most recent revision | Blame | Compare with Previous | 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. // main.c -- the main driver module for the PCIVME PCI to VME 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.10  2006/06/04 12:20:46  klaus
  37. // release_20060604; Version 3.2; pci_{en|dis}able_device() added
  38. //
  39. // Revision 1.9  2004/08/13 19:23:26  klaus
  40. // conversion to kernel-version 2.6, released version 3.0
  41. //
  42. // Revision 1.8  2003/06/27 17:25:52  klaus
  43. // incomplete try to get mmap() with nopage() running for automatic page switch
  44. //
  45. // Revision 1.7  2002/10/18 21:56:28  klaus
  46. // completed functional features, untested
  47. //
  48. // Revision 1.6  2002/10/18 21:56:28  klaus
  49. // completed functional features, untested
  50. //
  51. // Revision 1.5  2002/10/17 19:05:03  klaus
  52. // VME access is working through test to lib to driver
  53. //
  54. //****************************************************************************
  55. #define NOPAGE_SIGBUS   (NULL)
  56.  
  57. #define VERSION_HI 3
  58. #define VERSION_LO 2
  59.  
  60. /*--- INCLUDES ---------------------------------------------------------------------------*/
  61. #include "common.h"  /* must be the first include */
  62. #include <linux/module.h>
  63.  
  64. #include <linux/sched.h>
  65. #include <linux/proc_fs.h>
  66. #include <linux/pci.h>
  67. #include <asm/types.h>
  68. #include <linux/mm.h>
  69. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
  70. #include <linux/interrupt.h>
  71. #endif
  72.  
  73. #include "askpci.h"
  74. #include "plxbug.h"
  75. #include "plx9050.h"
  76. #include "fops.h"
  77. #include "pcivme.h"
  78. #include "pciif.h"
  79. #include "vic.h"
  80. #include "main.h"
  81.  
  82. /*--- DEFINES -----------------------------------------------------------------------------*/
  83. MODULE_AUTHOR("klaus.hitschler@gmx.de");
  84. MODULE_DESCRIPTION("Driver for ARW Elektronik PCI VME interface.");
  85. MODULE_SUPPORTED_DEVICE("PCIVME");
  86. MODULE_LICENSE("GPL");
  87.  
  88. #define MAJOR_NO               0                         /* use dynamic assignment */
  89.  
  90. #define PCIVME_VENDOR_ID  0x10B5
  91. #define PCIVME_DEVICE_ID  0x9050
  92. #define PCIVME_SUBSYS_ID  0x1167
  93. #define PCIVME_SUBVEN_ID  0x9050
  94.  
  95. /*--- TYPEDEFS ----------------------------------------------------------------------------*/
  96.  
  97. /*--- GLOBALS -----------------------------------------------------------------------------*/
  98. DRIVER_OBJ drv;
  99.  
  100. /*--- LOCALS ------------------------------------------------------------------------------*/
  101.  
  102. /*--- FUNCTIONS ---------------------------------------------------------------------------*/
  103. static int my_interrupt(u16 intCSR)
  104. {
  105.     int result = NOT_MY_INTERRUPT;
  106.  
  107.     if (intCSR & 0x0040)  // it is global enabled
  108.     {
  109.         if ((intCSR & 0x0028) == 0x0028) // it is a enabled PCIADA interrupt
  110.             result = PCIADA_INTERRUPT;
  111.         else
  112.             if ((intCSR & 0x0005) == 0x0005) // it is a enabled VMEMM interrupt
  113.             result = VMEMM_INTERRUPT;
  114.     }
  115.  
  116.     return result;
  117. }
  118.  
  119. static irqreturn_t pcivme_irqhandler(int irq, void *dev_id, struct pt_regs *regs)
  120. {
  121.     DEVICE_OBJ *pd = (DEVICE_OBJ *)dev_id;
  122.  
  123.     if (pd)
  124.     {
  125.         // evaluate the reason of the interrupt - if it is mine
  126.         u16 intCSR          = readw((const volatile void *) (pd->pPCIADAIntCSR));
  127.         int which_interrupt = my_interrupt(intCSR);
  128.  
  129.         if (which_interrupt)
  130.         {
  131.             writew(intCSR & ~0x40, (volatile void *) pd->pPCIADAIntCSR); /* disable global interrupts */
  132.             pd->wIrqStatus = (u16)which_interrupt;
  133.             pd->dwInterruptCount++;
  134.             wake_up_interruptible(&pd->event_queue);   /* stop blocking if any */
  135.                        
  136.                         return IRQ_RETVAL(1);
  137.         }
  138.     }
  139.        
  140.         return IRQ_RETVAL(0);
  141. }
  142.  
  143. static int request_io_memory(PCIConfig *pPch)
  144. {
  145.     if (check_mem_region(pci_resource_start(pPch->pciDev, 0),  LCR_SPACE))
  146.     {
  147.         PRINTK(KERN_DEBUG "%s : LCR 0x%08lx\n", DEVICE_NAME, (long unsigned int) pci_resource_start(pPch->pciDev, 0));
  148.         return -EBUSY;
  149.     }
  150.  
  151.     if (check_mem_region(pci_resource_start(pPch->pciDev, 2),  CTL_SPACE))
  152.     {
  153.         PRINTK(KERN_DEBUG "%s : CTL 0x%08lx\n", DEVICE_NAME, (long unsigned int) pci_resource_start(pPch->pciDev, 2));
  154.         return -EBUSY;
  155.     }
  156.  
  157.     if (check_mem_region(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE))
  158.     {
  159.         PRINTK(KERN_DEBUG "%s : VME 0x%08lx\n", DEVICE_NAME, (long unsigned int) pci_resource_start(pPch->pciDev, 2) + CTL_SPACE);
  160.         return -EBUSY;
  161.     }
  162.  
  163.     request_mem_region(pci_resource_start(pPch->pciDev, 0),  LCR_SPACE, DEVICE_NAME);
  164.     request_mem_region(pci_resource_start(pPch->pciDev, 2),  CTL_SPACE, DEVICE_NAME);
  165.     request_mem_region(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE, DEVICE_NAME);
  166.  
  167.     return 0;
  168. }
  169.  
  170. static void release_io_memory(PCIConfig *pPch)
  171. {
  172.     release_mem_region(pci_resource_start(pPch->pciDev, 0),  LCR_SPACE);
  173.     release_mem_region(pci_resource_start(pPch->pciDev, 2),  CTL_SPACE);
  174.     release_mem_region(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE);
  175. }
  176.  
  177. static int translate_addresses(DEVICE_OBJ *pd, PCIConfig *pPch)    /* differs from PCICC32 */
  178. {
  179.     if (pci_resource_start(pPch->pciDev, 0) < LOW_MEMORY)  /* LCR ISA base addresses */
  180.         pd->pLCR = (u32)bus_to_virt(pci_resource_start(pPch->pciDev, 0));
  181.     else
  182.         pd->pLCR = (u32)ioremap(pci_resource_start(pPch->pciDev, 0), LCR_SPACE);
  183.  
  184.     if (pci_resource_start(pPch->pciDev, 2) < LOW_MEMORY)  /* User ISA base addresses */
  185.     {
  186.         pd->pCtl = (u32)bus_to_virt(pci_resource_start(pPch->pciDev, 2)            );
  187.         pd->pVME = (u32)bus_to_virt(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE);
  188.     }
  189.     else
  190.     {
  191.         pd->pPhysVME = pci_resource_start(pPch->pciDev, 2) + CTL_SPACE;
  192.  
  193.         pd->pCtl = (u32)ioremap(pci_resource_start(pPch->pciDev, 2)            , CTL_SPACE);
  194.         pd->pVME = (u32)ioremap(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE);
  195.     }
  196.  
  197.     return 0;
  198. }
  199.  
  200. static void un_translate_addresses(DEVICE_OBJ *pd, PCIConfig *pPch)
  201. {
  202.     if (pci_resource_start(pPch->pciDev, 0) >= LOW_MEMORY)  /* no LCR ISA base addresses */
  203.         iounmap((void *)pd->pLCR);
  204.  
  205.     if (pci_resource_start(pPch->pciDev, 2) >= LOW_MEMORY)
  206.     {
  207.         pd->pPhysVME = 0;
  208.  
  209.         iounmap((void *)pd->pCtl);
  210.         iounmap((void *)pd->pVME);
  211.     }
  212. }
  213.  
  214. static void soft_init(DEVICE_OBJ *pd)
  215. {
  216.     if (pd)
  217.     {
  218.         init_waitqueue_head(&pd->event_queue);
  219.  
  220.         pd->pLCR = pd->pCtl = pd->pVME = 0;
  221.         pd->pPch = (PCIConfig *)NULL;
  222.         pd->bConnected = 0;
  223.         pd->wInitStep = 0;    
  224.         pd->wIrq = 0xFFFF;          
  225.         pd->dwInterruptCount = 0;
  226.         pd->wIrqStatus = 0;
  227.         pd->nOpenCounter = 0;
  228.  
  229.         pd->cModuleNumber = 255;
  230.         pd->cFPGAVersion = 255;
  231.         pd->cSystemController = 0;
  232.         pd->cWordMode         = 0;
  233.  
  234.         pd->pAdrMod = 0;
  235.         pd->pAdrReg = 0;
  236.         pd->pCSR    = 0;
  237.  
  238.         pd->pPCIADACntrl  = 0;
  239.         pd->pPCIADAIntCSR = 0;      
  240.  
  241.         pd->bCurrentModifier     = 0;
  242.         pd->dwCurrentPageAddress = -1;
  243.                                 pd->currentMap.pageptr = NOPAGE_SIGBUS;
  244.                                 pd->currentMap.addr = 0;
  245.     }
  246. }
  247.  
  248. int test_connection(DEVICE_OBJ *pd)
  249. {
  250.     u16 intCSR_store;
  251.     u16 cntrl_store;
  252.     int i;      
  253.     int error   = 0;
  254.     u32 dwADRH  = pd->pCtl + ADRH;
  255.     u32 dwADRL  = pd->pCtl + ADRL;
  256.     u32 dwADRHL = pd->pCtl + ADRHL;
  257.     u32 dwStore;
  258.     u16 wRet;
  259.  
  260.     cntrl_store  = readw((const volatile void *) pd->pPCIADACntrl);         /* read CONTROL register */
  261.     intCSR_store = readw((const volatile void *) pd->pPCIADAIntCSR);        /* read interrupt + CSR register */
  262.  
  263.     writew(0, (volatile void *) pd->pPCIADAIntCSR);                   /* disable interrupts */
  264.     writew(cntrl_store | 0x0180, (volatile void *) pd->pPCIADACntrl); /* enable access */
  265.  
  266.     // save adr register
  267.     dwStore = readl((const volatile void *) dwADRHL);
  268.     for (i = 1000; i; i--)
  269.     {
  270.         writew(0x5555, (volatile void *) dwADRH);
  271.         writew(0xAAAA, (volatile void *) dwADRL);
  272.         wRet   = readw((const volatile void *) dwADRH);
  273.         if (wRet != 0x5555)
  274.         {
  275.             error = 1;
  276.             break;
  277.         }
  278.  
  279.         writew(0xAAAA, (volatile void *) dwADRH);
  280.         writew(0x5555, (volatile void *) dwADRL);
  281.         wRet   = readw((const volatile void *) dwADRH);
  282.         if (wRet != 0xAAAA)
  283.         {
  284.             error = 1;
  285.             break;
  286.         }
  287.  
  288.         writew(0x0000, (volatile void *) dwADRH);
  289.         writew(0xFFFF, (volatile void *) dwADRL);
  290.         wRet   = readw((const volatile void *) dwADRH);
  291.         if (wRet != 0x0000)
  292.         {
  293.             error = 1;
  294.             break;
  295.         }
  296.  
  297.         writew(0xFFFF, (volatile void *) dwADRH);
  298.         writew(0x0000, (volatile void *) dwADRL);
  299.         wRet   = readw((const volatile void *) dwADRH);
  300.         if (wRet != 0xFFFF)
  301.         {
  302.             error = 1;
  303.             break;
  304.         }
  305.     }
  306.  
  307.     // restore register
  308.     writel(dwStore, (volatile void *) dwADRHL);
  309.  
  310.     //clear possible interrupts
  311.     writew(cntrl_store & ~0x0100, (volatile void *) pd->pPCIADACntrl); /* clear potential interrupt */
  312.  
  313.     // restore LCR registers
  314.     writew(cntrl_store,  (volatile void *) pd->pPCIADACntrl);
  315.     writew(intCSR_store, (volatile void *) pd->pPCIADAIntCSR);
  316.  
  317.     return error;  
  318. }
  319.  
  320. int get_module_info(DEVICE_OBJ *pd)
  321. {
  322.     u16 intCSR_store;
  323.     u16 cntrl_store;
  324.     int found = 0;
  325.     u16 data;
  326.  
  327.     cntrl_store  = readw((const volatile void *) pd->pPCIADACntrl);  /* read CONTROL register */
  328.     intCSR_store = readw((const volatile void *) pd->pPCIADAIntCSR); /* read interrupt + CSR register */
  329.  
  330.     PRINTK(KERN_DEBUG "%s : cntrl=0x%04x, intCSR=0x%04x\n", DEVICE_NAME, cntrl_store, intCSR_store);
  331.  
  332.     if (cntrl_store & 0x0800) /* a VMEMM is connected */
  333.     {
  334.         u16 bla = cntrl_store | 0x0180;
  335.  
  336.         writew(0,   (volatile void *) pd->pPCIADAIntCSR); /* disable interrupts */
  337.         writew(bla, (volatile void *) pd->pPCIADACntrl);  /* enable access */
  338.  
  339.         // read main status register
  340.         data = readw((const volatile void *) pd->pCSR);
  341.  
  342.         if ((data & 0xF000) != VMEMM_MODULE_TYPE)
  343.         {
  344.             pd->cModuleNumber = pd->cFPGAVersion = 255;
  345.             printk(KERN_ERR "%s : Wrong module type connected @ index %d!\n", DEVICE_NAME, pd->wIndex);
  346.         }
  347.         else
  348.         {
  349.             found = 1;
  350.             pd->cModuleNumber     = (data >> 4) & 0xF;
  351.             pd->cFPGAVersion      = (data >> 8) & 0xF;
  352.             pd->cSystemController = (data & 0x0008);
  353.             pd->cWordMode         = (data & 0x0004);
  354.         }
  355.  
  356.         // clear possible interrupts
  357.         writew(cntrl_store & ~0x0100, (volatile void *) pd->pPCIADACntrl); /* clear potential interrupt */
  358.  
  359.         /* restore all contents */
  360.         writew(cntrl_store,  (volatile void *) pd->pPCIADACntrl);
  361.         writew(intCSR_store, (volatile void *) pd->pPCIADAIntCSR);
  362.     }
  363.  
  364.     return found;  
  365. }
  366.  
  367. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
  368. static int pcivme_read_proc(char *buf, char **start, off_t offset, int len)
  369. #else
  370. static int pcivme_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *data)
  371. #endif
  372.     {
  373.     int              pos = 0;
  374.     DEVICE_OBJ       *pd;
  375.     PCIConfig        *ch;
  376.     u16                cntrl;
  377.     char             *cause = "none";
  378.     struct list_head *ptr;
  379.  
  380.     pos += sprintf(buf + pos, "\nPCIVME information. Version %d.%d of %s from Klaus Hitschler.\n", VERSION_HI, VERSION_LO, __DATE__);
  381.  
  382.     pos += sprintf(buf + pos, " ---------------------\n");
  383.     pos += sprintf(buf + pos, " Interfaces found : %d\n", drv.count);
  384.     pos += sprintf(buf + pos, " Major Number     : %d\n", drv.nMajor);
  385.  
  386.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  387.     {
  388.         pd = list_entry(ptr, DEVICE_OBJ, list);
  389.         ch = pd->pPch;
  390.         cntrl = readw((const volatile void *)(pd->pLCR + PLX9050_CNTRL));
  391.         pos += sprintf(buf + pos, " --- %d ---------------\n", pd->wIndex + 1);
  392.         pos += sprintf(buf + pos, " LCR     phys/virt/size : 0x%08lx/0x%08x/%d\n",(long unsigned int) pci_resource_start(ch->pciDev, 0),             pd->pLCR, LCR_SPACE);
  393.         pos += sprintf(buf + pos, " Control phys/virt/size : 0x%08lx/0x%08x/%d\n",(long unsigned int)  pci_resource_start(ch->pciDev, 2),             pd->pCtl, CTL_SPACE);
  394.         pos += sprintf(buf + pos, " VME     phys/virt/size : 0x%08lx/0x%08x/%d\n",(long unsigned int)  pci_resource_start(ch->pciDev, 2) + CTL_SPACE, pd->pVME, VME_SPACE);
  395.         pos += sprintf(buf + pos, " Irq                    : %d\n", pd->wIrq);
  396.  
  397.         if (pd->bConnected)
  398.         {
  399.             pos += sprintf(buf + pos, " VMEMM is or was        : (software) connected.\n");
  400.             pos += sprintf(buf + pos, " Module-Number          : %d\n", pd->cModuleNumber);
  401.             pos += sprintf(buf + pos, " FPGA-Version           : %d\n", pd->cFPGAVersion);
  402.             pos += sprintf(buf + pos, " Systemcontroller       : %s\n", (pd->cSystemController) ? "yes" : "no");
  403.             pos += sprintf(buf + pos, " Word Mode              : %s\n", (pd->cWordMode) ? "yes" : "no");
  404.         }
  405.         else
  406.             pos += sprintf(buf + pos, " VMEMM is or was        : not (software) connected.\n");      
  407.  
  408.         if (!((cntrl & 0x0800) && (!(cntrl & 0x0600))))
  409.             pos += sprintf(buf + pos, " VMEMM is               : powered off or cable disconnected.\n");
  410.  
  411.         pos += sprintf(buf + pos, " IrqCount               : %d\n", pd->dwInterruptCount);
  412.         if (pd->wIrqStatus & PCIADA_INTERRUPT)
  413.             cause = "Timeout";
  414.         else
  415.             if (pd->wIrqStatus & VMEMM_INTERRUPT)
  416.                 cause = "VME";
  417.         pos += sprintf(buf + pos, " Pending IrqStatus      : %s\n", cause);
  418.     }
  419.  
  420.     pos += sprintf(buf + pos, "\n");
  421.  
  422. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
  423.     *eof = 1;
  424. #endif
  425.  
  426.     return pos;
  427. }
  428.  
  429. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
  430. struct proc_dir_entry pcimod_proc_entry =
  431. {
  432.     namelen:    7,                  /* len of name */
  433.     name:       DEVICE_NAME,        /* entry  name */
  434.     mode:       S_IFREG | S_IRUGO,  /* mode */
  435.     nlink:      1,                  /* nlinks */
  436.     get_info:   pcivme_read_proc,  /* function used to read data */
  437. };
  438. #endif
  439.  
  440. static void deleteMyLists(void)
  441. {
  442.     DEVICE_OBJ      *pd;
  443.  
  444.     /* delete my lists */
  445.     while (!list_empty(&drv.devList))                 // cycle through the list of pci devices and remove them
  446.     {
  447.         pd = (DEVICE_OBJ *)drv.devList.prev;            // empty in reverse order
  448.         list_del(&pd->list);
  449.         kfree(pd);
  450.     }
  451.  
  452.     DeletePCIConfig(&drv);
  453. }
  454.  
  455. int init_module(void)
  456. {
  457.     PCIConfig        *ch;
  458.     DEVICE_OBJ       *pd;
  459.     int              result = 0;
  460.     struct list_head *ptr;
  461.  
  462.     PRINTK(KERN_DEBUG "%s : init_module\n", DEVICE_NAME);
  463.  
  464.     /* create list of PCIADAs and work devices */
  465.     INIT_LIST_HEAD(&drv.devList);
  466.     INIT_LIST_HEAD(&drv.pciList);
  467.  
  468.     drv.count = 0;
  469.  
  470.     /* search for all PCIADA modules */
  471.     if ((result = GetPCIConfig(&drv, PCIVME_DEVICE_ID, PCIVME_VENDOR_ID, PCIVME_SUBSYS_ID, PCIVME_SUBVEN_ID)))
  472.     {
  473.         deleteMyLists();
  474.         return result;
  475.     }
  476.  
  477.     /* fix the PLX bug in all PCIADAs */
  478.     for (ptr = drv.pciList.next; ptr != &drv.pciList; ptr = ptr->next)
  479.     {
  480.         ch = list_entry(ptr, PCIConfig, list);
  481.         PLX9050BugFix(ch);
  482.     }
  483.  
  484.     /* create work_devices and translate the access addresses */
  485.     for (ptr = drv.pciList.next; ptr != &drv.pciList; ptr = ptr->next)
  486.     {
  487.         ch = list_entry(ptr, PCIConfig, list);
  488.  
  489.         pd = (DEVICE_OBJ *)kmalloc(sizeof(DEVICE_OBJ), GFP_ATOMIC);
  490.         soft_init(pd);
  491.         pd->pPch = ch;
  492.         pd->wIndex = drv.count;
  493.  
  494.         if (!request_io_memory(ch))
  495.         {
  496.             pd->wInitStep = 1;
  497.  
  498.             if (translate_addresses(pd, ch))
  499.             {
  500.                 printk(KERN_ERR "%s : translation of addresses failed!\n", DEVICE_NAME);
  501.                 kfree_s(pd, sizeof(*pd));       // FREE(pd);
  502.             }
  503.             else
  504.             {
  505.                 // successful translate_addresses
  506.                 pd->wInitStep = 2;
  507.  
  508.                 // create some 'fast access' addresses
  509.                 pd->pAdrMod = pd->pCtl + VICBASE + AMSR;
  510.                 pd->pAdrReg = pd->pCtl + ADRHL;
  511.                 pd->pCSR    = pd->pCtl + CSR;
  512.  
  513.                 pd->pPCIADACntrl  = pd->pLCR + PLX9050_CNTRL;
  514.                 pd->pPCIADAIntCSR = pd->pLCR + PLX9050_INTCSR;
  515.  
  516.                 if (request_irq(pd->pPch->pciDev->irq, pcivme_irqhandler, IRQF_DISABLED| IRQF_SHARED, DEVICE_NAME, pd))
  517.                 {
  518.                     printk(KERN_ERR "%s : can't get irq @ %d\n", DEVICE_NAME, pd->pPch->pciDev->irq);
  519.                     kfree_s(pd, sizeof(*pd));       // FREE(pd);
  520.                 }
  521.                 else
  522.                 {
  523.                     // successful request_irq
  524.                     pd->wInitStep = 3;
  525.                     pd->wIrq = pd->pPch->pciDev->irq;
  526.  
  527.                     list_add_tail(&pd->list, &drv.devList);  /* add this device to list of working devices*/
  528.                     drv.count++;
  529.  
  530.                     pd->bConnected =  get_module_info(pd);
  531.                     if (pd->bConnected && test_connection(pd))
  532.                     {
  533.                         printk(KERN_ERR "%s : connection test @ driver install failed!\n", DEVICE_NAME);
  534.                         pd->bConnected = 0;
  535.                     }
  536.                 }
  537.             }
  538.         }
  539.         else
  540.             printk(KERN_ERR "%s : requested io-memory still claimed!\n", DEVICE_NAME);
  541.     }
  542.  
  543.     drv.nMajor = MAJOR_NO;
  544.     result = register_chrdev(drv.nMajor, DEVICE_NAME, &pcivme_fops);
  545.     if (result < 0)
  546.     {
  547.         printk(KERN_ERR "%s: Can't install driver (%d)\n", DEVICE_NAME, result);
  548.  
  549.         /* untranslate translated addresses */
  550.         for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  551.         {
  552.             pd = list_entry(ptr, DEVICE_OBJ, list);
  553.             ch = pd->pPch;
  554.             un_translate_addresses(pd, ch);
  555.         }
  556.  
  557.         /* delete my lists */
  558.         deleteMyLists();
  559.  
  560.         return result;
  561.     }
  562.     else
  563.     {
  564.         if (drv.nMajor == 0)
  565.             drv.nMajor = result;
  566.  
  567.         printk(KERN_DEBUG "%s : major #%d assigned.\n", DEVICE_NAME, drv.nMajor);
  568.     }
  569.  
  570.     /* register the proc device */
  571. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)  
  572.     proc_register_dynamic(&proc_root, &pcimod_proc_entry);
  573.  
  574.     return 0;
  575. #else
  576.     return create_proc_read_entry(DEVICE_NAME, 0, NULL, pcivme_read_proc, NULL) ? 0 : -ENODEV;
  577. #endif
  578. }
  579.  
  580. void cleanup_module(void)
  581. {
  582.     PCIConfig        *ch;
  583.     DEVICE_OBJ       *pd;
  584.     struct list_head *ptr;
  585.  
  586.     PRINTK(KERN_DEBUG "%s : cleanup_module.\n", DEVICE_NAME);
  587.  
  588.     unregister_chrdev(drv.nMajor, DEVICE_NAME);
  589.  
  590.     /* unregister the proc device */
  591. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
  592.     proc_unregister(&proc_root, pcimod_proc_entry.low_ino);
  593. #else
  594.     remove_proc_entry(DEVICE_NAME, NULL);
  595. #endif
  596.  
  597.     /* redo all */
  598.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  599.     {
  600.         pd = list_entry(ptr, DEVICE_OBJ, list);
  601.         ch = pd->pPch;
  602.         switch (pd->wInitStep)
  603.         {
  604.             case 3:  writew(readw((const volatile void *)(pd->pLCR + PLX9050_INTCSR)) & ~0x40, (volatile void *) (pd->pLCR + PLX9050_INTCSR));  // disable global interrupts
  605.                      free_irq(pd->wIrq, pd);  
  606.             case 2:  un_translate_addresses(pd, ch);
  607.             case 1:  release_io_memory(ch);
  608.             default: pd->wInitStep = 0;
  609.         }
  610.  
  611.         drv.count--;
  612.     }
  613.  
  614.     deleteMyLists();
  615.  
  616.     return;
  617. }
  618.  
  619.