RK DWC3 gadget模块 分析

news2024/9/23 13:21:53

1. dw3 core代码分析

文件:[drivers/usb/dwc3/core.c]

dwc3_probe 函数主要申请dwc3_vendor 参数内存(dwc3_vendor的dwc成员即是 struct dwc3结构体参数),对dwc3 通过设备树 以及寄存器信息对 dwc3的成员进行初始化,申请缓存,创建debugfs文件节点,配置dwc3寄存器 ,依据传输模式配置dwc3

static int dwc3_probe(struct platform_device *pdev)
{
    struct dwc3_vendor  *vdwc;                                                      |   -
    struct dwc3     *dwc;

    vdwc = devm_kzalloc(dev, sizeof(*vdwc), GFP_KERNEL); 
    /* 申请dwc3_vendor内存 */
    
    dwc = &vdwc->dwc; /* 获取到dwc3参数用于后续初始化 */

    regs = devm_ioremap_resource(dev, &dwc_res);
    
    dwc->regs   = regs;                                                             |   -    
    dwc->regs_size  = resource_size(&dwc_res); 
    /* 配置寄存器地址参数 */

    dwc3_get_properties(dwc);    
    /* 通过设备树获取信息进行 dwc3的成员初始化 例如: dr_mode,maximum_speed,max_ssp_rate

    dwc3_cache_hwparams(dwc);
    /* 初始化 dwc->hwparams 用于保存 DWC3_GHWPARAMS 0~9 的寄存器参数 */

    ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
    /* 申请 dwc->ev_buf 内存:dma 与 cache 的内存都在此处申请  */

    ret = dwc3_get_dr_mode(dwc);
    /* 根据dwc3 寄存器 hwparams0 参数对比 设备树获取的参数 dr_mode,不同则以hwparams0 寄存器参 
       数对dr_mode 重新配置 */
    
    dwc3_debugfs_init(dwc);
    /* 创建 dwc3 debugfs 文件节点: regdump,lsp_dump,mode */
    
    ret = dwc3_core_init_mode(dwc);
    /*根据创数模式 对 dwc3进行初始化*/

}

dwc3_core_init_mode(struct dwc3 *dwc) 函数,这模式是 :USB_DR_MODE_PERIPHERAL模式

所以 dwc3_core_init_mode 会调用 dwc3_gadget_init进行初始化

int dwc3_gadget_init(struct dwc3 *dwc)
{
    irq = dwc3_gadget_get_irq(dwc);
    /* 获取中断号配置 dwc->irq_gadget */

    dwc->ep0_trb = dma_alloc_coherent
    /* dma 申请 ep0 trb */

    dwc->setup_buf = kzalloc(DWC3_EP0_SETUP_SIZE, GFP_KERNEL);
    /* 申请 setup_bug 内存 */
    
    dwc->bounce = dma_alloc_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, 
         &dwc->bounce_addr, GFP_KERNEL);
    /* dma 申请  bounce */

    dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);
    /* 申请 usb_gadget */

    usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
    /* 初始化 gadget->dev(device 成员),work,以及dev的父节点配置为dwc->dev */
    dev             = &dwc->gadget->dev;
    dev->platform_data      = dwc;
    /* dwc3 配置为 gadget device 的 platform_data */
    dwc->gadget->ops        = &dwc3_gadget_ops;
    /* 初始化 gadget 成员:ops */
    dwc->gadget->speed      = USB_SPEED_UNKNOWN;
    dwc->gadget->ssp_rate       = USB_SSP_GEN_UNKNOWN;
    dwc->gadget->sg_supported   = true;
    dwc->gadget->name       = "dwc3-gadget";
    dwc->gadget->lpm_capable    = !dwc->usb2_gadget_lpm_disable;

    dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
    /* 初始化 dwc->gadget的 ep_list,依据 dwc->num_eps 数进行循环调用 
       dwc3_gadget_init_endpoint 申请 dwc3_ep内存,配置对应的 regs:寄存器地址
       dirction:方向,number:端点号, pending_list,cancelled_list,started_list链表,
       保存在 dwc->eps中, 依据端口号以及方向
       调用不同接口函数 对 dep->endpoint (usb_endpoint)进行初始化,
       端口0 :
       endpoint ops:dwc3_gadget_ep0_ops, endpoint 添加进 gadget->ep0
       非端口0:
       endpoint ops: dwc3_gadget_ep_ops, endpoint 添加进 gadget->ep_list链表 */
    
    ret = usb_add_gadget(dwc->gadget);
    /* 申请 strut usb_udc 参数内存并对 成员dev( struct device) 初始化:类 udc_class,父设备
       dwc->dev,添加 gadget 设备以及 udc 设备,并且添加进 udc_list中 */
      
    
}

