patch-2.3.11 linux/drivers/usb/ezusb.c
Next file: linux/drivers/usb/ezusb.h
Previous file: linux/drivers/usb/cpia.c
Back to the patch index
Back to the overall index
- Lines: 474
- Date:
Wed Jul 14 10:30:36 1999
- Orig file:
v2.3.10/linux/drivers/usb/ezusb.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.10/linux/drivers/usb/ezusb.c linux/drivers/usb/ezusb.c
@@ -0,0 +1,473 @@
+/*****************************************************************************/
+
+/*
+ * 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
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+
+#include "usb.h"
+#include "ezusb.h"
+
+/* --------------------------------------------------------------------- */
+
+#define NREZUSB 1
+
+static struct ezusb {
+ struct semaphore mutex;
+ struct usb_device *usbdev;
+ unsigned int irqep;
+ unsigned int intlen;
+ unsigned char intdata[64];
+} ezusb[NREZUSB];
+
+/* --------------------------------------------------------------------- */
+
+static int ezusb_irq(int state, void *__buffer, int len, void *dev_id)
+{
+ struct ezusb *ez = (struct ezusb *)dev_id;
+
+ if (len > sizeof(ez->intdata))
+ len = sizeof(ez->intdata);
+ ez->intlen = len;
+ memcpy(ez->intdata, __buffer, len);
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+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];
+ devrequest dr;
+ 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;
+ dr.requesttype = 0xc0;
+ dr.request = 0xa0;
+ dr.value = pos;
+ dr.index = 0;
+ dr.length = len;
+ i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, 0), &dr, b, len);
+ if (i) {
+ up(&ez->mutex);
+ printk(KERN_WARNING "ezusb: upload failed pos %u len %u ret %d\n", dr.value, dr.length, 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];
+ devrequest dr;
+ 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;
+ }
+ dr.requesttype = 0x40;
+ dr.request = 0xa0;
+ dr.value = pos;
+ dr.index = 0;
+ dr.length = len;
+ i = ez->usbdev->bus->op->control_msg(ez->usbdev, usb_sndctrlpipe(ez->usbdev, 0), &dr, b, len);
+ if (i) {
+ up(&ez->mutex);
+ printk(KERN_WARNING "ezusb: download failed pos %u len %u ret %d\n", dr.value, dr.length, 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;
+ return 0;
+}
+
+static int ezusb_release(struct inode *inode, struct file *file)
+{
+ struct ezusb *ez = (struct ezusb *)file->private_data;
+ 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;
+ struct ezusb_ctrltransfer ctrl;
+ struct ezusb_bulktransfer bulk;
+ unsigned int len1, ep;
+ unsigned long len2;
+ unsigned int irqep;
+ unsigned char tbuf[1024];
+ int i;
+
+ 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);
+ }
+ 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;
+ }
+ return 0;
+
+ case EZUSB_INTERRUPT:
+ get_user_ret(irqep, (unsigned int *)arg, -EFAULT);
+ if (irqep != ez->irqep) {
+ if (ez->irqep)
+ return -EIO;
+ ez->irqep = irqep;
+ usb_request_irq(ez->usbdev, usb_rcvctrlpipe(ez->usbdev, ez->irqep),
+ ezusb_irq, 2 /* interval */, ez);
+ ez->intlen = 0;
+ return -EAGAIN;
+ }
+ copy_to_user_ret((&((struct ezusb_interrupttransfer *)0)->data) + arg,
+ ez->intdata, 64, -EFAULT);
+ return ez->intlen;
+
+ case EZUSB_BULK:
+ copy_from_user_ret(&bulk, (void *)arg, sizeof(bulk), -EFAULT);
+ 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, usb_rcvbulkpipe(ez->usbdev, bulk.ep & 0x7f),
+ 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, usb_sndbulkpipe(ez->usbdev, bulk.ep & 0x7f),
+ tbuf, len1, &len2);
+ up(&ez->mutex);
+ }
+ if (i) {
+ printk(KERN_WARNING "ezusb: EZUSB_BULK failed ep 0x%x len %u ret %d\n",
+ bulk.ep, bulk.len, i);
+ return -ENXIO;
+ }
+ return len2;
+
+ 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;
+ }
+ return -ENOIOCTLCMD;
+}
+
+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 struct miscdevice ezusb_misc = {
+ 192, "ezusb", &ezusb_fops
+};
+
+/* --------------------------------------------------------------------- */
+
+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;
+
+ /* We don't handle multiple interfaces */
+ if (usbdev->config[0].bNumInterfaces != 1)
+ return -1;
+
+ 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)) {
+ printk(KERN_ERR "ezusb: set_configuration failed\n");
+ goto err;
+ }
+ interface = &usbdev->config[0].altsetting[1].interface[0];
+ if (usb_set_interface(usbdev, 0, 1)) {
+ 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);
+ ez->usbdev = NULL;
+ up(&ez->mutex);
+ usbdev->private = NULL;
+ MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver ezusb_driver = {
+ "ezusb",
+ ezusb_probe,
+ ezusb_disconnect,
+ { NULL, NULL }
+};
+
+/* --------------------------------------------------------------------- */
+
+int ezusb_init(void)
+{
+ unsigned u;
+
+ /* initialize struct */
+ for (u = 0; u < NREZUSB; u++) {
+ init_MUTEX(&ezusb[u].mutex);
+ ezusb[u].usbdev = NULL;
+ ezusb[u].irqep = 0;
+ }
+ /* register misc device */
+ if (misc_register(&ezusb_misc)) {
+ printk(KERN_WARNING "ezusb: cannot register minor %d\n", ezusb_misc.minor);
+ return -1;
+ }
+ usb_register(&ezusb_driver);
+ printk(KERN_INFO "ezusb: Anchorchip firmware download driver registered\n");
+ return 0;
+}
+
+void ezusb_cleanup(void)
+{
+ usb_deregister(&ezusb_driver);
+ misc_deregister(&ezusb_misc);
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+int minor = 192;
+
+int init_module(void)
+{
+ ezusb_misc.minor = minor;
+ return ezusb_init();
+}
+
+void cleanup_module(void)
+{
+ ezusb_cleanup();
+}
+
+#endif
+
+/* --------------------------------------------------------------------- */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)