patch-2.3.17 linux/drivers/usb/ezusb.c
Next file: linux/drivers/usb/ezusb.h
Previous file: linux/drivers/usb/cpia.h
Back to the patch index
Back to the overall index
- Lines: 1177
- Date:
Tue Sep 7 10:19:45 1999
- Orig file:
v2.3.16/linux/drivers/usb/ezusb.c
- Orig date:
Thu Aug 26 13:05:39 1999
diff -u --recursive --new-file v2.3.16/linux/drivers/usb/ezusb.c linux/drivers/usb/ezusb.c
@@ -26,18 +26,22 @@
* bulk reads.
* Implemented EZUSB_SETINTERFACE, more sanity checks for EZUSB_BULK.
* Preliminary ISO support
+ * 0.3 01.09.99 Async Bulk and ISO support
+ * 0.4 01.09.99
*
*/
/*****************************************************************************/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/socket.h>
-#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/list.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
#include "usb.h"
#include "ezusb.h"
@@ -49,27 +53,190 @@
static struct ezusb {
struct semaphore mutex;
struct usb_device *usbdev;
- struct list_head iso;
+ struct list_head async_pending;
+ struct list_head async_completed;
+ wait_queue_head_t wait;
+ spinlock_t lock;
} ezusb[NREZUSB];
-struct isodesc {
- struct list_head isolist;
- spinlock_t lock;
- struct usb_device *usbdev;
- unsigned int ep;
- unsigned int pipe;
- unsigned int pktsz;
- unsigned int framesperint;
- unsigned int rd, wr, buflen;
- unsigned int flags;
- unsigned int schedcnt, unschedcnt;
-
- void *hcbuf[2];
- void *hcisodesc[2];
- unsigned char *buf;
-};
+struct async {
+ struct list_head asynclist;
+ struct ezusb *ez;
+ void *data;
+ unsigned dataorder;
+ void *userdata;
+ unsigned datalen;
+ union {
+ struct usb_isoc_desc *iso;
+ void *bulk;
+ } desc;
+ unsigned numframes; /* 0 means bulk, > 0 means iso */
+ struct ezusb_asynccompleted completed;
+};
-#define ISOFLG_ACTIVE (1<<0)
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned ld2(unsigned int x)
+{
+ unsigned r = 0;
+
+ if (x >= 0x10000) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x >= 0x100) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x >= 0x10) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x >= 4) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x >= 2)
+ r++;
+ return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void async_removelist(struct async *as)
+{
+ struct ezusb *ez = as->ez;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ez->lock, flags);
+ list_del(&as->asynclist);
+ INIT_LIST_HEAD(&as->asynclist);
+ spin_unlock_irqrestore(&ez->lock, flags);
+}
+
+extern __inline__ void async_newpending(struct async *as)
+{
+ struct ezusb *ez = as->ez;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ez->lock, flags);
+ list_add_tail(&as->asynclist, &ez->async_pending);
+ spin_unlock_irqrestore(&ez->lock, flags);
+}
+
+extern __inline__ void async_movetocompleted(struct async *as)
+{
+ struct ezusb *ez = as->ez;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ez->lock, flags);
+ list_del(&as->asynclist);
+ list_add_tail(&as->asynclist, &ez->async_completed);
+ spin_unlock_irqrestore(&ez->lock, flags);
+}
+
+extern __inline__ struct async *async_getcompleted(struct ezusb *ez)
+{
+ unsigned long flags;
+ struct async *as = NULL;
+
+ spin_lock_irqsave(&ez->lock, flags);
+ if (!list_empty(&ez->async_completed)) {
+ as = list_entry(ez->async_completed.next, struct async, asynclist);
+ list_del(&as->asynclist);
+ INIT_LIST_HEAD(&as->asynclist);
+ }
+ spin_unlock_irqrestore(&ez->lock, flags);
+ return as;
+}
+
+extern __inline__ struct async *async_getpending(struct ezusb *ez, void *context)
+{
+ unsigned long flags;
+ struct async *as;
+ struct list_head *p;
+
+ spin_lock_irqsave(&ez->lock, flags);
+ for (p = ez->async_pending.next; p != &ez->async_pending; ) {
+ as = list_entry(p, struct async, asynclist);
+ p = p->next;
+ if (as->completed.context != context)
+ continue;
+ list_del(&as->asynclist);
+ INIT_LIST_HEAD(&as->asynclist);
+ spin_unlock_irqrestore(&ez->lock, flags);
+ return as;
+ }
+ spin_unlock_irqrestore(&ez->lock, flags);
+ return NULL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int async_completed(int status, void *__buffer, int rval, void *dev_id)
+{
+ struct async *as = (struct async *)dev_id;
+ struct ezusb *ez = as->ez;
+ unsigned cnt;
+
+printk(KERN_DEBUG "ezusb: async_completed: status %d rval %d\n", status, rval);
+ as->completed.length = rval;
+ if (as->numframes > 0) {
+ as->completed.status = USB_ST_NOERROR;
+ for (cnt = 0; cnt < as->numframes; cnt++) {
+ as->completed.isostat[cnt].status = as->desc.iso->frames[cnt].frame_status;
+ as->completed.isostat[cnt].length = as->desc.iso->frames[cnt].frame_length;
+ }
+ } else
+ as->completed.status = status;
+ spin_lock(&ez->lock);
+ list_del(&as->asynclist);
+ list_add_tail(&as->asynclist, &ez->async_completed);
+ spin_unlock(&ez->lock);
+ wake_up(&ez->wait);
+ return 0;
+}
+
+static void remove_async(struct async *as)
+{
+ if (as->data && as->dataorder)
+ free_pages((unsigned long)as->data, as->dataorder);
+ if (as->numframes)
+ usb_free_isoc(as->desc.iso);
+ kfree(as);
+}
+
+static void kill_async(struct async *as)
+{
+ struct ezusb *ez = as->ez;
+
+ if (as->numframes)
+ /* ISO case */
+ usb_kill_isoc(as->desc.iso);
+ else
+ usb_terminate_bulk(ez->usbdev, as->desc.bulk);
+ as->completed.status = USB_ST_REMOVED;
+ async_movetocompleted(as);
+}
+
+static void destroy_all_async(struct ezusb *ez)
+{
+ struct async *as;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ez->lock, flags);
+ if (!list_empty(&ez->async_pending)) {
+ as = list_entry(ez->async_pending.next, struct async, asynclist);
+ list_del(&as->asynclist);
+ INIT_LIST_HEAD(&as->asynclist);
+ spin_unlock_irqrestore(&ez->lock, flags);
+ kill_async(as);
+ spin_lock_irqsave(&ez->lock, flags);
+ }
+ spin_unlock_irqrestore(&ez->lock, flags);
+ while ((as = async_getcompleted(ez)))
+ remove_async(as);
+}
/* --------------------------------------------------------------------- */
@@ -216,439 +383,505 @@
up(&ez->mutex);
file->f_pos = 0;
file->private_data = ez;
+ MOD_INC_USE_COUNT;
return 0;
}
static int ezusb_release(struct inode *inode, struct file *file)
{
struct ezusb *ez = (struct ezusb *)file->private_data;
+
+ down(&ez->mutex);
+ destroy_all_async(ez);
+ up(&ez->mutex);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int ezusb_control(struct usb_device *usbdev, unsigned char requesttype,
+ unsigned char request, unsigned short value,
+ unsigned short index, unsigned short length,
+ void *data)
+{
+ unsigned char *tbuf = NULL;
+ unsigned int pipe;
+ int i;
+
+ if (length > PAGE_SIZE)
+ return -EINVAL;
+ /* __range_ok is broken;
+ with unsigned short size, it gave
+ addl %si,%edx ; sbbl %ecx,%ecx; cmpl %edx,12(%eax); sbbl $0,%ecx
+ */
+ if (requesttype & 0x80) {
+ pipe = usb_rcvctrlpipe(usbdev, 0);
+ if (length > 0 && !access_ok(VERIFY_WRITE, data, (unsigned int)length))
+ return -EFAULT;
+ } else
+ pipe = usb_sndctrlpipe(usbdev, 0);
+ if (length > 0) {
+ if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ if (!(requesttype & 0x80)) {
+ if (copy_from_user(tbuf, data, length)) {
+ free_page((unsigned long)tbuf);
+ return -EFAULT;
+ }
+ }
+ }
+ i = usb_control_msg(usbdev, pipe, request, requesttype, value, index, tbuf, length);
+ if (i < 0) {
+ if (length > 0)
+ free_page((unsigned long)tbuf);
+ printk(KERN_WARNING "ezusb: EZUSB_CONTROL failed rqt %u rq %u len %u ret %d\n",
+ requesttype, request, length, i);
+ return -ENXIO;
+ }
+ if (requesttype & 0x80 && length > 0 && copy_to_user(data, tbuf, length))
+ i = -EFAULT;
+ if (length > 0)
+ free_page((unsigned long)tbuf);
+ return i;
+}
+
+static int ezusb_bulk(struct usb_device *usbdev, unsigned int ep, unsigned int length, void *data)
+{
+ unsigned char *tbuf = NULL;
+ unsigned int pipe;
+ unsigned long len2 = 0;
+ int ret = 0;
+
+ if (length > PAGE_SIZE)
+ return -EINVAL;
+ if ((ep & ~0x80) >= 16)
+ return -EINVAL;
+ if (ep & 0x80) {
+ pipe = usb_rcvbulkpipe(usbdev, ep & 0x7f);
+ if (length > 0 && !access_ok(VERIFY_WRITE, data, length))
+ return -EFAULT;
+ } else
+ pipe = usb_sndbulkpipe(usbdev, ep & 0x7f);
+ if (!usb_maxpacket(usbdev, pipe, !(ep & 0x80)))
+ return -EINVAL;
+ if (length > 0) {
+ if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+ if (!(ep & 0x80)) {
+ if (copy_from_user(tbuf, data, length)) {
+ free_page((unsigned long)tbuf);
+ return -EFAULT;
+ }
+ }
+ }
+ ret = usbdev->bus->op->bulk_msg(usbdev, pipe, tbuf, length, &len2);
+ if (ret < 0) {
+ if (length > 0)
+ free_page((unsigned long)tbuf);
+ printk(KERN_WARNING "ezusb: EZUSB_BULK failed ep 0x%x len %u ret %d\n",
+ ep, length, ret);
+ return -ENXIO;
+ }
+ if (len2 > length)
+ len2 = length;
+ ret = len2;
+ if (ep & 0x80 && len2 > 0 && copy_to_user(data, tbuf, len2))
+ ret = -EFAULT;
+ if (length > 0)
+ free_page((unsigned long)tbuf);
+ return ret;
+}
+
+static int ezusb_resetep(struct usb_device *usbdev, unsigned int ep)
+{
+ if ((ep & ~0x80) >= 16)
+ return -EINVAL;
+ usb_settoggle(usbdev, ep & 0xf, !(ep & 0x80), 0);
return 0;
}
-static void iso_schedrcv(struct isodesc *isodesc)
+static int ezusb_setinterface(struct usb_device *usbdev, unsigned int interface, unsigned int altsetting)
{
- unsigned diff;
+ if (usb_set_interface(usbdev, interface, altsetting) < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int ezusb_setconfiguration(struct usb_device *usbdev, unsigned int config)
+{
+ if (usb_set_configuration(usbdev, config) < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int ezusb_requestbulk(struct ezusb *ez, struct ezusb_asyncbulk *ab)
+{
+ struct async *as = NULL;
+ unsigned int pipe;
+
+ if (ab->len > PAGE_SIZE)
+ return -EINVAL;
+ if ((ab->ep & ~0x80) >= 16)
+ return -EINVAL;
+ if (ab->ep & 0x80) {
+ pipe = usb_rcvbulkpipe(ez->usbdev, ab->ep & 0x7f);
+ if (ab->len > 0 && !access_ok(VERIFY_WRITE, ab->data, ab->len))
+ return -EFAULT;
+ } else
+ pipe = usb_sndbulkpipe(ez->usbdev, ab->ep & 0x7f);
+ if (!usb_maxpacket(ez->usbdev, pipe, !(ab->ep & 0x80)))
+ return -EINVAL;
+ if (!(as = kmalloc(sizeof(struct async), GFP_KERNEL)))
+ return -ENOMEM;
+ INIT_LIST_HEAD(&as->asynclist);
+ as->ez = ez;
+ as->userdata = ab->data;
+ as->numframes = 0;
+ as->data = 0;
+ as->dataorder = 0;
+ as->datalen = ab->len;
+ as->completed.context = ab->context;
+ if (ab->len > 0) {
+ as->dataorder = 1;
+ if (!(as->data = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+ kfree(as);
+ return -ENOMEM;
+ }
+ if (!(ab->ep & 0x80)) {
+ if (copy_from_user(as->data, ab->data, ab->len))
+ goto err_fault;
+ as->datalen = 0; /* no need to copy back at completion */
+ }
+ }
+ async_newpending(as);
+ if (!(as->desc.bulk = usb_request_bulk(ez->usbdev, pipe, async_completed, as->data, ab->len, as))) {
+ async_removelist(as);
+ goto err_inval;
+ }
+ return 0;
- if (!(isodesc->flags & ISOFLG_ACTIVE))
- return;
- diff = (isodesc->buflen - 1 + isodesc->rd - isodesc->wr) % isodesc->buflen;
- if (diff < isodesc->framesperint * isodesc->pktsz)
- return;
- for (;;) {
- diff = (isodesc->schedcnt - isodesc->unschedcnt) & 3;
- if (diff >= 2)
- return;
- usb_schedule_isochronous(isodesc->usbdev, isodesc->hcisodesc[isodesc->schedcnt & 1],
- diff ? isodesc->hcisodesc[(isodesc->schedcnt - 1) & 1] : NULL);
- isodesc->schedcnt++;
- }
-}
-
-static void iso_schedsnd(struct isodesc *isodesc)
-{
- unsigned diff, bcnt, x;
- unsigned char *p1, *p2;
-
- if (!(isodesc->flags & ISOFLG_ACTIVE))
- return;
- for (;;) {
- diff = (isodesc->schedcnt - isodesc->unschedcnt) & 3;
- if (diff >= 2)
- return;
- bcnt = (isodesc->buflen - isodesc->rd + isodesc->wr) % isodesc->buflen;
- if (bcnt < isodesc->framesperint * isodesc->pktsz)
- return;
- p2 = isodesc->hcbuf[isodesc->schedcnt & 1];
- for (bcnt = 0; bcnt < isodesc->framesperint; bcnt++) {
- p1 = isodesc->buf + isodesc->rd;
- if (isodesc->rd + isodesc->pktsz > isodesc->buflen) {
- x = isodesc->buflen - isodesc->rd;
- memcpy(p2, p1, x);
- memcpy(p2+x, isodesc->buf, isodesc->pktsz - x);
- } else
- memcpy(p2, p1, isodesc->pktsz);
- isodesc->rd = (isodesc->rd + isodesc->pktsz) % isodesc->buflen;
- p2 += isodesc->pktsz;
- if (((unsigned long)p2 ^ ((unsigned long)p2 + isodesc->pktsz - 1)) & (~(PAGE_SIZE - 1)))
- p2 = (void *)(((unsigned long)p2 + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)));
- }
- usb_schedule_isochronous(isodesc->usbdev, isodesc->hcisodesc[isodesc->schedcnt & 1],
- diff ? isodesc->hcisodesc[(isodesc->schedcnt - 1) & 1] : NULL);
- isodesc->schedcnt++;
- }
-}
-
-static int ezusb_isorcv_irq(int status, void *__buffer, int __len, void *dev_id)
-{
- struct isodesc *isodesc = (struct isodesc *)dev_id;
- unsigned int len, len2;
- unsigned char *p1;
-
- spin_lock(&isodesc->lock);
- usb_unschedule_isochronous(isodesc->usbdev, isodesc->hcisodesc[isodesc->unschedcnt & 1]);
- len = usb_compress_isochronous(isodesc->usbdev, isodesc->hcisodesc[isodesc->unschedcnt & 1]);
- printk(KERN_DEBUG "ezusb_isorcv_irq: %u bytes recvd\n", len);
- p1 = isodesc->hcbuf[isodesc->unschedcnt & 1];
- while (len > 0) {
- len2 = (isodesc->buflen - 1 + isodesc->rd - isodesc->wr) % isodesc->buflen;
- if (!len2)
- break;
- if (isodesc->wr + len2 > isodesc->buflen)
- len2 = isodesc->buflen - isodesc->wr;
- if (len2 > len)
- len2 = len;
- memcpy(isodesc->buf + isodesc->wr, p1, len2);
- isodesc->wr = (isodesc->wr + len2) % isodesc->buflen;
- p1 += len2;
- len -= len2;
- }
- isodesc->unschedcnt++;
- iso_schedrcv(isodesc);
- spin_unlock(&isodesc->lock);
- return 1;
-}
-
-static int ezusb_isosnd_irq(int status, void *__buffer, int len, void *dev_id)
-{
- struct isodesc *isodesc = (struct isodesc *)dev_id;
-
- spin_lock(&isodesc->lock);
- usb_unschedule_isochronous(isodesc->usbdev, isodesc->hcisodesc[isodesc->unschedcnt & 1]);
- isodesc->unschedcnt++;
- iso_schedsnd(isodesc);
- spin_unlock(&isodesc->lock);
- return 1;
-}
-
-static struct isodesc *findiso(struct ezusb *ez, unsigned int ep)
-{
- struct list_head *head = &ez->iso;
- struct list_head *tmp = head->next;
- struct isodesc *id;
-
- while (tmp != head) {
- id = list_entry(tmp, struct isodesc, isolist);
- if (id->ep == ep)
- return id;
- tmp = tmp->next;
+ err_fault:
+ if (as) {
+ if (as->data)
+ free_page((unsigned long)as->data);
+ kfree(as);
}
- return NULL;
+ return -EFAULT;
+
+ err_inval:
+ if (as) {
+ if (as->data)
+ free_page((unsigned long)as->data);
+ kfree(as);
+ }
+ return -EINVAL;
+}
+
+static int ezusb_requestiso(struct ezusb *ez, struct ezusb_asynciso *ai, unsigned char *cmd)
+{
+ struct async *as;
+ unsigned int maxpkt, pipe;
+ unsigned int dsize, order, assize, j;
+ int i;
+
+ if ((ai->ep & ~0x80) >= 16 || ai->framecnt < 1 || ai->framecnt > 128)
+ return -EINVAL;
+ if (ai->ep & 0x80)
+ pipe = usb_rcvisocpipe(ez->usbdev, ai->ep & 0x7f);
+ else
+ pipe = usb_sndisocpipe(ez->usbdev, ai->ep & 0x7f);
+ if (!(maxpkt = usb_maxpacket(ez->usbdev, pipe, !(ai->ep & 0x80))))
+ return -EINVAL;
+ dsize = maxpkt * ai->framecnt;
+ if (dsize > 65536)
+ return -EINVAL;
+ order = ld2(dsize >> PAGE_SHIFT);
+ if (dsize > (PAGE_SIZE << order))
+ order++;
+ if (ai->ep & 0x80)
+ if (dsize > 0 && !access_ok(VERIFY_WRITE, ai->data, dsize))
+ return -EFAULT;
+ assize = sizeof(struct async) + ai->framecnt * sizeof(struct ezusb_isoframestat);
+ if (!(as = kmalloc(assize, GFP_KERNEL)))
+ return -ENOMEM;
+ memset(as, 0, assize);
+ INIT_LIST_HEAD(&as->asynclist);
+ as->ez = ez;
+ as->userdata = ai->data;
+ as->numframes = ai->framecnt;
+ as->data = 0;
+ as->dataorder = order;
+ as->datalen = dsize;
+ as->completed.context = ai->context;
+ as->desc.iso = NULL;
+ if (dsize > 0) {
+ if (!(as->data = (unsigned char *)__get_free_pages(GFP_KERNEL, order))) {
+ kfree(as);
+ return -ENOMEM;
+ }
+ if (!(ai->ep & 0x80)) {
+ if (copy_from_user(as->data, ai->data, dsize))
+ goto err_fault;
+ as->datalen = 0; /* no need to copy back at completion */
+ }
+ }
+ if ((i = usb_init_isoc(ez->usbdev, pipe, ai->framecnt, as, &as->desc.iso))) {
+ printk(KERN_DEBUG "ezusb: usb_init_isoc error %d\n", i);
+ goto err_inval;
+ }
+ as->desc.iso->start_type = START_ABSOLUTE;
+ as->desc.iso->start_frame = ai->startframe;
+ as->desc.iso->callback_frames = 0;
+ as->desc.iso->callback_fn = async_completed;
+ as->desc.iso->data = as->data;
+ as->desc.iso->buf_size = dsize;
+ for (j = 0; j < ai->framecnt; j++) {
+ if (get_user(i, (int *)(cmd + j * sizeof(struct ezusb_isoframestat)))) {
+ usb_free_isoc(as->desc.iso);
+ kfree(as);
+ return -EFAULT;
+ }
+ if (i < 0)
+ i = 0;
+ as->desc.iso->frames[j].frame_length = i;
+ }
+ async_newpending(as);
+ if ((i = usb_run_isoc(as->desc.iso, NULL))) {
+ printk(KERN_DEBUG "ezusb: usb_run_isoc error %d\n", i);
+ async_removelist(as);
+ goto err_inval;
+ }
+ return 0;
+
+ err_fault:
+ if (as) {
+ if (as->desc.iso)
+ usb_free_isoc(as->desc.iso);
+ if (as->data)
+ free_page((unsigned long)as->data);
+ kfree(as);
+ }
+ return -EFAULT;
+
+ err_inval:
+ if (as) {
+ if (as->desc.iso)
+ usb_free_isoc(as->desc.iso);
+ if (as->data)
+ free_page((unsigned long)as->data);
+ kfree(as);
+ }
+ return -EINVAL;
+}
+
+static int ezusb_terminateasync(struct ezusb *ez, void *context)
+{
+ struct async *as;
+ int ret = 0;
+
+ while ((as = async_getpending(ez, context))) {
+ kill_async(as);
+ ret++;
+ }
+ return ret;
+}
+
+static int ezusb_asynccompl(struct async *as, void *arg)
+{
+ if (as->datalen > 0) {
+ if (copy_to_user(as->userdata, as->data, as->datalen)) {
+ remove_async(as);
+ return -EFAULT;
+ }
+ }
+ if (copy_to_user(arg, &as->completed,
+ sizeof(struct ezusb_asynccompleted) +
+ as->numframes * sizeof(struct ezusb_isoframestat))) {
+ remove_async(as);
+ return -EFAULT;
+ }
+ remove_async(as);
+ return 0;
}
static int ezusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct ezusb *ez = (struct ezusb *)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ struct usb_proc_ctrltransfer pctrl;
+ struct usb_proc_bulktransfer pbulk;
+ struct usb_proc_setinterface psetintf;
struct ezusb_ctrltransfer ctrl;
struct ezusb_bulktransfer bulk;
struct ezusb_setinterface setintf;
- unsigned int len1, ep, pipe, cnt;
- unsigned long len2;
- unsigned char tbuf[1024];
- int i;
- struct ezusb_isotransfer isot;
- struct ezusb_isodata isod;
- struct isodesc *isodesc;
- usb_device_irq isocompl;
- unsigned long flags;
- unsigned char *p1, *p2;
+ struct ezusb_asyncbulk abulk;
+ struct ezusb_asynciso aiso;
+ struct async *as;
+ void *context;
+ unsigned int ep, cfg;
+ int i, ret = 0;
+ down(&ez->mutex);
+ if (!ez->usbdev) {
+ up(&ez->mutex);
+ return -EIO;
+ }
switch (cmd) {
- case EZUSB_CONTROL:
- copy_from_user_ret(&ctrl, (void *)arg, sizeof(ctrl), -EFAULT);
- if (ctrl.dlen > sizeof(tbuf) || ctrl.dlen > 1024)
- return -EINVAL;
- if (ctrl.requesttype & 0x80) {
- if (ctrl.dlen && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.dlen))
- return -EINVAL;
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
- }
- i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0),
- (devrequest *)&ctrl, tbuf, ctrl.dlen);
- up(&ez->mutex);
- if (!i && ctrl.dlen) {
- copy_to_user_ret(ctrl.data, tbuf, ctrl.dlen, -EFAULT);
- }
- } else {
- if (ctrl.dlen) {
- copy_from_user_ret(tbuf, ctrl.data, ctrl.dlen, -EFAULT);
- }
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
- }
- i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0),
- (devrequest *)&ctrl, tbuf, ctrl.dlen);
- up(&ez->mutex);
+ case USB_PROC_CONTROL:
+ if (copy_from_user(&pctrl, (void *)arg, sizeof(pctrl))) {
+ ret = -EFAULT;
+ break;
}
- if (i) {
- printk(KERN_WARNING "ezusb: EZUSB_CONTROL failed rqt %u rq %u len %u ret %d\n",
- ctrl.requesttype, ctrl.request, ctrl.length, i);
- return -ENXIO;
+ ret = ezusb_control(ez->usbdev, pctrl.requesttype, pctrl.request,
+ pctrl.value, pctrl.index, pctrl.length, pctrl.data);
+ break;
+
+ case USB_PROC_BULK:
+ if (copy_from_user(&pbulk, (void *)arg, sizeof(pbulk))) {
+ ret = -EFAULT;
+ break;
}
- return 0;
+ ret = ezusb_bulk(ez->usbdev, pbulk.ep, pbulk.len, pbulk.data);
+ break;
- case EZUSB_BULK:
- copy_from_user_ret(&bulk, (void *)arg, sizeof(bulk), -EFAULT);
- if (bulk.ep & 0x80)
- pipe = usb_rcvbulkpipe(ez->usbdev, bulk.ep & 0x7f);
- else
- pipe = usb_sndbulkpipe(ez->usbdev, bulk.ep & 0x7f);
- if (!usb_maxpacket(ez->usbdev, pipe, !(bulk.ep & 0x80)))
- return -EINVAL;
- len1 = bulk.len;
- if (len1 > sizeof(tbuf))
- len1 = sizeof(tbuf);
- if (bulk.ep & 0x80) {
- if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1))
- return -EINVAL;
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
- }
- i = ez->usbdev->bus->op->bulk_msg(ez->usbdev, pipe, tbuf, len1, &len2);
- up(&ez->mutex);
- if (!i && len2) {
- copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT);
- }
- } else {
- if (len1) {
- copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT);
- }
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
- }
- i = ez->usbdev->bus->op->bulk_msg(ez->usbdev, pipe, tbuf, len1, &len2);
- up(&ez->mutex);
+ case USB_PROC_RESETEP:
+ if (get_user(ep, (unsigned int *)arg)) {
+ ret = -EFAULT;
+ break;
}
- if (i) {
- printk(KERN_WARNING "ezusb: EZUSB_BULK failed ep 0x%x len %u ret %d\n",
- bulk.ep, bulk.len, i);
- return -ENXIO;
+ ret = ezusb_resetep(ez->usbdev, ep);
+ break;
+
+ case USB_PROC_SETINTERFACE:
+ if (copy_from_user(&psetintf, (void *)arg, sizeof(psetintf))) {
+ ret = -EFAULT;
+ break;
}
- return len2;
+ ret = ezusb_setinterface(ez->usbdev, psetintf.interface, psetintf.altsetting);
+ break;
- case EZUSB_RESETEP:
- get_user_ret(ep, (unsigned int *)arg, -EFAULT);
- if ((ep & ~0x80) >= 16)
- return -EINVAL;
- usb_settoggle(ez->usbdev, ep & 0xf, !(ep & 0x80), 0);
- return 0;
+ case USB_PROC_SETCONFIGURATION:
+ if (get_user(cfg, (unsigned int *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = ezusb_setconfiguration(ez->usbdev, cfg);
+ break;
- case EZUSB_SETINTERFACE:
- copy_from_user_ret(&setintf, (void *)arg, sizeof(setintf), -EFAULT);
- if (usb_set_interface(ez->usbdev, setintf.interface, setintf.altsetting))
- return -EINVAL;
- return 0;
-
- case EZUSB_STARTISO:
- copy_from_user_ret(&isot, (void *)arg, sizeof(isot), -EFAULT);
- len1 = isot.framesperint * isot.pktsz;
- if (len1 > PAGE_SIZE) {
- len1 = PAGE_SIZE / isot.pktsz;
- len1 = PAGE_SIZE * ((isot.framesperint + len1 - 1) / len1);
- }
- len2 = (isot.pktsz * 1000 + PAGE_SIZE - 1) & (PAGE_SIZE-1);
- if (len2 > 32*PAGE_SIZE)
- len2 = PAGE_SIZE;
- if ((isot.ep & ~0x80) >= 16 || isot.pktsz < 1 || isot.pktsz > 1023 ||
- isot.framesperint < 1 || isot.framesperint > 1000 ||
- len1 > 4*PAGE_SIZE)
- return -EINVAL;
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
+ case EZUSB_CONTROL:
+ if (copy_from_user(&ctrl, (void *)arg, sizeof(ctrl))) {
+ ret = -EFAULT;
+ break;
}
- if (findiso(ez, isod.ep)) {
- up(&ez->mutex);
- return -EBUSY;
+ if (ctrl.dlen != ctrl.length) {
+ ret = -EINVAL;
+ break;
}
- if (isot.ep & 0x80) {
- pipe = usb_rcvisocpipe(ez->usbdev, isot.ep & 15);
- isocompl = ezusb_isorcv_irq;
- } else {
- pipe = usb_sndisocpipe(ez->usbdev, isot.ep & 15);
- isocompl = ezusb_isosnd_irq;
+ ret = ezusb_control(ez->usbdev, ctrl.requesttype, ctrl.request,
+ ctrl.value, ctrl.index, ctrl.length, ctrl.data);
+ break;
+
+ case EZUSB_BULK:
+ if (copy_from_user(&bulk, (void *)arg, sizeof(bulk))) {
+ ret = -EFAULT;
+ break;
}
- if (!(isodesc = kmalloc(sizeof(struct isodesc), GFP_KERNEL))) {
- up(&ez->mutex);
- return -ENOMEM;
+ ret = ezusb_bulk(ez->usbdev, bulk.ep, bulk.len, bulk.data);
+ break;
+
+ case EZUSB_RESETEP:
+ if (get_user(ep, (unsigned int *)arg)) {
+ ret = -EFAULT;
+ break;
}
- memset(isodesc, 0, sizeof(struct isodesc));
- INIT_LIST_HEAD(&isodesc->isolist);
- spin_lock_init(&isodesc->lock);
- isodesc->usbdev = ez->usbdev;
- isodesc->ep = isot.ep;
- isodesc->pktsz = isot.pktsz;
- isodesc->framesperint = isot.framesperint;
- isodesc->buflen = len2;
- if (!(isodesc->hcbuf[0] = kmalloc(len1, GFP_KERNEL)) ||
- !(isodesc->hcbuf[1] = kmalloc(len1, GFP_KERNEL)))
- goto startisomemerr;
- if (!(isodesc->hcisodesc[0] = usb_allocate_isochronous(ez->usbdev, pipe, isodesc->hcbuf[0],
- len1, isodesc->pktsz, isocompl, isodesc)) ||
- !(isodesc->hcisodesc[1] = usb_allocate_isochronous(ez->usbdev, pipe, isodesc->hcbuf[1],
- len1, isodesc->pktsz, isocompl, isodesc)))
- goto startisomemerr;
- if (!(isodesc->buf = vmalloc(isodesc->buflen)))
- goto startisomemerr;
- up(&ez->mutex);
- return 0;
+ ret = ezusb_resetep(ez->usbdev, ep);
+ break;
+
+ case EZUSB_SETINTERFACE:
+ if (copy_from_user(&setintf, (void *)arg, sizeof(setintf))) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = ezusb_setinterface(ez->usbdev, setintf.interface, setintf.altsetting);
+ break;
- startisomemerr:
- if (isodesc->hcisodesc[0])
- usb_delete_isochronous(ez->usbdev, isodesc->hcisodesc[0]);
- if (isodesc->hcisodesc[1])
- usb_delete_isochronous(ez->usbdev, isodesc->hcisodesc[1]);
- if (isodesc->hcbuf[0])
- kfree(isodesc->hcbuf[0]);
- if (isodesc->hcbuf[1])
- kfree(isodesc->hcbuf[1]);
- if (isodesc->buf)
- vfree(isodesc->buf);
- up(&ez->mutex);
- return -ENOMEM;
+ case EZUSB_SETCONFIGURATION:
+ if (get_user(cfg, (unsigned int *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = ezusb_setconfiguration(ez->usbdev, cfg);
+ break;
- case EZUSB_STOPISO:
- get_user_ret(ep, (unsigned int *)arg, -EFAULT);
- if ((ep & ~0x80) >= 16)
- return -EINVAL;
- down(&ez->mutex);
- if (!ez->usbdev) {
+ case EZUSB_ASYNCCOMPLETED:
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&ez->wait, &wait);
+ for (;;) {
+ if (!ez->usbdev)
+ break;
+ if ((as = async_getcompleted(ez)))
+ break;
+ if (signal_pending(current))
+ break;
up(&ez->mutex);
- return -EIO;
+ schedule();
+ down(&ez->mutex);
}
- if (!(isodesc = findiso(ez, ep))) {
- up(&ez->mutex);
- return -EINVAL;
+ remove_wait_queue(&ez->wait, &wait);
+ current->state = TASK_RUNNING;
+ if (as) {
+ ret = ezusb_asynccompl(as, (void *)arg);
+ break;
}
- list_del(&isodesc->isolist);
- usb_delete_isochronous(ez->usbdev, isodesc->hcisodesc[0]);
- usb_delete_isochronous(ez->usbdev, isodesc->hcisodesc[1]);
- kfree(isodesc->hcbuf[0]);
- kfree(isodesc->hcbuf[1]);
- vfree(isodesc->buf);
- up(&ez->mutex);
- return 0;
-
- case EZUSB_ISODATA:
- copy_from_user_ret(&isod, (void *)arg, sizeof(isod), -EFAULT);
- if ((isod.ep & ~0x80) >= 16)
- return -EINVAL;
- if (isod.size)
- if (!access_ok((isod.ep & 0x80) ? VERIFY_WRITE : VERIFY_READ, isod.data, isod.size))
- return -EFAULT;
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
}
- if (!(isodesc = findiso(ez, ep))) {
- up(&ez->mutex);
- return -EINVAL;
+ ret = -EIO;
+ break;
+
+ case EZUSB_ASYNCCOMPLETEDNB:
+ if ((as = async_getcompleted(ez))) {
+ ret = ezusb_asynccompl(as, (void *)arg);
+ break;
}
- if (isod.ep & 0x80) {
- cnt = 0;
- p1 = isod.data;
- while (cnt < isod.size) {
- spin_lock_irqsave(&isodesc->lock, flags);
- p2 = isodesc->buf + isodesc->rd;
- len2 = (isodesc->rd >= isodesc->wr) ? isodesc->buflen : isodesc->wr;
- len2 -= isodesc->rd;
- spin_unlock_irqrestore(&isodesc->lock, flags);
- if (len2 <= 0)
- break;
- if (len2 >= isod.size - cnt)
- len2 = isod.size - cnt;
- if (__copy_to_user(p1, p2, len2)) {
- up(&ez->mutex);
- return -EFAULT;
- }
- p1 += len2;
- cnt += len2;
- spin_lock_irqsave(&isodesc->lock, flags);
- isodesc->rd = (isodesc->rd + len2) % isodesc->buflen;
- spin_unlock_irqrestore(&isodesc->lock, flags);
- }
- isod.size = cnt;
- iso_schedrcv(isodesc);
- } else {
- cnt = 0;
- p1 = isod.data;
- while (cnt < isod.size) {
- spin_lock_irqsave(&isodesc->lock, flags);
- p2 = isodesc->buf + isodesc->wr;
- len2 = (isodesc->buflen - 1 + isodesc->rd - isodesc->wr) % isodesc->buflen;
- if (isodesc->wr + len2 > isodesc->buflen)
- len2 = isodesc->buflen - isodesc->wr;
- spin_unlock_irqrestore(&isodesc->lock, flags);
- if (len2 <= 0)
- break;
- if (len2 >= isod.size - cnt)
- len2 = isod.size - cnt;
- if (__copy_from_user(p2, p1, len2)) {
- up(&ez->mutex);
- return -EFAULT;
- }
- p1 += len2;
- cnt += len2;
- spin_lock_irqsave(&isodesc->lock, flags);
- isodesc->wr = (isodesc->wr + len2) % isodesc->buflen;
- spin_unlock_irqrestore(&isodesc->lock, flags);
- }
- isod.size = cnt;
- iso_schedsnd(isodesc);
+ ret = -EAGAIN;
+ break;
+
+ case EZUSB_REQUESTBULK:
+ if (copy_from_user(&abulk, (void *)arg, sizeof(abulk))) {
+ ret = -EFAULT;
+ break;
}
- spin_lock_irqsave(&isodesc->lock, flags);
- isod.bufqueued = (isodesc->buflen + isodesc->wr - isodesc->rd) % isodesc->buflen;
- isod.buffree = (isodesc->buflen - 1 + isodesc->rd - isodesc->wr) % isodesc->buflen;
- spin_unlock_irqrestore(&isodesc->lock, flags);
- up(&ez->mutex);
- copy_to_user_ret((void *)arg, &isod, sizeof(isod), -EFAULT);
- return 0;
+ ret = ezusb_requestbulk(ez, &abulk);
+ break;
- case EZUSB_PAUSEISO:
- get_user_ret(ep, (unsigned int *)arg, -EFAULT);
- if ((ep & ~0x80) >= 16)
- return -EINVAL;
- if (!(isodesc = findiso(ez, ep)))
- return -EINVAL;
- spin_lock_irqsave(&isodesc->lock, flags);
- isodesc->flags &= ~ISOFLG_ACTIVE;
- spin_unlock_irqrestore(&isodesc->lock, flags);
- return 0;
-
- case EZUSB_RESUMEISO:
- get_user_ret(ep, (unsigned int *)arg, -EFAULT);
- if ((ep & ~0x80) >= 16)
- return -EINVAL;
- down(&ez->mutex);
- if (!ez->usbdev) {
- up(&ez->mutex);
- return -EIO;
+ case EZUSB_REQUESTISO:
+ if (copy_from_user(&aiso, (void *)arg, sizeof(aiso))) {
+ ret = -EFAULT;
+ break;
}
- if (!(isodesc = findiso(ez, ep))) {
- up(&ez->mutex);
- return -EINVAL;
+ ret = ezusb_requestiso(ez, &aiso, ((unsigned char *)arg)+sizeof(aiso));
+ break;
+
+ case EZUSB_TERMINATEASYNC:
+ if (get_user(context, (void **)arg)) {
+ ret = -EFAULT;
+ break;
}
- spin_lock_irqsave(&isodesc->lock, flags);
- isodesc->flags |= ISOFLG_ACTIVE;
- if (isot.ep & 0x80)
- iso_schedrcv(isodesc);
- else
- iso_schedsnd(isodesc);
- spin_unlock_irqrestore(&isodesc->lock, flags);
- up(&ez->mutex);
- return 0;
+ ret = ezusb_terminateasync(ez, context);
+ break;
+
+ case EZUSB_GETFRAMENUMBER:
+ i = usb_get_current_frame_number(ez->usbdev);
+ ret = put_user(i, (int *)arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
}
- return -ENOIOCTLCMD;
+ up(&ez->mutex);
+ return ret;
}
static struct file_operations ezusb_fops = {
@@ -707,12 +940,12 @@
}
ez->usbdev = usbdev;
usbdev->private = ez;
- if (usb_set_configuration(usbdev, usbdev->config[0].bConfigurationValue)) {
+ if (usb_set_configuration(usbdev, usbdev->config[0].bConfigurationValue) < 0) {
printk(KERN_ERR "ezusb: set_configuration failed\n");
goto err;
}
interface = &usbdev->config[0].interface[0].altsetting[1];
- if (usb_set_interface(usbdev, 0, 1)) {
+ if (usb_set_interface(usbdev, 0, 1) < 0) {
printk(KERN_ERR "ezusb: set_interface failed\n");
goto err;
}
@@ -730,20 +963,12 @@
static void ezusb_disconnect(struct usb_device *usbdev)
{
struct ezusb *ez = (struct ezusb *)usbdev->private;
- struct isodesc *isodesc;
down(&ez->mutex);
- while (!list_empty(&ez->iso)) {
- isodesc = list_entry(ez->iso.next, struct isodesc, isolist);
- list_del(ez->iso.next);
- usb_delete_isochronous(ez->usbdev, isodesc->hcisodesc[0]);
- usb_delete_isochronous(ez->usbdev, isodesc->hcisodesc[1]);
- kfree(isodesc->hcbuf[0]);
- kfree(isodesc->hcbuf[1]);
- vfree(isodesc->buf);
- }
+ destroy_all_async(ez);
ez->usbdev = NULL;
up(&ez->mutex);
+ wake_up(&ez->wait);
usbdev->private = NULL;
MOD_DEC_USE_COUNT;
}
@@ -765,7 +990,10 @@
for (u = 0; u < NREZUSB; u++) {
init_MUTEX(&ezusb[u].mutex);
ezusb[u].usbdev = NULL;
- INIT_LIST_HEAD(&ezusb[u].iso);
+ INIT_LIST_HEAD(&ezusb[u].async_pending);
+ INIT_LIST_HEAD(&ezusb[u].async_completed);
+ init_waitqueue_head(&ezusb[u].wait);
+ spin_lock_init(&ezusb[u].lock);
}
/* register misc device */
if (misc_register(&ezusb_misc)) {
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)