Linux usb gadget框架概述

2023-11-08

大大小小开发了四个与gadget相关的驱动,字符驱动、g_multi、g_ether、g_zero,在这里把自己对gadget的开发中自己的感悟记录之。

 

想要了解gadget,必须了解其框架,知道composite、gadget、udc三者之间的联系,知道usb描述符的作用。

一个usb device有一个设备描述符。

有一个或者多个配置描述符

一个配置描述符有一个或者多个接口(在gadget端,接口正式命名是usb_func)。

一个接口有0个或者多个端点。

 

编写gadget的关键是在于了解udc、gadget、composite三者之间的联系和架构层次,在实际应用中gadget是不需要我们去编写的,需要我们自己去编写的是composite层,以及地对udc层的修改,下面开始详细介绍着三者。

1、composite英文意思是复合的意思,估计是编写usb gadget层设备驱动都整合到一起,通过统一的函数usb_composite_register注册。功能各异,杂七杂八,所以称为复合层吧。

在该层,我们需要注意的相关结构体和函数有如下:

 

struct usb_composite_dev { //作为composite复合设备,所有composite设备都必须实现该设备。
    struct usb_gadget        *gadget; //设备和gadget交互,gadget和udc交互。
    struct usb_request        *req;   //每个设备自带一个usb请求,所有的数据交互都是通过该请求发送的。

    struct usb_configuration    *config; 一个设备有一个或者多个配置。

    /* private: */
    /* internals */
    unsigned int            suspended:1;
    struct usb_device_descriptor    desc;  //设备描述符,唯一
    struct list_head        configs; //配置
    struct list_head        gstrings; //字符描述
    struct usb_composite_driver    *driver; //设备绑定的驱动
    u8                next_string_id;  
    char                *def_manufacturer;  //默认制造商
    /* the gadget driver won't enable the data pullup
     * while the deactivation count is nonzero.
     */
    unsigned            deactivations;

    /* the composite driver won't complete the control transfer's
     * data/status stages till delayed_status is zero.
     */
    int                delayed_status;

    /* protects deactivations and delayed_status counts*/
    spinlock_t            lock;
};
307 struct usb_composite_driver {  //所有compesite驱动必须填充该结构体。
308     const char              *name;
309     const struct usb_device_descriptor  *dev; //必须实现
310     struct usb_gadget_strings       **strings;
311     enum usb_device_speed           max_speed;
312     unsigned        needs_serial:1;
313    
314     int         (*bind)(struct usb_composite_dev *cdev); //必须实现的
315     int         (*unbind)(struct usb_composite_dev *); //必须实现
316    
317     void            (*disconnect)(struct usb_composite_dev *);
318    
319     /* global suspend hooks */
320     void            (*suspend)(struct usb_composite_dev *);
321     void            (*resume)(struct usb_composite_dev *);
322     struct usb_gadget_driver        gadget_driver; //这个地方的驱动由composite提供,所有和composite相关的驱动都会默认分配该驱动。该驱动是
323 };
 
 852 struct usb_gadget_driver {  //该驱动是usbcore和composite之间交互必不可少的一环,两者之间的联系主要靠他来维持,内核已经提供好了,不需要我们去实现。
 853     char            *function;
 854     enum usb_device_speed   max_speed;
 855     int         (*bind)(struct usb_gadget *gadget,
 856                     struct usb_gadget_driver *driver);
 857     void            (*unbind)(struct usb_gadget *);
 858     int         (*setup)(struct usb_gadget *,    //枚举过程中必不可少的函数。不需要驱动去实现。
 859                     const struct usb_ctrlrequest *);
 860     void            (*disconnect)(struct usb_gadget *);
 861     void            (*suspend)(struct usb_gadget *);
 862     void            (*resume)(struct usb_gadget *);
 863    
 864     /* FIXME support safe rmmod */
 865     struct device_driver    driver;
 866 }; 
 

 

 

1772 static const struct usb_gadget_driver composite_driver_template = { //所有的composite设备都会在注册gadet驱动的时候采用该实例填充。
                                                                         //笔者认为这么做的原因是gadget驱动永远只有一个,composite可以随便实现。体现分层的思想。
1773     .bind       = composite_bind,      
1774     .unbind     = composite_unbind,    
1775                                        
1776     .setup      = composite_setup,     
1777     .disconnect = composite_disconnect,                                                                                                                           
1778                                        
1779     .suspend    = composite_suspend,   
1780     .resume     = composite_resume,    
1781                                        
1782     .driver = {                        
1783         .owner      = THIS_MODULE,     
1784     },                                 
1785 }; 

 

 

下面首先介绍composite驱动的注册过程,讲完后介绍驱动的编写过程。

以zero.c为例:                                                                                                                    

  1.  

    static int __init init(void)  
    {  
        return usb_composite_register(&zero_driver);  
    }
    usb_composite_register(&zero_driver);
    1========》 driver->gadget_driver = composite_driver_template; //此过程并未涉及到对compoite设备的注册的操作,
                                                            //而是将composite驱动中注册的相关信息填充到gadget中,利用gadget去和udc打交道
         ---->return usb_gadget_probe_driver(gadget_driver);  该函数首先判定bind setup等函数是否实现了。不需要我们去实现。
    
         ---->  list_for_each_entry(udc, &udc_list, list) //查找注册在内核中的udc实例,找到了进行下一步操作,没找到退出。驱动注册失败。
    
         ---->  ret = udc_bind_to_driver(udc, driver);  //将udc和gadget驱动绑定在一起。
    
    
    
    2======》 udc_bind_to_driver(udc, driver);
    
         ---->404     ret = driver->bind(udc->gadget, driver);//最关键的莫过于该函数了,最初笔者分析的时候,以为是composite的bind函数,后来才弄清楚是gadget层
    
                                                              //的bind函数composite_bind ,将在后面介绍。
    
        -----> ret = usb_gadget_udc_start(udc->gadget, driver); //此处可以理解为一切就绪,udc相关设置已经写入寄存器。
    
        -----> ret = usb_gadget_connect(udc->gadget); //插入host usb口,检查D+电平的变化。也就是枚举过冲
    
    3=====》 composite_bind //最重要的函数了。是理解gadget设计的关键

     

     

     

     

    1672 static int composite_bind(struct usb_gadget *gadget,//该函数zhu要是实现配置描述符接口等操作。
    1673         struct usb_gadget_driver *gdriver)
    1674 {           
    1675     struct usb_composite_dev    *cdev;
    1676     struct usb_composite_driver *composite = to_cdriver(gdriver);                                                                                                 
    1677     int             status = -ENOMEM;
    1678             
    1679     cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
    1680     if (!cdev)
    1681         return status;
    1682             
    1683     spin_lock_init(&cdev->lock);
    1684     cdev->gadget = gadget;
    1685     set_gadget_data(gadget, cdev);
    1686     INIT_LIST_HEAD(&cdev->configs);
    1687     INIT_LIST_HEAD(&cdev->gstrings);
    1688             
    1689     status = composite_dev_prepare(composite, cdev);
    1690     if (status)
    1691         goto fail;
    1692             
    1693     /* composite gadget needs to assign strings for whole device (like
    1694     ┊* serial number), register function drivers, potentially update
    1695     ┊* power state and consumption, etc
    1696     ┊*/     
    1697     /*此处才是开始调用驱动的bind函数*/
    1698     status = composite->bind(cdev);
    1699     if (status < 0)
    1700         goto fail;
    1701             
    1702     update_unchanged_dev_desc(&cdev->desc, composite->dev);
    1703             
    1704     /* has userspace failed to provide a serial number? */
    1705     if (composite->needs_serial && !cdev->desc.iSerialNumber)
    1706         WARNING(cdev, "userspace failed to provide iSerialNumber\n");
    1707             
    1708     INFO(cdev, "%s ready\n", composite->name);
    1709     return 0;
    1710             
    1711 fail:       
    1712     __composite_unbind(gadget, false);
    1713     return status;
    }
    1====》  composite_bind
        ---->1676  struct usb_composite_driver *composite = to_cdriver(gdriver);  //这个函数就是将gadet转换为composite的关键。在gadget驱动注册时联系在一起。
                                                                                  //return container_of(gdrv, struct usb_composite_driver, gadget_driver);  
        ---->cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //实现cdev设备。
        ---->set_gadget_data(gadget, cdev); //填充私有数据,以便内核中可以通过gadget获取cdev.
        ---->INIT_LIST_HEAD(&cdev->configs); //初始化配置描述链表,在开头本人介绍过,一个设备有一个或者多种配置。
        ---->1689     status = composite_dev_prepare(composite, cdev);  //这个函数十分重要,包括usb_request、complete(回调函数实现)、设备和驱动的绑定等)
        ---->1698 status = composite->bind(cdev) //此处才是开始调用驱动的bind函数,后面会详细介绍该函数。
        ----> INFO(cdev, "%s ready\n", composite->name); //至此,composite设备创建成功。

     

     

     

    1610 int composite_dev_prepare(struct usb_composite_driver *composite,  //该函数主要是实现了至关重要的usb_request,针对端点0,即控制端点
    1611         struct usb_composite_dev *cdev)                                                                                                                           
    1612 {   
    1613     struct usb_gadget *gadget = cdev->gadget;
    1614     int ret = -ENOMEM;
    1615     
    1616     /* preallocate control response and buffer */
    1617     cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); //申请控制端点usb请求
    1618     if (!cdev->req)
    1619         return -ENOMEM;
    1620     
    1621     cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL); //请求发送内容将保持在此
    1622     if (!cdev->req->buf)
    1623         goto fail;
    1624     
    1625     ret = device_create_file(&gadget->dev, &dev_attr_suspended);//创建设备文件,位于/sys/class/dev目录下
    1626     if (ret)
    1627         goto fail_dev;
    1628     
    1629     cdev->req->complete = composite_setup_complete;   //回调函数。
    1630     gadget->ep0->driver_data = cdev; //通过端点即可获取设备。
    1631     
    1632     cdev->driver = composite; //将composite设备和驱动绑定在一起,所以usb gadget端是没有枚举过程的,驱动直接注册成功,创建设备。
    1633     
    1634     /*
    1635     ┊* As per USB compliance update, a device that is actively drawing
    1636     ┊* more than 100mA from USB must report itself as bus-powered in
    1637     ┊* the GetStatus(DEVICE) call.
    1638     ┊*/
    1639     if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
    1640         usb_gadget_set_selfpowered(gadget);
    1641     
    1642     /* interface and string IDs start at zero via kzalloc.
    1643     ┊* we force endpoints to start unassigned; few controller
    1644     ┊* drivers will zero ep->driver_data.
    1645     ┊*/
    1646     usb_ep_autoconfig_reset(gadget);  //
    1647     return 0;
    1648 fail_dev
      
    1649 kfree(cdev->req->buf);
    
    1650 fail:
    /* 此处知识初始化了usb配置链表,并未实例化接口和端点,所以将端点driver_data又重设为空,批量输入和输出点号为0;
    
    1642 /* interface and string IDs start at zero via kzalloc.
    1643 ┊* we force endpoints to start unassigned; few controller
    1644 ┊* drivers will zero ep->driver_data.
    1645 ┊*/ 
    1646 usb_ep_autoconfig_reset(gadget);
    
    */  
    
    1651 usb_ep_free_request(gadget->ep0, cdev->req);  
      1652 cdev->req = NULL;
      1653 return ret;
      1654 }

     


    下面即将进行到至关重要的一环,调用composite驱动的bind函数。

    1.  

       

       

      275 static int __init zero_bind(struct usb_composite_dev *cdev) //里面是实现composite设备的关键,设计到strings,配置描述符,接口(function)、端点的实例化。
      /276 {                  
      277     struct f_ss_opts    *ss_opts;
      278     struct f_lb_opts    *lb_opts;
      279     int         status;
      280                    
      281     /* Allocate string descriptor numbers ... note that string
      282     ┊* contents can be overridden by the composite_dev glue.
      283     ┊*/            
      284     status = usb_string_ids_tab(cdev, strings_dev);
      285     if (status < 0)
      286         return status;
      287                    
      288     device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;//制造商
      289     device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;//产品Id
      290     device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;//设备序列号功能索引,个人认为是针对多function设备而言的。
      291                    
      292     setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
      293                    
      294     func_inst_ss = usb_get_function_instance("SourceSink"); //获取function,此处的实现十分巧妙,通过usb_function_register实现。
      295     if (IS_ERR(func_inst_ss))
      296         return PTR_ERR(func_inst_ss);
      297                    
      298     ss_opts =  container_of(func_inst_ss, struct f_ss_opts, func_inst);
      299     ss_opts->pattern = gzero_options.pattern;
      300     ss_opts->isoc_interval = gzero_options.isoc_interval;
      301     ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
      302     ss_opts->isoc_mult = gzero_options.isoc_mult;
      303     ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
      304     ss_opts->bulk_buflen = gzero_options.bulk_buflen; //每次传送的buf大小
      305              
      306     func_ss = usb_get_function(func_inst_ss); //获取source_link实例,同样通过usb_function_register注册获取。
      307     if (IS_ERR(func_ss)) {
      308         status = PTR_ERR(func_ss);
      309         goto err_put_func_inst_ss;
      310     }              
      311     
      312     func_inst_lb = usb_get_function_instance("Loopback");
      313     if (IS_ERR(func_inst_lb)) {
      314         status = PTR_ERR(func_inst_lb);
      315         goto err_put_func_ss;
      316     }             
      317     
      318     lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst);
      319     lb_opts->bulk_buflen = gzero_options.bulk_buflen; //在usb_function_registe注册时就已经分配了。
      320     lb_opts->qlen = gzero_options.qlen;
      321     
      322     func_lb = usb_get_function(func_inst_lb);
      323     if (IS_ERR(func_lb)) {
      324         status = PTR_ERR(func_lb);
      325         goto err_put_func_inst_lb;
      326     }             
      327     
      328     sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id;//设置配置描述符索引
      329     loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id;
      330     
      331     /* support autoresume for remote wakeup testing */
      332     sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
      333     loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
      334     sourcesink_driver.descriptors = NULL;
      335     loopback_driver.descriptors = NULL;
      336     if (autoresume) {
      337         sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
      338         loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
      339         autoresume_step_ms = autoresume * 1000;
      340     }             
      341     
      342     /* support OTG systems */
      343     if (gadget_is_otg(cdev->gadget)) {
      344         sourcesink_driver.descriptors = otg_desc;
      345         sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
      346         loopback_driver.descriptors = otg_desc;
      347         loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
      348     }
      349                    
      350     /* Register primary, then secondary configuration.  Note that
      351     ┊* SH3 only allows one config...
      352     ┊*/            
      353     if (loopdefault) {//若只支持loopback即回环模式。
      354         usb_add_config_only(cdev, &loopback_driver);//则loopback配置先注册
      355         usb_add_config_only(cdev, &sourcesink_driver);//后注册
      356     } else {       
      357         usb_add_config_only(cdev, &sourcesink_driver);//同上
      358         usb_add_config_only(cdev, &loopback_driver);
      359     }              
      360     status = usb_add_function(&sourcesink_driver, func_ss);//将功能即接口绑定到配置描述符,此处还有一次bind操作,隐藏的极深。
      361     if (status)    
      362         goto err_conf_flb;
      363                    
      364     usb_ep_autoconfig_reset(cdev->gadget);//重新设置ep
      365     status = usb_add_function(&loopback_driver, func_lb);
      366     if (status)    
      367         goto err_conf_flb;
      368                    
      369     usb_ep_autoconfig_reset(cdev->gadget);
      370     usb_composite_overwrite_options(cdev, &coverwrite);//支持传参。修改iverdor iproduct等。
      371                    
      372     INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
      373                    
      374     return 0;      
      375                    
      376 err_conf_flb:      
      377     usb_put_function(func_lb);
      378     func_lb = NULL;
      379 err_put_func_inst_lb:
      380     usb_put_function_instance(func_inst_lb);
      381     func_inst_lb = NULL;
      382 err_put_func_ss:   
      383     usb_put_function(func_ss);
      384     func_ss = NULL;
      385 err_put_func_inst_ss:
      386     usb_put_function_instance(func_inst_ss);
      387     func_inst_ss = NULL;                                                                                                                                           
      388     return status; 
      389 } 

       

      189 int usb_add_function(struct usb_configuration *config, //配置中实例化接口。
       190         struct usb_function *function)
       191 {  
       192     int value = -EINVAL;
       193    
       194     DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
       195             function->name, function,
       196             config->label, config);
       197    
       198     if (!function->set_alt || !function->disable)//接口是否设置了set_alt函数,该函数调用表示当前接口可用,其他接口不可用。
       199         goto done;
       200    
       201     function->config = config;
       202     list_add_tail(&function->list, &config->functions);
       203    
       204     /* REVISIT *require* function->bind? */
         205 if (function->bind) {
        206 value = function->bind(config, function);//对于一个配置多个接口的cdev设备,再次对function进行bin操作。
         207 if (value < 0) {
         208 list_del(&function->list);
         209 function->config = NULL;
         210 }
         211 } else
         212 value = 0;
         213 
         214 /* We allow configurations that don't work at both speeds.
         215 ┊* If we run into a lowspeed Linux system, treat it the same
         216 ┊* as full speed ... it's the function drivers that will need
         217 ┊* to avoid bulk and ISO transfers.
         218 ┊*/
         219 if (!config->fullspeed && function->fs_descriptors)
         220 config->fullspeed = true;
         221 if (!config->highspeed && function->hs_descriptors)
         222 config->highspeed = true;
         223 if (!config->superspeed && function->ss_descriptors)
         224 config->superspeed = true;
         225 
         226 done:
         227 if (value)
         228 DBG(config->cdev, "adding '%s'/%p --> %d\n",
         229 function->name, function, value);
         230 return value; 
         231 }

       

      以f_loopback.c中的bind为例。

  2.  

    175 static int loopback_bind(struct usb_configuration *c, struct usb_function *f) //将配置和功能绑定在一起
    176 {                   
    177     struct usb_composite_dev *cdev = c->cdev;
    178     struct f_loopback   *loop = func_to_loop(f);
    179     int         id; 
    180     int ret;        
    181                     
    182     /* allocate interface ID(s) */
    183     id = usb_interface_id(c, f);//一般从0开始配置。分配接口id号
    184     if (id < 0)     
    185         return id;  
    186     loopback_intf.bInterfaceNumber = id;
    187                     
    188     id = usb_string_id(cdev);//获取字符描述符索引
    189     if (id < 0)     
    190         return id;  
    191     strings_loopback[0].id = id;
    192     loopback_intf.iInterface = id;
    193                      
    194     /* allocate endpoints */
    195                      
    196     loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);//分配批量输入端点
    197     if (!loop->in_ep) {
    198 autoconf_fail:       
    199         ERROR(cdev, "%s: can't autoconfigure on %s\n",
    200             f->name, cdev->gadget->name);
    201         return -ENODEV;
    202     }                
    203     loop->in_ep->driver_data = cdev;    /* claim */
    204                      
    205     loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);//分配批量输出端点
    206     if (!loop->out_ep)
    207         goto autoconf_fail;
    208     loop->out_ep->driver_data = cdev;   /* claim */
    209                      
    210     /* support high speed hardware */
    211     hs_loop_source_desc.bEndpointAddress =
    212         fs_loop_source_desc.bEndpointAddress;
    213     hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
    214                      
    215     /* support super speed hardware */
    216     ss_loop_source_desc.bEndpointAddress =
    217         fs_loop_source_desc.bEndpointAddress;
    218     ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
    219                      
    220     ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs, //此处主要设设置usb速度。
    221             ss_loopback_descs);
    222     if (ret)         
    223         return ret;  
    224                      
    225     DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
    226     ┊   (gadget_is_superspeed(c->cdev->gadget) ? "super" :
    227     ┊   ┊(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
    228             f->name, loop->in_ep->name, loop->out_ep->name);
    229     return 0;        
    230 }

     

    至此gadget框架只驱动注册过程已经介绍完成。下面再来介绍下驱动的注册流程。

    1. 填充usb_composite_driver驱动实例,调用usb_composite_probe进行注。
    2. 移花接木,填充usb_composite驱动中gadget_driver,调用usb_gadget_probe_driver(gadget_driver);使得udc能够和composite设备联系起来。
    3. 调用udc_bind_to_driver,慢慢的将udc和composite绑定在一起。
    4. 调用driver->bind(udc->gadget, driver);实际上是调用composite_bind函数,该函数由内核实现。该函数主要是创建cdev设备,将真正的驱动和cdev绑定在一起。
    5. 此后再调用编写的驱动的bind函数。此时主要是讲cdev设备的配置和function进行填充。设备必须有配置,配置必须有接口。
    6. 针对多function的驱动,必须再次绑定bind函数,此次主要是设置接口id,实例化ep等。
    7. 前6步操作的完成,表示composite设备驱动已经注册成功了。成功了之后呢?那就涉及到对udc的操作了,udc进入请求连接状态,等待中断的响应。
    8. 中断响应也就是响应主设备发起的枚举操作,完成枚举过程,枚举响应主要是调用function->setup函数。枚举过程将在另外一篇文章中介绍。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux usb gadget框架概述 的相关文章

  • 使用“设备过滤器”解决方案时如何处理 Android 上的 USB 权限对话框事件?

    当我将 已知 USB 设备连接到 Android 手机时出现的自动权限对话框中 用户按下 确定 或 取消 时 我试图处理该事件 我正在使用 android usb host 库 可以在 Android 手机和设备之间发送和接收 此外 我使用
  • ALSA 记录 - 了解内存映射

    我尝试使用 ALSA 从 USB 音频设备获取输入并将其作为一系列内容写入磁盘signed short价值观 我最终得到的是看似有效的数据块 其中散布着大块的零 我猜测我的缓冲区设置不正确并且没有正确使用内存映射 我正在尝试什么 采样率 8
  • Mac中如何获取usb设备的设备描述符和配置描述符?

    I have minimum exposure to xcode and I Okit framework I have seen device descriptor and configuration descriptor of a us
  • 发现多个 USB-IrDA 设备,打开并连接每个设备的套接字

    对于任何优秀的套接字程序员 是否可以使用套接字枚举通过 USB 端口连接到 PC 的多个活动 IrDA 设备 如果是这样 怎么办 这确实是我的主要问题 帖子的其余部分完善了细节并描述了我所尝试的内容 我正在使用 Microsoft SDK
  • OSX:如何从 IOUSBDeviceInterface 或位置 id 获取卷名称(或 bsd 名称)

    我正在尝试编写一个应用程序 将特定的 USB 字符串描述符 USB 大容量存储设备 与其卷或 bsd 名称相关联 因此 代码会遍历所有连接的 USB 设备 获取字符串描述符并从其中之一提取信息 我想获取这些 USB 设备的卷名 我找不到合适
  • 使用Python直接向USB发送信号

    如何使用 Python 将 USB 端口连接设置为高或低 这可用于定制 USB 设备 例如 假设我有一个 LED 连接到 USB 端口 数据线 现在通过代码我想闪烁它或控制它 现在 这可以通过使用任何微控制器 Arduino Raspber
  • 使用自定义 INF 的微控制器的 USB 驱动程序安装问题

    我正在使用 NXP LPC1788 微控制器 并尝试在主机上创建一个驱动程序以通过 USB 与其进行通信 我相信我已经设法让设备正确处理标准 USB 请求 PC 能够正确读取字符串描述符 不过 我在编写示例 USB 驱动程序并为设备安装它时
  • 如何枚举所有HID设备? C#

    我需要枚举连接到我的 PC 的所有 HID 设备 我尝试使用这个答案 https stackoverflow com a 3331509 902424 但它枚举了 USBHub 设备 但我在那里找不到我的 HID 设备 EDIT 我很高兴知
  • 从网络浏览器中检测和访问 USB 设备 - 即使用插件

    是否可以通过插件 activeX Java 或 Flex 编写一个能够检测从 USB 设备通道读取数据流的浏览器插件 我做了一些研究 但没有找到答案 http support microsoft com kb 832678 http sup
  • 如何查找USB盘符?

    我正在编写一个安装程序来将应用程序安装到 USB 驱动器 该应用程序只能从 USB 驱动器使用 因此它可以通过自动选择要安装的 USB 驱动器来为用户节省额外的步骤 我可能会尝试使用 Nullsoft 或 MSI 进行安装 但由于我最熟悉
  • USB接口条码扫描器

    不完全是一个编程问题 但很接近 无论如何我都会试试运气 The 键盘楔子 扫码机插入 读者之间的翻译装置 和键盘 数据发送通过 楔形看起来就像是被输入的 电脑 而键盘 本身仍保持完整功能 因为电脑使用键盘 楔子无法区分 在输入的数据之间 扫
  • c#.NET USB设备持久标识符

    我正在寻找一种在 C 中为 USB 插槽 更具体地说是 SD 卡读卡器 提供持久标识符 在最终用户拔下集线器 重新启动计算机时 的方法 驱动器号并不总是分配给同一个插槽 但我需要一种方法来物理识别插槽 A 一旦我能够识别它 我就可以连接到驱
  • 如何在 C# 中从 USB 令牌读取证书

    我有一个 USB 令牌 其中包含一个加密证书 该证书具有公钥和私钥 现在我想用 C 创建一个应用程序 通过它我可以找到可访问的证书信息 当我插入 USB 令牌时 它会被检测到 但计算机部分上没有显示任何驱动器 就像闪存驱动器一样 如何从 U
  • USB 端口速度 Linux [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何以编程方式确定运行 Linux 内核的嵌入式设备中的 USB 端口速度 你可以阅读 sys bus usb devices usb s
  • 时间:2019-03-17 标签:c#usbdecision

    我们当前的应用程序中有几个进程 一个进程处理 USB 加载程序的检测和删除 处理检测和删除的代码如下 protected override void WndProc ref Message m switch m Msg case Win32
  • iOS 中通过 USB 进行反向端口转发

    我在桌面上有一个 Web 套接字服务器 在 iPhone 设备上有一个客户端 我想使用 USB 而不是任何网络与他们通信 我已经使用 adb reverse 在 android 上实现了它 但无法找到适用于 iOS 的任何解决方案 我尝试使
  • 如何在 ADB 连接期间禁用电池充电?

    问题描述 每次我在电脑和手机之间连接 USB 线时 电池都会自动充电 我想使用 ADB 协议 但我不想在 ADB 连接期间为电池充电 是否可以关闭此充电功能 当然 我该怎么做呢 环境 Android 操作系统 4 及更高版本的手机 我只需要
  • Python:获取USB闪存驱动器设备的名称[windows]

    我正在尝试编写一个小程序 它将能够读取有关 REMOVEABLE DEVICE USB 的一些信息 我试过了pyusb https github com walac pyusb但我无法提取我需要的数据 我想从系统中读取 USB 设备的名称
  • Swift 上的 USB 连接委托

    Swift 中是否有一个代表可以让我的班级知道何时通过计算机的 USB 插入新设备 我想知道我的程序何时可以使用新设备 Eric Aya 的答案已经相当不错了 但这里有一个 Swift 3 的改编 我把大部分丑陋的东西包裹在一个USBWat
  • Linux中根据USB VID:PID获取设备路径

    如果我插入一个设备 比如说 dev ttyUSB0我想知道号码0基于其 VID PID 通过lsusb 我怎样才能在 C Linux 中做到这一点 我有这个代码来查找一台打印机设备 如果有帮助的话 int printer open void

随机推荐

  • Vue中@click事件无效?@click.native中.native的含义和使用

    记录 click绑定事件的一个坑 问题描述 今天开发的时候 给组件绑定了 click事件 但是事件却没有执行 代码如下
  • 【Clion】自定义内容的自动补全——动态模板使用教程

    Clion的动态模板 动态模板的作用 实现自动补全 添加动态模板 添加动态模板的步骤 第一步是找到动态模板在哪里添加 打开clion的设置setting 找到编辑器选项editor 选择实时模板live template 选择C C 栏目
  • TCP协议疑难杂症全景解析

    说明 1 本文以TCP的发展历程解析容易引起混淆 误会的方方面面 2 本文不会贴大量的源码 大多数是以文字形式描述 我相信文字看起来是要比代码更轻松的 3 针对对象 对TCP已经有了全面了解的人 因为本文不会解析TCP头里面的每一个字段或者
  • Qt 主窗口与子窗口之间传值

    1 主函数向子函数传值 主窗口定义信号 子窗口定义槽函数 在主窗口将信号与槽连接起来 mainwindow h include
  • VTK笔记-使用vtkUnstructuredGrid类构建非结构化数据

    非结构化数据 非结构化点 非结构化点是在空间中离散随意分布的点 没有拓扑结构 几何机构也是完全没有规则的 非结构化点由顶点和多点的单元组成 非结构化点是一种简单但很重要的数据集类型 在部分可视化工作中会使用它来表示无结构的数据 非结构化网格
  • Windows Server 2019,开启多用户远程

    Windows Server 2019 开启多用户远程 最后记得重启计算机
  • DevopsCamp 第 2 期作业: 《cobra - 05 Cobra 的子命令》 简单说下 cobra 命令树和 gin 路由树的实现差异

    DevopsCamp 第 2 期作业 cobra 05 Cobra 的子命令 简单说下 cobra 命令树和 gin 路由树的实现差异 原文链接 https typonotes com posts 2023 02 14 devopscamp
  • PCL 体素滤波(C++详细过程版)

    体素滤波 一 概述 二 代码实现 三 结果展示 1 原始点云 2 滤波结果 一 概述 体素滤波PCL中经典的点云下采样算法 具体算法原理和实现代码见 PCL体素滤波器 为充分了解算法实现的每一个细节和有待改进的地方 使用C 代码对算法实现过
  • 力扣题---二叉树---相同的树

    题目连接 相同的树 首先我看题目要求以及例题 给你两棵二叉树的根节点 p 和 q 编写一个函数来检验这两棵树是否相同 如果两个树在结构上相同 并且节点具有相同的值 则认为它们是相同的 示例 1 输入 p 1 2 3 q 1 2 3 输出 t
  • FeignClient接口的几种方式总结

    FeignClient这个注解 已经封装了远程调用协议 在springboot的开发 或者微服务的开发过程中 我们需要跨服务调用 或者调用外部的接口 我们都可以使用FeignClient 一 FeignClient介绍 FeignClien
  • ChatGLM2-6B本地部署

    ChatGLM2 6B本地部署 ChatGLM2 6B 是开源中英双语对话模型 ChatGLM 6B 的第二代版本 在保留了初代模型对话流畅 部署门槛较低等众多优秀特性的基础之上 ChatGLM2 6B 引入了如下新特性 更强大的性能 基于
  • uniapp截取部分区域

    通过canvas来绘制截取部分 然后保存 html部分
  • 手把手教你,Selenium 遇见伪元素该如何处理?

    问题发生 在很多前端页面中 大家会见到很多 before after 元素 比如 百度流量研究院 比如 百度疫情大数据平台 以 百度疫情大数据平台 为例 累计确诊 文本并没有显示在 HTML 源代码中 如果通过常规的 xpath 元素定位方
  • Java 经典面试题:聊一聊 JUC 下的 LinkedBlockingQueue

    本文聊一下 JUC 下的 LinkedBlockingQueue 队列 先说说 LinkedBlockingQueue 队列的特点 然后再从源码的角度聊一聊 LinkedBlockingQueue 的主要实现 LinkedBlockingQ
  • c++中的关联容器

    c 中的关联容器主要是map set 已经multimap multiset 为了讲map 得先将pair类型 pair就是一个两个类型的组合 比如一个人的学号就可以是pair
  • 【重磅最新】163篇ICML-2021强化学习领域论文整理汇总(2021.06.07)

    深度强化学习实验室 官网 http www neurondance com 论坛 http deeprl neurondance com 作者 深度强化学习实验室 来源 整理自https icml cc ICML 是机器学习领域最重要的会议
  • 【python爬虫专项(25)】新型冠状病毒肺炎B站视频弹幕数据爬并做数据词云展示

    1 查看要爬取页面 打开B站网址 输入 新型冠状病毒肺炎 关键字 显示界面如下 2 确定爬虫逻辑 查看网页的内容后 一个网址页面下20个视频 这里只采集20页的视频数据 共400个视频 因为是出现的视频按照点击量进行排序的 所以再往后的视频
  • Java使用多线程异步执行批量更新操作

    写在前面 相信不少开发者在遇到项目对数据进行批量操作的时候 都会有不少的烦恼 尤其是针对数据量极大的情况下 效率问题就直接提上了菜板 因此 开多线程来执行批量任务是十分重要的一种批量操作思路 其实这种思路实现起来也十分简单 就拿批量更新的操
  • 观察者模式(Observer)

    一 观察者模式定义 对象间的一种一对多的依赖关系 当一个对象的状态发生改变时 所有依赖它的对象都得到通知并自动更新 二 观察者模式的结构说明 1 Subject 目标对象 a 一个目标可以被多个观察者观察 b 目标可以提供添加和删除观察者
  • Linux usb gadget框架概述

    大大小小开发了四个与gadget相关的驱动 字符驱动 g multi g ether g zero 在这里把自己对gadget的开发中自己的感悟记录之 想要了解gadget 必须了解其框架 知道composite gadget udc三者之