精华内容
下载资源
问答
  • gadget usb 增加端点

    2021-08-02 08:53:51
    <h1>usb_ep_autoconfig 主要是这个函数,函数的说明 ...* @gadget: The device to which the endpoint must belong. * @desc: Endpoint descriptor, with endpoint direction and transfer mode * init...

    实现在gadget中添加一个新的in端点进行数据传输

    主要是这个函数,函数的说明

     /**
     * usb_ep_autoconfig - choose an endpoint matching the descriptor
     * @gadget: The device to which the endpoint must belong.
     * @desc: Endpoint descriptor, with endpoint direction and transfer mode
     *    initialized.  For periodic transfers, the maximum packet
     *    size must also be initialized.  This is modified on success.
     *
     * By choosing an endpoint to use with the specified descriptor, this
     * routine simplifies writing gadget drivers that work with multiple
     * USB device controllers.  The endpoint would be passed later to
     * usb_ep_enable(), along with some descriptor.
     *
     * That second descriptor won't always be the same as the first one.
     * For example, isochronous endpoints can be autoconfigured for high
     * bandwidth, and then used in several lower bandwidth altsettings.
     * Also, high and full speed descriptors will be different.
     *
     * Be sure to examine and test the results of autoconfiguration on your
     * hardware.  This code may not make the best choices about how to use the
     * USB controller, and it can't know all the restrictions that may apply.
     * Some combinations of driver and hardware won't be able to autoconfigure.
     *
     * On success, this returns an un-claimed usb_ep, and modifies the endpoint
     * descriptor bEndpointAddress.  For bulk endpoints, the wMaxPacket value
     * is initialized as if the endpoint were used at full speed.  To prevent
     * the endpoint from being returned by a later autoconfig call, claim it
     * by assigning ep->driver_data to some non-null value.
     *
     * On failure, this returns a null endpoint descriptor.
     */

    0、在f_sourcesink结构体里加上新的端点指针,与新的请求指针

    struct usb_ep        *in_ep1;
    struct usb_request *in_req1;

    1、定义端点描述符

    static struct usb_endpoint_descriptor fs_source_desc1 = {
        .bLength =        USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =    USB_DT_ENDPOINT,

        .bEndpointAddress =    0x83,
        .bmAttributes =        USB_ENDPOINT_XFER_BULK,
    };

    2、配置的描述符

    static struct usb_descriptor_header *fs_source_sink_descs[] = {
        (struct usb_descriptor_header *) &source_sink_intf,
        (struct usb_descriptor_header *) &fs_source_desc,
        (struct usb_descriptor_header *) &fs_source_desc1,
        (struct usb_descriptor_header *) &fs_sink_desc,
        NULL,
    };

    3、将端点的描述符fs_source_desc1与定义的端点匹配

    in_ep1是增加的端点

        ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
        ss->in_ep1 = usb_ep_autoconfig(cdev->gadget, &fs_source_desc1);/

    4、给端点分配请求

    ss->in_req = alloc_ep_req(ss->in_ep, TX_REQ_LEN);
        if(ss->in_req == NULL){
            DBG_PRT("Alloc_ep_req for write in failed.\n");
            return -EFAULT;
        }
        ss->in_req->complete = source_sink_complete;


        ss->in_req1 = alloc_ep_req(ss->in_ep1, TX_REQ_LEN);
        if(ss->in_req1 == NULL){
            DBG_PRT("Alloc_ep_req for write in failed.\n");
            return -EFAULT;
        }
        ss->in_req1->complete = source_sink_complete;

    5、在写函数中将原端点未写完的数据通过新端点写

    ret = usb_ep_queue(ss->in_ep1, ss->in_req1, GFP_ATOMIC);

    展开全文
  • usb gadget usb host数据传输

    千次阅读 2017-07-14 17:28:19
    usb gadget usb host 数据传输 gadget driver gadget usb gadget

    usb gadget usb host数据传输


    pc作为host: 使用libusb库提供的接口开发

    android作为gadget: 在/kernel/driver/usb/gadget/android.c的基础上,添加function(即一个interface,包含两个endpoint)

    static int __init init(void)
    {
        int ret;
    
        INIT_LIST_HEAD(&android_dev_list);
        android_dev_count = 0;
    
        ret = platform_driver_register(&android_platform_driver);
        ...
    }

    在android.c中,初始化android_dev_list列表,现在只有一个dev,android_dev_count是对android_dev的记数。下面注册platform总线,关注android_platform_driver

    static struct platform_driver android_platform_driver = {
        .driver = {
            .name = "android_usb",
            .of_match_table = usb_android_dt_match,
        },
        .probe = android_probe,
        .remove = android_remove,
        .id_table = android_id_table,
    };

    platform总线注册,重点进android_probe函数

    static int android_probe(struct platform_device *pdev)
    {
        struct android_usb_platform_data *pdata;
        ...
        if (pdev->dev.of_node) {
            pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        ...
            of_get_property(pdev->dev.of_node, "qcom,pm-qos-latency",
                                    &prop_len);
        ...
    
        len = of_property_count_strings(pdev->dev.of_node,
                "qcom,supported-func");
        ...
            android_class = class_create(THIS_MODULE, "android_usb");
        ...
        android_dev = kzalloc(sizeof(*android_dev), GFP_KERNEL);
        android_dev->name = pdev->name;
        android_dev->disable_depth = 1;
        android_dev->functions = 
            supported_list ? supported_list : default_functions;
        android_dev->pdata = pdata;
        list_add_tail(&android_dev->list_item, &android_dev_list);
        ...
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res) {
            diag_dload = devm_ioremap(&pdev->dev, res->start,
                                resource_size(res));
            if (!diag_dload) {
                dev_err(&pdev->dev, "ioremap failed\n");
                ret = -ENOMEM;
                goto err_dev;
            }
        }
        ...
        if (pdata)
            android_usb_driver.gadget_driver.usb_core_id =
                            pdata->usb_core_id;
        ret = android_create_device(android_dev,
                android_usb_driver.gadget_driver.usb_core_id);
        ...
        ret = usb_composite_probe(&android_usb_driver);
        if (ret) {
            /* Perhaps UDC hasn't probed yet, try again later */
            if (ret == -ENODEV)
                ret = -EPROBE_DEFER;
            else
                pr_err("%s(): Failed to register android composite driver\n",
                    __func__);
            goto err_probe;
        }
    
    }

    这个函数在注册platform总线时调用,很长,慢慢看。
    1.首先为android_usb_platform_data申请内存,不深究了。
    2.之后通过of_get_property,of_property_count_strings等读取dts的信息。
    3.创建名为android_usb的sys文件,在/sys/class/android_usb 该节点下,会用来存放所有的usb信息,类似:android0代表第一个之前说的第一个android_dev,android0下又会存放该设备的所有信息,待会再看

    nedplus:/sys/class/android_usb # ls
    android0    f_audio        f_charging f_ecm_qc f_loopback     f_mtp f_qdss  f_rndis_qc  f_usb_mbim 
    f_accessory f_audio_source f_diag     f_ffs    f_mass_storage f_ncm f_rmnet f_serial    f_video    
    f_acm       f_ccid         f_ecm      f_gps    f_midi         f_ptp f_rndis f_uac2_func

    4.回到probe函数,创建好android_usb后,需要初始化android_dev的信息,然后添加到android_dev_list列表中去
    5.platform_get_resource获取io资源,以便初始化及使用
    6.进入到android_create_device

    static int android_create_device(struct android_dev *dev, u8 usb_core_id)
    {
        struct device_attribute **attrs = android_usb_attributes;
        struct device_attribute *attr;
        ...
        dev->dev = device_create(android_class, NULL, MKDEV(0, usb_core_id),
            NULL, device_node_name);
        ...
        while ((attr = *attrs++)) {
            err = device_create_file(dev->dev, attr);
        }
    }
    static struct device_attribute *android_usb_attributes[] = {
        &dev_attr_idVendor,
        &dev_attr_idProduct,
        &dev_attr_bcdDevice,
        &dev_attr_bDeviceClass,
        &dev_attr_bDeviceSubClass,
        &dev_attr_bDeviceProtocol,
        &dev_attr_iManufacturer,
        &dev_attr_iProduct,
        &dev_attr_iSerial,
        &dev_attr_functions,
        &dev_attr_enable,
        &dev_attr_pm_qos,
        &dev_attr_up_pm_qos_sample_sec,
        &dev_attr_down_pm_qos_sample_sec,
        &dev_attr_up_pm_qos_threshold,
        &dev_attr_down_pm_qos_threshold,
        &dev_attr_idle_pc_rpm_no_int_secs,
        &dev_attr_pm_qos_state,
        &dev_attr_state,
        &dev_attr_remote_wakeup,
        NULL
    };

    6.1先device_create创建android0节点,再在android0下根据android_usb_attributes创建其属性文件,以便和user交互

    nedplus:/sys/class/android_usb/android0 # ls
    bDeviceClass           f_audio_source f_midi      f_usb_mbim              pm_qos_state         
    bDeviceProtocol        f_ccid         f_mtp       f_video                 power                
    bDeviceSubClass        f_charging     f_ncm       functions               remote_wakeup        
    bcdDevice              f_diag         f_ptp       iManufacturer           state                
    down_pm_qos_sample_sec f_ecm          f_qdss      iProduct                subsystem            
    down_pm_qos_threshold  f_ecm_qc       f_rmnet     iSerial                 uevent               
    enable                 f_ffs          f_rndis     idProduct               up_pm_qos_sample_sec 
    f_accessory            f_gps          f_rndis_qc  idVendor                up_pm_qos_threshold  
    f_acm                  f_loopback     f_serial    idle_pc_rpm_no_int_secs 
    f_audio                f_mass_storage f_uac2_func pm_qos 

    7.回到probe函数,最后调用usb_composite_probe(&android_usb_driver)在注册usb驱动,又到了重点:android_usb_driver。看下usb_composite_probe函数的说明先,总体把握下

    /**
     * usb_composite_probe() - register a composite driver
     * @driver: the driver to register
     *
     * Context: single threaded during gadget setup
     *
     * This function is used to register drivers using the composite driver
     * framework.  The return value is zero, or a negative errno value.
     * Those values normally come from the driver's @bind method, which does
     * all the work of setting up the driver to match the hardware.
     *
     * On successful return, the gadget is ready to respond to requests from
     * the host, unless one of its components invokes usb_gadget_disconnect()
     * while it was binding.  That would usually be done in order to wait for
     * some userspace participation.
     */

    继续跟进android_usb_driver

    static struct usb_composite_driver android_usb_driver = {
        .name       = "android_usb",
        .dev        = &device_desc,
        .strings    = dev_strings,
        .bind       = android_bind,
        .unbind     = android_usb_unbind,
        .disconnect = android_disconnect,
        .max_speed  = USB_SPEED_SUPER
    };

    结构体很简单,主要是要实现里面的函数。dev代表usb_device_descriptor。关于描述符,可以参考http://www.cnblogs.com/tianchiyuyin/p/5139948.html
    再看usb_composite_probe函数介绍时了解到了他的bind函数很重要

    static int android_bind(struct usb_composite_dev *cdev)
    {
        ...
        /* Allocate string descriptor numbers ... note that string
         * contents can be overridden by the composite_dev glue.
         */
        id = usb_string_id(cdev);
        ...
            ret = android_init_functions(dev->functions, cdev);
    }

    usb_string_id,应该是用来保存配置及interface信息的,无需深究。主要看android_init_functions

    static int android_init_functions(struct android_usb_function **functions,
                      struct usb_composite_dev *cdev)
    {
        ...
        for (; (f = *functions++); index++) {
            f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name);
            f->android_dev = NULL;
            f->dev = device_create(android_class, dev->dev,
                    MKDEV(0, index), f, f->dev_name);
            if (f->init) {
                err = f->init(f, cdev);
            }
    
            attrs = f->attributes;
            if (attrs) {
                while ((attr = *attrs++) && !err)
                    err = device_create_file(f->dev, attr);
            }
        }
    }

    这里的functions,主要是probe函数中赋值的:android_dev->functions = supported_list ? supported_functions : default_functions;在android_usb创建f_开头的节点,如果函数声明了init函数则调用,并且在该节点下创建属性文件,让user来配置读取信息的老手段。
    到了这里是不是感觉结束了,并没有,这个时候该请出我们的init.qcom.usb.rc文件了,关于init.rc,网上很多说明,直接问度娘

    on property:sys.usb.config=mtp,diag,adb && property:sys.usb.configfs=0
        write /sys/class/android_usb/android0/enable 0
        write /sys/class/android_usb/android0/iSerial ${ro.serialno}
        write /sys/class/android_usb/android0/idVendor 05C6
        write /sys/class/android_usb/android0/idProduct 903A
        write /sys/class/android_usb/android0/f_diag/clients diag
        write /sys/class/android_usb/android0/functions mtp,diag,adb,loopback
        write /sys/class/android_usb/android0/enable  1
        start adbd
        setprop sys.usb.state ${sys.usb.config}

    截取了其中的一段,当sys.usb.config属性被设置为mtp,diag,adb时候执行,这些设备节点是不是很熟悉,没错,就是刚刚在probe中调用android_create_device创建的android_usb_attributes,可以回头查看。我们一句一句的跟进

    static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
                    const char *buff, size_t size)
    {
        struct android_dev *dev = dev_get_drvdata(pdev);
        ...
        sscanf(buff, "%d", &enabled);
        if (enabled && !dev->enabled) {
    
            ...
    
        } else if (!enabled && dev->enabled) {
            android_disable(dev);
            list_for_each_entry(conf, &dev->configs, list_item)
                list_for_each_entry(f_holder, &conf->enabled_functions,
                            enabled_list) {
                    if (f_holder->f->disable)
                        f_holder->f->disable(f_holder->f);
                }
            dev->enabled = false;
        } 
        ...
    }

    先往enable中写0,调用android_disable,从字面意思就可以看出是关闭usb_dev的功能,里面主要调用usb_gadget_disconnect。之后便是遍历每个config,循环遍历config下的functions(这个function可以简单理解为interface),最后调用每个被function的disable函数。ok,继续
    往iSerial,idVendor,idProduct,中写值很简单,就是改变device_desc中的变量
    往f_diag/clients写值,这个和配置function相关,关注functions属性节点

    static ssize_t
    functions_store(struct device *pdev, struct device_attribute *attr,
                       const char *buff, size_t size)
    {
        ...
        /* Clear previous enabled list */
        list_for_each_entry(conf, &dev->configs, list_item) {
            while (conf->enabled_functions.next !=
                    &conf->enabled_functions) {
                f_holder = list_entry(conf->enabled_functions.next,
                        typeof(*f_holder),
                        enabled_list);
                f_holder->f->android_dev = NULL;
                list_del(&f_holder->enabled_list);
                kfree(f_holder);
            }
            INIT_LIST_HEAD(&conf->enabled_functions);
        }
        ...
        while (b) {
            ...
            while (conf_str) {
                name = strsep(&conf_str, ",");
                is_ffs = 0;
                strlcpy(aliases, dev->ffs_aliases, sizeof(aliases));
                a = aliases;
    
                while (a) {
                    char *alias = strsep(&a, ",");
                    if (alias && !strcmp(name, alias)) {
                        is_ffs = 1;
                        break;
                    }
                }
    
                if (is_ffs) {
                    if (ffs_enabled)
                        continue;
                    err = android_enable_function(dev, conf, "ffs");
                    if (err)
                        pr_err("android_usb: Cannot enable ffs (%d)",
                                        err);
                    else
                        ffs_enabled = 1;
                    continue;
                }
    
                if (!strcmp(name, "rndis") &&
                    !strcmp(strim(rndis_transports), "BAM2BAM_IPA"))
                    name = "rndis_qc";
    
                err = android_enable_function(dev, conf, name);
                if (err)
                    pr_err("android_usb: Cannot enable '%s' (%d)",
                                name, err);
            }
        }
    
        /* Free uneeded configurations if exists */
        while (curr_conf->next != &dev->configs) {
            conf = list_entry(curr_conf->next,
                      struct android_configuration, list_item);
            free_android_config(dev, conf);
        }
    
        mutex_unlock(&dev->mutex);
    
        return size;
    }

    Clear previous enabled list,清除之前的enable functions。解析传进的值,先判断是不是adb,是的话开启ffs函数。总之会调用到android_enable_function来打开需要开启的function

    static int android_enable_function(struct android_dev *dev,
                       struct android_configuration *conf,
                       char *name)
    {
        struct android_usb_function **functions = dev->functions;
        struct android_usb_function_holder *f_holder;
        ...
        while ((f = *functions++)) {
            if (!strcmp(name, f->name)) {
                    ...
                    f_holder = kzalloc(sizeof(*f_holder),
                            GFP_KERNEL);
                    if (!f_holder) {
                        pr_err("Failed to alloc f_holder\n");
                        return -ENOMEM;
                    }
    
                    f->android_dev = dev;
                    f_holder->f = f;
                    list_add_tail(&f_holder->enabled_list,
                              &conf->enabled_functions);
                    ...
                }
            }
        }
        return -EINVAL;
    }

    根据name,和android_dev下的functions进行逐个对比直至找到那个function,做的很简单,就是申请f_holder并和android_dev以及找到的function绑定,添加到conf->enabled_functions中去一边在往enable中写1的时候使用

    static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
                    const char *buff, size_t size)
    {
        ...
        if (enabled && !dev->enabled) {
            ...
            list_for_each_entry(conf, &dev->configs, list_item)
                list_for_each_entry(f_holder, &conf->enabled_functions,
                            enabled_list) {
                    if (f_holder->f->enable)
                        f_holder->f->enable(f_holder->f);
                        ...
                }
            err = android_enable(dev);
            ...
            dev->enabled = true;
        }
    }

    又回到了enable_store函数,也很简单,调用各个function下的enable函数就ok了,之后调用到android_enable,这个函数可比android_disable有意思多了

    static int android_enable(struct android_dev *dev)
    {
        struct usb_composite_dev *cdev = dev->cdev;
        struct android_configuration *conf;
        ...
        if (--dev->disable_depth == 0) {
    
            list_for_each_entry(conf, &dev->configs, list_item) {
                err = usb_add_config(cdev, &conf->usb_config,
                            android_bind_config);
                ...
                }
            }
        ...
        return err;
    }

    感觉list_for_each_entry用的很多啊,遍历dev->configs链表,usb_add_config来为dev_desc添加usb_configuration。这边将函数android_bind_config作为参数传了进去。跟进usb_add_config

    int usb_add_config(struct usb_composite_dev *cdev,
            struct usb_configuration *config,
            int (*bind)(struct usb_configuration *))
    {
        ...
        status = bind(config);
        ...
        return status;
    }

    调用了我们传进去的android_bind_config函数,go go go

    static int android_bind_config(struct usb_configuration *c)
    {
        ...
        ret = android_bind_enabled_functions(dev, c);
        ...
        return 0;
    }

    继续…

    static int
    android_bind_enabled_functions(struct android_dev *dev,
                       struct usb_configuration *c)
    {
        struct android_usb_function_holder *f_holder;
        struct android_configuration *conf =
            container_of(c, struct android_configuration, usb_config);
        int ret;
    
        list_for_each_entry(f_holder, &conf->enabled_functions, enabled_list) {
            ret = f_holder->f->bind_config(f_holder->f, c);
            if (ret) {
                pr_err("%s: %s failed\n", __func__, f_holder->f->name);
                while (!list_empty(&c->functions)) {
                    struct usb_function     *f;
    
                    f = list_first_entry(&c->functions,
                        struct usb_function, list);
                    if (f->config) {
                        list_del(&f->list);
                        if (f->unbind)
                            f->unbind(c, f);
                    }`这里写代码片`
                }
                if (c->unbind)
                    c->unbind(c);
                return ret;
            }
            f_holder->f->bound = true;
        }
        return 0;
    }

    函数不是很长,重点在链表的遍历。遍历conf->enabled_functions,调用各个function下的bind_config函数
    至此,我们调用了dev functions下的init,enable,bind_config 整个function的使能工作就完成了,但是一个function的各个函数怎么定义呢,这个时候就出现了android_usb_function结构体了,ok定义一个简单的function来实验一下

    static struct android_usb_function loopback_function = {
        .name       = "loopback",
        //.init     = loopback_function_init,
        //.enable       = loopback_function_enable,
        //.disable  = loopback_function_disable,
        //.cleanup  = loopback_function_cleanup,
        .bind_config    = loopback_function_bind_config,
        //.attributes   = ffs_function_attributes,
    };

    我们定义了一个android_usb_function名叫loopback_function,对结构体赋值,名字叫loopback,回忆一下,之前往/sys/class/android_usb/android0/functions写使能的function时需要与其对比。之后主要实现bind_config函数
    我在参考了别的function后写了一个简单的,来看一下

    static int loopback_function_bind_config(struct android_usb_function *f,
                        struct usb_configuration *c)
    {
        int ret;
        struct functionfs_config *config = 
                kzalloc(sizeof(struct functionfs_config), GFP_KERNEL);
        if (!config){
            pr_err("[LOL] loopback_function_bind_config kzalloc failed\n");
                return -ENOMEM;
        }
        f->config = config;
            //config = f->config;
        config->fi = loopback_alloc_instance();
        if (IS_ERR(config->fi)){
            pr_err("[LOL] loopback_function_bind_config usb_get_function_instance failed\n");
            return PTR_ERR(config->fi);
        }
    
        config->func = loopback_alloc(config->fi);
        if (IS_ERR(config->func)){
            pr_err("[LOL] loopback_function_bind_config usb_get_function failed\n");
            return PTR_ERR(config->func);
        }
    
        ret = usb_add_function(c, config->func);
        if (ret) {
            pr_err("%s(): usb_add_function() fails (err:%d) for ffs\n",
                                __func__, ret);
    
            usb_put_function(config->func);
            config->func = NULL;
        }
    
        return ret;
    }

    首先为我们的functions_config申请内存,并绑定到android_usb_function上去,调用loopback_alloc_instance获取usb_function_instance,根据usb_function_instance获取usb_function,得到了一个usb_function结构体。用usb_add_function(c, config->func);添加到usb_configuration中去,usb_add_function是composite.c实现的函数我们不要关心,我们要关心的是我们得到usb_function是什么样的,我们如果要修改其功能需要怎么做呢?上面的loopback_alloc函数是在kernel/drivers/usb/gadget/function/loopback.c中,源码可以访问https://github.com/torvalds/linux/blob/master/drivers/usb/gadget/function/f_loopback.c
    f_loopback.c中用来获取usb_function

    struct usb_function *loopback_alloc(struct usb_function_instance *fi)
    {
        struct f_loopback   *loop;
        struct f_lb_opts    *lb_opts;
    
        loop = kzalloc(sizeof *loop, GFP_KERNEL);
        if (!loop)
            return ERR_PTR(-ENOMEM);
    
        lb_opts = container_of(fi, struct f_lb_opts, func_inst);
    
        mutex_lock(&lb_opts->lock);
        lb_opts->refcnt++;
        mutex_unlock(&lb_opts->lock);
    
        buflen = lb_opts->bulk_buflen;
        qlen = lb_opts->qlen;
        if (!qlen)
            qlen = 32;
    
        loop->function.name = "loopback";
        loop->function.bind = loopback_bind;
        loop->function.set_alt = loopback_set_alt;
        loop->function.disable = loopback_disable;
        loop->function.strings = loopback_strings;
    
        loop->function.free_func = lb_free_func;
    
        return &loop->function;
    }

    主要看bind,set_alt,这两个函数已经被我修改过了,变得更简单的,源码请访问https://github.com/torvalds/linux/blob/master

    static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
    {
        struct usb_composite_dev *cdev = c->cdev;
        struct f_loopback   *loop = func_to_loop(f);
        int         id;
        int ret;
        /* allocate interface ID(s) */
        id = usb_interface_id(c, f);
        if (id < 0)
            return id;
        loopback_intf.bInterfaceNumber = id;
    
        id = usb_string_id(cdev);
        if (id < 0)
            return id;
        strings_loopback[0].id = id;
        loopback_intf.iInterface = id;
    
        /* allocate endpoints */
    
        loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
        if (!loop->in_ep) {
    autoconf_fail:
            ERROR(cdev, "%s: can't autoconfigure on %s\n",
                f->name, cdev->gadget->name);
            return -ENODEV;
        }
        loop->in_ep->driver_data = cdev;    /* claim */
    
        loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
        if (!loop->out_ep)
            goto autoconf_fail;
        loop->out_ep->driver_data = cdev;   /* claim */
    
        /* support high speed hardware */
        hs_loop_source_desc.bEndpointAddress =
            fs_loop_source_desc.bEndpointAddress;
        hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
    
        /* support super speed hardware */
        ss_loop_source_desc.bEndpointAddress =
            fs_loop_source_desc.bEndpointAddress;
        ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
    
        ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
                ss_loopback_descs);
        if (ret)
            return ret;
        DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
            (gadget_is_superspeed(c->cdev->gadget) ? "super" :
             (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
                f->name, loop->in_ep->name, loop->out_ep->name);
        return 0;
    }

    先allocate interface ID,这个是必须的为该function申请一个interface编号,之后便是使用usb_ep_autoconfig申请了两个端点,一个输出一个输入。为每个端点的driver_data赋值是必要的

    /**
     * usb_ep_autoconfig() - choose an endpoint matching the
     * descriptor
     * @gadget: The device to which the endpoint must belong.
     * @desc: Endpoint descriptor, with endpoint direction and transfer mode
     *  initialized.  For periodic transfers, the maximum packet
     *  size must also be initialized.  This is modified on success.
     *
     * By choosing an endpoint to use with the specified descriptor, this
     * routine simplifies writing gadget drivers that work with multiple
     * USB device controllers.  The endpoint would be passed later to
     * usb_ep_enable(), along with some descriptor.
     *
     * That second descriptor won't always be the same as the first one.
     * For example, isochronous endpoints can be autoconfigured for high
     * bandwidth, and then used in several lower bandwidth altsettings.
     * Also, high and full speed descriptors will be different.
     *
     * Be sure to examine and test the results of autoconfiguration on your
     * hardware.  This code may not make the best choices about how to use the
     * USB controller, and it can't know all the restrictions that may apply.
     * Some combinations of driver and hardware won't be able to autoconfigure.
     *
     * On success, this returns an un-claimed usb_ep, and modifies the endpoint
     * descriptor bEndpointAddress.  For bulk endpoints, the wMaxPacket value
     * is initialized as if the endpoint were used at full speed.  To prevent
     * the endpoint from being returned by a later autoconfig call, claim it
     * by assigning ep->driver_data to some non-null value.
     *
     * On failure, this returns a null endpoint descriptor.
     */

    成功返回un-claimed usb_ep,To prevent the endpoint from being returned by a later autoconfig call, claim it by assigning ep->driver_data to some non-null value.
    上面还说需要usb_ep_enable()进行时能别急,我们在进入loopback_set_alt

    static int loopback_set_alt(struct usb_function *f,
            unsigned intf, unsigned alt)
    {
    
        struct f_loopback   *loop = func_to_loop(f);
        struct usb_composite_dev *cdev = f->config->cdev;
    
        /* we know alt is zero */
        if (loop->in_ep->driver_data){
            disable_loopback(loop);
        }
        return enable_loopback(cdev, loop);
    }

    进入enable_loopback

    static int
    enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
    {
        int                 result = 0;
    
        pr_err("[LOL]enable_loopback!!!\n");
    
        result = enable_endpoint(cdev, loop, loop->in_ep);
        if (result)
            goto out;
    
        result = enable_endpoint(cdev, loop, loop->out_ep);
        if (result)
            goto disable_in;
    
        result = alloc_requests(cdev, loop);
        if (result)
            goto disable_out;
    
        pr_err( "[LOL]%s enabled\n", loop->function.name);
        DBG(cdev, "%s enabled\n", loop->function.name);
        return result;
    
    disable_out:
        usb_ep_disable(loop->out_ep);
    disable_in:
        usb_ep_disable(loop->in_ep);
    out:
        return result;
    }

    先进入enable_endpoint

    static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
            struct usb_ep *ep)
    {
        int                 result;
        /*
         * one endpoint writes data back IN to the host while another endpoint
         * just reads OUT packets
         */
        result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
        if (result)
            goto fail0;
        result = usb_ep_enable(ep);
        if (result < 0)
            goto fail0;
        ep->driver_data = loop;
        return 0;
    fail0:
        pr_err("[LOL]enable_endpoint failed!!!\n");
        return result;
    }
    struct send_data {
        struct usb_ep *ep;
        char c ;
    };

    这里为端点配置了传输速度,之后便是调用usb_ep_enable来使能端点了

    static int alloc_requests(struct usb_composite_dev *cdev,
                  struct f_loopback *loop)
    {
        struct usb_request *in_req, *out_req;
        //int i;
        int result = 0;
    
        //send data
        in_req = lb_alloc_ep_req(loop->in_ep, 1024*1024*2);
        in_req->complete = loopback_complete;
        memset(in_req->buf, 0 , in_req->length);
        result = usb_ep_queue(loop->in_ep, in_req, GFP_ATOMIC);
    
        //recv_data
        out_req = lb_alloc_ep_req(loop->out_ep, 512);
        out_req->complete = loopback_complete;
        memset(out_req->buf, 0 , out_req->length);
        result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC);
        return result;
    }

    这个函数被我修改的不成人形了,并且没有做错误处理,不要学我…
    我们在之前已经申请了两个用来传输的端点,那么怎么来使用它们呢?答案就在这里:
    usb的传输需要一个usb_request结构体,跟进lb_alloc_ep_req会看到这个函数

    struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
    {
        struct usb_request      *req;
    
        req = usb_ep_alloc_request(ep, GFP_ATOMIC);
        if (req) {
            req->length = len ?: default_len;
            req->buf = kmalloc(req->length, GFP_ATOMIC);
            if (!req->buf) {
                usb_ep_free_request(ep, req);
                req = NULL;
            }
        }
        return req;
    }

    必须使用usb_ep_alloc_request来申请结构体,之后便是申请存放传输数据的内存
    传输完成后回调用usb_request中的complete函数
    最后便是usb_ep_queue将我们刚创建的usb_request放入到传输队列等到传输了
    来看一下complete函数

    static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
    {
        static unsigned char c = 0;
        struct f_loopback   *loop = ep->driver_data;
    
        if (ep == loop->out_ep){
            c++;
            memset(req->buf, c , req->length);
            usb_ep_queue(ep, req, GFP_ATOMIC);
    
        }else if(ep == loop->in_ep){
            ((char *)req->buf)[req->length-1] = '\0';
            //do what you want , copy
            usb_ep_queue(ep, req, GFP_ATOMIC);
        }
    }

    当端点是out_ep时,说明之前的发送数据完成了,我们改变发送的字符,继续将usb_request添加到队列中进行传输,至此整个分析流程就完成了,但是这里还有很多不完善的地方比如出错的处理,内存的回收,还需要改善啊,但是测试是够了,接下来就是host端了

    host:

    host我是在libusb中的examples中的testlibusb基础上做了简单的修改,废话不多说直接上代码

    #include <stdio.h>
    #include <string.h>
    #include "libusb.h"
    #define msleep(msecs) nanosleep(&(struct timespec){msecs / 1000, (msecs * 1000000) % 1000000000UL}, NULL);
    
    int main (int argc, char *argv[])
    {
        libusb_device **devs;       //pointer to pointer of device, used to retrieve a list of devices  
        libusb_context *ctx = NULL; //a libusb session  
        int r;                      //for return values  
        ssize_t cnt;                //holding number of devices in list  
        r = libusb_init (&ctx);     //initialize a library session  
        if (r < 0)
        {
            printf ("Init Error %d\n", r);  //there was an error  
            return 1;
        }
        libusb_set_debug (ctx, 3);  //set verbosity level to 3, as suggested in the documentation  
        cnt = libusb_get_device_list (ctx, &devs);  //get the list of devices  
        if (cnt < 0)
        {
            printf ("Get Device Error\n");  //there was an error  
        }
    
        libusb_device_handle *dev_handle;   //a device handle  
        dev_handle = libusb_open_device_with_vid_pid (ctx, 0x05c6, 0x9999); //open device
        if (dev_handle == NULL)
        {
            printf ("Cannot open device\n");
            libusb_free_device_list (devs, 1);  //free the list, unref the devices in it  
            libusb_exit (ctx);      //close the session  
            return 0;
        }
        else
        {
            printf ("Device Opened\n");
            libusb_free_device_list (devs, 1);  //free the list, unref the devices in it  
            /*
               if(libusb_kernel_driver_active(dev_handle, 3) == 1) { //find out if kernel driver is attached  
               printf("Kernel Driver Active\n");  
               if(libusb_detach_kernel_driver(dev_handle, 3) == 0) //detach it  
               printf("Kernel Driver Detached!\n");  
               }  
             */
            r = libusb_claim_interface (dev_handle, 3); //这边的3代表3号interface,claim interface 3 (the first) of device (mine had jsut 1)  
            if (r < 0)
            {
                printf ("Cannot Claim Interface\n");
                return 1;
            }
            printf ("Claimed Interface\n");
            int size;
            unsigned char read_buf[1024 * 1024 * 2] = "\0";
            unsigned char send_buf[1024] = "\0";
            struct timeval old_time, current_time;
            gettimeofday (&old_time, NULL);
            static unsigned long count = 0;
            while (1)
            {
                int i = 0;
                size = 0;
                int rr = 0;
                rr = libusb_bulk_transfer (dev_handle, 0x85, read_buf, sizeof (read_buf),   //1024*1024,  
                    &size, 1000);
                count = size + count;
                gettimeofday (&current_time, NULL);
                if ((1000000 * (current_time.tv_sec - old_time.tv_sec) + current_time.tv_usec - old_time.tv_usec) > 1000000)
                {
                    printf ("count:%lu ----\n", count / 1024);
                    count = 0;
                    old_time = current_time;
                    /*
                       printf("libusb_bulk_transfer rr: %d \n" , rr);  
                       printf("size: %d\n" ,size);  
                       printf("data:  recv");
                       for(int j=0; j<size; j++)  
                       printf("%02x", (unsigned char)(read_buf[j]));  
                       printf("\n");
                     */
                }
            }
    
            r = libusb_release_interface (dev_handle, 3);   //release the claimed interface  
            if (r != 0)
            {
                printf ("Cannot Release Interface\n");
                return 1;
            }
            printf ("Released Interface\n");
    
            libusb_attach_kernel_driver (dev_handle, 3);
            libusb_close (dev_handle);
            libusb_exit (ctx);      //close the session  
            return 0;
        }
    }
    展开全文
  • gadgetusb_function

    2021-03-17 22:26:25
    gadgetusb_function usb_function /** * struct usb_function - describes one function of a configuration * @name: For diagnostics, identifies the function. * @strings: tables of strings, keyed by ...

    gadget之usb_function

    usb_function

    
    /**
     * struct usb_function - describes one function of a configuration
     * @name: For diagnostics, identifies the function.
     * @strings: tables of strings, keyed by identifiers assigned during bind()
     *	and by language IDs provided in control requests
     * @fs_descriptors: Table of full (or low) speed descriptors, using interface and
     *	string identifiers assigned during @bind().  If this pointer is null,
     *	the function will not be available at full speed (or at low speed).
     * @hs_descriptors: Table of high speed descriptors, using interface and
     *	string identifiers assigned during @bind().  If this pointer is null,
     *	the function will not be available at high speed.
     * @ss_descriptors: Table of super speed descriptors, using interface and
     *	string identifiers assigned during @bind(). If this
     *	pointer is null after initiation, the function will not
     *	be available at super speed.
     * @ssp_descriptors: Table of super speed plus descriptors, using
     *	interface and string identifiers assigned during @bind(). If
     *	this pointer is null after initiation, the function will not
     *	be available at super speed plus.
     * @config: assigned when @usb_add_function() is called; this is the
     *	configuration with which this function is associated.
     * @os_desc_table: Table of (interface id, os descriptors) pairs. The function
     *	can expose more than one interface. If an interface is a member of
     *	an IAD, only the first interface of IAD has its entry in the table.
     * @os_desc_n: Number of entries in os_desc_table
     * @bind: Before the gadget can register, all of its functions bind() to the
     *	available resources including string and interface identifiers used
     *	in interface or class descriptors; endpoints; I/O buffers; and so on.
     * @unbind: Reverses @bind; called as a side effect of unregistering the
     *	driver which added this function.
     * @free_func: free the struct usb_function.
     * @mod: (internal) points to the module that created this structure.
     * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
     *	initialize usb_ep.driver data at this time (when it is used).
     *	Note that setting an interface to its current altsetting resets
     *	interface state, and that all interfaces have a disabled state.
     * @get_alt: Returns the active altsetting.  If this is not provided,
     *	then only altsetting zero is supported.
     * @disable: (REQUIRED) Indicates the function should be disabled.  Reasons
     *	include host resetting or reconfiguring the gadget, and disconnection.
     * @setup: Used for interface-specific control requests.
     * @req_match: Tests if a given class request can be handled by this function.
     * @suspend: Notifies functions when the host stops sending USB traffic.
     * @resume: Notifies functions when the host restarts USB traffic.
     * @get_status: Returns function status as a reply to
     *	GetStatus() request when the recipient is Interface.
     * @func_suspend: callback to be called when
     *	SetFeature(FUNCTION_SUSPEND) is reseived
     *
     * A single USB function uses one or more interfaces, and should in most
     * cases support operation at both full and high speeds.  Each function is
     * associated by @usb_add_function() with a one configuration; that function
     * causes @bind() to be called so resources can be allocated as part of
     * setting up a gadget driver.  Those resources include endpoints, which
     * should be allocated using @usb_ep_autoconfig().
     *
     * To support dual speed operation, a function driver provides descriptors
     * for both high and full speed operation.  Except in rare cases that don't
     * involve bulk endpoints, each speed needs different endpoint descriptors.
     *
     * Function drivers choose their own strategies for managing instance data.
     * The simplest strategy just declares it "static', which means the function
     * can only be activated once.  If the function needs to be exposed in more
     * than one configuration at a given speed, it needs to support multiple
     * usb_function structures (one for each configuration).
     *
     * A more complex strategy might encapsulate a @usb_function structure inside
     * a driver-specific instance structure to allows multiple activations.  An
     * example of multiple activations might be a CDC ACM function that supports
     * two or more distinct instances within the same configuration, providing
     * several independent logical data links to a USB host.
     */
    //描述一个配置的一个功能
    struct usb_function {
    	const char			*name;//功能名称,匹配时需要用到
    	struct usb_gadget_strings	**strings;//string数组,通过bind中分配的id访问
    	struct usb_descriptor_header	**fs_descriptors;//全速和低速的描述符表,用于bind中分配的接口描述符和string描述符
    	struct usb_descriptor_header	**hs_descriptors;//高速描述符表
    	struct usb_descriptor_header	**ss_descriptors;
    	struct usb_descriptor_header	**ssp_descriptors;
    
    	struct usb_configuration	*config;//调用usb_add_function()函数赋值,是这个功能关联的配置描述符
    
    	struct usb_os_desc_table	*os_desc_table;
    	unsigned			os_desc_n;
    
    	/* REVISIT:  bind() functions can be marked __init, which
    	 * makes trouble for section mismatch analysis.  See if
    	 * we can't restructure things to avoid mismatching.
    	 * Related:  unbind() may kfree() but bind() won't...
    	 */
    
    	/* configuration management:  bind/unbind */
    	int			(*bind)(struct usb_configuration *,
    					struct usb_function *);/*unbind的逆操作*/
    	void			(*unbind)(struct usb_configuration *,
    					struct usb_function *);
    	void			(*free_func)(struct usb_function *f);
    	struct module		*mod;
    
    	/* runtime state management */
    	int			(*set_alt)(struct usb_function *,
    					unsigned interface, unsigned alt);
    	int			(*get_alt)(struct usb_function *,
    					unsigned interface);
    	void			(*disable)(struct usb_function *);
    	int			(*setup)(struct usb_function *,
    					const struct usb_ctrlrequest *);
    	bool			(*req_match)(struct usb_function *,
    					const struct usb_ctrlrequest *,
    					bool config0);
    	void			(*suspend)(struct usb_function *);
    	void			(*resume)(struct usb_function *);
    
    	/* USB 3.0 additions */
    	int			(*get_status)(struct usb_function *);
    	int			(*func_suspend)(struct usb_function *,
    						u8 suspend_opt);
    	/* private: */
    	/* internals */
    	struct list_head		list;
    	DECLARE_BITMAP(endpoints, 32);
    	const struct usb_function_instance *fi;
    
    	unsigned int		bind_deactivated:1;
    };
    
    

    usb_function_driver

    struct usb_function_driver {
    	const char *name;//usb_function_driver的name,如:"acm"
    	struct module *mod;
    	struct list_head list;
    	struct usb_function_instance *(*alloc_inst)(void);//alloc usb_function_instance
    	struct usb_function *(*alloc_func)(struct usb_function_instance *inst);//alloc usb_function
    };
    

    创建usb_function_driver
    源码:include/linux/usb/composite.h

    #define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)		\
    	static struct usb_function_driver _name ## usb_func = {		\
    		.name = __stringify(_name),				\
    		.mod  = THIS_MODULE,					\
    		.alloc_inst = _inst_alloc,				\
    		.alloc_func = _func_alloc,				\
    	};								\
    	MODULE_ALIAS("usbfunc:"__stringify(_name));
    

    注册usb_function_driver
    源码:include/linux/usb/composite.h

    #define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc)	\
    	DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)		\
    	static int __init _name ## mod_init(void)			\
    	{								\
    		return usb_function_register(&_name ## usb_func);	\
    	}								\
    	static void __exit _name ## mod_exit(void)			\
    	{								\
    		usb_function_unregister(&_name ## usb_func);		\
    	}								\
    	module_init(_name ## mod_init);					\
    	module_exit(_name ## mod_exit)
    

    源码:drivers/usb/gadget/functions.c

    int usb_function_register(struct usb_function_driver *newf)
    {
    	struct usb_function_driver *fd;
    	int ret;
    
    	ret = -EEXIST;
    
    	mutex_lock(&func_lock);
    	//遍历func_list链表,根据func_name判断该func是否注册
    	list_for_each_entry(fd, &func_list, list) {
    		if (!strcmp(fd->name, newf->name))
    			goto out;
    	}
    	ret = 0;
    	//将new_func添加到func_list中
    	list_add_tail(&newf->list, &func_list);
    out:
    	mutex_unlock(&func_lock);
    	return ret;
    }
    EXPORT_SYMBOL_GPL(usb_function_register);
    
    void usb_function_unregister(struct usb_function_driver *fd)
    {
    	mutex_lock(&func_lock);
    	list_del(&fd->list);
    	mutex_unlock(&func_lock);
    }
    EXPORT_SYMBOL_GPL(usb_function_unregister);
    

    示例:以acm function为例说明,如下:

    源码:drivers/usb/gadget/function/f_acm.c

    DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
    

    usb_function_instance

    源码:include/linux/usb/composite.h

    struct usb_function_instance {
    	struct config_group group;
    	struct list_head cfs_list;
    	struct usb_function_driver *fd;
    	int (*set_inst_name)(struct usb_function_instance *inst,
    			      const char *name);
    	void (*free_func_inst)(struct usb_function_instance *inst);
    };
    
    展开全文
  • 作者:刘洪涛,华清远见嵌入式学院讲师。  Linux USB 设备端驱动有两部分组成。一部分是USB 设备控制器(USB... Gadget 框架提出了一套标准 API, 在底层, USB 设备控制器驱动则实现这一套 API, 不同的 UDC需要不同的
  • 一、几个重要设备端Gadget驱动结构体: ...1. struct usb_gadget {//代表一个UDC设备   /* readonly to gadget driver */   const struct usb_gadget_ops *ops; //设备的操作集  struct usb_ep *ep0; //ep0

    一、几个重要设备端Gadget驱动结构体:

    1. struct usb_gadget {//代表一个UDC设备 
            /* readonly to gadget driver */ 
                   const struct usb_gadget_ops *ops; //设备的操作集
                   struct usb_ep *ep0; //ep0(USB协议中的端点0), 处理setup()请求
                   struct list_head ep_list; /* of usb_ep */本设备支持的端点链表
                   enum usb_device_speed speed; //如:USB_SPEED_LOW、USB_SPEED_FULL等
                   unsigned is_dualspeed:1; //支持full/high speed
                   unsigned is_otg:1; //OTG的特性
                   unsigned is_a_peripheral:1; //当前是A-peripheral,而不是A-host 
                   unsigned b_hnp_enable:1; 
                   unsigned a_hnp_support:1; 
                   unsigned a_alt_hnp_support:1; 
                   const char *name;
                   struct device dev; 
            };


    2. struct usb_gadget_driver {//代表一个gadget设备driver,如:file_storage.c中的fsg_driver
    //又如:如zero.c中的zero_driver
                   char *function; //一个字符串,如"Gadget Zero" 
                   enum usb_device_speed speed; 
                   int (*bind)(struct usb_gadget *);       //常用语将dev 与driver的绑定,类似于probe,会被底层Gadget自动调用
                   void (*unbind)(struct usb_gadget *);    //与bind作用相反
                   int (*setup)(struct usb_gadget *,    const struct usb_ctrlrequest *);    //用于usb设备setup阶段的USB_REQ_GET_DESCRIPTOR等主机端的请求处理,完成USB设置阶段和具体功能相关的交互
                   void (*disconnect)(struct usb_gadget *);
                   void (*suspend)(struct usb_gadget *); 
                   void (*resume)(struct usb_gadget *)

           /* FIXME support safe rmmod */ 
                   struct device_driver driver; 
            };

    3. struct usb_gadget_ops {//代表设备的操作集
                           int (*get_frame)(struct usb_gadget *);
                           int (*wakeup)(struct usb_gadget *);
                           int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
                           nt (*vbus_session) (struct usb_gadget *, int is_active);
                           int (*vbus_draw) (struct usb_gadget *, unsigned mA);
                           int (*pullup) (struct usb_gadget *, int is_on);
                           int (*ioctl)(struct usb_gadget *,
                           unsigned code, unsigned long param); 
            };

    4. struct usb_ep {//代表一个端点
                           void *driver_data // 
                           ...
                           const struct usb_ep_ops *ops; //端点的操作集,如上
                           struct list_head ep_list; //gadget的所有ep的list
                           ...
            };
       5.     struct usb_ep_ops {//表示端点的操作集 
                           ... 
                           int (*queue) (struct usb_ep *ep, struct usb_request *req, 
                           gfp_t gfp_flags); //将一个usb_request提交给endpoint 
                           //是数据传输的关键函数 
                           ...
            };

    6.  struct usb_request {//表示一个传输的请求,这与usb host端的urb类似
                           void *buf; 
                           unsigned length; 
                           dma_addr_t dma;
                           unsigned no_interrupt:1;
                           unsigned zero:1;
                           unsigned short_not_ok:1;
                           void (*complete)(struct usb_ep *ep,  struct usb_request *req);   //一个usb请求提交完成后的回调函数
                           void *context;
                           struct list_head list;
                           int status;
                           unsigned actual;
            };

    7.  struct usb_ctrlrequest (用在setup函数中)
    |-----------------------|
    | __u8    bRequestType -|
    | __u8    bRequest     -|
    | __le16 -wValue       -|
    | __le16 -wIndex       -|
    | __le16 -wLength      -|
    |-----------------------|

    这个数据结构就是SETUP信包的内容,而缓冲区的内容,就是随后的数据信包的内容。
    ---------------------------------------------------------------
    bRequestType
        D7     数据的传输方向:0表示从主机到设备; 1表示从设备到主机;
        D6~5   命令的类型:   0表示标准命令;    1表示类命令;      2表示厂商提供的命令; 3保留;
        D4~0   接收对象;     0表示设备;       1表示接口;       2表示端点;         3表示其他;
    bRequest
        命令的序号(其实就是命令);所有的命令都是以不同编码值的方式传递给设备的,bRequest就表示USB命令的编码值
    wValue, wIndex
        这两个字段对于不同的命令有不同的含义
    wLength
        表示在完成命令控制传输的数据阶段,要求传输数据的字节长度。一般不论是输入还是输出都要求给出准确的数字。当命令不需要传输数据时,此字段设为0

    二、Gadget层驱动提供的几个常用的函数:

    1. usb_gadget_register_driver  注册usb_gadget_driver类型的驱动

    2. usb_gadget_unregister_driver 注销usb_gadget_driver类型驱动

    3. struct usb_ep * __init usb_ep_autoconfig (
        struct usb_gadget        *gadget,
        struct usb_endpoint_descriptor    *desc
    )    用于根据端点描述符及控制器端点情况,分配一个合适的端点。

    4. static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
                                   gfp_t gfp_flags)  分配一个usb_request

    3. 通过Gadget Core 驱动,向usb_ep发送usb_request, 请求读写

    static inline int usb_ep_queue(struct usb_ep *ep,
                       struct usb_request *req, gfp_t gfp_flags);    

    三、USB gadget功能驱动

           如果内核已经支持了SOC的UDC驱动,很多时候,我们可以只关心这部分代码的编写。那么我们如何编写出一个类似usb 功能驱动呢?

           usb 功能驱动应该至少要实现如下功能:

           .       实现USB协议中端点0部分和具体功能相关的部分(UDC驱动无法帮我们完成的部分)。如:USB_REQ_GET_DESCRIPTOR、USB_REQ_GET_CONFIGURATION等;
                           完成了这个功能以后,USB主机端系统就会设别出我们是一个什么样的设备。
                   .       实现数据交互功能
                           即如何实现向硬件控制器的端点发出读、写请求来完成数据交互;
                   .       具体功能的实现如:如何实现一个usb net驱动,或是一个usb storage驱动。
    四、实例

    Linux内核2.6.28中 drivers/usb/gadget/Zero.c的源码提供了一个编写USB 设备端的驱动框架(利用Gadget层提供的函数,当然底层还需要有UDC层的支持)。

    该例子的功能是: USB 设备端提供了一个USB_DIR_IN 用于接收主机数据的端点,同时通过产生一个字符设备与之关联,对其进行读的操作:

    字符设备只定义了如下接口

    struct file_operations usb_zero_fops = {
                    .owner = THIS_MODULE,
                    .read = usb_zero_read,
                    .open = usb_zero_open,
                    .release = usb_zero_release,
            };

    完整的源代码:

    /*

    * zero.c -- Gadget Zero, for simple USB development
            * lht@farsight.com.cn
            * All rights reserved.*/
            /* #define VERBOSE_DEBUG */

    #include <linux/kernel.h>
            #include <linux/utsname.h>
            #include <linux/device.h>
            #include <linux/usb/ch9.h>
            #include <linux/usb/gadget.h>
            #include "gadget_chips.h"
            #include <linux/slab.h>
            #include <linux/module.h>
            #include <linux/init.h>
            #include <linux/usb/input.h>
            #include <linux/cdev.h>
            #include <asm/uaccess.h>
            #include <linux/fs.h>
            #include <linux/poll.h>
            #include <linux/types.h> /* size_t */
            #include <linux/errno.h> /* error codes */
            #include <asm/system.h>
            #include <asm/io.h>
            #include <linux/sched.h>

    /*-------------------------------------------------------------------------*/
            static const char shortname[] = "zero";
            static const char loopback[] = "loop input to output";
            static const char longname[] = "Gadget Zero";
            static const char source_sink[] = "source and sink data";
            #define STRING_MANUFACTURER 25
            #define STRING_PRODUCT 42
            #define STRING_SERIAL 101
            #define STRING_SOURCE_SINK 250
            #define STRING_LOOPBACK 251

    //#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */
            //#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */
            #define DRIVER_VENDOR_NUM 0x5345 /* NetChip */
            #define DRIVER_PRODUCT_NUM 0x1234 /* Linux-USB "Gadget Zero" */

    static int usb_zero_major = 251;
            /*-------------------------------------------------------------------------*/
            static const char *EP_OUT_NAME; /* sink */
            /*-------------------------------------------------------------------------*/

    /* big enough to hold our biggest descriptor */
            #define USB_BUFSIZ 256
            struct zero_dev { //zero设备结构
                        spinlock_t lock;
                        struct usb_gadget *gadget;
                        struct usb_request *req; /* for control responses */
                        struct usb_ep *out_ep;
                        struct cdev cdev;
                        unsigned char data[128];
                        unsigned int data_size;
                        wait_queue_head_t bulkrq;
            };
            #define CONFIG_LOOPBACK 2
            static struct usb_device_descriptor device_desc = { //设备描述符
                        .bLength = sizeof device_desc,
                        .bDescriptorType = USB_DT_DEVICE,
                        .bcdUSB = __constant_cpu_to_le16(0x0110),
                        .bDeviceClass = USB_CLASS_VENDOR_SPEC,
                        .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
                        .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
                        .iManufacturer = STRING_MANUFACTURER,
                        .iProduct = STRING_PRODUCT,
                        .iSerialNumber = STRING_SERIAL,
                        .bNumConfigurations = 1,
            };
            static struct usb_endpoint_descriptor fs_sink_desc = { //端点描述符
                        .bLength = USB_DT_ENDPOINT_SIZE,
                        .bDescriptorType = USB_DT_ENDPOINT,

       .bEndpointAddress = USB_DIR_OUT, //对主机端来说,输出
                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
            };

    static struct usb_config_descriptor loopback_config = { //配置描述符
                        .bLength = sizeof loopback_config,
                        .bDescriptorType = USB_DT_CONFIG,
                        /* compute wTotalLength on the fly */
                        .bNumInterfaces = 1,
                        .bConfigurationValue = CONFIG_LOOPBACK,
                        .iConfiguration = STRING_LOOPBACK,
                        .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
                        .bMaxPower = 1, /* self-powered */
            };
            static const struct usb_interface_descriptor loopback_intf = { //接口描述符
                        .bLength = sizeof loopback_intf,
                        .bDescriptorType = USB_DT_INTERFACE,

        .bNumEndpoints = 1,
                        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
                        .iInterface = STRING_LOOPBACK,
            };
            /* static strings, in UTF-8 */
            #define STRING_MANUFACTURER 25
            #define STRING_PRODUCT 42
            #define STRING_SERIAL 101
            #define STRING_SOURCE_SINK 250
            #define STRING_LOOPBACK 251
            static char manufacturer[50];
            /* default serial number takes at least two packets */
            static char serial[] = "0123456789.0123456789.0123456789";
            static struct usb_string strings[] = { //字符串描述符
                        { STRING_MANUFACTURER, manufacturer, },
                        { STRING_PRODUCT, longname, },
                        { STRING_SERIAL, serial, },
                        { STRING_LOOPBACK, loopback, },
                        { STRING_SOURCE_SINK, source_sink, },
                        { } /* end of list */
            };

    static struct usb_gadget_strings stringtab = {
                        .language = 0x0409, /* en-us */
                        .strings = strings,
            };

    static const struct usb_descriptor_header *fs_loopback_function[] = {
                        (struct usb_descriptor_header *) &loopback_intf,
                        (struct usb_descriptor_header *) &fs_sink_desc,
                        NULL,
            };

    static int
            usb_zero_open (struct inode *inode, struct file *file) //打开设备
            {
                      struct zero_dev *dev =
                        container_of (inode->i_cdev, struct zero_dev, cdev);
                        file->private_data = dev;
                      init_waitqueue_head (&dev->bulkrq);

             return 0;
            }

    static int
            usb_zero_release (struct inode *inode, struct file *file) //关闭设备
            {
                      return 0;
            }
            static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
            {
                        kfree(req->buf);
                        usb_ep_free_request(ep, req);
            }
            static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)//分配请求
            {
                        struct usb_request *req;

                req = usb_ep_alloc_request(ep, GFP_ATOMIC);
                        if (req) {
                                    req->length = length;
                                    req->buf = kmalloc(length, GFP_ATOMIC);
                                    if (!req->buf) {
                                            usb_ep_free_request(ep, req);
                                            req = NULL;
                                    }
                        }
                        return req;
            }
            static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)//请求完成函数
            {
                        struct zero_dev *dev = ep->driver_data;
                        int status = req->status;
                        switch (status) {
                        case 0: /* normal completion */
                                   if (ep == dev->out_ep) {
                                            memcpy(dev->data, req->buf, req-> actual);//返回数据拷贝到req->buf中,                                                                                                     //dev->data_size=req->length;
                                            dev->data_size=req->actual; //实际长度为req-> actual;需要确认
            req –>short_not_ok为0。参考gadget.h中关于usb_request结构的注释
                                    }
                                    break;
                         /* this endpoint is normally active while we're configured */
                        case -ECONNABORTED: /* hardware forced ep reset */
                        case -ECONNRESET: /* request dequeued */
                        case -ESHUTDOWN: /* disconnect from host */
                                   printk("%s gone (%d), %d/%d\n", ep->name, status,
                                                      req->actual, req->length);
                        case -EOVERFLOW: /* buffer overrun on read means that
                                                                  * we didn't provide a big enough
                                                                  * buffer.
                                                                  */
                        default:
            #if 1
                                   printk("%s complete --> %d, %d/%d\n", ep->name,
                                                      status, req->actual, req->length);
            #endif
                        case -EREMOTEIO: /* short read */
                                   break;
                        }
                        free_ep_req(ep, req);
                        wake_up_interruptible (&dev->bulkrq); //唤醒读函数
            }

    static struct usb_request *source_sink_start_ep(struct usb_ep *ep)//构造并发送读请求
            {
                        struct usb_request *req;
                        int status;
                        //printk("in %s\n",__FUNCTION__);
                        req = alloc_ep_req(ep, 128);
                        if (!req)
                                   return NULL;
                        memset(req->buf, 0, req->length);
                        req->complete = source_sink_complete; //请求完成函数
                        status = usb_ep_queue(ep, req, GFP_ATOMIC); //递交请求
                        if (status) {
                                 struct zero_dev *dev = ep->driver_data;
                                 printk("start %s --> %d\n", ep->name, status);
                                 free_ep_req(ep, req);
                                 req = NULL;
                        }
                        return req;
            }
            ssize_t
            usb_zero_read (struct file * file, const char __user * buf, size_t count,loff_t * f_pos) //读设备
            {
                        struct zero_dev *dev =file->private_data;
                        struct usb_request *req;
                        int status;
                        struct usb_ep *ep;
                        struct usb_gadget *gadget = dev->gadget;
                        ssize_t ret = 0;
                        int result;
                        ep=dev->out_ep;
                        source_sink_start_ep(ep);//构造、递交读请求
                        if (count < 0)
                                 return -EINVAL;
                        interruptible_sleep_on (&dev->bulkrq);//睡眠,等到请求完成
                        if (copy_to_user (buf,dev->data,dev->data_size)) //拷贝读取的数据到用户空间
                        {
                             ret = -EFAULT;
                        }
                        else
                        {
                            ret = dev->data_size;
                        }
                        return ret;
            }

    struct file_operations usb_zero_fops = {
                    .owner = THIS_MODULE,
                    .read = usb_zero_read,
                    .open = usb_zero_open,
                    .release = usb_zero_release,
            };

    static void
            usb_zero_setup_cdev (struct zero_dev *dev, int minor)//注册字符设备驱动
            {
                    int err, devno = MKDEV (usb_zero_major, minor);

         cdev_init(&dev->cdev, &usb_zero_fops);
                    dev->cdev.owner = THIS_MODULE;
                    err = cdev_add (&dev->cdev, devno, 1);
                    if (err)
                       printk ("Error adding usb_rcv\n");
            }

    static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req)//配置端点0的请求
    完成处理
            {
                       if (req->status || req->actual != req->length)
                          printk("setup complete --> %d, %d/%d\n",
                                          req->status, req->actual, req->length);
            }
            static void zero_reset_config(struct zero_dev *dev) //复位配置
            {
                          usb_ep_disable(dev->out_ep);
                          dev->out_ep = NULL;
            }
            static void zero_disconnect(struct usb_gadget *gadget)//卸载驱动时被调用,做一些注销工作
            {
                       struct zero_dev *dev = get_gadget_data(gadget);
                       unsigned long flags;
                       unregister_chrdev_region (MKDEV (usb_zero_major, 0), 1);
                       cdev_del (&(dev->cdev));
                       zero_reset_config(dev);
                       printk("in %s\n",__FUNCTION__);
            }

    static int config_buf(struct usb_gadget *gadget,
                          u8 *buf, u8 type, unsigned index)
            {
                       //int is_source_sink;
                       int len;
                       const struct usb_descriptor_header **function;
                       int hs = 0;
                       function =fs_loopback_function;//根据fs_loopback_function,得到长度,
                                                             //此处len=配置(9)+1个接口(9)+1个端点(7)=25
                       len = usb_gadget_config_buf(&loopback_config,
                                          buf, USB_BUFSIZ, function);
                       if (len < 0)
                                          return len;
                       ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
                       return len;
            }

    static int set_loopback_config(struct zero_dev *dev)
            {
                    int result = 0;
                    struct usb_ep *ep;
                    struct usb_gadget *gadget = dev->gadget;
                    ep=dev->out_ep;
                    const struct usb_endpoint_descriptor *d;
                    d = &fs_sink_desc;
                    result = usb_ep_enable(ep, d); //激活端点
                    //printk("");
                    if (result == 0) {
                                    printk("connected\n"); //如果成功,打印“connected”
                    }
                    else
                                    printk("can't enable %s, result %d\n", ep->name, result);
                    return result;
            }
            static int zero_set_config(struct zero_dev *dev, unsigned number)
            {
                    int result = 0;
                    struct usb_gadget *gadget = dev->gadget;
                    result = set_loopback_config(dev);//激活设备
                    if (result)
                            zero_reset_config(dev); //复位设备
                    else {
                            char *speed;

    switch (gadget->speed) {
                            case USB_SPEED_LOW: speed = "low"; break;
                            case USB_SPEED_FULL: speed = "full"; break;
                            case USB_SPEED_HIGH: speed = "high"; break;
                            default: speed = " "; break;
                            }
                    }
                    return result;
            }
            /***
            zero_setup完成USB设置阶段和具体功能相关的交互部分
            ***/
            static int
            zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
            {
                    struct zero_dev *dev = get_gadget_data(gadget);
                    struct usb_request *req = dev->req;
                    int value = -EOPNOTSUPP;
                    u16 w_index = le16_to_cpu(ctrl->wIndex);
                    u16 w_value = le16_to_cpu(ctrl->wValue);
                    u16 w_length = le16_to_cpu(ctrl->wLength);

    /* usually this stores reply data in the pre-allocated ep0 buffer,
                       * but config change events will reconfigure hardware.
                       */
                    req->zero = 0;

    switch (ctrl->bRequest) {
                    case USB_REQ_GET_DESCRIPTOR: //获取描述符
                            if (ctrl->bRequestType != USB_DIR_IN)
                                   goto unknown;
                            switch (w_value >> 8) {
                            case USB_DT_DEVICE: //获取设备描述符
                                    value = min(w_length, (u16) sizeof device_desc);
                                    memcpy(req->buf, &device_desc, value);
                                    break;
                            case USB_DT_CONFIG: //获取配置,注意:会根据fs_loopback_function读取到接口、端点描述符,注意通过config_buf完成读取数据及数量的统计。
                                    value = config_buf(gadget, req->buf,
                                                    w_value >> 8,
                                                    w_value & 0xff);
                                    if (value >= 0)
                                            value = min(w_length, (u16) value);
                                    break;

    case USB_DT_STRING:
                                    value = usb_gadget_get_string(&stringtab,
                                                    w_value & 0xff, req->buf);
                                    if (value >= 0)
                                            value = min(w_length, (u16) value);
                                    break;
                            }
                            break;

    case USB_REQ_SET_CONFIGURATION:
                            if (ctrl->bRequestType != 0)
                                    goto unknown;
                            spin_lock(&dev->lock);
                            value = zero_set_config(dev, w_value);//激活相应的端点
                            spin_unlock(&dev->lock);
                            break;

    default:
            unknown:
                            printk(
                                    "unknown control req%02x.%02x v%04x i%04x l%d\n",
                                    ctrl->bRequestType, ctrl->bRequest,
                                    w_value, w_index, w_length);
                      }
                      /* respond with data transfer before status phase */
                      if (value >= 0) {
                            req->length = value;
                            req->zero = value < w_length;
                            value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);//通过端点0完成setup
                            if (value < 0) {
                                           printk("ep_queue --> %d\n", value);
                                           req->status = 0;
                                           zero_setup_complete(gadget->ep0, req);
                            }
                      }
                      /* device either stalls (value < 0) or reports success */
                      return value;
            }
            static void zero_unbind(struct usb_gadget *gadget) //解除绑定
            {
                    struct zero_dev *dev = get_gadget_data(gadget);

    printk("unbind\n");
                    unregister_chrdev_region (MKDEV (usb_zero_major, 0), 1);
                    cdev_del (&(dev->cdev));
                    /* we've already been disconnected ... no i/o is active */
                    if (dev->req) {
                            dev->req->length = USB_BUFSIZ;
                            free_ep_req(gadget->ep0, dev->req);
                    }
                    kfree(dev);
                    set_gadget_data(gadget, NULL);
            }
            static int __init zero_bind(struct usb_gadget *gadget) //绑定过程
            {
                    struct zero_dev *dev;
                    struct usb_ep *ep;
                    int gcnum;
                    usb_ep_autoconfig_reset(gadget);
                    ep = usb_ep_autoconfig(gadget, &fs_sink_desc);//根据端点描述符及控制器端点情况,分配一个合适的端点。
                    if (!ep)
                            goto enomem;
                    EP_OUT_NAME = ep->name; //记录名称
                    gcnum = usb_gadget_controller_number(gadget);//获得控制器代号
                    if (gcnum >= 0)
                            device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);//赋值设备描述符
                    else {
                            pr_warning("%s: controller '%s' not recognized\n",
                                  shortname, gadget->name);
                            device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
            }
            dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配设备结构体
            if (!dev)
                    return -ENOMEM;
            spin_lock_init(&dev->lock);
            dev->gadget = gadget;
            set_gadget_data(gadget, dev);
            dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);//分配一个请求
            if (!dev->req)
                    goto enomem;
            dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
            if (!dev->req->buf)
                    goto enomem;
            dev->req->complete = zero_setup_complete;
            dev->out_ep=ep; //记录端点(就是接收host端数据的端点)
            printk("name=%s\n",dev->out_ep->name); //打印出这个端点的名称
            ep->driver_data=dev;
            device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
            usb_gadget_set_selfpowered(gadget);
            gadget->ep0->driver_data = dev;
            snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
                    init_utsname()->sysname, init_utsname()->release,
            gadget->name);
    /**************************字符设备注册*******************/
            dev_t usb_zero_dev = MKDEV (usb_zero_major, 0);
            int result = register_chrdev_region (usb_zero_dev, 1, "usb_zero");
            if (result < 0)
            {
                    printk (KERN_NOTICE "Unable to get usb_transfer region, error %d\n",result);
                    return 0;
            }
            usb_zero_setup_cdev (dev, 0);
            return 0;
        enomem:
            zero_unbind(gadget);
            return -ENOMEM;
        }
    /*-------------------------------------------------------------------------*/
            static struct usb_gadget_driver zero_driver = { //gadget驱动的核心数据结构
            #ifdef CONFIG_USB_GADGET_DUALSPEED
                            .speed = USB_SPEED_HIGH,
            #else
                            .speed = USB_SPEED_FULL,
            #endif
                            .function = (char *) longname,
                            .bind = zero_bind,
                            .unbind = __exit_p(zero_unbind),
                            .setup = zero_setup,
                            .disconnect = zero_disconnect,
                            //.suspend = zero_suspend, //不考虑电源管理的功能
                            //.resume = zero_resume,
                            .driver = {
                                    .name = (char *) shortname,
                                    .owner = THIS_MODULE,
                            },
            };
            MODULE_AUTHOR("David Brownell");
            MODULE_LICENSE("GPL");
            static int __init init(void)
            {
                    return usb_gadget_register_driver(&zero_driver); //注册驱动,调用bind绑定到控制器
            }
            module_init(init);

    static void __exit cleanup(void)
            {
                    usb_gadget_unregister_driver(&zero_driver); //注销驱动,通常会调用到unbind解除绑定, //在s3c2410_udc.c中调用的是disconnect方法
            }
            module_exit(cleanup);

    展开全文
  • gadgetusb_gadget

    2021-03-17 22:27:13
    gadgetusb_gadget 简介 略。 struct usb_gadget /** * struct usb_gadget - represents a usb slave device * @work: (internal use) Workqueue to be used for sysfs_notify() * @udc: struct usb_udc pointer...
  • reference: ... there are TWO kind of gadgetconfiguration 1)USB Gadget functions configurable through configfs(config it in userspace) ...2)USB Gadget precomposed configurations(config it in...
  • 是Linux USB Gadget子系统的底层接口。它可以用于模拟具有物理USB设备,也可以用于与虚拟的设备(对于正在运行的内核)。该存储库包含有关使用Raw Gadget的说明和。 Raw Gadget已在5.7 到主线Linux内核中。有没有...
  • linux usb gadget驱动详解(五)

    千次阅读 2020-07-04 12:46:40
    现从fsg_bind()讲起。 //不失一般性,删掉错误处理和configfs相关代码 static int fsg_bind(struct usb_configuration *c, struct usb_function *f) ... struct usb_gadget *gadget = c->cdev-...
  • 一、USB从设备驱动介绍USB总线上主要有三类设备:主控制器(Host Controller, such asEHCI、UHCI、OHCI)、集线器(hub)和设备(device)。Host controller(HC)负责总线的管理,是总线的指挥官,总线上一切传输都是由HC...
  • 利用Linux USB gadget设备驱动可以实现一些比较有意思的功能,举两个例子: 1、一个嵌入式产品中的某个存储设备,或是一个存储设备的某个分区,可以作为一个U盘被PC;设别,从而非常方便的完成文件交互,这个功能被...
  • Linux usb gadget 驱动

    2010-02-26 10:06:24
    Linux usb gadget 驱动 刘洪涛老师的讲稿,详细整理过的资源。转换为pdf版本。 linux从设备驱动开发的参考资源不多,这是非常好的参考资料。
  • USB Gadget软件结构总共分为三层,其软件架构图如下 一. UDC层  这一层是与硬件相关层。相关文件ambarella_udc.c ambarella_udc.h。ambarella设备控制器作为一个linux设备在这一层是作为platform...
  • 在linux gadget zero驱动的基础上实现了文件接口,阻塞读写,设备打开数量限制等功能,支持直接通过cat和echo的重定向机制操作此驱动,实现usb gadget驱动的读写,在linux3,3通过测试。 使用方法,将这些文件拷贝到...
  • Building Gadget USB Module

    2017-03-17 16:34:11
    建立Gadget USB模块 1、主要注释  该指南目前适用于基于Debian的发行版。随时为他人添加说明。你很可能需要内核版本2.6.20或更高版本。 2、Debian GNU / Linux Lenny和UbuntuFeisty Fawn 7.04 / Hardy 8.04  要...
  • 16.4.1 UDC(USB设备控制器)和Gadget(小配件)驱动的关键数据结构与API USB设备控制器(UDC)驱动指的是作为其他USB主机控制器外设的USB硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个USB设备依附于一...
  • linux usb gadget驱动详解(一)

    千次阅读 2019-07-28 12:39:33
    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网、PCIE总线和RS232串口等。这里我们主要讨论USBUSB是一个主从通信架构,但只能一主多从。其中usb主机控制器有ECHI...
  • linux之usb gadget详解

    2019-10-22 11:13:07
    待写
  • 这个程序考虑到了多配置、高速传输、USB OTG等因素。应该说写的比较清楚,是我们了解gadget驱动架构的一个非常好的途径。但把这些东西都放在一起,对很多初学人员来说还是不能快速理解。那就再把它简化一些,针对S3C...
  • Linux USB Gadget--分析

    千次阅读 2018-03-05 14:37:33
    Linux USB Gadget软件结构一文中分析Linux USB Gadget软件分为三层。这三层其中两层是与硬件无关的,分别是Gadget功能驱动层,USB设备层。一层是与硬件相关的是UDC层。每一层都提供一种关键的数据结构与函数与其他层...
  • Linux_USB_gadget设备驱动Linux usb gadget 驱动利用 Linux USB gadget 设备驱动可以实现一些比较有意思的功能,举两个例子: 1、一个嵌入式产品中的某个存储设备,或是一个存储设备的某个分区,可以作为一个 U盘被 ...
  • 本文分析的是linux-5.4.3一.Gadget Audio设备驱动分析drivers/usb/gadget/legacy/audio.c因为项目的问题,了解usb音频设备的工作原理,为啥它能让PC识别成“speak”或者“mic”,以及你能够播放录音。主要涉及下面两...
  • 问题描述: gadget驱动加载时有一传参removable,当removable=1时作为移动设备,U盘可以被windows、linux系统电脑识别;当removable=0时,windows系统下不能识别为U盘,...在内核 drivers/usb/gadget/f_mass_stora
  • 在内核中使能Mass Storage Gadget ,如下图,配置路径是...→ Device Drivers → USB support → USB Gadget Support -&gt;Mass Storage Gadget 使能Pseudo文件系统,如图 配置Device Tree &amp;dwc3_0 {...
  • USB Gadget Tool需要root权限和具有ConfigFS支持的内核。目前,该应用仅启用USB小工具。为了使用这些设备端点(例如/ dev / hidg0),需要其他应用程序(请参阅用例)。 用例 路线图 挂载/ config(如果
  • usb gadget g_webcam uvc gadget调试

    千次阅读 2017-07-20 15:30:30
    调试 usb gadget webcam时的记录: 1. 加载webcam驱动 2. 从http://git.ideasonboard.org/uvc-gadget.git下载 uvc应用层的代码,编译 3. 执行uvc_gadget时发现一直提示 iotrl 错误,原来使用的交叉编译器中的内核...
  • USB Gadget Storage功能调试

    千次阅读 2018-09-11 10:34:39
    由于工作的需要,实现板卡通过Micro USB线与PC连接,作为PC的 外设存储,PC拷贝数据到板卡中,或者把板卡中的数据通过USB线拷贝到PC端,实现数据的交互,板卡采用Linux操作系统,笔者采用的是AM335X 处理器平台: ...
  • Linux集成下USB GadgetUSB虚拟网口设计.pdf
  • 上个小patch吧... 关于昨天的usb枚举失败(获取配置描述符失败) ...1. 我的gadget配置成了usb3.2版本, (设置成1.0, 2.0也遇到一些问题, 暂表不论) Protocols Supported: USB 1.1: no USB 2.0: no ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,088
精华内容 2,035
关键字:

gadgetusb