Subversion Repositories f9daq

Rev

Rev 11 | Rev 44 | 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 )
  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. static int pcivme_read_proc(struct file *filp,char *buf,size_t count,loff_t *offp )
  372. #endif
  373.     {
  374.     int              pos = 0;
  375.     DEVICE_OBJ       *pd;
  376.     PCIConfig        *ch;
  377.     u16                cntrl;
  378.     char             *cause = "none";
  379.     struct list_head *ptr;
  380.  
  381.     pos += sprintf(buf + pos, "\nPCIVME information. Version %d.%d of %s from Klaus Hitschler.\n", VERSION_HI, VERSION_LO, __DATE__);
  382.  
  383.     pos += sprintf(buf + pos, " ---------------------\n");
  384.     pos += sprintf(buf + pos, " Interfaces found : %d\n", drv.count);
  385.     pos += sprintf(buf + pos, " Major Number     : %d\n", drv.nMajor);
  386.  
  387.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  388.     {
  389.         pd = list_entry(ptr, DEVICE_OBJ, list);
  390.         ch = pd->pPch;
  391.         cntrl = readw((const volatile void *)(pd->pLCR + PLX9050_CNTRL));
  392.         pos += sprintf(buf + pos, " --- %d ---------------\n", pd->wIndex + 1);
  393.         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);
  394.         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);
  395.         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);
  396.         pos += sprintf(buf + pos, " Irq                    : %d\n", pd->wIrq);
  397.  
  398.         if (pd->bConnected)
  399.         {
  400.             pos += sprintf(buf + pos, " VMEMM is or was        : (software) connected.\n");
  401.             pos += sprintf(buf + pos, " Module-Number          : %d\n", pd->cModuleNumber);
  402.             pos += sprintf(buf + pos, " FPGA-Version           : %d\n", pd->cFPGAVersion);
  403.             pos += sprintf(buf + pos, " Systemcontroller       : %s\n", (pd->cSystemController) ? "yes" : "no");
  404.             pos += sprintf(buf + pos, " Word Mode              : %s\n", (pd->cWordMode) ? "yes" : "no");
  405.         }
  406.         else
  407.             pos += sprintf(buf + pos, " VMEMM is or was        : not (software) connected.\n");      
  408.  
  409.         if (!((cntrl & 0x0800) && (!(cntrl & 0x0600))))
  410.             pos += sprintf(buf + pos, " VMEMM is               : powered off or cable disconnected.\n");
  411.  
  412.         pos += sprintf(buf + pos, " IrqCount               : %d\n", pd->dwInterruptCount);
  413.         if (pd->wIrqStatus & PCIADA_INTERRUPT)
  414.             cause = "Timeout";
  415.         else
  416.             if (pd->wIrqStatus & VMEMM_INTERRUPT)
  417.                 cause = "VME";
  418.         pos += sprintf(buf + pos, " Pending IrqStatus      : %s\n", cause);
  419.     }
  420.  
  421.     pos += sprintf(buf + pos, "\n");
  422.  
  423. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
  424.     //*eof = 1;
  425. #endif
  426.  
  427.     return pos;
  428. }
  429.  
  430.  
  431.  
  432.  
  433. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
  434. struct proc_dir_entry pcimod_proc_entry =
  435. {
  436.     namelen:    7,                  /* len of name */
  437.     name:       DEVICE_NAME,        /* entry  name */
  438.     mode:       S_IFREG | S_IRUGO,  /* mode */
  439.     nlink:      1,                  /* nlinks */
  440.     get_info:   pcivme_read_proc,  /* function used to read data */
  441. };
  442. #else
  443. struct file_operations proc_fops = {
  444. read: pcivme_read_proc
  445. };
  446. #endif
  447.  
  448. static void deleteMyLists(void)
  449. {
  450.     DEVICE_OBJ      *pd;
  451.  
  452.     /* delete my lists */
  453.     while (!list_empty(&drv.devList))                 // cycle through the list of pci devices and remove them
  454.     {
  455.         pd = (DEVICE_OBJ *)drv.devList.prev;            // empty in reverse order
  456.         list_del(&pd->list);
  457.         kfree(pd);
  458.     }
  459.  
  460.     DeletePCIConfig(&drv);
  461. }
  462.  
  463. int init_module(void)
  464. {
  465.     PCIConfig        *ch;
  466.     DEVICE_OBJ       *pd;
  467.     int              result = 0;
  468.     struct list_head *ptr;
  469.  
  470.     PRINTK(KERN_DEBUG "%s : init_module\n", DEVICE_NAME);
  471.  
  472.     /* create list of PCIADAs and work devices */
  473.     INIT_LIST_HEAD(&drv.devList);
  474.     INIT_LIST_HEAD(&drv.pciList);
  475.  
  476.     drv.count = 0;
  477.  
  478.     /* search for all PCIADA modules */
  479.     if ((result = GetPCIConfig(&drv, PCIVME_DEVICE_ID, PCIVME_VENDOR_ID, PCIVME_SUBSYS_ID, PCIVME_SUBVEN_ID)))
  480.     {
  481.         deleteMyLists();
  482.         return result;
  483.     }
  484.  
  485.     /* fix the PLX bug in all PCIADAs */
  486.     for (ptr = drv.pciList.next; ptr != &drv.pciList; ptr = ptr->next)
  487.     {
  488.         ch = list_entry(ptr, PCIConfig, list);
  489.         PLX9050BugFix(ch);
  490.     }
  491.  
  492.     /* create work_devices and translate the access addresses */
  493.     for (ptr = drv.pciList.next; ptr != &drv.pciList; ptr = ptr->next)
  494.     {
  495.         ch = list_entry(ptr, PCIConfig, list);
  496.  
  497.         pd = (DEVICE_OBJ *)kmalloc(sizeof(DEVICE_OBJ), GFP_ATOMIC);
  498.         soft_init(pd);
  499.         pd->pPch = ch;
  500.         pd->wIndex = drv.count;
  501.  
  502.         if (!request_io_memory(ch))
  503.         {
  504.             pd->wInitStep = 1;
  505.  
  506.             if (translate_addresses(pd, ch))
  507.             {
  508.                 printk(KERN_ERR "%s : translation of addresses failed!\n", DEVICE_NAME);
  509.                 kfree_s(pd, sizeof(*pd));       // FREE(pd);
  510.             }
  511.             else
  512.             {
  513.                 // successful translate_addresses
  514.                 pd->wInitStep = 2;
  515.  
  516.                 // create some 'fast access' addresses
  517.                 pd->pAdrMod = pd->pCtl + VICBASE + AMSR;
  518.                 pd->pAdrReg = pd->pCtl + ADRHL;
  519.                 pd->pCSR    = pd->pCtl + CSR;
  520.  
  521.                 pd->pPCIADACntrl  = pd->pLCR + PLX9050_CNTRL;
  522.                 pd->pPCIADAIntCSR = pd->pLCR + PLX9050_INTCSR;
  523.  
  524.                 if (request_irq(pd->pPch->pciDev->irq, pcivme_irqhandler, IRQF_DISABLED| IRQF_SHARED, DEVICE_NAME, pd))
  525.                 {
  526.                     printk(KERN_ERR "%s : can't get irq @ %d\n", DEVICE_NAME, pd->pPch->pciDev->irq);
  527.                     kfree_s(pd, sizeof(*pd));       // FREE(pd);
  528.                 }
  529.                 else
  530.                 {
  531.                     // successful request_irq
  532.                     pd->wInitStep = 3;
  533.                     pd->wIrq = pd->pPch->pciDev->irq;
  534.  
  535.                     list_add_tail(&pd->list, &drv.devList);  /* add this device to list of working devices*/
  536.                     drv.count++;
  537.  
  538.                     pd->bConnected =  get_module_info(pd);
  539.                     if (pd->bConnected && test_connection(pd))
  540.                     {
  541.                         printk(KERN_ERR "%s : connection test @ driver install failed!\n", DEVICE_NAME);
  542.                         pd->bConnected = 0;
  543.                     }
  544.                 }
  545.             }
  546.         }
  547.         else
  548.             printk(KERN_ERR "%s : requested io-memory still claimed!\n", DEVICE_NAME);
  549.     }
  550.  
  551.     drv.nMajor = MAJOR_NO;
  552.     result = register_chrdev(drv.nMajor, DEVICE_NAME, &pcivme_fops);
  553.     if (result < 0)
  554.     {
  555.         printk(KERN_ERR "%s: Can't install driver (%d)\n", DEVICE_NAME, result);
  556.  
  557.         /* untranslate translated addresses */
  558.         for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  559.         {
  560.             pd = list_entry(ptr, DEVICE_OBJ, list);
  561.             ch = pd->pPch;
  562.             un_translate_addresses(pd, ch);
  563.         }
  564.  
  565.         /* delete my lists */
  566.         deleteMyLists();
  567.  
  568.         return result;
  569.     }
  570.     else
  571.     {
  572.         if (drv.nMajor == 0)
  573.             drv.nMajor = result;
  574.  
  575.         printk(KERN_DEBUG "%s : major #%d assigned.\n", DEVICE_NAME, drv.nMajor);
  576.     }
  577.  
  578.     /* register the proc device */
  579. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)  
  580.     proc_register_dynamic(&proc_root, &pcimod_proc_entry);
  581.  
  582.     return 0;
  583. #else
  584.     // create_proc_read_entry is depricated since kernel 3.10
  585.     //return create_proc_read_entry(DEVICE_NAME, 0, NULL, pcivme_read_proc, NULL) ? 0 : -ENODEV;
  586.     return proc_create_data(DEVICE_NAME, 0, NULL, &pcivme_fops, NULL) ? 0 : -ENODEV;
  587. #endif
  588. }
  589.  
  590. void cleanup_module(void)
  591. {
  592.     PCIConfig        *ch;
  593.     DEVICE_OBJ       *pd;
  594.     struct list_head *ptr;
  595.  
  596.     PRINTK(KERN_DEBUG "%s : cleanup_module.\n", DEVICE_NAME);
  597.  
  598.     unregister_chrdev(drv.nMajor, DEVICE_NAME);
  599.  
  600.     /* unregister the proc device */
  601. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
  602.     proc_unregister(&proc_root, pcimod_proc_entry.low_ino);
  603. #else
  604.     remove_proc_entry(DEVICE_NAME, NULL);
  605. #endif
  606.  
  607.     /* redo all */
  608.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  609.     {
  610.         pd = list_entry(ptr, DEVICE_OBJ, list);
  611.         ch = pd->pPch;
  612.         switch (pd->wInitStep)
  613.         {
  614.             case 3:  writew(readw((const volatile void *)(pd->pLCR + PLX9050_INTCSR)) & ~0x40, (volatile void *) (pd->pLCR + PLX9050_INTCSR));  // disable global interrupts
  615.                      free_irq(pd->wIrq, pd);  
  616.             case 2:  un_translate_addresses(pd, ch);
  617.             case 1:  release_io_memory(ch);
  618.             default: pd->wInitStep = 0;
  619.         }
  620.  
  621.         drv.count--;
  622.     }
  623.  
  624.     deleteMyLists();
  625.  
  626.     return;
  627. }
  628.  
  629.