USB Gadget
+ -

USB Gadget数据收发-批量传输

2025-09-11 0 0

当执行命令

ln -s functions/Loopback.0 configs/c.1

之后,会调用loopback_alloc函数创建usb_function

struct f_loopback {
    struct usb_function    function;

    struct usb_ep        *in_ep;
    struct usb_ep        *out_ep;
};

usb_function结构体中的回调函数就是启用、禁用卸载设备的回调

    loop = kzalloc(sizeof *loop, GFP_KERNEL);
    ...
    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;

usb_function的创建,代表着物理固件配置参数的准备好,但对于USB设备来说,存在着高速全速等因硬件原因而导致的动态配置,故在实际启用UDC设备时:
1.先调用loopback_bind来动态配置这些数据。这些数据包括

2.当物理设备插入主机后,主机根据物理连接情况确定其工作于那种模式下(高速、全速还是起高速),然后主机开始获取设备描述符,分配设备地址,获取配置描述符,字符串描述符后,即开始SET_CONFIG.
SET_CONFIG会SET_INTERFACE对应的回调应是loopback_set_alt
选择了接口SET_INTERFACE,即根据USB定义,其接口描述符下的端点应该可用,这在gadaget中就是初始化端点的收发,对于enable_endpoint。

static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,    struct usb_ep *ep)
{
    struct usb_request            *req;
    unsigned                i;
    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;

    printk("now enable_endpoint ep=0x%02x,name=%s\n",ep->address,ep->name);
    /*
     * allocate a bunch of read buffers and queue them all at once.
     * we buffer at most 'qlen' transfers; fewer if any need more
     * than 'buflen' bytes each.
     */
    for (i = 0; i < qlen && result == 0; i++) {
        req = lb_alloc_ep_req(ep, 0);
        if (!req)
            goto fail1;

        req->complete = loopback_complete;
        result = usb_ep_queue(ep, req, GFP_ATOMIC);
        if (result) {
            ERROR(cdev, "%s queue req --> %d\n",ep->name, result);
            goto fail1;
        }
    }

    return 0;

fail1:
    usb_ep_disable(ep);
fail0:
    return result;
}

这其中包括两部分:

  • 配置端点及使用
      result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
      result = usb_ep_enable(ep);
    
  • 初始化收发队列回调,这里配置了32个请求回调
    //创建端点请求及初始化完成回调
          req = lb_alloc_ep_req(ep, 0);
          req->complete = loopback_complete;
    //入队列
          result = usb_ep_queue(ep, req, GFP_ATOMIC);
    
    当端点有动作时,执行请求回调。
static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
    struct f_loopback    *loop = ep->driver_data;
    struct usb_composite_dev *cdev = loop->function.config->cdev;
    int            status = req->status;
    int i;
    unsigned char* pData;

    printk("loopback_complete status=%d\n",status);
    switch (status) {
    case 0:                /* normal completion? */
        if (ep == loop->out_ep) {
            req->zero = (req->actual < req->length);
            req->length = req->actual;
        }
        printk("ep=0x%02x\n",ep->address);
        printk("length=%d\n",req->length);
        pData = req->buf;
        //for(i=0;i<10&&i<req->length;i++){
        //    printk("data[%d]=%02x\n ",i,pData[i]);
        //}

    /* queue the buffer for some later OUT packet */
        req->length = buflen;
        status = usb_ep_queue(ep, req, GFP_ATOMIC);
        if (status == 0)
            return;

        /* "should never get here" */
        /* FALLTHROUGH */

    default:
        ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
                status, req->actual, req->length);
        /* FALLTHROUGH */

    /* NOTE:  since this driver doesn't maintain an explicit record
     * of requests it submitted (just maintains qlen count), we
     * rely on the hardware driver to clean up on disconnect or
     * endpoint disable.
     */
    case -ECONNABORTED:        /* hardware forced ep reset */
    case -ECONNRESET:        /* request dequeued */
    case -ESHUTDOWN:        /* disconnect from host */
        free_ep_req(ep, req);
        return;
    }
}

数据的收发

对于发送数送数据,调用usb_ep_queue挂入队列的请求即表示需要发送的数据包,当发送完成后,会调用loopback_complete表示该请求的执行结果。

对于接收数据,调用usb_ep_queue挂入队列的请求表示收到的数据包,可以直接解析出收到的数据。

如本人将bulk_buflen改为512之后,收到32个包之后,才会在主机端收到指定的数据.前面的32位为初始化OUT端点挂入的32个数据包,数据为随机值。当第一个完成之后,在完成例程中重置自定义数据,再次复用该请求发送数据。故32个之后才是自定义数据。

