//Galil PCI driver for DMC-18xx. Requires kernel >= 2.6.19. printk output shows up in /var/log/messages or dmesg /* #include <linux/fs.h> //struct file, struct inode #include <linux/pci.h> #include <linux/miscdevice.h> #include <asm/uaccess.h> //copy_to_user(), copy_from_user() #include <linux/interrupt.h> //request_irq() #include <linux/sched.h> // TASK_INTERRUPTIBLE moved to different header #include <linux/mutex.h> // replacing lock_kernel and unlock_kernel #include <linux/module.h> */ #ifdef __OS_UBUNTU #include <linux/init.h> #include <linux/module.h> #endif #include <linux/kernel.h> #include <linux/sched.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/file.h> #include <linux/syscalls.h> #include <linux/fcntl.h> #include <linux/fs.h> //struct file, struct inode #include <linux/pci.h> #include <linux/ioport.h> #include <linux/miscdevice.h> #include <asm/uaccess.h> //copy_to_user(), copy_from_user() #include <linux/interrupt.h> //request_irq() #include <linux/version.h> #include <linux/unistd.h> MODULE_LICENSE("Dual BSD/GPL"); #ifdef __DRIVER_VER static char * gVersion = __DRIVER_VER; // internal version displayed in log #else static char * gVersion = "0.0.9-0"; // internal version displayed in log #endif #define GALILNAME "galilpci"//name shows up in /dev and /sys/bus/pci/drivers #define BUFFERSIZE 512 //size of command and response buffer #define MAX_DEVICES 16 //max number of Galil PCI boards in a single PC #define SIZE 255 //max records/status bytes + 1 #define DMC1417 0x1417 #define DMC1640 0x1640 #define DMC18x0 0x1800 #define DMC18x2 0x1842 #define DMC18x6 0x1806 #define REV1 1 // PLX based board #define REV2 2 // LILINX based board // Kevin 20140911 // These were added because these def was removed from Ubuntu kernel header starting around 12.04.03 // #ifndef __devinit #define __devinit #endif #ifndef __devexit #define __devexit #endif #ifndef __devinitdata #define __devinitdata #endif #ifndef __devinit_p #define __devinit_p #endif #ifndef __devexit_p #define __devexit_p #endif typedef struct CPciInfo { __u16 m_fSubVendor; // 2 byte unsigned number __u16 m_fSubSystem; // 2 byte unsigned number __u16 m_fRevsion; // 2 byte unsigned number } CPciInfo; typedef struct //queue to hold status bytes (from isr to ioctl) { unsigned char q[SIZE]; //queue (circular buffer) unsigned char in; // in pointer into q unsigned char out; //out pointer into q } ByteQ; //out == in means q is empty. out == (in + 1) % SIZE means q is full typedef struct //queue to hold records (up to 512 bytes) from isr to ioctl { unsigned char q[SIZE][512]; //queue (circular buffer). 512 is DPRAM size (maximum data record size). Currently 1886 uses 366 bytes of this unsigned char in; // in pointer into q unsigned char out; //out pointer into q } RecordQ; //out == in means q is empty. out == (in + 1) % SIZE means q is full typedef struct //contains data private to each Galil PCI device installed in the PC { struct miscdevice galilpci_miscdev; //contains the minor number. misc devices have a major number of 10. See http://www.linuxjournal.com/node/2920/print CPciInfo pciInfo; unsigned long baseAddress; //base address N from BAR2. This is 16 bytes of I/O ports. unsigned long irqAddress; //BAR1: 18x2 only. This is 0 on 18x0/6. All switching between 18x2 and 18x0/6 is done based on this variable char buffer[BUFFERSIZE]; //kernel buffer for holding controller commands and responses int bOpen; //exclusive file open flag // long recordAddress; //BAR0: controller writes the data record here (in dual port RAM) void __iomem *recordAddress; //BAR0: controller writes the data record here (in dual port RAM) RecordQ recordQ; //record q from isr to ioctl (circular buffer) wait_queue_head_t recordWaitQ; //this is a list of processes waiting for a record interrupt from the controller unsigned int interrupt; //irq number (e.g. 11). This is stored only to call free_irq upon rmmod ByteQ interruptQ; //status byte q from isr to ioctl (circular buffer). Contains Cx, Dx, Ex, Fx wait_queue_head_t interruptWaitQ; //this is a list of processes waiting for an UI or EI interrupt from the controller wait_queue_head_t sleepQ; } DeviceContext; static DeviceContext d[MAX_DEVICES]; //array of galil device (board) structure that hold the IO address static int device = -1; //total galil boards found minus 1 (matching PLX vendor and device IDs) static ssize_t GetReadSize ( DeviceContext *d ) { size_t result = 0; switch(d->pciInfo.m_fSubSystem) { case DMC18x2: if ( d->pciInfo.m_fRevsion == REV2 ) result = inw(d->baseAddress + 6); else { // printk(KERN_INFO "Read size flag[%X]\n", inb(d->baseAddress+4)); result = ((inb(d->baseAddress+1) & 0x20) == 0) ? 1 : 0; } break; case DMC1640: case DMC18x0: case DMC18x6: if ( d->pciInfo.m_fRevsion == REV2 ) result = inw(d->baseAddress + 6); else { // printk(KERN_INFO "Read size flag[%X]\n", inb(d->baseAddress+4)); result = ((inb(d->baseAddress+4) & 0x04) == 0) ? 1 : 0; } break; case DMC1417: result = ((inb(d->baseAddress+1) & 0x01) == 0) ? 1 : 0; break; default: ; }; return result; } static ssize_t GetWriteSize ( DeviceContext *d ) { size_t result = 0; u8 flag; switch(d->pciInfo.m_fSubSystem) { case DMC18x2: if ( d->pciInfo.m_fRevsion == REV2 ) result = (size_t) 511 - inw(d->baseAddress + 10); else result = ((inb(d->baseAddress+1) & 0x40) == 0) ? 1 : 0; break; case DMC1640: case DMC18x0: case DMC18x6: if ( d->pciInfo.m_fRevsion == REV2 ) result = (size_t) 511 - inw(d->baseAddress + 10); else result = ((inb(d->baseAddress+4) & 0x01) == 0) ? 1 : 0; break; case DMC1417: result = ((inb(d->baseAddress+1) & 0x02) == 0) ? 1 : 0; break; default: ; }; return result; } //___________________________________________________________________________________________ //called when the user process calls read(). Never fails. Returns 0 if no data to read. Otherwise, returns number of bytes read. ssize_t EvtIoRead(struct file *filep, char *buf, size_t count, loff_t *ppos) { unsigned char tempBuf[BUFFERSIZE]; // unsigned char tempBuf[3]; bool noData = false; size_t i, bytesToRead; size_t bufIdx = 0; size_t curTempIdx = 0; size_t bytesRead = 0; //number of bytes actually read from the controller (may not reach count if there's only a few bytes to read) DeviceContext *d = filep->private_data; size_t bytesAvailable; #if 0 unsigned char dumpChar; char dumpBuf[512]; printk(KERN_INFO "EvtIoRead TotalBuffer[%i]\n", count); #endif bufIdx = 0; while ( bytesRead < count && !noData ) { curTempIdx = 0; bytesToRead = min(sizeof(tempBuf), count-bytesRead); while ( curTempIdx < sizeof(tempBuf) && curTempIdx < count ) { bytesAvailable = GetReadSize(d); if ( bytesAvailable <= 0 ) { noData = true; break; } bytesToRead = min(bytesToRead, bytesAvailable); tempBuf[curTempIdx++] = inb(d->baseAddress); } copy_to_user(buf+bytesRead, tempBuf, curTempIdx); bytesRead += curTempIdx; // printk(KERN_INFO "EvtIoRead Copy to user Total[%i] Cycle[%i]\n", bytesRead, curTempIdx); } #if 0 if ( bytesRead > 0 ) { memset(dumpBuf, 0, sizeof(dumpBuf)); for ( i = 0; i < 16 && i < bytesRead; i++ ) { dumpChar = *(buf+i); // sprintf(dumpBuf+strlen(dumpBuf), "%X ", dumpChar); dumpBuf[i] = *(buf+i); } printk(KERN_INFO "EvtIoRead Dump: size(%i) data[%s]\n", bytesRead, dumpBuf); printk(KERN_INFO "EvtIoRead Total[%i] Read[%i]\n", count, bytesRead); } #endif return bytesRead; } //___________________________________________________________________________________________ //called when the user process calls write(). ssize_t EvtIoWrite(struct file *filep, const char *buf, size_t count, loff_t *ppos) { size_t maxBytes; DeviceContext *d = filep->private_data; size_t curBytes; size_t bytesToWrite; size_t bytesInFifo; size_t bytesWritten = 0; int i; #if 0 unsigned char dumpChar; char dumpBuf[512]; memset(dumpBuf, 0, sizeof(dumpBuf)); for ( i = 0; i < 16 && i < count; i++ ) { dumpChar = *(buf+i); // sprintf(dumpBuf+strlen(dumpBuf), "%X ", dumpChar); dumpBuf[i] = *(buf+i); } printk(KERN_INFO "EvtIoWrite Dump: size(%i) data[%s]\n", count, dumpBuf); #endif while ( bytesWritten < count ) { // printk(KERN_INFO "EvtIoWrite Total[%i] Written[%i]\n", count, bytesWritten); bytesInFifo = GetWriteSize(d); //(size_t) 511 - inw(d->m_pBaseAddr + 10); while ( bytesInFifo <= 0 ) // wait for available space { wait_event_interruptible_timeout(d->sleepQ, 1 == 0, 10*1000/HZ); bytesInFifo = GetWriteSize(d); // (size_t) 511 - inw(d->m_pBaseAddr + 10); // printk(KERN_INFO "EvtIoWrite Wait for flag[%i]\n", bytesInFifo); } maxBytes = min(count-bytesWritten, bytesInFifo); // bytes to xfer this cycle while ( maxBytes > 0 ) { curBytes = min(maxBytes, sizeof(d->buffer)); // sub cycle if ( copy_from_user(d->buffer, buf+bytesWritten, curBytes) ) return -EFAULT; for ( i = 0; i < curBytes; i++ ) outb(d->buffer[i], d->baseAddress); bytesWritten += curBytes; maxBytes -= curBytes; // printk(KERN_INFO "EvtIoWrite Total[%i] Written[%i] Cycle[%i] Max[%i]\n", count, bytesWritten, curBytes, maxBytes); } } // printk(KERN_INFO "EvtIoWrite Total[%i] Written[%i]\n", count, bytesWritten); return bytesWritten; } //___________________________________________________________________________________________ int EvtIoDeviceControl(struct inode* inode, struct file* filep, unsigned int cmd, unsigned long arg) { int iTimeoutMs, iSize; //used by interrupt and record DeviceContext *d = filep->private_data; switch(cmd) { case 'r': //get record, arg contains pointer to buffer containing timeout and size, and arg returns pointer to record if( copy_from_user(&iSize, (int*)arg, sizeof(iSize)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if( copy_from_user(&iTimeoutMs, (int*) (arg + sizeof(iSize)), sizeof(iTimeoutMs)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if (iTimeoutMs > 0) //positive timeout wait_event_interruptible_timeout(d->recordWaitQ, d->recordQ.in != d->recordQ.out, iTimeoutMs * HZ / 1000); //wait up to the timeout for the IRQ byte to be set by the IRQ handler (wait for the circular buffer to be non-empty) else if(iTimeoutMs < 0) //negative timeout (timeout disabled) wait_event_interruptible(d->recordWaitQ, d->recordQ.in != d->recordQ.out); //wait forever if(d->recordQ.in != d->recordQ.out) //q non empty { if(copy_to_user((char*)arg, d->recordQ.q[ d->recordQ.out ], iSize)) //return the record retrieved in the interrupt handler to users space in arg return -EFAULT; d->recordQ.out = (d->recordQ.out + 1) % SIZE; //increment the read pointer } else //wait_event was interrupted by a signal (e.g. ctrl-c) < 0, or timeout = 0 return -ERESTARTSYS; //user mode ioctl returns -1 break; case 'i': //interrupt (get EI/UI status byte). arg contains pointer to timeout, and arg returns pointer to status byte if( copy_from_user(&iTimeoutMs, (int*)arg, sizeof(iTimeoutMs)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if (iTimeoutMs > 0) //positive timeout wait_event_interruptible_timeout(d->interruptWaitQ, d->interruptQ.in != d->interruptQ.out, iTimeoutMs * HZ / 1000); //wait up to the timeout for the IRQ byte to be set by the IRQ handler (wait for the circular buffer to be non-empty) else if(iTimeoutMs < 0) //negative timeout (timeout disabled) wait_event_interruptible(d->interruptWaitQ, d->interruptQ.in != d->interruptQ.out); //wait forever if(d->interruptQ.in != d->interruptQ.out) //q non empty { if(copy_to_user((int*)arg, &(d->interruptQ.q[ d->interruptQ.out ]), sizeof(unsigned char))) //return the status byte retrieved in the interrupt handler to users space in arg return -EFAULT; d->interruptQ.out = (d->interruptQ.out + 1) % SIZE; //increment the read pointer } else //wait_event was interrupted by a signal (e.g. ctrl-c) < 0, or timeout = 0 return -ERESTARTSYS; //user mode ioctl returns -1 break; case 'R': //reset DMC-18x0/6 if(d->pciInfo.m_fSubSystem == DMC18x6) //18x6 (do nothing on 18x2) outb(0x80, d->baseAddress + 8); //reset 18x6 break; case 'v': printk(KERN_INFO "GalilIoctl IOCTL_GALIL_VERSION\n"); if (copy_to_user((char*)arg, "galiltools", strlen("galiltools"))) return -EACCES; break; default: return -ENOTTY; //invalid ioctl code (switch fell thru). POSIX says we should return -ENOTTY instead of EINVAL } return 0; //normal } //___________________________________________________________________________________________ long EvtUnlockedIoDeviceControl(struct file* filep, unsigned int cmd, unsigned long arg) { int iTimeoutMs, iSize; //used by interrupt and record DeviceContext *d = filep->private_data; switch(cmd) { case 'r': //get record, arg contains pointer to buffer containing timeout and size, and arg returns pointer to record if( copy_from_user(&iSize, (int*)arg, sizeof(iSize)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if( copy_from_user(&iTimeoutMs, (int*) (arg + sizeof(iSize)), sizeof(iTimeoutMs)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if (iTimeoutMs > 0) //positive timeout wait_event_interruptible_timeout(d->recordWaitQ, d->recordQ.in != d->recordQ.out, iTimeoutMs * HZ / 1000); //wait up to the timeout for the IRQ byte to be set by the IRQ handler (wait for the circular buffer to be non-empty) else if(iTimeoutMs < 0) //negative timeout (timeout disabled) wait_event_interruptible(d->recordWaitQ, d->recordQ.in != d->recordQ.out); //wait forever if(d->recordQ.in != d->recordQ.out) //q non empty { if(copy_to_user((char*)arg, d->recordQ.q[ d->recordQ.out ], iSize)) //return the record retrieved in the interrupt handler to users space in arg return -EFAULT; d->recordQ.out = (d->recordQ.out + 1) % SIZE; //increment the read pointer } else //wait_event was interrupted by a signal (e.g. ctrl-c) < 0, or timeout = 0 return -ERESTARTSYS; //user mode ioctl returns -1 break; case 'i': //interrupt (get EI/UI status byte). arg contains pointer to timeout, and arg returns pointer to status byte if( copy_from_user(&iTimeoutMs, (int*)arg, sizeof(iTimeoutMs)) ) //get data from user space in arg (e.g. 1000) and copy to kernel buffer (iTimeoutMs) return -EFAULT; if (iTimeoutMs > 0) //positive timeout wait_event_interruptible_timeout(d->interruptWaitQ, d->interruptQ.in != d->interruptQ.out, iTimeoutMs * HZ / 1000); //wait up to the timeout for the IRQ byte to be set by the IRQ handler (wait for the circular buffer to be non-empty) else if(iTimeoutMs < 0) //negative timeout (timeout disabled) wait_event_interruptible(d->interruptWaitQ, d->interruptQ.in != d->interruptQ.out); //wait forever if(d->interruptQ.in != d->interruptQ.out) //q non empty { if(copy_to_user((int*)arg, &(d->interruptQ.q[ d->interruptQ.out ]), sizeof(unsigned char))) //return the status byte retrieved in the interrupt handler to users space in arg return -EFAULT; d->interruptQ.out = (d->interruptQ.out + 1) % SIZE; //increment the read pointer } else //wait_event was interrupted by a signal (e.g. ctrl-c) < 0, or timeout = 0 return -ERESTARTSYS; //user mode ioctl returns -1 break; case 'R': //reset DMC-18x0/6 if(d->pciInfo.m_fSubSystem == DMC18x6) //18x6 (do nothing on 18x2) outb(0x80, d->baseAddress + 8); //reset 18x6 break; case 'v': printk(KERN_INFO "GalilIoctl IOCTL_GALIL_VERSION\n"); if (copy_to_user((char*)arg, "galiltools", strlen("galiltools"))) return -EACCES; break; default: return -ENOTTY; //invalid ioctl code (switch fell thru). POSIX says we should return -ENOTTY instead of EINVAL } return 0; //normal } //___________________________________________________________________________________________ static irqreturn_t EvtInterruptIsr(int irq, void *dev_id) // struct pt_regs *regs) //Kernel 2.6.19 removed regs argument http://lwn.net/Articles/202449/ { unsigned char flagByte; unsigned char statusByte; DeviceContext *d = dev_id; //get a pointer to the device. This was passed to request_irq() int dataRecordSize; #if 1 switch(d->pciInfo.m_fSubSystem) { case DMC18x2: if(0x45 == (inl(d->irqAddress) & 0x45)) //this is our interrupt (bit 0 local interrupts enabled, bit 2 status (our)s, bit 6 enable (set below)) { unsigned char status = inb(d->baseAddress + 1); //read 4701 status register (clear 4701 IRQ flag) if(status & 0x04) //bit 2 means mailbox interrupt (controller sending status byte to PC) { outb(0x06, d->baseAddress + 1); //write 6 to N+1 (set 4701 pointer register to point to other port's mailbox) statusByte = inb(d->baseAddress + 1); //read status byte (4701 other port's mailbox) } else //controller probably set MR bit (bit 7) of 4701 (by hard resetting controller) { outb(0x02, d->baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d->baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d->baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb(d->baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) return IRQ_HANDLED; //Tell WDF, and hence Windows, that there WAS an interrupt outstanding for us to process } } else return IRQ_NONE; break; case DMC18x6: if(0x60 == (inb(d->baseAddress + 4) & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(0x20 | inb(d->baseAddress + 4), d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) } else return IRQ_NONE; break; case DMC1640: case DMC18x0: flagByte = inb(d->baseAddress + 4); if ( d->pciInfo.m_fRevsion == REV1 ) { if(0x60 == (flagByte & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(flagByte, d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) } else return IRQ_NONE; } else { if(0x60 == (flagByte & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(0x20 | flagByte, d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) } else return IRQ_NONE; } break; case DMC1417: if(0x45 == (inb(d->irqAddress) & 0x45)) { statusByte = 0xFF; // 1417 interrupt need to be read via IV command outb(0x05, d->irqAddress + 1); // stop Interrupt } else return IRQ_NONE; break; default: ; }; #else if(d->irqAddress) //18x2 { if(0x45 == (inl(d->irqAddress) & 0x45)) //this is our interrupt (bit 0 local interrupts enabled, bit 2 status (our)s, bit 6 enable (set below)) { unsigned char status = inb(d->baseAddress + 1); //read 4701 status register (clear 4701 IRQ flag) if(status & 0x04) //bit 2 means mailbox interrupt (controller sending status byte to PC) { outb(0x06, d->baseAddress + 1); //write 6 to N+1 (set 4701 pointer register to point to other port's mailbox) statusByte = inb(d->baseAddress + 1); //read status byte (4701 other port's mailbox) goto DPC; } else //controller probably set MR bit (bit 7) of 4701 (by hard resetting controller) { outb(0x02, d->baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d->baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d->baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb(d->baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) } return IRQ_HANDLED; //Tell WDF, and hence Windows, that there WAS an interrupt outstanding for us to process } } else //18x6 { if(0x60 == (inb(d->baseAddress + 4) & 0x60)) //if the Galil controller generated an interrupt and interrupts were enabled on the controller { statusByte = inb(d->baseAddress + 8); //read status byte (must happen before acknowledging the interrupt below) outb(0x20 | inb(d->baseAddress + 4), d->baseAddress + 4); //acknowledge the interrupt (else the controller will keep interrupting) goto DPC; } } return IRQ_NONE; //not our interrupt, another device sharing the IRQ generated the interrupt DPC: #endif // printk("EvtInterruptIsr 0x%X\n", statusByte); switch(statusByte) { case 0xBA: break; //command done (colon sent) case 0xBB: break; //MG case 0xBC: //record if( (d->recordAddress != 0) && (d->recordQ.out != (d->recordQ.in + 1) % SIZE) ) //circular buffer NOT FULL (will lose new status bytes if full--old ones are retained) { if ( d->pciInfo.m_fRevsion == REV2 ) memcpy_fromio(d->recordQ.q[ d->recordQ.in ], (void __iomem *)d->recordAddress, 512); //copy 512 bytes from the dual port RAM else { dataRecordSize = 0; memset(d->recordQ.q[ d->recordQ.in ], 0, sizeof(d->recordQ.q[ d->recordQ.in ])); while ( (0x00 == (inb(d->baseAddress+4) & 0x80)) && dataRecordSize < 512 ) { // printk(KERN_INFO "DR read flag[%X]\n", inb(d->baseAddress+4)); d->recordQ.q[ d->recordQ.in ][dataRecordSize++] = inb(d->baseAddress+0xC); } printk("PLX data record size = %i\n", dataRecordSize); } d->recordQ.in = (d->recordQ.in + 1) % SIZE; //increment in (write) pointer wake_up_interruptible(&(d->recordWaitQ)); //wake up ioctl } //else FULL break; default: //EI (0xCx, Dx, Ex) and UI (0xf0 to ff) //CACHE the status byte for later retreival the next time ioctl is called from user mode if( d->interruptQ.out != (d->interruptQ.in + 1) % SIZE ) //circular buffer NOT FULL (will lose new status bytes if full--old ones are retained) { d->interruptQ.q[ d->interruptQ.in ] = statusByte; //store status byte read above d->interruptQ.in = (d->interruptQ.in + 1) % SIZE; //increment in (write) pointer wake_up_interruptible(&(d->interruptWaitQ)); //wake up ioctl } //else FULL break; }//switch return IRQ_HANDLED; } //___________________________________________________________________________________________ //called when the open() system call is invoked from user space. Implements exclusive open (Chapter 6 Single-Open Devices) int EvtDeviceFileOpen(struct inode *inode, struct file *filep) //Create in WDF { int i = 0; // printk("EvtDeviceFileOpen %i %i\n", imajor(inode), iminor(inode)); for(i = 0; i <= device; i++) //search for the minor number if(d[i].galilpci_miscdev.minor == iminor(inode)) break; //found it if(d[i].bOpen) //file already open return -EBUSY; //fail the open else { filep->private_data = &d[i]; //initialize the private data for this file descriptor. This will be accessed in read() and write() d[i].bOpen = 1; //file is open now return 0; //successful open } } //___________________________________________________________________________________________ //called when the close() system call is invoked from user space int EvtFileClose(struct inode *inode, struct file *filep) { int i = 0; // printk("EvtFileClose %i %i\n", imajor(inode), iminor(inode)); for(i = 0; i <= device; i++) //search for the minor number if(d[i].galilpci_miscdev.minor == iminor(inode)) break; //found it d[i].bOpen = 0; //file is closed now return 0; } //___________________________________________________________________________________________ //file operations structure static struct file_operations galilpci_fops = { .owner = THIS_MODULE, .read = EvtIoRead, .write = EvtIoWrite, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)) .ioctl = EvtIoDeviceControl, #else .unlocked_ioctl = EvtUnlockedIoDeviceControl, #endif .open = EvtDeviceFileOpen, .release = EvtFileClose, }; //___________________________________________________________________________________________ //PCI device ID structure. EvtDevicePrepareHardware will be called for each board that matches one the of the following //sets of vendor ID, device ID, subsystem vendor ID, and subsystem device ID. This is a filter. static struct pci_device_id pcigalil_pci_tbl[] __devinitdata = { {.vendor = 0x10B5, .device = 0x9050, .subvendor = 0x1079, .subdevice = 0x1640}, //DMC-16x0 could set .subdevice to PCI_ANY_ID {.vendor = 0x10B5, .device = 0x9050, .subvendor = 0x1079, .subdevice = 0x1800}, //DMC-18x0 {.vendor = 0x10B5, .device = 0x9050, .subvendor = 0x1079, .subdevice = 0x1842}, //DMC-18x2 {.vendor = 0x10B5, .device = 0x9050, .subvendor = 0x1079, .subdevice = 0x1806}, //DMC-18x6 { 0 }, }; MODULE_DEVICE_TABLE(pci, pcigalil_pci_tbl); //let module loading system know what module works with what hardware devices //___________________________________________________________________________________________ //PCI "probe" function called when the kernel finds a Galil board (matching pcigalil_pci_tbl). //0 is normal return. Called once per board. If the function doesn't want to control the device //or an error occurs, returns a negative value. This function stores the minor number //in galilpci_miscdev.minor so that open() can later figure out which board is being opened, //and set the correct filep->private_data (so that read() and write() know which ioaddr to access). static int __devinit EvtDevicePrepareHardware(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = -EIO; u16 subVender; u16 uModel; //will contain galil model number (e.g. 0x1802) long resourceStart, resourceEnd, resourceLen; char name[10]; //will contain name in /dev/ (e.g. galilpci0) device++; //incremented each time be find a galil board if (device >= MAX_DEVICES) { printk("EvtDevicePrepareHardware This driver only supports %i devices\n", MAX_DEVICES); device--; //update the device count return -ENODEV; } if (pci_enable_device(pdev)) { //wake up the device printk("EvtDevicePrepareHardware Not possible to enable PCI Device\n"); device--; //update the device count return -ENODEV; } //Mark all PCI regions associated with PCI device pdev as being reserved by owner res_name. //Do not access any address inside the PCI regions unless this call returns successfully. //This will reverve both i/o ports and memory regions, and shows up in /proc/ioports and /proc/iomem if (pci_request_regions(pdev, GALILNAME)) { printk("EvtDevicePrepareHardware I/O address (0x%04x) already in use\n", (unsigned int) d[device].baseAddress); device--; //update the device count return -ENODEV; } //I/O ADDRESSES d[device].baseAddress = pci_resource_start(pdev, /*BAR*/ 2); //get base address N from BAR2 d[device].irqAddress = pci_resource_start(pdev, /*BAR*/ 1); //get base address N from BAR2. 0 on 18x0/2. Non zero on 18x2. resourceStart = pci_resource_start(pdev, /*BAR*/ 0); if ( resourceStart == 0x0000 ) { printk("No data record channel\n"); resourceEnd = 0; resourceLen = 0; } else { resourceEnd = pci_resource_end(pdev, /*BAR*/ 0); resourceLen = resourceEnd-resourceStart+1; } printk("Bar 0 address: 0x%X to 0x%X, len=%i\n", resourceStart, resourceEnd, resourceLen); if ( resourceLen == 128 ) { // d[device].recordAddress = pci_iomap(pdev, /*BAR*/ 0, 128 /*bytes*/); d[device].pciInfo.m_fRevsion = REV1; // PLX based device d[device].recordAddress = resourceStart; printk("PLX based controller - not supported\n"); device--; //update the device count return -ENODEV; } else if ( resourceLen == 512 ) { d[device].pciInfo.m_fRevsion = REV2; // XININX based device d[device].recordAddress = pci_iomap(pdev, 0, 512); printk("XINUNX based controller\n"); } if (d[device].baseAddress == 0x0000) { //check base address. printk("EvtDevicePrepareHardware No Main I/O-Address for device detected\n"); ret = -ENODEV; goto release_regions; } printk("Base Address: 0x%X\n", d[device].baseAddress); printk("Record Address: 0x%X\n", d[device].recordAddress); // Identify PCI device pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subVender); //read galil model (e.g. 0x1806) pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &uModel); //read galil model (e.g. 0x1806) d[device].pciInfo.m_fSubVendor = subVender; d[device].pciInfo.m_fSubSystem = uModel; //register the device under major number 10. Minor number will show up in /proc/misc d[device].galilpci_miscdev.minor = MISC_DYNAMIC_MINOR, //assign the minor number dynamically (ask for a free one). This field will eventually contain the actual minor number. sprintf(name, GALILNAME "%i", device); d[device].galilpci_miscdev.name = name, //the name for this device, meant for human consumption: users will find the name in the /proc/misc file. d[device].galilpci_miscdev.fops = &galilpci_fops, //the file operations d[device].bOpen = 0; //file not open ret = misc_register(&(d[device].galilpci_miscdev)); if (ret) { printk ("EvtDevicePrepareHardware cannot register miscdev (err=%d)\n", ret); goto release_regions; } init_waitqueue_head(&d[device].sleepQ); //INTERRUPTS d[device].interrupt = pdev->irq; //store the IRQ number so we can call free-irq in the cleanup function when the module us removed with rmmod if (request_irq(d[device].interrupt, EvtInterruptIsr, IRQF_SHARED, GALILNAME, &d[device])) //register the interrupt handler. This should happen before we enable interrupts on the controller. Kernel 2.6.18 2006Sep changed SA_SHIRQ to IRQF_SHARED http://lwn.net/Articles/2.6-kernel-api/ { printk("EvtDevicePrepareHardware IRQ %x is not free\n", d[device].interrupt); goto misc_deregister; } init_waitqueue_head(&d[device].interruptWaitQ); //initialize the list of processes waiting for EI/UI interrupts d[device].interruptQ.in = d[device].interruptQ.out = 0; //initialize circular buffer pointers init_waitqueue_head(&d[device].recordWaitQ); //initialize the list of processes waiting for record interrupts d[device].recordQ.in = d[device].recordQ.out = 0; //initialize circular buffer pointers //ENABLE INTERRUPTS ON CONTROLLER #if 1 switch (d[device].pciInfo.m_fSubSystem) { case DMC18x2: inb( d[device].baseAddress + 1); //reset 4701 ("clear FIFOs"). Interrupts won't enable on 18x2 rev F without this (rev D1 doesn't require it) outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); inb( d[device].baseAddress + 1); outb(0x02, d[device].baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d[device].baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d[device].baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb ( d[device].baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) d[device].irqAddress += 0x4c; //76 byte offset from the address in BAR1 outl( 0x40 | inl(d[device].irqAddress), d[device].irqAddress); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) break; case DMC18x6: case DMC18x0: outb(0x40 | inb(d[device].baseAddress + 4), d[device].baseAddress + 4); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) break; case DMC1417: d[device].irqAddress += 0x4c; outb(0x40 | inb(d[device].irqAddress), d[device].irqAddress); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) break; case DMC1640: default: ; }; #else if(d[device].irqAddress) //18x2 { inb( d[device].baseAddress + 1); //reset 4701 ("clear FIFOs"). Interrupts won't enable on 18x2 rev F without this (rev D1 doesn't require it) outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); outb(0x01, d[device].baseAddress + 1); outb(0x80, d[device].baseAddress + 1); inb( d[device].baseAddress + 1); outb(0x02, d[device].baseAddress + 1); //set 4701 pointer register to point to the interrupt mask register (register 2) outb(0x04, d[device].baseAddress + 1); //interrupt when the controller writes to its mailbox (don't interrupt on 6 full, 5 empty, 4 almost full, 3 almost empty, 1 byte detect, nor 0 parity/frame error //below is necessary so 18x2 doesn't keep interrupting outb(0x06, d[device].baseAddress + 1); //set 4701 pointer register to point to the other port's mailbox inb ( d[device].baseAddress + 1); //read other port's mailbox (clears any pending mailbox interrupt) d[device].irqAddress += 0x4c; //76 byte offset from the address in BAR1 outl( 0x40 | inl(d[device].irqAddress), d[device].irqAddress); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) } else //18x6 outb(0x40 | inb(d[device].baseAddress + 4), d[device].baseAddress + 4); //enable interrupts on the controller. They will stay enabled until the module is unloaded (or power is shut off) #endif printk("EvtDevicePrepareHardware I/O address (0x%lx), Model %x, minor number %d Rev(%i)\n", d[device].baseAddress, uModel, d[device].galilpci_miscdev.minor, d[device].pciInfo.m_fRevsion); return 0; misc_deregister: misc_deregister(&(d[device].galilpci_miscdev)); //unregister the device with major number 10 release_regions: pci_release_regions(pdev); disable_device: pci_disable_device(pdev); device--; //update the device count return ret; } //___________________________________________________________________________________________ //Called once per board upon rmmod static void __devexit EvtDeviceReleaseHardware(struct pci_dev *pdev) { // printk("EvtDeviceReleaseHardware\n"); misc_deregister(&(d[device].galilpci_miscdev)); //unregister the device with major number 10 #if 1 switch (d[device].pciInfo.m_fSubSystem) { case DMC18x2: outl(~0x40 & inl(d[device].irqAddress), d[device].irqAddress); break; case DMC18x6: case DMC18x0: outb(~0x40 & inb(d[device].baseAddress + 4), d[device].baseAddress + 4); break; case DMC1417: outl(~0x40 & inl(d[device].irqAddress), d[device].irqAddress); break; case DMC1640: default: ; }; #else if(d[device].irqAddress) //18x2 outl(~0x40 & inl(d[device].irqAddress), d[device].irqAddress ); //disable interrupts on the controller. They will stay disabled until the module is loaded again. else //18x6 outb(~0x40 & inb(d[device].baseAddress + 4), d[device].baseAddress + 4); //disable interrupts on the controller. They will stay disabled until the module is loaded again. #endif free_irq(d[device].interrupt, &d[device]); //should happen after interrupts are disabled on the controller pci_release_regions(pdev); //relase the I/O ports and memory regions pci_disable_device(pdev); device--; } //___________________________________________________________________________________________ //PCI driver structure static struct pci_driver pcigalil_driver = { .name = GALILNAME, //name of driver (must be unique among all PCI drivers). Shows up under /sys/bus/pci/drivers .id_table = pcigalil_pci_tbl, //list of PCI IDs this driver supports .probe = EvtDevicePrepareHardware, //called when a board is found .remove = __devexit_p(EvtDeviceReleaseHardware), //called on exit }; //___________________________________________________________________________________________ //called when module is inserted into the kernel (insmod) static int __init DriverEntry(void) { printk("DriverEntry\n"); return pci_register_driver(&pcigalil_driver); //calls EvtDevicePrepareHardware. pci_module_init() is obselete } //___________________________________________________________________________________________ //called when module is removed from kernel (rmmod) static void __exit DriverExit(void) { printk("DriverExit\n"); pci_unregister_driver(&pcigalil_driver); //calls EvtDeviceReleaseHardware } //___________________________________________________________________________________________ module_init(DriverEntry); module_exit(DriverExit); /* [root@localhost PCIDriver]# ls galilpci.c Makefile [root@localhost PCIDriver]# make #build the pci driver make -C /lib/modules/2.6.11-1.1369_FC4/build SUBDIRS=/root/galilpci modules make[1]: Entering directory `/usr/src/kernels/2.6.11-1.1369_FC4-i686' CC [M] /root/galilpci/galilpci.o Building modules, stage 2. MODPOST CC /root/galilpci/galilpci.mod.o LD [M] /root/galilpci/galilpci.ko make[1]: Leaving directory `/usr/src/kernels/2.6.11-1.1369_FC4-i686' [root@localhost PCIDriver]# insmod galilpci.ko #load the module into the kernel [root@localhost PCIDriver]# lsmod | grep galilpci #the galilpci kernel module should show up here galilpci 5416 0 [root@localhost PCIDriver]# ls /dev/g* #the device file of the same name should show up here /dev/galilpci0 /dev/gpmctl [root@localhost PCIDriver]# g=/dev/galilpci0 #set an environment variable so we type less [root@localhost PCIDriver]# echo -en ER0\\r > $g #send a command to the board. The red LED should turn on [root@localhost PCIDriver]# cat $g #get the colon response :[root@localhost PCIDriver]# [root@localhost PCIDriver]# echo -en ER-1\\r > $g && cat $g && echo #send a command and get the response. The red LED should turn off : [root@localhost PCIDriver]# cat > test.dmc #create a small program to download to the controller #AUTO ER0 WT1000 ER-1 WT1000 JP#AUTO [root@localhost PCIDriver]# cat test.dmc #AUTO ER0 WT1000 ER-1 WT1000 JP#AUTO [root@localhost PCIDriver]# #now download the file we just created [root@localhost PCIDriver]# echo -en DL\\r > $g && cat test.dmc | tr \\n \\r > $g && echo -en \\ > $g && cat $g [root@localhost PCIDriver]# echo -en LS\\r > $g && cat $g && echo #list the program 0 #AUTO 1 ER0 2 WT1000 3 ER-1 4 WT1000 5 JP#AUTO 6 7 : [root@localhost PCIDriver]# echo -en XQ\\r > $g && cat $g && echo #run the program (the red LED should blink every second) : [root@localhost PCIDriver]# #now upload the program to a file [root@localhost PCIDriver]# echo -en UL\\r > $g && cat $g | tr -d \\r:\\032 > ul.dmc [root@localhost PCIDriver]# cat ul.dmc #AUTO ER0 WT1000 ER-1 WT1000 JP#AUTO */ /* #0.0.0.2 2008-02-25 Add interrupt-driven DR (need DR fw for 18x2), Add EI/UI support, Add register-level reset for 18x6 OS #1.0.0.0 2008-05-16 Same as 0.0.0.2 */