• 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)
    ");
                }
    
                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
    ");
                else if (gadget->a_alt_hnp_support)
                    DBG(cdev, "HNP on another port
    ");
                else
                    VDBG(cdev, "HNP inactive
    ");
            }
            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
    ",
                        __func__, intf, f->name);
                cdev->delayed_status++;
                DBG(cdev, "delayed_status count %d
    ",
                        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
    ",
                          value);
                    value = 0;
                }
                break;
            }
            break;
        default:
    ...
    
    
        /* device either stalls (value < 0) or reports success */
        return value;
    }
  • 相关阅读:
    docker使用
    window版docker安装及配置
    mysql命令
    xshell
    git 命令
    分页器原理
    PCL-Kinfu编译手册
    cmake-add_definitions
    cmake-include_directories
    cmake-source_group
  • 原文地址:https://www.cnblogs.com/jamboo/p/5033086.html
Copyright © 2020-2023  润新知