static struct usb_function_instance *loopback_alloc_instance(void)
{
    struct f_lb_opts *lb_opts;
    printk("loopback_alloc_instance\n");

    lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
    if (!lb_opts)
        return ERR_PTR(-ENOMEM);

    mutex_init(&lb_opts->lock);
    lb_opts->func_inst.free_func_inst = lb_free_instance;
    lb_opts->bulk_buflen = 512;// GZERO_BULK_BUFLEN;//4096
    lb_opts->qlen = GZERO_QLEN;//32

    config_group_init_type_name(&lb_opts->func_inst.group, "",&lb_func_type);

    return  &lb_opts->func_inst;
}

BUSHOUND抓包如下

Device    Length    Phase  Data                                               
------    --------  -----  ---------------------------------------------------
  54.0              CTL    80 06 00 01  00 00 12 00                           
  54.0          18  IN     12 01 00 02  00 00 00 40  6b 1d 01 2d  01 04 01 02 
  54.0              CTL    80 06 00 02  00 00 09 00                           
  54.0           9  IN     09 02 20 00  01 01 04 80  3c                       
  54.0              CTL    80 06 00 02  00 00 20 00                           
  54.0          32  IN     09 02 20 00  01 01 04 80  3c 09 04 00  00 02 ff 00 
  54.0              CTL    80 00 00 00  00 00 02 00                           
  54.0           2  IN     01 00                                              
  54.0              CTL    00 09 01 00  00 00 00 00                           
  54.1         512  IN     00 84 8d 94  65 63 20 20  36 20 30 39  3a 33 39 3a 
  54.1         512  IN     00 86 8d 94  64 65 76 00  fe ed ca fe  28 00 00 00 
  54.1         512  IN     00 8c 8d 94  ab 00 00 00  00 00 00 00  00 00 00 00 
  54.1         512  IN     00 00 00 00  24 00 00 00  00 00 00 00  00 00 00 00 
  54.1         512  IN     00 00 8c 94  65 63 20 20  36 20 30 39  3a 33 39 3a 
  54.1         512  IN     00 00 00 00  65 63 20 20  36 20 30 39  3a 33 39 3a 
  54.1         512  IN     00 00 00 00  65 63 20 20  36 20 30 39  3a 33 39 3a 
  54.1         512  IN     00 60 0b 94  40 00 00 00  a4 72 f3 b0  1f 00 00 00 
  54.1         512  IN     00 00 00 00  65 63 20 20  36 20 30 39  3a 33 38 3a 
  54.1         512  IN     00 90 8d 94  65 63 20 20  36 20 30 39  3a 33 38 3a 
  54.1         512  IN     00 9a 8d 94  65 63 20 20  36 20 30 39  3a 33 38 3a 
  54.1         512  IN     00 98 8d 94  65 63 20 20  36 20 30 39  3a 33 38 3a 
  54.1         512  IN     00 00 00 00  65 63 20 20  36 20 30 39  3a 33 38 3a 
  54.1         512  IN     00 9c 43 94  06 00 00 00  00 00 00 00  00 00 00 00 
  54.1         512  IN     00 90 43 94  06 00 00 00  00 00 00 00  00 00 00 00 
  54.1         512  IN     00 9e 43 94  02 00 00 00  00 00 00 00  00 00 00 00 
  54.1         512  IN     00 00 00 00  02 00 00 00  00 00 00 00  00 00 00 00 
  54.1         512  IN     00 4c 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 4a 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 48 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 46 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 44 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 42 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 40 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 00 00 00  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 2c 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 2a 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 28 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 26 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 24 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 22 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 20 75 94  2f 64 65 76  69 63 65 73  2f 70 6c 61 
  54.1         512  IN     00 00 00 00  00 00 00 00  00 00 30 39  3a 33 39 3a 
  54.1         512  IN     01 01 01 01  01 01 01 01  01 01 ca fe  28 00 00 00 
  54.1         512  IN     02 02 02 02  02 02 02 02  02 02 00 00  00 00 00 00 
  54.1         512  IN     03 03 03 03  03 03 03 03  03 03 00 00  00 00 00 00 
  54.1         512  IN     04 04 04 04  04 04 04 04  04 04 30 39  3a 33 39 3a 
  54.1         512  IN     05 05 05 05  05 05 05 05  05 05 30 39  3a 33 39 3a 
  54.1         512  IN     06 06 06 06  06 06 06 06  06 06 30 39  3a 33 39 3a 
  54.1         512  IN     07 07 07 07  07 07 07 07  07 07 f3 b0  1f 00 00 00 
  54.1         512  IN     08 08 08 08  08 08 08 08  08 08 30 39  3a 33 38 3a 
  54.1         512  IN     09 09 09 09  09 09 09 09  09 09 30 39  3a 33 38 3a

无论是接收和发送数据,在该请求包完成后,会自动从端点队列中“移除”,然后再执行loopback_complete,如果需要复用,则需要调用usb_ep_queue重新挂入队列。

0 篇笔记 写笔记

USB Gadget CDC-ECM网卡实例
ECM和NCM网卡类似,也需要安装ECM驱动设备兼容ID为:USBClass_02&SubClass_06&Prot_00USBClass_02&SubClass_06USBClass_02创建脚本:#!/bin/bashmodprobe libcompos......
Linux Gadget驱动结构关系图
Linux Gadget驱动层级如下:Gadget Function驱动/gadget legacy驱动Gadget Compoiste驱动USB UDC驱动(USB Device Control)Gadget Function驱动/gadget legacy驱动function, 更加现代......
gadget probe过程-以g_audio为例
g_audio中通过如下代码调用usb_composite_probe,其中入参为audio_driverstatic struct usb_composite_driver audio_driver = { .name = "g_audio", ......
configfs文件系统
Linux内核使用CONFIGFS配置项支持可以通过用户创建USB Compoiste Gadget。CONFIG_CONFIGFS_FS项用于支持配置文件系统。Menconfig -> FileSystems -> Psuedo File System -> {M}User......
Gadget legacy和Gadget Function的区别
在 Linux 的 USB Gadget 子系统中,Legacy GadgetGadget Function(FunctionFS 或 configfs) 是两种不同的实现方式,用于将 Linux 设备配置为 USB 外设(如 U 盘、网卡、串口等)。它们的区别主要体现在架构、使用方式和灵活性......
usb_gadget复合HID键盘和U盘实例
#!/bin/bash# 加载模块modprobe libcompositemodprobe usb_f_hidmodprobe usb_f_mass_storage.komount -t configfs none /sys/kernel/config# 创建 Gadgetcd......
usb gadget创建uvc相机脚本实例
#!/bin/bashmodprobe libcompositemodprobe usb_f_uvcmount -t configfs none /sys/kernel/configmkdir -p /sys/kernel/config/usb_gadget/g1cd /sys/kern......
USB Gadget CDC-ACM串口实例
符合USB ACM规范的设备由Windows提供其驱动程序usbser.sysuserser.infLinux Gadget对应的ACM驱动依赖为root@ATK-IMX6U:/sys/kernel/config/usb_gadget/g1# lsmodModule ......
USB Gadget CDC-NCM网卡实例
USG Gadget也提供了CDC-NCM网卡驱动程序,使用USB Gadget CDC-NCM驱动,这样在Windows主机端和Linux设备端都会创建一个USB网卡设备。root@ATK-IMX6U:/lib/modules/4.1.15# ifconfig -ausb0 Lin......
USB Gadget 打印机实例
打印机这一块应该要与实例相连,具体这一块没有研究过。生成的设备如下:设备模块如下:root@ATK-IMX6U:/lib/modules/4.1.15# lsmodModule Size Used byusb_f_printer 1137......
USB Gadget CDC-EEM网卡实例
从理论上来讲,EEM网卡Windows是应该支持的,实际上也确实不用像NCM、ECM需要手动安装驱动,系统会自动匹配兼容ID来进行安装驱动,但是驱动启动失败,这个Linux系统下就没有问题。#!/bin/bashmodprobe libcompositemodprobe usb_f_eemm......
USB Gadget LoopBack实例
Loopback Function 提供的功能简单,它分配了两个 bulk endpoint,所做的就是把 out_ep 接收到的数据转发到 in_ep。#!/bin/bashmodprobe libcompositemodprobe usb_f_ss_lbmount -t configfs......
USB Gadget RNDIS网络适配器实列
和NCM一样,由于兼容ID的问题,RNDIS网络适配器也需要手动安装驱动。其对应的驱动为:netrndis.infrndismp6.sys其创建脚本如下:#!/bin/bashmodprobe libcompositemodprobe usb_f_rndismount -t conf......
USB Gadget实例分析usb_function_instance和usb_function
linux usb gadtet的设备类驱动有两个很重要的概念:usb_function_instance和usb_function从名来上看,usb_function_instance像是usb_function的实例化,但实际是恰恰相反。这里以f_loopback为示例解释:USB设备信息执......
USB Gadget数据收发-批量传输
当执行命令ln -s functions/Loopback.0 configs/c.1之后,会调用loopback_alloc函数创建usb_functionstruct f_loopback { struct usb_function function; struct......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    您的支持,是我们前进的动力!