框架图

流程图:

2. composite 层代码分析

这里以 ncm 为例子分析 

文件: [kernel-5.10/drivers/usb/gadget/legacy/ncm.c]

设备描述符:

module_usb_composite_driver 调用 usb_composite_probe函数进行注册,主要是初始化usb_gadget_driver 以及遍历udc_list 与usb_udc进行配对

int usb_composite_probe(struct usb_composite_driver *driver)
{
    driver->gadget_driver = composite_driver_template;
    /* 配置 composite_driver的 gadget_driver */

    gadget_driver->function =  (char *) driver->name;
    gadget_driver->driver.name = driver->name;
    gadget_driver->max_speed = driver->max_speed;
    /* 对gadget_drvier参数进行初始化 */

    return usb_gadget_probe_driver(gadget_driver);
    /* 遍历 udc_list 拿到每个usb_udc 与 gadget_driver 进行匹配
       配对逻辑:
       1. gadget_driver->udc_name不为空则遍历 udc_list 与 usb_udc的dev中kobj->name配对
       2. gadget_driver->udc_name为空 拿到第一个usb_udc的driver为空的(未配对过的)进行配对
    */
}

usb_gadget_probe_driver 配对完后会调用 udc_bind_to_driver 函数,主要 配置 usb_udc参数的

usb_gadget_driver 成员以及回调 usb_gagdget_driver的bind函数,初始化启动dwc3的中断线程以及启动 dwc3 usb gadget

static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
    udc->driver = driver;
    /* udc的driver成员 与 usb_gadget_drvier参数:driver 进行链接 */
    
    udc->gadget->dev.driver = &driver->driver;
    /* gadget->device 的driver 与 gadget_drver的driver成员进行链接 */

    ret = driver->bind(udc->gadget, driver);
    /* 回调 usb_gdget_driver 的bind 函数*/

    ret = usb_gadget_udc_start(udc);
    /* 回调 udc->gadget->ops->udc_start(udc->gadget, udc->driver)
       回调函数:dwc3_gadget_start 主要启动dwc3 中断处理线程:dwc3_thread_interrupt
       配置dwc的gadget_driver */
 
    usb_udc_connect_control(udc);
    /* 通过 usb_gadget_connect 回调 gadget->ops->pullup 即 dwc3_gadget_pullup 
       最终调用 __dwc3_gadget_start 初始化DWC3 USB gadget并启动它 */

    kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
    /* 发送内核事件 KOBJ_CHANGE 通知对象的状态发生了改变 */
}

driver->bind回调:gadget_driver的回调 即运行 composite_driver_template 的bind: composite_bind 主要申请usb_composite_dev 参数内存,把usb_composite_dev与 usb_gadget联系起来,申请 ep0 usb_requset ,回调composite_driver的bind

 static int composite_bind(struct usb_gadget *gadget,struct usb_gadget_driver *gdriver)
{
    struct usb_composite_dev    *cdev;
    struct usb_composite_driver *composite = to_cdriver(gdriver);
    
    cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
    /* 申请 usb_composite_dev 参数内存 */

    cdev->gadget = gadget;
    set_gadget_data(gadget, cdev);
    /* usb_composite_dev 设置为 gadget device的私有数据 */

    status = composite_dev_prepare(composite, cdev);
    /* 主要回调dwc3_gadget_ep_alloc_request 申请 usb_request, usb_request的buf缓存,
       配置urb 的 compelte 回调函数,配置 ep0的driver_data,配置composite_dev的driver成员:
       composite_driver */

    status = composite->bind(cdev);
    /* 回调 composite_driver的 bind函数 */
    
    update_unchanged_dev_desc(&cdev->desc, composite->dev);
    /* composite_driver的 usb 设备描述符(usb_device_descriptor)对 composite_dev的usb
       设备描述符进行初始化 */
}:

