1. Gadget驱动

1.1 Gadget框架结构

kernel/drivers/usb/gadget,这个目录是android下usb gadget的主要目录。
(1)Gadget功能组织单元:主要文件android.c,usb gadget功能的统领文件,负责组织usb 复合设备的功能,与上层应用提供交互的接口,面向市场需求的产品规划部门。

(2)复合设备逻辑处理单元(复合设备管理单元):composite.c,这个文件类似于一个项目管理组,负责各个单元的接口对接,资源整理。针对拥有多个usb功能的复合设备,这部分负责将支持的各个功能组织到一起,协助各个功能与UDC控制器单元建立联系。

流程:android.c init-- usb_composite_probe--usb_gadget_probe_driver---composite_bind---
composite_gadget_bind(android_bind)

(3)具体功能单元:以U盘为例,f_mass_storge.c文件,用来完成具体的功能。这个部分是一个功能性很强的部分,将与UDC控制器单元直接对话,完成数据的传输。

(4)UDC控制器单元:只做一件事情,收发usb数据,将数据透明的传递出去。

1.2 android.c

Android USB驱动中,上层应用协议里最重要的一个文件是android/kernel/drivers/usb/gadget/android.c。这个文件实现USB的上层应用协议。
在我们USB配置文件(system/core/usb/compositions/)中,直接往驱动在上层的接口中写值。

run_9607() {
    if [ $from_adb = "n" ]
    then
        pkill adbd
    fi
    echo 0 > /sys/class/android_usb/android$num/enable
    echo 8241 > /sys/class/android_usb/android$num/idProduct
    echo 2949 > /sys/class/android_usb/android$num/idVendor
    echo diag > /sys/class/android_usb/android$num/f_diag/clients
    echo smd,tty,tty > /sys/class/android_usb/android$num/f_serial/transports
    echo rndis,serial,diag > /sys/class/android_usb/android$num/functions 
    echo 1 > /sys/class/android_usb/android$num/remote_wakeup
    echo 1 > /sys/class/android_usb/android$num/f_rndis_qc/wceis
    sleep $delay
    echo 1 > /sys/class/android_usb/android$num/enable
    if [ $from_adb = "n" ]
    then
        /etc/init.d/adbd start
    fi
}

在上层*/sys/class/android_usb/android0*中可以看到如下接口:
在这里插入图片描述

1.2.1 android.c 功能代码解析

kernel/drivers/usb/gadget/android.c
(1)会调用到具体的function驱动,如下将调用f_serial.c

gserial_init_port()	gport_setup()

在这里插入图片描述
在这里插入图片描述
(2)android_init_functions()函数中会为function创建一个名为 f_xxx的sys class,比如说这里用到的是adb的function,那么名字就为 f_adb。因此可以在*/sys/class/android_usb/android0/看到f_adb*的目录。
在这里插入图片描述

(3)跳转执行adb驱动f_fs.c
在这里插入图片描述
在这里插入图片描述

(4)在android_bind()初始化USB设备描述符并分配string ID.
在这里插入图片描述

(5)添加functions到list中
这一步需要用户手动配置,因为我们是使用adb的功能,因此需要输入以下命令:

#echo adb > /sys/class/android_usb/android0/functions

对应的驱动接口是 functions_store()函数,这个函数的内容是先解析传进来的字符串,然后调用android_enable_function(),根据解析出来的字符串是否与之前 android_init_functions()中设定的名字(也就是该驱动现在支持哪些function)是否有相匹配的,如果有,那么就将名字添加到enabled_list中。
在这里插入图片描述

(6)使能androidadb

#echo 1 > /sys/class/android_usb/android0/enable

对应的驱动接口是enable_store(),在这个函数里面会去完善USB设备描述符,然后从enabled_list中找出与之对应的struct android_usb_function,并执行它的.enable()成员函数,这里当然是会去执行adb_android_function_enable()函数。注意,在adb_android_function_enable()函数中会去先执行一次android_disable()。执行完这一步还没绑定USB configuration和USB function。

(7)启动adbd进程
启动adbd进程,那么首先会去调用adb_open(),最终会调用usb_add_config()adb_bind_config()函数将USB configurationUSB function绑定起来。此时,这个USB设备已经配置完整了,可以接受USB Host的枚举。
f_fs.c
在这里插入图片描述

