📄 inode.c
字号:
/* * inode.c -- user mode filesystem api for usb gadget controllers * * Copyright (C) 2003-2004 David Brownell * Copyright (C) 2003 Agilent Technologies * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */// #define DEBUG /* data to help fault diagnosis */// #define VERBOSE /* extra debug messages (success too) */#include <linux/init.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/uts.h>#include <linux/wait.h>#include <linux/compiler.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/moduleparam.h>#include <linux/usb_gadgetfs.h>#include <linux/usb_gadget.h>/* * The gadgetfs API maps each endpoint to a file descriptor so that you * can use standard synchronous read/write calls for I/O. There's some * O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode * drivers show how this works in practice. You can also use AIO to * eliminate I/O gaps between requests, to help when streaming data. * * Key parts that must be USB-specific are protocols defining how the * read/write operations relate to the hardware state machines. There * are two types of files. One type is for the device, implementing ep0. * The other type is for each IN or OUT endpoint. In both cases, the * user mode driver must configure the hardware before using it. * * - First, dev_config() is called when /dev/gadget/$CHIP is configured * (by writing configuration and device descriptors). Afterwards it * may serve as a source of device events, used to handle all control * requests other than basic enumeration. * * - Then either immediately, or after a SET_CONFIGURATION control request, * ep_config() is called when each /dev/gadget/ep* file is configured * (by writing endpoint descriptors). Afterwards these files are used * to write() IN data or to read() OUT data. To halt the endpoint, a * "wrong direction" request is issued (like reading an IN endpoint). * * Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe * not possible on all hardware. For example, precise fault handling with * respect to data left in endpoint fifos after aborted operations; or * selective clearing of endpoint halts, to implement SET_INTERFACE. */#define DRIVER_DESC "USB Gadget filesystem"#define DRIVER_VERSION "24 Aug 2004"static const char driver_desc [] = DRIVER_DESC;static const char shortname [] = "gadgetfs";MODULE_DESCRIPTION (DRIVER_DESC);MODULE_AUTHOR ("David Brownell");MODULE_LICENSE ("GPL");/*----------------------------------------------------------------------*/#define GADGETFS_MAGIC 0xaee71ee7#define DMA_ADDR_INVALID (~(dma_addr_t)0)/* /dev/gadget/$CHIP represents ep0 and the whole device */enum ep0_state { /* DISBLED is the initial state. */ STATE_DEV_DISABLED = 0, /* Only one open() of /dev/gadget/$CHIP; only one file tracks * ep0/device i/o modes and binding to the controller. Driver * must always write descriptors to initialize the device, then * the device becomes UNCONNECTED until enumeration. */ STATE_OPENED, /* From then on, ep0 fd is in either of two basic modes: * - (UN)CONNECTED: read usb_gadgetfs_event(s) from it * - SETUP: read/write will transfer control data and succeed; * or if "wrong direction", performs protocol stall */ STATE_UNCONNECTED, STATE_CONNECTED, STATE_SETUP, /* UNBOUND means the driver closed ep0, so the device won't be * accessible again (DEV_DISABLED) until all fds are closed. */ STATE_DEV_UNBOUND,};/* enough for the whole queue: most events invalidate others */#define N_EVENT 5struct dev_data { spinlock_t lock; atomic_t count; enum ep0_state state; struct usb_gadgetfs_event event [N_EVENT]; unsigned ev_next; struct fasync_struct *fasync; u8 current_config; /* drivers reading ep0 MUST handle control requests (SETUP) * reported that way; else the host will time out. */ unsigned usermode_setup : 1, setup_in : 1, setup_can_stall : 1, setup_out_ready : 1, setup_out_error : 1, setup_abort : 1; /* the rest is basically write-once */ struct usb_config_descriptor *config, *hs_config; struct usb_device_descriptor *dev; struct usb_request *req; struct usb_gadget *gadget; struct list_head epfiles; void *buf; wait_queue_head_t wait; struct super_block *sb; struct dentry *dentry; /* except this scratch i/o buffer for ep0 */ u8 rbuf [256];};static inline void get_dev (struct dev_data *data){ atomic_inc (&data->count);}static void put_dev (struct dev_data *data){ if (likely (!atomic_dec_and_test (&data->count))) return; /* needs no more cleanup */ BUG_ON (waitqueue_active (&data->wait)); kfree (data);}static struct dev_data *dev_new (void){ struct dev_data *dev; dev = kmalloc (sizeof *dev, GFP_KERNEL); if (!dev) return NULL; memset (dev, 0, sizeof *dev); dev->state = STATE_DEV_DISABLED; atomic_set (&dev->count, 1); spin_lock_init (&dev->lock); INIT_LIST_HEAD (&dev->epfiles); init_waitqueue_head (&dev->wait); return dev;}/*----------------------------------------------------------------------*//* other /dev/gadget/$ENDPOINT files represent endpoints */enum ep_state { STATE_EP_DISABLED = 0, STATE_EP_READY, STATE_EP_DEFER_ENABLE, STATE_EP_ENABLED, STATE_EP_UNBOUND,};struct ep_data { struct semaphore lock; enum ep_state state; atomic_t count; struct dev_data *dev; /* must hold dev->lock before accessing ep or req */ struct usb_ep *ep; struct usb_request *req; ssize_t status; char name [16]; struct usb_endpoint_descriptor desc, hs_desc; struct list_head epfiles; wait_queue_head_t wait; struct dentry *dentry; struct inode *inode;};static inline void get_ep (struct ep_data *data){ atomic_inc (&data->count);}static void put_ep (struct ep_data *data){ if (likely (!atomic_dec_and_test (&data->count))) return; put_dev (data->dev); /* needs no more cleanup */ BUG_ON (!list_empty (&data->epfiles)); BUG_ON (waitqueue_active (&data->wait)); BUG_ON (down_trylock (&data->lock) != 0); kfree (data);}/*----------------------------------------------------------------------*//* most "how to use the hardware" policy choices are in userspace: * mapping endpoint roles (which the driver needs) to the capabilities * which the usb controller has. most of those capabilities are exposed * implicitly, starting with the driver name and then endpoint names. */static const char *CHIP;/*----------------------------------------------------------------------*//* NOTE: don't use dev_printk calls before binding to the gadget * at the end of ep0 configuration, or after unbind. *//* too wordy: dev_printk(level , &(d)->gadget->dev , fmt , ## args) */#define xprintk(d,level,fmt,args...) \ printk(level "%s: " fmt , shortname , ## args)#ifdef DEBUG#define DBG(dev,fmt,args...) \ xprintk(dev , KERN_DEBUG , fmt , ## args)#else#define DBG(dev,fmt,args...) \ do { } while (0)#endif /* DEBUG */#ifdef VERBOSE#define VDEBUG DBG#else#define VDEBUG(dev,fmt,args...) \ do { } while (0)#endif /* DEBUG */#define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args)#define WARN(dev,fmt,args...) \ xprintk(dev , KERN_WARNING , fmt , ## args)#define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args)/*----------------------------------------------------------------------*//* SYNCHRONOUS ENDPOINT OPERATIONS (bulk/intr/iso) * * After opening, configure non-control endpoints. Then use normal * stream read() and write() requests; and maybe ioctl() to get more * precise FIFO status when recovering from cancellation. */static void epio_complete (struct usb_ep *ep, struct usb_request *req){ struct ep_data *epdata = ep->driver_data; if (!req->context) return; if (req->status) epdata->status = req->status; else epdata->status = req->actual; complete ((struct completion *)req->context);}/* tasklock endpoint, returning when it's connected. * still need dev->lock to use epdata->ep. */static intget_ready_ep (unsigned f_flags, struct ep_data *epdata){ int val; if (f_flags & O_NONBLOCK) { if (down_trylock (&epdata->lock) != 0) goto nonblock; if (epdata->state != STATE_EP_ENABLED) { up (&epdata->lock);nonblock: val = -EAGAIN; } else val = 0; return val; } if ((val = down_interruptible (&epdata->lock)) < 0) return val;newstate: switch (epdata->state) { case STATE_EP_ENABLED: break; case STATE_EP_DEFER_ENABLE: DBG (epdata->dev, "%s wait for host\n", epdata->name); if ((val = wait_event_interruptible (epdata->wait, epdata->state != STATE_EP_DEFER_ENABLE || epdata->dev->state == STATE_DEV_UNBOUND )) < 0) goto fail; goto newstate; // case STATE_EP_DISABLED: /* "can't happen" */ // case STATE_EP_READY: /* "can't happen" */ default: /* error! */ pr_debug ("%s: ep %p not available, state %d\n", shortname, epdata, epdata->state); // FALLTHROUGH case STATE_EP_UNBOUND: /* clean disconnect */ val = -ENODEV;fail: up (&epdata->lock); } return val;}static ssize_tep_io (struct ep_data *epdata, void *buf, unsigned len){ DECLARE_COMPLETION (done); int value; spin_lock_irq (&epdata->dev->lock); if (likely (epdata->ep != NULL)) { struct usb_request *req = epdata->req; req->context = &done; req->complete = epio_complete; req->buf = buf; req->length = len; value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC); } else value = -ENODEV; spin_unlock_irq (&epdata->dev->lock); if (likely (value == 0)) { value = wait_event_interruptible (done.wait, done.done); if (value != 0) { spin_lock_irq (&epdata->dev->lock); if (likely (epdata->ep != NULL)) { DBG (epdata->dev, "%s i/o interrupted\n", epdata->name); usb_ep_dequeue (epdata->ep, epdata->req); spin_unlock_irq (&epdata->dev->lock); wait_event (done.wait, done.done); if (epdata->status == -ECONNRESET) epdata->status = -EINTR; } else { spin_unlock_irq (&epdata->dev->lock); DBG (epdata->dev, "endpoint gone\n"); epdata->status = -ENODEV; } } return epdata->status; } return value;}/* handle a synchronous OUT bulk/intr/iso transfer */static ssize_tep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr){ struct ep_data *data = fd->private_data; void *kbuf; ssize_t value; if ((value = get_ready_ep (fd->f_flags, data)) < 0) return value; /* halt any endpoint by doing a "wrong direction" i/o call */ if (data->desc.bEndpointAddress & USB_DIR_IN) { if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC) return -EINVAL; DBG (data->dev, "%s halt\n", data->name); spin_lock_irq (&data->dev->lock); if (likely (data->ep != NULL)) usb_ep_set_halt (data->ep); spin_unlock_irq (&data->dev->lock); up (&data->lock); return -EBADMSG; } /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */ value = -ENOMEM; kbuf = kmalloc (len, SLAB_KERNEL); if (unlikely (!kbuf)) goto free1; value = ep_io (data, kbuf, len); VDEBUG (data->dev, "%s read %zu OUT, status %d\n", data->name, len, (int) value); if (value >= 0 && copy_to_user (buf, kbuf, value)) value = -EFAULT;free1: up (&data->lock); kfree (kbuf); return value;}/* handle a synchronous IN bulk/intr/iso transfer */static ssize_tep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr){ struct ep_data *data = fd->private_data; void *kbuf; ssize_t value; if ((value = get_ready_ep (fd->f_flags, data)) < 0) return value; /* halt any endpoint by doing a "wrong direction" i/o call */ if (!(data->desc.bEndpointAddress & USB_DIR_IN)) { if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC) return -EINVAL; DBG (data->dev, "%s halt\n", data->name); spin_lock_irq (&data->dev->lock); if (likely (data->ep != NULL)) usb_ep_set_halt (data->ep); spin_unlock_irq (&data->dev->lock); up (&data->lock); return -EBADMSG; } /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ value = -ENOMEM; kbuf = kmalloc (len, SLAB_KERNEL); if (!kbuf) goto free1; if (copy_from_user (kbuf, buf, len)) { value = -EFAULT; goto free1; } value = ep_io (data, kbuf, len); VDEBUG (data->dev, "%s write %zu IN, status %d\n", data->name, len, (int) value);free1: up (&data->lock); kfree (kbuf); return value;}static intep_release (struct inode *inode, struct file *fd){ struct ep_data *data = fd->private_data; /* clean up if this can be reopened */ if (data->state != STATE_EP_UNBOUND) { data->state = STATE_EP_DISABLED; data->desc.bDescriptorType = 0; data->hs_desc.bDescriptorType = 0; usb_ep_disable(data->ep); } put_ep (data); return 0;}static int ep_ioctl (struct inode *inode, struct file *fd, unsigned code, unsigned long value){ struct ep_data *data = fd->private_data; int status; if ((status = get_ready_ep (fd->f_flags, data)) < 0) return status; spin_lock_irq (&data->dev->lock); if (likely (data->ep != NULL)) { switch (code) { case GADGETFS_FIFO_STATUS: status = usb_ep_fifo_status (data->ep); break; case GADGETFS_FIFO_FLUSH: usb_ep_fifo_flush (data->ep); break; case GADGETFS_CLEAR_HALT: status = usb_ep_clear_halt (data->ep); break; default: status = -ENOTTY; } } else status = -ENODEV; spin_unlock_irq (&data->dev->lock); up (&data->lock); return status;}/*----------------------------------------------------------------------*//* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */struct kiocb_priv { struct usb_request *req;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -