USB Gadget
+ -

USB请求队列

2025-09-26 本文链接为:http://www.usbzh.com/article/detail-1591.html ,欢迎转载,转载请附上本文链接。

和端点的使能与禁用一样,端点的请求也与UDC端点的ops相关。

static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
    return ep->ops->alloc_request(ep, gfp_flags);
}
static inline void usb_ep_free_request(struct usb_ep *ep,struct usb_request *req)
{
    ep->ops->free_request(ep, req);
}
static inline int usb_ep_queue(struct usb_ep *ep,struct usb_request *req, gfp_t gfp_flags)
{
    return ep->ops->queue(ep, req, gfp_flags);
}
static inline int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
    return ep->ops->dequeue(ep, req);
}

ep_alloc_request

队列的申请.Linux充分体现了与Windows的不同,可以光明正大的将结构体暴露给上一层,但Windows就可能是一个PVOID类型的指针。

USB请求创建与端点相关。通过队列提交ep_queue请求后,通过回调函数通知操作完成。

static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
    struct ci_hw_req *hwreq = NULL;

    if (ep == NULL)
        return NULL;

    hwreq = kzalloc(sizeof(struct ci_hw_req), gfp_flags);
    if (hwreq != NULL) {
        INIT_LIST_HEAD(&hwreq->queue);
        INIT_LIST_HEAD(&hwreq->tds);
    }

    return (hwreq == NULL) ? NULL : &hwreq->req;
}

ep_free_request

static void ep_free_request(struct usb_ep *ep, struct usb_request *req)
{
    struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
    struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
    struct td_node *node, *tmpnode;
    unsigned long flags;

    if (ep == NULL || req == NULL) {
        return;
    } else     if (!list_empty(&hwreq->queue)) {
    //还在队列中
        dev_err(hwep->ci->dev, "freeing queued request\n");
        return;
    }

    spin_lock_irqsave(hwep->lock, flags);

    //释放DMA td
    list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
        dma_pool_free(hwep->td_pool, node->ptr, node->dma);
        list_del_init(&node->td);
        node->ptr = NULL;
        kfree(node);
    }

    kfree(hwreq);

    spin_unlock_irqrestore(hwep->lock, flags);
}

ep_queue

static int ep_queue(struct usb_ep *ep, struct usb_request *req,
            gfp_t __maybe_unused gfp_flags)
{
    struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
    int retval = 0;
    unsigned long flags;

    if (ep == NULL || req == NULL || hwep->ep.desc == NULL)
        return -EINVAL;

    spin_lock_irqsave(hwep->lock, flags);
    if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) {
        spin_unlock_irqrestore(hwep->lock, flags);
        return 0;
    }

    retval = _ep_queue(ep, req, gfp_flags);
    spin_unlock_irqrestore(hwep->lock, flags);
    return retval;
}

·_ep_queue主要执行:

  • list_empty(&hwreq->queue))判断是否已经在队列中,防止重复挂入
  • _hardware_enqueue硬件拆包
  • list_add_tail(&hwreq->queue, &hwep->qh.queue)
static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
            gfp_t __maybe_unused gfp_flags)
{
    struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
    struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
    struct ci_hdrc *ci = hwep->ci;
    int retval = 0;

    if (ep == NULL || req == NULL || hwep->ep.desc == NULL)
        return -EINVAL;

    if (hwep->type == USB_ENDPOINT_XFER_CONTROL) {
        if (req->length)
            hwep = (ci->ep0_dir == RX) ?  ci->ep0out : ci->ep0in;
        if (!list_empty(&hwep->qh.queue)) {
            _ep_nuke(hwep);
            dev_warn(hwep->ci->dev, "endpoint ctrl %X nuked\n",
                 _usb_addr(hwep));
        }
    }

    //同步传输的请求数据长度,不能超过一次传输的最大数据量
    if (usb_endpoint_xfer_isoc(hwep->ep.desc) &&  hwreq->req.length > (1 + hwep->ep.mult) * hwep->ep.maxpacket)
    {
        dev_err(hwep->ci->dev, "request length too big for isochronous\n");
        return -EMSGSIZE;
    }

    //该请求不能已经在其它队列中排队,必须为空闲的
    if (!list_empty(&hwreq->queue)) {
        dev_err(hwep->ci->dev, "request already in queue\n");
        return -EBUSY;
    }

    /* push request */
    hwreq->req.status = -EINPROGRESS;    //默认状态设置
    hwreq->req.actual = 0;                //无论是收还是发,实际传输都应先是0

    //该列列请求在硬件设备层的配置,DMA相关
    retval = _hardware_enqueue(hwep, hwreq);

    if (retval == -EALREADY)
        retval = 0;
    if (!retval)
    {
    //该端点的请求都在hwep->qh.queue队列中
        list_add_tail(&hwreq->queue, &hwep->qh.queue);
    }
    return retval;
}

ep_dequeue

ep_dequeue会将一个已经调用ep_queue的请求从硬件队列中移除,并会调用该请求的完成函数。

其余功能详见注释。

static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
    struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
    struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
    unsigned long flags;
    struct td_node *node, *tmpnode;

    if (ep == NULL || req == NULL || hwreq->req.status != -EALREADY ||
        hwep->ep.desc == NULL || list_empty(&hwreq->queue) ||
        list_empty(&hwep->qh.queue))
        return -EINVAL;

    spin_lock_irqsave(hwep->lock, flags);
    if (hwep->ci->gadget.speed != USB_SPEED_UNKNOWN)
        hw_ep_flush(hwep->ci, hwep->num, hwep->dir);

    list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
        dma_pool_free(hwep->td_pool, node->ptr, node->dma);
        list_del(&node->td);
        kfree(node);
    }

    /* pop request */
    //从端点队列中移除
    list_del_init(&hwreq->queue);

    //移除dma映射
    usb_gadget_unmap_request(&hwep->ci->gadget, req, hwep->dir);

    //设备请求标识
    req->status = -ECONNRESET;

    //调用完成函数
    if (hwreq->req.complete != NULL) {
        spin_unlock(hwep->lock);
        usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
        spin_lock(hwep->lock);
    }

    spin_unlock_irqrestore(hwep->lock, flags);
    return 0;
}

void usb_gadget_giveback_request(struct usb_ep *ep,    struct usb_request *req)
{
    if (likely(req->status == 0))
        usb_led_activity(USB_LED_EVENT_GADGET);

    req->complete(ep, req);
}

数据中断流向

“drivers\usb\chipidea\udc.c”
UDC驱动中,驱动配置数下:

int ci_hdrc_gadget_init(struct ci_hdrc *ci)
{
    struct ci_role_driver *rdrv;

    if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC))
        return -ENXIO;

    rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
    if (!rdrv)
        return -ENOMEM;

    rdrv->start    = udc_id_switch_for_device;
    rdrv->stop    = udc_id_switch_for_host;
    rdrv->irq    = udc_irq;
    rdrv->suspend    = udc_suspend;
    rdrv->resume    = udc_resume;
    rdrv->name    = "gadget";
    ci->roles[CI_ROLE_GADGET] = rdrv;

    return udc_start(ci);
}

中断函数为udc_irq。

static irqreturn_t udc_irq(struct ci_hdrc *ci)
{
...
    intr = hw_test_and_clear_intr_active(ci);
    if (intr) {
        ....
        //中断执行
        if (USBi_UI  & intr)
            isr_tr_complete_handler(ci);
    }
...
}

isr_tr_complete_handler是事务完成中断处理函数。

static void isr_tr_complete_handler(struct ci_hdrc *ci)
__releases(ci->lock)
__acquires(ci->lock)
{
    unsigned i;
    int err;

    for (i = 0; i < ci->hw_ep_max; i++) {
        struct ci_hw_ep *hwep  = &ci->ci_hw_ep[i];

        if (hwep->ep.desc == NULL)
            continue;   /* not configured */

        if (hw_test_and_clear_complete(ci, i)) {
            //端点数据收发请求
            err = isr_tr_complete_low(hwep);
            if (hwep->type == USB_ENDPOINT_XFER_CONTROL) {
                if (err > 0)   /* needs status phase */
                    err = isr_setup_status_phase(ci);
                if (err < 0) {
                    spin_unlock(&ci->lock);
                    if (_ep_set_halt(&hwep->ep, 1, false))
                        dev_err(ci->dev,    "error: _ep_set_halt\n");
                    spin_lock(&ci->lock);
                }
            }
        }

        //setup请求,包括ClearFeature等标准请求
        if (i == 0 &&
            hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
            isr_setup_packet_handler(ci);
    }
}

数据收发请求是从队列中

static int isr_tr_complete_low(struct ci_hw_ep *hwep)
__releases(hwep->lock)
__acquires(hwep->lock)
{
    struct ci_hw_req *hwreq, *hwreqtemp;
    struct ci_hw_ep *hweptemp = hwep;
    int retval = 0;

    list_for_each_entry_safe(hwreq, hwreqtemp, &hwep->qh.queue,    queue){

    //数据拷贝
        retval = _hardware_dequeue(hwep, hwreq);
        if (retval < 0)
            break;

        //取出队列,并完成
        list_del_init(&hwreq->queue);
        if (hwreq->req.complete != NULL) {
            spin_unlock(hwep->lock);
            if ((hwep->type == USB_ENDPOINT_XFER_CONTROL) && hwreq->req.length)
                hweptemp = hwep->ci->ep0in;
                //调用完成例程
            usb_gadget_giveback_request(&hweptemp->ep, &hwreq->req);
            spin_lock(hwep->lock);
        }
    }

    if (retval == -EBUSY)
        retval = 0;

    return retval;
}

而关于数据到队列中的拷贝,则是_hardware_dequeue函数来实现的。

本文链接为:http://www.usbzh.com/article/detail-1591.html ,欢迎转载,转载请附上本文链接。

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, 更加现......
fusb300_udc
usbgadgetudcfusb300_udc是平台设备驱动程序static struct platform_driver fusb300_driver = { .remove = fusb300_remove, .driver = { ......
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设备信息执......
关注公众号
  • HID人机交互
  • Linux&USB
  • UAC音频
  • CDC
  • TYPE-C
  • USB规范
  • USB大容量存储
  • USB百科
  • USB周边
  • UVC摄像头
  • Windows系统USB
  • 音视频博客
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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