1.3 f_serial

在较新的版本中,通过USB gadget驱动程序与SMD层进行交互。这为QTI的设计提供了另一个变化,它引入了u_smd 驱动程序。
在这里插入图片描述

初始化流程与以前的实现类似。

1.3.1 gport_setup

SMD端口是在gport_setup()调用gsmd_setup()时分配的。
在这里插入图片描述

(1)gport_setup():两个tty_ports(NMEA和AT口)

[   22.709616] water_usb: line = 342, gport_setup
[   22.723022] water_usb: line = 342, gport_setup

在这里插入图片描述

(2)gsmd_setup():一个smd_ports(modem口)

[   22.735678] water_usb: line = 998, gsmd_setup

在这里插入图片描述
在这里插入图片描述

注意:此处调用platform_driver_register()注册gsmd驱动,并执行其probe函数gsmd_ch_probe

1.3.2 gser_alloc

在这里插入图片描述

(1)三个serial端口调用三次函数去分配端口,当前只支持三个端口:

端口0——modem口
端口1——NMEA口
端口2——modem2(因该是指AT口)

在这里插入图片描述
(2)gser_bind()
在这里插入图片描述
(3)gser_set_alt()
在这里插入图片描述
f_serial驱动程序从主机接收到SET_INTERFACE数据包后,将调用gport_connect(),它将根据所选内容打开传输。在本例中,它调用gsmd_connect()
在这里插入图片描述
gsmd_connect()然后负责使能USB端点和局部变量,并延时去打开SMD通道,打开SMD通道的函数是gsmd_connect_work()
smd_port
在这里插入图片描述
tty_port
在这里插入图片描述
(4)gsmd_connect和gser_connect
一个smd port:

[   23.431556] water_usb: line = 603, gsmd_connect_work

两个tty port:

[   23.526257] gs_open: start ttyGS1
[   23.529155] water_usb: line = 816, gser_connect
[   23.533056] gs_open: ttyGS1 (cb24a400,cb15ca80)
[   23.537564] water_usb: gs_open: ttyGS1 (cb24a400,cb15ca80)
[   23.545823] gs_open: start ttyGS0
[   23.548703] water_usb: line = 816, gser_connect
[   23.552618] gs_open: ttyGS0 (cb24a000,cb15c600)
[   23.557133] water_usb: gs_open: ttyGS0 (cb24a000,cb15c600)

2、Host:usb_serial 驱动

2.1 bus.c

(1)在usb.c中的usb_init()函数会调用bus_register(&usb_bus_type)注册一条USB总线。
(2)在usb.c中会在这条总线上注册一个驱动程序:

usb_generic_driver{
      		generic_probe()
     		device_id
}

在这个驱动中包含自己的probe函数和USB设备ID(注意,这里是设备ID而不是接口的ID)。

2.2 usb_serial.c

2.2.1 入口函数

module_init(usb_serial_init);

2.2.2 usb_serial_init()

1)按照 tty 驱动结构,先创建 tty_driver对象

usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);

2)注册USB转串口总线。会出现在:/sys/bus/usb-serial,其下挂载的驱动(/sys/bus/usb-serial/drivers)包括:generic、option1、sprd_u2s等。

result = bus_register(&usb_serial_bus_type);	

3)向内核注册tty类USB转串口驱动,并在USB转串口总线上添加这个驱动

tty_register_driver(usb_serial_tty_driver)

4)向内核注册USB转串口驱动,该驱动挂载在USB总线下:

usb_register(&usb_serial_driver);

/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
	.name =		"usbserial",
	.probe =		usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.suspend =	usb_serial_suspend,
	.resume =	usb_serial_resume,
	.no_dynamic_id = 	1,
};

5)向内核注册通用的USB转串口驱动。

usb_serial_generic_register(debug);

2.2.3 usb_serial_probe()

