···11+config TRANZPORT22+ tristate "Frontier Tranzport and Alphatrack support"33+ depends on USB44+ default N55+ ---help---66+ Enable support for the Frontier Tranzport and Alphatrack devices.
···11+This directory contains the USB Tranzport and Alphatrack Kernel drivers for Linux.22+33+At present the tranzport does reads/writes of 8 byte cmds to /dev/tranzport0 to control44+the lights and screen and wheel55+66+At present the alphatrack accepts reads/writes of 12 byte cmds to /dev/tranzport0 to control77+the lights and screen and fader.88+99+Both drivers also have some sysfs hooks that are non-functional at the moment.1010+1111+The API is currently closely tied to the ardour revision and WILL change.1212+1313+A sysfs interface is PERFECT for simple userspace apps to do fun things with the1414+lights and screen. It's fairly lousy for handling input events and very lousy1515+for watching the state of the shuttle wheel.1616+1717+A linux input events interface is great for the input events and shuttle wheel. It's1818+theoretically OK on LEDs. A Fader can be mapped to an absolute mouse device.1919+But there is no LCD support at all.2020+2121+In the end this is going to be driven by a midi layer, which handles all those2222+cases via a defined API, but - among other things - is slow, doesn't do2323+flow control, and is a LOT of extra work. Frankly, I'd like to keep the2424+core driver simple because the only realtime work really required is2525+the bottom half interrupt handler and the output overlapping.2626+2727+Exposing some sort of clean aio api to userspace would be perfect. What that2828+API looks like? Gah. beats me.
+9
drivers/staging/frontier/TODO
···11+TODO:22+ - checkpatch.pl clean33+ - sparse clean44+ - fix userspace interface to be sane55+ - possibly just port to userspace with libusb66+ - review by the USB developer community77+88+Please send any patches for this driver to Greg Kroah-Hartman <greg@kroah.com>99+and David Taht <d@teklibre.com>.
+901
drivers/staging/frontier/alphatrack.c
···11+/*22+ * Frontier Designs Alphatrack driver33+ *44+ * Copyright (C) 2007 Michael Taht (m@taht.net)55+ *66+ * Based on the usbled driver and ldusb drivers by77+ *88+ * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)99+ * Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de>1010+ *1111+ * The ldusb driver was, in turn, derived from Lego USB Tower driver1212+ * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net>1313+ * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net>1414+ *1515+ * This program is free software; you can redistribute it and/or1616+ * modify it under the terms of the GNU General Public License as1717+ * published by the Free Software Foundation, version 2.1818+ *1919+ */2020+2121+/**2222+ * This driver uses a ring buffer for time critical reading of2323+ * interrupt in reports and provides read and write methods for2424+ * raw interrupt reports.2525+ */2626+2727+/* Note: this currently uses a dumb ringbuffer for reads and writes.2828+ * A more optimal driver would cache and kill off outstanding urbs that are2929+ * now invalid, and ignore ones that already were in the queue but valid3030+ * as we only have 30 commands for the alphatrack. In particular this is3131+ * key for getting lights to flash in time as otherwise many commands3232+ * can be buffered up before the light change makes it to the interface.3333+*/3434+3535+#include <linux/kernel.h>3636+#include <linux/errno.h>3737+#include <linux/init.h>3838+#include <linux/slab.h>3939+#include <linux/module.h>4040+#include <linux/kobject.h>4141+#include <linux/mutex.h>4242+#include <linux/version.h>4343+4444+#include <asm/uaccess.h>4545+#include <linux/input.h>4646+#include <linux/usb.h>4747+#include <linux/poll.h>4848+4949+#include "surface_sysfs.h"5050+5151+/* make this work on older kernel versions */5252+5353+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)5454+#include "frontier_compat.h"5555+#endif /* older kernel versions */5656+5757+#include "alphatrack.h"5858+5959+#define VENDOR_ID 0x165b6060+#define PRODUCT_ID 0xfad16161+6262+#ifdef CONFIG_USB_DYNAMIC_MINORS6363+#define USB_ALPHATRACK_MINOR_BASE 06464+#else6565+// FIXME 176 - is another driver's minor - apply for that6666+// #define USB_ALPHATRACK_MINOR_BASE 1776767+#define USB_ALPHATRACK_MINOR_BASE 1766868+#endif6969+7070+/* table of devices that work with this driver */7171+static struct usb_device_id usb_alphatrack_table [] = {7272+ { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },7373+ { } /* Terminating entry */7474+};7575+7676+MODULE_DEVICE_TABLE(usb, usb_alphatrack_table);7777+MODULE_VERSION("0.40");7878+MODULE_AUTHOR("Mike Taht <m@taht.net>");7979+MODULE_DESCRIPTION("Alphatrack USB Driver");8080+MODULE_LICENSE("GPL");8181+MODULE_SUPPORTED_DEVICE("Frontier Designs Alphatrack Control Surface");8282+8383+/* These aren't done yet */8484+8585+#define ALPHATRACK_HAVE_SYSFS 08686+#define SUPPRESS_EXTRA_ONLINE_EVENTS 08787+#define BUFFERED_WRITES 08888+#define SUPPRESS_EXTRA_OFFLINE_EVENTS 08989+#define COMPRESS_FADER_EVENTS 09090+9191+#define BUFFERED_READS 19292+#define RING_BUFFER_SIZE 5129393+#define WRITE_BUFFER_SIZE 349494+#define ALPHATRACK_USB_TIMEOUT 109595+#define OUTPUT_CMD_SIZE 89696+#define INPUT_CMD_SIZE 129797+9898+9999+static int debug = 0;100100+101101+/* Use our own dbg macro */102102+#define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)103103+104104+#if 0105105+#define alphatrack_ocmd_info(dev, cmd, format, arg...) do { if (debug) ocmd_info(dev , cmd , format, ## arg); } while (0)106106+107107+#define alphatrack_icmd_info(dev, cmd, format, arg...) do { if (debug) icmd_info(dev , cmd, format, ## arg); } while (0)108108+#else109109+#define alphatrack_ocmd_info(dev, cmd, format, arg...)110110+111111+#define alphatrack_icmd_info(dev, cmd, format, arg...)112112+113113+#endif114114+115115+/* Module parameters */116116+117117+module_param(debug, int, S_IRUGO | S_IWUSR);118118+MODULE_PARM_DESC(debug, "Debug enabled or not");119119+120120+/* All interrupt in transfers are collected in a ring buffer to121121+ * avoid racing conditions and get better performance of the driver.122122+ */123123+124124+static int ring_buffer_size = RING_BUFFER_SIZE;125125+126126+module_param(ring_buffer_size, int, S_IRUGO);127127+MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size");128128+129129+/* The write_buffer can one day contain more than one interrupt out transfer.130130+ */131131+132132+static int write_buffer_size = WRITE_BUFFER_SIZE;133133+module_param(write_buffer_size, int, S_IRUGO);134134+MODULE_PARM_DESC(write_buffer_size, "Write buffer size");135135+136136+/*137137+ * Increase the interval for debugging purposes.138138+ * or set to 1 to use the standard interval from the endpoint descriptors.139139+ */140140+141141+static int min_interrupt_in_interval = ALPHATRACK_USB_TIMEOUT;142142+module_param(min_interrupt_in_interval, int, 0);143143+MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms");144144+145145+static int min_interrupt_out_interval = ALPHATRACK_USB_TIMEOUT;146146+module_param(min_interrupt_out_interval, int, 0);147147+MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms");148148+149149+150150+151151+/* Structure to hold all of our device specific stuff */152152+153153+struct usb_alphatrack {154154+ struct semaphore sem; /* locks this structure */155155+ struct usb_interface* intf; /* save off the usb interface pointer */156156+ int open_count; /* number of times this port has been opened */157157+158158+ struct alphatrack_icmd (*ring_buffer)[RING_BUFFER_SIZE]; /* just make c happy */159159+ struct alphatrack_ocmd (*write_buffer)[WRITE_BUFFER_SIZE]; /* just make c happy */160160+ unsigned int ring_head;161161+ unsigned int ring_tail;162162+163163+ wait_queue_head_t read_wait;164164+ wait_queue_head_t write_wait;165165+166166+ unsigned char* interrupt_in_buffer;167167+ unsigned char* oldi_buffer;168168+ struct usb_endpoint_descriptor* interrupt_in_endpoint;169169+ struct urb* interrupt_in_urb;170170+ int interrupt_in_interval;171171+ size_t interrupt_in_endpoint_size;172172+ int interrupt_in_running;173173+ int interrupt_in_done;174174+175175+ char* interrupt_out_buffer;176176+ struct usb_endpoint_descriptor* interrupt_out_endpoint;177177+ struct urb* interrupt_out_urb;178178+ int interrupt_out_interval;179179+ size_t interrupt_out_endpoint_size;180180+ int interrupt_out_busy;181181+182182+ atomic_t writes_pending;183183+ int event; /* alternate interface to events */184184+ int fader; /* 10 bits */185185+ int lights; /* 23 bits */186186+ unsigned char dump_state; /* 0 if disabled 1 if enabled */187187+ unsigned char enable; /* 0 if disabled 1 if enabled */188188+ unsigned char offline; /* if the device is out of range or asleep */189189+ unsigned char verbose; /* be verbose in error reporting */190190+ unsigned char last_cmd[OUTPUT_CMD_SIZE];191191+ unsigned char screen[32];192192+};193193+194194+/* prevent races between open() and disconnect() */195195+static DEFINE_MUTEX(disconnect_mutex);196196+197197+/* forward declaration */198198+199199+static struct usb_driver usb_alphatrack_driver;200200+201201+static void icmd_info(struct usb_alphatrack *dev, char *cmd, char *str, char *a) {202202+/*203203+if (dev->verbose) {204204+} else {205205+}206206+*/207207+}208208+209209+static void ocmd_info(struct usb_alphatrack *dev, char *cmd, char *str, char* a) {210210+/*211211+if (dev->verbose) {212212+} else {213213+}214214+*/215215+}216216+217217+218218+/**219219+ * usb_alphatrack_abort_transfers220220+ * aborts transfers and frees associated data structures221221+ */222222+static void usb_alphatrack_abort_transfers(struct usb_alphatrack *dev)223223+{224224+ /* shutdown transfer */225225+ if (dev->interrupt_in_running) {226226+ dev->interrupt_in_running = 0;227227+ if (dev->intf)228228+ usb_kill_urb(dev->interrupt_in_urb);229229+ }230230+ if (dev->interrupt_out_busy)231231+ if (dev->intf)232232+ usb_kill_urb(dev->interrupt_out_urb);233233+}234234+235235+#if ALPHATRACK_HAVE_SYSFS236236+/* lots and lots and lots of sysfs stuff */237237+/* Currently borked, probably useless */238238+#include "alphatrack_sysfs.c"239239+#endif240240+241241+/**242242+ * usb_alphatrack_delete243243+ */244244+static void usb_alphatrack_delete(struct usb_alphatrack *dev)245245+{246246+ usb_alphatrack_abort_transfers(dev);247247+ usb_free_urb(dev->interrupt_in_urb);248248+ usb_free_urb(dev->interrupt_out_urb);249249+ kfree(dev->ring_buffer);250250+ kfree(dev->interrupt_in_buffer);251251+ kfree(dev->interrupt_out_buffer);252252+ kfree(dev); // fixme oldi_buffer253253+}254254+255255+/**256256+ * usb_alphatrack_interrupt_in_callback257257+ */258258+259259+static void usb_alphatrack_interrupt_in_callback(struct urb *urb)260260+{261261+ struct usb_alphatrack *dev = urb->context;262262+ unsigned int next_ring_head;263263+ int retval = -1;264264+ int *iptr;265265+266266+ if (urb->status) {267267+ if (urb->status == -ENOENT ||268268+ urb->status == -ECONNRESET ||269269+ urb->status == -ESHUTDOWN) {270270+ goto exit;271271+ } else {272272+ dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n",273273+ __FUNCTION__, urb->status);274274+ goto resubmit; /* maybe we can recover */275275+ }276276+ }277277+278278+ if (urb->actual_length != INPUT_CMD_SIZE) {279279+ dev_warn(&dev->intf->dev,280280+ "Urb length was %d bytes!! Do something intelligent \n", urb->actual_length);281281+ } else {282282+ alphatrack_ocmd_info(&dev->intf->dev,&(*dev->ring_buffer)[dev->ring_tail].cmd,"%s", "bla");283283+ if(memcmp(dev->interrupt_in_buffer,dev->oldi_buffer,INPUT_CMD_SIZE)==0) {284284+ goto resubmit;285285+ }286286+ memcpy(dev->oldi_buffer,dev->interrupt_in_buffer,INPUT_CMD_SIZE);287287+288288+#if SUPPRESS_EXTRA_OFFLINE_EVENTS289289+ if(dev->offline == 2 && dev->interrupt_in_buffer[1] == 0xff) { goto resubmit; }290290+ if(dev->offline == 1 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 2; goto resubmit; }291291+/* Always pass one offline event up the stack */292292+ if(dev->offline > 0 && dev->interrupt_in_buffer[1] != 0xff) { dev->offline = 0; }293293+ if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; }294294+#endif295295+ dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail);296296+ next_ring_head = (dev->ring_head+1) % ring_buffer_size;297297+298298+ if (next_ring_head != dev->ring_tail) {299299+ memcpy(&((*dev->ring_buffer)[dev->ring_head]),300300+ dev->interrupt_in_buffer, urb->actual_length);301301+ dev->ring_head = next_ring_head;302302+ retval = 0;303303+ memset(dev->interrupt_in_buffer, 0, urb->actual_length);304304+ } else {305305+ dev_warn(&dev->intf->dev,306306+ "Ring buffer overflow, %d bytes dropped\n",307307+ urb->actual_length);308308+ memset(dev->interrupt_in_buffer, 0, urb->actual_length);309309+ }310310+ }311311+312312+resubmit:313313+ /* resubmit if we're still running */314314+ if (dev->interrupt_in_running && dev->intf) {315315+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);316316+ if (retval)317317+ dev_err(&dev->intf->dev,318318+ "usb_submit_urb failed (%d)\n", retval);319319+ }320320+321321+exit:322322+ dev->interrupt_in_done = 1;323323+ wake_up_interruptible(&dev->read_wait);324324+}325325+326326+/**327327+ * usb_alphatrack_interrupt_out_callback328328+ */329329+static void usb_alphatrack_interrupt_out_callback(struct urb *urb)330330+{331331+ struct usb_alphatrack *dev = urb->context;332332+333333+ /* sync/async unlink faults aren't errors */334334+ if (urb->status && !(urb->status == -ENOENT ||335335+ urb->status == -ECONNRESET ||336336+ urb->status == -ESHUTDOWN))337337+ dbg_info(&dev->intf->dev,338338+ "%s - nonzero write interrupt status received: %d\n",339339+ __FUNCTION__, urb->status);340340+ atomic_dec(&dev->writes_pending);341341+ dev->interrupt_out_busy = 0;342342+ wake_up_interruptible(&dev->write_wait);343343+}344344+345345+/**346346+ * usb_alphatrack_open347347+ */348348+static int usb_alphatrack_open(struct inode *inode, struct file *file)349349+{350350+ struct usb_alphatrack *dev;351351+ int subminor;352352+ int retval = 0;353353+ struct usb_interface *interface;354354+355355+ nonseekable_open(inode, file);356356+ subminor = iminor(inode);357357+358358+ mutex_lock(&disconnect_mutex);359359+360360+ interface = usb_find_interface(&usb_alphatrack_driver, subminor);361361+362362+ if (!interface) {363363+ err("%s - error, can't find device for minor %d\n",364364+ __FUNCTION__, subminor);365365+ retval = -ENODEV;366366+ goto unlock_disconnect_exit;367367+ }368368+369369+ dev = usb_get_intfdata(interface);370370+371371+ if (!dev) {372372+ retval = -ENODEV;373373+ goto unlock_disconnect_exit;374374+ }375375+376376+ /* lock this device */377377+ if (down_interruptible(&dev->sem)) {378378+ retval = -ERESTARTSYS;379379+ goto unlock_disconnect_exit;380380+ }381381+382382+ /* allow opening only once */383383+ if (dev->open_count) {384384+ retval = -EBUSY;385385+ goto unlock_exit;386386+ }387387+ dev->open_count = 1;388388+389389+ /* initialize in direction */390390+ dev->ring_head = 0;391391+ dev->ring_tail = 0;392392+ usb_fill_int_urb(dev->interrupt_in_urb,393393+ interface_to_usbdev(interface),394394+ usb_rcvintpipe(interface_to_usbdev(interface),395395+ dev->interrupt_in_endpoint->bEndpointAddress),396396+ dev->interrupt_in_buffer,397397+ dev->interrupt_in_endpoint_size,398398+ usb_alphatrack_interrupt_in_callback,399399+ dev,400400+ dev->interrupt_in_interval);401401+402402+ dev->interrupt_in_running = 1;403403+ dev->interrupt_in_done = 0;404404+ dev->enable = 1;405405+ dev->offline = 0;406406+407407+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);408408+ if (retval) {409409+ dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval);410410+ dev->interrupt_in_running = 0;411411+ dev->open_count = 0;412412+ goto unlock_exit;413413+ }414414+415415+ /* save device in the file's private structure */416416+ file->private_data = dev;417417+418418+419419+unlock_exit:420420+ up(&dev->sem);421421+422422+unlock_disconnect_exit:423423+ mutex_unlock(&disconnect_mutex);424424+425425+ return retval;426426+}427427+428428+/**429429+ * usb_alphatrack_release430430+ */431431+static int usb_alphatrack_release(struct inode *inode, struct file *file)432432+{433433+ struct usb_alphatrack *dev;434434+ int retval = 0;435435+436436+ dev = file->private_data;437437+438438+ if (dev == NULL) {439439+ retval = -ENODEV;440440+ goto exit;441441+ }442442+443443+ if (down_interruptible(&dev->sem)) {444444+ retval = -ERESTARTSYS;445445+ goto exit;446446+ }447447+448448+ if (dev->open_count != 1) {449449+ retval = -ENODEV;450450+ goto unlock_exit;451451+ }452452+453453+ if (dev->intf == NULL) {454454+ /* the device was unplugged before the file was released */455455+ up(&dev->sem);456456+ /* unlock here as usb_alphatrack_delete frees dev */457457+ usb_alphatrack_delete(dev);458458+ retval = -ENODEV;459459+ goto exit;460460+ }461461+462462+ /* wait until write transfer is finished */463463+ if (dev->interrupt_out_busy)464464+ wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ);465465+ usb_alphatrack_abort_transfers(dev);466466+ dev->open_count = 0;467467+468468+unlock_exit:469469+ up(&dev->sem);470470+471471+exit:472472+ return retval;473473+}474474+475475+/**476476+ * usb_alphatrack_poll477477+ */478478+static unsigned int usb_alphatrack_poll(struct file *file, poll_table *wait)479479+{480480+ struct usb_alphatrack *dev;481481+ unsigned int mask = 0;482482+483483+ dev = file->private_data;484484+485485+ poll_wait(file, &dev->read_wait, wait);486486+ poll_wait(file, &dev->write_wait, wait);487487+488488+ if (dev->ring_head != dev->ring_tail)489489+ mask |= POLLIN | POLLRDNORM;490490+ if (!dev->interrupt_out_busy)491491+ mask |= POLLOUT | POLLWRNORM;492492+493493+ return mask;494494+}495495+496496+/**497497+ * usb_alphatrack_read498498+ */499499+static ssize_t usb_alphatrack_read(struct file *file, char __user *buffer, size_t count,500500+ loff_t *ppos)501501+{502502+ struct usb_alphatrack *dev;503503+ int retval = 0;504504+505505+ int c = 0;506506+507507+ dev = file->private_data;508508+509509+ /* verify that we actually have some data to read */510510+ if (count == 0)511511+ goto exit;512512+513513+ /* lock this object */514514+ if (down_interruptible(&dev->sem)) {515515+ retval = -ERESTARTSYS;516516+ goto exit;517517+ }518518+519519+ /* verify that the device wasn't unplugged */520520+ if (dev->intf == NULL) {521521+ retval = -ENODEV;522522+ err("No device or device unplugged %d\n", retval);523523+ goto unlock_exit;524524+ }525525+526526+ while (dev->ring_head == dev->ring_tail) {527527+ if (file->f_flags & O_NONBLOCK) {528528+ retval = -EAGAIN;529529+ goto unlock_exit;530530+ }531531+ dev->interrupt_in_done = 0 ;532532+ retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done);533533+ if (retval < 0) {534534+ goto unlock_exit;535535+ }536536+ }537537+538538+ alphatrack_ocmd_info(&dev->intf->dev, &(*dev->ring_buffer)[dev->ring_tail].cmd, "%s", ": copying to userspace");539539+540540+ c = 0;541541+ while((c < count) && (dev->ring_tail != dev->ring_head)) {542542+ if (copy_to_user(&buffer[c], &(*dev->ring_buffer)[dev->ring_tail], INPUT_CMD_SIZE)) {543543+ retval = -EFAULT;544544+ goto unlock_exit;545545+ }546546+ dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size;547547+ c+=INPUT_CMD_SIZE;548548+ dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail);549549+ }550550+ retval = c;551551+552552+unlock_exit:553553+ /* unlock the device */554554+ up(&dev->sem);555555+556556+exit:557557+ return retval;558558+}559559+560560+/**561561+ * usb_alphatrack_write562562+ */563563+static ssize_t usb_alphatrack_write(struct file *file, const char __user *buffer,564564+ size_t count, loff_t *ppos)565565+{566566+ struct usb_alphatrack *dev;567567+ size_t bytes_to_write;568568+ int retval = 0;569569+570570+ dev = file->private_data;571571+572572+ /* verify that we actually have some data to write */573573+ if (count == 0)574574+ goto exit;575575+576576+ /* lock this object */577577+ if (down_interruptible(&dev->sem)) {578578+ retval = -ERESTARTSYS;579579+ goto exit;580580+ }581581+582582+ /* verify that the device wasn't unplugged */583583+ if (dev->intf == NULL) {584584+ retval = -ENODEV;585585+ err("No device or device unplugged %d\n", retval);586586+ goto unlock_exit;587587+ }588588+589589+ /* wait until previous transfer is finished */590590+ if (dev->interrupt_out_busy) {591591+ if (file->f_flags & O_NONBLOCK) {592592+ retval = -EAGAIN;593593+ goto unlock_exit;594594+ }595595+ retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy);596596+ if (retval < 0) {597597+ goto unlock_exit;598598+ }599599+ }600600+601601+ /* write the data into interrupt_out_buffer from userspace */602602+ /* FIXME - if you write more than 12 bytes this breaks */603603+ bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size);604604+ if (bytes_to_write < count)605605+ dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write);606606+607607+ dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write);608608+609609+ if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {610610+ retval = -EFAULT;611611+ goto unlock_exit;612612+ }613613+614614+ if (dev->interrupt_out_endpoint == NULL) {615615+ err("Endpoint should not be be null! \n");616616+ goto unlock_exit;617617+ }618618+619619+ /* send off the urb */620620+ usb_fill_int_urb(dev->interrupt_out_urb,621621+ interface_to_usbdev(dev->intf),622622+ usb_sndintpipe(interface_to_usbdev(dev->intf),623623+ dev->interrupt_out_endpoint->bEndpointAddress),624624+ dev->interrupt_out_buffer,625625+ bytes_to_write,626626+ usb_alphatrack_interrupt_out_callback,627627+ dev,628628+ dev->interrupt_out_interval);629629+ dev->interrupt_out_busy = 1;630630+ atomic_inc(&dev->writes_pending);631631+ wmb();632632+633633+ retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);634634+ if (retval) {635635+ dev->interrupt_out_busy = 0;636636+ err("Couldn't submit interrupt_out_urb %d\n", retval);637637+ atomic_dec(&dev->writes_pending);638638+ goto unlock_exit;639639+ }640640+ retval = bytes_to_write;641641+642642+unlock_exit:643643+ /* unlock the device */644644+ up(&dev->sem);645645+646646+exit:647647+ return retval;648648+}649649+650650+/* file operations needed when we register this driver */651651+static const struct file_operations usb_alphatrack_fops = {652652+ .owner = THIS_MODULE,653653+ .read = usb_alphatrack_read,654654+ .write = usb_alphatrack_write,655655+ .open = usb_alphatrack_open,656656+ .release = usb_alphatrack_release,657657+ .poll = usb_alphatrack_poll,658658+};659659+660660+/*661661+ * usb class driver info in order to get a minor number from the usb core,662662+ * and to have the device registered with the driver core663663+ */664664+665665+static struct usb_class_driver usb_alphatrack_class = {666666+ .name = "alphatrack%d",667667+ .fops = &usb_alphatrack_fops,668668+ .minor_base = USB_ALPHATRACK_MINOR_BASE,669669+};670670+671671+672672+/**673673+ * usb_alphatrack_probe674674+ *675675+ * Called by the usb core when a new device is connected that it thinks676676+ * this driver might be interested in.677677+ */678678+static int usb_alphatrack_probe(struct usb_interface *intf, const struct usb_device_id *id)679679+{680680+ struct usb_device *udev = interface_to_usbdev(intf);681681+ struct usb_alphatrack *dev = NULL;682682+ struct usb_host_interface *iface_desc;683683+ struct usb_endpoint_descriptor *endpoint;684684+ int i;685685+ int true_size;686686+ int retval = -ENOMEM;687687+688688+ /* allocate memory for our device state and intialize it */689689+690690+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);691691+ if (dev == NULL) {692692+ dev_err(&intf->dev, "Out of memory\n");693693+ goto exit;694694+ }695695+ init_MUTEX(&dev->sem);696696+ dev->intf = intf;697697+ init_waitqueue_head(&dev->read_wait);698698+ init_waitqueue_head(&dev->write_wait);699699+700700+ iface_desc = intf->cur_altsetting;701701+702702+ /* set up the endpoint information */703703+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {704704+ endpoint = &iface_desc->endpoint[i].desc;705705+706706+ if (usb_endpoint_is_int_in(endpoint))707707+ dev->interrupt_in_endpoint = endpoint;708708+709709+ if (usb_endpoint_is_int_out(endpoint))710710+ dev->interrupt_out_endpoint = endpoint;711711+ }712712+ if (dev->interrupt_in_endpoint == NULL) {713713+ dev_err(&intf->dev, "Interrupt in endpoint not found\n");714714+ goto error;715715+ }716716+ if (dev->interrupt_out_endpoint == NULL)717717+ dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");718718+719719+ dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);720720+721721+ if (dev->interrupt_in_endpoint_size != 64)722722+ dev_warn(&intf->dev, "Interrupt in endpoint size is not 64!\n");723723+724724+ if(ring_buffer_size == 0) { ring_buffer_size = RING_BUFFER_SIZE; }725725+726726+ true_size = min(ring_buffer_size,RING_BUFFER_SIZE);727727+728728+ /* FIXME - there are more usb_alloc routines for dma correctness. Needed? */729729+730730+// dev->ring_buffer = kmalloc((true_size*sizeof(struct alphatrack_icmd))+12, GFP_KERNEL);731731+ dev->ring_buffer = kmalloc((true_size*sizeof(struct alphatrack_icmd)), GFP_KERNEL);732732+733733+ if (!dev->ring_buffer) {734734+ dev_err(&intf->dev, "Couldn't allocate input ring_buffer of size %d\n",true_size);735735+ goto error;736736+ }737737+738738+ dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);739739+740740+ if (!dev->interrupt_in_buffer) {741741+ dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n");742742+ goto error;743743+ }744744+ dev->oldi_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);745745+ if (!dev->oldi_buffer) {746746+ dev_err(&intf->dev, "Couldn't allocate old buffer\n");747747+ goto error;748748+ }749749+ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);750750+ if (!dev->interrupt_in_urb) {751751+ dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n");752752+ goto error;753753+ }754754+755755+ dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) :756756+ udev->descriptor.bMaxPacketSize0;757757+758758+ if (dev->interrupt_out_endpoint_size !=64)759759+ dev_warn(&intf->dev, "Interrupt out endpoint size is not 64!)\n");760760+761761+ if(write_buffer_size == 0) { write_buffer_size = WRITE_BUFFER_SIZE; }762762+ true_size = min(write_buffer_size,WRITE_BUFFER_SIZE);763763+764764+ dev->interrupt_out_buffer = kmalloc(true_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);765765+766766+ if (!dev->interrupt_out_buffer) {767767+ dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n");768768+ goto error;769769+ }770770+771771+ dev->write_buffer = kmalloc(sizeof(struct alphatrack_ocmd)*true_size, GFP_KERNEL);772772+773773+ if (!dev->write_buffer) {774774+ dev_err(&intf->dev, "Couldn't allocate write_buffer \n");775775+ goto error;776776+ }777777+778778+ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);779779+ if (!dev->interrupt_out_urb) {780780+ dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n");781781+ goto error;782782+ }783783+ dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;784784+ if (dev->interrupt_out_endpoint)785785+ dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;786786+787787+ /* we can register the device now, as it is ready */788788+ usb_set_intfdata(intf, dev);789789+790790+ atomic_set(&dev->writes_pending,0);791791+ retval = usb_register_dev(intf, &usb_alphatrack_class);792792+ if (retval) {793793+ /* something prevented us from registering this driver */794794+ dev_err(&intf->dev, "Not able to get a minor for this device.\n");795795+ usb_set_intfdata(intf, NULL);796796+ goto error;797797+ }798798+799799+ /* let the user know what node this device is now attached to */800800+ dev_info(&intf->dev, "Alphatrack Device #%d now attached to major %d minor %d\n",801801+ (intf->minor - USB_ALPHATRACK_MINOR_BASE), USB_MAJOR, intf->minor);802802+803803+#if ALPHATRACK_HAVE_SYSFS804804+ if((retval = device_create_file(&intf->dev, &dev_attr_event))) goto error;805805+ if((retval = device_create_file(&intf->dev, &dev_attr_dump_state))) goto error;806806+ if((retval = device_create_file(&intf->dev, &dev_attr_enable))) goto error;807807+ if((retval = device_create_file(&intf->dev, &dev_attr_offline))) goto error;808808+809809+ /* exercise sysfs */810810+811811+ set_lights("32767"); // turn on all the lights812812+ set_fader0("1023"); // Move fader to max813813+ set_screen("INITIALIZING ALPHATRACK...");814814+ set_lights("0");815815+ set_fader0("0");816816+ set_screen(" ");817817+818818+#endif819819+820820+exit:821821+ return retval;822822+823823+error:824824+ usb_alphatrack_delete(dev);825825+826826+ return retval;827827+}828828+829829+/**830830+ * usb_alphatrack_disconnect831831+ *832832+ * Called by the usb core when the device is removed from the system.833833+ */834834+static void usb_alphatrack_disconnect(struct usb_interface *intf)835835+{836836+ struct usb_alphatrack *dev;837837+ int minor;838838+839839+ mutex_lock(&disconnect_mutex);840840+841841+ dev = usb_get_intfdata(intf);842842+ usb_set_intfdata(intf, NULL);843843+844844+ down(&dev->sem);845845+846846+ minor = intf->minor;847847+848848+ /* give back our minor */849849+ usb_deregister_dev(intf, &usb_alphatrack_class);850850+851851+ /* if the device is not opened, then we clean up right now */852852+ if (!dev->open_count) {853853+ up(&dev->sem);854854+ usb_alphatrack_delete(dev);855855+ } else {856856+ dev->intf = NULL;857857+ up(&dev->sem);858858+ }859859+860860+ atomic_set(&dev->writes_pending,0);861861+ mutex_unlock(&disconnect_mutex);862862+863863+ dev_info(&intf->dev, "Alphatrack Surface #%d now disconnected\n",864864+ (minor - USB_ALPHATRACK_MINOR_BASE));865865+}866866+867867+/* usb specific object needed to register this driver with the usb subsystem */868868+static struct usb_driver usb_alphatrack_driver = {869869+ .name = "alphatrack",870870+ .probe = usb_alphatrack_probe,871871+ .disconnect = usb_alphatrack_disconnect,872872+ .id_table = usb_alphatrack_table,873873+};874874+875875+/**876876+ * usb_alphatrack_init877877+ */878878+static int __init usb_alphatrack_init(void)879879+{880880+ int retval;881881+882882+ /* register this driver with the USB subsystem */883883+ retval = usb_register(&usb_alphatrack_driver);884884+ if (retval)885885+ err("usb_register failed for the "__FILE__" driver. Error number %d\n", retval);886886+887887+ return retval;888888+}889889+890890+/**891891+ * usb_alphatrack_exit892892+ */893893+static void __exit usb_alphatrack_exit(void)894894+{895895+ /* deregister this driver with the USB subsystem */896896+ usb_deregister(&usb_alphatrack_driver);897897+}898898+899899+module_init(usb_alphatrack_init);900900+module_exit(usb_alphatrack_exit);901901+
···11+/* USB defines for older kernels */22+33+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)44+55+/**66+ * usb_endpoint_dir_out - check if the endpoint has OUT direction77+ * @epd: endpoint to be checked88+ *99+ * Returns true if the endpoint is of type OUT, otherwise it returns false.1010+ */1111+1212+static inline int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)1313+{1414+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);1515+}1616+1717+static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)1818+{1919+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);2020+}2121+2222+2323+/**2424+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type2525+ * @epd: endpoint to be checked2626+ *2727+ * Returns true if the endpoint is of type interrupt, otherwise it returns2828+ * false.2929+ */3030+static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)3131+{3232+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==3333+ USB_ENDPOINT_XFER_INT);3434+}3535+3636+3737+/**3838+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN3939+ * @epd: endpoint to be checked4040+ *4141+ * Returns true if the endpoint has interrupt transfer type and IN direction,4242+ * otherwise it returns false.4343+ */4444+4545+static inline int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)4646+{4747+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));4848+}4949+5050+/**5151+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT5252+ * @epd: endpoint to be checked5353+ *5454+ * Returns true if the endpoint has interrupt transfer type and OUT direction,5555+ * otherwise it returns false.5656+ */5757+5858+static inline int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)5959+{6060+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));6161+}6262+6363+#endif /* older kernel versions */