📄 devio.c
字号:
/*****************************************************************************//* * 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 usbdevfs/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 *//*****************************************************************************/#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 <asm/uaccess.h>#include <asm/byteorder.h>#include <linux/moduleparam.h>#include "hcd.h" /* for usbcore internals */#include "usb.h"struct async { struct list_head asynclist; struct dev_state *ps; struct task_struct *task; 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)static 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; down(&dev->serialize); if (!connected(dev)) { ret = -ENODEV; goto err; } else if (pos < 0) { ret = -EINVAL; goto err; } if (pos < sizeof(struct usb_device_descriptor)) { len = sizeof(struct usb_device_descriptor) - pos; if (len > nbytes) len = nbytes; if (copy_to_user(buf, ((char *)&dev->descriptor) + pos, len)) { ret = -EFAULT; goto err; } *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 = 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: up(&dev->serialize); 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 = kmalloc(assize, GFP_KERNEL); if (!as) return NULL; memset(as, 0, assize); as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL); if (!as->urb) { kfree(as); return NULL; } return as;}static void free_async(struct async *as){ if (as->urb->transfer_buffer) kfree(as->urb->transfer_buffer); if (as->urb->setup_packet) 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 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; send_sig_info(as->signr, &sinfo, as->task); } 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); spin_unlock_irqrestore(&ps->lock, flags); /* usb_unlink_urb calls the completion handler with status == -ENOENT */ usb_unlink_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 usbdevfs_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(&usbdevfs_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(&usbdevfs_driver, intf); err = 0; } up_write(&usb_bus_type.subsys.rwsem); return err;}static int checkintf(struct dev_state *ps, unsigned int ifnum){ 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -