Staging: add princeton instruments usb camera driver

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>
This commit is contained in:
Greg Kroah-Hartman 2008-08-21 14:04:55 -07:00
parent 88a1e909da
commit 28397ffef1
7 changed files with 944 additions and 0 deletions

View 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

View 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/

View File

@ -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.

View File

@ -0,0 +1 @@
obj-$(CONFIG_USB_RSPI) += rspiusb.o

View File

@ -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.

View File

@ -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");

View File

@ -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