Usb gadget驱动
struct usb_gadget_driver { char *function; enum usb_device_speed max_speed; int (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver); void (*unbind)(struct usb_gadget *); int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); void (*disconnect)(struct usb_gadget *); void (*suspend)(struct usb_gadget *); void (*resume)(struct usb_gadget *); /* FIXME support safe rmmod */ struct device_driver driver; u8 usb_core_id; };
setup函数是一个非常重要的函数,从注释来说,“setup invoke ep0 contril request.” 主要是一些标准的各种描述符。必须实现所有的get_descriptor request,返回至少一个设备描述符和一个配置描述符。他也必须实现set_configuration set_interface, get_configuration, and get_interface
udc 驱动主要处理标准的usb request.包括set_address, and feature flags for devices, interfaces, and endpoints
以通用的composite驱动来说
static const struct usb_gadget_driver composite_driver_template = { .bind = composite_bind, .unbind = composite_unbind, .setup = composite_setup, .disconnect = composite_disconnect, .suspend = composite_suspend, .resume = composite_resume, .driver = { .owner = THIS_MODULE, }, };
一般而言,composite_setup函数已经给我们做了
int composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; int status = 0; u16 w_index = le16_to_cpu(ctrl->wIndex); u8 intf = w_index & 0xFF; u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; u8 endp; struct usb_configuration *c; if (w_length > USB_COMP_EP0_BUFSIZ) return value; /* partial re-init of the response message; the function or the * gadget might need to intercept e.g. a control-OUT completion * when we delegate to it. */ req->zero = 0; req->complete = composite_setup_complete; req->length = 0; gadget->ep0->driver_data = cdev; switch (ctrl->bRequest) { /* we handle all standard USB descriptors */ case USB_REQ_GET_DESCRIPTOR: if (ctrl->bRequestType != USB_DIR_IN) goto unknown; switch (w_value >> 8) { case USB_DT_DEVICE: cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE); cdev->desc.bMaxPacketSize0 = cdev->gadget->ep0->maxpacket; if (gadget_is_superspeed(gadget)) { if (gadget->speed >= USB_SPEED_SUPER) { cdev->desc.bcdUSB = cpu_to_le16(0x0300); cdev->desc.bMaxPacketSize0 = 9; } else { cdev->desc.bcdUSB = cpu_to_le16(0x0210); } } else if (gadget->l1_supported) { cdev->desc.bcdUSB = cpu_to_le16(0x0201); DBG(cdev, "Config HS device with LPM(L1)\n"); } value = min(w_length, (u16) sizeof cdev->desc); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: if (!gadget_is_dualspeed(gadget) || gadget->speed >= USB_SPEED_SUPER) break; device_qual(cdev); value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor)); break; case USB_DT_OTHER_SPEED_CONFIG: if (!gadget_is_dualspeed(gadget) || gadget->speed >= USB_SPEED_SUPER) break; /* FALLTHROUGH */ case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) value = min(w_length, (u16) value); break; case USB_DT_OTG: if (!gadget_is_otg(gadget)) break; c = list_first_entry(&cdev->configs, struct usb_configuration, list); if (c && c->descriptors) value = usb_find_descriptor_fillbuf(req->buf, USB_COMP_EP0_BUFSIZ, c->descriptors, USB_DT_OTG); break; case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); if (value >= 0) value = min(w_length, (u16) value); break; case USB_DT_BOS: if (gadget_is_superspeed(gadget) || gadget->l1_supported) { value = bos_desc(cdev); value = min(w_length, (u16) value); } break; } break; /* any number of configs can work */ case USB_REQ_SET_CONFIGURATION: if (ctrl->bRequestType != 0) goto unknown; if (gadget_is_otg(gadget)) { if (gadget->a_hnp_support) DBG(cdev, "HNP available\n"); else if (gadget->a_alt_hnp_support) DBG(cdev, "HNP on another port\n"); else VDBG(cdev, "HNP inactive\n"); } spin_lock(&cdev->lock); value = set_config(cdev, ctrl, w_value); spin_unlock(&cdev->lock); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) goto unknown; if (cdev->config) *(u8 *)req->buf = cdev->config->bConfigurationValue; else *(u8 *)req->buf = 0; value = min(w_length, (u16) 1); break; /* function drivers must handle get/set altsetting; if there's * no get() method, we know only altsetting zero works. */ case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE) goto unknown; if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) break; if (w_value && !f->set_alt) break; /* * We put interfaces in default settings (alt 0) * upon set config#1. Call set_alt for non-zero * alternate setting. */ if (!w_value && cdev->config) { value = 0; break; } value = f->set_alt(f, w_index, w_value); if (value == USB_GADGET_DELAYED_STATUS) { DBG(cdev, "%s: interface %d (%s) requested delayed status\n", __func__, intf, f->name); cdev->delayed_status++; DBG(cdev, "delayed_status count %d\n", cdev->delayed_status); } break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto unknown; if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) break; /* lots of interfaces only need altsetting zero... */ value = f->get_alt ? f->get_alt(f, w_index) : 0; if (value < 0) break; *((u8 *)req->buf) = value; value = min(w_length, (u16) 1); break; /* * USB 3.0 additions: * Function driver should handle get_status request. If such cb * wasn't supplied we respond with default value = 0 * Note: function driver should supply such cb only for the first * interface of the function */ case USB_REQ_GET_STATUS: if (!gadget_is_superspeed(gadget)) goto unknown; if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE)) goto unknown; value = 2; /* This is the length of the get_status reply */ put_unaligned_le16(0, req->buf); if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) break; status = f->get_status ? f->get_status(f) : 0; if (status < 0) break; put_unaligned_le16(status & 0x0000ffff, req->buf); break; /* * Function drivers should handle SetFeature/ClearFeature * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied * only for the first interface of the function */ case USB_REQ_CLEAR_FEATURE: case USB_REQ_SET_FEATURE: if (!gadget_is_superspeed(gadget)) goto unknown; if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE)) goto unknown; switch (w_value) { case USB_INTRF_FUNC_SUSPEND: if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) break; f = cdev->config->interface[intf]; if (!f) break; value = 0; if (f->func_suspend) value = f->func_suspend(f, w_index >> 8); if (value < 0) { ERROR(cdev, "func_suspend() returned error %d\n", value); value = 0; } break; } break; default: ... /* device either stalls (value < 0) or reports success */ return value; }