/* * pulseblaster.c - Communication with pulseblaster * author: Achim Gaedke * created: February 2005 * the amcc_outb routine comes from SpinCore * initially the module is built from example 4 of "The Linux Kernel Module Programming Guide" * the version of June 2008 is written with the help of "Linux device drivers" * Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman, O' Reilly, 3rd Edition */ #include /* We're doing kernel work */ #include /* Specifically, a module */ #include #include #include #include #include #include #include #include /* for get_user and put_user */ #include #include "pulseblaster.h" #define SUCCESS 0 #define DEVICE_NAME "pulseblaster" enum pulseblaster_board { PBB_DEBUG = 0, PBB_GENERIC_AMCC, PBB_GENERIC_PCI, }; /* number of found and allocated devices */ static int pb_dev_no=0; /* * dynamic allocated device number */ /* start of allocated minor number(s)*/ static dev_t pb_dev_no_start; struct pulseblaster_device { /* * Is the device open right now? Used to prevent * concurent access into the same device */ int device_open; /** * The type of this board */ enum pulseblaster_board boardtype; /** * The pci device for this pulseblaster */ struct pci_dev *pciboard; /* * the base io address * 0 means debug mode without hardware accesss, writes everything to kernel log */ unsigned long base_addr; /* * protect access to io memory */ spinlock_t io_lock; /* * char dev associated with it */ struct cdev cdev; }; /* array of char_devices */ /* todo: make this a pointer array with dynamic allocation... */ #define pulseblaster_max_devno 4 static struct pulseblaster_device pb_devs[pulseblaster_max_devno]; // a lock for that structure static spinlock_t pb_devs_lock; // debug version static struct pulseblaster_device pb_dev_debug; static int base_address=-1; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Achim Gaedke"); MODULE_DESCRIPTION("Driver for SpinCore's PulseBlaster"); module_param(base_address, int, S_IRUGO); MODULE_PARM_DESC(base_address, "not supported anymore, IO base addresses of device are detected automatically"); /*************************************************************************************/ /* code taken form spincore example: * This function writes a byte to a board that uses the amcc chip. * SP3 and previous revision boards use this interface. Later designs * can be programmed directly. */ #if 1 // version without delay (faster) #define pb_out(bwl) out##bwl #define pb_in(bwl) in##bwl #else // version with extra delay (works on odd hardware, too) #define pb_out(bwl) out##bwl##_p #define pb_in(bwl) in##bwl##_p #endif static int pb_outb_debug(char data, unsigned short address, unsigned long my_base_address) { printk("%s: reg %02x=%02x\n",DEVICE_NAME, 0xff&address, 0xff&data); return 0; } static int amcc_outb(char data, unsigned short address, unsigned long my_base_address) { unsigned int MAX_RECV_TIMEOUT = 10; unsigned int RECV_START, RECV_TOGGLE, RECV_TIMEOUT = 0; int XFER_ERROR = 0; int RECV_POLLING = 0; unsigned int OGMB = 0x0C; unsigned int CHK_RECV = 0x1F; unsigned int SIG_TRNS = 0x0F; unsigned int CLEAR31 = 0x00000001; unsigned int CLEAR24 = 0x000000FF; unsigned int CLEAR28 = 0x0000000F; unsigned int SET_XFER = 0x01000000; unsigned int Temp_Address = address; unsigned int Temp_Data = data; if (my_base_address==0) { return pb_outb_debug(data, address, my_base_address); } // Prepare Address Transfer Temp_Address &= CLEAR28; Temp_Address |= SET_XFER; // Prepare Data Transfer Temp_Data &= CLEAR24; Temp_Data |= SET_XFER; // Clear the XFER bit (Should already be cleared) pb_out(b) (0,my_base_address+SIG_TRNS); // Transfer Address // Read RECV bit from the Board RECV_START = pb_in(b) (my_base_address+CHK_RECV); RECV_START &= CLEAR31; // Only Save LSB // Send Address to OGMB pb_out(l) (Temp_Address, my_base_address+OGMB); RECV_POLLING = 1; // Set Polling Flag RECV_TIMEOUT = 0; while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) { RECV_TOGGLE = pb_in(b)(my_base_address+CHK_RECV); RECV_TOGGLE &= CLEAR31; // Only Save LSB if (RECV_TOGGLE != RECV_START) RECV_POLLING = 0; // Finished if Different else RECV_TIMEOUT++; if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2; } // Transfer Complete (Clear) Signal pb_out(b)(0, my_base_address+SIG_TRNS); // Transfer Data // Read RECV bit from the Board RECV_START = pb_in(b)(my_base_address+CHK_RECV); RECV_START &= CLEAR31; // Only Save LSB // Send Data to OGMB pb_out(l)(Temp_Data, my_base_address+OGMB); RECV_POLLING = 1; // Set Polling Flag RECV_TIMEOUT = 0; while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) { // Check for Toggled RECV RECV_TOGGLE = pb_in(b)(my_base_address+CHK_RECV); RECV_TOGGLE &= CLEAR31; // Only Save LSB if (RECV_TOGGLE != RECV_START) RECV_POLLING = 0; // Finished if Different else RECV_TIMEOUT++; if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2; } // Transfer Complete (Clear) Signal pb_out(b)(0, my_base_address+SIG_TRNS); return XFER_ERROR; } static int pb_outb_pci(char data, unsigned short address, unsigned long my_base_address) { if (my_base_address == 0) return pb_outb_debug(data, address, my_base_address); pb_out(b)(data, my_base_address + address); return 0; } /** * write to byte register on board */ static int pb_dev_outb(struct pulseblaster_device *my_dev, char data, unsigned short address) { unsigned long my_base_address; if (my_dev == NULL) return -1; my_base_address = my_dev->base_addr; switch (my_base_address != 0 ? my_dev->boardtype : PBB_DEBUG) { case PBB_DEBUG: return pb_outb_debug(data, address, my_base_address); case PBB_GENERIC_AMCC: return amcc_outb(data, address, my_base_address); case PBB_GENERIC_PCI: return pb_outb_pci(data, address, my_base_address); default: return -1; } return -1; } static int pb_inb_debug(unsigned int address, unsigned long my_base_address) { printk("%s: reg(%02x)=0 (guessed)\n",DEVICE_NAME, 0xff&address); return 0; } /** * Read a byte from a board using the AMCC chip */ static int amcc_inb(unsigned int address, unsigned long my_base_address) { unsigned int MAX_RECV_TIMEOUT = 10000; unsigned int RECV_START, RECV_STOP, RECV_TOGGLE, RECV_TIMEOUT = 0; int XFER_ERROR = 0; int RECV_POLLING = 0; unsigned int CHK_RECV = 0x1F; unsigned int SIG_TRNS = 0x0F; unsigned int ICMB = 0x1C; unsigned int CLEAR24 = 0x000000FF; unsigned int BIT1 = 0x00000002; unsigned int INV1 = 0x000000FD; unsigned short READ_ADDR = 0x9; int Toggle = 0; int Toggle_Temp = 0; unsigned int Temp_Data = 0; if (my_base_address==0) { return pb_inb_debug(address, my_base_address); } // CHEK FOR 1 in MD1 if ((XFER_ERROR=amcc_outb(address, 8, my_base_address))!=0 || (XFER_ERROR=amcc_outb(0, READ_ADDR, my_base_address))!=0) // Tell board to start a read cycle return XFER_ERROR; RECV_POLLING = 1; // Set Polling Flag RECV_TIMEOUT = 0; RECV_START = 0x2; // Value expected when data is ready while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) { // Check for Toggled RECV //RECV_TOGGLE = _inp(CHK_RECV); RECV_TOGGLE = pb_in(b) (my_base_address+CHK_RECV); RECV_TOGGLE &= BIT1; // Only Save Bit 1 if (RECV_TOGGLE == RECV_START) RECV_POLLING = 0; // Finished if Different else RECV_TIMEOUT++; if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2; } if (XFER_ERROR != 0) { return XFER_ERROR; } //Temp_Data = _inp(ICMB); // Read RECV bit from the Board Temp_Data = pb_in(b)(my_base_address+ICMB); Temp_Data &= CLEAR24; //Toggle = _inp(SIG_TRNS); Toggle = pb_in(b) (my_base_address+SIG_TRNS); Toggle_Temp = Toggle & BIT1; // Only Save Bit 1 if (Toggle_Temp == 0x0) { Toggle |= BIT1; // If Bit 1 is zero, set it to 1 } else { Toggle &= INV1; // IF Bit 1 is 1, set it to 0 } //_outp(SIG_TRNS, Toggle); pb_out(b) (Toggle,my_base_address+SIG_TRNS); RECV_POLLING = 1; // Set Polling Flag RECV_TIMEOUT = 0; RECV_STOP = 0x0; while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) { // Check for Toggled RECV //RECV_TOGGLE = _inp(CHK_RECV); RECV_TOGGLE = pb_in(b) (my_base_address + CHK_RECV); RECV_TOGGLE &= BIT1; // Only Save Bit 1 if (RECV_TOGGLE == RECV_STOP) RECV_POLLING = 0; // Finished if Different else RECV_TIMEOUT++; if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -3; } if (XFER_ERROR != 0) { return XFER_ERROR; } return Temp_Data; } static int pb_inb_pci(unsigned int address, unsigned long my_base_address) { if (my_base_address == 0) return pb_inb_debug(address, my_base_address); return pb_in(b)(my_base_address + address); } /** * Read byte register from board */ static int pb_dev_inb(struct pulseblaster_device *my_dev, unsigned short address) { unsigned long my_base_address; if (my_dev == NULL) return -1; my_base_address = my_dev->base_addr; switch (my_base_address != 0 ? my_dev->boardtype : PBB_DEBUG) { case PBB_DEBUG: return pb_inb_debug(address, my_base_address); case PBB_GENERIC_AMCC: return amcc_inb(address, my_base_address); case PBB_GENERIC_PCI: return pb_inb_pci(address, my_base_address); default: return -1; } return -1; } /** * Setup and get the base_addr */ static unsigned long pb_dev_setup_baseaddr(struct pulseblaster_device *my_dev) { unsigned long addr = 0x0; if (my_dev == NULL) return 0x0; if ((my_dev->boardtype != PBB_DEBUG) && (my_dev->pciboard != NULL)) { addr = pci_resource_start(my_dev->pciboard, 0); } my_dev->base_addr = addr; return addr; } static inline bool pb_is_debug(struct pulseblaster_device *my_dev) { return ((my_dev->boardtype == PBB_DEBUG) || (my_dev->pciboard == NULL)); } /* * This is called whenever a process attempts to open the device file */ static int device_open(struct inode *inode, struct file *file) { struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev); file->private_data=my_dev; if (pb_is_debug(my_dev)) printk("%s: device_open(%p)\n", DEVICE_NAME,file); /* * We don't want to talk to two processes at the same time */ if (my_dev->device_open) return -EBUSY; my_dev->device_open++; try_module_get(THIS_MODULE); return SUCCESS; } /* * Device released (a.k.a. closed) */ static int device_release(struct inode *inode, struct file *file) { struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev); pb_dev_setup_baseaddr(my_dev); if (pb_is_debug(my_dev)) printk("%s: device_release(%p,%p)\n",DEVICE_NAME, inode, file); /* * reset device */ if (0) { // this procedure works only on DDS cards // DDS Manual spin_lock(&(my_dev->io_lock)); pb_dev_outb(my_dev, 0, 0); //dev reset pb_dev_outb(my_dev, 4, 2); //bytes per word pb_dev_outb(my_dev, 0xFF, 3); //dev to program pb_dev_outb(my_dev, 0, 4); //reset address counter pb_dev_outb(my_dev, 0, 6); //data out pb_dev_outb(my_dev, 0, 6); //data out pb_dev_outb(my_dev, 0, 6); //data out pb_dev_outb(my_dev, 0, 6); //data out pb_dev_outb(my_dev, 0, 5); //strobe clock pb_dev_outb(my_dev, 0, 5); //strobe clock spin_unlock(&(my_dev->io_lock)); } if (0) { spin_lock(&(my_dev->io_lock)); // this procedure is successful for 24Bit cards // SpinCore by mail Oct 10th 2007 pb_dev_outb(my_dev, 0,6);//store 0's to memory pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,6); pb_dev_outb(my_dev, 0,0);//dev reset pb_dev_outb(my_dev, 4,2);//bytes per word pb_dev_outb(my_dev, 0,3);//write to internal memory pb_dev_outb(my_dev, 0,4);//clear address counter pb_dev_outb(my_dev, 0,6);//data out pb_dev_outb(my_dev, 0,6);//data out pb_dev_outb(my_dev, 0,6);//data out pb_dev_outb(my_dev, 0,6);//data out pb_dev_outb(my_dev, 7,7);//programming finished pb_dev_outb(my_dev, 7,1);//trigger spin_unlock(&(my_dev->io_lock)); } /* * We're now ready for our next caller */ my_dev->device_open--; module_put(THIS_MODULE); return SUCCESS; } /* * This function is called whenever a process which has already opened the * device file attempts to read from it. */ static ssize_t device_read(struct file *file, /* see include/linux/fs.h */ char __user * buffer, /* buffer to be * filled with data */ size_t length, /* length of the buffer */ loff_t * offset) { // not implemented return 0; } /* * This function is called when somebody tries to * write into our device file. * everything goes to register 6: data */ static ssize_t device_write(struct file *file, const char __user * buffer, size_t length, loff_t * offset) { struct pulseblaster_device* my_dev=(struct pulseblaster_device*)file->private_data; register int i=0; pb_dev_setup_baseaddr(my_dev); while (1) { // sometimes unsigned char temp_byte; int retval; get_user(temp_byte, buffer + i); // the hardware access spin_lock(&(my_dev->io_lock)); retval = pb_dev_outb(my_dev, temp_byte,6); spin_unlock(&(my_dev->io_lock)); if (retval!=0) { printk("%s: IO Error, pb_dev_outb returned %d\n",DEVICE_NAME, retval); return -EIO; // make an ordinary io error } i++; /* break codition */ if (i>=length) break; /* be decent to all other processes --- at least sometimes */ if ((i & ((1<<14)-1)) == 0) schedule(); } /* * Again, return the number of input characters used */ return i; } /* * This function is called whenever a process tries to do an ioctl on our * device file. We get two extra parameters (additional to the inode and file * structures, which all device functions get): the number of the ioctl called * and the parameter given to the ioctl function. * * If the ioctl is write or read/write (meaning output is returned to the * calling process), the ioctl call returns the output of this function. * */ static long device_ioctl( struct file *file, /* ditto */ unsigned int ioctl_num, /* number and param for ioctl */ unsigned long ioctl_param) { struct inode *inode = file->f_path.dentry->d_inode; struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev); int ret_val=SUCCESS; pb_dev_setup_baseaddr(my_dev); if (ioctl_num==IOCTL_OUTB){ unsigned char reg=(ioctl_param>>8)&0xFF; unsigned char val=ioctl_param&0xFF; /* check for register boundaries! */ if (reg>7) { printk("%s: got bad register number %02x\n",DEVICE_NAME,0x0ff®); ret_val=-EINVAL; } else { spin_lock(&(my_dev->io_lock)); ret_val = pb_dev_outb(my_dev, val, reg); spin_unlock(&(my_dev->io_lock)); if (ret_val!=0) { printk("%s: IO Error, pb_dev_outb returned %d\n",DEVICE_NAME, ret_val); ret_val=-EIO; // make an ordinary io error } } } else if (ioctl_num==IOCTL_INB) { unsigned char reg=ioctl_param&0xFF; //printk("%s: reading register number %02x\n",DEVICE_NAME,0x0ff®); spin_lock(&(my_dev->io_lock)); ret_val = pb_dev_inb(my_dev, reg); spin_unlock(&(my_dev->io_lock)); //printk("%s: found %02x=%02x\n", DEVICE_NAME, 0x0ff®, 0x0ff&val); if (ret_val<0) { printk("%s: IO Error, pb_dev_inb returned %d\n",DEVICE_NAME, ret_val); ret_val=-EIO; // make an ordinary io error } } else { printk("%s: unknown ioctl request number %d\n",DEVICE_NAME, ioctl_num); ret_val=-EINVAL; } return ret_val; } /* Module Declarations */ /* * This structure will hold the functions to be called * when a process does something to the device we * created. Since a pointer to this structure is kept in * the devices table, it can't be local to * init_module. NULL is for unimplemented functions. */ struct file_operations pulseblaster_fops = { .read = device_read, .write = device_write, .unlocked_ioctl = device_ioctl, .open = device_open, .release = device_release, /* a.k.a. close */ }; /* here all the basic pci stuff follows */ /* * no hotpluging after initialisation... */ static int pulseblaster_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { int ret_val; spin_lock(&pb_devs_lock); // check wether device numbers were already allocated if (pb_dev_no_start!=0) { //we are too late spin_unlock(&pb_devs_lock); printk("%s: Sorry, hotpluging not supported!\n", DEVICE_NAME); return -1; } if (pb_dev_no>=pulseblaster_max_devno) { spin_unlock(&pb_devs_lock); printk("%s: Sorry, maximum number of allocatable devices reached !\n", DEVICE_NAME); return -1; } /* do i need this?, what about pci_enable_device_bars() */ ret_val=pci_enable_device(dev); if (ret_val!=0) { spin_unlock(&pb_devs_lock); printk("%s: failed to enable pci device!\n",DEVICE_NAME); return -1; } #ifndef CONFIG_XEN // why is pci_request_region not defined in a XEN kernel? /* exclusive use */ ret_val=pci_request_region(dev, 0, DEVICE_NAME); if (ret_val!=0) { spin_unlock(&pb_devs_lock); printk("%s: failed to enable pci device!\n",DEVICE_NAME); pci_disable_device(dev); return -1; } #else # warning "in XEN version pci_request_region and pci_release_region are not defined, omitting function call" #endif /* initialize the structure */ pb_devs[pb_dev_no].device_open=0; spin_lock_init(&(pb_devs[pb_dev_no].io_lock)); pb_devs[pb_dev_no].pciboard=dev; pb_devs[pb_dev_no].boardtype = id->driver_data; pb_devs[pb_dev_no].base_addr = 0x0; // todo: inform about slots as a hint for the physical location of the board! printk("%s: found a pci board with i/o base address 0x%lx, assigning no %d\n", DEVICE_NAME, (unsigned long)pci_resource_start(dev, 0), pb_dev_no); ++pb_dev_no; spin_unlock(&pb_devs_lock); return SUCCESS; } static void pulseblaster_pci_remove(struct pci_dev* dev) { int i=0; /* for hotplugging, maintain the list in a smart way here: remove only dangling device pointer! */ spin_lock(&pb_devs_lock); for (i=0; i= KERNEL_VERSION(2, 6, 26) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) #define WITH_DRVDATA #endif static struct class *pulseblaster_class = NULL; static void pb_class_setup(void) { /* * create class and register devices in /sys */ pulseblaster_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(pulseblaster_class)) { printk(KERN_ERR "%s: failed to register class", DEVICE_NAME); pulseblaster_class = NULL; } } static void pb_class_destroy(void) { if (pulseblaster_class) class_destroy(pulseblaster_class); pulseblaster_class = NULL; } /** * Add device to our class */ static void pb_class_device_create(dev_t devno, int nr) { struct device *dev; if (pulseblaster_class == NULL) return; if (nr == -1) { dev = device_create(pulseblaster_class, NULL, devno, #ifdef WITH_DRVDATA NULL, #endif DEVICE_NAME"_dbg"); } else { dev = device_create(pulseblaster_class, NULL, devno, #ifdef WITH_DRVDATA NULL, #endif DEVICE_NAME"%d", nr); } if (IS_ERR(dev)) { printk(KERN_WARNING "%s: device_create for %d failed\n", DEVICE_NAME, nr); } } /** * Remove device from class */ static void pb_class_device_destroy(dev_t devno) { if (pulseblaster_class == NULL) return; device_destroy(pulseblaster_class, devno); } #else static void pb_class_setup(void) { printk(KERN_WARNING "%s: No udev support\n", DEVICE_NAME); } static void pb_class_destroy(void) { } static void pb_class_device_create(dev_t devno, int nr) { } static void pb_class_device_destroy(dev_t devno) { } # warning "No class support, hotplug/udev wont wrok" #endif /* * Initialize the module - Register the character device */ static int __init init_pulseblaster_module(void) { int ret_val; int i; int major_dev_num, minor_dev_num; if (base_address!=-1) { printk("%s: no longer supporting 'base_address' parameter\n", DEVICE_NAME); } pb_dev_no_start=0; pb_dev_no=0; // lock for manipulating the device list spin_lock_init(&pb_devs_lock); // always provide a debug device pb_dev_debug.device_open=0; spin_lock_init(&(pb_dev_debug.io_lock)); pb_dev_debug.pciboard=0x0; // this is the debug flag for amcc functions pb_dev_debug.boardtype = PBB_DEBUG; pb_dev_debug.base_addr = 0x0; // register code for pci bus scanning ret_val=pci_register_driver(&pulseblaster_pci_driver); // todo error checking if (ret_val<0) { printk("%s: registering the pci driver failed", DEVICE_NAME); return ret_val; } // get device ids ret_val= alloc_chrdev_region(&pb_dev_no_start, 0, pb_dev_no+1, DEVICE_NAME); major_dev_num=MAJOR(pb_dev_no_start); minor_dev_num=MINOR(pb_dev_no_start); if (ret_val < 0) { printk("%s failed with %d\n", "Sorry, registering the character device failed\n", ret_val); pci_unregister_driver(&pulseblaster_pci_driver); return ret_val; } pb_class_setup(); // register debug device { dev_t devno = MKDEV(major_dev_num, minor_dev_num+pb_dev_no); cdev_init(&(pb_dev_debug.cdev), &pulseblaster_fops); pb_dev_debug.cdev.owner=THIS_MODULE; pb_dev_debug.cdev.ops=&pulseblaster_fops; ret_val=cdev_add(&(pb_dev_debug.cdev), devno, 1); pb_class_device_create(devno, -1); } spin_lock(&pb_devs_lock); for (i=0; i