int usb_serial_probe(struct usb_interface *interface,
			       const struct usb_device_id *id)
{
	. . .
	/* register all of the individual ports with the driver core */
	for (i = 0; i < num_ports; ++i) {
		port = serial->port[i];
		dev_set_name(&port->dev, "ttyUSB%d", port->number);
		dbg ("%s - registering %s", __func__, dev_name(&port->dev));
		port->dev_state = PORT_REGISTERING;
		device_enable_async_suspend(&port->dev);

		retval = device_add(&port->dev);
		if (retval) {
			dev_err(&port->dev, "Error registering port device, "
				"continuing\n");
			port->dev_state = PORT_UNREGISTERED;
		} else {
			port->dev_state = PORT_REGISTERED;
		}
	}
	. . .
}

2.3 option.c

该驱动挂载在usb-serial总线下,实现USB接口(端口),添加PID、VID在此文件中实现。
(1)option.c中的option_init()函数调用:

static struct usb_serial_driver option_1port_device = {
	.driver = {
		.owner =	THIS_MODULE,
		.name =		"option1",
	},
	.description       = "GSM modem (1-port)",
	.id_table          = option_ids,
	.num_ports         = 1,
	.probe             = option_probe,
    ......
};

static struct usb_serial_driver * const serial_drivers[] = {
	&option_1port_device, NULL
};

module_usb_serial_driver(serial_drivers, option_ids);

(2)option.c在USB转串口总线上注册驱动option_1port_device(注意,这仅仅是在总线上注册,并不向内核注册)。

module_usb_serial_driver(serial_drivers, option_ids);

到这里,总线和驱动都已经注册完毕了,就等着设备过来了

2.4 generic.c

驱动generic挂载在usb-serial总线下。

static struct usb_serial_driver usb_serial_generic_device = {
	.driver = {
		.owner =	THIS_MODULE,
		.name =		"generic",
	},
	.id_table =		generic_device_ids,
	.probe =		usb_serial_generic_probe,
	.calc_num_ports =	usb_serial_generic_calc_num_ports,
	.throttle =		usb_serial_generic_throttle,
	.unthrottle =		usb_serial_generic_unthrottle,
	.resume =		usb_serial_generic_resume,
};

注册 generic_driver 驱动主要是为了注册一个 generic_probe 函数,而该
函数将会在设备连上系统后被调用以来匹配设备。除此之外该驱动没什么用,而在这个初始化函数中把 vendor,product 都保存在了 generic_device_ids 里, 因此可以肯定以后的匹配将用这个设备列表.

2.5 设备

2.5.1 从设备插入到进入自己的probe函数——usb_serial_probe()的过程

(1)当我们的USB Modem设备插入USB端口时,要调用bus_add_device()在USB总线上添加一个USB设备。

(2)该USB设备由于有USB设备号,会找到刚才注册的usb_generic_driver()中的generic_probe()函数,在这个函数中经过一系列的函数调用最后会进入usb_set_configuration()

(3)usb_set_configuration()函数会根据HOST和Device沟通的情况,进行总线枚举,这个我们的设备会生成3个interface,该函数会依次将这三个interface添加到USB总线上。

(4)每个interface会根据VID和PID找到合适自己的probe函数,设备的三个接口会依次进入usb_serial_probe()

2.5.2 从进入自己的probe到虚拟出ttyUSB设备

(1)在usb_serial_probe()中,首先生成三个usb_serial_port,port1,port2,port3。接着调用device_add()函数——调用tty_register_device()-调用device_create()

(2)tty_register_device()函数主要做了三件事:
1)向系统注册这三个串口设备。
2)将串口设备,次设备号,串口驱动usb_serial_tty_driver绑定到一起。
3)在/dev目录下生成/dev/ttyUSB1,/dev/ttyUSB2,/dev/ttyUSB3三个设备。

2.6 总线

(1)挂载在usb-serial总线下的drivers(/sys/bus/usb-serial/drivers)有:generic、option1、sprd_u2s(展讯驱动)等;

(2)挂载在usb-serial总线下的devices(/sys/bus/usb-serial/devices)有:ttyUSB0、ttyUSB1、ttyUSB2等;

(3)挂载在usb总线下的drivers(/sys/bus/usb/drivers)有:cdc_ether、usbserial、sprd_u2s、option等。
在这里插入图片描述

2.7 设备描述符

设备——配置——接口——端点

/sys/bus/usb/devices/
2-1:1.0
根集线器-集线器端口号(-集线器端口号...):配置.接口
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