diff options
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/rspiusb/Kconfig | 6 | ||||
-rw-r--r-- | drivers/staging/rspiusb/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/rspiusb/TODO | 22 | ||||
-rw-r--r-- | drivers/staging/rspiusb/rspiusb.c | 887 | ||||
-rw-r--r-- | drivers/staging/rspiusb/rspiusb.h | 25 |
7 files changed, 944 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 53404a4..fe3b23e 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -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 diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 170789f..bd4cb92 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -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 index 0000000..d225f67 --- /dev/null +++ b/drivers/staging/rspiusb/Kconfig @@ -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 index 0000000..cc7aed9 --- /dev/null +++ b/drivers/staging/rspiusb/Makefile @@ -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 index 0000000..cd6336a --- /dev/null +++ b/drivers/staging/rspiusb/TODO @@ -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 index 0000000..ca281d6 --- /dev/null +++ b/drivers/staging/rspiusb/rspiusb.c @@ -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(¤t->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(¤t->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 index 0000000..965cd2d --- /dev/null +++ b/drivers/staging/rspiusb/rspiusb.h @@ -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 |