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

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)