composite_driver的bind:gncm_bind 主要是 对composite_dev添加 usb_configuration,对usb_configuration添加usb_function

static int gncm_bind(struct usb_composite_dev *cdev)
{
    struct usb_gadget   *gadget = cdev->gadget;
    struct f_ncm_opts   *ncm_opts;

    f_ncm_inst = usb_get_function_instance("ncm");
    /* 遍历 func_list 通过 name进行配对,配对完成回调 alloc_inst() ,
       初始配置组,获取usb_function_instance 内存 */

    status = usb_add_config(cdev, &ncm_config_driver,
                ncm_do_config);
    /* 把 usb_configuration: ncm_config_driver 添加进 composite_dev的configs链表中,
       回调ncm_do_config 该函数 主要功能:
       
       1.通过 usb_get_function 函数
         用f_ncm_inst 参数 回调 alloc_func 对usb_function 参数进行赋值,function的name,bind
         unbind.setup等会调函数在此处进行赋值

       2.调用usb_add_function 把对应的usb_function 添加进 usb_configuration中,回调function
         bind函数:ncm_bind
     */ 
  
    
}

函数调用图:

框架图:

3. configfs 配置流程分析

结构体参数:

创建目录函数:主要在 usb_gadget目录下生成对应的目录 : UDC,configs, functions 等,以及初始化 usb_composite_driver 以及 usb_composite_dev

static struct config_group *gadgets_make(
        struct config_group *group,
        const char *name)
{

    config_group_init_type_name(&gi->functions_group, "functions",
            &functions_type);
    configfs_add_default_group(&gi->functions_group, &gi->group);

    config_group_init_type_name(&gi->configs_group, "configs",
            &config_desc_type);
    configfs_add_default_group(&gi->configs_group, &gi->group);

    gi->composite.bind = configfs_do_nothing;
    gi->composite.unbind = configfs_do_nothing;
    /* 配置 composite_driver 的 bind,unbind 回调函数 */

    composite_init_dev(&gi->cdev);
    gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
    gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
    gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
    /* 初始化 composite_dev */

    gi->composite.gadget_driver = configfs_driver_template;
    /* 配置 composite_driver 的 usb_gadget_driver参数 */

    gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
    gi->composite.name = gi->composite.gadget_driver.function;

}

配置流程:

例子:

  1. write /config/usb_gadget/g1/UDC "none"   

调用函数:gadget_dev_desc_UDC_store 主要 关闭dwc3的数据传输 关闭 中断,disable ep0等, 代码流程如下

 2.  write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ncm"

     主要调用 config_desc_make 函数初始化usb_configuration:b1,创建 b.1和strings配置组, 以及调用usb_add_config_only 把 usb_configuration 添加进  usb_compsite_dev(gi->cdev)的configs 链表中

configuration 写入 “ncm”,具体的 store函数依据以下的宏定义

GS_STRINGS_RW(gadget_config_name, configuration);

3. mkdir /config/usb_gadget/g1/functions/ncm.gs7

  调用:function_make 函数:主要遍历func_list 找到对应的function_instance,添加进

gi->available_func

static struct config_group *function_make( struct config_group *group,const char *name )
{
    func_name = buf;
    instance_name = strchr(func_name, '.');
    *instance_name = '\0';
    instance_name++;
    /* 分割出func_name:ncm 以及 instance_name:gs7 */    

    fi = usb_get_function_instance(func_name);
    /*遍历 func_list进行配对, 回调alloc_inst 申请 usb_function_instance,
      初始化配置组 */

    ret = config_item_set_name(&fi->group.cg_item, "%s", name);
    /* 配置 ncm function的配置组名: ncm.gs7 */

    list_add_tail(&fi->cfs_list, &gi->available_func);
    /* usb_function_instance 添加进 gadget_info */
}

4. symlink /config/usb_gadget/g1/functions/ncm.gs7 /config/usb_gadget/g1/configs/b.1/f1

调用 config_usb_cfg_link函数: 遍历 gadget_info 的 available_func链表 进行配对,获取

usb_function 添加进 config_usb_cfg 的func_list链表

static int config_usb_cfg_link(
    struct config_item *usb_cfg_ci,
    struct config_item *usb_func_ci)
{
    struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci);
    struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
 
    list_for_each_entry(a_fi, &gi->available_func, cfs_list) {
        if (a_fi == fi)
            break;
    }
    /*遍历 available_func list 匹配对应的 usb_function_instance */

    f = usb_get_function(fi);
    /* 获取 usb_function */
    list_add_tail(&f->list, &cfg->func_list);
    /* 添加进 config_usb_cfg cfg 的 func_list */
}

5. write /config/usb_gadget/g1/UDC ${sys.usb.controller}

调用 gadget_dev_desc_UDC_store

static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
 const char *page, size_t len)
{
    gi->composite.gadget_driver.udc_name = name;
    ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);
    
    /* 配置 udc_name 遍历 udc_list进行配对,回调 udc_bind_to_driver 对 
    usb_udc与 usb_gadget_driver进行绑定, 回调gadgdget_driver: 
    configfs_driver_template的 bind函数:configfs_composite_bind */
}

static int configfs_composite_bind(struct usb_gadget *gadget,
  struct usb_gadget_driver *gdriver)
{
    struct usb_composite_dev    *cdev = &gi->cdev;

    cdev->gadget = gadget;
    ret = composite_dev_prepare(composite, cdev);
    /* composite_dev与 usb_gadget 绑定,composite_dev 与composite_driver绑定,
       申请 usb_request, requst 缓存 */

    list_for_each_entry_safe(f, tmp, &cfg->func_list, list) {
        list_del(&f->list);
        ret = usb_add_function(c, f);
    }
    /* 遍历 function_list的usb_function 从function_list移除, 
       调用 usb_add_function 把 function 添加进 usb_configuration中 */

    usb_ep_autoconfig_reset(cdev->gadget) 
    /*复位 gadget*/

}

5个步骤 组合起来 注册 config,注册 function 与 udc_list 的gadge配对 搭建框架如下图

流程图 :号码对应上面的步骤

4.小结

1 dwc3 和dwc3_ep 负责最终的数据传数

2 dwc3配置 usb_gadget 以及 usb_udc

3 usb_udc 负责usb_gadget 与usb_gadget_driver配对

4 composite 端 主要是构建 usb_configuration以及usb_function

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

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

相关文章

cloudstack平台host加入后,显示CPU speed为0GHz

一、环境说明 操作系统:openEuler 22.03CPU:Kunpeng-920,arm v8cloudstack:4.18libvirtd:6.2.0 二、问题描述 cloudstack平台初始化完成后,第一次加入host,系统虚拟机一直无法正常创建&#…

瑞吉外卖项目----(2)缓存优化

1 缓存优化 1.0 问题说明 1.1 环境搭建 将项目推送到远程仓库里,教程在git 提交远程仓库前建议取消代码检查 创建新的分支v1.0(用于实现缓存优化)并推送到远程仓库 1.1.1 maven坐标 导入spring-data-redis的maven坐标: &l…

PyTorch代码实战入门

人这辈子千万不要马虎两件事 一是找对爱人、二是选对事业 因为太阳升起时要投身事业 太阳落山时要与爱人相拥 一、准备数据集 蚂蚁蜜蜂数据集 蚂蚁蜜蜂的图片,文件名就是数据的label 二、使用Dataset加载数据 打开pycharm,选择Anaconda创建的pytorch环…

FTP Server

简介 FTP:File Transfer Protocol 文件传输协议;它工作在 OSI 模型的第七层, TCP 模型的第四层, 即应用层, 使用 TCP 传输而不是 UDP, 客户在和服务器建立连接前要经过一个“三次握手”的过程,…

捷码低代码|FreeContainer 自由布局组件详解

背景知识: 1、布局组件: 布局组件是一种用于在用户界面中安排和组织其他组件的组件。它们提供了一种简单的方法来控制和管理页面上组件的位置、大小和层次结构。布局组件可以是容器,可以包含其他组件,并确定它们在界面上的显示方式…

【MyBatis】 框架原理

目录 10.3【MyBatis】 框架原理 10.3.1 【MyBatis】 整体架构 10.3.2 【MyBatis】 运行原理 10.4 【MyBatis】 核心组件的生命周期 10.4.1 SqlSessionFactoryBuilder 10.4.2 SqlSessionFactory 10.4.3 SqlSession 10.4.4 Mapper Instances 与 Hibernate 框架相比&#…

深入理解MVVM架构模式

MVVM原理 MVVM是一种用于构建用户界面的软件架构模式,它的名称代表着三个组成部分:Model(模型)、View(视图)和ViewModel(视图模型)。MVVM的主要目标是将应用程序的UI与其底层数据模…

SERDES关键技术

目录 一、SERDES介绍 二、SERDES关键技术 2.1 多重相位技术 2.2 线路编解码技术 2.2.1 8B/10B编解码 2.2.2 控制字符(Control Characters) 2.2.3 Comma检测 2.2.4 扰码(Scrambling) 2.2.5 4B/5B与64B/66B编解码技术 2.3 包传…

Halcon学习之一维测量实战之测量矩形(一)

一、采集图像 (1)测量充电器 测量充电器的引脚,然后每次旋转充电器,让测量矩形都跟着它转,这就是定位+测量, (2)测量钥匙 (3)测量瓶盖 我们后面还会涉及到拟合的问

牛客网Verilog刷题——VL53

牛客网Verilog刷题——VL53 题目答案 题目 设计一个单端口RAM,它有: 写接口,读接口,地址接口,时钟接口和复位;存储宽度是4位,深度128。注意rst为低电平复位。模块的接口示意图如下。 输入输出描…

HDFS的QJM方案

Quorum Journal Manager仲裁日志管理器 介绍主备切换,脑裂问题解决---ZKFailoverController(zkfc)主备切换,脑裂问题解决-- Fencing(隔离)机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…

解决git仓库无效问题

解决fatal: … not valid: is this a git repository?问题 凭证编辑修改成自己的账号密码即可解决

2023年第四届“华数杯”数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集身份认证…

LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 论文阅读

论文信息 题目:LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 作者:Devendra Singh Chaplot, Dhiraj Gandhi 项目地址:https://devendrachaplot.github.io/projects/Neural-SLAM 代码地址:https://github.com/devendrachaplot/N…

python 统计所有的 仓库 提交者的提交次数

字典去重 YYDS 然后再写入excel 表 yyds #!/bin/env python3 from git.repo import Repo import os import pandas as pdspath "/home/labstation/workqueue/sw" url "git10.0.128.128" date [str(x) for x in range(202307, 202308)] datefmt "%…

用html+javascript打造公文一键排版系统11:改进单一附件说明排版

一、用htmljavascript打造公文一键排版系统10中的一个bug 在 用htmljavascript打造公文一键排版系统10:单一附件说明排版 中,我们对附件说明的排版函数是: function setAtttDescFmt(p) {var t p;var a ;if (-1 ! t.indexOf(:))//是半角冒…

SQL注入之sqlmap

SQL注入之sqlmap 6.1 SQL注入之sqlmap安装 sqlmap简介: sqlmap是一个自动化的SQL注入工具,其主要功能是扫描,发现并利用给定的URL的SQL注入漏洞,目前支持的数据库是MS-SQL,MYSQL,ORACLE和POSTGRESQL。SQLMAP采用四种独特的SQL注…

Moonbeam:开发者的多链教科书

了解波卡的技术架构,只需掌握3个关键词: Relay Chain(中继链):Polkadot将自身视作多核计算机,承载区块链底层安全架构的辐射中心。Parachain(平行链):在“Layer 0”架构…

现货白银投资中的头寸是什么

头寸是现货白银市场上的一个投资术语。建立头寸就是建仓的意思,投资者所持有的头寸也叫敞口。投资如果看涨做多,就是持有多头头寸,如果看跌做空,就持有空头头寸。计算交易的头寸的大小并不复杂,关键是在于投资者要设定…

Linux(New)---历史与虚拟机安装CentOS7.6

前言 其实之前已经学过一遍Linux了,但是感觉学的不够深入和成体系(某节的教学视频不完整),所以这次打算完整的跟一遍韩顺平老师的Linux课程,Linux从入门到精通,就从现在开始! Linux历史概述 L…