📄 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.11 2000/03/14 00:20:33 acher 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 <asm/uaccess.h>#include <linux/usb.h>#include "usbdevice_fs.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;};/* * my own sync control and bulk methods. Here to experiment * and because the kernel ones set the process to TASK_UNINTERRUPTIBLE. */struct sync { wait_queue_head_t wait;};static void sync_completed(purb_t urb){ struct sync *s = (struct sync *)urb->context; wake_up(&s->wait); }static int do_sync(purb_t urb, int timeout){ DECLARE_WAITQUEUE(wait, current); unsigned long tm; signed long tmdiff; struct sync s; int ret; tm = jiffies+timeout; init_waitqueue_head(&s.wait); add_wait_queue(&s.wait, &wait); urb->context = &s; urb->complete = sync_completed; set_current_state(TASK_INTERRUPTIBLE); if ((ret = usb_submit_urb(urb))) goto out; while (urb->status == -EINPROGRESS) { tmdiff = tm - jiffies; if (tmdiff <= 0) { ret = -ETIMEDOUT; goto out; } if (signal_pending(current)) { ret = -EINTR; goto out; } schedule_timeout(tmdiff); } ret = urb->status; out: set_current_state(TASK_RUNNING); usb_unlink_urb(urb); remove_wait_queue(&s.wait, &wait); return ret;}static int my_usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout){ urb_t *urb; int ret; if (!(urb = usb_alloc_urb(0))) return -ENOMEM; if (!(urb->setup_packet = kmalloc(8, GFP_KERNEL))) { usb_free_urb(urb); return -ENOMEM; } urb->setup_packet[0] = requesttype; urb->setup_packet[1] = request; urb->setup_packet[2] = value; urb->setup_packet[3] = value >> 8; urb->setup_packet[4] = index; urb->setup_packet[5] = index >> 8; urb->setup_packet[6] = size; urb->setup_packet[7] = size >> 8; urb->dev = dev; urb->pipe = pipe; urb->transfer_buffer = data; urb->transfer_buffer_length = size; ret = do_sync(urb, timeout); //if (ret >= 0) // ret = urb->status; if (ret >= 0) ret = urb->actual_length; kfree(urb->setup_packet); usb_free_urb(urb); return ret;}static int my_usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout){ urb_t *urb; int ret; if (!(urb = usb_alloc_urb(0))) return -ENOMEM; urb->dev = dev; urb->pipe = pipe; urb->transfer_buffer = data; urb->transfer_buffer_length = len; ret = do_sync(urb, timeout); //if (ret >= 0) // ret = urb->status; if (ret >= 0 && actual_length != NULL) *actual_length = urb->actual_length; usb_free_urb(urb); return ret;}static long long usbdev_lseek(struct file *file, long long 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; pos = *ppos; down_read(&ps->devsem); if (!ps->dev) ret = -ENODEV; else if (pos < 0) ret = -EINVAL; else 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; else { *ppos += len; buf += len; nbytes -= len; ret += len; } } 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; return as;}static void free_async(struct async *as){ if (as->urb.transfer_buffer) kfree(as->urb.transfer_buffer); 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;#if 0 printk(KERN_DEBUG "usbdevfs: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", urb->status, urb->error_count, urb->actual_length, urb->pipe);#endif 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){ 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 = { "usbdevfs", driver_probe, driver_disconnect, LIST_HEAD_INIT(usbdevfs_driver.driver_list), NULL, 0};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; }/* * file operations */static int usbdev_open(struct inode *inode, struct file *file){ struct usb_device *dev; struct dev_state *ps; int ret; /* * no locking necessary here, as both sys_open (actually filp_open) * and the hub thread have the kernel lock * (still acquire the kernel lock for safety) */ lock_kernel(); ret = -ENOENT; if (ITYPE(inode->i_ino) != IDEVICE) goto out; dev = inode->u.usbdev_i.p.dev; if (!dev) goto out; ret = -ENOMEM; if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -