Linux之 USB驱动框架-usb_submit_urb和usb_fill_*_urb分析(6)

一、usb_fill_*_urb 函数调用流程

// 控制
static inline void usb_fill_control_urb(struct urb *urb,
                    struct usb_device *dev,
                    unsigned int pipe,
                    unsigned char *setup_packet,
                    void *transfer_buffer,
                    int buffer_length,
                    usb_complete_t complete_fn,
                    void *context)
// 中断
static inline void usb_fill_int_urb(struct urb *urb,
                    struct usb_device *dev,
                    unsigned int pipe,
                    void *transfer_buffer,
                    int buffer_length,
                    usb_complete_t complete_fn,
                    void *context,
                    int interval)
// 批量

static inline void usb_fill_bulk_urb(struct urb *urb,
                     struct usb_device *dev,
                     unsigned int pipe,
                     void *transfer_buffer,
                     int buffer_length,
                     usb_complete_t complete_fn,
                     void *context)
    
// 实时   
// 实时urb 没有和中断、控制、批量urb 类似的初始化函数,因此它们在提交到USB核心之前,需要在驱动程序中手动的初始化    
    
 

 拿其中的 usb_fill_int_urb 具体分析

static inline void usb_fill_int_urb(struct urb *urb,
                                    struct usb_device *dev,
                                    unsigned int pipe,
                                    void *transfer_buffer,
                                    int buffer_length,
                                    usb_complete_t complete_fn,
                                    void *context,
                                    int interval)
{
        urb->dev = dev;
        urb->pipe = pipe;
        urb->transfer_buffer = transfer_buffer;
        urb->transfer_buffer_length = buffer_length;
        urb->complete = complete_fn;
        urb->context = context;

        if (dev->speed == USB_SPEED_HIGH || dev->speed >= USB_SPEED_SUPER) {
                /* make sure interval is within allowed range */
                interval = clamp(interval, 1, 16);

                urb->interval = 1 << (interval - 1);
        } else {
                urb->interval = interval;
        }

        urb->start_frame = -1;
}
 

 上面含义就是填充urb 成员,其中的complete_fn 函数,是urb 执行完毕后,usb core 执行的回调函数。

完成函数的作用:

比如:调用usb_fill_int_urb(),其中complete_fn相当于中断函数,即主机读取完数据会放到指定的内存data_dma中。然后通知CPU处理这些数据,这些数据只有USB设备驱动程序知道其含义。所以要在这个完成函数中处理这些数据,如上报。

1、urb的complete 执行流程

以usb鼠标驱动为例,具体分析如下:

 usb_mouse_probe->

                usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
                         (maxp > 8 ? 8 : maxp),
                         usb_mouse_irq, mouse, endpoint->bInterval); //填充 urb

usb_mouse_irq 为 urb 的complete 函数。

那么urb->complete 执行流程:

usb_mouse_irq  <- __usb_hcd_giveback_urb <-  usb_giveback_urb_bh

usb_giveback_urb_bh 这个函数是怎么初始化并触发执行?

 在增加usb 控制器驱动中,会调用 usb_add_hcd 函数 ,接着,在usb_add_hcd 函数中,会初始化两个软中断:

/* initialize tasklets */
        init_giveback_urb_bh(&hcd->high_prio_bh);
        init_giveback_urb_bh(&hcd->low_prio_bh);
 

 接着看 init_giveback_urb_bh :

static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
{

        spin_lock_init(&bh->lock);
        INIT_LIST_HEAD(&bh->head);
        tasklet_setup(&bh->bh, usb_giveback_urb_bh);
}


从上面可知,init_giveback_urb_bh 是初始化tasklet 软中断函数。

        

 如何调度到这个 tasklet 函数:usb_giveback_urb_bh  ???

答案:在主控驱动中,注册hcd 时,会调用usb_create_hcd:

ohci_hcd_s3c2410_probe -> 

        usb_create_hcd->

                __usb_create_hcd(driver, dev, dev, bus_name, NULL);->

                                timer_setup(&hcd->rh_timer, rh_timer_func, 0);

 这里创建了 timer 定时,初始化定时函数为 rh_timer_func。

rh_timer_func

/* timer callback */
static void rh_timer_func (struct timer_list *t)
{
        struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer);

        usb_hcd_poll_rh_status(_hcd);
}
 

 usb_hcd_poll_rh_status

void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
        struct urb      *urb;
        int             length;
        int             status;
        unsigned long   flags;
        char            buffer[6];      /* Any root hubs with > 31 ports? */

        if (unlikely(!hcd->rh_pollable))
                return;
        if (!hcd->uses_new_polling && !hcd->status_urb)
                return;

        length = hcd->driver->hub_status_data(hcd, buffer);  //检测hub是否有数据到来 
        if (length > 0) {

                /* try to complete the status urb */
                spin_lock_irqsave(&hcd_root_hub_lock, flags);
                urb = hcd->status_urb;
                if (urb) {
                        clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
                        hcd->status_urb = NULL;
                        if (urb->transfer_buffer_length >= length) {
                                status = 0;
                        } else {
                                status = -EOVERFLOW;
                                length = urb->transfer_buffer_length;
                        }
                        urb->actual_length = length;
                        memcpy(urb->transfer_buffer, buffer, length);

                        usb_hcd_unlink_urb_from_ep(hcd, urb);
                        usb_hcd_giveback_urb(hcd, urb, status);
                } else {
                        length = 0;
                        set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
                }
                spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
        }

        /* The USB 2.0 spec says 256 ms.  This is close enough and won't
         * exceed that limit if HZ is 100. The math is more clunky than
         * maybe expected, this is to make sure that all timers for USB devices
         * fire at the same time to give the CPU a break in between */
        if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :
                        (length == 0 && hcd->status_urb != NULL))
                mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
 

 usb_hcd_giveback_urb

void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
        struct giveback_urb_bh *bh;
        bool running, high_prio_bh;
        dump_stack();
        /* pass status to tasklet via unlinked */
        if (likely(!urb->unlinked))
                urb->unlinked = status;

        if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
                __usb_hcd_giveback_urb(urb);
                return;
        }

        if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
                bh = &hcd->high_prio_bh;
                high_prio_bh = true;
        } else {
                bh = &hcd->low_prio_bh;
                high_prio_bh = false;
        }

        spin_lock(&bh->lock);
        list_add_tail(&urb->urb_list, &bh->head);
        running = bh->running;
        spin_unlock(&bh->lock);

        if (running)
                ;
        else if (high_prio_bh)
                tasklet_hi_schedule(&bh->bh);
        else
                tasklet_schedule(&bh->bh);
}
EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb);

上面的tasklet 函数会调用到 tasklet_hi_schedule(&bh->bh);或者 tasklet_schedule(&bh->bh);

这样就会触发usb_giveback_urb_bh 。

初始化后这个定时函数后,什么时候使用mod_timer 触发呢?

答案:

ohci_hcd_s3c2410_probe -> 

        usb_add_hcd ->

                register_root_hub->

                        usb_get_device_descriptor->

                                          usb_get_descriptor->

                                                        usb_control_msg->

                                                                       usb_internal_control_msg->

                                                                                        usb_fill_control_urb->

                                                                                                                usb_submit_urb->

                

 从下面的usb_submit_urb 分析可知,这是主控驱动注册root_hub,那么调用rh_urb_enqueue

usb_submit_urb->

               usb_hcd_submit_urb->

                                rh_urb_enqueue->

                                                rh_queue_status->

                                                                  mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));

                        

 上面可知,主控会注册一个timer 定时函数,进行检测hub 的端口是否有数据到来,然后处理数据,处理之后,调用处理完成函数。和之前分析的  hub_wq 工作队列检测是否有usb设备连接和断开 是不一样的。工作函数hub_event会被循环执行检测是否有设备连接和断开。

 二、usb_submit_urb

usb_submit_urb 调用流程:

usb_submit_urb->

        usb_hcd_submit_urb

 usb_hcd_submit_urb 函数:

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
        int                     status;
        struct usb_hcd          *hcd = bus_to_hcd(urb->dev->bus);

        /* increment urb's reference count as part of giving it to the HCD
         * (which will control it).  HCD guarantees that it either returns
         * an error or calls giveback(), but not both.
         */
        usb_get_urb(urb);
        atomic_inc(&urb->use_count);
        atomic_inc(&urb->dev->urbnum);
        usbmon_urb_submit(&hcd->self, urb);

        /* NOTE requirements on root-hub callers (usbfs and the hub
         * driver, for now):  URBs' urb->transfer_buffer must be
         * valid and usb_buffer_{sync,unmap}() not be needed, since
         * they could clobber root hub response data.  Also, control
         * URBs must be submitted in process context with interrupts
         * enabled.
         */

        if (is_root_hub(urb->dev)) {          -------------------------------------(1)
                status = rh_urb_enqueue(hcd, urb);
        } else {
                status = map_urb_for_dma(hcd, urb, mem_flags);
                if (likely(status == 0)) {
                        status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); -----------(2)
                        if (unlikely(status))
                                unmap_urb_for_dma(hcd, urb);
                }
        }

        if (unlikely(status)) {
                usbmon_urb_submit_error(&hcd->self, urb, status);
                urb->hcpriv = NULL;
                INIT_LIST_HEAD(&urb->urb_list);
                atomic_dec(&urb->use_count);
                /*
                 * Order the write of urb->use_count above before the read
                 * of urb->reject below.  Pairs with the memory barriers in
                 * usb_kill_urb() and usb_poison_urb().
                 */
                smp_mb__after_atomic();

                atomic_dec(&urb->dev->urbnum);
                if (atomic_read(&urb->reject))
                        wake_up(&usb_kill_urb_queue);
                usb_put_urb(urb);
        }
        return status;
}
 

 (1)如果是 root_hub,则调用 rh_urb_enqueue 函数,

static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
        if (usb_endpoint_xfer_int(&urb->ep->desc))   //中断传输
                return rh_queue_status (hcd, urb);
        if (usb_endpoint_xfer_control(&urb->ep->desc))  //控制传输
                return rh_call_control (hcd, urb);
        return -EINVAL;
}
共有两个事务。

 (2)如果非root_hub ,则调用回调函数urb_enqueue

拿ohci 举例:

static const struct hc_driver ohci_hc_driver = {
        .description =          hcd_name,
        .product_desc =         "OHCI Host Controller",
        .hcd_priv_size =        sizeof(struct ohci_hcd),

        /*
         * generic hardware linkage
        */
        .irq =                  ohci_irq,
        .flags =                HCD_MEMORY | HCD_DMA | HCD_USB11,

        /*
        * basic lifecycle operations
        */
        .reset =                ohci_setup,
        .start =                ohci_start,
        .stop =                 ohci_stop,
        .shutdown =             ohci_shutdown,

        /*
         * managing i/o requests and associated device resources
        */
        .urb_enqueue =          ohci_urb_enqueue,
        .urb_dequeue =          ohci_urb_dequeue,
        .endpoint_disable =     ohci_endpoint_disable,

        /*
        * scheduling support
        */
        .get_frame_number =     ohci_get_frame,

        /*
        * root hub support
        */
        .hub_status_data =      ohci_hub_status_data,
        .hub_control =          ohci_hub_control,
#ifdef CONFIG_PM
        .bus_suspend =          ohci_bus_suspend,
        .bus_resume =           ohci_bus_resume,
#endif
        .start_port_reset =     ohci_start_port_reset,
};
 

  .urb_enqueue =          ohci_urb_enqueue,调用ohci_urb_enqueue 函数,

ohci_urb_enqueue ->td_submit_urb->ohci_writel

留一个问题:ohci 执行完urb 后,如何触发 compelte 函数?

root hub 有一个定时timer函数周期性检测,然后触发 tasklet 函数,调用urb->compelete 函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/3256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CST Studio初级教程 一

本教程将详细介绍CST Studio Project创建。 新建Project 1. 点击New and Recent&#xff0c;然后点击New Template。 然后依据我们的仿真属类&#xff0c;在下图中做选择需要的模板。 如果做高频连接器信号完整性&#xff08;SI&#xff09;仿真&#xff0c;我们就选Microwaves…

数据结构之二叉搜索树底层实现洞若观火!

目录 题外话 正题 二叉搜索树 底层实现 二叉搜索树查找操作 查找操作思路 查找代码实现详解 二叉搜索树插入操作 插入操作思路 插入代码详解 二叉搜索树删除操作 删除操作思路 删除代码详解 小结 题外话 我的一切都是党给的,都是人民给的,都是家人们给的!! 十分感…

Java分布式ID

1 什么是分布式ID 分布式ID是指在分布式系统中生成的唯一标识符&#xff0c;用于标识不同实体或数据的唯一性。在分布式系统中&#xff0c;多台机器并行处理任务&#xff0c;为了确保生成的ID在整个系统中的唯一性&#xff0c;需要采用特殊的算法来生成分布式ID。 在传统的单机…

Docker的数据管理、网络通信和dockerfile

目录 一、Docker的数据管理 1. 数据卷 1.1 数据卷定义 1.2 数据卷配置 2. 数据卷容器 2.1 创建数据卷容器 2.2 使用--volume-from来挂载test1 二、端口映射 三、容器互联 1. 创建容器互联 ​编辑2. 进入test2测试&#xff08;ping 容器名/别名&#xff09; 四、Dock…

Python的pytest框架(5)--测试标记(Markers)

该篇将循序渐进地详细拆解 pytest.mark 装饰器&#xff1a; 目录 一、概念 二、标记的基本结构与使用 三、标记在测试中的层次应用 四、标记的筛选与运行 五、标记与测试行为控制 六、标记与测试参数化 七、标记的注册与自定义 1、通过pytest.ini配置文件&#xff1a;…

SpringBoot钩子函数

在Java Spring Boot中&#xff0c;并没有直接称为“钩子函数”的概念&#xff0c;但你可以通过实现特定的接口、注解、事件监听或使用AOP&#xff08;面向切面编程&#xff09;来实现类似的功能。这些功能允许你在应用的特定点插入自定义逻辑&#xff0c;类似于钩子函数的作用。…

c++11详解

