/***************************************************************************** * USB2000 Kernel Driver * *****************************************************************************/ #include #include #include #include #include #include #include #define DRIVER_VERSION "USB2000 Driver Version 1.04" #define USB2000_MINOR 192 #define USB_VENDOR_ID 0x2457 #define USB_PRODUCT_ID 0x1002 #define USB2000_CMD_INIT 0x01 #define USB2000_CMD_GETSERIALNUMBER 0x08 #define USB2000_SIZEOF_SERIALNUMBER 0x10 #define USB2000_CMD_SETINTEGRATIONTIME 0x02 #define USB2000_SIZEOF_INTEGRATIONTIME 0x00 #define USB2000_CMD_GETSPECTRUM 0x09 #define USB2000_SIZEOF_SPECTRUM 0x1001 #define USB2000_CMD_GETSTATUS 0xfe #define USB2000_SIZEOF_STATUS 0x10 #define USB2000_DEFAULT_INTEGRATION_TIME 450 #define NAK_TIMEOUT HZ #define NDR_TIMEOUT 5 #define EP2 2 #define EP7 7 #define USB2000_MODE_BIN 1 #define USB2000_MODE_ASCII 2 //#define _DEBUG struct s2000_usb_data { struct usb_device *s2000_dev; /* init: probe_s2000 */ unsigned int ifnum; /* Interface number of the USB device */ char present; /* Device is present on the bus */ int integration_time; /* integration time */ unsigned char buffer[4097]; /* data buffer */ unsigned short int data[2048];/* spectral data */ int count; /* data length */ int read; /* data read */ wait_queue_head_t wait_q; /* for timeouts */ unsigned int mode; /* reading mode */ char isopen; }; static struct s2000_usb_data s2000_instance; static ssize_t get_data_from_ep(struct s2000_usb_data *s2000, int size, int ep) { unsigned int count = 0; int this_read = 0; int retval = 0; int nTry = 0; unsigned char *buffer = kmalloc(size, GFP_KERNEL); do { #ifdef _DEBUG info("DBG USB2000 getting %d bytes to clean ep %d", size - this_read, ep); #endif retval = usb_bulk_msg(s2000->s2000_dev, usb_rcvbulkpipe(s2000->s2000_dev, ep), buffer, size - this_read, &count, 10 * HZ); if (retval) { printk("USB2000: Error %d while retrieving data from ep %d\n", retval, ep); return -EIO; } else { #ifdef _DEBUG info("DBG USB2000 got %d bytes from EP %d", count, ep); #endif } nTry++; this_read += count; #ifdef _DEBUG info("DBG USB2000 total readings: %d bytes", this_read); #endif } while ((this_read < size) && (nTry < NDR_TIMEOUT)); kfree(buffer); return retval; } static void initialize(struct s2000_usb_data *s2000) { int size = 1; unsigned char *buffer = kmalloc(size, GFP_KERNEL); int retval = 0; unsigned int count = -1; if (!buffer) { printk("USB2000: Out of memory\n"); return; } *buffer = USB2000_CMD_INIT; retval = usb_bulk_msg(s2000->s2000_dev, usb_sndbulkpipe(s2000->s2000_dev, 2), buffer, 1, &count, 10 * HZ); printk("USB2000 initializing: %d\n", retval); if (retval) { printk("USB2000: Error in initialization\n"); s2000->present = 0; kfree(buffer); return; } kfree(buffer); return; } static ssize_t vendor_command(struct s2000_usb_data *s2000, unsigned char *cmd, int cmd_size, int result_size, int ep) { /* perform a vendor command */ int i, size; unsigned char *buffer; int retval = 0; unsigned int count; size = result_size > cmd_size ? result_size : cmd_size; buffer = kmalloc(size, GFP_KERNEL); memset(buffer, 0, size); #ifdef _DEBUG info("DBG: allocated %d bytes for command 0x%02x", size, (int)cmd[0]); #endif if (!buffer) { printk("USB2000: Out of memory executing command 0x%02x\n", (int)cmd[0]); return -ENOMEM; } for (i = 0; i < cmd_size; i++) { buffer[i] = cmd[0]; } #ifdef _DEBUG info("DBG: performing command 0x%02x", (int)buffer[0]); #endif retval = usb_bulk_msg(s2000->s2000_dev, usb_sndbulkpipe(s2000->s2000_dev, 2), buffer, cmd_size, &count, 10 * HZ); if (retval) { printk("USB2000: Error %d in executing command 0x%02x\n", retval, (int)cmd[0]); kfree(buffer); return -EIO; } #ifdef _DEBUG info("DBG: command executed"); #endif s2000->count = 0; if (result_size > 0) { #ifdef _DEBUG info("DBG: getting result as a list of %d bytes", result_size); #endif retval = usb_bulk_msg(s2000->s2000_dev, usb_rcvbulkpipe(s2000->s2000_dev, ep), buffer, result_size, &count, 10 * HZ); if (retval) { printk("USB2000: Error %d while retrieving data for command 0x%02x " "on pipe %d\n", retval, (int)cmd[0], ep); kfree(buffer); return -EIO; } for (i = 0; i < count; i++) { s2000->buffer[i] = buffer[i]; } s2000->count = count; #ifdef _DEBUG info("DBG: got %d bytes...", count); #endif } kfree(buffer); return retval; } static ssize_t get_status(struct s2000_usb_data *s2000) { int retval = -1; unsigned char cmd = USB2000_CMD_GETSTATUS; retval = vendor_command(s2000, &cmd, 1, USB2000_SIZEOF_STATUS, EP7); return retval; } static int open_s2000(struct inode *inode, struct file *file) { struct s2000_usb_data *s2000 = &s2000_instance; unsigned char cmd = USB2000_CMD_GETSTATUS; int retval = -1; if ((!s2000->present) || (s2000->isopen)) { return -EBUSY; } if (s2000->integration_time == -1) { info("USB2000 not ready: set integration time first"); return -EAGAIN; } info("USB2000 opening..."); /* first of all check that EP2 is clean */ retval = vendor_command(s2000, &cmd, 1, USB2000_SIZEOF_STATUS, EP7); if (s2000->count == 0x10) { if (s2000->buffer[8] != 0) { get_data_from_ep(s2000, USB2000_SIZEOF_SPECTRUM, EP2); } } init_waitqueue_head(&s2000->wait_q); s2000->isopen = 1; s2000->read = 0; #ifdef _DEBUG info("USB2000 now open"); #endif return 0; } static int close_s2000(struct inode *inode, struct file *file) { struct s2000_usb_data *s2000 = &s2000_instance; s2000->isopen = 0; info("USB2000 closed."); return 0; } static ssize_t read_s2000(struct file *file, char __user *buffer, size_t count, loff_t * ppos) { struct s2000_usb_data *s2000 = &s2000_instance; int retval = 0; int copied = 0; int to_copy = 0; int i = 0, j = 0, k = 0, kmax = 0; unsigned char lsb = 0, msb = 0; unsigned char *ascdata; unsigned char tmp[6]; unsigned char cmd = USB2000_CMD_GETSPECTRUM; if (!s2000->isopen) { return -EIO; } #ifdef _DEBUG info("DBG USB2000: data read so far: %d", s2000->read); #endif if (s2000->read == 0) { retval = vendor_command(s2000, &cmd, 1, USB2000_SIZEOF_SPECTRUM, EP2); memset(s2000->data, 0, sizeof(s2000->data)); while (copied < sizeof(s2000->data)) { lsb = s2000->buffer[copied]; msb = s2000->buffer[copied + 64]; s2000->data[i++] = (msb <<8) + lsb; copied++; if (!(copied % 64)) { copied += 64; #ifdef _DEBUG info("DBG USB2000 jumping to %d (last data at channel %d = %d)", copied, i-1, s2000->data[i - 1]); #endif } } } #ifdef _DEBUG info("DBG USB2000 reading mode: %d", s2000->mode); #endif if (s2000->mode == USB2000_MODE_BIN) { to_copy = count < sizeof(s2000->buffer) ? count : sizeof(s2000->buffer); #ifdef _DEBUG info("DBG USB2000 requested %d bytes. Remains %d bytes to copy.", count, to_copy); info("DBG USB2000 copying data from channel %d", s2000->read); #endif copy_to_user((void *)buffer, (void *)&(s2000->data[s2000->read]), to_copy); s2000->read += (to_copy / sizeof(unsigned short int)); } else { ascdata = (unsigned char *)kmalloc(count, GFP_KERNEL); to_copy = 0; k = s2000->read; kmax = s2000->count / sizeof(unsigned short int); #ifdef _DEBUG if (!(k %64)) { info("DBG USB2000: channel %8d value %8d", k, s2000->data[k]); } #endif sprintf(tmp, "%d\n", s2000->data[k++]); j = 0; while (tmp[j] != 0) { put_user(tmp[j++], buffer + to_copy); to_copy++; } s2000->read++; kfree(ascdata); } if (s2000->read >= 2048) { s2000->read = 0; } return to_copy; } static struct file_operations usb_s2000_fops = { .owner = THIS_MODULE, .read = read_s2000, .open = open_s2000, .release = close_s2000, }; static struct usb_class_driver usb_s2000_class = { .name = "usb/s2000%d", .fops = &usb_s2000_fops, .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, .minor_base = USB2000_MINOR, }; static ssize_t show_serial_number(struct device *dev, char *buf) { unsigned char cmd = USB2000_CMD_GETSERIALNUMBER; struct usb_interface *intf = to_usb_interface(dev); struct s2000_usb_data *s2000 = usb_get_intfdata(intf); int retval = vendor_command(s2000, &cmd, 1, USB2000_SIZEOF_SERIALNUMBER, EP7); if (s2000->count > 0) { retval = sprintf(buf, "%s\n", s2000->buffer); s2000->count = 0; } return retval; } static DEVICE_ATTR(serial_number, S_IRUGO, show_serial_number, NULL); static ssize_t show_status(struct device *dev, char *buf) { int retval = -1; struct usb_interface *intf = to_usb_interface(dev); struct s2000_usb_data *s2000 = usb_get_intfdata(intf); retval = get_status(s2000); if (s2000->count == 0x10) { retval = sprintf(buf, "%d\n", s2000->buffer[8]); } return retval; } static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); static ssize_t set_mode(struct device *dev, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct s2000_usb_data *s2000 = usb_get_intfdata(intf); unsigned int mode = simple_strtoul(buf, NULL, 10); if ((mode != USB2000_MODE_BIN) && (mode != USB2000_MODE_ASCII)) { info("USB2000: invalid mode %d", mode); return count; } s2000->mode = mode; return count; } static ssize_t show_mode(struct device *dev, char *buf) { int retval = -1; struct usb_interface *intf = to_usb_interface(dev); struct s2000_usb_data *s2000 = usb_get_intfdata(intf); retval = sprintf(buf, "%d\n", s2000->mode); return retval; } static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, set_mode); static ssize_t show_integration_time(struct device *dev, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct s2000_usb_data *s2000 = usb_get_intfdata(intf); int retval = sprintf(buf, "%d\n", s2000->integration_time); return retval; } static ssize_t set_integration_time(struct device *dev, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct s2000_usb_data *s2000 = usb_get_intfdata(intf); int retval = 0; int int_time = simple_strtoul(buf, NULL, 10); unsigned char * cmd = kmalloc(3, GFP_KERNEL); if (int_time < 3) { int_time = 3; } if (int_time > 65535) { int_time = 65535; } cmd[0] = USB2000_CMD_SETINTEGRATIONTIME; cmd[1] = (unsigned char) (int_time & 0xff); cmd[2] = (unsigned char) ((int_time >> 0x08) & 0xff); retval = vendor_command(s2000, cmd, 0x03, USB2000_SIZEOF_INTEGRATIONTIME, EP7); if (!retval) { s2000->integration_time = int_time; } kfree(cmd); return count; } static DEVICE_ATTR(integration_time, S_IRUGO | S_IWUGO, show_integration_time, set_integration_time); static int probe_s2000(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); struct s2000_usb_data *s2000 = &s2000_instance; int i; int retval; printk("S2000 Probing...\n"); if (s2000->present == 1) { warn(KERN_INFO "Multiple USB2000s are not supported!"); return -ENODEV; } printk("S2000 found...\n"); i = dev->descriptor.bcdDevice; info("USB2000 Version %1d%1d.%1d%1d found at address %d", (i & 0xF000)>>12,(i & 0xF00)>>8,(i & 0xF0)>>4,(i & 0xF), dev->devnum); s2000->present = 1; s2000->isopen = 0; s2000->s2000_dev = dev; s2000->integration_time = 100; s2000->read = 0; s2000->mode = USB2000_MODE_ASCII; retval = usb_register_dev(intf, &usb_s2000_class); if (retval) { err("Not able to get a minor for this device."); return -ENOMEM; } usb_set_intfdata (intf, s2000); initialize(s2000); i = 0; do { info("USB2000 cleaning... status = %d", retval = get_status(s2000)); i++; } while ((retval) && (i < NDR_TIMEOUT)); info("USB2000 status: %d", s2000->buffer[8]); device_create_file(&intf->dev, &dev_attr_serial_number); device_create_file(&intf->dev, &dev_attr_integration_time); device_create_file(&intf->dev, &dev_attr_status); device_create_file(&intf->dev, &dev_attr_mode); return 0; } static void disconnect_s2000(struct usb_interface *intf) { struct s2000_usb_data *s2000 = usb_get_intfdata (intf); s2000->present = 0; device_remove_file(&intf->dev, &dev_attr_serial_number); device_remove_file(&intf->dev, &dev_attr_integration_time); device_remove_file(&intf->dev, &dev_attr_status); device_remove_file(&intf->dev, &dev_attr_mode); usb_set_intfdata (intf, NULL); if (s2000) { usb_deregister_dev(intf, &usb_s2000_class); info("USB2000 disconnected."); s2000->present = 0; } } static struct usb_device_id id_table [] = { { USB_DEVICE(USB_VENDOR_ID, USB_PRODUCT_ID) }, {}, }; MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver s2000_driver = { .owner = THIS_MODULE, .name = "usb2000", .probe = (void *)probe_s2000, .disconnect = disconnect_s2000, .id_table = id_table, }; static int __init usb_s2000_init(void) { int retval; retval = usb_register(&s2000_driver); if (retval) goto out; info("USB2000 support registered."); printk("USB2000 support registered\n"); out: return retval; } static void __exit usb_s2000_cleanup(void) { struct s2000_usb_data *s2000 = &s2000_instance; s2000->present = 0; usb_deregister(&s2000_driver); } module_init(usb_s2000_init); module_exit(usb_s2000_cleanup); MODULE_AUTHOR("Giovanni Organtini (G.Organtini@roma1.infn.it)"); MODULE_DESCRIPTION(DRIVER_VERSION); MODULE_LICENSE("GPL");