📄 dummy_hcd.c
字号:
} else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, setup.wIndex); if (!ep2) { value = -EOPNOTSUPP; break; } ep2->halted = 1; value = 0; maybe_set_status (urb, 0); } break; case USB_REQ_CLEAR_FEATURE: if (setup.bRequestType == Dev_Request) { switch (setup.wValue) { case USB_DEVICE_REMOTE_WAKEUP: dum->devstatus &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); value = 0; maybe_set_status (urb, 0); break; default: value = -EOPNOTSUPP; break; } } else if (setup.bRequestType == Ep_Request) { // endpoint halt ep2 = find_endpoint (dum, setup.wIndex); if (!ep2) { value = -EOPNOTSUPP; break; } ep2->halted = 0; value = 0; maybe_set_status (urb, 0); } break; case USB_REQ_GET_STATUS: if (setup.bRequestType == Dev_InRequest || setup.bRequestType == Intf_InRequest || setup.bRequestType == Ep_InRequest ) { char *buf; // device: remote wakeup, selfpowered // interface: nothing // endpoint: halt buf = (char *)urb->transfer_buffer; if (urb->transfer_buffer_length > 0) { if (setup.bRequestType == Ep_InRequest) { ep2 = find_endpoint (dum, setup.wIndex); if (!ep2) { value = -EOPNOTSUPP; break; } buf [0] = ep2->halted; } else if (setup.bRequestType == Dev_InRequest) { buf [0] = (u8) dum->devstatus; } else buf [0] = 0; } if (urb->transfer_buffer_length > 1) buf [1] = 0; urb->actual_length = min (2, urb->transfer_buffer_length); value = 0; maybe_set_status (urb, 0); } break; } /* gadget driver handles all other requests. block * until setup() returns; no reentrancy issues etc. */ if (value > 0) { spin_unlock (&dum->lock); value = dum->driver->setup (&dum->gadget, &setup); spin_lock (&dum->lock); if (value >= 0) { /* no delays (max 64KB data stage) */ limit = 64*1024; goto treat_control_like_bulk; } /* error, see below */ } if (value < 0) { if (value != -EOPNOTSUPP) dev_dbg (dummy_dev(dum), "setup --> %d\n", value); maybe_set_status (urb, -EPIPE); urb->actual_length = 0; } goto return_urb; } /* non-control requests */ limit = total; switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: /* FIXME is it urb->interval since the last xfer? * use urb->iso_frame_desc[i]. * complete whether or not ep has requests queued. * report random errors, to debug drivers. */ limit = max (limit, periodic_bytes (dum, ep)); maybe_set_status (urb, -ENOSYS); break; case PIPE_INTERRUPT: /* FIXME is it urb->interval since the last xfer? * this almost certainly polls too fast. */ limit = max (limit, periodic_bytes (dum, ep)); /* FALLTHROUGH */ // case PIPE_BULK: case PIPE_CONTROL: default: treat_control_like_bulk: ep->last_io = jiffies; total = transfer (dum, urb, ep, limit); break; } /* incomplete transfer? */ if (urb->status == -EINPROGRESS) continue;return_urb: urb->hcpriv = NULL; list_del (&urbp->urbp_list); kfree (urbp); if (ep) ep->already_seen = ep->setup_stage = 0; spin_unlock (&dum->lock); usb_hcd_giveback_urb (dummy_to_hcd(dum), urb, NULL); spin_lock (&dum->lock); goto restart; } /* want a 1 msec delay here */ if (!list_empty (&dum->urbp_list)) mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); else { usb_put_dev (dum->udev); dum->udev = NULL; } spin_unlock_irqrestore (&dum->lock, flags);}/*-------------------------------------------------------------------------*/#define PORT_C_MASK \ ((1 << USB_PORT_FEAT_C_CONNECTION) \ | (1 << USB_PORT_FEAT_C_ENABLE) \ | (1 << USB_PORT_FEAT_C_SUSPEND) \ | (1 << USB_PORT_FEAT_C_OVER_CURRENT) \ | (1 << USB_PORT_FEAT_C_RESET))static int dummy_hub_status (struct usb_hcd *hcd, char *buf){ struct dummy *dum; unsigned long flags; int retval; dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); if (!(dum->port_status & PORT_C_MASK)) retval = 0; else { *buf = (1 << 1); dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", dum->port_status); retval = 1; } spin_unlock_irqrestore (&dum->lock, flags); return retval;}static inline voidhub_descriptor (struct usb_hub_descriptor *desc){ memset (desc, 0, sizeof *desc); desc->bDescriptorType = 0x29; desc->bDescLength = 9; desc->wHubCharacteristics = __constant_cpu_to_le16 (0x0001); desc->bNbrPorts = 1; desc->bitmap [0] = 0xff; desc->bitmap [1] = 0xff;}static int dummy_hub_control ( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct dummy *dum; int retval = 0; unsigned long flags; dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); switch (typeReq) { case ClearHubFeature: break; case ClearPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) { /* 20msec resume signaling */ dum->resuming = 1; dum->re_timeout = jiffies + msecs_to_jiffies(20); } break; case USB_PORT_FEAT_POWER: dum->port_status = 0; dum->resuming = 0; stop_activity(dum, dum->driver); break; default: dum->port_status &= ~(1 << wValue); } break; case GetHubDescriptor: hub_descriptor ((struct usb_hub_descriptor *) buf); break; case GetHubStatus: *(u32 *) buf = __constant_cpu_to_le32 (0); break; case GetPortStatus: if (wIndex != 1) retval = -EPIPE; /* whoever resets or resumes must GetPortStatus to * complete it!! */ if (dum->resuming && time_after (jiffies, dum->re_timeout)) { dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND); dum->resuming = 0; dum->re_timeout = 0; if (dum->driver && dum->driver->resume) { spin_unlock (&dum->lock); dum->driver->resume (&dum->gadget); spin_lock (&dum->lock); } } if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0 && time_after (jiffies, dum->re_timeout)) { dum->port_status |= (1 << USB_PORT_FEAT_C_RESET); dum->port_status &= ~(1 << USB_PORT_FEAT_RESET); dum->re_timeout = 0; if (dum->driver) { dum->port_status |= USB_PORT_STAT_ENABLE; /* give it the best speed we agree on */ dum->gadget.speed = dum->driver->speed; dum->gadget.ep0->maxpacket = 64; switch (dum->gadget.speed) { case USB_SPEED_HIGH: dum->port_status |= USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW: dum->gadget.ep0->maxpacket = 8; dum->port_status |= USB_PORT_STAT_LOW_SPEED; break; default: dum->gadget.speed = USB_SPEED_FULL; break; } } } ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status); ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); break; case SetHubFeature: retval = -EPIPE; break; case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_SUSPEND: if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) == 0) { dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND); if (dum->driver && dum->driver->suspend) { spin_unlock (&dum->lock); dum->driver->suspend (&dum->gadget); spin_lock (&dum->lock); } } break; case USB_PORT_FEAT_RESET: /* if it's already running, disconnect first */ if (dum->port_status & USB_PORT_STAT_ENABLE) { dum->port_status &= ~(USB_PORT_STAT_ENABLE | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); if (dum->driver) { dev_dbg (dummy_dev(dum), "disconnect\n"); stop_activity (dum, dum->driver); } /* FIXME test that code path! */ } /* 50msec reset signaling */ dum->re_timeout = jiffies + msecs_to_jiffies(50); /* FALLTHROUGH */ default: dum->port_status |= (1 << wValue); } break; default: dev_dbg (dummy_dev(dum), "hub control req%04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore (&dum->lock, flags); return retval;}/*-------------------------------------------------------------------------*/static inline ssize_tshow_urb (char *buf, size_t size, struct urb *urb){ int ep = usb_pipeendpoint (urb->pipe); return snprintf (buf, size, "urb/%p %s ep%d%s%s len %d/%d\n", urb, ({ char *s; switch (urb->dev->speed) { case USB_SPEED_LOW: s = "ls"; break; case USB_SPEED_FULL: s = "fs"; break; case USB_SPEED_HIGH: s = "hs"; break; default: s = "?"; break; }; s; }), ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype (urb->pipe)) { \ case PIPE_CONTROL: s = ""; break; \ case PIPE_BULK: s = "-bulk"; break; \ case PIPE_INTERRUPT: s = "-int"; break; \ default: s = "-iso"; break; \ }; s;}), urb->actual_length, urb->transfer_buffer_length);}static ssize_tshow_urbs (struct device *dev, char *buf){ struct usb_hcd *hcd = dev_get_drvdata (dev); struct dummy *dum = hcd_to_dummy (hcd); struct urbp *urbp; size_t size = 0; unsigned long flags; spin_lock_irqsave (&dum->lock, flags); list_for_each_entry (urbp, &dum->urbp_list, urbp_list) { size_t temp; temp = show_urb (buf, PAGE_SIZE - size, urbp->urb); buf += temp; size += temp; } spin_unlock_irqrestore (&dum->lock, flags); return size;}static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);static int dummy_start (struct usb_hcd *hcd){ struct dummy *dum; struct usb_device *root; int retval; dum = hcd_to_dummy (hcd); /* * MASTER side init ... we emulate a root hub that'll only ever * talk to one device (the slave side). Also appears in sysfs, * just like more familiar pci-based HCDs. */ spin_lock_init (&dum->lock); init_timer (&dum->timer); dum->timer.function = dummy_timer; dum->timer.data = (unsigned long) dum; INIT_LIST_HEAD (&dum->urbp_list); root = usb_alloc_dev (NULL, &hcd->self, 0); if (!root) return -ENOMEM; /* root hub enters addressed state... */ hcd->state = USB_STATE_RUNNING; root->speed = USB_SPEED_HIGH; /* ...then configured, so khubd sees us. */ if ((retval = hcd_register_root (root, hcd)) != 0) { usb_put_dev (root);clean: hcd->state = USB_STATE_QUIESCING; return retval; } /* only show a low-power port: just 8mA */ hub_set_power_budget (root, 8); if ((retval = dummy_register_udc (dum)) != 0) { usb_disconnect (&hcd->self.root_hub); goto clean; } /* FIXME 'urbs' should be a per-device thing, maybe in usbcore */ device_create_file (dummy_dev(dum), &dev_attr_urbs); dum->started = 1; return 0;}static void dummy_stop (struct usb_hcd *hcd){ struct dummy *dum; dum = hcd_to_dummy (hcd); if (!dum->started) return; dum->started = 0; device_remove_file (dummy_dev(dum), &dev_attr_urbs); usb_gadget_unregister_driver (dum->driver); dummy_unregister_udc (dum); dev_info (dummy_dev(dum), "stopped\n");}/*-------------------------------------------------------------------------*/static int dummy_h_get_frame (struct usb_hcd *hcd){ return dummy_g_get_frame (NULL);}static const struct hc_driver dummy_hcd = { .description = (char *) driver_name, .product_desc = "Dummy host controller", .hcd_priv_size = sizeof(struct dummy), .flags = HCD_USB2, .start = dummy_start, .stop = dummy_stop, .urb_enqueue = dummy_urb_enqueue, .urb_dequeue = dummy_urb_dequeue, .get_frame_number = dummy_h_get_frame, .hub_status_data = dummy_hub_status, .hub_control = dummy_hub_control,};static void dummy_remove (struct device *dev);static int dummy_probe (struct device *dev){ struct usb_hcd *hcd; struct dummy *dum; int retval; dev_info (dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); hcd = usb_create_hcd (&dummy_hcd); if (hcd == NULL) { dev_dbg (dev, "hcd_alloc failed\n"); return -ENOMEM; } dev_set_drvdata (dev, hcd); dum = hcd_to_dummy (hcd); the_controller = dum; hcd->self.controller = dev; /* FIXME don't require the pci-based buffer/alloc impls; * the "generic dma" implementation still requires them, * it's not very generic yet. */ retval = hcd_buffer_create (hcd); if (retval != 0) { dev_dbg (dev, "pool alloc failed\n"); goto err1; } hcd->self.bus_name = dev->bus_id; usb_register_bus (&hcd->self); if ((retval = dummy_start (hcd)) < 0) dummy_remove (dev); return retval;err1: usb_put_hcd (hcd); dev_set_drvdata (dev, NULL); return retval;}static void dummy_remove (struct device *dev){ struct usb_hcd *hcd; struct dummy *dum; hcd = dev_get_drvdata (dev); dum = hcd_to_dummy (hcd); hcd->state = USB_STATE_QUIESCING; dev_dbg (dev, "roothub graceful disconnect\n"); usb_disconnect (&hcd->self.root_hub); hcd->driver->stop (hcd); hcd->state = USB_STATE_HALT; hcd_buffer_destroy (hcd); dev_set_drvdata (dev, NULL); usb_deregister_bus (&hcd->self); the_controller = NULL;}/*-------------------------------------------------------------------------*/static int dummy_pdev_detect (void){ int retval; retval = driver_register (&dummy_driver); if (retval < 0) return retval; the_pdev.name = "hc"; the_pdev.dev.driver = &dummy_driver; the_pdev.dev.release = dummy_pdev_release; retval = platform_device_register (&the_pdev); if (retval < 0) driver_unregister (&dummy_driver); return retval;}static void dummy_pdev_remove (void){ platform_device_unregister (&the_pdev); driver_unregister (&dummy_driver);}/*-------------------------------------------------------------------------*/static int __init init (void){ int retval; if (usb_disabled ()) return -ENODEV; if ((retval = dummy_pdev_detect ()) != 0) return retval; if ((retval = dummy_probe (&the_pdev.dev)) != 0) dummy_pdev_remove (); return retval;}module_init (init);static void __exit cleanup (void){ dummy_remove (&the_pdev.dev); dummy_pdev_remove ();}module_exit (cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -