📄 inode.c
字号:
value = min (ctrl->wLength, (u16) sizeof *dev->dev); req->buf = dev->dev; break;#ifdef HIGHSPEED case USB_DT_DEVICE_QUALIFIER: if (!dev->hs_config) break; value = min (ctrl->wLength, (u16) sizeof (struct usb_qualifier_descriptor)); make_qualifier (dev); break; case USB_DT_OTHER_SPEED_CONFIG: // FALLTHROUGH#endif case USB_DT_CONFIG: value = config_buf (dev, ctrl->wValue >> 8, ctrl->wValue & 0xff); if (value >= 0) value = min (ctrl->wLength, (u16) value); break; case USB_DT_STRING: goto unrecognized; default: // all others are errors break; } break; /* currently one config, two speeds */ case USB_REQ_SET_CONFIGURATION: if (ctrl->bRequestType != 0) break; if (0 == (u8) ctrl->wValue) { value = 0; dev->current_config = 0; // user mode expected to disable endpoints } else { u8 config;#ifdef HIGHSPEED if (gadget->speed == USB_SPEED_HIGH) config = dev->hs_config->bConfigurationValue; else#endif config = dev->config->bConfigurationValue; if (config == (u8) ctrl->wValue) { value = 0; dev->current_config = config; } } /* report SET_CONFIGURATION like any other control request, * except that usermode may not stall this. the next * request mustn't be allowed start until this finishes: * endpoints and threads set up, etc. * * NOTE: older PXA hardware (before PXA 255: without UDCCFR) * has bad/racey automagic that prevents synchronizing here. * even kernel mode drivers often miss them. */ if (value == 0) { INFO (dev, "configuration #%d\n", dev->current_config); if (dev->usermode_setup) { dev->setup_can_stall = 0; goto delegate; } } break;#ifndef CONFIG_USB_GADGETFS_PXA2XX /* PXA automagically handles this request too */ case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != 0x80) break; *(u8 *)req->buf = dev->current_config; value = min (ctrl->wLength, (u16) 1); break;#endif default:unrecognized: VDEBUG (dev, "%s req%02x.%02x v%04x i%04x l%d\n", dev->usermode_setup ? "delegate" : "fail", ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); /* if there's an ep0 reader, don't stall */ if (dev->usermode_setup) { dev->setup_can_stall = 1;delegate: dev->setup_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0; dev->setup_out_ready = 0; dev->setup_out_error = 0; value = 0; /* read DATA stage for OUT right away */ if (unlikely (!dev->setup_in && ctrl->wLength)) { value = setup_req (gadget->ep0, dev->req, ctrl->wLength); if (value < 0) break; value = usb_ep_queue (gadget->ep0, dev->req, GFP_ATOMIC); if (value < 0) { clean_req (gadget->ep0, dev->req); break; } /* we can't currently stall these */ dev->setup_can_stall = 0; } /* state changes when reader collects event */ event = next_event (dev, GADGETFS_SETUP); event->u.setup = *ctrl; ep0_readable (dev); spin_unlock (&dev->lock); return 0; } } /* proceed with data transfer and status phases? */ if (value >= 0 && dev->state != STATE_SETUP) { req->length = value; value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); if (value < 0) { DBG (dev, "ep_queue --> %d\n", value); req->status = 0; } } /* device stalls when value < 0 */ spin_unlock (&dev->lock); return value;}static void destroy_ep_files (struct dev_data *dev){ struct list_head *entry, *tmp; DBG (dev, "%s %d\n", __FUNCTION__, dev->state); /* dev->state must prevent interference */restart: spin_lock_irq (&dev->lock); list_for_each_safe (entry, tmp, &dev->epfiles) { struct ep_data *ep; /* break link to FS */ ep = list_entry (entry, struct ep_data, epfiles); list_del_init (&ep->epfiles); /* break link to controller */ if (ep->state == STATE_EP_ENABLED) (void) usb_ep_disable (ep->ep); ep->state = STATE_EP_UNBOUND; usb_ep_free_request (ep->ep, ep->req); ep->ep = 0; wake_up (&ep->wait); put_ep (ep); spin_unlock_irq (&dev->lock); /* fds may still be open */ goto restart; } spin_unlock_irq (&dev->lock);}static int activate_ep_files (struct dev_data *dev){ struct usb_ep *ep; gadget_for_each_ep (ep, dev->gadget) { struct ep_data *data; data = kmalloc (sizeof *data, GFP_KERNEL); if (!data) goto enomem; memset (data, 0, sizeof data); data->state = STATE_EP_DISABLED; init_MUTEX (&data->lock); init_waitqueue_head (&data->wait); strncpy (data->name, ep->name, sizeof (data->name) - 1); atomic_set (&data->count, 1); data->dev = dev; get_dev (dev); data->ep = ep; ep->driver_data = data; data->req = usb_ep_alloc_request (ep, GFP_KERNEL); if (!data->req) goto enomem; list_add_tail (&data->epfiles, &dev->epfiles); } return 0;enomem: DBG (dev, "%s enomem\n", __FUNCTION__); destroy_ep_files (dev); return -ENOMEM;}static voidgadgetfs_unbind (struct usb_gadget *gadget){ struct dev_data *dev = get_gadget_data (gadget); DBG (dev, "%s\n", __FUNCTION__); spin_lock_irq (&dev->lock); dev->state = STATE_DEV_UNBOUND; spin_unlock_irq (&dev->lock); destroy_ep_files (dev); gadget->ep0->driver_data = 0; set_gadget_data (gadget, 0); /* we've already been disconnected ... no i/o is active */ if (dev->req) usb_ep_free_request (gadget->ep0, dev->req); DBG (dev, "%s done\n", __FUNCTION__); put_dev (dev);}static struct dev_data *the_device;static intgadgetfs_bind (struct usb_gadget *gadget){ struct dev_data *dev = the_device; if (!dev) return -ESRCH; if (0 != strcmp (CHIP, gadget->name)) { printk (KERN_ERR "%s expected " CHIP " controller not %s\n", shortname, gadget->name); return -ENODEV; } set_gadget_data (gadget, dev); dev->gadget = gadget; gadget->ep0->driver_data = dev; dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; /* preallocate control response and buffer */ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); if (!dev->req) goto enomem; dev->req->context = 0; dev->req->complete = epio_complete; if (activate_ep_files (dev) < 0) goto enomem; INFO (dev, "bound to %s driver\n", gadget->name); dev->state = STATE_UNCONNECTED; get_dev (dev); return 0;enomem: gadgetfs_unbind (gadget); return -ENOMEM;}static voidgadgetfs_disconnect (struct usb_gadget *gadget){ struct dev_data *dev = get_gadget_data (gadget); if (dev->state == STATE_UNCONNECTED) { DBG (dev, "already unconnected\n"); return; } dev->state = STATE_UNCONNECTED; INFO (dev, "disconnected\n"); spin_lock (&dev->lock); next_event (dev, GADGETFS_DISCONNECT); ep0_readable (dev); spin_unlock (&dev->lock);}static voidgadgetfs_suspend (struct usb_gadget *gadget){ struct dev_data *dev = get_gadget_data (gadget); INFO (dev, "suspended from state %d\n", dev->state); spin_lock (&dev->lock); switch (dev->state) { case STATE_SETUP: // VERY odd... host died?? case STATE_CONNECTED: case STATE_UNCONNECTED: next_event (dev, GADGETFS_SUSPEND); ep0_readable (dev); /* FALLTHROUGH */ default: break; } spin_unlock (&dev->lock);}static struct usb_gadget_driver gadgetfs_driver = {#ifdef HIGHSPEED .speed = USB_SPEED_HIGH,#else .speed = USB_SPEED_FULL,#endif .function = (char *) driver_desc, .bind = gadgetfs_bind, .unbind = gadgetfs_unbind, .setup = gadgetfs_setup, .disconnect = gadgetfs_disconnect, .suspend = gadgetfs_suspend, .driver = { .name = (char *) shortname, // .shutdown = ... // .suspend = ... // .resume = ... },};/*----------------------------------------------------------------------*//* DEVICE INITIALIZATION * * fd = open ("/dev/gadget/$CHIP", O_RDWR) * status = write (fd, descriptors, sizeof descriptors) * * That write establishes the device configuration, so the kernel can * bind to the controller ... guaranteeing it can handle enumeration * at all necessary speeds. Descriptor order is: * * . message tag (u32, host order) ... for now, must be zero; it * would change to support features like multi-config devices * . full/low speed config ... all wTotalLength bytes (with interface, * class, altsetting, endpoint, and other descriptors) * . high speed config ... all descriptors, for high speed operation; * this one's optional except for high-speed hardware * . device descriptor * * Endpoints are not yet enabled. Drivers may want to immediately * initialize them, using the /dev/gadget/ep* files that are available * as soon as the kernel sees the configuration, or they can wait * until device configuration and interface altsetting changes create * the need to configure (or unconfigure) them. * * After initialization, the device stays active for as long as that * $CHIP file is open. Events may then be read from that descriptor, * such configuration notifications. More complex drivers will handle * some control requests in user space. */static int is_valid_config (struct usb_config_descriptor *config){ return config->bDescriptorType == USB_DT_CONFIG && config->bLength == USB_DT_CONFIG_SIZE && config->bConfigurationValue != 0 && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0 && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0; /* FIXME check lengths: walk to end */}static ssize_tdev_config (struct file *fd, const char *buf, size_t len, loff_t *ptr){ struct dev_data *dev = fd->private_data; ssize_t value = len, length = len; unsigned total; u32 tag; char *kbuf; if (dev->state != STATE_OPENED) return -EEXIST; if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) return -EINVAL; /* we might need to change message format someday */ if (copy_from_user (&tag, buf, 4)) return -EFAULT; if (tag != 0) return -EINVAL; buf += 4; length -= 4; kbuf = kmalloc (length, SLAB_KERNEL); if (!kbuf) return -ENOMEM; if (copy_from_user (kbuf, buf, length)) { kfree (kbuf); return -EFAULT; } spin_lock_irq (&dev->lock); value = -EINVAL; if (dev->buf) goto fail; dev->buf = kbuf; /* full or low speed config */ dev->config = (void *) kbuf; total = le16_to_cpup (&dev->config->wTotalLength); if (!is_valid_config (dev->config) || total >= length) goto fail; kbuf += total; length -= total; /* optional high speed config */ if (kbuf [1] == USB_DT_CONFIG) { dev->hs_config = (void *) kbuf; total = le16_to_cpup (&dev->hs_config->wTotalLength); if (!is_valid_config (dev->hs_config) || total >= length) goto fail; kbuf += total; length -= total; } /* could support multiple configs, using another encoding! */ /* device descriptor (tweaked for paranoia) */ if (length != USB_DT_DEVICE_SIZE) goto fail; dev->dev = (void *)kbuf; if (dev->dev->bLength != USB_DT_DEVICE_SIZE || dev->dev->bDescriptorType != USB_DT_DEVICE || dev->dev->bNumConfigurations != 1) goto fail; dev->dev->bNumConfigurations = 1; dev->dev->bcdUSB = __constant_cpu_to_le16 (0x0200); /* triggers gadgetfs_bind(); then we can enumerate. */ spin_unlock_irq (&dev->lock); value = usb_gadget_register_driver (&gadgetfs_driver); if (value != 0) { kfree (dev->buf); dev->buf = 0; } else { /* at this point "good" hardware has for the first time * let the USB the host see us. alternatively, if users * unplug/replug that will clear all the error state. * * note: everything running before here was guaranteed * to choke driver model style diagnostics. from here * on, they can work ... except in cleanup paths that * kick in after the ep0 descriptor is closed. */ fd->f_op = &ep0_io_operations; value = len; } return value;fail: spin_unlock_irq (&dev->lock); pr_debug ("%s: %s fail %d, %p\n", shortname, __FUNCTION__, value, dev); kfree (dev->buf); dev->buf = 0; return value;}static intdev_open (struct inode *inode, struct file *fd){ struct dev_data *dev = inode->u.generic_ip; int value = -EBUSY; if (dev->state == STATE_DEV_DISABLED) { dev->ev_next = 0; dev->state = STATE_OPENED; fd->private_data = dev; get_dev (dev); value = 0; } return value;}static struct file_operations dev_init_operations = { .owner = THIS_MODULE, .open = dev_open, .write = dev_config, .fasync = ep0_fasync, .ioctl = dev_ioctl, .release = dev_release,};/*----------------------------------------------------------------------*//* * implementation for 2.4 uses character special files * ep0/device file MKDEV (c_major, 0) * first data ep MKDEV (c_major, 1) * second data ep MKDEV (c_major, 2) * ... * * FIXME can do it as a real filesystem on 2.4 too, without libfs */static int c_major = 240; /* 240 is local/experimental */MODULE_PARM (c_major, "i");MODULE_PARM_DESC (c_major, "major number for char special files");static int gadget_open (struct inode *ino, struct file *fp){ int num = minor (ino->i_rdev); struct dev_data *dev; struct file_operations *ops; /* ep0 file, "/dev/gadget/$CHIP" */ if (num == 0) { int status; if (the_device != 0) return -EBUSY; the_device = dev_new (); if (the_device == 0) return -ENOMEM; dev = the_device; ino->u.generic_ip = dev; ops = &dev_init_operations; fp->f_op = ops; status = ops->open (ino, fp); if (status < 0) { put_dev (dev); the_device = 0; } return status; /* ep files, "/dev/gadget/$ENDPOINT" */ } else { struct list_head *entry; struct ep_data *data; /* unavailable till device is initted */ dev = the_device; if (dev == 0) return -ENODEV; /* order in controller's name listing matters! */ list_for_each (entry, &dev->epfiles) { if (--num == 0) goto found; } return -ENODEV;found: data = list_entry (entry, struct ep_data, epfiles); ino->u.generic_ip = data; ops = &ep_config_operations; fp->f_op = ops; return ops->open (ino, fp); }}static struct file_operations gadget_fops = { .owner = THIS_MODULE, .open = gadget_open,};/*----------------------------------------------------------------------*/static int __init init (void){ int status; status = register_chrdev (c_major, shortname, &gadget_fops); if (status < 0) { printk (KERN_WARNING "%s: can't get major %d\n", shortname, c_major); return status; } /* dynamic assignment */ if (c_major == 0) c_major = status; status = 0; pr_info ("%s: using char major %d\n", shortname, c_major); if (status == 0) pr_info ("%s: %s, version " DRIVER_VERSION "\n", shortname, driver_desc); return status;}module_init (init);static void __exit cleanup (void){ pr_debug ("unregister %s\n", shortname); unregister_chrdev (c_major, shortname);}module_exit (cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -