g_zero 代码分析

g_zero 代码分析

g_zero.ko是USB Device端代码的一个驱动,具备两个功能:loopbacksourcesink,使用bulk的端点进行传输,作者写这个驱动的目的是用来测试UDC驱动的,但我们也可以在这个驱动的基础进行修改,以实现一些我们自己的功能,比如生成字符设备,提供ioctl接口、mmap接口等,以实现与host端的主动发起交互。

当前内核版本linux-4.9.37

usb_f_ss_lb.ko模块

这个模块包含两个function,分别是f_sourcesinkf_loopback,接下来就从初始化的部分分别看这两个代码。

f_sourcesink.c

初始化模块

先看一下模块初始化的部分,注册一个SourceSink功能,如果成功,再调用lb_modinit()进行loopback功能的初始化。

在这里插入图片描述

注册的过程也相当简单,usb_function_register()函数判断一下是否以及注册,没有注册的话就加到链表后,就表面注册成功了。

在这里插入图片描述

声明USB功能

初始化过程使用到的SourceSinkusb_func是在下图所在的DECLARE_USB_FUNCTION()定义的,同时指定了实例申请和功能申请的函数。

在这里插入图片描述

声明的过程也是比较简单,就是一个赋值的宏:

在这里插入图片描述

struce f_ss_opts结构体值得关注的目前只有里面的功能实例:

在这里插入图片描述

看一下source_sink_free_instance()的过程,就是释放申请的struct f_ss_opts内存。

在这里插入图片描述

再回过头看一下source_sink_alloc_func()函数,里面主要关注的是一个function的赋值过程。

在这里插入图片描述

再看一下里面的struce f_sourcesink结构体,里面其实主要注意的是IN/OUT类型的端点描述。

IN类型传输即数据从 Device 传输到 Host,Host端发出应答。

OUT类型传输即数据从 Host 传输到 Device,Device 端发出应答。

如果需要实现自己的字符设备,那么就要注意了,对于Device端,Device端从用户空间写数据到内核空间,然后驱动通过IN端点发送出去给Host;内核通过OUT端点,从Host端读到数据后,再拷贝到用户空间,即用户空间的一个读过程。这个与Host代码是有区别的,只要搞清楚这个IN/OUT方向的描述即可。
在这里插入图片描述

如下图所示:
在这里插入图片描述
在这里插入图片描述

function之setup

其实setup的内容大部分都由libcomposite.ko模块进行了处理了,这里就剩下两个测试的请求,最后通过ep0端点发起请求,高速Host端结果即可。关于setup的过程还需要对比一下USB协议,这里就暂时先略过了。

在这里插入图片描述

在这里插入图片描述

function之bind

sourcesink_bind()对两个配置的接口ID进行赋值,申请端点后,分别对全速、高速、超高速的端点参数进行修正,最后将端点描述符赋给function

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

看一下全速下的描述符:

在这里插入图片描述

高速描述符:与全速描述符有些差异,BULK端点限定了最多传输大小512字节,ISOC端点最大传输大小限定为1024字节,为什么端点没有了传输描述方向的,我猜想应该是bind过程中端点赋值了之后可以省略这个过程,而全速端点在申请时必须附带上方向。

在这里插入图片描述

超高速描述符:与高速与全速不同,除了BULK端点限制变为1024字节,每一个端点过后还多了一个usb_ss_ep_comp_descriptor结构体的描述。

在这里插入图片描述

在这里插入图片描述

function之get_alt, set_alt, disable

这三个函数比较简短,放在一起介绍。sourcesink_get_alt()函数返回当前的配置选择,上面bind的时候以及端点描述符数组里面都可以看到有两个配置,但一般默认情况是配置0,而配置1是需要有isoc相关端点的。sourcesink_set_alt()自然就是用来选择不同的配置的。sourcesink_disable()用于失能相关的功能,把端点disable之后就无法进行传输了。

在这里插入图片描述

看一下disable_source_sink()函数,就是禁用所有的端点。

在这里插入图片描述

disable_endpoints()就是对传入的参数进行了一下判断,继续调用disable_ep()

在这里插入图片描述

disable_ep()则是调用标准的udccore里面提供的接口,禁用单个端点。

在这里插入图片描述

udc提供的标准接口,最后会执行对应的控制器的代码,当前控制器是dwc3,暂不展开。

在这里插入图片描述

使能端点

然后看一下在set_alt中用到的enable_source_sink()函数,根据控制器最后协商的结果来配置IN/OUT端点,以及ISOC的两个端点(如果配置是1的话)的速度以及传输大小等数据,并调用source_sink_start_ep()来申请端点描述符,发起传输等。

在这里插入图片描述

在这里插入图片描述

看一下source_sink_start_ep()的过程,根据不同的端点类型申请设置不同的传输大小,并申请端点传输请求以及传输buffer大小,设置回调函数并发起请求。至于里面的pattern,是里面用来测试的,自己修改代码或者编写代码时可以不用管这个。如果是想要实现自己的字符设备,这个source_sink_start_ep()的过程可以省略掉。

在这里插入图片描述

在这里插入图片描述

ss_alloc_ep_req()就是封装了一下udc的标准接口。

在这里插入图片描述

alloc_ep_req()申请端点请求以及传输的buffer,OUT端点传输长度需要进行对齐,否则Host端会报协议错误。usb_ep_alloc_request()最后也是调用dwc3里面的申请端点,这里就不展开了。如果是想在从端编写字符设备,并且将速度进行优化,那么申请内存的部分可以用mmap替代,这样也可以省略从用户空间拷贝的过程。

在这里插入图片描述

发起端点传输

可以看到从端的发起传输是通过usb_ep_queue()函数来发起的,在使用dwc3控制器时,执行dwc3_gadget_ep_queue()函数,这个函数可以展开一下,里面有可以进行修改优化的过程。

在这里插入图片描述

这里就简单地放一张图来描述一下,因为申请的内存是通过kmalloc申请过来的,所以直接线性映射后是可以得到物理地址的。而MMZ的内存不在内核管理,如果想要直接使用MMZ的物理内存,可以先使用ioremap映射到高端地址,再进行操作;或者想直接用USB传输的,可以直接把MMZ的物理地址交给USB控制器,也可以正常完成传输。

在这里插入图片描述

回调函数

再看一下回调函数做了什么操作,主要就是一个完成之后里面再发起请求。

在这里插入图片描述

在这里插入图片描述

function之free

这个free的过程就是减少对应的function的实例引用,释放所有端点,以及申请的结构体内存。

在这里插入图片描述

f_loopback.c

经过上面的f_sourcesink.c的分析,其实大部分大同小异,这里就不花太多篇幅去描述了。

初始化模块

注册一个Loopback功能。

在这里插入图片描述

声明USB功能

初始化过程使用到的Loopback_func是在下图所在的DECLARE_USB_FUNCTION()定义的,同时指定了实例申请和功能申请的函数。

在这里插入图片描述

在这里插入图片描述

function的功能

功能使能、禁止以及选项配置:

在这里插入图片描述

申请请求:

在这里插入图片描述

完成回调:

在这里插入图片描述

g_zero.ko 模块

g_zero模块是是属于Gadget 功能驱动层,本身具有两个function,分别是LOOPBACK functionSOURCESINK function

先看一些注册驱动的函数以及对应的zero_driver驱动结构体内容:

驱动注册

在这里插入图片描述

看一下设备描述符:

在这里插入图片描述

usb_composite_driver定义如下:

在这里插入图片描述

zero_driverzero_bind()函数的被调用过程会经过如下步骤:

在这里插入图片描述

composite_bind()里面会申请一个usb_composite_dev结构体,用于保存设备的相关信息:

在这里插入图片描述

设备绑定

在进到zero_bind()函数里面仔细看一下做了哪些事情。

1- 设置定时器用于自动挂起,获取 SourceSink 功能实例,设置 SourceSink 参数,从 SourceSink 实例中申请 function。

在这里插入图片描述

2- 获取 Loopback 功能实例,设置 Loopback 参数,从Loopback 实例中申请 function。

在这里插入图片描述

3- 将配置增加到设备中,并将function添加到配置。

在这里插入图片描述

如果要想在从端生成字符设备,最好在bind()这里生成,然后操作之前,需要进行一下判断端点是否使能,使能之后再进行操作。

zero_bind()里面用到的usb_configuration是保存设备的配置相关的信息:

在这里插入图片描述

解绑过程就是绑定过程的逆操作,释放相应的资源。

在这里插入图片描述