blob: 04e2f92c0f6293292d55d5a56822a936d0a4ba52 [file] [log] [blame]
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -07001/*
2 * rspiusb.c
3 *
4 * Copyright (C) 2005, 2006 Princeton Instruments
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation version 2 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include <linux/vmalloc.h>
21#include <linux/kernel.h>
22#include <linux/errno.h>
23#include <linux/init.h>
24#include <linux/slab.h>
25#include <linux/module.h>
26#include <linux/smp_lock.h>
27#include <linux/completion.h>
28#include <linux/scatterlist.h>
29#include <linux/usb.h>
30#include <linux/mm.h>
31#include <linux/pagemap.h>
32#include <linux/ioctl.h>
33#include "rspiusb.h"
34
35#ifdef CONFIG_USB_DEBUG
36static int debug = 1;
37#else
38static int debug;
39#endif
40/* Use our own dbg macro */
41#undef dbg
Richard Genoudf2d46e22009-05-17 13:06:30 +020042#define dbg(format, arg...) \
43 do { \
44 if (debug) \
45 printk(KERN_DEBUG __FILE__ ": " format "\n" , ##arg); \
46 } while (0)
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -070047
48/* Version Information */
49#define DRIVER_VERSION "V1.0.1"
50#define DRIVER_AUTHOR "Princeton Instruments"
51#define DRIVER_DESC "PI USB2.0 Device Driver for Linux"
52
53/* Define these values to match your devices */
54#define VENDOR_ID 0x0BD7
55#define ST133_PID 0xA010
56#define PIXIS_PID 0xA026
57
58/* Get a minor range for your devices from the usb maintainer */
59#ifdef CONFIG_USB_DYNAMIC_MINORS
60#define PIUSB_MINOR_BASE 0
61#else
62#define PIUSB_MINOR_BASE 192
63#endif
64
65/* prevent races between open() and disconnect() */
66static DECLARE_MUTEX(disconnect_sem);
67
68/* Structure to hold all of our device specific stuff */
69struct device_extension {
Richard Genoudf2d46e22009-05-17 13:06:30 +020070 struct usb_device *udev; /* save off the usb device pointer */
71 struct usb_interface *interface; /* the interface for this device */
72 unsigned char minor; /* the starting minor number
73 * for this device
74 */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -070075 size_t bulk_in_size_returned;
76 int bulk_in_byte_trk;
77 struct urb ***PixelUrb;
78 int frameIdx;
79 int urbIdx;
80 unsigned int *maplist_numPagesMapped;
Richard Genoudf2d46e22009-05-17 13:06:30 +020081 int open; /* if the port is open or not */
82 int present; /* if the device is not disconnected */
83 int userBufMapped; /* has the user buffer been mapped ? */
84 struct scatterlist **sgl; /* scatter-gather list for user buffer */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -070085 unsigned int *sgEntries;
86 struct kref kref;
87 int gotPixelData;
88 int pendingWrite;
89 char **pendedPixelUrbs;
Richard Genoudf2d46e22009-05-17 13:06:30 +020090 int iama; /* PIXIS or ST133 */
91 int num_frames; /* the number of frames that will fit
92 * in the user buffer
93 */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -070094 int active_frame;
95 unsigned long frameSize;
96 struct semaphore sem;
Richard Genoudf2d46e22009-05-17 13:06:30 +020097 unsigned int hEP[8]; /* FX2 specific endpoints */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -070098};
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -070099
Richard Genoudf2d46e22009-05-17 13:06:30 +0200100#define to_pi_dev(d) container_of(d, struct device_extension, kref)
101
102/* Prototypes */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700103static int MapUserBuffer(struct ioctl_struct *, struct device_extension *);
104static int UnMapUserBuffer(struct device_extension *);
105static int piusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
106 unsigned long arg);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200107static int piusb_output(struct ioctl_struct *, unsigned char *, int,
108 struct device_extension *);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700109static struct usb_driver piusb_driver;
110
111/* table of devices that work with this driver */
112static struct usb_device_id pi_device_table[] = {
113 {USB_DEVICE(VENDOR_ID, ST133_PID)},
114 {USB_DEVICE(VENDOR_ID, PIXIS_PID)},
Richard Genoudf2d46e22009-05-17 13:06:30 +0200115 {0, } /* Terminating entry */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700116};
117
118MODULE_DEVICE_TABLE(usb, pi_device_table);
119
Richard Genoudf2d46e22009-05-17 13:06:30 +0200120static int lastErr;
121static int errCnt;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700122
123static void piusb_delete(struct kref *kref)
124{
125 struct device_extension *pdx = to_pi_dev(kref);
126
127 dev_dbg(&pdx->udev->dev, "%s\n", __func__);
128 usb_put_dev(pdx->udev);
129 kfree(pdx);
130}
131
132static int piusb_open(struct inode *inode, struct file *file)
133{
134 struct device_extension *pdx = NULL;
135 struct usb_interface *interface;
136 int subminor;
137 int retval = 0;
138
139 dbg("Piusb_Open()");
140 subminor = iminor(inode);
141 interface = usb_find_interface(&piusb_driver, subminor);
142 if (!interface) {
143 printk(KERN_ERR "%s - error, can't find device for minor %d\n",
144 __func__, subminor);
145 retval = -ENODEV;
146 goto exit_no_device;
147 }
148
149 pdx = usb_get_intfdata(interface);
150 if (!pdx) {
151 retval = -ENODEV;
152 goto exit_no_device;
153 }
154 dbg("Alternate Setting = %d", interface->num_altsetting);
155
Richard Genoudf2d46e22009-05-17 13:06:30 +0200156 pdx->bulk_in_size_returned = 0;
157 pdx->bulk_in_byte_trk = 0;
158 pdx->PixelUrb = NULL;
159 pdx->frameIdx = 0;
160 pdx->urbIdx = 0;
161 pdx->maplist_numPagesMapped = NULL;
162 pdx->userBufMapped = 0;
163 pdx->sgl = NULL;
164 pdx->sgEntries = NULL;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700165 pdx->gotPixelData = 0;
166 pdx->pendingWrite = 0;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200167 pdx->pendedPixelUrbs = NULL;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700168 pdx->num_frames = 0;
169 pdx->active_frame = 0;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200170 pdx->frameSize = 0;
171
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700172 /* increment our usage count for the device */
173 kref_get(&pdx->kref);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200174
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700175 /* save our object in the file's private structure */
176 file->private_data = pdx;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200177
178exit_no_device:
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700179 return retval;
180}
181
182static int piusb_release(struct inode *inode, struct file *file)
183{
184 struct device_extension *pdx;
185 int retval = 0;
186
187 dbg("Piusb_Release()");
188 pdx = (struct device_extension *)file->private_data;
189 if (pdx == NULL) {
190 dbg("%s - object is NULL", __func__);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200191 retval = -ENODEV;
192 goto object_null;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700193 }
194 /* decrement the count on our device */
195 kref_put(&pdx->kref, piusb_delete);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200196
197object_null:
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700198 return retval;
199}
200
Richard Genoudc854b5e2009-05-17 13:06:31 +0200201static int pixis_io(struct ioctl_struct *ctrl, struct device_extension *pdx,
202 struct ioctl_struct *arg)
203{
204 unsigned int numToRead = 0;
205 unsigned int totalRead = 0;
206 unsigned char *uBuf;
207 int numbytes;
208 int i;
209
210 uBuf = kmalloc(ctrl->numbytes, GFP_KERNEL);
211 if (!uBuf) {
212 dbg("Alloc for uBuf failed");
213 return 0;
214 }
215 numbytes = (int) ctrl->numbytes;
216 numToRead = (unsigned int) ctrl->numbytes;
217 dbg("numbytes to read = %d", numbytes);
218 dbg("endpoint # %d", ctrl->endpoint);
219
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530220 if (copy_from_user(uBuf, ctrl->pData, numbytes)) {
Richard Genoudc854b5e2009-05-17 13:06:31 +0200221 dbg("copying ctrl->pData to dummyBuf failed");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530222 return -EFAULT;
223 }
Richard Genoudc854b5e2009-05-17 13:06:31 +0200224
225 do {
226 i = usb_bulk_msg(pdx->udev, pdx->hEP[ctrl->endpoint],
227 (uBuf + totalRead),
228 /* EP0 can only handle 64 bytes at a time */
229 (numToRead > 64) ? 64 : numToRead,
230 &numbytes, HZ * 10);
231 if (i) {
232 dbg("CMD = %s, Address = 0x%02X",
233 ((uBuf[3] == 0x02) ? "WRITE" : "READ"),
234 uBuf[1]);
235 dbg("Number of bytes Attempted to read = %d",
236 (int)ctrl->numbytes);
237 dbg("Blocking ReadI/O Failed with status %d", i);
238 kfree(uBuf);
239 return -1;
240 }
241 dbg("Pixis EP0 Read %d bytes", numbytes);
242 totalRead += numbytes;
243 numToRead -= numbytes;
244 } while (numToRead);
245
246 memcpy(ctrl->pData, uBuf, totalRead);
247 dbg("Total Bytes Read from PIXIS EP0 = %d", totalRead);
248 ctrl->numbytes = totalRead;
249
250 if (copy_to_user(arg, ctrl, sizeof(struct ioctl_struct)))
251 dbg("copy_to_user failed in IORB");
252
253 kfree(uBuf);
254 return ctrl->numbytes;
255}
256
Richard Genoudc854b5e2009-05-17 13:06:31 +0200257static int pixel_data(struct ioctl_struct *ctrl, struct device_extension *pdx)
258{
259 int i;
260
261 if (!pdx->gotPixelData)
262 return 0;
263
264 pdx->gotPixelData = 0;
265 ctrl->numbytes = pdx->bulk_in_size_returned;
266 pdx->bulk_in_size_returned -= pdx->frameSize;
267
268 for (i = 0; i < pdx->maplist_numPagesMapped[pdx->active_frame]; i++)
vibi sreenivasan8d2db512009-06-04 20:56:45 +0530269 SetPageDirty(sg_page(&pdx->sgl[pdx->active_frame][i]));
Richard Genoudc854b5e2009-05-17 13:06:31 +0200270
271 pdx->active_frame = ((pdx->active_frame + 1) % pdx->num_frames);
272
273 return ctrl->numbytes;
274}
275
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700276/**
277 * piusb_ioctl
278 */
279static int piusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
280 unsigned long arg)
281{
282 struct device_extension *pdx;
283 char dummyCtlBuf[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
284 unsigned long devRB = 0;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700285 int err = 0;
286 int retval = 0;
287 struct ioctl_struct ctrl;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700288 unsigned short controlData = 0;
289
290 pdx = (struct device_extension *)file->private_data;
291 /* verify that the device wasn't unplugged */
292 if (!pdx->present) {
293 dbg("No Device Present\n");
294 return -ENODEV;
295 }
296 /* fill in your device specific stuff here */
297 if (_IOC_DIR(cmd) & _IOC_READ)
Richard Genoudf2d46e22009-05-17 13:06:30 +0200298 err = !access_ok(VERIFY_WRITE, (void __user *)arg,
299 _IOC_SIZE(cmd));
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700300 else if (_IOC_DIR(cmd) & _IOC_WRITE)
Richard Genoudf2d46e22009-05-17 13:06:30 +0200301 err = !access_ok(VERIFY_READ, (void __user *)arg,
302 _IOC_SIZE(cmd));
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700303 if (err) {
304 dev_err(&pdx->udev->dev, "return with error = %d\n", err);
305 return -EFAULT;
306 }
307 switch (cmd) {
308 case PIUSB_GETVNDCMD:
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530309 if (__copy_from_user
310 (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct))) {
J.R. Mauro6546f082009-03-28 00:10:35 -0400311 dev_err(&pdx->udev->dev, "copy_from_user failed\n");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530312 return -EFAULT;
313 }
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700314 dbg("%s %x\n", "Get Vendor Command = ", ctrl.cmd);
315 retval =
316 usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
317 ctrl.cmd, USB_DIR_IN, 0, 0, &devRB,
318 ctrl.numbytes, HZ * 10);
319 if (ctrl.cmd == 0xF1) {
320 dbg("FW Version returned from HW = %ld.%ld",
321 (devRB >> 8), (devRB & 0xFF));
322 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200323 if (retval >= 0)
324 retval = (int)devRB;
325 return retval;
326
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700327 case PIUSB_SETVNDCMD:
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530328 if (__copy_from_user
329 (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct))) {
J.R. Mauro6546f082009-03-28 00:10:35 -0400330 dev_err(&pdx->udev->dev, "copy_from_user failed\n");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530331 return -EFAULT;
332 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200333 /* dbg( "%s %x", "Set Vendor Command = ",ctrl.cmd ); */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700334 controlData = ctrl.pData[0];
335 controlData |= (ctrl.pData[1] << 8);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200336 /* dbg( "%s %d", "Vendor Data =",controlData ); */
337 retval = usb_control_msg(pdx->udev,
338 usb_sndctrlpipe(pdx->udev, 0),
339 ctrl.cmd,
340 (USB_DIR_OUT | USB_TYPE_VENDOR
341 /* | USB_RECIP_ENDPOINT */),
342 controlData, 0,
343 &dummyCtlBuf, ctrl.numbytes, HZ * 10);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700344 return retval;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200345
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700346 case PIUSB_ISHIGHSPEED:
347 return ((pdx->udev->speed == USB_SPEED_HIGH) ? 1 : 0);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200348
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700349 case PIUSB_WRITEPIPE:
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530350 if (__copy_from_user(&ctrl, (void __user *)arg, _IOC_SIZE(cmd))) {
Richard Genoudf2d46e22009-05-17 13:06:30 +0200351 dev_err(&pdx->udev->dev,
352 "copy_from_user WRITE_DUMMY failed\n");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530353 return -EFAULT;
354 }
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700355 if (!access_ok(VERIFY_READ, ctrl.pData, ctrl.numbytes)) {
356 dbg("can't access pData");
357 return 0;
358 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200359 piusb_output(&ctrl, ctrl.pData /* uBuf */, ctrl.numbytes, pdx);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700360 return ctrl.numbytes;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200361
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700362 case PIUSB_USERBUFFER:
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530363 if (__copy_from_user
364 (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct))) {
J.R. Mauro6546f082009-03-28 00:10:35 -0400365 dev_err(&pdx->udev->dev, "copy_from_user failed\n");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530366 return -EFAULT;
367 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200368 return MapUserBuffer((struct ioctl_struct *) &ctrl, pdx);
369
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700370 case PIUSB_UNMAP_USERBUFFER:
Richard Genoudf2d46e22009-05-17 13:06:30 +0200371 retval = UnMapUserBuffer(pdx);
372 return retval;
373
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700374 case PIUSB_READPIPE:
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530375 if (__copy_from_user(&ctrl, (void __user *)arg,
376 sizeof(struct ioctl_struct))) {
J.R. Mauro6546f082009-03-28 00:10:35 -0400377 dev_err(&pdx->udev->dev, "copy_from_user failed\n");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530378 return -EFAULT;
379 }
Richard Genoud14e8bcd2009-05-17 13:06:32 +0200380 if (((0 == ctrl.endpoint) && (PIXIS_PID == pdx->iama)) ||
381 (1 == ctrl.endpoint) || /* ST133IO */
382 (4 == ctrl.endpoint)) /* PIXIS IO */
383 return pixis_io(&ctrl, pdx,
384 (struct ioctl_struct *)arg);
385 else if ((0 == ctrl.endpoint) || /* ST133 Pixel Data */
386 (2 == ctrl.endpoint) || /* PIXIS Ping */
387 (3 == ctrl.endpoint)) /* PIXIS Pong */
Richard Genoudc854b5e2009-05-17 13:06:31 +0200388 return pixel_data(&ctrl, pdx);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200389
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700390 break;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200391
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700392 case PIUSB_WHATCAMERA:
393 return pdx->iama;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200394
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700395 case PIUSB_SETFRAMESIZE:
396 dbg("PIUSB_SETFRAMESIZE");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530397 if (__copy_from_user
398 (&ctrl, (void __user *)arg, sizeof(struct ioctl_struct))) {
J.R. Mauro6546f082009-03-28 00:10:35 -0400399 dev_err(&pdx->udev->dev, "copy_from_user failed\n");
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530400 return -EFAULT;
401 }
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700402 pdx->frameSize = ctrl.numbytes;
403 pdx->num_frames = ctrl.numFrames;
404 if (!pdx->sgl)
405 pdx->sgl =
406 kmalloc(sizeof(struct scatterlist *) *
407 pdx->num_frames, GFP_KERNEL);
408 if (!pdx->sgEntries)
409 pdx->sgEntries =
410 kmalloc(sizeof(unsigned int) * pdx->num_frames,
411 GFP_KERNEL);
412 if (!pdx->PixelUrb)
413 pdx->PixelUrb =
414 kmalloc(sizeof(struct urb **) * pdx->num_frames,
415 GFP_KERNEL);
416 if (!pdx->maplist_numPagesMapped)
417 pdx->maplist_numPagesMapped =
418 vmalloc(sizeof(unsigned int) * pdx->num_frames);
419 if (!pdx->pendedPixelUrbs)
420 pdx->pendedPixelUrbs =
421 kmalloc(sizeof(char *) * pdx->num_frames,
422 GFP_KERNEL);
423 return 0;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200424
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700425 default:
426 dbg("%s\n", "No IOCTL found");
427 break;
428
429 }
430 /* return that we did not understand this ioctl call */
431 dbg("Returning -ENOTTY");
432 return -ENOTTY;
433}
434
435static void piusb_write_bulk_callback(struct urb *urb)
436{
437 struct device_extension *pdx = urb->context;
438 int status = urb->status;
439
440 /* sync/async unlink faults aren't errors */
441 if (status && !(status == -ENOENT || status == -ECONNRESET))
442 dev_dbg(&urb->dev->dev,
443 "%s - nonzero write bulk status received: %d",
444 __func__, status);
445
446 pdx->pendingWrite = 0;
Pete Zaitcevaea0d43b2009-06-10 14:44:14 -0600447 kfree(urb->transfer_buffer);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700448}
449
Richard Genoudf2d46e22009-05-17 13:06:30 +0200450int piusb_output(struct ioctl_struct *io, unsigned char *uBuf, int len,
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700451 struct device_extension *pdx)
452{
453 struct urb *urb = NULL;
454 int err = 0;
455 unsigned char *kbuf = NULL;
456
457 urb = usb_alloc_urb(0, GFP_KERNEL);
458 if (urb != NULL) {
Pete Zaitcevaea0d43b2009-06-10 14:44:14 -0600459 kbuf = kmalloc(len, GFP_KERNEL);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700460 if (!kbuf) {
J.R. Mauro6546f082009-03-28 00:10:35 -0400461 dev_err(&pdx->udev->dev, "buffer_alloc failed\n");
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700462 return -ENOMEM;
463 }
vibi sreenivasan7a80bfc2009-06-04 20:59:17 +0530464 if(__copy_from_user(kbuf, uBuf, len)) {
465 dev_err(&pdx->udev->dev, "__copy_from_user failed\n");
466 return -EFAULT;
467 }
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700468 usb_fill_bulk_urb(urb, pdx->udev, pdx->hEP[io->endpoint], kbuf,
469 len, piusb_write_bulk_callback, pdx);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700470 err = usb_submit_urb(urb, GFP_KERNEL);
471 if (err) {
472 dev_err(&pdx->udev->dev,
473 "WRITE ERROR:submit urb error = %d\n", err);
474 }
475 pdx->pendingWrite = 1;
476 usb_free_urb(urb);
477 }
478 return -EINPROGRESS;
479}
480
481static int UnMapUserBuffer(struct device_extension *pdx)
482{
483 int i = 0;
484 int k = 0;
485 unsigned int epAddr;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200486
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700487 for (k = 0; k < pdx->num_frames; k++) {
488 dbg("Killing Urbs for Frame %d", k);
489 for (i = 0; i < pdx->sgEntries[k]; i++) {
490 usb_kill_urb(pdx->PixelUrb[k][i]);
491 usb_free_urb(pdx->PixelUrb[k][i]);
492 pdx->pendedPixelUrbs[k][i] = 0;
493 }
494 dbg("Urb error count = %d", errCnt);
495 errCnt = 0;
496 dbg("Urbs free'd and Killed for Frame %d", k);
497 }
498
499 for (k = 0; k < pdx->num_frames; k++) {
Richard Genoudf2d46e22009-05-17 13:06:30 +0200500 if (pdx->iama == PIXIS_PID)
501 /* which EP should we map this frame to ? */
502 /* PONG, odd frames: hEP[3] */
503 /* PING, even frames and zero hEP[2] */
504 epAddr = (k % 2) ? pdx->hEP[3] : pdx->hEP[2];
505 else
506 /* ST133 only has 1 endpoint for Pixel data transfer */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700507 epAddr = pdx->hEP[0];
Richard Genoudf2d46e22009-05-17 13:06:30 +0200508
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700509 usb_buffer_unmap_sg(pdx->udev, epAddr, pdx->sgl[k],
510 pdx->maplist_numPagesMapped[k]);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200511 for (i = 0; i < pdx->maplist_numPagesMapped[k]; i++)
vibi sreenivasan8d2db512009-06-04 20:56:45 +0530512 page_cache_release(sg_page(&pdx->sgl[k][i]));
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700513 kfree(pdx->sgl[k]);
514 kfree(pdx->PixelUrb[k]);
515 kfree(pdx->pendedPixelUrbs[k]);
516 pdx->sgl[k] = NULL;
517 pdx->PixelUrb[k] = NULL;
518 pdx->pendedPixelUrbs[k] = NULL;
519 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200520
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700521 kfree(pdx->sgEntries);
522 vfree(pdx->maplist_numPagesMapped);
523 pdx->sgEntries = NULL;
524 pdx->maplist_numPagesMapped = NULL;
525 kfree(pdx->sgl);
526 kfree(pdx->pendedPixelUrbs);
527 kfree(pdx->PixelUrb);
528 pdx->sgl = NULL;
529 pdx->pendedPixelUrbs = NULL;
530 pdx->PixelUrb = NULL;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200531
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700532 return 0;
533}
534
535static void piusb_readPIXEL_callback(struct urb *urb)
536{
537 int i = 0;
538 struct device_extension *pdx = urb->context;
539 int status = urb->status;
540
541 if (status && !(status == -ENOENT || status == -ECONNRESET)) {
542 dbg("%s - nonzero read bulk status received: %d", __func__,
543 status);
544 dbg("Error in read EP2 callback");
545 dbg("FrameIndex = %d", pdx->frameIdx);
546 dbg("Bytes received before problem occurred = %d",
547 pdx->bulk_in_byte_trk);
548 dbg("Urb Idx = %d", pdx->urbIdx);
549 pdx->pendedPixelUrbs[pdx->frameIdx][pdx->urbIdx] = 0;
550 } else {
551 pdx->bulk_in_byte_trk += urb->actual_length;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200552 i = usb_submit_urb(urb, GFP_ATOMIC); /* resubmit the URB */
553 if (i) {
554 errCnt++;
555 if (i != lastErr) {
556 dbg("submit urb in callback failed "
557 "with error code %d", i);
558 lastErr = i;
559 }
560 } else {
561 pdx->urbIdx++; /* point to next URB when we callback */
562 if (pdx->bulk_in_byte_trk >= pdx->frameSize) {
563 pdx->bulk_in_size_returned =
564 pdx->bulk_in_byte_trk;
565 pdx->bulk_in_byte_trk = 0;
566 pdx->gotPixelData = 1;
567 pdx->frameIdx =
568 ((pdx->frameIdx +
569 1) % pdx->num_frames);
570 pdx->urbIdx = 0;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700571 }
572 }
573 }
574}
575
576/* MapUserBuffer(
577 inputs:
Richard Genoudf2d46e22009-05-17 13:06:30 +0200578 struct ioctl_struct *io - structure containing user address,
579 frame #, and size
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700580 struct device_extension *pdx - the PIUSB device extension
Richard Genoudf2d46e22009-05-17 13:06:30 +0200581
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700582 returns:
583 int - status of the task
Richard Genoudf2d46e22009-05-17 13:06:30 +0200584
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700585 Notes:
Richard Genoudf2d46e22009-05-17 13:06:30 +0200586 MapUserBuffer maps a buffer passed down through an ioctl.
587 The user buffer is Page Aligned by the app and then passed down.
588 The function get_free_pages(...) does the actual mapping of the buffer
589 from user space to kernel space.
590 From there a scatterlist is created from all the pages.
591 The next function called is to usb_buffer_map_sg which allocated
592 DMA addresses for each page, even coalescing them if possible.
593 The DMA address is placed in the scatterlist structure.
594 The function returns the number of DMA addresses.
595 This may or may not be equal to the number of pages that
596 the user buffer uses.
597 We then build an URB for each DMA address and then submit them.
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700598*/
Richard Genoudf2d46e22009-05-17 13:06:30 +0200599
600/*
601int MapUserBuffer(unsigned long uaddr, unsigned long numbytes,
602 unsigned long frameInfo, struct device_extension *pdx)
603*/
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700604static int MapUserBuffer(struct ioctl_struct *io, struct device_extension *pdx)
605{
606 unsigned long uaddr;
607 unsigned long numbytes;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200608 int frameInfo; /* which frame we're mapping */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700609 unsigned int epAddr = 0;
610 unsigned long count = 0;
611 int i = 0;
612 int k = 0;
613 int err = 0;
614 struct page **maplist_p;
615 int numPagesRequired;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200616
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700617 frameInfo = io->numFrames;
618 uaddr = (unsigned long)io->pData;
619 numbytes = io->numbytes;
620
Richard Genoudf2d46e22009-05-17 13:06:30 +0200621 if (pdx->iama == PIXIS_PID) {
622 /* which EP should we map this frame to ? */
623 /* PONG, odd frames: hEP[3] */
624 /* PING, even frames and zero hEP[2] */
625 epAddr = (frameInfo % 2) ? pdx->hEP[3] : pdx->hEP[2];
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700626 dbg("Pixis Frame #%d: EP=%d", frameInfo,
627 (epAddr == pdx->hEP[2]) ? 2 : 4);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200628 } else { /* ST133 only has 1 endpoint for Pixel data transfer */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700629 epAddr = pdx->hEP[0];
630 dbg("ST133 Frame #%d: EP=2", frameInfo);
631 }
632 count = numbytes;
633 dbg("UserAddress = 0x%08lX", uaddr);
634 dbg("numbytes = %d", (int)numbytes);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200635
636 /* number of pages to map the entire user space DMA buffer */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700637 numPagesRequired =
638 ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT;
639 dbg("Number of pages needed = %d", numPagesRequired);
Pete Zaitcevaea0d43b2009-06-10 14:44:14 -0600640 maplist_p = vmalloc(numPagesRequired * sizeof(struct page *));
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700641 if (!maplist_p) {
642 dbg("Can't Allocate Memory for maplist_p");
643 return -ENOMEM;
644 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200645
646 /* map the user buffer to kernel memory */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700647 down_write(&current->mm->mmap_sem);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200648 pdx->maplist_numPagesMapped[frameInfo] = get_user_pages(current,
649 current->mm, (uaddr & PAGE_MASK), numPagesRequired,
650 WRITE, 0 /* Don't Force*/, maplist_p, NULL);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700651 up_write(&current->mm->mmap_sem);
652 dbg("Number of pages mapped = %d",
653 pdx->maplist_numPagesMapped[frameInfo]);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200654
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700655 for (i = 0; i < pdx->maplist_numPagesMapped[frameInfo]; i++)
656 flush_dcache_page(maplist_p[i]);
657 if (!pdx->maplist_numPagesMapped[frameInfo]) {
658 dbg("get_user_pages() failed");
659 vfree(maplist_p);
660 return -ENOMEM;
661 }
Richard Genoudf2d46e22009-05-17 13:06:30 +0200662
663 /* need to create a scatterlist that spans each frame
664 * that can fit into the mapped buffer
665 */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700666 pdx->sgl[frameInfo] =
667 kmalloc((pdx->maplist_numPagesMapped[frameInfo] *
668 sizeof(struct scatterlist)), GFP_ATOMIC);
669 if (!pdx->sgl[frameInfo]) {
670 vfree(maplist_p);
671 dbg("can't allocate mem for sgl");
672 return -ENOMEM;
673 }
vibi sreenivasan8d2db512009-06-04 20:56:45 +0530674 sg_assign_page(&pdx->sgl[frameInfo][0], maplist_p[0]);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700675 pdx->sgl[frameInfo][0].offset = uaddr & ~PAGE_MASK;
676 if (pdx->maplist_numPagesMapped[frameInfo] > 1) {
677 pdx->sgl[frameInfo][0].length =
678 PAGE_SIZE - pdx->sgl[frameInfo][0].offset;
679 count -= pdx->sgl[frameInfo][0].length;
680 for (k = 1; k < pdx->maplist_numPagesMapped[frameInfo]; k++) {
681 pdx->sgl[frameInfo][k].offset = 0;
vibi sreenivasan8d2db512009-06-04 20:56:45 +0530682 sg_assign_page(&pdx->sgl[frameInfo][k], maplist_p[k]);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700683 pdx->sgl[frameInfo][k].length =
684 (count < PAGE_SIZE) ? count : PAGE_SIZE;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200685 count -= PAGE_SIZE; /* example had PAGE_SIZE here */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700686 }
687 } else {
688 pdx->sgl[frameInfo][0].length = count;
689 }
690 pdx->sgEntries[frameInfo] =
691 usb_buffer_map_sg(pdx->udev, epAddr, pdx->sgl[frameInfo],
692 pdx->maplist_numPagesMapped[frameInfo]);
693 dbg("number of sgEntries = %d", pdx->sgEntries[frameInfo]);
694 pdx->userBufMapped = 1;
695 vfree(maplist_p);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200696
697 /* Create and Send the URB's for each s/g entry */
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700698 pdx->PixelUrb[frameInfo] =
699 kmalloc(pdx->sgEntries[frameInfo] * sizeof(struct urb *),
700 GFP_KERNEL);
701 if (!pdx->PixelUrb[frameInfo]) {
702 dbg("Can't Allocate Memory for Urb");
703 return -ENOMEM;
704 }
705 for (i = 0; i < pdx->sgEntries[frameInfo]; i++) {
Richard Genoudf2d46e22009-05-17 13:06:30 +0200706 /* 0 iso packets because we're using BULK transfers */
707 pdx->PixelUrb[frameInfo][i] = usb_alloc_urb(0, GFP_KERNEL);
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700708 usb_fill_bulk_urb(pdx->PixelUrb[frameInfo][i],
709 pdx->udev,
710 epAddr,
Pete Zaitcevaea0d43b2009-06-10 14:44:14 -0600711 NULL, // non-DMA HC? buy a better hardware
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700712 sg_dma_len(&pdx->sgl[frameInfo][i]),
713 piusb_readPIXEL_callback, (void *)pdx);
714 pdx->PixelUrb[frameInfo][i]->transfer_dma =
715 sg_dma_address(&pdx->sgl[frameInfo][i]);
716 pdx->PixelUrb[frameInfo][i]->transfer_flags =
717 URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT;
718 }
Roel Kluin749d00d2009-08-07 21:00:10 +0200719 if (i == 0)
720 return -EINVAL;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200721 /* only interrupt when last URB completes */
722 pdx->PixelUrb[frameInfo][--i]->transfer_flags &= ~URB_NO_INTERRUPT;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700723 pdx->pendedPixelUrbs[frameInfo] =
724 kmalloc((pdx->sgEntries[frameInfo] * sizeof(char)), GFP_KERNEL);
725 if (!pdx->pendedPixelUrbs[frameInfo])
726 dbg("Can't allocate Memory for pendedPixelUrbs");
727 for (i = 0; i < pdx->sgEntries[frameInfo]; i++) {
728 err = usb_submit_urb(pdx->PixelUrb[frameInfo][i], GFP_ATOMIC);
729 if (err) {
730 dbg("%s %d\n", "submit urb error =", err);
731 pdx->pendedPixelUrbs[frameInfo][i] = 0;
732 return err;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200733 }
734 pdx->pendedPixelUrbs[frameInfo][i] = 1;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700735 }
736 return 0;
737}
738
Richard Genoudf2d46e22009-05-17 13:06:30 +0200739static const struct file_operations piusb_fops = {
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700740 .owner = THIS_MODULE,
741 .ioctl = piusb_ioctl,
742 .open = piusb_open,
743 .release = piusb_release,
744};
745
746static struct usb_class_driver piusb_class = {
747 .name = "usb/rspiusb%d",
748 .fops = &piusb_fops,
749 .minor_base = PIUSB_MINOR_BASE,
750};
751
752/**
753 * piusb_probe
754 *
755 * Called by the usb core when a new device is connected that it thinks
756 * this driver might be interested in.
757 */
758static int piusb_probe(struct usb_interface *interface,
759 const struct usb_device_id *id)
760{
761 struct device_extension *pdx = NULL;
762 struct usb_host_interface *iface_desc;
763 struct usb_endpoint_descriptor *endpoint;
764 int i;
765 int retval = -ENOMEM;
766
767 dev_dbg(&interface->dev, "%s - Looking for PI USB Hardware", __func__);
768
769 pdx = kzalloc(sizeof(struct device_extension), GFP_KERNEL);
770 if (pdx == NULL) {
771 dev_err(&interface->dev, "Out of memory\n");
772 goto error;
773 }
774 kref_init(&pdx->kref);
775 pdx->udev = usb_get_dev(interface_to_usbdev(interface));
776 pdx->interface = interface;
777 iface_desc = interface->cur_altsetting;
778
779 /* See if the device offered us matches what we can accept */
780 if ((pdx->udev->descriptor.idVendor != VENDOR_ID)
781 || ((pdx->udev->descriptor.idProduct != PIXIS_PID)
Richard Genoudf2d46e22009-05-17 13:06:30 +0200782 && (pdx->udev->descriptor.idProduct != ST133_PID)))
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700783 return -ENODEV;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200784
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700785 pdx->iama = pdx->udev->descriptor.idProduct;
786
787 if (debug) {
788 if (pdx->udev->descriptor.idProduct == PIXIS_PID)
789 dbg("PIUSB:Pixis Camera Found");
790 else
791 dbg("PIUSB:ST133 USB Controller Found");
792 if (pdx->udev->speed == USB_SPEED_HIGH)
793 dbg("Highspeed(USB2.0) Device Attached");
794 else
795 dbg("Lowspeed (USB1.1) Device Attached");
796
797 dbg("NumEndpoints in Configuration: %d",
798 iface_desc->desc.bNumEndpoints);
799 }
800 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
801 endpoint = &iface_desc->endpoint[i].desc;
802 if (debug) {
803 dbg("Endpoint[%d]->bDescriptorType = %d", i,
804 endpoint->bDescriptorType);
805 dbg("Endpoint[%d]->bEndpointAddress = 0x%02X", i,
806 endpoint->bEndpointAddress);
807 dbg("Endpoint[%d]->bbmAttributes = %d", i,
808 endpoint->bmAttributes);
809 dbg("Endpoint[%d]->MaxPacketSize = %d\n", i,
810 endpoint->wMaxPacketSize);
811 }
Julia Lawallc62d8432008-12-29 11:21:52 +0100812 if (usb_endpoint_xfer_bulk(endpoint)) {
813 if (usb_endpoint_dir_in(endpoint))
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700814 pdx->hEP[i] =
815 usb_rcvbulkpipe(pdx->udev,
816 endpoint->bEndpointAddress);
817 else
818 pdx->hEP[i] =
819 usb_sndbulkpipe(pdx->udev,
820 endpoint->bEndpointAddress);
821 }
822 }
823 usb_set_intfdata(interface, pdx);
824 retval = usb_register_dev(interface, &piusb_class);
825 if (retval) {
826 err("Not able to get a minor for this device.");
827 usb_set_intfdata(interface, NULL);
828 goto error;
829 }
830 pdx->present = 1;
831
832 /* we can register the device now, as it is ready */
833 pdx->minor = interface->minor;
834 /* let the user know what node this device is now attached to */
835 dbg("PI USB2.0 device now attached to piusb-%d", pdx->minor);
836 return 0;
837
Richard Genoudf2d46e22009-05-17 13:06:30 +0200838error:
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700839 if (pdx)
840 kref_put(&pdx->kref, piusb_delete);
841 return retval;
842}
843
844/**
845 * piusb_disconnect
846 *
847 * Called by the usb core when the device is removed from the system.
848 *
849 * This routine guarantees that the driver will not submit any more urbs
850 * by clearing pdx->udev. It is also supposed to terminate any currently
851 * active urbs. Unfortunately, usb_bulk_msg(), used in piusb_read(), does
852 * not provide any way to do this. But at least we can cancel an active
853 * write.
854 */
855static void piusb_disconnect(struct usb_interface *interface)
856{
857 struct device_extension *pdx;
858 int minor = interface->minor;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200859
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700860 lock_kernel();
Richard Genoudf2d46e22009-05-17 13:06:30 +0200861
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700862 pdx = usb_get_intfdata(interface);
863 usb_set_intfdata(interface, NULL);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200864
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700865 /* give back our minor */
866 usb_deregister_dev(interface, &piusb_class);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200867
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700868 unlock_kernel();
Richard Genoudf2d46e22009-05-17 13:06:30 +0200869
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700870 /* prevent device read, write and ioctl */
871 pdx->present = 0;
872 kref_put(&pdx->kref, piusb_delete);
873 dbg("PI USB2.0 device #%d now disconnected\n", minor);
874}
875
876static struct usb_driver piusb_driver = {
877 .name = "sub",
878 .probe = piusb_probe,
879 .disconnect = piusb_disconnect,
880 .id_table = pi_device_table,
881};
882
883/**
884 * piusb_init
885 */
886static int __init piusb_init(void)
887{
888 int result;
Richard Genoudf2d46e22009-05-17 13:06:30 +0200889
890 lastErr = 0;
891 errCnt = 0;
892
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700893 /* register this driver with the USB subsystem */
894 result = usb_register(&piusb_driver);
Richard Genoudf2d46e22009-05-17 13:06:30 +0200895 if (result)
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700896 printk(KERN_ERR KBUILD_MODNAME
Richard Genoudf2d46e22009-05-17 13:06:30 +0200897 ": usb_register failed. Error number %d\n",
898 result);
899 else
900 printk(KERN_INFO KBUILD_MODNAME ":%s: %s\n", DRIVER_DESC,
901 DRIVER_VERSION);
902 return result;
Greg Kroah-Hartman28397ff2008-08-21 14:04:55 -0700903}
904
905/**
906 * piusb_exit
907 */
908static void __exit piusb_exit(void)
909{
910 /* deregister this driver with the USB subsystem */
911 usb_deregister(&piusb_driver);
912}
913
914module_init(piusb_init);
915module_exit(piusb_exit);
916
917/* Module parameters */
918module_param(debug, int, 0);
919MODULE_PARM_DESC(debug, "Debug enabled or not");
920
921MODULE_AUTHOR(DRIVER_AUTHOR);
922MODULE_DESCRIPTION(DRIVER_DESC);
923MODULE_LICENSE("GPL v2");