Subversion Repositories f9daq

Rev

Rev 9 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. //****************************************************************************
  2. // Copyright (C) 2000-2004  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. // fops.c -- the file operations module for the PCIVME PCI to VME Interface
  32. //
  33. // $Log: fops.c,v $
  34. // Revision 1.11  2005/03/01 10:56:12  klaus
  35. // removed warnings with gcc 3.3.3
  36. //
  37. // Revision 1.10  2004/08/13 19:23:26  klaus
  38. // conversion to kernel-version 2.6, released version 3.0
  39. //
  40. // Revision 1.9  2003/06/27 17:25:52  klaus
  41. // incomplete try to get mmap() with nopage() running for automatic page switch
  42. //
  43. // Revision 1.8  2002/10/20 18:06:51  klaus
  44. // changed error handling
  45. //
  46. // Revision 1.7  2002/10/18 21:56:28  klaus
  47. // completed functional features, untested
  48. //
  49. // Revision 1.6  2002/10/18 21:56:28  klaus
  50. // completed functional features, untested
  51. //
  52. // Revision 1.5  2002/10/17 19:05:03  klaus
  53. // VME access is working through test to lib to driver
  54. //
  55. //****************************************************************************
  56.  
  57. /*--- INCLUDES -----------------------------------------------------------------------------------*/
  58. #include "common.h"  /* must be the first include */
  59.  
  60. #include <linux/kernel.h> /* printk() */
  61. #include <linux/module.h> /* only here ?cause of MAJOR ... */
  62. #include <linux/pci.h>
  63. #include <linux/list.h>
  64. #include <asm/errno.h>
  65. #include <asm/types.h>
  66. #include <asm/uaccess.h>
  67.  
  68. #include <linux/sched.h>
  69. #include <linux/fs.h>
  70. #if HAVE_UNLOCKED_IOCTL
  71.     #include <linux/mutex.h>
  72. #else
  73.     #include <linux/smp_lock.h>
  74. #endif
  75.  
  76.  
  77. #include "fops.h"
  78. #include "plx9050.h"
  79. #include "pcivme.h"      /* the common ioctl commands and structures between driver and application */
  80. #include "main.h"
  81. #include "askpci.h"
  82. #include "pciif.h"
  83. #include "vic.h"
  84. #include "vme.h"
  85.  
  86. /*--- DEFINES ------------------------------------------------------------------------------------*/
  87.  
  88. #ifndef MINOR
  89. #define MINOR(x) minor(x)            // since 2.5.?
  90. #endif
  91.  
  92. static PCIVME_INIT_ELEMENT init_element[] =
  93. {{LCR,  WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts
  94.     {LCR,  WORD_ACCESS, PLX9050_CNTRL,  RELEASE_VMEMM},       // enable interface
  95.  
  96.     {VIC,  BYTE_ACCESS, VIICR, 0xf8+1},      // VIICR
  97.  
  98.     {VIC,  BYTE_ACCESS, VICR1, 0x78+1},      // VICR1
  99.     {VIC,  BYTE_ACCESS, VICR2, 0x78+2},
  100.     {VIC,  BYTE_ACCESS, VICR3, 0x78+3},
  101.     {VIC,  BYTE_ACCESS, VICR4, 0x78+4},
  102.     {VIC,  BYTE_ACCESS, VICR5, 0x78+5},
  103.     {VIC,  BYTE_ACCESS, VICR6, 0x78+6},
  104.     {VIC,  BYTE_ACCESS, VICR7, 0x78+7},      // VICR7
  105.  
  106.     {VIC,  BYTE_ACCESS, DSICR, 0xf8+0},      // DSICR
  107.  
  108.     {VIC,  BYTE_ACCESS, LICR1, 0xf8+1},      // LICR1
  109.     {VIC,  BYTE_ACCESS, LICR2, 0xf8+2},
  110.     {VIC,  BYTE_ACCESS, LICR3, 0xf8+3},
  111.     {VIC,  BYTE_ACCESS, LICR4, 0xf8+4},
  112.     {VIC,  BYTE_ACCESS, LICR5, 0xf8+5},
  113.     {VIC,  BYTE_ACCESS, LICR6, 0x38+6},
  114.     {VIC,  BYTE_ACCESS, LICR7, 0x38+7},      // LICR7
  115.  
  116.     {VIC,  BYTE_ACCESS, ICGSICR, 0xf8+2},    // ICGS
  117.     {VIC,  BYTE_ACCESS, ICMSICR, 0xf8+3},    // ICMS
  118.  
  119.     {VIC,  BYTE_ACCESS, EGICR, 0xf8+6},      // EGICR
  120.  
  121.     {VIC,  BYTE_ACCESS, ICGSVBR, 0x08},      // ICGS-IVBR (!)
  122.     {VIC,  BYTE_ACCESS, ICMSVBR, 0x0c},      // ICMS-IVBR (!)
  123.  
  124.     {VIC,  BYTE_ACCESS, LIVBR, 0x00},        // LIVBR (!)
  125.  
  126.     {VIC,  BYTE_ACCESS, EGIVBR, 0x10},       // EGIVBR (!)
  127.  
  128.     {VIC,  BYTE_ACCESS, ICSR, 0x00},         // ICSR
  129.  
  130.     {VIC,  BYTE_ACCESS, ICR0, 0x00},         // ICR0
  131.     {VIC,  BYTE_ACCESS, ICR1, 0x00},
  132.     {VIC,  BYTE_ACCESS, ICR2, 0x00},
  133.     {VIC,  BYTE_ACCESS, ICR3, 0x00},
  134.     {VIC,  BYTE_ACCESS, ICR4, 0x00},         // ICR4
  135.  
  136.     {VIC,  BYTE_ACCESS, VIRSR, 0xfe},        // VIRSR
  137.  
  138.     {VIC,  BYTE_ACCESS, VIVR1, 0x0f},        // VIVR1
  139.     {VIC,  BYTE_ACCESS, VIVR2, 0x0f},
  140.     {VIC,  BYTE_ACCESS, VIVR3, 0x0f},
  141.     {VIC,  BYTE_ACCESS, VIVR4, 0x0f},
  142.     {VIC,  BYTE_ACCESS, VIVR5, 0x0f},
  143.     {VIC,  BYTE_ACCESS, VIVR6, 0x0f},
  144.     {VIC,  BYTE_ACCESS, VIVR7, 0x0f},        // VIVR7
  145.  
  146.     {VIC,  BYTE_ACCESS, TTR, 0x3c},          // TTR
  147.  
  148.     {VIC,  BYTE_ACCESS, ARCR, 0x40},         // ARCR
  149.     {VIC,  BYTE_ACCESS, AMSR, 0x29},         // AMSR
  150.     {VIC,  BYTE_ACCESS, RCR, 0x00},          // RCR
  151.  
  152.     {IFR,  LONG_ACCESS, (u16)ADRHL, 0xF0F0F0F0},  // ADR-H, ADR-L
  153.     {IFR,  WORD_ACCESS, (u16)CSR  , 0x0000},      // Contr-Reg
  154.  
  155.     {VIC,  BYTE_ACCESS, ICR7, 0x80},         // ICR7
  156.  
  157.     {LCR,  WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS},  // disable interrupts
  158.  
  159.     {STOP, WORD_ACCESS, 0,     0}};
  160.  
  161. static PCIVME_INIT_ELEMENT deinit_element_pre[] =
  162. {{VIC,  BYTE_ACCESS, ICR7, 0x00},         // ICR7 - sysfail
  163.     {LCR,  WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS},  // disable interrupts
  164.     {STOP, WORD_ACCESS, 0,    0}};
  165.  
  166. static PCIVME_INIT_ELEMENT deinit_element_post[] =
  167. {{LCR,  WORD_ACCESS, PLX9050_CNTRL, INHIBIT_VMEMM},     // disable interface
  168.     {STOP, WORD_ACCESS, 0,    0}};
  169.  
  170.  
  171. /*--- EXTERNALS ----------------------------------------------------------------------------------*/
  172.  
  173. /*--- TYPEDEFS -----------------------------------------------------------------------------------*/
  174.  
  175. /*--- FUNCTIONS ----------------------------------------------------------------------------------*/
  176. static inline void switch_VMEMM_on(DEVICE_OBJ *pd)
  177. {
  178.     writew(RELEASE_VMEMM, (volatile void *) (pd->pLCR + PLX9050_CNTRL)); /* enable access */
  179. }
  180.  
  181. static inline void switch_VMEMM_off(DEVICE_OBJ *pd)
  182. {
  183.     writew(INHIBIT_VMEMM, (volatile void *) (pd->pLCR + PLX9050_CNTRL)); /* enable access */
  184. }
  185.  
  186. static inline void setPageAddress(DEVICE_OBJ *pd, u32 newPageAddress)
  187. {
  188.     PRINTK(KERN_DEBUG "%s : setPageAddress(0x%08x)\n", DEVICE_NAME, newPageAddress);
  189.  
  190.     writel(newPageAddress, (volatile void *) pd->pAdrReg);
  191.     pd->dwCurrentPageAddress = newPageAddress;
  192. }
  193.  
  194. static inline void setModifier(DEVICE_OBJ *pd, u8 newModifier)
  195. {
  196.     PRINTK(KERN_DEBUG "%s : setModifier(0x%02x)\n", DEVICE_NAME, newModifier);
  197.  
  198.     writeb(newModifier, (volatile void *) pd->pAdrMod);
  199.     pd->bCurrentModifier = newModifier;
  200. }
  201.  
  202. /* read and write functions -----------------------------------------------------------------------*/
  203. static inline u8 *increment8(void **pvBuffer)
  204. {
  205.   u8 *tmp = (u8*)*pvBuffer;
  206.  
  207.   *pvBuffer += sizeof(u8);
  208.  
  209.   return tmp;
  210. }
  211.  
  212. static inline u16 *increment16(void **pvBuffer)
  213. {
  214.   u16 *tmp = (u16*)*pvBuffer;
  215.  
  216.   *pvBuffer += sizeof(u16);
  217.  
  218.   return tmp;
  219. }
  220.  
  221. static inline u32 *increment32(void **pvBuffer)
  222. {
  223.   u32 *tmp = (u32*)*pvBuffer;
  224.  
  225.   *pvBuffer += sizeof(u32);
  226.  
  227.   return tmp;
  228. }
  229.  
  230. static void readByte(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage)
  231. {
  232.     u8 tmp;
  233.  
  234.     tmp = readb((const volatile void *) (pd->pVME + dwLocalAddressInPage));
  235.     __put_user(tmp, increment8(pvBuffer));
  236. }
  237.  
  238. static void writeByte(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer)
  239. {
  240.     u8 tmp;
  241.  
  242.     __get_user(tmp, increment8(pvBuffer));
  243.     writeb(tmp, (volatile void *) (pd->pVME + dwLocalAddressInPage ));
  244. }
  245.  
  246. static void readWord(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage)
  247. {
  248.     u16 tmp;
  249.  
  250.     tmp = readw((const volatile void *) (pd->pVME + dwLocalAddressInPage));
  251.     __put_user(tmp, increment16(pvBuffer));
  252. }
  253.  
  254. static void writeWord(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer)
  255. {
  256.     u16 tmp;
  257.  
  258.     __get_user(tmp, increment16(pvBuffer));
  259.     writew(tmp, (volatile void *) ( pd->pVME + dwLocalAddressInPage ));
  260. }
  261.  
  262. static void readLong(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage)
  263. {
  264.     u32 tmp;
  265.  
  266.     tmp = readl((const volatile void *) (pd->pVME + dwLocalAddressInPage));
  267.     __put_user(tmp, increment32(pvBuffer));
  268. }
  269.  
  270. static void writeLong(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer)
  271. {
  272.     u32 tmp;
  273.  
  274.     __get_user(tmp, increment32(pvBuffer));
  275.     writel(tmp, (volatile void *) (pd->pVME + dwLocalAddressInPage));
  276. }
  277.  
  278. /* test alignment functions -----------------------------------------------------------------------*/
  279. static int MisalignmentForByteAccess(loff_t offset)
  280. {
  281.     return 0;
  282. }
  283.  
  284. static int MisalignmentForWordAccess(loff_t offset)
  285. {
  286.     return(offset & 1);
  287. }
  288.  
  289. static int MisalignmentForLongAccess(loff_t offset)
  290. {
  291.     return(offset & 3);
  292. }
  293.  
  294. // helper functions --------------------------------------------------------------------------------
  295. int check_command(const PCIVME_INIT_ELEMENT *psInitElement)
  296. {
  297.     u16 range;
  298.     u16 access_size;
  299.  
  300.     // PRINTK(KERN_DEBUG "%s : check_command()\n", DEVICE_NAME);
  301.  
  302.     switch (psInitElement->bDestination)
  303.     {
  304.         case LCR:
  305.             range = 0x54;    
  306.             break;
  307.         case IFR:
  308.             range = 0x0c;    
  309.             break;
  310.         case VIC:
  311.             range = 0xe4;
  312.             if ((psInitElement->wOffset & 3) != 3)
  313.                 return -EINVAL;
  314.             break;
  315.         default:  
  316.             return -EINVAL;        
  317.             break;
  318.     }
  319.  
  320.     // check alignment and allowed address range
  321.     switch (psInitElement->bAccessType)
  322.     {
  323.         case LONG_ACCESS:
  324.             if (psInitElement->wOffset & 3)
  325.                 return -EINVAL;
  326.             access_size = sizeof(u32);
  327.             break;
  328.         case WORD_ACCESS:
  329.             if (psInitElement->wOffset & 1)
  330.                 return -EINVAL;
  331.             access_size = sizeof(u16);
  332.             break;
  333.         case BYTE_ACCESS:
  334.             access_size = sizeof(u8);
  335.             break;
  336.         default         :
  337.             return -EINVAL;        
  338.             break;
  339.     }
  340.  
  341.     if ((psInitElement->wOffset + access_size) > range)
  342.         return -EINVAL;       // ignore it
  343.  
  344.     return 0;
  345. }
  346.  
  347. static int CmdMachine(DEVICE_OBJ *pd, const PCIVME_INIT_ELEMENT *psInitElement)
  348. {
  349.     u32 adr;
  350.     int err;
  351.  
  352.     //PRINTK(KERN_DEBUG "%s : CmdMachine()\n", DEVICE_NAME);
  353.  
  354.     // loop through the init (or deinit) list
  355.     while (psInitElement->bDestination != STOP)
  356.     {
  357.         err = check_command(psInitElement);
  358.         if (!err)
  359.         {
  360.             switch (psInitElement->bDestination)
  361.             {
  362.                 case LCR:
  363.                     adr = pd->pLCR;
  364.                     break;
  365.                 case VIC:
  366.                     adr = pd->pCtl + VICBASE;
  367.                     break;
  368.                 case IFR:
  369.                     adr = pd->pCtl + CSR;
  370.                     break;  
  371.                 default:
  372.                     return -EINVAL;
  373.             }
  374.  
  375.             switch (psInitElement->bAccessType)
  376.             {
  377.                 case LONG_ACCESS:
  378.                     writel(psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset));
  379.                     break;
  380.                 case WORD_ACCESS:
  381.                     writew((u16)psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset));
  382.                     break;
  383.                 case BYTE_ACCESS:
  384.                     writeb((u8)psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset));
  385.                     break;
  386.                 default:
  387.                     return -EINVAL;
  388.             }
  389.         }
  390.         else
  391.             return err;
  392.  
  393.         psInitElement++;
  394.     }
  395.  
  396.     return 0;
  397. }
  398.  
  399. // all ioctls --------------------------------------------------------------------------------------
  400. static int init_hardware(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_INIT_COMMAND *init)
  401. {
  402.     int err;
  403.     PCIVME_INIT_ELEMENT *element = init->sVie;
  404.  
  405.     PRINTK(KERN_INFO "%s : init_hardware()\n", DEVICE_NAME);
  406.  
  407.     err = CmdMachine(pd, element);
  408.     if (err)
  409.     {
  410.         PRINTK(KERN_DEBUG "%s : init failed with err = %d!\n", DEVICE_NAME, err);
  411.         return err;
  412.     }
  413.  
  414.     // sync storage with hardware
  415.     pd->bCurrentModifier     = readb((const volatile void *) pd->pAdrMod) & 0x3f;
  416.     pd->dwCurrentPageAddress = readl((const volatile void *) pd->pAdrReg) & HI_ADDRESS_MASK;
  417.  
  418.     return 0;
  419. }
  420.  
  421. static int deinit_hardware(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_INIT_COMMAND *deinit)
  422. {
  423.     int err;
  424.     PCIVME_INIT_ELEMENT *element = deinit->sVie;
  425.  
  426.     PRINTK(KERN_DEBUG "%s : deinit_hardware()\n", DEVICE_NAME);
  427.  
  428.     err = CmdMachine(pd, deinit_element_pre);  
  429.     if (err)
  430.         goto fail;
  431.  
  432.     err = CmdMachine(pd, element);
  433.     if (err)
  434.         goto fail;
  435.  
  436.     err = CmdMachine(pd, deinit_element_post);
  437.     if (err)
  438.         goto fail;
  439.  
  440.     return 0;
  441.  
  442.     fail:
  443.     return err;
  444. }
  445.  
  446. static int access_command(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_ACCESS_COMMAND *cmd)
  447. {
  448.     PRINTK(KERN_DEBUG "%s : access_command()\n", DEVICE_NAME);
  449.  
  450.     pp->bModifier    = cmd->bModifier;
  451.     pp->bAccessType  = cmd->bAccessType;
  452.     pp->bIncrement   = cmd->bIncrement;
  453.  
  454.     switch (pp->bAccessType)
  455.     {
  456.         case BYTE_ACCESS:
  457.             pp->read  = readByte;
  458.             pp->write = writeByte;
  459.             pp->AlignmentCheck = MisalignmentForByteAccess;
  460.             break;
  461.         case WORD_ACCESS:
  462.             pp->read  = readWord;
  463.             pp->write = writeWord;
  464.             pp->AlignmentCheck = MisalignmentForWordAccess;
  465.             break;
  466.         case LONG_ACCESS:
  467.             pp->read  = readLong;
  468.             pp->write = writeLong;
  469.             pp->AlignmentCheck = MisalignmentForLongAccess;
  470.             break;
  471.         default:
  472.             return -EINVAL;
  473.     }
  474.  
  475.     return 0;
  476. }
  477.  
  478. static int get_static_status(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_STATIC_STATUS *static_status)
  479. {
  480.     PRINTK(KERN_DEBUG "%s : get_static_status()\n", DEVICE_NAME);
  481.  
  482.     static_status->bConnected        = pd->bConnected;
  483.     static_status->cModuleNumber     = pd->cModuleNumber;
  484.     static_status->cFPGAVersion      = pd->cFPGAVersion;
  485.     static_status->cSystemController = pd->cSystemController;
  486.     static_status->cWordMode         = pd->cWordMode;
  487.  
  488.     return 0;
  489. }
  490.  
  491. static int get_dynamic_status(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_DYNAMIC_STATUS *dynamic_status)
  492. {
  493.     u16 cntrl  = readw((const volatile void *)  pd->pPCIADACntrl);
  494.     u16 intCSR = readw((const volatile void *)  pd->pPCIADAIntCSR);
  495.  
  496.     PRINTK(KERN_DEBUG "%s : get_dynamic_status()\n", DEVICE_NAME);
  497.  
  498.     dynamic_status->bConnected = (cntrl  & 0x0800) ? 1 : 0;
  499.     dynamic_status->bPCIADAIrq = (intCSR & 0x0020) ? 1 : 0;
  500.     dynamic_status->bVMEMMIrq  = (intCSR & 0x0004) ? 1 : 0;
  501.  
  502.     return 0;
  503. }
  504.  
  505. static int read_vector_polling(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VECTOR_LEVEL *vector)
  506. {
  507.     u16 cntrl  = readw((const volatile void *) pd->pPCIADACntrl);
  508.     u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
  509.  
  510.     PRINTK(KERN_DEBUG "%s : read_vector()\n", DEVICE_NAME);
  511.  
  512.     vector->dwStatusID = 0;  
  513.     vector->bLevel     = 0;
  514.     vector->bPCIADAIrq = 0;
  515.  
  516.     if (intCSR & 0x20) // check for PCIADA interrupt
  517.     {
  518.         vector->bPCIADAIrq = 1;
  519.         vector->dwStatusID = 1; // force for PCIADA irqs
  520.  
  521.         writew(cntrl & ~0x0100, (volatile void *) pd->pPCIADACntrl);   // clear pending PCIADA irq
  522.         writew(cntrl,           (volatile void *) pd->pPCIADACntrl);
  523.     }
  524.     else
  525.     {
  526.         if ((cntrl & 0x0980) == 0x0980) // check if VMEMM is connected and ready
  527.         {
  528.             vector->bLevel = (u8)readw((const volatile void *) ( pd->pCtl + VICRES ));
  529.             if (vector->bLevel & 1)
  530.             {
  531.                 if (vector->bLevel != 1)
  532.                     vector->dwStatusID = (u32)readb((const volatile void *) (pd->pCtl + VECBASE + vector->bLevel));
  533.  
  534.                 vector->bLevel >>= 1;
  535.             }
  536.         }
  537.     }
  538.     return 0;
  539. }
  540.  
  541. static int read_vector_blocking(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VECTOR_LEVEL *vector, struct file *pFile)
  542. {
  543.     int error;
  544.  
  545.     vector->dwStatusID = 0;  
  546.     vector->bLevel     = 0;
  547.     vector->bPCIADAIrq = 0;
  548.  
  549.     // support nonblocking read if requested
  550.     if ((pFile->f_flags & O_NONBLOCK) && (!pd->wIrqStatus))
  551.         return -EAGAIN;
  552.  
  553.     // sleep until data are available
  554.     if ((error = wait_event_interruptible(pd->event_queue, (pd->wIrqStatus))))
  555.         return error;
  556.  
  557.     error = read_vector_polling(pp, pd, vector);
  558.  
  559.     pd->wIrqStatus  = 0;  // clear the status since it is read 
  560.  
  561.     return error;
  562. }
  563.  
  564.  
  565. static int control_interrupts(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_IRQ_CONTROL *irq_control)
  566. {
  567.     u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
  568.     u8  ret    = (intCSR & 0x40) ? 1 : 0;
  569.  
  570.     PRINTK(KERN_DEBUG "%s : control_interrupts()\n", DEVICE_NAME);
  571.  
  572.     if (irq_control->bEnable)
  573.         writew(intCSR |  0x40, (volatile void *) pd->pPCIADAIntCSR);
  574.     else
  575.         writew(intCSR & ~0x40, (volatile void *) pd->pPCIADAIntCSR);
  576.  
  577.     // return the switch before set
  578.     irq_control->bEnable = ret;
  579.  
  580.     return 0;
  581. }
  582.  
  583. static int VME_TAS(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_TAS_STRUCT *tas_cmd)
  584. {
  585.     u32 access_adr = pd->pVME + (tas_cmd->dwAddress & LO_ADDRESS_MASK); // make low part of address
  586.     u8  data;
  587.  
  588.     // save old contents
  589.     u32 old_address         = readl((const volatile void *) pd->pAdrReg);
  590.     u16 old_CSR             = readw((const volatile void *) pd->pCSR);
  591.     u16 intCSR              = readw((const volatile void *) pd->pPCIADAIntCSR);
  592.     pd->bCurrentModifier    = readb((const volatile void *) pd->pAdrMod) & 0x3f;
  593.  
  594.     PRINTK(KERN_DEBUG "%s : VME_TAS()\n", DEVICE_NAME);
  595.  
  596.     // set new contents
  597.     writew(DISABLE_PCIADA_IRQS,           (volatile void *) pd->pPCIADAIntCSR);
  598.     writeb((u8)tas_cmd->bModifier & 0x3f, (volatile void *) pd->pAdrMod);
  599.     writel(tas_cmd->dwAddress,            (volatile void *) pd->pAdrReg);
  600.     writew(old_CSR | FLAG_RMC,            (volatile void *) pd->pCSR);
  601.  
  602.     // do the read - modify - write
  603.     data = readb((const volatile void *) access_adr);
  604.     writeb(tas_cmd->bContent, (volatile void *) access_adr);
  605.  
  606.     // restore old contents
  607.     writeb(pd->bCurrentModifier, (volatile void *) pd->pAdrMod);
  608.     writew(old_CSR,              (volatile void *) pd->pCSR);
  609.     writel(old_address,          (volatile void *) pd->pAdrReg);
  610.     writew(intCSR,               (volatile void *) pd->pPCIADAIntCSR);
  611.  
  612.     // get back read data
  613.     tas_cmd->bContent = data;
  614.  
  615.     return 0;
  616. }
  617.  
  618. static int VMEMM_RESET(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_RESET_COMMAND *reset_cmd)
  619. {
  620.     u16 cntrl  = readw((const volatile void *) pd->pPCIADACntrl);
  621.     u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
  622.     int status = 0;
  623.  
  624.     PRINTK(KERN_INFO "%s : VMEMM_RESET()\n", DEVICE_NAME);
  625.  
  626.     // am I connected and switched on??
  627.     if ((cntrl & 0x0980) == 0x0980)
  628.     {
  629.         // do command
  630.         switch (reset_cmd->bCommand)
  631.         {
  632.             case POLL_RESET_CMD:
  633.                 break;
  634.             case VME_RESET_CMD:
  635.                 writeb(0, (volatile void *) pd->pAdrMod);
  636.                 writeb(0xf0, (volatile void *) (pd->pCtl + VICBASE + SRR));  // make VME reset
  637.                 break;
  638.             case LOCAL_RESET_CMD:
  639.                 writeb(0, (volatile void *) pd->pAdrMod);
  640.                 writew(LOCAL_RESET,  (volatile void *) (pd->pCtl + VICRES));
  641.                 break;
  642.             case GLOBAL_RESET_CMD:
  643.                 writeb(0, (volatile void *) pd->pAdrMod);
  644.                 writew(GLOBAL_RESET, (volatile void *) (pd->pCtl + VICRES));
  645.                 break;
  646.  
  647.             default: status = -EINVAL;
  648.         }
  649.  
  650.         // inhibit PCIADA generated irqs
  651.         writew(DISABLE_PCIADA_IRQS, (volatile void *) pd->pPCIADAIntCSR);
  652.  
  653.         // always poll reset status - access will sometimes generate PCIADA #2 interrupt
  654.         reset_cmd->bResult = readb((const volatile void *) pd->pAdrMod);
  655.  
  656.         // reset any pending PCIADA interrupt #2
  657.         writew(cntrl & ~0x0100, (volatile void *) pd->pPCIADACntrl);
  658.         writew(cntrl          , (volatile void *) pd->pPCIADACntrl);
  659.  
  660.         // restore IRQStatus
  661.         writew(intCSR          , (volatile void *) pd->pPCIADAIntCSR);
  662.     }
  663.     else
  664.         status = -EBUSY;
  665.  
  666.     // sync storage with hardware
  667.     pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f;
  668.  
  669.     return status;
  670. }
  671.  
  672. static int access_VIC68A(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VIC68A_ACTION *action)
  673. {
  674.     int nStatus = 0;
  675.  
  676.     PRINTK(KERN_DEBUG "%s : access_VIC68A()\n", DEVICE_NAME);
  677.  
  678.     if ((action->wRegisterAddress <= SRR) && ((action->wRegisterAddress & 0x03) == 3))
  679.     {
  680.         u32 dwAddress;
  681.         u8  bByte = 0;
  682.  
  683.         dwAddress = (pd->pCtl + VICBASE + action->wRegisterAddress);
  684.  
  685.         switch (action->bAccessMode)
  686.         {
  687.             case VIC68A_WRITE_ONLY:
  688.                 writeb(action->bContent, (volatile void *) dwAddress);
  689.                 break;
  690.             case VIC68A_WRITE:  
  691.                 writeb(action->bContent, (volatile void *) dwAddress);
  692.                 action->bContent = readb((const volatile void *) dwAddress);
  693.                 break;
  694.             case VIC68A_OR:    
  695.                 bByte      = readb((const volatile void *) dwAddress);
  696.                 bByte     |= action->bContent;
  697.                 writeb(bByte, (volatile void *) dwAddress);
  698.                 action->bContent = readb((const volatile void *) dwAddress);
  699.                 break;
  700.             case VIC68A_AND:    
  701.                 bByte      = readb((const volatile void *) dwAddress);
  702.                 bByte     &= action->bContent;
  703.                 writeb(bByte, (volatile void *) dwAddress);
  704.                 action->bContent = readb((const volatile void *) dwAddress);
  705.                 break;
  706.             case VIC68A_READ:  
  707.                 action->bContent = readb((const volatile void *) dwAddress);
  708.                 break;
  709.             default:            
  710.                 nStatus = -EINVAL;
  711.         }
  712.     }
  713.     else
  714.         nStatus = -EINVAL;
  715.  
  716.     return nStatus;
  717. }
  718.  
  719. // the dispatcher ----------------------------------------------------------------------------------
  720. int pcivme_ioctl(struct inode *pInode, struct file *pFile, unsigned int cmd, unsigned long arg)
  721. {
  722.     PATH_OBJ   *pp = (PATH_OBJ *)pFile->private_data;
  723.     DEVICE_OBJ *pd = pp->pDo;
  724.     int err = 1;
  725.    
  726.     PRINTK(KERN_DEBUG "%s : pcivme_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd));
  727.  
  728.     if (_IOC_TYPE(cmd) != PCIVME_MAGIC)
  729.         return -EINVAL;
  730.  
  731.     // check for accessible user buffer
  732.     if (_IOC_DIR(cmd) & _IOC_READ)
  733.         err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
  734.     if (_IOC_DIR(cmd) & _IOC_WRITE)
  735.         err = !access_ok(VERIFY_READ,  (void *)arg, _IOC_SIZE(cmd));
  736.     if (err)
  737.         return -EFAULT;
  738.  
  739.     switch (_IOC_NR(cmd))
  740.     {
  741.         case _IOC_NR(PCIVME_READ_VECTOR_BLOCK):
  742.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_VECTOR_LEVEL))
  743.                 return -EINVAL;
  744.             return read_vector_blocking(pp, pd, (PCIVME_VECTOR_LEVEL *)arg, pFile);
  745.  
  746.         case _IOC_NR(PCIVME_READ_VECTOR_POLL):
  747.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_VECTOR_LEVEL))
  748.                 return -EINVAL;
  749.             return read_vector_polling(pp, pd, (PCIVME_VECTOR_LEVEL *)arg);
  750.  
  751.         case _IOC_NR(PCIVME_CONTROL_INTERRUPTS):
  752.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_IRQ_CONTROL))
  753.                 return -EINVAL;
  754.             return control_interrupts(pp, pd, (PCIVME_IRQ_CONTROL *)arg);
  755.  
  756.         case _IOC_NR(PCIVME_TAS):
  757.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_TAS_STRUCT))
  758.                 return -EINVAL;
  759.             return VME_TAS(pp, pd, (PCIVME_TAS_STRUCT *)arg);
  760.  
  761.         case _IOC_NR(PCIVME_ACCESS_VIC68A):
  762.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_VIC68A_ACTION))
  763.                 return -EINVAL;
  764.             return access_VIC68A(pp, pd, (PCIVME_VIC68A_ACTION *)arg);
  765.  
  766.         case _IOC_NR(PCIVME_GET_DYNAMIC_STATUS):
  767.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_DYNAMIC_STATUS))
  768.                 return -EINVAL;
  769.             return get_dynamic_status(pp, pd, (PCIVME_DYNAMIC_STATUS *)arg);
  770.  
  771.         case _IOC_NR(PCIVME_RESET):
  772.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_RESET_COMMAND))
  773.                 return -EINVAL;
  774.             return VMEMM_RESET(pp, pd, (PCIVME_RESET_COMMAND *)arg);
  775.  
  776.         case _IOC_NR(PCIVME_SET_ACCESS_PARA):
  777.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_ACCESS_COMMAND))
  778.                 return -EINVAL;
  779.             return access_command(pp, pd, (PCIVME_ACCESS_COMMAND *)arg);
  780.  
  781.         case _IOC_NR(PCIVME_GET_STATIC_STATUS):
  782.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_STATIC_STATUS))
  783.                 return -EINVAL;
  784.             return get_static_status(pp, pd, (PCIVME_STATIC_STATUS *)arg);
  785.  
  786.         case _IOC_NR(PCIVME_INIT_HARDWARE):
  787.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_INIT_COMMAND))
  788.                 return -EINVAL;
  789.             return init_hardware(pp, pd, (PCIVME_INIT_COMMAND *)arg);
  790.  
  791.         case _IOC_NR(PCIVME_DEINIT_HARDWARE):
  792.             if (_IOC_SIZE(cmd) < sizeof(PCIVME_INIT_COMMAND))
  793.                  return -EINVAL;
  794.             return deinit_hardware(pp, pd, (PCIVME_INIT_COMMAND *)arg);
  795.                                                
  796.         default:
  797.             PRINTK(KERN_DEBUG "%s : pcivme_ioctl(0x%08x) is illegal\n", DEVICE_NAME, cmd);
  798.             return -EINVAL;
  799.     }
  800.  
  801.     return 0;
  802. }
  803.  
  804. /*
  805. static long  pcivme_compat_ioctl(struct file *pFile, unsigned int cmd, unsigned long arg){
  806. PRINTK(KERN_DEBUG "%s : pcivme_compat_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd));
  807. return pcivme_ioctl(NULL, pFile, cmd,arg);
  808. }
  809. */
  810.  
  811. static long  pcivme_unlocked_ioctl(struct file *pFile, unsigned int cmd, unsigned long arg){
  812. long retval=0;
  813.  
  814.  
  815. #if HAVE_UNLOCKED_IOCTL
  816.     struct mutex  fs_mutex;
  817.    mutex_init(&fs_mutex);
  818.    mutex_lock(&fs_mutex);
  819. #else
  820.    lock_kernel();
  821. #endif
  822.  
  823. PRINTK(KERN_DEBUG "%s : pcivme_unlocked_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd));
  824. retval = pcivme_ioctl(NULL, pFile, cmd,arg);
  825.  
  826. #if HAVE_UNLOCKED_IOCTL
  827.    mutex_unlock(&fs_mutex);
  828. #else
  829.    unlock_kernel();
  830. #endif
  831.  
  832. return retval;
  833. }
  834.  
  835. int pcivme_open(struct inode *pInode, struct file *pFile)
  836. {
  837.     DEVICE_OBJ *pd   = 0;
  838.     DEVICE_OBJ *desc = 0;
  839.     int nMinor = MINOR(pInode->i_rdev);
  840.     struct list_head *ptr;
  841.  
  842.     PRINTK(KERN_DEBUG "%s : pcivme_open(), %d, scanning %d devices\n", DEVICE_NAME, nMinor, drv.count);
  843.  
  844.     /* search for device */
  845.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  846.     {
  847.         pd = list_entry(ptr, DEVICE_OBJ, list);
  848.         pd->bConnected =  get_module_info(pd);
  849.         if (pd->bConnected)
  850.         {
  851.             if (test_connection(pd))
  852.             {
  853.                 printk(KERN_ERR "%s :  pcivme_open() connection test for module %d failed!\n", DEVICE_NAME, pd->cModuleNumber);
  854.                 pd->bConnected = 0;
  855.             }
  856.             else
  857.                 if (pd->cModuleNumber == nMinor)
  858.             {
  859.                 desc = pd;
  860.                 break;
  861.             }
  862.         }
  863.         else
  864.             PRINTK(KERN_DEBUG "%s  pcivme_open(): module %d not connected!\n", DEVICE_NAME, nMinor);
  865.     }
  866.  
  867.     if (desc)
  868.     {
  869.         int       err;
  870.         PATH_OBJ  *pp;
  871.  
  872.         pp = (PATH_OBJ *)kmalloc(sizeof(PATH_OBJ), GFP_ATOMIC);
  873.         if (!pp)
  874.             return -ENOMEM;
  875.  
  876.         // file PATH_OBJ structure with initialisation data            
  877.         pp->pDo            = pd;
  878.         pp->bAccessType    = pp->bIncrement = BYTE_ACCESS;  
  879.         pp->bModifier      = Short_NoPriv;  
  880.         pp->read           = readByte;
  881.         pp->write          = writeByte;
  882.         pp->AlignmentCheck = MisalignmentForByteAccess;
  883.         pFile->private_data = (void *)pp;
  884.  
  885.         PRINTK(KERN_DEBUG "%s :  pcivme_open() found VMEMM module with number %d.\n", DEVICE_NAME, nMinor);
  886.  
  887.         if (!pd->nOpenCounter)
  888.         {
  889.             err = CmdMachine(pd, init_element);
  890.             if (err)
  891.             {
  892.                 printk(KERN_ERR "%s :  pcivme_open() default init failed with err = %d!\n", DEVICE_NAME, err);
  893.                 kfree_s(pp, sizeof(*pp));    // FREE(pFile->private_data);
  894.                 return err;
  895.             }
  896.         }
  897.  
  898.         pd->nOpenCounter++;
  899.     }
  900.     else
  901.     {
  902.         printk(KERN_ERR "%s  pcivme_open(): No VMEMM module found.\n", DEVICE_NAME);
  903.         return -ENODEV;
  904.     }      
  905.  
  906.     __MOD_INC_USE_COUNT__;
  907.     return 0;
  908. }
  909.  
  910. int pcivme_release(struct inode *pInode, struct file *pFile)
  911. {
  912.     PATH_OBJ *pp;
  913.  
  914.     PRINTK(KERN_DEBUG "%s : pcivme_release()\n", DEVICE_NAME);
  915.  
  916.     if (pFile->private_data)
  917.     {
  918.         pp = (PATH_OBJ *)pFile->private_data;
  919.         if (pp && pp->pDo )
  920.         {
  921.             DEVICE_OBJ *pd = pp->pDo;
  922.  
  923.             pd->nOpenCounter--;
  924.  
  925.             // the last one closes the door
  926.             if (pd->nOpenCounter <= 0)
  927.             {
  928.                 CmdMachine(pd, deinit_element_pre);
  929.                 CmdMachine(pd, deinit_element_post);
  930.  
  931.                 // Vorsicht ist die Mutter der Porzelankiste!
  932.                 pd->nOpenCounter = 0;
  933.             }
  934.  
  935.             pp->pDo = 0;            
  936.         }
  937.  
  938.         kfree_s(pp, sizeof(*pp));    // FREE(pFile->private_data);
  939.     }
  940.  
  941.     __MOD_DEC_USE_COUNT__;
  942.     return 0;
  943. }
  944.  
  945. static ssize_t pcivme_read(struct file *pFile, char *pcBuffer, size_t count, loff_t *offp)
  946. {
  947.     PATH_OBJ *pp     = (PATH_OBJ *)pFile->private_data;
  948.     DEVICE_OBJ *pd   = pp->pDo;
  949.     u32 dwLocalCount = count;
  950.     register u32 dwLocalPageAddress;
  951.     u32 dwLocalAddressInPage;
  952.  
  953.     PRINTK(KERN_DEBUG "%s : pcivme_read(0x%08x, %d)\n", DEVICE_NAME, (u32)*offp, dwLocalCount);
  954.  
  955.     // inhibit misaligned accesses
  956.     if (pp->AlignmentCheck(*offp))
  957.         return -EFAULT;
  958.  
  959.     // check for free access to user buffer
  960.     if (!access_ok(VERIFY_WRITE, pcBuffer, count))
  961.         return -EFAULT;
  962.  
  963.     // do I still have the same modifier?
  964.     if (pp->bModifier != pd->bCurrentModifier)
  965.         setModifier(pd, pp->bModifier);
  966.  
  967.     while (count >= pp->bAccessType)
  968.     {
  969.         dwLocalPageAddress   = *offp & HI_ADDRESS_MASK;
  970.         dwLocalAddressInPage = *offp & LO_ADDRESS_MASK;
  971.  
  972.         // do I still work in the same page?
  973.         if (dwLocalPageAddress != pd->dwCurrentPageAddress)
  974.             setPageAddress(pd, dwLocalPageAddress);
  975.  
  976.         // standard access method
  977.         pp->read(pd, (void **)&pcBuffer, dwLocalAddressInPage);  
  978.  
  979.         // decrement count and update pointer to next access address
  980.         count -= pp->bAccessType;
  981.         *offp += pp->bIncrement;
  982.     }
  983.  
  984.     return dwLocalCount - count;
  985. }
  986.  
  987. static ssize_t pcivme_write(struct file *pFile, const char *pcBuffer, size_t count, loff_t *offp)
  988. {
  989.     PATH_OBJ *pp     = (PATH_OBJ *)pFile->private_data;
  990.     DEVICE_OBJ *pd   = pp->pDo;
  991.     u32 dwLocalCount = count;
  992.     register u32 dwLocalPageAddress;
  993.     u32 dwLocalAddressInPage;
  994.  
  995.     PRINTK(KERN_DEBUG "%s : pcivme_write(0x%08x, %d)\n", DEVICE_NAME, (u32)*offp, dwLocalCount);
  996.  
  997.     // inhibit misaligned accesses
  998.     if (pp->AlignmentCheck(*offp))
  999.         return -EFAULT;
  1000.  
  1001.     // check for free access to user buffer
  1002.     if (!access_ok(VERIFY_READ, pcBuffer, count))
  1003.         return -EFAULT;
  1004.  
  1005.     // do I still have the same modifier?
  1006.     if (pp->bModifier != pd->bCurrentModifier)
  1007.         setModifier(pd, pp->bModifier);
  1008.  
  1009.     while (count >= pp->bAccessType)
  1010.     {
  1011.         dwLocalPageAddress   = *offp & HI_ADDRESS_MASK;
  1012.         dwLocalAddressInPage = *offp & LO_ADDRESS_MASK;
  1013.  
  1014.         // do I still work in the same page?
  1015.         if (dwLocalPageAddress != pd->dwCurrentPageAddress)
  1016.             setPageAddress(pd, dwLocalPageAddress);
  1017.  
  1018.         // standard access method
  1019.         pp->write(pd, dwLocalAddressInPage, (void **)&pcBuffer);
  1020.  
  1021.         // decrement count and update pointer to next access address
  1022.         count -= pp->bAccessType;
  1023.         *offp += pp->bIncrement;
  1024.     }
  1025.  
  1026.     return dwLocalCount - count;
  1027. }
  1028.  
  1029.  
  1030. // http://learninglinuxkernel.in/writing-char-driver-for-linux-kernel-2-6/
  1031. // http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/
  1032. loff_t pcivme_lseek(struct file* filep, loff_t offset, int whence)
  1033. {
  1034.  
  1035. PRINTK(KERN_DEBUG "%s : pcivme_lseek(0x%08x, %d)\n", DEVICE_NAME, (u32) offset, whence);
  1036.     switch (whence) {
  1037.       case 0: /* SEEK_SET */
  1038.         filep->f_pos = offset;
  1039.         break;
  1040.       case 1: /* SEEK_CUR */
  1041.         filep->f_pos += offset;
  1042.         break;
  1043.       case 2: /* SEEK_END */
  1044.         return -EINVAL;
  1045.       default:
  1046.         return -EINVAL;
  1047.     };
  1048.  
  1049.     return filep->f_pos;
  1050. }
  1051.  
  1052.  
  1053.  
  1054. struct file_operations pcivme_fops =
  1055. {
  1056.     .llseek    =    pcivme_lseek,  /* lseek  */
  1057.     .read    =    pcivme_read,     /* read  */
  1058.     .write   =    pcivme_write,    /* write */
  1059. //    .compat_ioctl = pcivme_compat_ioctl,  /* ioctl */
  1060.     .unlocked_ioctl = pcivme_unlocked_ioctl,  /* ioctl */
  1061.     .open    =    pcivme_open,     /* open */
  1062.     .release =    pcivme_release,  /* release */
  1063. };
  1064.  
  1065.  
  1066.  
  1067.                                                                                                                              
  1068.                                                                                                                              
  1069.                                                                                                                              
  1070.