目录 1.列表初始化 2.声明 3.右值引用和移动语句 4. c11新的类功能 5. 可变参数模板 6.lambda表达式 7.包装器 8. 后言 1. 列表初始化 1.1 {}的初始化 (1) c98标准规定可以使用{}对数组以及结构体进行统一的列表初始化. struct Point {int _x;int _y; };int main() {in…

Python数据权限的管理通常涉及到几个关键组件:身份验证,、授权和访问控制。这通常是通过使用数据库、ORM(对象关系映射)框架、API框架和中间件

在Python中&#xff0c;数据权限的管理通常涉及到几个关键组件&#xff1a;身份验证&#xff0c;、授权和访问控制。这通常是通过使用数据库、ORM&#xff08;对象关系映射&#xff09;框架、API框架和中间件等技术来实现的。以下是一些建议的步骤和工具&#xff0c;用于在Pyth…

C语言面经

25.类型相同的两个指针之间不能进行的运算 指针主要用于存储变量的内存地址。对于同类型的指针变量之间&#xff0c;有一些规则&#xff1a; a. 小于运算&#xff08;<&#xff09;&#xff1a;指针间的小于比较是基于它们指向的内存地址。地址较小的指针在小于比较中被认为…

【Unity】shader中参数传递

1、前言 unity shader这个对于我来说是真的有点难&#xff0c;今天这篇文章主要还是总结下最近学习到的一些东西&#xff0c;避免过段时间忘记了&#xff0c;可能有不对&#xff0c;欢迎留言纠正。 2、参数传递的两种方式 2.1 语义传递 语义传递这个相对来说是简单的 shad…

Csharp_pta2_2

7-7 C# 1.12 区间找数 编写控制台应用程序&#xff0c;根据用户输入的a、b、c、d值&#xff08;均为正整数且a不大于b&#xff09;&#xff0c;输出在[a, b]区间中能被c整除&#xff0c;但是不能被d整除的数。 输入格式: 用户在一行中输入四个正整数&#xff0c;分别对应a、…

数组模拟几种基本的数据结构

文章目录 数组模拟单链表数组模拟双链表数组实现栈数组模拟队列总结 数组模拟单链表 首先类比结构体存储单链表&#xff0c;我们需要一个存放下一个节点下标的数组&#xff0c;还需要一个存储当前节点的值的数组&#xff0c;其次就是一个int类型的索引&#xff0c;这个索引指向…

Python 实现视频去抖动技术

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 视频去抖动是视频处理中的一项重要技术&#xff0c;它可以有效地减少视频中由于相机震动或手…

springSecurity简单直接说明

引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombo…

MyBatis处理SQL中的特殊字符

方式一&#xff1a;转义字符 如下案例&#xff1a; < 表示小于的转义字符 <!-- 在Mapper XML文件中定义SQL语句 --> <select id"selectById" resultMap"BaseResultMap">select *from userwhere id < #{id}; </select>方式二&am…

设计模式:依赖倒转原则(Dependency Inversion Principle,DIP)介绍

依赖倒转原则&#xff08;Dependency Inversion Principle&#xff0c;DIP&#xff09;是面向对象设计原则之一&#xff0c;它强调高层模块不应该依赖于底层模块&#xff0c;二者都应该依赖于抽象。同时&#xff0c;抽象不应该依赖于具体实现细节&#xff0c;具体实现细节应该依…

嵌入式开发学习--进程、线程

什么是进程 进程和程序的区别 概念 程序&#xff1a;编译好的可执行文件&#xff0c;存放在磁盘上的指令和数据的有序集合&#xff08;文件&#xff09;&#xff0c;程序是静态的&#xff0c;没有任何执行的概念。 进程&#xff1a;一个独立的可调度的任务&#xff0c;执行一…

高可靠性部署系列(3)--- ASG双机热备(HA)

高可靠性部署系列(3)--- ASG双机热备(HA) 前言网络拓扑设备选型网络规划组网需求配置思路操作步骤步骤 1 HA接口管理地址配置步骤 2 HA全局配置步骤 3 配置同步步骤 4 接口状态同步组创建结果验证前言 近期有读者留言:“因华为数通模拟器仅能支持USG6000V的防火墙,无法支…

东方博宜1009 - 数组逆序

题目描述 给你 nn 个整数&#xff0c;将其逆序输出。 输入 第一行一个整数 nn &#xff08;3 \le n \le 1003≤n≤100)代表数的个数。 第二行 nn 个整数&#xff08;空格隔开&#xff09;&#xff08;这些数在 0 \sim 10^60∼106 之间)。 输出 nn 个整数&#xff08;空格…

恶补《操作系统》3_1——王道学习笔记

3内存管理 3.1_1 内存的基础知识 1、什么是内存&#xff0c;作用 &#xff08;1&#xff09;内存&#xff1a;内存用来存放数据。程序执行前需要先放到内存中才能被CPU处理――缓和CPU与硬盘之间的速度矛盾。 &#xff08;2&#xff09;内存存储单元&#xff1a;每个地址对应…