📄 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 <linux/version.h>#include <linux/module.h>#include <linux/socket.h>#include <linux/miscdevice.h>#include <linux/list.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <asm/uaccess.h>//#include <linux/spinlock.h>#include "usb.h"#include "ezusb.h"/* --------------------------------------------------------------------- */#define NREZUSB 1static 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){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -