当前位置:网站首页>usb设备复合g_webcam摄像头码流传输功能以及g_serial串口功能
usb设备复合g_webcam摄像头码流传输功能以及g_serial串口功能
2022-08-04 09:40:00 【侃侃普奇瑞瑞亚】
0.为什么要复合?因为usb驱动里的UDC(USB设备控制器)数量不够,会报错 udc资源已占用。所以使用insmod、modprobe等加载模块的方式一次只能有一个功能(内核定值好的三功能模块g_multi.ko除外),所以需要使用另一种方式USB Gadget Configfs
1.usb大框架组成:主要由udc,gadget,composite组成。编写的关键在于了解udc<-gadget<-composite三者之间的联系和架构。一个usb device在这里就是webcam设备有且仅有一个设备描述符,有一个或者多个配置描述符在,但这个复合当中只存在一个配置。其中udc是上层。
---------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------
上层调用结构体usb_composite_driver(webcam_config_bind,(结构体 usb_composite_descriptor,bind,unbind))其中:
bind包括各类型的复合功能例如(rndis_bind,dfu_bind)等。
usb_composite_driver结构体有且只有一个描述符结构体usb_composite_descriptor
2.结构体usb_composite_driver webcam_driver
上层接口结构体就是这个
static struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
.max_speed = USB_SPEED_SUPER,
.bind = webcam_bind,
.unbind = webcam_unbind,
};
module_usb_composite_driver(webcam_driver);
这个结构体其中有绑定webcam_device_descriptor设备描述符,以及bind函数,unbind函数。在webcam中这两个函数叫做webcam_bind以及webcam_unbind。我在这里主要修改了这两个函数中的子函数。上层调用这个.c文件的主要API接口就是init()中引用的这个usb_composite_driver 结构体中的webcam_bind函数。先从composite层开始往上层走。因为一般gadget是不需要我们进行编写的。在这个webcam设备层面对上的接口主要是这个usb_composite_driver结构体以及module_usb_composite_driver驱动函数,它主要是将此设复合设备注册到上层gadget形成一个模块。设备描述符结构体见下
static struct usb_device_descriptor webcam_device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
/* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = 0x02,
.bDeviceProtocol = 0x01,
.bMaxPacketSize0 = 0, /* dynamic */
.idVendor = cpu_to_le16(WEBCAM_VENDOR_ID),
.idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID),
.bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD),
.iManufacturer = 0, /* dynamic */
.iProduct = 0, /* dynamic */
.iSerialNumber = 0, /* dynamic */
.bNumConfigurations = 0, /* dynamic */
};
3.__init webcam_bind函数
正如上说的上层gadget绑定webcam的地方在于函数__init webcam_bind(),webcam_bind中主要实现将多个功能或者说配置绑定在一起暴露给上层调用。里面已经复合了RNDIS通过usb网络传输功能,UAC编码流设置等功能。这个函数的参数是在usb_gadet层传入的usb_composite_dev结构体,这个不用我们去管。我们需要操作的是结构体webcam_config_driver中的bind函数(部分见下),他的类型是唯一一个结构体usb_configuration(见下)以及函数webcam_bind_config。
if ((ret = usb_add_config(cdev, &webcam_config_driver,
webcam_bind_config)) < 0)
{
goto error;
}
usb_composite_overwrite_options(cdev, &coverwrite);
static struct usb_configuration webcam_config_driver = {
.label = webcam_config_label,
.bConfigurationValue = 1,
.iConfiguration = 0, /* dynamic */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
4.其中我复合的特殊之处在于serial.c,其中的gs_bind函数以及bind_config函数被整合在一起了,其余功能应该是拆分成bind以及config_bind两个部分的。整好适配上框架的init()层级中的两个部分。原因是在init webcam_bind函数中参数为usb_composite_dev结构体,而webcam_bind_config的参数又是usb_configration结构体,但是上层调用函数是采用回调函数的模式来采用的,传入的参数是固定的。所以尽量不去修改上层调用函数的参数。
if ((ret = usb_add_config(cdev, &webcam_config_driver,
webcam_bind_config)) < 0)
{
goto error;
}
static int serial_register_ports(struct usb_composite_dev *cdev,
struct usb_configuration *c, const char *f_name)
{
int i;
int ret;
ret = usb_add_config_only(cdev, c);
if (ret)
goto out;
for (i = 0; i < n_ports; i++) {
fi_serial[i] = usb_get_function_instance(f_name);
if (IS_ERR(fi_serial[i])) {
ret = PTR_ERR(fi_serial[i]);
goto fail;
}
f_serial[i] = usb_get_function(fi_serial[i]);
if (IS_ERR(f_serial[i])) {
ret = PTR_ERR(f_serial[i]);
goto err_get_func;
}
ret = usb_add_function(c, f_serial[i]);
if (ret)
goto err_add_func;
}
return 0;
static int __init serial_bind_config(struct usb_configuration *c){
int i;
int ret;
for (i = 0; i < n_ports; i++) {
f_serial[i] = usb_get_function(fi_serial[i]);
if (IS_ERR(f_serial[i])) {
ret = PTR_ERR(f_serial[i]);
return ret;
}
ret = usb_add_function(c, f_serial[i]);
if (ret)
return ret;
}
return 0;
}
static int __init serial_bind(struct usb_composite_dev *cdev){
int i;
int ret;
for (i = 0; i < n_ports; i++) {
fi_serial[i] = usb_get_function_instance("acm");
if (IS_ERR(fi_serial[i])) {
ret = PTR_ERR(fi_serial[i]);
return ret;
}
}
return 0;
}
static int __exit serial_unbind(struct usb_composite_dev *cdev){
int i;
for(i = 0; i < n_ports; i++){
if (!IS_ERR_OR_NULL(f_serial[i]))
{
usb_put_function(f_serial[i]);
f_serial[i] = NULL;
}
if (!IS_ERR_OR_NULL(f_serial[i]))
{
usb_put_function_instance(fi_serial[i]);
fi_serial[i] = NULL;
}
}
return 0;
}
5.那么在serial.c那边需要我们移植什么代码过来呢。因为结构体usb_composite_driver,usb_composite_descriptor,usb_configuration在legacy设备功能驱动层有且唯一,所以我们需要做的就是将依赖的头文件还有模块参数传递宏定义函数转移过来。因为我们使用的acm驱动的将usb看做串口设备加载的模块,所以甚至不需要serial.c中携带的obex的驱动使能。注意三个地方:一个是CONFIG_USB_G_SERIAL_MODULE预编译,以及static int serial_function_enable = 1使能参数,还有f_serial[]端点结构体数组。其中预编译不是空穴来风。是在你在sdk编译内核,勾选menuconfig中选择了serial gadget(acm)功能后才会在autoconf.h中才存在的参数。这个使能参数纯粹是我自己加上的一个参数。用来宏观控制是否打开这个功能。其中n_ports是端口数,这个由系统获取,默认1个此处。端点结构体数组如下
#if defined(CONFIG_USB_G_SERIAL_MODULE)
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include "u_serial.h"
static bool use_acm = true;
static int serial_function_enable = 1;
module_param(use_acm, bool, 0);
MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
static unsigned n_ports = 1;
module_param(n_ports, uint, 0);
MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
#endif
#if defined(CONFIG_USB_G_SERIAL_MODULE)
static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];//必要的
static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
#endif
6.编译内核出u_f_acm.ko,以及g_webcam.ko,并且加载后,可以看到从设备存在ttyGS0以及video0两个节点。这两个节点一个就是usb转串口设备文件,一个就是用于码流数据传输的设备文件,分别来自我们整合的serial.c以及webcam.c。当然每家芯片都是不一样的,一个模块需要依赖哪些模块我们需要去查芯片手册才行。本篇文章主要是理清一下usb设备符合的思路。
边栏推荐
- leetcode动态规划系列(求路径篇)
- 思想茶叶蛋 (Jul 31,2022)| 元宇宙(Metaverse)下了一枚什么样的蛋
- Shell编程的条件语句
- 有坦荡的远方
- Inheritance and the static keyword
- How Oracle for current library or certain library data on the same server number?
- redis解决分布式session问题
- Detailed Explanation of Addresses Delivered by DHCP on Routing/Layer 3 Switches [Huawei eNSP]
- leetcode经典例题——49.字母异位词分组
- TiDB升级与案例分享(TiDB v4.0.1 → v5.4.1)
猜你喜欢
随机推荐
LVGL's multi-language conversion tool -- a good assistant for font settings
参数优化。
How Oracle for current library or certain library data on the same server number?
TiFlash 源码阅读(五) DeltaTree 存储引擎设计及实现分析 - Part 2
VRRP + MSTP configuration, huawei eNSP experiment 】 【
ansible部署脚本--亲测可用无坑
After four years of outsourcing, the autumn recruits finally landed
学习在微信小程序中判断url的文件后缀格式
去掉js代码文件所有注释
TiDB升级与案例分享(TiDB v4.0.1 → v5.4.1)
字符串相关题目
leetcode二叉树系列(二)
leetcode二叉树系列(二叉搜索树篇)
【C补充】指针相关知识点收集01
MindSpore:MindSpore GPU版本安装问题
IDEA 自动导入的配置(Auto import)
canvas画图时的bug记录
关于技术学习的6个观点
NAT/NAPT地址转换(内外网通信)技术详解【华为eNSP】
罗克韦尔AB PLC RSLogix5000中定时器指令使用方法介绍