Rev 9 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
//****************************************************************************
// Copyright (C) 2000-2004 ARW Elektronik Germany
//
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// This product is not authorized for use as critical component in
// life support systems without the express written approval of
// ARW Elektronik Germany.
//
// Please announce changes and hints to ARW Elektronik
//
// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
//
//****************************************************************************
//****************************************************************************
//
// fops.c -- the file operations module for the PCIVME PCI to VME Interface
//
// $Log: fops.c,v $
// Revision 1.11 2005/03/01 10:56:12 klaus
// removed warnings with gcc 3.3.3
//
// Revision 1.10 2004/08/13 19:23:26 klaus
// conversion to kernel-version 2.6, released version 3.0
//
// Revision 1.9 2003/06/27 17:25:52 klaus
// incomplete try to get mmap() with nopage() running for automatic page switch
//
// Revision 1.8 2002/10/20 18:06:51 klaus
// changed error handling
//
// Revision 1.7 2002/10/18 21:56:28 klaus
// completed functional features, untested
//
// Revision 1.6 2002/10/18 21:56:28 klaus
// completed functional features, untested
//
// Revision 1.5 2002/10/17 19:05:03 klaus
// VME access is working through test to lib to driver
//
//****************************************************************************
/*--- INCLUDES -----------------------------------------------------------------------------------*/
#include "common.h" /* must be the first include */
#include <linux/kernel.h> /* printk() */
#include <linux/module.h> /* only here ?cause of MAJOR ... */
#include <linux/pci.h>
#include <linux/list.h>
#include <asm/errno.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/fs.h>
#if HAVE_UNLOCKED_IOCTL
#include <linux/mutex.h>
#else
#include <linux/smp_lock.h>
#endif
#include "fops.h"
#include "plx9050.h"
#include "pcivme.h" /* the common ioctl commands and structures between driver and application */
#include "main.h"
#include "askpci.h"
#include "pciif.h"
#include "vic.h"
#include "vme.h"
/*--- DEFINES ------------------------------------------------------------------------------------*/
#ifndef MINOR
#define MINOR(x) minor(x) // since 2.5.?
#endif
static PCIVME_INIT_ELEMENT init_element[] =
{{LCR, WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts
{LCR, WORD_ACCESS, PLX9050_CNTRL, RELEASE_VMEMM}, // enable interface
{VIC, BYTE_ACCESS, VIICR, 0xf8+1}, // VIICR
{VIC, BYTE_ACCESS, VICR1, 0x78+1}, // VICR1
{VIC, BYTE_ACCESS, VICR2, 0x78+2},
{VIC, BYTE_ACCESS, VICR3, 0x78+3},
{VIC, BYTE_ACCESS, VICR4, 0x78+4},
{VIC, BYTE_ACCESS, VICR5, 0x78+5},
{VIC, BYTE_ACCESS, VICR6, 0x78+6},
{VIC, BYTE_ACCESS, VICR7, 0x78+7}, // VICR7
{VIC, BYTE_ACCESS, DSICR, 0xf8+0}, // DSICR
{VIC, BYTE_ACCESS, LICR1, 0xf8+1}, // LICR1
{VIC, BYTE_ACCESS, LICR2, 0xf8+2},
{VIC, BYTE_ACCESS, LICR3, 0xf8+3},
{VIC, BYTE_ACCESS, LICR4, 0xf8+4},
{VIC, BYTE_ACCESS, LICR5, 0xf8+5},
{VIC, BYTE_ACCESS, LICR6, 0x38+6},
{VIC, BYTE_ACCESS, LICR7, 0x38+7}, // LICR7
{VIC, BYTE_ACCESS, ICGSICR, 0xf8+2}, // ICGS
{VIC, BYTE_ACCESS, ICMSICR, 0xf8+3}, // ICMS
{VIC, BYTE_ACCESS, EGICR, 0xf8+6}, // EGICR
{VIC, BYTE_ACCESS, ICGSVBR, 0x08}, // ICGS-IVBR (!)
{VIC, BYTE_ACCESS, ICMSVBR, 0x0c}, // ICMS-IVBR (!)
{VIC, BYTE_ACCESS, LIVBR, 0x00}, // LIVBR (!)
{VIC, BYTE_ACCESS, EGIVBR, 0x10}, // EGIVBR (!)
{VIC, BYTE_ACCESS, ICSR, 0x00}, // ICSR
{VIC, BYTE_ACCESS, ICR0, 0x00}, // ICR0
{VIC, BYTE_ACCESS, ICR1, 0x00},
{VIC, BYTE_ACCESS, ICR2, 0x00},
{VIC, BYTE_ACCESS, ICR3, 0x00},
{VIC, BYTE_ACCESS, ICR4, 0x00}, // ICR4
{VIC, BYTE_ACCESS, VIRSR, 0xfe}, // VIRSR
{VIC, BYTE_ACCESS, VIVR1, 0x0f}, // VIVR1
{VIC, BYTE_ACCESS, VIVR2, 0x0f},
{VIC, BYTE_ACCESS, VIVR3, 0x0f},
{VIC, BYTE_ACCESS, VIVR4, 0x0f},
{VIC, BYTE_ACCESS, VIVR5, 0x0f},
{VIC, BYTE_ACCESS, VIVR6, 0x0f},
{VIC, BYTE_ACCESS, VIVR7, 0x0f}, // VIVR7
{VIC, BYTE_ACCESS, TTR, 0x3c}, // TTR
{VIC, BYTE_ACCESS, ARCR, 0x40}, // ARCR
{VIC, BYTE_ACCESS, AMSR, 0x29}, // AMSR
{VIC, BYTE_ACCESS, RCR, 0x00}, // RCR
{IFR, LONG_ACCESS, (u16)ADRHL, 0xF0F0F0F0}, // ADR-H, ADR-L
{IFR, WORD_ACCESS, (u16)CSR , 0x0000}, // Contr-Reg
{VIC, BYTE_ACCESS, ICR7, 0x80}, // ICR7
{LCR, WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts
{STOP, WORD_ACCESS, 0, 0}};
static PCIVME_INIT_ELEMENT deinit_element_pre[] =
{{VIC, BYTE_ACCESS, ICR7, 0x00}, // ICR7 - sysfail
{LCR, WORD_ACCESS, PLX9050_INTCSR, DISABLE_PCIADA_IRQS}, // disable interrupts
{STOP, WORD_ACCESS, 0, 0}};
static PCIVME_INIT_ELEMENT deinit_element_post[] =
{{LCR, WORD_ACCESS, PLX9050_CNTRL, INHIBIT_VMEMM}, // disable interface
{STOP, WORD_ACCESS, 0, 0}};
/*--- EXTERNALS ----------------------------------------------------------------------------------*/
/*--- TYPEDEFS -----------------------------------------------------------------------------------*/
/*--- FUNCTIONS ----------------------------------------------------------------------------------*/
static inline void switch_VMEMM_on(DEVICE_OBJ *pd)
{
writew(RELEASE_VMEMM, (volatile void *) (pd->pLCR + PLX9050_CNTRL)); /* enable access */
}
static inline void switch_VMEMM_off(DEVICE_OBJ *pd)
{
writew(INHIBIT_VMEMM, (volatile void *) (pd->pLCR + PLX9050_CNTRL)); /* enable access */
}
static inline void setPageAddress(DEVICE_OBJ *pd, u32 newPageAddress)
{
PRINTK(KERN_DEBUG "%s : setPageAddress(0x%08x)\n", DEVICE_NAME, newPageAddress);
writel(newPageAddress, (volatile void *) pd->pAdrReg);
pd->dwCurrentPageAddress = newPageAddress;
}
static inline void setModifier(DEVICE_OBJ *pd, u8 newModifier)
{
PRINTK(KERN_DEBUG "%s : setModifier(0x%02x)\n", DEVICE_NAME, newModifier);
writeb(newModifier, (volatile void *) pd->pAdrMod);
pd->bCurrentModifier = newModifier;
}
/* read and write functions -----------------------------------------------------------------------*/
static inline u8 *increment8(void **pvBuffer)
{
u8 *tmp = (u8*)*pvBuffer;
*pvBuffer += sizeof(u8);
return tmp;
}
static inline u16 *increment16(void **pvBuffer)
{
u16 *tmp = (u16*)*pvBuffer;
*pvBuffer += sizeof(u16);
return tmp;
}
static inline u32 *increment32(void **pvBuffer)
{
u32 *tmp = (u32*)*pvBuffer;
*pvBuffer += sizeof(u32);
return tmp;
}
static void readByte(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage)
{
u8 tmp;
tmp = readb((const volatile void *) (pd->pVME + dwLocalAddressInPage));
__put_user(tmp, increment8(pvBuffer));
}
static void writeByte(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer)
{
u8 tmp;
__get_user(tmp, increment8(pvBuffer));
writeb(tmp, (volatile void *) (pd->pVME + dwLocalAddressInPage ));
}
static void readWord(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage)
{
u16 tmp;
tmp = readw((const volatile void *) (pd->pVME + dwLocalAddressInPage));
__put_user(tmp, increment16(pvBuffer));
}
static void writeWord(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer)
{
u16 tmp;
__get_user(tmp, increment16(pvBuffer));
writew(tmp, (volatile void *) ( pd->pVME + dwLocalAddressInPage ));
}
static void readLong(DEVICE_OBJ *pd, void **pvBuffer, u32 dwLocalAddressInPage)
{
u32 tmp;
tmp = readl((const volatile void *) (pd->pVME + dwLocalAddressInPage));
__put_user(tmp, increment32(pvBuffer));
}
static void writeLong(DEVICE_OBJ *pd, u32 dwLocalAddressInPage, void **pvBuffer)
{
u32 tmp;
__get_user(tmp, increment32(pvBuffer));
writel(tmp, (volatile void *) (pd->pVME + dwLocalAddressInPage));
}
/* test alignment functions -----------------------------------------------------------------------*/
static int MisalignmentForByteAccess(loff_t offset)
{
return 0;
}
static int MisalignmentForWordAccess(loff_t offset)
{
return(offset & 1);
}
static int MisalignmentForLongAccess(loff_t offset)
{
return(offset & 3);
}
// helper functions --------------------------------------------------------------------------------
int check_command(const PCIVME_INIT_ELEMENT *psInitElement)
{
u16 range;
u16 access_size;
// PRINTK(KERN_DEBUG "%s : check_command()\n", DEVICE_NAME);
switch (psInitElement->bDestination)
{
case LCR:
range = 0x54;
break;
case IFR:
range = 0x0c;
break;
case VIC:
range = 0xe4;
if ((psInitElement->wOffset & 3) != 3)
return -EINVAL;
break;
default:
return -EINVAL;
break;
}
// check alignment and allowed address range
switch (psInitElement->bAccessType)
{
case LONG_ACCESS:
if (psInitElement->wOffset & 3)
return -EINVAL;
access_size = sizeof(u32);
break;
case WORD_ACCESS:
if (psInitElement->wOffset & 1)
return -EINVAL;
access_size = sizeof(u16);
break;
case BYTE_ACCESS:
access_size = sizeof(u8);
break;
default :
return -EINVAL;
break;
}
if ((psInitElement->wOffset + access_size) > range)
return -EINVAL; // ignore it
return 0;
}
static int CmdMachine(DEVICE_OBJ *pd, const PCIVME_INIT_ELEMENT *psInitElement)
{
u32 adr;
int err;
//PRINTK(KERN_DEBUG "%s : CmdMachine()\n", DEVICE_NAME);
// loop through the init (or deinit) list
while (psInitElement->bDestination != STOP)
{
err = check_command(psInitElement);
if (!err)
{
switch (psInitElement->bDestination)
{
case LCR:
adr = pd->pLCR;
break;
case VIC:
adr = pd->pCtl + VICBASE;
break;
case IFR:
adr = pd->pCtl + CSR;
break;
default:
return -EINVAL;
}
switch (psInitElement->bAccessType)
{
case LONG_ACCESS:
writel(psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset));
break;
case WORD_ACCESS:
writew((u16)psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset));
break;
case BYTE_ACCESS:
writeb((u8)psInitElement->dwValue, (volatile void *) (adr + psInitElement->wOffset));
break;
default:
return -EINVAL;
}
}
else
return err;
psInitElement++;
}
return 0;
}
// all ioctls --------------------------------------------------------------------------------------
static int init_hardware(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_INIT_COMMAND *init)
{
int err;
PCIVME_INIT_ELEMENT *element = init->sVie;
PRINTK(KERN_INFO "%s : init_hardware()\n", DEVICE_NAME);
err = CmdMachine(pd, element);
if (err)
{
PRINTK(KERN_DEBUG "%s : init failed with err = %d!\n", DEVICE_NAME, err);
return err;
}
// sync storage with hardware
pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f;
pd->dwCurrentPageAddress = readl((const volatile void *) pd->pAdrReg) & HI_ADDRESS_MASK;
return 0;
}
static int deinit_hardware(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_INIT_COMMAND *deinit)
{
int err;
PCIVME_INIT_ELEMENT *element = deinit->sVie;
PRINTK(KERN_DEBUG "%s : deinit_hardware()\n", DEVICE_NAME);
err = CmdMachine(pd, deinit_element_pre);
if (err)
goto fail;
err = CmdMachine(pd, element);
if (err)
goto fail;
err = CmdMachine(pd, deinit_element_post);
if (err)
goto fail;
return 0;
fail:
return err;
}
static int access_command(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_ACCESS_COMMAND *cmd)
{
PRINTK(KERN_DEBUG "%s : access_command()\n", DEVICE_NAME);
pp->bModifier = cmd->bModifier;
pp->bAccessType = cmd->bAccessType;
pp->bIncrement = cmd->bIncrement;
switch (pp->bAccessType)
{
case BYTE_ACCESS:
pp->read = readByte;
pp->write = writeByte;
pp->AlignmentCheck = MisalignmentForByteAccess;
break;
case WORD_ACCESS:
pp->read = readWord;
pp->write = writeWord;
pp->AlignmentCheck = MisalignmentForWordAccess;
break;
case LONG_ACCESS:
pp->read = readLong;
pp->write = writeLong;
pp->AlignmentCheck = MisalignmentForLongAccess;
break;
default:
return -EINVAL;
}
return 0;
}
static int get_static_status(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_STATIC_STATUS *static_status)
{
PRINTK(KERN_DEBUG "%s : get_static_status()\n", DEVICE_NAME);
static_status->bConnected = pd->bConnected;
static_status->cModuleNumber = pd->cModuleNumber;
static_status->cFPGAVersion = pd->cFPGAVersion;
static_status->cSystemController = pd->cSystemController;
static_status->cWordMode = pd->cWordMode;
return 0;
}
static int get_dynamic_status(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_DYNAMIC_STATUS *dynamic_status)
{
u16 cntrl = readw((const volatile void *) pd->pPCIADACntrl);
u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
PRINTK(KERN_DEBUG "%s : get_dynamic_status()\n", DEVICE_NAME);
dynamic_status->bConnected = (cntrl & 0x0800) ? 1 : 0;
dynamic_status->bPCIADAIrq = (intCSR & 0x0020) ? 1 : 0;
dynamic_status->bVMEMMIrq = (intCSR & 0x0004) ? 1 : 0;
return 0;
}
static int read_vector_polling(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VECTOR_LEVEL *vector)
{
u16 cntrl = readw((const volatile void *) pd->pPCIADACntrl);
u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
PRINTK(KERN_DEBUG "%s : read_vector()\n", DEVICE_NAME);
vector->dwStatusID = 0;
vector->bLevel = 0;
vector->bPCIADAIrq = 0;
if (intCSR & 0x20) // check for PCIADA interrupt
{
vector->bPCIADAIrq = 1;
vector->dwStatusID = 1; // force for PCIADA irqs
writew(cntrl & ~0x0100, (volatile void *) pd->pPCIADACntrl); // clear pending PCIADA irq
writew(cntrl, (volatile void *) pd->pPCIADACntrl);
}
else
{
if ((cntrl & 0x0980) == 0x0980) // check if VMEMM is connected and ready
{
vector->bLevel = (u8)readw((const volatile void *) ( pd->pCtl + VICRES ));
if (vector->bLevel & 1)
{
if (vector->bLevel != 1)
vector->dwStatusID = (u32)readb((const volatile void *) (pd->pCtl + VECBASE + vector->bLevel));
vector->bLevel >>= 1;
}
}
}
return 0;
}
static int read_vector_blocking(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VECTOR_LEVEL *vector, struct file *pFile)
{
int error;
vector->dwStatusID = 0;
vector->bLevel = 0;
vector->bPCIADAIrq = 0;
// support nonblocking read if requested
if ((pFile->f_flags & O_NONBLOCK) && (!pd->wIrqStatus))
return -EAGAIN;
// sleep until data are available
if ((error = wait_event_interruptible(pd->event_queue, (pd->wIrqStatus))))
return error;
error = read_vector_polling(pp, pd, vector);
pd->wIrqStatus = 0; // clear the status since it is read
return error;
}
static int control_interrupts(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_IRQ_CONTROL *irq_control)
{
u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
u8 ret = (intCSR & 0x40) ? 1 : 0;
PRINTK(KERN_DEBUG "%s : control_interrupts()\n", DEVICE_NAME);
if (irq_control->bEnable)
writew(intCSR | 0x40, (volatile void *) pd->pPCIADAIntCSR);
else
writew(intCSR & ~0x40, (volatile void *) pd->pPCIADAIntCSR);
// return the switch before set
irq_control->bEnable = ret;
return 0;
}
static int VME_TAS(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_TAS_STRUCT *tas_cmd)
{
u32 access_adr = pd->pVME + (tas_cmd->dwAddress & LO_ADDRESS_MASK); // make low part of address
u8 data;
// save old contents
u32 old_address = readl((const volatile void *) pd->pAdrReg);
u16 old_CSR = readw((const volatile void *) pd->pCSR);
u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f;
PRINTK(KERN_DEBUG "%s : VME_TAS()\n", DEVICE_NAME);
// set new contents
writew(DISABLE_PCIADA_IRQS, (volatile void *) pd->pPCIADAIntCSR);
writeb((u8)tas_cmd->bModifier & 0x3f, (volatile void *) pd->pAdrMod);
writel(tas_cmd->dwAddress, (volatile void *) pd->pAdrReg);
writew(old_CSR | FLAG_RMC, (volatile void *) pd->pCSR);
// do the read - modify - write
data = readb((const volatile void *) access_adr);
writeb(tas_cmd->bContent, (volatile void *) access_adr);
// restore old contents
writeb(pd->bCurrentModifier, (volatile void *) pd->pAdrMod);
writew(old_CSR, (volatile void *) pd->pCSR);
writel(old_address, (volatile void *) pd->pAdrReg);
writew(intCSR, (volatile void *) pd->pPCIADAIntCSR);
// get back read data
tas_cmd->bContent = data;
return 0;
}
static int VMEMM_RESET(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_RESET_COMMAND *reset_cmd)
{
u16 cntrl = readw((const volatile void *) pd->pPCIADACntrl);
u16 intCSR = readw((const volatile void *) pd->pPCIADAIntCSR);
int status = 0;
PRINTK(KERN_INFO "%s : VMEMM_RESET()\n", DEVICE_NAME);
// am I connected and switched on??
if ((cntrl & 0x0980) == 0x0980)
{
// do command
switch (reset_cmd->bCommand)
{
case POLL_RESET_CMD:
break;
case VME_RESET_CMD:
writeb(0, (volatile void *) pd->pAdrMod);
writeb(0xf0, (volatile void *) (pd->pCtl + VICBASE + SRR)); // make VME reset
break;
case LOCAL_RESET_CMD:
writeb(0, (volatile void *) pd->pAdrMod);
writew(LOCAL_RESET, (volatile void *) (pd->pCtl + VICRES));
break;
case GLOBAL_RESET_CMD:
writeb(0, (volatile void *) pd->pAdrMod);
writew(GLOBAL_RESET, (volatile void *) (pd->pCtl + VICRES));
break;
default: status = -EINVAL;
}
// inhibit PCIADA generated irqs
writew(DISABLE_PCIADA_IRQS, (volatile void *) pd->pPCIADAIntCSR);
// always poll reset status - access will sometimes generate PCIADA #2 interrupt
reset_cmd->bResult = readb((const volatile void *) pd->pAdrMod);
// reset any pending PCIADA interrupt #2
writew(cntrl & ~0x0100, (volatile void *) pd->pPCIADACntrl);
writew(cntrl , (volatile void *) pd->pPCIADACntrl);
// restore IRQStatus
writew(intCSR , (volatile void *) pd->pPCIADAIntCSR);
}
else
status = -EBUSY;
// sync storage with hardware
pd->bCurrentModifier = readb((const volatile void *) pd->pAdrMod) & 0x3f;
return status;
}
static int access_VIC68A(PATH_OBJ *pp, DEVICE_OBJ *pd, PCIVME_VIC68A_ACTION *action)
{
int nStatus = 0;
PRINTK(KERN_DEBUG "%s : access_VIC68A()\n", DEVICE_NAME);
if ((action->wRegisterAddress <= SRR) && ((action->wRegisterAddress & 0x03) == 3))
{
u32 dwAddress;
u8 bByte = 0;
dwAddress = (pd->pCtl + VICBASE + action->wRegisterAddress);
switch (action->bAccessMode)
{
case VIC68A_WRITE_ONLY:
writeb(action->bContent, (volatile void *) dwAddress);
break;
case VIC68A_WRITE:
writeb(action->bContent, (volatile void *) dwAddress);
action->bContent = readb((const volatile void *) dwAddress);
break;
case VIC68A_OR:
bByte = readb((const volatile void *) dwAddress);
bByte |= action->bContent;
writeb(bByte, (volatile void *) dwAddress);
action->bContent = readb((const volatile void *) dwAddress);
break;
case VIC68A_AND:
bByte = readb((const volatile void *) dwAddress);
bByte &= action->bContent;
writeb(bByte, (volatile void *) dwAddress);
action->bContent = readb((const volatile void *) dwAddress);
break;
case VIC68A_READ:
action->bContent = readb((const volatile void *) dwAddress);
break;
default:
nStatus = -EINVAL;
}
}
else
nStatus = -EINVAL;
return nStatus;
}
// the dispatcher ----------------------------------------------------------------------------------
int pcivme_ioctl(struct inode *pInode, struct file *pFile, unsigned int cmd, unsigned long arg)
{
PATH_OBJ *pp = (PATH_OBJ *)pFile->private_data;
DEVICE_OBJ *pd = pp->pDo;
int err = 1;
PRINTK(KERN_DEBUG "%s : pcivme_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd));
if (_IOC_TYPE(cmd) != PCIVME_MAGIC)
return -EINVAL;
// check for accessible user buffer
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
switch (_IOC_NR(cmd))
{
case _IOC_NR(PCIVME_READ_VECTOR_BLOCK):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_VECTOR_LEVEL))
return -EINVAL;
return read_vector_blocking(pp, pd, (PCIVME_VECTOR_LEVEL *)arg, pFile);
case _IOC_NR(PCIVME_READ_VECTOR_POLL):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_VECTOR_LEVEL))
return -EINVAL;
return read_vector_polling(pp, pd, (PCIVME_VECTOR_LEVEL *)arg);
case _IOC_NR(PCIVME_CONTROL_INTERRUPTS):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_IRQ_CONTROL))
return -EINVAL;
return control_interrupts(pp, pd, (PCIVME_IRQ_CONTROL *)arg);
case _IOC_NR(PCIVME_TAS):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_TAS_STRUCT))
return -EINVAL;
return VME_TAS(pp, pd, (PCIVME_TAS_STRUCT *)arg);
case _IOC_NR(PCIVME_ACCESS_VIC68A):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_VIC68A_ACTION))
return -EINVAL;
return access_VIC68A(pp, pd, (PCIVME_VIC68A_ACTION *)arg);
case _IOC_NR(PCIVME_GET_DYNAMIC_STATUS):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_DYNAMIC_STATUS))
return -EINVAL;
return get_dynamic_status(pp, pd, (PCIVME_DYNAMIC_STATUS *)arg);
case _IOC_NR(PCIVME_RESET):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_RESET_COMMAND))
return -EINVAL;
return VMEMM_RESET(pp, pd, (PCIVME_RESET_COMMAND *)arg);
case _IOC_NR(PCIVME_SET_ACCESS_PARA):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_ACCESS_COMMAND))
return -EINVAL;
return access_command(pp, pd, (PCIVME_ACCESS_COMMAND *)arg);
case _IOC_NR(PCIVME_GET_STATIC_STATUS):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_STATIC_STATUS))
return -EINVAL;
return get_static_status(pp, pd, (PCIVME_STATIC_STATUS *)arg);
case _IOC_NR(PCIVME_INIT_HARDWARE):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_INIT_COMMAND))
return -EINVAL;
return init_hardware(pp, pd, (PCIVME_INIT_COMMAND *)arg);
case _IOC_NR(PCIVME_DEINIT_HARDWARE):
if (_IOC_SIZE(cmd) < sizeof(PCIVME_INIT_COMMAND))
return -EINVAL;
return deinit_hardware(pp, pd, (PCIVME_INIT_COMMAND *)arg);
default:
PRINTK(KERN_DEBUG "%s : pcivme_ioctl(0x%08x) is illegal\n", DEVICE_NAME, cmd);
return -EINVAL;
}
return 0;
}
/*
static long pcivme_compat_ioctl(struct file *pFile, unsigned int cmd, unsigned long arg){
PRINTK(KERN_DEBUG "%s : pcivme_compat_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd));
return pcivme_ioctl(NULL, pFile, cmd,arg);
}
*/
static long pcivme_unlocked_ioctl(struct file *pFile, unsigned int cmd, unsigned long arg){
long retval=0;
#if HAVE_UNLOCKED_IOCTL
struct mutex fs_mutex;
mutex_init(&fs_mutex);
mutex_lock(&fs_mutex);
#else
lock_kernel();
#endif
PRINTK(KERN_DEBUG "%s : pcivme_unlocked_ioctl(0x%08x), size = %d\n", DEVICE_NAME, cmd, _IOC_SIZE(cmd));
retval = pcivme_ioctl(NULL, pFile, cmd,arg);
#if HAVE_UNLOCKED_IOCTL
mutex_unlock(&fs_mutex);
#else
unlock_kernel();
#endif
return retval;
}
int pcivme_open(struct inode *pInode, struct file *pFile)
{
DEVICE_OBJ *pd = 0;
DEVICE_OBJ *desc = 0;
int nMinor = MINOR(pInode->i_rdev);
struct list_head *ptr;
PRINTK(KERN_DEBUG "%s : pcivme_open(), %d, scanning %d devices\n", DEVICE_NAME, nMinor, drv.count);
/* search for device */
for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
{
pd = list_entry(ptr, DEVICE_OBJ, list);
pd->bConnected = get_module_info(pd);
if (pd->bConnected)
{
if (test_connection(pd))
{
printk(KERN_ERR "%s : pcivme_open() connection test for module %d failed!\n", DEVICE_NAME, pd->cModuleNumber);
pd->bConnected = 0;
}
else
if (pd->cModuleNumber == nMinor)
{
desc = pd;
break;
}
}
else
PRINTK(KERN_DEBUG "%s pcivme_open(): module %d not connected!\n", DEVICE_NAME, nMinor);
}
if (desc)
{
int err;
PATH_OBJ *pp;
pp = (PATH_OBJ *)kmalloc(sizeof(PATH_OBJ), GFP_ATOMIC);
if (!pp)
return -ENOMEM;
// file PATH_OBJ structure with initialisation data
pp->pDo = pd;
pp->bAccessType = pp->bIncrement = BYTE_ACCESS;
pp->bModifier = Short_NoPriv;
pp->read = readByte;
pp->write = writeByte;
pp->AlignmentCheck = MisalignmentForByteAccess;
pFile->private_data = (void *)pp;
PRINTK(KERN_DEBUG "%s : pcivme_open() found VMEMM module with number %d.\n", DEVICE_NAME, nMinor);
if (!pd->nOpenCounter)
{
err = CmdMachine(pd, init_element);
if (err)
{
printk(KERN_ERR "%s : pcivme_open() default init failed with err = %d!\n", DEVICE_NAME, err);
kfree_s(pp, sizeof(*pp)); // FREE(pFile->private_data);
return err;
}
}
pd->nOpenCounter++;
}
else
{
printk(KERN_ERR "%s pcivme_open(): No VMEMM module found.\n", DEVICE_NAME);
return -ENODEV;
}
__MOD_INC_USE_COUNT__;
return 0;
}
int pcivme_release(struct inode *pInode, struct file *pFile)
{
PATH_OBJ *pp;
PRINTK(KERN_DEBUG "%s : pcivme_release()\n", DEVICE_NAME);
if (pFile->private_data)
{
pp = (PATH_OBJ *)pFile->private_data;
if (pp && pp->pDo )
{
DEVICE_OBJ *pd = pp->pDo;
pd->nOpenCounter--;
// the last one closes the door
if (pd->nOpenCounter <= 0)
{
CmdMachine(pd, deinit_element_pre);
CmdMachine(pd, deinit_element_post);
// Vorsicht ist die Mutter der Porzelankiste!
pd->nOpenCounter = 0;
}
pp->pDo = 0;
}
kfree_s(pp, sizeof(*pp)); // FREE(pFile->private_data);
}
__MOD_DEC_USE_COUNT__;
return 0;
}
static ssize_t pcivme_read(struct file *pFile, char *pcBuffer, size_t count, loff_t *offp)
{
PATH_OBJ *pp = (PATH_OBJ *)pFile->private_data;
DEVICE_OBJ *pd = pp->pDo;
u32 dwLocalCount = count;
register u32 dwLocalPageAddress;
u32 dwLocalAddressInPage;
PRINTK(KERN_DEBUG "%s : pcivme_read(0x%08x, %d)\n", DEVICE_NAME, (u32)*offp, dwLocalCount);
// inhibit misaligned accesses
if (pp->AlignmentCheck(*offp))
return -EFAULT;
// check for free access to user buffer
if (!access_ok(VERIFY_WRITE, pcBuffer, count))
return -EFAULT;
// do I still have the same modifier?
if (pp->bModifier != pd->bCurrentModifier)
setModifier(pd, pp->bModifier);
while (count >= pp->bAccessType)
{
dwLocalPageAddress = *offp & HI_ADDRESS_MASK;
dwLocalAddressInPage = *offp & LO_ADDRESS_MASK;
// do I still work in the same page?
if (dwLocalPageAddress != pd->dwCurrentPageAddress)
setPageAddress(pd, dwLocalPageAddress);
// standard access method
pp->read(pd, (void **)&pcBuffer, dwLocalAddressInPage);
// decrement count and update pointer to next access address
count -= pp->bAccessType;
*offp += pp->bIncrement;
}
return dwLocalCount - count;
}
static ssize_t pcivme_write(struct file *pFile, const char *pcBuffer, size_t count, loff_t *offp)
{
PATH_OBJ *pp = (PATH_OBJ *)pFile->private_data;
DEVICE_OBJ *pd = pp->pDo;
u32 dwLocalCount = count;
register u32 dwLocalPageAddress;
u32 dwLocalAddressInPage;
PRINTK(KERN_DEBUG "%s : pcivme_write(0x%08x, %d)\n", DEVICE_NAME, (u32)*offp, dwLocalCount);
// inhibit misaligned accesses
if (pp->AlignmentCheck(*offp))
return -EFAULT;
// check for free access to user buffer
if (!access_ok(VERIFY_READ, pcBuffer, count))
return -EFAULT;
// do I still have the same modifier?
if (pp->bModifier != pd->bCurrentModifier)
setModifier(pd, pp->bModifier);
while (count >= pp->bAccessType)
{
dwLocalPageAddress = *offp & HI_ADDRESS_MASK;
dwLocalAddressInPage = *offp & LO_ADDRESS_MASK;
// do I still work in the same page?
if (dwLocalPageAddress != pd->dwCurrentPageAddress)
setPageAddress(pd, dwLocalPageAddress);
// standard access method
pp->write(pd, dwLocalAddressInPage, (void **)&pcBuffer);
// decrement count and update pointer to next access address
count -= pp->bAccessType;
*offp += pp->bIncrement;
}
return dwLocalCount - count;
}
// http://learninglinuxkernel.in/writing-char-driver-for-linux-kernel-2-6/
// http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/
loff_t pcivme_lseek(struct file* filep, loff_t offset, int whence)
{
PRINTK(KERN_DEBUG "%s : pcivme_lseek(0x%08x, %d)\n", DEVICE_NAME, (u32) offset, whence);
switch (whence) {
case 0: /* SEEK_SET */
filep->f_pos = offset;
break;
case 1: /* SEEK_CUR */
filep->f_pos += offset;
break;
case 2: /* SEEK_END */
return -EINVAL;
default:
return -EINVAL;
};
return filep->f_pos;
}
struct file_operations pcivme_fops =
{
.llseek = pcivme_lseek, /* lseek */
.read = pcivme_read, /* read */
.write = pcivme_write, /* write */
// .compat_ioctl = pcivme_compat_ioctl, /* ioctl */
.unlocked_ioctl = pcivme_unlocked_ioctl, /* ioctl */
.open = pcivme_open, /* open */
.release = pcivme_release, /* release */
};