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;
}

 

posted on 2015-12-09 15:52  jamboo  阅读(986)  评论(0编辑  收藏  举报

导航