/*****************************************************************************/ /* * ezusb.c -- Firmware download miscdevice for Anchorchips EZUSB microcontrollers. * * Copyright (C) 1999 * Thomas Sailer (sailer@ife.ee.ethz.ch) * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * History: * 0.1 26.05.99 Created * 0.2 23.07.99 Removed EZUSB_INTERRUPT. Interrupt pipes may be polled with * 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 Set callback_frames to the total number of frames to make * it work with OHCI-HCD * */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include "usb.h" #include "ezusb.h" /* --------------------------------------------------------------------- */ #define NREZUSB 1 static struct ezusb { struct semaphore mutex; struct usb_device *usbdev; struct list_head async_pending; struct list_head async_completed; wait_queue_head_t wait; spinlock_t lock; } ezusb[NREZUSB]; 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; }; /* --------------------------------------------------------------------- */ extern inline unsigned int ld2(unsigned int x) { unsigned int 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; } #if 0 /* why doesn't this work properly on i386? */ extern inline unsigned int ld2(unsigned int x) { unsigned int r; __asm__("bsrl %1,%0" : "=r" (r) : "g" (x)); return r; } #endif /* --------------------------------------------------------------------- */ 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 bulk_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: bulk_completed: status %d rval %d\n", status, rval); as->completed.length = rval; 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 int iso_completed(int status, void *__buffer, int rval, void *dev_id) { #if 1 struct async *as = (struct async *)dev_id; #else struct usb_isoc_desc *id = (struct usb_isoc_desc *)dev_id; struct async *as = (struct async *)id->context; #endif struct ezusb *ez = as->ez; unsigned cnt; printk(KERN_DEBUG "ezusb: iso_completed: status %d rval %d\n", status, rval); as->completed.length = rval; 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; } 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); } /* --------------------------------------------------------------------- */ static loff_t ezusb_llseek(struct file *file, loff_t offset, int origin) { struct ezusb *ez = (struct ezusb *)file->private_data; switch(origin) { case 1: offset += file->f_pos; break; case 2: offset += 0x10000; break; } if (offset < 0 || offset >= 0x10000) return -EINVAL; return (file->f_pos = offset); } static ssize_t ezusb_read(struct file *file, char *buf, size_t sz, loff_t *ppos) { struct ezusb *ez = (struct ezusb *)file->private_data; unsigned pos = *ppos; unsigned ret = 0; unsigned len; unsigned char b[64]; int i; if (*ppos < 0 || *ppos >= 0x10000) return -EINVAL; down(&ez->mutex); if (!ez->usbdev) { up(&ez->mutex); return -EIO; } while (sz > 0 && pos < 0x10000) { len = sz; if (len > sizeof(b)) len = sizeof(b); if (pos + len > 0x10000) len = 0x10000 - pos; i = usb_control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0), 0xa0, 0xc0, pos, 0, b, len, HZ); if (i) { up(&ez->mutex); printk(KERN_WARNING "ezusb: upload failed pos %u len %u ret %d\n", pos, len, i); *ppos = pos; if (ret) return ret; return -ENXIO; } if (copy_to_user(buf, b, len)) { up(&ez->mutex); *ppos = pos; if (ret) return ret; return -EFAULT; } pos += len; buf += len; sz -= len; ret += len; } up(&ez->mutex); *ppos = pos; return ret; } static ssize_t ezusb_write(struct file *file, const char *buf, size_t sz, loff_t *ppos) { struct ezusb *ez = (struct ezusb *)file->private_data; unsigned pos = *ppos; unsigned ret = 0; unsigned len; unsigned char b[64]; int i; if (*ppos < 0 || *ppos >= 0x10000) return -EINVAL; down(&ez->mutex); if (!ez->usbdev) { up(&ez->mutex); return -EIO; } while (sz > 0 && pos < 0x10000) { len = sz; if (len > sizeof(b)) len = sizeof(b); if (pos + len > 0x10000) len = 0x10000 - pos; if (copy_from_user(b, buf, len)) { up(&ez->mutex); *ppos = pos; if (ret) return ret; return -EFAULT; } i = usb_control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), 0xa0, 0x40, pos, 0, b, len, HZ); if (i) { up(&ez->mutex); printk(KERN_WARNING "ezusb: download failed pos %u len %u ret %d\n", pos, len, i); *ppos = pos; if (ret) return ret; return -ENXIO; } pos += len; buf += len; sz -= len; ret += len; } up(&ez->mutex); *ppos = pos; return ret; } static int ezusb_open(struct inode *inode, struct file *file) { struct ezusb *ez = &ezusb[0]; down(&ez->mutex); while (!ez->usbdev) { up(&ez->mutex); if (!(file->f_flags & O_NONBLOCK)) { return -EIO; } schedule_timeout(HZ/2); if (signal_pending(current)) return -EAGAIN; down(&ez->mutex); } 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, unsigned int timeout, 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, timeout); 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, unsigned int timeout, 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, timeout); 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 int ezusb_setinterface(struct usb_device *usbdev, unsigned int interface, unsigned int altsetting) { 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, bulk_completed, as->data, ab->len, as))) { async_removelist(as); goto err_inval; } return 0; err_fault: if (as) { if (as->data) free_page((unsigned long)as->data); kfree(as); } 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; printk(KERN_DEBUG "ezusb: iso: dsize %d\n", dsize); 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; printk(KERN_DEBUG "ezusb: iso: assize %d\n", assize); 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*/ai->framecnt; as->desc.iso->callback_fn = iso_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_old_ctrltransfer opctrl; struct usb_proc_old_bulktransfer opbulk; struct usb_proc_setinterface psetintf; struct ezusb_ctrltransfer ctrl; struct ezusb_bulktransfer bulk; struct ezusb_old_ctrltransfer octrl; struct ezusb_old_bulktransfer obulk; struct ezusb_setinterface setintf; 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 USB_PROC_CONTROL: if (copy_from_user(&pctrl, (void *)arg, sizeof(pctrl))) { ret = -EFAULT; break; } ret = ezusb_control(ez->usbdev, pctrl.requesttype, pctrl.request, pctrl.value, pctrl.index, pctrl.length, (pctrl.timeout * HZ + 500) / 1000, pctrl.data); break; case USB_PROC_BULK: if (copy_from_user(&pbulk, (void *)arg, sizeof(pbulk))) { ret = -EFAULT; break; } ret = ezusb_bulk(ez->usbdev, pbulk.ep, pbulk.len, (pbulk.timeout * HZ + 500) / 1000, pbulk.data); break; case USB_PROC_OLD_CONTROL: if (copy_from_user(&opctrl, (void *)arg, sizeof(opctrl))) { ret = -EFAULT; break; } ret = ezusb_control(ez->usbdev, opctrl.requesttype, opctrl.request, opctrl.value, opctrl.index, opctrl.length, HZ, opctrl.data); break; case USB_PROC_OLD_BULK: if (copy_from_user(&opbulk, (void *)arg, sizeof(opbulk))) { ret = -EFAULT; break; } ret = ezusb_bulk(ez->usbdev, opbulk.ep, opbulk.len, 5*HZ, opbulk.data); break; case USB_PROC_RESETEP: if (get_user(ep, (unsigned int *)arg)) { ret = -EFAULT; break; } ret = ezusb_resetep(ez->usbdev, ep); break; case USB_PROC_SETINTERFACE: if (copy_from_user(&psetintf, (void *)arg, sizeof(psetintf))) { ret = -EFAULT; break; } ret = ezusb_setinterface(ez->usbdev, psetintf.interface, psetintf.altsetting); break; case USB_PROC_SETCONFIGURATION: if (get_user(cfg, (unsigned int *)arg)) { ret = -EFAULT; break; } ret = ezusb_setconfiguration(ez->usbdev, cfg); break; case EZUSB_CONTROL: if (copy_from_user(&ctrl, (void *)arg, sizeof(ctrl))) { ret = -EFAULT; break; } ret = ezusb_control(ez->usbdev, ctrl.requesttype, ctrl.request, ctrl.value, ctrl.index, ctrl.length, (ctrl.timeout * HZ + 500) / 1000, ctrl.data); break; case EZUSB_BULK: if (copy_from_user(&bulk, (void *)arg, sizeof(bulk))) { ret = -EFAULT; break; } ret = ezusb_bulk(ez->usbdev, bulk.ep, bulk.len, (bulk.timeout * HZ + 500) / 1000, bulk.data); break; case EZUSB_OLD_CONTROL: if (copy_from_user(&octrl, (void *)arg, sizeof(octrl))) { ret = -EFAULT; break; } if (octrl.dlen != octrl.length) { ret = -EINVAL; break; } ret = ezusb_control(ez->usbdev, octrl.requesttype, octrl.request, octrl.value, octrl.index, octrl.length, HZ, octrl.data); break; case EZUSB_OLD_BULK: if (copy_from_user(&obulk, (void *)arg, sizeof(obulk))) { ret = -EFAULT; break; } ret = ezusb_bulk(ez->usbdev, obulk.ep, obulk.len, 5*HZ, obulk.data); break; case EZUSB_RESETEP: if (get_user(ep, (unsigned int *)arg)) { ret = -EFAULT; break; } 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; case EZUSB_SETCONFIGURATION: if (get_user(cfg, (unsigned int *)arg)) { ret = -EFAULT; break; } ret = ezusb_setconfiguration(ez->usbdev, cfg); break; case EZUSB_ASYNCCOMPLETED: as = NULL; 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); schedule(); down(&ez->mutex); } remove_wait_queue(&ez->wait, &wait); current->state = TASK_RUNNING; if (as) { ret = ezusb_asynccompl(as, (void *)arg); break; } if (signal_pending(current)) { ret = -EINTR; break; } ret = -EIO; break; case EZUSB_ASYNCCOMPLETEDNB: if ((as = async_getcompleted(ez))) { ret = ezusb_asynccompl(as, (void *)arg); break; } ret = -EAGAIN; break; case EZUSB_REQUESTBULK: if (copy_from_user(&abulk, (void *)arg, sizeof(abulk))) { ret = -EFAULT; break; } ret = ezusb_requestbulk(ez, &abulk); break; case EZUSB_REQUESTISO: if (copy_from_user(&aiso, (void *)arg, sizeof(aiso))) { ret = -EFAULT; break; } ret = ezusb_requestiso(ez, &aiso, ((unsigned char *)arg)+sizeof(aiso)); break; case EZUSB_TERMINATEASYNC: if (get_user(context, (void **)arg)) { ret = -EFAULT; break; } 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; } up(&ez->mutex); return ret; } static struct file_operations ezusb_fops = { ezusb_llseek, ezusb_read, ezusb_write, NULL, /* readdir */ NULL, /* poll */ ezusb_ioctl, NULL, /* mmap */ ezusb_open, NULL, /* flush */ ezusb_release, NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */ NULL /* lock */ }; /* --------------------------------------------------------------------- */ static int ezusb_probe(struct usb_device *usbdev) { struct ezusb *ez = &ezusb[0]; struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; #undef KERN_DEBUG #define KERN_DEBUG "" printk(KERN_DEBUG "ezusb: probe: vendor id 0x%x, device id 0x%x\n", usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); /* the 1234:5678 is just a self assigned test ID */ if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && (usbdev->descriptor.idVendor != 0x1234 || usbdev->descriptor.idProduct != 0x5678)) return -1; /* We don't handle multiple configurations */ if (usbdev->descriptor.bNumConfigurations != 1) return -1; #if 0 /* We don't handle multiple interfaces */ if (usbdev->config[0].bNumInterfaces != 1) return -1; #endif down(&ez->mutex); if (ez->usbdev) { up(&ez->mutex); printk(KERN_INFO "ezusb: device already used\n"); return -1; } ez->usbdev = usbdev; usbdev->private = ez; 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) < 0) { printk(KERN_ERR "ezusb: set_interface failed\n"); goto err; } up(&ez->mutex); MOD_INC_USE_COUNT; return 0; err: up(&ez->mutex); ez->usbdev = NULL; usbdev->private = NULL; return -1; } static void ezusb_disconnect(struct usb_device *usbdev) { struct ezusb *ez = (struct ezusb *)usbdev->private; down(&ez->mutex); destroy_all_async(ez); ez->usbdev = NULL; up(&ez->mutex); wake_up(&ez->wait); usbdev->private = NULL; MOD_DEC_USE_COUNT; } static struct usb_driver ezusb_driver = { "ezusb", ezusb_probe, ezusb_disconnect, { NULL, NULL }, &ezusb_fops, 32 }; /* --------------------------------------------------------------------- */ int ezusb_init(void) { unsigned u; /* initialize struct */ for (u = 0; u < NREZUSB; u++) { init_MUTEX(&ezusb[u].mutex); ezusb[u].usbdev = NULL; 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); } usb_register(&ezusb_driver); printk(KERN_INFO "ezusb: Anchorchip firmware download driver registered\n"); return 0; } void ezusb_cleanup(void) { usb_deregister(&ezusb_driver); } /* --------------------------------------------------------------------- */ #ifdef MODULE int init_module(void) { return ezusb_init(); } void cleanup_module(void) { ezusb_cleanup(); } #endif /* --------------------------------------------------------------------- */