www.pudn.com > usbold11.rar > ezusb.c
/*****************************************************************************/ /* * 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 * 03.12.99 Now that USB error codes are negative, return them to application * instead of ENXIO * $Id: ezusb.c,v 1.24 2000/01/09 12:19:42 fliegl Exp $ */ /*****************************************************************************/ #include#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 *userdata; unsigned datalen; void *context; urb_t urb; }; /*-------------------------------------------------------------------*/ static struct async *alloc_async(unsigned int numisoframes) { unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t); struct async *as = kmalloc(assize, GFP_KERNEL); if (!as) return NULL; memset(as, 0, assize); as->urb.number_of_packets = numisoframes; return as; } static void free_async(struct async *as) { if (as->urb.transfer_buffer) kfree(as->urb.transfer_buffer); kfree(as); } /* --------------------------------------------------------------------- */ 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_removepending(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__ 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->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 void async_completed(purb_t urb) { struct async *as = (struct async *)urb->context; struct ezusb *ez = as->ez; unsigned cnt; printk(KERN_DEBUG "ezusb: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", urb->status, urb->error_count, urb->actual_length, urb->pipe); spin_lock(&ez->lock); list_del(&as->asynclist); list_add_tail(&as->asynclist, &ez->async_completed); spin_unlock(&ez->lock); wake_up(&ez->wait); } 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); /* discard_urb calls the completion handler with status == USB_ST_URB_KILLED */ usb_unlink_urb(&as->urb); spin_lock_irqsave(&ez->lock, flags); } spin_unlock_irqrestore(&ez->lock, flags); while ((as = async_getcompleted(ez))) free_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 < 0) { 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 i; } 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; } printk("writemem: %d %p %d\n",pos,b,len); i = usb_control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), 0xa0, 0x40, pos, 0, b, len, HZ); if (i < 0) { 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 i; } 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 i; } 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; int 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 = usb_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; } #define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) #define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) char* stuff[512]; static void int_compl(purb_t purb) { printk("INT_COMPL\n"); } static int ezusb_interrupt(struct ezusb *ez, struct ezusb_interrupt *ab) { urb_t *urb; unsigned int pipe; urb=(urb_t*)kmalloc(sizeof(urb_t),GFP_KERNEL); if (!urb) { return -ENOMEM; } memset(urb,0,sizeof(urb_t)); if ((ab->ep & ~0x80) >= 16) return -EINVAL; if (ab->ep & 0x80) { pipe = usb_rcvintpipe(ez->usbdev, ab->ep & 0x7f); if (ab->len > 0 && !access_ok(VERIFY_WRITE, ab->data, ab->len)) return -EFAULT; } else pipe = usb_sndintpipe(ez->usbdev, ab->ep & 0x7f); memcpy(stuff,ab->data,64); urb->transfer_buffer=stuff; urb->transfer_buffer_length=ab->len; urb->complete=int_compl; urb->pipe=pipe; urb->dev=ez->usbdev; urb->interval=ab->interval; return usb_submit_urb(urb); } static int ezusb_requestbulk(struct ezusb *ez, struct ezusb_asyncbulk *ab) { struct async *as = NULL; void *data = NULL; unsigned int pipe; int ret; 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 (ab->len > 0 && !(data = kmalloc(ab->len, GFP_KERNEL))) return -ENOMEM; if (!(as = alloc_async(0))) { if (data) kfree(data); return -ENOMEM; } INIT_LIST_HEAD(&as->asynclist); as->ez = ez; as->userdata = ab->data; as->datalen = ab->len; as->context = ab->context; as->urb.dev = ez->usbdev; as->urb.pipe = pipe; as->urb.transfer_flags = 0; as->urb.transfer_buffer = data; as->urb.transfer_buffer_length = ab->len; as->urb.context = as; as->urb.complete = (usb_complete_t)async_completed; if (ab->len > 0 && !(ab->ep & 0x80)) { if (copy_from_user(data, ab->data, ab->len)) { free_async(as); return -EFAULT; } as->userdata = NULL; /* no need to copy back at completion */ } async_newpending(as); if ((ret = usb_submit_urb(&as->urb))) { printk(KERN_DEBUG "ezusb: bulk: usb_submit_urb returned %d\n", ret); async_removepending(as); free_async(as); return -EINVAL; /* return ret; */ } return 0; } static int ezusb_requestiso(struct ezusb *ez, struct ezusb_asynciso *ai, unsigned char *cmd) { struct async *as; void *data = NULL; unsigned int maxpkt, pipe, dsize, totsize, i, j; int ret; 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; if (ai->ep & 0x80) if (dsize > 0 && !access_ok(VERIFY_WRITE, ai->data, dsize)) return -EFAULT; if (dsize > 0 && !(data = kmalloc(dsize, GFP_KERNEL))) { printk("dsize: %d failed\n",dsize); return -ENOMEM; } if (!(as = alloc_async(ai->framecnt))) { if (data) kfree(data); printk("alloc_async failed\n"); return -ENOMEM; } INIT_LIST_HEAD(&as->asynclist); as->ez = ez; as->userdata = ai->data; as->datalen = dsize; as->context = ai->context; as->urb.dev = ez->usbdev; as->urb.pipe = pipe; as->urb.transfer_flags = USB_ISO_ASAP; as->urb.transfer_buffer = data; as->urb.transfer_buffer_length = dsize; as->urb.context = as; as->urb.complete = (usb_complete_t)async_completed; for (i = totsize = 0; i < as->urb.number_of_packets; i++) { as->urb.iso_frame_desc[i].offset = totsize; if (get_user(j, (int *)(cmd + i * sizeof(struct ezusb_isoframestat)))) { free_async(as); return -EFAULT; } as->urb.iso_frame_desc[i].length = j; totsize += j; } if (dsize > 0 && totsize > 0 && !(ai->ep & 0x80)) { if (copy_from_user(data, ai->data, totsize)) { free_async(as); return -EFAULT; } as->userdata = NULL; /* no need to copy back at completion */ } async_newpending(as); if ((ret = usb_submit_urb(&as->urb))) { printk(KERN_DEBUG "ezusb: iso: usb_submit_urb returned %d\n", ret); async_removepending(as); free_async(as); return -EINVAL; /* return ret; */ } return 0; } static int ezusb_terminateasync(struct ezusb *ez, void *context) { struct async *as; int ret = 0; while ((as = async_getpending(ez, context))) { usb_unlink_urb(&as->urb); ret++; } return ret; } static int ezusb_asynccompl(struct async *as, void *arg) { struct ezusb_asynccompleted *cpl; unsigned int numframes, cplsize, i; if (as->userdata) { if (copy_to_user(as->userdata, as->urb.transfer_buffer, as->datalen)) { free_async(as); return -EFAULT; } } numframes = as->urb.number_of_packets; cplsize = sizeof(struct ezusb_asynccompleted) + numframes * sizeof(struct ezusb_isoframestat); if (!(cpl = kmalloc(cplsize, GFP_KERNEL))) { free_async(as); return -ENOMEM; } cpl->status = as->urb.status; cpl->length = as->urb.actual_length; cpl->context = as->context; for (i = 0; i < numframes; i++) { cpl->isostat[i].length = as->urb.iso_frame_desc[i].length; cpl->isostat[i].status = as->urb.iso_frame_desc[i].status; } free_async(as); if (copy_to_user(arg, cpl, cplsize)) { kfree(cpl); return -EFAULT; } kfree(cpl); 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 ezusb_interrupt ezint; 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; case EZUSB_INTERRUPT: printk("INT START\n"); if (copy_from_user(&ezint, (void *)arg, sizeof(ezint))) { ret = -EFAULT; break; } ret=ezusb_interrupt(ez,&ezint); 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 /* lock */ }; /* --------------------------------------------------------------------- */ static void * ezusb_probe(struct usb_device *usbdev, unsigned int ifnum) { 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) #if 1 && (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999) && (usbdev->descriptor.idVendor != 0x1234 || usbdev->descriptor.idProduct != 0x5678) #endif ) return NULL; /* We don't handle multiple configurations */ if (usbdev->descriptor.bNumConfigurations != 1) return NULL; #if 0 /* We don't handle multiple interfaces */ if (usbdev->config[0].bNumInterfaces != 1) return NULL; #endif down(&ez->mutex); if (ez->usbdev) { up(&ez->mutex); printk(KERN_INFO "ezusb: device already used\n"); return NULL; } ez->usbdev = usbdev; 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 ez; err: up(&ez->mutex); ez->usbdev = NULL; return NULL; } static void ezusb_disconnect(struct usb_device *usbdev, void *ptr) { struct ezusb *ez = (struct ezusb *)ptr; down(&ez->mutex); destroy_all_async(ez); ez->usbdev = NULL; up(&ez->mutex); wake_up(&ez->wait); MOD_DEC_USE_COUNT; } static struct usb_driver ezusb_driver = { "ezusb", ezusb_probe, ezusb_disconnect, { NULL, NULL }, &ezusb_fops, 192 }; /* --------------------------------------------------------------------- */ 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); } /* register misc device */ 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 minor = 192; int init_module(void) { return ezusb_init(); } void cleanup_module(void) { ezusb_cleanup(); } #endif /* --------------------------------------------------------------------- */