📄 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/usb.h>#include <linux/usbdevice_fs.h>#include <asm/uaccess.h>struct async { struct list_head asynclist; struct dev_state *ps; struct task_struct *task; unsigned int signr; void *userbuffer; void *userurb; urb_t urb;};static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig){ switch (orig) { case 0: file->f_pos = offset; return file->f_pos; case 1: file->f_pos += offset; return file->f_pos; case 2: return -EINVAL; default: return -EINVAL; }}static ssize_t usbdev_read(struct file *file, char * buf, size_t nbytes, loff_t *ppos){ struct dev_state *ps = (struct dev_state *)file->private_data; ssize_t ret = 0; unsigned len; loff_t pos; int i; pos = *ppos; down_read(&ps->devsem); if (!ps->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 *)&ps->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 < ps->dev->descriptor.bNumConfigurations; i++) { struct usb_config_descriptor *config = (struct usb_config_descriptor *)ps->dev->rawdescriptors[i]; unsigned int length = le16_to_cpu(config->wTotalLength); if (*ppos < pos + length) { len = length - (*ppos - pos); if (len > nbytes) len = nbytes; if (copy_to_user(buf, ps->dev->rawdescriptors[i] + (*ppos - pos), len)) { ret = -EFAULT; goto err; } *ppos += len; buf += len; nbytes -= len; ret += len; } pos += length; }err: up_read(&ps->devsem); return ret;}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;}/* * async list handling */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; spin_lock_init(&as->urb.lock); 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); kfree(as);}extern __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);}extern __inline__ void async_removepending(struct async *as){ struct dev_state *ps = as->ps; unsigned long flags; spin_lock_irqsave(&ps->lock, flags); list_del(&as->asynclist); INIT_LIST_HEAD(&as->asynclist); spin_unlock_irqrestore(&ps->lock, flags);}extern __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(&as->asynclist); INIT_LIST_HEAD(&as->asynclist); } spin_unlock_irqrestore(&ps->lock, flags); return as;}extern __inline__ struct async *async_getpending(struct dev_state *ps, void *userurb){ unsigned long flags; struct async *as; struct list_head *p; spin_lock_irqsave(&ps->lock, flags); for (p = ps->async_pending.next; p != &ps->async_pending; ) { as = list_entry(p, struct async, asynclist); p = p->next; if (as->userurb != userurb) continue; list_del(&as->asynclist); INIT_LIST_HEAD(&as->asynclist); spin_unlock_irqrestore(&ps->lock, flags); return as; } spin_unlock_irqrestore(&ps->lock, flags); return NULL;}static void async_completed(purb_t urb){ struct async *as = (struct async *)urb->context; struct dev_state *ps = as->ps; struct siginfo sinfo; spin_lock(&ps->lock); list_del(&as->asynclist); list_add_tail(&as->asynclist, &ps->async_completed); spin_unlock(&ps->lock); wake_up(&ps->wait); 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); }}static void destroy_all_async(struct dev_state *ps){ struct async *as; unsigned long flags; spin_lock_irqsave(&ps->lock, flags); while (!list_empty(&ps->async_pending)) { as = list_entry(ps->async_pending.next, struct async, asynclist); list_del(&as->asynclist); INIT_LIST_HEAD(&as->asynclist); spin_unlock_irqrestore(&ps->lock, flags); /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */ usb_unlink_urb(&as->urb); spin_lock_irqsave(&ps->lock, flags); } spin_unlock_irqrestore(&ps->lock, flags); while ((as = async_getcompleted(ps))) free_async(as);}/* * interface claiming */static void *driver_probe(struct usb_device *dev, unsigned int intf, const struct usb_device_id *id){ return NULL;}static void driver_disconnect(struct usb_device *dev, void *context){ struct dev_state *ps = (struct dev_state *)context; ps->ifclaimed = 0;}struct usb_driver usbdevfs_driver = { name: "usbdevfs", probe: driver_probe, disconnect: driver_disconnect,};static int claimintf(struct dev_state *ps, unsigned int intf){ struct usb_device *dev = ps->dev; struct usb_interface *iface; int err; if (intf >= 8*sizeof(ps->ifclaimed) || !dev || intf >= dev->actconfig->bNumInterfaces) return -EINVAL; /* already claimed */ if (test_bit(intf, &ps->ifclaimed)) return 0; iface = &dev->actconfig->interface[intf]; err = -EBUSY; lock_kernel(); if (!usb_interface_claimed(iface)) { usb_driver_claim_interface(&usbdevfs_driver, iface, ps); set_bit(intf, &ps->ifclaimed); err = 0; } unlock_kernel(); return err;}static int releaseintf(struct dev_state *ps, unsigned int intf){ struct usb_device *dev; struct usb_interface *iface; int err; if (intf >= 8*sizeof(ps->ifclaimed)) return -EINVAL; err = -EINVAL; lock_kernel(); dev = ps->dev; if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) { iface = &dev->actconfig->interface[intf]; usb_driver_release_interface(&usbdevfs_driver, iface); err = 0; } unlock_kernel(); return err;}static int checkintf(struct dev_state *ps, unsigned int intf){ if (intf >= 8*sizeof(ps->ifclaimed)) return -EINVAL; if (test_bit(intf, &ps->ifclaimed)) return 0; /* if not yet claimed, claim it for the driver */ printk(KERN_WARNING "usbdevfs: process %d (%s) did not claim interface %u before use\n", current->pid, current->comm, intf); return claimintf(ps, intf);}static int findintfep(struct usb_device *dev, unsigned int ep){ unsigned int i, j, e; struct usb_interface *iface; struct usb_interface_descriptor *alts; struct usb_endpoint_descriptor *endpt; if (ep & ~(USB_DIR_IN|0xf)) return -EINVAL; for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { iface = &dev->actconfig->interface[i]; for (j = 0; j < iface->num_altsetting; j++) { alts = &iface->altsetting[j]; for (e = 0; e < alts->bNumEndpoints; e++) { endpt = &alts->endpoint[e]; if (endpt->bEndpointAddress == ep) return i; } } } return -ENOENT; }static int findintfif(struct usb_device *dev, unsigned int ifn){ unsigned int i, j; struct usb_interface *iface; struct usb_interface_descriptor *alts; if (ifn & ~0xff) return -EINVAL; for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { iface = &dev->actconfig->interface[i]; for (j = 0; j < iface->num_altsetting; j++) { alts = &iface->altsetting[j]; if (alts->bInterfaceNumber == ifn) return i; } } return -ENOENT; }extern struct list_head usb_driver_list;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -