⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 devio.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/*****************************************************************************//* *      devio.c  --  User space communication with USB devices. * *      Copyright (C) 1999-2000  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. * *  $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ * *  This file implements the usbfs/x/y files, where *  x is the bus number and y the device number. * *  It allows user space programs/"drivers" to communicate directly *  with USB devices without intervening kernel driver. * *  Revision history *    22.12.1999   0.1   Initial release (split from proc_usb.c) *    04.01.2000   0.2   Turned into its own filesystem *    30.09.2005   0.3   Fix user-triggerable oops in async URB delivery *    			 (CAN-2005-3055) *//*****************************************************************************/#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/signal.h>#include <linux/poll.h>#include <linux/module.h>#include <linux/usb.h>#include <linux/usbdevice_fs.h>#include <linux/cdev.h>#include <linux/notifier.h>#include <asm/uaccess.h>#include <asm/byteorder.h>#include <linux/moduleparam.h>#include "hcd.h"	/* for usbcore internals */#include "usb.h"#define USB_MAXBUS			64#define USB_DEVICE_MAX			USB_MAXBUS * 128static struct class *usb_device_class;struct async {	struct list_head asynclist;	struct dev_state *ps;	pid_t pid;	uid_t uid, euid;	unsigned int signr;	unsigned int ifnum;	void __user *userbuffer;	void __user *userurb;	struct urb *urb;};static int usbfs_snoop = 0;module_param (usbfs_snoop, bool, S_IRUGO | S_IWUSR);MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");#define snoop(dev, format, arg...)				\	do {							\		if (usbfs_snoop)				\			dev_info( dev , format , ## arg);	\	} while (0)#define USB_DEVICE_DEV		MKDEV(USB_DEVICE_MAJOR, 0)#define	MAX_USBFS_BUFFER_SIZE	16384static inline int connected (struct usb_device *dev){	return dev->state != USB_STATE_NOTATTACHED;}static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig){	loff_t ret;	lock_kernel();	switch (orig) {	case 0:		file->f_pos = offset;		ret = file->f_pos;		break;	case 1:		file->f_pos += offset;		ret = file->f_pos;		break;	case 2:	default:		ret = -EINVAL;	}	unlock_kernel();	return ret;}static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){	struct dev_state *ps = (struct dev_state *)file->private_data;	struct usb_device *dev = ps->dev;	ssize_t ret = 0;	unsigned len;	loff_t pos;	int i;	pos = *ppos;	usb_lock_device(dev);	if (!connected(dev)) {		ret = -ENODEV;		goto err;	} else if (pos < 0) {		ret = -EINVAL;		goto err;	}	if (pos < sizeof(struct usb_device_descriptor)) {		struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL);		if (!desc) {			ret = -ENOMEM;			goto err;		}		memcpy(desc, &dev->descriptor, sizeof(dev->descriptor));		le16_to_cpus(&desc->bcdUSB);		le16_to_cpus(&desc->idVendor);		le16_to_cpus(&desc->idProduct);		le16_to_cpus(&desc->bcdDevice);		len = sizeof(struct usb_device_descriptor) - pos;		if (len > nbytes)			len = nbytes;		if (copy_to_user(buf, ((char *)desc) + pos, len)) {			kfree(desc);			ret = -EFAULT;			goto err;		}		kfree(desc);		*ppos += len;		buf += len;		nbytes -= len;		ret += len;	}	pos = sizeof(struct usb_device_descriptor);	for (i = 0; nbytes && i < dev->descriptor.bNumConfigurations; i++) {		struct usb_config_descriptor *config =			(struct usb_config_descriptor *)dev->rawdescriptors[i];		unsigned int length = le16_to_cpu(config->wTotalLength);		if (*ppos < pos + length) {			/* The descriptor may claim to be longer than it			 * really is.  Here is the actual allocated length. */			unsigned alloclen =				le16_to_cpu(dev->config[i].desc.wTotalLength);			len = length - (*ppos - pos);			if (len > nbytes)				len = nbytes;			/* Simply don't write (skip over) unallocated parts */			if (alloclen > (*ppos - pos)) {				alloclen -= (*ppos - pos);				if (copy_to_user(buf,				    dev->rawdescriptors[i] + (*ppos - pos),				    min(len, alloclen))) {					ret = -EFAULT;					goto err;				}			}			*ppos += len;			buf += len;			nbytes -= len;			ret += len;		}		pos += length;	}err:	usb_unlock_device(dev);	return ret;}/* * async list handling */static struct async *alloc_async(unsigned int numisoframes){        unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);        struct async *as = kzalloc(assize, GFP_KERNEL);        if (!as)                return NULL;	as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);	if (!as->urb) {		kfree(as);		return NULL;	}        return as;}static void free_async(struct async *as){	kfree(as->urb->transfer_buffer);	kfree(as->urb->setup_packet);	usb_free_urb(as->urb);	kfree(as);}static inline void async_newpending(struct async *as){        struct dev_state *ps = as->ps;        unsigned long flags;                spin_lock_irqsave(&ps->lock, flags);        list_add_tail(&as->asynclist, &ps->async_pending);        spin_unlock_irqrestore(&ps->lock, flags);}static inline void async_removepending(struct async *as){        struct dev_state *ps = as->ps;        unsigned long flags;                spin_lock_irqsave(&ps->lock, flags);        list_del_init(&as->asynclist);        spin_unlock_irqrestore(&ps->lock, flags);}static inline struct async *async_getcompleted(struct dev_state *ps){        unsigned long flags;        struct async *as = NULL;        spin_lock_irqsave(&ps->lock, flags);        if (!list_empty(&ps->async_completed)) {                as = list_entry(ps->async_completed.next, struct async, asynclist);                list_del_init(&as->asynclist);        }        spin_unlock_irqrestore(&ps->lock, flags);        return as;}static inline struct async *async_getpending(struct dev_state *ps, void __user *userurb){        unsigned long flags;        struct async *as;        spin_lock_irqsave(&ps->lock, flags);	list_for_each_entry(as, &ps->async_pending, asynclist)		if (as->userurb == userurb) {			list_del_init(&as->asynclist);			spin_unlock_irqrestore(&ps->lock, flags);			return as;		}        spin_unlock_irqrestore(&ps->lock, flags);        return NULL;}static void snoop_urb(struct urb *urb, void __user *userurb){	int j;	unsigned char *data = urb->transfer_buffer;	if (!usbfs_snoop)		return;	if (urb->pipe & USB_DIR_IN)		dev_info(&urb->dev->dev, "direction=IN\n");	else		dev_info(&urb->dev->dev, "direction=OUT\n");	dev_info(&urb->dev->dev, "userurb=%p\n", userurb);	dev_info(&urb->dev->dev, "transfer_buffer_length=%d\n",		 urb->transfer_buffer_length);	dev_info(&urb->dev->dev, "actual_length=%d\n", urb->actual_length);	dev_info(&urb->dev->dev, "data: ");	for (j = 0; j < urb->transfer_buffer_length; ++j)		printk ("%02x ", data[j]);	printk("\n");}static void async_completed(struct urb *urb, struct pt_regs *regs){        struct async *as = (struct async *)urb->context;        struct dev_state *ps = as->ps;	struct siginfo sinfo;        spin_lock(&ps->lock);        list_move_tail(&as->asynclist, &ps->async_completed);        spin_unlock(&ps->lock);	if (as->signr) {		sinfo.si_signo = as->signr;		sinfo.si_errno = as->urb->status;		sinfo.si_code = SI_ASYNCIO;		sinfo.si_addr = as->userurb;		kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, 				      as->euid);	}	snoop(&urb->dev->dev, "urb complete\n");	snoop_urb(urb, as->userurb);	wake_up(&ps->wait);}static void destroy_async (struct dev_state *ps, struct list_head *list){	struct async *as;	unsigned long flags;	spin_lock_irqsave(&ps->lock, flags);	while (!list_empty(list)) {		as = list_entry(list->next, struct async, asynclist);		list_del_init(&as->asynclist);		/* drop the spinlock so the completion handler can run */		spin_unlock_irqrestore(&ps->lock, flags);		usb_kill_urb(as->urb);		spin_lock_irqsave(&ps->lock, flags);	}	spin_unlock_irqrestore(&ps->lock, flags);	as = async_getcompleted(ps);	while (as) {		free_async(as);		as = async_getcompleted(ps);	}}static void destroy_async_on_interface (struct dev_state *ps, unsigned int ifnum){	struct list_head *p, *q, hitlist;	unsigned long flags;	INIT_LIST_HEAD(&hitlist);	spin_lock_irqsave(&ps->lock, flags);	list_for_each_safe(p, q, &ps->async_pending)		if (ifnum == list_entry(p, struct async, asynclist)->ifnum)			list_move_tail(p, &hitlist);	spin_unlock_irqrestore(&ps->lock, flags);	destroy_async(ps, &hitlist);}static inline void destroy_all_async(struct dev_state *ps){	        destroy_async(ps, &ps->async_pending);}/* * interface claims are made only at the request of user level code, * which can also release them (explicitly or by closing files). * they're also undone when devices disconnect. */static int driver_probe (struct usb_interface *intf,			 const struct usb_device_id *id){	return -ENODEV;}static void driver_disconnect(struct usb_interface *intf){	struct dev_state *ps = usb_get_intfdata (intf);	unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber;	if (!ps)		return;	/* NOTE:  this relies on usbcore having canceled and completed	 * all pending I/O requests; 2.6 does that.	 */	if (likely(ifnum < 8*sizeof(ps->ifclaimed)))		clear_bit(ifnum, &ps->ifclaimed);	else		warn("interface number %u out of range", ifnum);	usb_set_intfdata (intf, NULL);	/* force async requests to complete */	destroy_async_on_interface(ps, ifnum);}struct usb_driver usbfs_driver = {	.owner =	THIS_MODULE,	.name =		"usbfs",	.probe =	driver_probe,	.disconnect =	driver_disconnect,};static int claimintf(struct dev_state *ps, unsigned int ifnum){	struct usb_device *dev = ps->dev;	struct usb_interface *intf;	int err;	if (ifnum >= 8*sizeof(ps->ifclaimed))		return -EINVAL;	/* already claimed */	if (test_bit(ifnum, &ps->ifclaimed))		return 0;	/* lock against other changes to driver bindings */	down_write(&usb_bus_type.subsys.rwsem);	intf = usb_ifnum_to_if(dev, ifnum);	if (!intf)		err = -ENOENT;	else		err = usb_driver_claim_interface(&usbfs_driver, intf, ps);	up_write(&usb_bus_type.subsys.rwsem);	if (err == 0)		set_bit(ifnum, &ps->ifclaimed);	return err;}static int releaseintf(struct dev_state *ps, unsigned int ifnum){	struct usb_device *dev;	struct usb_interface *intf;	int err;	err = -EINVAL;	if (ifnum >= 8*sizeof(ps->ifclaimed))		return err;	dev = ps->dev;	/* lock against other changes to driver bindings */	down_write(&usb_bus_type.subsys.rwsem);	intf = usb_ifnum_to_if(dev, ifnum);	if (!intf)		err = -ENOENT;	else if (test_and_clear_bit(ifnum, &ps->ifclaimed)) {		usb_driver_release_interface(&usbfs_driver, intf);		err = 0;	}	up_write(&usb_bus_type.subsys.rwsem);	return err;}static int checkintf(struct dev_state *ps, unsigned int ifnum){	if (ps->dev->state != USB_STATE_CONFIGURED)		return -EHOSTUNREACH;	if (ifnum >= 8*sizeof(ps->ifclaimed))		return -EINVAL;	if (test_bit(ifnum, &ps->ifclaimed))		return 0;	/* if not yet claimed, claim it for the driver */	dev_warn(&ps->dev->dev, "usbfs: process %d (%s) did not claim interface %u before use\n",	       current->pid, current->comm, ifnum);	return claimintf(ps, ifnum);}static int findintfep(struct usb_device *dev, unsigned int ep){	unsigned int i, j, e;        struct usb_interface *intf;	struct usb_host_interface *alts;	struct usb_endpoint_descriptor *endpt;	if (ep & ~(USB_DIR_IN|0xf))		return -EINVAL;	if (!dev->actconfig)		return -ESRCH;	for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {		intf = dev->actconfig->interface[i];		for (j = 0; j < intf->num_altsetting; j++) {                        alts = &intf->altsetting[j];			for (e = 0; e < alts->desc.bNumEndpoints; e++) {				endpt = &alts->endpoint[e].desc;				if (endpt->bEndpointAddress == ep)					return alts->desc.bInterfaceNumber;			}		}	}	return -ENOENT; }static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsigned int index){	int ret = 0;	if (ps->dev->state != USB_STATE_CONFIGURED)		return -EHOSTUNREACH;	if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))		return 0;	index &= 0xff;	switch (requesttype & USB_RECIP_MASK) {	case USB_RECIP_ENDPOINT:		if ((ret = findintfep(ps->dev, index)) >= 0)			ret = checkintf(ps, ret);		break;	case USB_RECIP_INTERFACE:		ret = checkintf(ps, index);		break;	}	return ret;}static struct usb_device *usbdev_lookup_minor(int minor){	struct class_device *class_dev;	struct usb_device *dev = NULL;	down(&usb_device_class->sem);	list_for_each_entry(class_dev, &usb_device_class->children, node) {		if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {			dev = class_dev->class_data;			break;		}	}	up(&usb_device_class->sem);	return dev;};/* * file operations */static int usbdev_open(struct inode *inode, struct file *file){	struct usb_device *dev = NULL;	struct dev_state *ps;	int ret;	/* 	 * no locking necessary here, as chrdev_open has the kernel lock	 * (still acquire the kernel lock for safety)	 */	ret = -ENOMEM;	if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))		goto out_nolock;	lock_kernel();	ret = -ENOENT;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -