]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
Staging: add princeton instruments usb camera driver
authorGreg Kroah-Hartman <gregkh@suse.de>
Thu, 21 Aug 2008 21:04:55 +0000 (14:04 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 6 Jan 2009 21:52:34 +0000 (13:52 -0800)
Adds the driver for the Princeton Instruments USB camera.

Needs a lot of work...

TODO:
- make checkpatch.pl clean
- coding style fixups (typedefs, etc.)
- get it to build properly
- audit ioctls
- remove ioctls if possible
- assign proper minor number
- remove dbg() macro
- lots of general cleanups
- review locking

Cc: Judd Montgomery <judd@jpilot.org>
Cc: Jeff Frontz <jeff.frontz@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/rspiusb/Kconfig [new file with mode: 0644]
drivers/staging/rspiusb/Makefile [new file with mode: 0644]
drivers/staging/rspiusb/TODO [new file with mode: 0644]
drivers/staging/rspiusb/rspiusb.c [new file with mode: 0644]
drivers/staging/rspiusb/rspiusb.h [new file with mode: 0644]

index 53404a4018fe88660de87b21b677353e9acfd7bf..fe3b23e435675520dea87eba59b00ef69208e2cb 100644 (file)
@@ -83,5 +83,7 @@ source "drivers/staging/altpciechdma/Kconfig"
 
 source "drivers/staging/rtl8187se/Kconfig"
 
+source "drivers/staging/rspiusb/Kconfig"
+
 endif # !STAGING_EXCLUDE_BUILD
 endif # STAGING
index 170789f3c4ca6ffd77f59dd02bd2925f4ebf0374..bd4cb9285a2f66f6ce007280aad7d4b179727fe1 100644 (file)
@@ -24,3 +24,4 @@ obj-$(CONFIG_ASUS_OLED)               += asus_oled/
 obj-$(CONFIG_PANEL)            += panel/
 obj-$(CONFIG_ALTERA_PCIE_CHDMA)        += altpciechdma/
 obj-$(CONFIG_RTL8187SE)                += rtl8187se/
+obj-$(CONFIG_USB_RSPI)         += rspiusb/
diff --git a/drivers/staging/rspiusb/Kconfig b/drivers/staging/rspiusb/Kconfig
new file mode 100644 (file)
index 0000000..d225f67
--- /dev/null
@@ -0,0 +1,6 @@
+config USB_RSPI
+       tristate "Princeton Instruments USB camera support"
+       default n
+       depends on USB && BROKEN
+       help
+         This driver is for the Princeton Instruments USB camera device.
diff --git a/drivers/staging/rspiusb/Makefile b/drivers/staging/rspiusb/Makefile
new file mode 100644 (file)
index 0000000..cc7aed9
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_USB_RSPI)         += rspiusb.o
diff --git a/drivers/staging/rspiusb/TODO b/drivers/staging/rspiusb/TODO
new file mode 100644 (file)
index 0000000..cd6336a
--- /dev/null
@@ -0,0 +1,22 @@
+This driver is for the Princeton Instruments USB camera.
+
+It needs lots of work to get it into the main drivers/usb/ subdirectory:
+
+Any patches to do any of the following changes are greatly appreciated:
+
+       - make checkpatch.pl clean
+       - coding style fixups (typedefs, etc.)
+       - get it to build properly
+       - audit ioctls
+       - remove ioctls if possible
+       - assign proper minor number
+       - remove dbg() macro
+       - lots of general cleanups
+       - review locking
+
+Please send patches to:
+       Greg Kroah-Hartman <gregkh@suse.de>
+and CC:
+       Judd Montgomery <judd@jpilot.org>
+       Jeff Frontz <jeff.frontz@gmail.com>
+as they have this device and can test any needed changes.
diff --git a/drivers/staging/rspiusb/rspiusb.c b/drivers/staging/rspiusb/rspiusb.c
new file mode 100644 (file)
index 0000000..ca281d6
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ * rspiusb.c
+ *
+ * Copyright (C) 2005, 2006 Princeton Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <linux/scatterlist.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/ioctl.h>
+#include "rspiusb.h"
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0)
+
+/* Version Information */
+#define DRIVER_VERSION "V1.0.1"
+#define DRIVER_AUTHOR  "Princeton Instruments"
+#define DRIVER_DESC    "PI USB2.0 Device Driver for Linux"
+
+/* Define these values to match your devices */
+#define VENDOR_ID   0x0BD7
+#define ST133_PID   0xA010
+#define PIXIS_PID   0xA026
+
+/* Get a minor range for your devices from the usb maintainer */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define PIUSB_MINOR_BASE    0
+#else
+#define PIUSB_MINOR_BASE    192
+#endif
+
+/* prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+/* Structure to hold all of our device specific stuff */
+struct device_extension {
+       struct usb_device *udev;        /* save off the usb device pointer */
+       struct usb_interface *interface;        /* the interface for this device */
+       unsigned char minor;    /* the starting minor number for this device */
+       size_t bulk_in_size_returned;
+       int bulk_in_byte_trk;
+       struct urb ***PixelUrb;
+       int frameIdx;
+       int urbIdx;
+       unsigned int *maplist_numPagesMapped;
+       int open;               /* if the port is open or not */
+       int present;            /* if the device is not disconnected */
+       int userBufMapped;      /* has the user buffer been mapped? */
+       struct scatterlist **sgl;       /* scatter-gather list for user buffer */
+       unsigned int *sgEntries;
+       struct kref kref;
+       int gotPixelData;
+       int pendingWrite;
+       char **pendedPixelUrbs;
+       int iama;               /*PIXIS or ST133 */
+       int num_frames;         /* the number of frames that will fit in the user buffer */
+       int active_frame;
+       unsigned long frameSize;
+       struct semaphore sem;
+       //FX2 specific endpoints
+       unsigned int hEP[8];
+};
+#define to_pi_dev(d) container_of( d, struct device_extension, kref )
+
+static int MapUserBuffer(struct ioctl_struct *, struct device_extension *);
+static int UnMapUserBuffer(struct device_extension *);
+static int piusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                      unsigned long arg);
+static int piusb_output(struct ioctl_struct *, unsigned char *, int, struct device_extension *);
+static struct usb_driver piusb_driver;
+
+/* table of devices that work with this driver */
+static struct usb_device_id pi_device_table[] = {
+       {USB_DEVICE(VENDOR_ID, ST133_PID)},
+       {USB_DEVICE(VENDOR_ID, PIXIS_PID)},
+       {0, }                   /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, pi_device_table);
+
+static int lastErr = 0;
+static int errCnt = 0;
+
+static void piusb_delete(struct kref *kref)
+{
+       struct device_extension *pdx = to_pi_dev(kref);
+
+       dev_dbg(&pdx->udev->dev, "%s\n", __func__);
+       usb_put_dev(pdx->udev);
+       kfree(pdx);
+}
+
+static int piusb_open(struct inode *inode, struct file *file)
+{
+       struct device_extension *pdx = NULL;
+       struct usb_interface *interface;
+       int subminor;
+       int retval = 0;
+
+       dbg("Piusb_Open()");
+       subminor = iminor(inode);
+       interface = usb_find_interface(&piusb_driver, subminor);
+       if (!interface) {
+               printk(KERN_ERR "%s - error, can't find device for minor %d\n",
+                      __func__, subminor);
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       pdx = usb_get_intfdata(interface);
+       if (!pdx) {
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+       dbg("Alternate Setting = %d", interface->num_altsetting);
+
+       pdx->frameIdx = pdx->urbIdx = 0;
+       pdx->gotPixelData = 0;
+       pdx->pendingWrite = 0;
+       pdx->frameSize = 0;
+       pdx->num_frames = 0;
+       pdx->active_frame = 0;
+       pdx->bulk_in_byte_trk = 0;
+       pdx->userBufMapped = 0;
+       pdx->pendedPixelUrbs = NULL;
+       pdx->sgEntries = NULL;
+       pdx->sgl = NULL;
+       pdx->maplist_numPagesMapped = NULL;
+       pdx->PixelUrb = NULL;
+       pdx->bulk_in_size_returned = 0;
+       /* increment our usage count for the device */
+       kref_get(&pdx->kref);
+       /* save our object in the file's private structure */
+       file->private_data = pdx;
+      exit_no_device:
+       return retval;
+}
+
+static int piusb_release(struct inode *inode, struct file *file)
+{
+       struct device_extension *pdx;
+       int retval = 0;
+
+       dbg("Piusb_Release()");
+       pdx = (struct device_extension *)file->private_data;
+       if (pdx == NULL) {
+               dbg("%s - object is NULL", __func__);
+               return -ENODEV;
+       }
+       /* decrement the count on our device */
+       kref_put(&pdx->kref, piusb_delete);
+       return retval;
+}
+
+/**
+ *     piusb_ioctl
+ */
+static int piusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                      unsigned long arg)
+{
+       struct device_extension *pdx;
+       char dummyCtlBuf[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+       unsigned long devRB = 0;
+       int i = 0;
+       int err = 0;
+       int retval = 0;
+       struct ioctl_struct ctrl;
+       unsigned char *uBuf;
+       int numbytes = 0;
+       unsigned short controlData = 0;
+
+       pdx = (struct device_extension *)file->private_data;
+       /* verify that the device wasn't unplugged */
+       if (!pdx->present) {
+               dbg("No Device Present\n");
+               return -ENODEV;
+       }
+       /* fill in your device specific stuff here */
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
+       else if (_IOC_DIR(cmd) & _IOC_WRITE)
+               err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
+       if (err) {
+               dev_err(&pdx->udev->dev, "return with error = %d\n", err);
+               return -EFAULT;
+       }
+       switch (cmd) {
+       case PIUSB_GETVNDCMD:
+               if (copy_from_user
+                   (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct)))
+                       info("copy_from_user failed\n");
+               dbg("%s %x\n", "Get Vendor Command = ", ctrl.cmd);
+               retval =
+                   usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+                                   ctrl.cmd, USB_DIR_IN, 0, 0, &devRB,
+                                   ctrl.numbytes, HZ * 10);
+               if (ctrl.cmd == 0xF1) {
+                       dbg("FW Version returned from HW = %ld.%ld",
+                           (devRB >> 8), (devRB & 0xFF));
+               }
+               return devRB;
+       case PIUSB_SETVNDCMD:
+               if (copy_from_user
+                   (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct)))
+                       info("copy_from_user failed\n");
+//            dbg( "%s %x", "Set Vendor Command = ",ctrl.cmd );
+               controlData = ctrl.pData[0];
+               controlData |= (ctrl.pData[1] << 8);
+//            dbg( "%s %d", "Vendor Data =",controlData );
+               retval = usb_control_msg(pdx->udev, usb_sndctrlpipe(pdx->udev, 0), ctrl.cmd, (USB_DIR_OUT | USB_TYPE_VENDOR),   /* | USB_RECIP_ENDPOINT), */
+                                        controlData,
+                                        0,
+                                        &dummyCtlBuf, ctrl.numbytes, HZ * 10);
+               return retval;
+               break;
+       case PIUSB_ISHIGHSPEED:
+               return ((pdx->udev->speed == USB_SPEED_HIGH) ? 1 : 0);
+               break;
+       case PIUSB_WRITEPIPE:
+               if (copy_from_user(&ctrl, (void __user *)arg, _IOC_SIZE(cmd)))
+                       info("copy_from_user WRITE_DUMMY failed\n");
+               if (!access_ok(VERIFY_READ, ctrl.pData, ctrl.numbytes)) {
+                       dbg("can't access pData");
+                       return 0;
+               }
+               piusb_output(&ctrl, ctrl.pData /*uBuf */ , ctrl.numbytes, pdx);
+               return ctrl.numbytes;
+               break;
+       case PIUSB_USERBUFFER:
+               if (copy_from_user
+                   (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct)))
+                       info("copy_from_user failed\n");
+               return MapUserBuffer((struct ioctl_struct *) & ctrl, pdx);
+               break;
+       case PIUSB_UNMAP_USERBUFFER:
+               UnMapUserBuffer(pdx);
+               return 0;
+               break;
+       case PIUSB_READPIPE:
+               if (copy_from_user
+                   (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct)))
+                       info("copy_from_user failed\n");
+               switch (ctrl.endpoint) {
+               case 0: //ST133 Pixel Data or PIXIS IO
+                       if (pdx->iama == PIXIS_PID) {
+                               unsigned int numToRead = 0;
+                               unsigned int totalRead = 0;
+                               uBuf = kmalloc(ctrl.numbytes, GFP_KERNEL);
+                               if (!uBuf) {
+                                       dbg("Alloc for uBuf failed");
+                                       return 0;
+                               }
+                               numbytes = ctrl.numbytes;
+                               numToRead = numbytes;
+                               dbg("numbytes to read = %d", numbytes);
+                               dbg("endpoint # %d", ctrl.endpoint);
+                               if (copy_from_user(uBuf, ctrl.pData, numbytes))
+                                       dbg("copying ctrl.pData to dummyBuf failed");
+                               do {
+                                       i = usb_bulk_msg(pdx->udev, pdx->hEP[ctrl.endpoint], (uBuf + totalRead), (numToRead > 64) ? 64 : numToRead, &numbytes, HZ * 10);        //EP0 can only handle 64 bytes at a time
+                                       if (i) {
+                                               dbg("CMD = %s, Address = 0x%02X", ((uBuf[3] == 0x02) ? "WRITE" : "READ"), uBuf[1]);
+                                               dbg("Number of bytes Attempted to read = %d", (int)ctrl.numbytes);
+                                               dbg("Blocking ReadI/O Failed with status %d", i);
+                                               kfree(uBuf);
+                                               return -1;
+                                       } else {
+                                               dbg("Pixis EP0 Read %d bytes",
+                                                   numbytes);
+                                               totalRead += numbytes;
+                                               numToRead -= numbytes;
+                                       }
+                               }
+                               while (numToRead);
+                               memcpy(ctrl.pData, uBuf, totalRead);
+                               dbg("Total Bytes Read from PIXIS EP0 = %d",
+                                   totalRead);
+                               ctrl.numbytes = totalRead;
+                               if (copy_to_user
+                                   ((struct ioctl_struct *) arg, &ctrl,
+                                    sizeof(struct ioctl_struct)))
+                                       dbg("copy_to_user failed in IORB");
+                               kfree(uBuf);
+                               return ctrl.numbytes;
+                       } else  //ST133 Pixel Data
+                       {
+                               if (!pdx->gotPixelData)
+                                       return 0;
+                               else {
+                                       pdx->gotPixelData = 0;
+                                       ctrl.numbytes =
+                                           pdx->bulk_in_size_returned;
+                                       pdx->bulk_in_size_returned -=
+                                           pdx->frameSize;
+                                       for (i = 0; i < pdx->maplist_numPagesMapped[pdx->active_frame]; i++)
+                                               SetPageDirty(pdx->sgl[pdx->active_frame][i].page_link);
+                                       pdx->active_frame =
+                                           ((pdx->active_frame +
+                                             1) % pdx->num_frames);
+                                       return ctrl.numbytes;
+                               }
+                       }
+                       break;
+               case 1: //ST133IO
+               case 4: //PIXIS IO
+                       uBuf = kmalloc(ctrl.numbytes, GFP_KERNEL);
+                       if (!uBuf) {
+                               dbg("Alloc for uBuf failed");
+                               return 0;
+                       }
+                       numbytes = ctrl.numbytes;
+//                                      dbg( "numbytes to read = %d", numbytes );
+                       if (copy_from_user(uBuf, ctrl.pData, numbytes))
+                               dbg("copying ctrl.pData to dummyBuf failed");
+                       i = usb_bulk_msg(pdx->udev, pdx->hEP[ctrl.endpoint],
+                                        uBuf, numbytes, &numbytes, HZ * 10);
+                       if (i) {
+                               dbg("Blocking ReadI/O Failed with status %d",
+                                   i);
+                               kfree(uBuf);
+                               return -1;
+                       } else {
+                               ctrl.numbytes = numbytes;
+                               memcpy(ctrl.pData, uBuf, numbytes);
+                               if (copy_to_user
+                                   ((struct ioctl_struct *) arg, &ctrl,
+                                    sizeof(struct ioctl_struct)))
+                                       dbg("copy_to_user failed in IORB");
+                               kfree(uBuf);
+                               return ctrl.numbytes;
+                       }
+                       break;
+
+               case 2: //PIXIS Ping
+               case 3: //PIXIS Pong
+                       if (!pdx->gotPixelData)
+                               return 0;
+                       else {
+                               pdx->gotPixelData = 0;
+                               ctrl.numbytes = pdx->bulk_in_size_returned;
+                               pdx->bulk_in_size_returned -= pdx->frameSize;
+                               for (i = 0;
+                                    i <
+                                    pdx->maplist_numPagesMapped[pdx->
+                                                                active_frame];
+                                    i++)
+                                       SetPageDirty(pdx->sgl[pdx->active_frame][i].page_link);
+                               pdx->active_frame =
+                                   ((pdx->active_frame + 1) % pdx->num_frames);
+                               return ctrl.numbytes;
+                       }
+                       break;
+               }
+               break;
+       case PIUSB_WHATCAMERA:
+               return pdx->iama;
+       case PIUSB_SETFRAMESIZE:
+               dbg("PIUSB_SETFRAMESIZE");
+               if (copy_from_user
+                   (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct)))
+                       info("copy_from_user failed\n");
+               pdx->frameSize = ctrl.numbytes;
+               pdx->num_frames = ctrl.numFrames;
+               if (!pdx->sgl)
+                       pdx->sgl =
+                           kmalloc(sizeof(struct scatterlist *) *
+                                   pdx->num_frames, GFP_KERNEL);
+               if (!pdx->sgEntries)
+                       pdx->sgEntries =
+                           kmalloc(sizeof(unsigned int) * pdx->num_frames,
+                                   GFP_KERNEL);
+               if (!pdx->PixelUrb)
+                       pdx->PixelUrb =
+                           kmalloc(sizeof(struct urb **) * pdx->num_frames,
+                                   GFP_KERNEL);
+               if (!pdx->maplist_numPagesMapped)
+                       pdx->maplist_numPagesMapped =
+                           vmalloc(sizeof(unsigned int) * pdx->num_frames);
+               if (!pdx->pendedPixelUrbs)
+                       pdx->pendedPixelUrbs =
+                           kmalloc(sizeof(char *) * pdx->num_frames,
+                                   GFP_KERNEL);
+               return 0;
+       default:
+               dbg("%s\n", "No IOCTL found");
+               break;
+
+       }
+       /* return that we did not understand this ioctl call */
+       dbg("Returning -ENOTTY");
+       return -ENOTTY;
+}
+
+static void piusb_write_bulk_callback(struct urb *urb)
+{
+       struct device_extension *pdx = urb->context;
+       int status = urb->status;
+
+       /* sync/async unlink faults aren't errors */
+       if (status && !(status == -ENOENT || status == -ECONNRESET))
+               dev_dbg(&urb->dev->dev,
+                       "%s - nonzero write bulk status received: %d",
+                       __func__, status);
+
+       pdx->pendingWrite = 0;
+       usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+                       urb->transfer_buffer, urb->transfer_dma);
+}
+
+int piusb_output(struct ioctl_struct * io, unsigned char *uBuf, int len,
+                struct device_extension *pdx)
+{
+       struct urb *urb = NULL;
+       int err = 0;
+       unsigned char *kbuf = NULL;
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (urb != NULL) {
+               kbuf =
+                   usb_buffer_alloc(pdx->udev, len, GFP_KERNEL,
+                                    &urb->transfer_dma);
+               if (!kbuf) {
+                       info("buffer_alloc failed\n");
+                       return -ENOMEM;
+               }
+               memcpy(kbuf, uBuf, len);
+               usb_fill_bulk_urb(urb, pdx->udev, pdx->hEP[io->endpoint], kbuf,
+                                 len, piusb_write_bulk_callback, pdx);
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               err = usb_submit_urb(urb, GFP_KERNEL);
+               if (err) {
+                       dev_err(&pdx->udev->dev,
+                               "WRITE ERROR:submit urb error = %d\n", err);
+               }
+               pdx->pendingWrite = 1;
+               usb_free_urb(urb);
+       }
+       return -EINPROGRESS;
+}
+
+static int UnMapUserBuffer(struct device_extension *pdx)
+{
+       int i = 0;
+       int k = 0;
+       unsigned int epAddr;
+       for (k = 0; k < pdx->num_frames; k++) {
+               dbg("Killing Urbs for Frame %d", k);
+               for (i = 0; i < pdx->sgEntries[k]; i++) {
+                       usb_kill_urb(pdx->PixelUrb[k][i]);
+                       usb_free_urb(pdx->PixelUrb[k][i]);
+                       pdx->pendedPixelUrbs[k][i] = 0;
+               }
+               dbg("Urb error count = %d", errCnt);
+               errCnt = 0;
+               dbg("Urbs free'd and Killed for Frame %d", k);
+       }
+
+       for (k = 0; k < pdx->num_frames; k++) {
+               if (pdx->iama == PIXIS_PID)     //if so, which EP should we map this frame to
+               {
+                       if (k % 2)      //check to see if this should use EP4(PONG)
+                       {
+                               epAddr = pdx->hEP[3];   //PONG, odd frames
+                       } else {
+                               epAddr = pdx->hEP[2];   //PING, even frames and zero
+                       }
+               } else          //ST133 only has 1 endpoint for Pixel data transfer
+               {
+                       epAddr = pdx->hEP[0];
+               }
+               usb_buffer_unmap_sg(pdx->udev, epAddr, pdx->sgl[k],
+                                   pdx->maplist_numPagesMapped[k]);
+               for (i = 0; i < pdx->maplist_numPagesMapped[k]; i++) {
+                       page_cache_release(pdx->sgl[k][i].page_link);
+               }
+               kfree(pdx->sgl[k]);
+               kfree(pdx->PixelUrb[k]);
+               kfree(pdx->pendedPixelUrbs[k]);
+               pdx->sgl[k] = NULL;
+               pdx->PixelUrb[k] = NULL;
+               pdx->pendedPixelUrbs[k] = NULL;
+       }
+       kfree(pdx->sgEntries);
+       vfree(pdx->maplist_numPagesMapped);
+       pdx->sgEntries = NULL;
+       pdx->maplist_numPagesMapped = NULL;
+       kfree(pdx->sgl);
+       kfree(pdx->pendedPixelUrbs);
+       kfree(pdx->PixelUrb);
+       pdx->sgl = NULL;
+       pdx->pendedPixelUrbs = NULL;
+       pdx->PixelUrb = NULL;
+       return 0;
+}
+
+static void piusb_readPIXEL_callback(struct urb *urb)
+{
+       int i = 0;
+       struct device_extension *pdx = urb->context;
+       int status = urb->status;
+
+       if (status && !(status == -ENOENT || status == -ECONNRESET)) {
+               dbg("%s - nonzero read bulk status received: %d", __func__,
+                   status);
+               dbg("Error in read EP2 callback");
+               dbg("FrameIndex = %d", pdx->frameIdx);
+               dbg("Bytes received before problem occurred = %d",
+                   pdx->bulk_in_byte_trk);
+               dbg("Urb Idx = %d", pdx->urbIdx);
+               pdx->pendedPixelUrbs[pdx->frameIdx][pdx->urbIdx] = 0;
+       } else {
+               pdx->bulk_in_byte_trk += urb->actual_length;
+               {
+                       i = usb_submit_urb(urb, GFP_ATOMIC);    //resubmit the URB
+                       if (i) {
+                               errCnt++;
+                               if (i != lastErr) {
+                                       dbg("submit urb in callback failed with error code %d", i);
+                                       lastErr = i;
+                               }
+                       } else {
+                               pdx->urbIdx++;  //point to next URB when we callback
+                               if (pdx->bulk_in_byte_trk >= pdx->frameSize) {
+                                       pdx->bulk_in_size_returned =
+                                           pdx->bulk_in_byte_trk;
+                                       pdx->bulk_in_byte_trk = 0;
+                                       pdx->gotPixelData = 1;
+                                       pdx->frameIdx =
+                                           ((pdx->frameIdx +
+                                             1) % pdx->num_frames);
+                                       pdx->urbIdx = 0;
+                               }
+                       }
+               }
+       }
+}
+
+/* MapUserBuffer(
+       inputs:
+       struct ioctl_struct *io - structure containing user address, frame #, and size
+       struct device_extension *pdx - the PIUSB device extension
+       returns:
+       int - status of the task
+       Notes:
+       MapUserBuffer maps a buffer passed down through an ioctl.  The user buffer is Page Aligned by the app
+       and then passed down.  The function get_free_pages(...) does the actual mapping of the buffer from user space to
+       kernel space.  From there a scatterlist is created from all the pages.  The next function called is to usb_buffer_map_sg
+       which allocated DMA addresses for each page, even coalescing them if possible.  The DMA address is placed in the scatterlist
+       structure.  The function returns the number of DMA addresses.  This may or may not be equal to the number of pages that
+       the user buffer uses.  We then build an URB for each DMA address and then submit them.
+*/
+//int MapUserBuffer( unsigned long uaddr, unsigned long numbytes, unsigned long frameInfo, struct device_extension *pdx )
+static int MapUserBuffer(struct ioctl_struct *io, struct device_extension *pdx)
+{
+       unsigned long uaddr;
+       unsigned long numbytes;
+       int frameInfo;          //which frame we're mapping
+       unsigned int epAddr = 0;
+       unsigned long count = 0;
+       int i = 0;
+       int k = 0;
+       int err = 0;
+       struct page **maplist_p;
+       int numPagesRequired;
+       frameInfo = io->numFrames;
+       uaddr = (unsigned long)io->pData;
+       numbytes = io->numbytes;
+
+       if (pdx->iama == PIXIS_PID)     //if so, which EP should we map this frame to
+       {
+               if (frameInfo % 2)      //check to see if this should use EP4(PONG)
+               {
+                       epAddr = pdx->hEP[3];   //PONG, odd frames
+               } else {
+                       epAddr = pdx->hEP[2];   //PING, even frames and zero
+               }
+               dbg("Pixis Frame #%d: EP=%d", frameInfo,
+                   (epAddr == pdx->hEP[2]) ? 2 : 4);
+       } else                  //ST133 only has 1 endpoint for Pixel data transfer
+       {
+               epAddr = pdx->hEP[0];
+               dbg("ST133 Frame #%d: EP=2", frameInfo);
+       }
+       count = numbytes;
+       dbg("UserAddress = 0x%08lX", uaddr);
+       dbg("numbytes = %d", (int)numbytes);
+       //number of pages to map the entire user space DMA buffer
+       numPagesRequired =
+           ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT;
+       dbg("Number of pages needed = %d", numPagesRequired);
+       maplist_p = vmalloc(numPagesRequired * sizeof(struct page));    //, GFP_ATOMIC);
+       if (!maplist_p) {
+               dbg("Can't Allocate Memory for maplist_p");
+               return -ENOMEM;
+       }
+       //map the user buffer to kernel memory
+       down_write(&current->mm->mmap_sem);
+       pdx->maplist_numPagesMapped[frameInfo] = get_user_pages(current, current->mm, (uaddr & PAGE_MASK), numPagesRequired, WRITE, 0,  //Don't Force
+                                                               maplist_p,
+                                                               NULL);
+       up_write(&current->mm->mmap_sem);
+       dbg("Number of pages mapped = %d",
+           pdx->maplist_numPagesMapped[frameInfo]);
+       for (i = 0; i < pdx->maplist_numPagesMapped[frameInfo]; i++)
+               flush_dcache_page(maplist_p[i]);
+       if (!pdx->maplist_numPagesMapped[frameInfo]) {
+               dbg("get_user_pages() failed");
+               vfree(maplist_p);
+               return -ENOMEM;
+       }
+       //need to create a scatterlist that spans each frame that can fit into the mapped buffer
+       pdx->sgl[frameInfo] =
+           kmalloc((pdx->maplist_numPagesMapped[frameInfo] *
+                    sizeof(struct scatterlist)), GFP_ATOMIC);
+       if (!pdx->sgl[frameInfo]) {
+               vfree(maplist_p);
+               dbg("can't allocate mem for sgl");
+               return -ENOMEM;
+       }
+       pdx->sgl[frameInfo][0].page_link = maplist_p[0];
+       pdx->sgl[frameInfo][0].offset = uaddr & ~PAGE_MASK;
+       if (pdx->maplist_numPagesMapped[frameInfo] > 1) {
+               pdx->sgl[frameInfo][0].length =
+                   PAGE_SIZE - pdx->sgl[frameInfo][0].offset;
+               count -= pdx->sgl[frameInfo][0].length;
+               for (k = 1; k < pdx->maplist_numPagesMapped[frameInfo]; k++) {
+                       pdx->sgl[frameInfo][k].offset = 0;
+                       pdx->sgl[frameInfo][k].page_link = maplist_p[k];
+                       pdx->sgl[frameInfo][k].length =
+                           (count < PAGE_SIZE) ? count : PAGE_SIZE;
+                       count -= PAGE_SIZE;     //example had PAGE_SIZE here;
+               }
+       } else {
+               pdx->sgl[frameInfo][0].length = count;
+       }
+       pdx->sgEntries[frameInfo] =
+           usb_buffer_map_sg(pdx->udev, epAddr, pdx->sgl[frameInfo],
+                             pdx->maplist_numPagesMapped[frameInfo]);
+       dbg("number of sgEntries = %d", pdx->sgEntries[frameInfo]);
+       pdx->userBufMapped = 1;
+       vfree(maplist_p);
+       //Create and Send the URB's for each s/g entry
+       pdx->PixelUrb[frameInfo] =
+           kmalloc(pdx->sgEntries[frameInfo] * sizeof(struct urb *),
+                   GFP_KERNEL);
+       if (!pdx->PixelUrb[frameInfo]) {
+               dbg("Can't Allocate Memory for Urb");
+               return -ENOMEM;
+       }
+       for (i = 0; i < pdx->sgEntries[frameInfo]; i++) {
+               pdx->PixelUrb[frameInfo][i] = usb_alloc_urb(0, GFP_KERNEL);     //0 because we're using BULK transfers
+               usb_fill_bulk_urb(pdx->PixelUrb[frameInfo][i],
+                                 pdx->udev,
+                                 epAddr,
+                                 (dma_addr_t *) sg_dma_address(&pdx->
+                                                               sgl[frameInfo]
+                                                               [i]),
+                                 sg_dma_len(&pdx->sgl[frameInfo][i]),
+                                 piusb_readPIXEL_callback, (void *)pdx);
+               pdx->PixelUrb[frameInfo][i]->transfer_dma =
+                   sg_dma_address(&pdx->sgl[frameInfo][i]);
+               pdx->PixelUrb[frameInfo][i]->transfer_flags =
+                   URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT;
+       }
+       pdx->PixelUrb[frameInfo][--i]->transfer_flags &= ~URB_NO_INTERRUPT;     //only interrupt when last URB completes
+       pdx->pendedPixelUrbs[frameInfo] =
+           kmalloc((pdx->sgEntries[frameInfo] * sizeof(char)), GFP_KERNEL);
+       if (!pdx->pendedPixelUrbs[frameInfo])
+               dbg("Can't allocate Memory for pendedPixelUrbs");
+       for (i = 0; i < pdx->sgEntries[frameInfo]; i++) {
+               err = usb_submit_urb(pdx->PixelUrb[frameInfo][i], GFP_ATOMIC);
+               if (err) {
+                       dbg("%s %d\n", "submit urb error =", err);
+                       pdx->pendedPixelUrbs[frameInfo][i] = 0;
+                       return err;
+               } else
+                       pdx->pendedPixelUrbs[frameInfo][i] = 1;;
+       }
+       return 0;
+}
+
+static struct file_operations piusb_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = piusb_ioctl,
+       .open = piusb_open,
+       .release = piusb_release,
+};
+
+static struct usb_class_driver piusb_class = {
+       .name = "usb/rspiusb%d",
+       .fops = &piusb_fops,
+       .minor_base = PIUSB_MINOR_BASE,
+};
+
+/**
+ *     piusb_probe
+ *
+ *     Called by the usb core when a new device is connected that it thinks
+ *     this driver might be interested in.
+ */
+static int piusb_probe(struct usb_interface *interface,
+                      const struct usb_device_id *id)
+{
+       struct device_extension *pdx = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int i;
+       int retval = -ENOMEM;
+
+       dev_dbg(&interface->dev, "%s - Looking for PI USB Hardware", __func__);
+
+       pdx = kzalloc(sizeof(struct device_extension), GFP_KERNEL);
+       if (pdx == NULL) {
+               dev_err(&interface->dev, "Out of memory\n");
+               goto error;
+       }
+       kref_init(&pdx->kref);
+       pdx->udev = usb_get_dev(interface_to_usbdev(interface));
+       pdx->interface = interface;
+       iface_desc = interface->cur_altsetting;
+
+       /* See if the device offered us matches what we can accept */
+       if ((pdx->udev->descriptor.idVendor != VENDOR_ID)
+           || ((pdx->udev->descriptor.idProduct != PIXIS_PID)
+               && (pdx->udev->descriptor.idProduct != ST133_PID))) {
+               return -ENODEV;
+       }
+       pdx->iama = pdx->udev->descriptor.idProduct;
+
+       if (debug) {
+               if (pdx->udev->descriptor.idProduct == PIXIS_PID)
+                       dbg("PIUSB:Pixis Camera Found");
+               else
+                       dbg("PIUSB:ST133 USB Controller Found");
+               if (pdx->udev->speed == USB_SPEED_HIGH)
+                       dbg("Highspeed(USB2.0) Device Attached");
+               else
+                       dbg("Lowspeed (USB1.1) Device Attached");
+
+               dbg("NumEndpoints in Configuration: %d",
+                   iface_desc->desc.bNumEndpoints);
+       }
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+               if (debug) {
+                       dbg("Endpoint[%d]->bDescriptorType = %d", i,
+                           endpoint->bDescriptorType);
+                       dbg("Endpoint[%d]->bEndpointAddress = 0x%02X", i,
+                           endpoint->bEndpointAddress);
+                       dbg("Endpoint[%d]->bbmAttributes = %d", i,
+                           endpoint->bmAttributes);
+                       dbg("Endpoint[%d]->MaxPacketSize = %d\n", i,
+                           endpoint->wMaxPacketSize);
+               }
+               if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                   USB_ENDPOINT_XFER_BULK) {
+                       if (endpoint->bEndpointAddress & USB_DIR_IN)
+                               pdx->hEP[i] =
+                                   usb_rcvbulkpipe(pdx->udev,
+                                                   endpoint->bEndpointAddress);
+                       else
+                               pdx->hEP[i] =
+                                   usb_sndbulkpipe(pdx->udev,
+                                                   endpoint->bEndpointAddress);
+               }
+       }
+       usb_set_intfdata(interface, pdx);
+       retval = usb_register_dev(interface, &piusb_class);
+       if (retval) {
+               err("Not able to get a minor for this device.");
+               usb_set_intfdata(interface, NULL);
+               goto error;
+       }
+       pdx->present = 1;
+
+       /* we can register the device now, as it is ready */
+       pdx->minor = interface->minor;
+       /* let the user know what node this device is now attached to */
+       dbg("PI USB2.0 device now attached to piusb-%d", pdx->minor);
+       return 0;
+
+      error:
+       if (pdx)
+               kref_put(&pdx->kref, piusb_delete);
+       return retval;
+}
+
+/**
+ *     piusb_disconnect
+ *
+ *     Called by the usb core when the device is removed from the system.
+ *
+ *     This routine guarantees that the driver will not submit any more urbs
+ *     by clearing pdx->udev.  It is also supposed to terminate any currently
+ *     active urbs.  Unfortunately, usb_bulk_msg(), used in piusb_read(), does
+ *     not provide any way to do this.  But at least we can cancel an active
+ *     write.
+ */
+static void piusb_disconnect(struct usb_interface *interface)
+{
+       struct device_extension *pdx;
+       int minor = interface->minor;
+       lock_kernel();
+       pdx = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+       /* give back our minor */
+       usb_deregister_dev(interface, &piusb_class);
+       unlock_kernel();
+       /* prevent device read, write and ioctl */
+       pdx->present = 0;
+       kref_put(&pdx->kref, piusb_delete);
+       dbg("PI USB2.0 device #%d now disconnected\n", minor);
+}
+
+static struct usb_driver piusb_driver = {
+       .name = "sub",
+       .probe = piusb_probe,
+       .disconnect = piusb_disconnect,
+       .id_table = pi_device_table,
+};
+
+/**
+ *     piusb_init
+ */
+static int __init piusb_init(void)
+{
+       int result;
+       /* register this driver with the USB subsystem */
+       result = usb_register(&piusb_driver);
+       if (result) {
+               printk(KERN_ERR KBUILD_MODNAME
+                      ": usb_register failed. Error number %d\n", result);
+               return result;
+       }
+       printk(KERN_INFO KBUILD_MODNAME ":%s: %s\n", DRIVER_DESC,
+              DRIVER_VERSION);
+       return 0;
+}
+
+/**
+ *     piusb_exit
+ */
+static void __exit piusb_exit(void)
+{
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&piusb_driver);
+}
+
+module_init(piusb_init);
+module_exit(piusb_exit);
+
+/* Module parameters */
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/rspiusb/rspiusb.h b/drivers/staging/rspiusb/rspiusb.h
new file mode 100644 (file)
index 0000000..965cd2d
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __RSPIUSB_H
+#define __RSPIUSB_H
+
+#define PIUSB_MAGIC            'm'
+#define PIUSB_IOCTL_BASE       192
+#define PIUSB_GETVNDCMD                _IOR(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 1, struct ioctl_struct)
+#define PIUSB_SETVNDCMD                _IOW(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 2, struct ioctl_struct)
+#define PIUSB_WRITEPIPE                _IOW(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 3, struct ioctl_struct)
+#define PIUSB_READPIPE         _IOR(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 4, struct ioctl_struct)
+#define PIUSB_SETFRAMESIZE     _IOW(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 5, struct ioctl_struct)
+#define PIUSB_WHATCAMERA       _IO(PIUSB_MAGIC,  PIUSB_IOCTL_BASE + 6)
+#define PIUSB_USERBUFFER       _IOW(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 7, struct ioctl_struct)
+#define PIUSB_ISHIGHSPEED      _IO(PIUSB_MAGIC,  PIUSB_IOCTL_BASE + 8)
+#define PIUSB_UNMAP_USERBUFFER _IOW(PIUSB_MAGIC, PIUSB_IOCTL_BASE + 9, struct ioctl_struct)
+
+struct ioctl_struct {
+       unsigned char cmd;
+       unsigned long numbytes;
+       unsigned char dir;      //1=out;0=in
+       int endpoint;
+       int numFrames;
+       unsigned char *pData;
+};
+
+#endif