USB gadget 驱动之 android.c
1. Gadget驱动 1.1 Gadget框架结构 kernel/drivers/usb/gadget,这个目录是android下usb gadget的主要目录。 (1)Gadget功能组织单元:主要文件android.c,usb gadget功能的统领文件,负责组织usb 复合设备的功能,与上层应用提供交互的接口,面向市场需求的产品规划部门。 (2)复合设备逻辑处理单元(复合设备管理单元):c
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 configuration
和USB 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
根集线器-集线器端口号(-集线器端口号...):配置.接口
更多推荐
所有评论(0)