/* SYSTEM: Acromag PCI carrier MODULE NAME: apc8620.c VERSION: A CREATION DATE: 14/05/04 CODED BY: AFN ABSTRACT: 8620 carrier device. CALLING SEQUENCE: MODULE TYPE: I/O RESOURCES: SYSTEM RESOURCES: MODULES CALLED: REVISIONS: DATE BY PURPOSE -------- ---- ------------------------------------------------ */ /* APC8620 device */ #include /* for read{b,w,l} and write{b,w,l} */ #include #include /* for copy_to/from_user */ #include #include #include /* for error numbers */ #include #include /* for __init and __exit */ #include /* for request_irq and free_irq */ #include #include #include #include /* for kmalloc and kfree */ #include #include /* KERNEL_VERSION(a,b,c) */ #include "apc8620.h" #include "isr330.h" #include "isr340.h" #include "isr480.h" #include "isr1k110Camera.h" #define DEVICE_NAME "apc8620" #define MAJOR_NUM 46 MODULE_LICENSE("GPL and additional rights"); /* funtion prototypes */ static ssize_t aio_read (struct kiocb *iocb, const struct iovec *vec, unsigned long count, loff_t offset); static ssize_t aio_write (struct kiocb *iocb, const struct iovec *vec, unsigned long count, loff_t offset); static int ioctl (struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg); static int open (struct inode *inode, struct file *fp); static ssize_t read (struct file *fp, char *buf, size_t length, loff_t *offset); static int release (struct inode *inode, struct file *fp); static ssize_t write (struct file *fp, const char *buf, size_t length, loff_t *offset); /* internal types */ struct apc_stateInformation { atomic_t interruptCount[NUMBER_OF_SLOTS]; int board_irq; int carrier; int isModule; int ret_val; int slot; unsigned long moduleOffset_IO; unsigned long moduleOffset_ID; unsigned long physicalAddress; void(*isr[NUMBER_OF_SLOTS])(void*); /* * Changes in fs.h struct file caused this pointer to be moved here. * Namely, file_operations (f_op*) was made const. */ ssize_t(*readv)(struct file *fp, const struct iovec *vec, unsigned long count, loff_t *offset); ssize_t(*writev)(struct file *fp, const struct iovec *vec, unsigned long count, loff_t *offset); }; /* declaration of available isr table */ /* these functions MUST be FULLY reentrant */ static void(*isrtable[LAST_MODULE_T_ENUM])(void*); static ssize_t(*readvTable[LAST_MODULE_T_ENUM])(struct file *fp, const struct iovec *vec, unsigned long count, loff_t *offset); static ssize_t(*writevTable[LAST_MODULE_T_ENUM])(struct file *fp, const struct iovec *vec, unsigned long count, loff_t *offset); /* declaration of state information */ static spinlock_t stateInformationSync = SPIN_LOCK_UNLOCKED; static struct apc_stateInformation apcsi[MAX_CARRIERS]; static struct file_operations apc8620_ops = { owner: THIS_MODULE, aio_read: aio_read, aio_write: aio_write, read: read, write: write, ioctl: ioctl, open: open, release: release, }; /* elaboration of function prototypes */ #define GET_SI(fp,si) if (!extractStateInformation (fp, &si)) return -EBADF; static int extractStateInformation (struct file *fp, struct apc_stateInformation *to) { int result = 0 == 1; struct apc_stateInformation *from; spin_lock (&stateInformationSync); { if (fp->private_data != NULL) { from = (struct apc_stateInformation*)fp->private_data; memcpy (to, from, sizeof(struct apc_stateInformation)); result = 0 == 0; } else result = 0 == 1; } spin_unlock (&stateInformationSync); return result; } static int open (struct inode *inode, struct file *fp) { const int minor = MINOR(inode->i_rdev); const int carrier = minor & 0x0f; const int slot = (minor & 0xf0) >> 4; struct apc_stateInformation *module = NULL; /* Check to make sure the request is valid */ if (carrier > (MAX_CARRIERS - 1)) return -ENODEV; if (slot > NUMBER_OF_SLOTS) return -ENODEV; if (apcsi[carrier].physicalAddress == 0) return -ENODEV; if (slot > 0) { module = kmalloc (sizeof(struct apc_stateInformation), GFP_KERNEL); module->board_irq = apcsi[carrier].board_irq; module->carrier = carrier; module->isModule = 0 == 0; module->ret_val = apcsi[carrier].ret_val; module->slot = slot - 1; module->moduleOffset_ID = SLOT_A_ID_OFFSET + (module->slot * 0x40); module->moduleOffset_IO = SLOT_A_IO_OFFSET + (module->slot * 0x80); module->physicalAddress = apcsi[carrier].physicalAddress; module->readv = NULL; module->writev = NULL; fp->private_data = module; } else fp->private_data = &apcsi[carrier]; return 0; } static int release (struct inode *inode, struct file *fp) { int result = 0; struct apc_stateInformation *si; spin_lock (&stateInformationSync); { if (fp->private_data == NULL) result = -EBADF; else { si = (struct apc_stateInformation*)fp->private_data; fp->private_data = NULL; if (si->isModule) kfree (si); } } spin_unlock (&stateInformationSync); return result; } static ssize_t read (struct file *fp, char *buf, size_t length, loff_t *offset) { const int size = length & 0x3; const size_t len = length >> 2; size_t i; struct apc_stateInformation si; unsigned char theData[(size==0?1:(size==3?4:size))*len]; unsigned long adata; GET_SI(fp, si); /* make sure file descriptor is valid. */ adata = si.physicalAddress + (size == 0 ? si.moduleOffset_ID : si.moduleOffset_IO) + *offset; /* printk(KERN_NOTICE "apc8620: reading from offset %lld\n", *offset); printk(KERN_NOTICE "apc8620: module space is 0x%lx\n", (size == 0 ? si.moduleOffset_ID : si.moduleOffset_IO)); printk(KERN_NOTICE "apc8620: reading %d items at %d bytes each from device...\n", len, size); */ switch (size) { case 0: /* 8 bit read but index output array as 16 bits */ for (i = 0 ; i < len ; i++, adata+=2) theData[i] = readb((char *)adata); break; case 1: /* 8 bit */ for (i = 0 ; i < len ; i++, adata++) theData[i] = readb((char *)adata); break; case 2: /* 16 bit */ { unsigned short *data = (unsigned short*)theData; for (i = 0 ; i < len ; i++, adata+=2) data[i] = readw((short *)adata); } break; case 3: /* 32 bit */ { unsigned long *data = (unsigned long*)theData; for (i = 0 ; i < len ; i++, adata+=4) data[i] = readl((long *)adata); } break; default: return (-EINVAL); break; } copy_to_user (buf, theData, sizeof(theData)); /*printk(KERN_NOTICE "apc8620: successfully read device...\n");*/ return (len); } static ssize_t write (struct file *fp, const char *buf, size_t length, loff_t *offset) { const int size = length & 0x3; const size_t len = length >> 2; size_t i; struct apc_stateInformation si; unsigned char theData[(size==0?1:(size==3?4:size))*len]; unsigned long adata; GET_SI(fp, si); /* make sure file descriptor is valid. */ adata = si.physicalAddress + si.moduleOffset_IO + *offset; copy_from_user (theData, buf, sizeof(theData)); /* printk(KERN_NOTICE "apc8620: writing %d items at %d bytes each from device...\n", len, size); printk(KERN_NOTICE "apc8620: physical address 0x%lx\n", si.physicalAddress); printk(KERN_NOTICE "apc8620: module offset 0x%lx\n", si.moduleOffset_IO); printk(KERN_NOTICE "apc8620: requested offset 0x%llx\n", *offset); printk(KERN_NOTICE "apc8620: computed result 0x%lx\n", adata); */ switch (size) { case 0: /* 8 bit write but index input array as 16 bits */ for (i = 0 ; i < len ; i++, adata+=2) writeb(theData[i], (char *)adata); break; case 1: /* 8 bit */ for (i = 0 ; i < len ; i++, adata++) writeb(theData[i], (char *)adata); break; case 2: /* 16 bit */ { unsigned short *data = (unsigned short*)theData; for (i = 0 ; i < len ; i++, adata+=2) writew(data[i], (short *)adata); } break; case 3: /* 32 bit */ { unsigned long *data = (unsigned long*)theData; for (i = 0 ; i < len ; i++, adata+=4) writel(data[i], (long *)adata); } break; default: return (-EINVAL); break; } /*printk(KERN_NOTICE "apc8620: successfully write device...\n");*/ return (len); } static int ioctl (struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg) { int i; module_t ip; struct apc_stateInformation si; unsigned int interruptCount[NUMBER_OF_SLOTS]; GET_SI(fp, si); /* make sure file descriptor is valid. */ /*printk(KERN_NOTICE "apc8620: ioctl device with %u...\n", cmd);*/ switch (cmd) { case 0: case 1: case 2: case 3: case 4: /* register or degister ISRs for a slot */ if (arg < LAST_MODULE_T_ENUM && si.isModule) { int result = 0; unsigned short int_pending, status; ip = (module_t)arg; status = readw ((void*)(apcsi[si.carrier].physicalAddress)); int_pending = readw ((void*)(apcsi[si.carrier].physicalAddress + 2)); printk (KERN_NOTICE "apc8620: hardware status (0x%hx) with pending interrupts (0x%hx).\n", status, int_pending); /* printk (KERN_NOTICE "apc8620: Registering an interrupt for slot %d and IP module %d\n", si.slot, ip); */ spin_lock (&stateInformationSync); { apcsi[si.carrier].isr[si.slot] = isrtable[ip]; if (fp->private_data == NULL) result = -EBADF; else { struct apc_stateInformation* real = (struct apc_stateInformation*)fp->private_data; real->readv = readvTable[ip]; real->writev = writevTable[ip]; } } spin_unlock (&stateInformationSync); if (result < 0) { printk (KERN_NOTICE "apc8620: Failure to insert ISR 0x%x.\n", -result); apcsi[si.carrier].isr[si.slot] = NULL; return result; } } else return -EINVAL; break; case 7: /* get the interrupt count for some slot */ if (!si.isModule) { for (i = 0 ; i < NUMBER_OF_SLOTS ; i++) { interruptCount[i] = atomic_read (&si.interruptCount[i]); /*printk(KERN_NOTICE "apc8620: interrupt count for %c is %u.\n", (char)(i+0x41), interruptCount[i]);*/ } copy_to_user ((void*)arg, (void*)interruptCount, sizeof(interruptCount)); } else return -EINVAL; break; default: return -EINVAL; } /*printk(KERN_NOTICE "apc8620: successfully ioctl device...\n");*/ return (cmd); } static ssize_t aio_read (struct kiocb *iocb, const struct iovec *vec, unsigned long count, loff_t offset) { ssize_t result = -EBADF; struct apc_stateInformation si; GET_SI(iocb->ki_filp, si); /* make sure file descriptor is valid. */ if (si.readv != NULL) result = si.readv (iocb->ki_filp, vec, count, &offset); return result; } static ssize_t aio_write (struct kiocb *iocb, const struct iovec *vec, unsigned long count, loff_t offset) { ssize_t result = -EBADF; struct apc_stateInformation si; GET_SI(iocb->ki_filp, si); /* make sure file descriptor is valid. */ if (si.writev != NULL) result = si.writev (iocb->ki_filp, vec, count, &offset); return result; } static irqreturn_t apc8620_handler (int irq, void *did) { struct apc_stateInformation *si = (struct apc_stateInformation*)did; int slot, ip_int0_pending = 0x01, ip_int1_pending = 0x2; int ip_io_offset = SLOT_A_IO_OFFSET; unsigned short dummyWord, nValue, *slotAddr; PCI_BOARD_MEMORY_MAP* pPCICard; printk (KERN_NOTICE "apc8620: did (0x%lx)\n", (unsigned long)did); pPCICard = (PCI_BOARD_MEMORY_MAP*)si->physicalAddress; nValue = readw ((unsigned short*)&pPCICard->intPending); slotAddr = &pPCICard->slotAInt0; for (slot = 0 ; slot < NUMBER_OF_SLOTS ; slot++) { if (nValue & ip_int0_pending || nValue & ip_int1_pending) { printk (KERN_NOTICE "apc8620: interrupt on slot %d.\n", slot); atomic_inc (&si->interruptCount[slot]); if (si->isr[slot] != NULL) si->isr[slot] ((void*)si->physicalAddress + ip_io_offset); else printk (KERN_NOTICE "apc8620: interrupt handler for slot %d is missing.\n", slot); /* read IP A Interrupt Select Space */ dummyWord = readw (slotAddr); dummyWord = readw (slotAddr+1); } ip_int0_pending *= 4; ip_int1_pending *= 4; slotAddr += 2; ip_io_offset += 0x0080; } return IRQ_HANDLED; } static int __init init_apc8620_module (void) { module_t mt; int i, j, done = 0 == 1, status; struct pci_dev *p8620 = NULL; /* build the interrupt table */ for (mt = NONE ; mt <= LAST_MODULE_T_ENUM ; mt++) { isrtable[mt] = NULL; readvTable[mt] = NULL; writevTable[mt] = NULL; } // override the IP 330 calls isrtable[IP330] = isr330; readvTable[IP330] = readv330; writevTable[IP330] = writev330; ip330isr_init(); // override the IP 340 calls isrtable[IP340] = isr340; readvTable[IP340] = readv340; writevTable[IP340] = writev340; ip340isr_init(); // override the IP 340 calls isrtable[IP480] = isr480; readvTable[IP480] = readv480; writevTable[IP480] = writev480; ip480isr_init(); // override the IP 1K110 isrtable[IP1K110_Camera] = isr1K110Camera; readvTable[IP1K110_Camera] = readv1K110Camera; writevTable[IP1K110_Camera] = writev1K110Camera; ip1K110Cameraisr_init(); /* initialize the private or instance data */ for (i = 0 ; i < MAX_CARRIERS ; i++) { for (j = 0 ; j < NUMBER_OF_SLOTS ; j++) { atomic_set (&apcsi[i].interruptCount[j], 0); apcsi[i].isr[j] = NULL; } apcsi[i].board_irq = 0; apcsi[i].carrier = i; apcsi[i].isModule = 0 == 1; apcsi[i].ret_val = 0; apcsi[i].slot = 0; apcsi[i].moduleOffset_IO = 0; apcsi[i].moduleOffset_ID = 0; apcsi[i].physicalAddress = 0; apcsi[i].readv = NULL; apcsi[i].writev = NULL; if (!done) p8620 = pci_get_device (APC8620_VENDOR_ID, APC8620_DEVICE_ID, p8620); if (p8620) { apcsi[i].physicalAddress = (unsigned long)p8620->resource[2].start; printk(KERN_NOTICE "apc8620: VID = 0x10B5\napc8620: DID = 0x1024\napc8620: physical addr = %lx\n", apcsi[i].physicalAddress); apcsi[i].physicalAddress = (unsigned long)ioremap_nocache (apcsi[i].physicalAddress, 4096); if (apcsi[i].physicalAddress == 0) return (-ENODEV); else printk(KERN_NOTICE "apc8620: mapped addr = %lx\n", apcsi[i].physicalAddress); writew (0x0100, (void*)apcsi[i].physicalAddress); /* reset the hardware */ apcsi[i].ret_val = register_chrdev (MAJOR_NUM, DEVICE_NAME, &apc8620_ops); if (apcsi[i].ret_val < 0) printk(KERN_ERR "apc8620: Failed to register error = %d\n", apcsi[i].ret_val); else { printk(KERN_NOTICE "apc8620: registered carrier %d\n", i); apcsi[i].board_irq = p8620->irq; status = request_irq (apcsi[i].board_irq, apc8620_handler, IRQF_DISABLED, DEVICE_NAME, (void*)&apcsi[i]); printk(KERN_NOTICE "apc8620: interrupt data (0x%lx) on irq (%d) and " "status (0x%x)\n", (unsigned long)&apcsi[i], apcsi[i].board_irq, -status); writew (0x0004, (void*)apcsi[i].physicalAddress);/* enable interrupts */ } } else done = 1 == 1; } return (0); } static void __exit cleanup_apc8620_module(void) { int i; for (i = 0 ; i < MAX_CARRIERS ; i++) { if (apcsi[i].ret_val >= 0 ) { unregister_chrdev (MAJOR_NUM, DEVICE_NAME); free_irq (apcsi[i].board_irq, (void *)&apcsi[i]); iounmap ((void *)apcsi[i].physicalAddress); } } } module_init (init_apc8620_module); module_exit (cleanup_apc8620_module);