通用USB设备驱动源码分析
Author:aaron
前段时间写了篇<qualcomm usb modem驱动小结>的文章,
描述了自己如何为高通的一个usb modem设备写驱动的过程, 最近发现实际上可以使用linux自带的一个叫usbserial的模块作为这个modem的驱动并能良好的工作, 所以写了这片文章来详细的分析下usbserial模块的源码(
应该来说, 对于那些仅仅是用USB来通信, 在上层可看作tty设备, 不属于任何USB设备类型, 没有什么流控等的普通USB设备来说都可以使用这个驱动来作为设备驱动程序.下面就来对这样一种通用的驱动程序来进行详细的分析. 不对之处敬请指正!
为了能让usbserail模块支持我的设备, 我必须在命令行上输入如下命令:
sudo modprobe usbserial vendor=0x12d1 product=0x1003
该命令用特权用户来加载usbserial模块,并把该模块依赖的模块一并加载进系统, 同时它还设置了usbserial的两个参数: vendor, product, 很显然这两个参数是厂商ID和设备ID, 而作用就是用于匹配设备.
首先, 当然是要知道usbserial模块由哪些文件编译而成, 这样才能有目的性的去分析其代码. 而要知道其组成当然是去其目录下看Makefile了, 它位于内核源码目录下的./drivers/usb/serial/下
./drivers/usb/serial/Makefile:
#
# Makefile for
the USB serial device drivers.
#
# Object file
lists.
obj-$(CONFIG_USB_SERIAL) += usbserial.o #编译内核时如何编译该模块
usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE) += console.o
usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o
usbserial-objs := usb-serial.o generic.o bus.o
$(usbserial-obj-y) #OK, 就是usbserial模块的组成了.
obj-$(CONFIG_USB_SERIAL_AIRPRIME) +=
airprime.o
obj-$(CONFIG_USB_SERIAL_ANYDATA) += anydata.o
.......
我们重点看的是usb-serial.c, generic.c, bus.c
在看源码之前我们先说说该模块的原理及整体结构:
很简单跟应用层交互的是一个tty设备, 也就是说该模块把USB设备映射成一个tty设备(即在/dev/目录下为该USB设备创建一个tty设备文件), 然后用于可以用minicom之类的串口工具来打开这个设备, 并同设备端的设备通信.
对于发送过程: tty设备文件在获取了用户要求发送的数据之后传递到下层usbserial模块的核心层,而该核心层就是将数据打包成USB格式的数据并由USB通信发送到设备端去,
对于接收过程: usbserial模块会在该设备打开时就启动一个urb在那等待设备端发数据过来, 收到数据后就push到上层tty设备的缓冲中去, 而tty设备在收到数据后就会给用户,或直接显示在minicom之类的工具上.
usb-serial.c 就是usbserial模块的核心, 它主要用来接收设备端发来的数据并传送到上层, 同时也接收来自上层应用的数据,并组装成urb包发送给设备.
generic.c 对特定设备单独的操作,相当于是设备自己的驱动程序, 由于很多设备具有通用性, 所以对于没有特殊要求的设备都可以使用这个驱动来作为自己设备的驱动程序. 它有两个参数vendor和product,上面提过了.
bus.c 每个usb驱动和设备都必须要归入某一条总线上, 即都是归属于某条总线的,只有这样系统才能从特定一条总线开始找到每个驱动和设备并为他们匹配. 这个文件就是用来模拟一条总线, 而usbserial的每个驱动和设备都会注册到这条总线上来.
好了,是时候分析usbserial模块了.
我们知道当把一个模块加载进系统时会调用这个模块里的一个由module_init()声明的一个初始化函数. usbserial当然也不另外,
usb-serial.c:
module_init(usb_serial_init);
module_exit(usb_serial_exit);
没错加载时调用的就是: usb_serial_init().
usb-serial.c:
struct
tty_driver *usb_serial_tty_driver;
static int
__init usb_serial_init(void)
{
int i;
int result;
//创建一个tty_driver对象, 对应的就是tty设备的驱动.
usb_serial_tty_driver =
alloc_tty_driver(SERIAL_TTY_MINORS);
if (!usb_serial_tty_driver)
return -ENOMEM;
/* Initialize our global data */
for (i = 0; i < SERIAL_TTY_MINORS;
++i) {
serial_table[i] = NULL; //该模块共支持SERIAL_TTY_MINORS个该类型设备.
}
result =
bus_register(&usb_serial_bus_type);
//注册这条serial bus.
if (result) {
err("%s - registering bus driver
failed", __FUNCTION__);
goto exit_bus;
}
//初始化tty_driver对象
usb_serial_tty_driver->owner =
THIS_MODULE;
usb_serial_tty_driver->driver_name =
"usbserial";
usb_serial_tty_driver->devfs_name =
"usb/tts/";
usb_serial_tty_driver->name = "ttyUSB"; //tty设备文件名以这个开头,后加0,1,2,3,....
usb_serial_tty_driver->major =
SERIAL_TTY_MAJOR; //主设备号
usb_serial_tty_driver->minor_start =
0;
usb_serial_tty_driver->type =
TTY_DRIVER_TYPE_SERIAL; //设备类型
usb_serial_tty_driver->subtype =
SERIAL_TYPE_NORMAL;
usb_serial_tty_driver->flags =
TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
usb_serial_tty_driver->init_termios =
tty_std_termios;
usb_serial_tty_driver->init_termios.c_cflag
= B9600 | CS8 | CREAD | HUPCL | CLOCAL;
//赋值tty设备的操作集合,即应用层调用open时最终会调到serial_ops->open里面
tty_set_operations(usb_serial_tty_driver,
&serial_ops);
result =
tty_register_driver(usb_serial_tty_driver);
//注册这个tty驱动
if (result) {
err("%s - tty_register_driver
failed", __FUNCTION__);
goto exit_reg_driver;
}
/* register the USB driver */
result =
usb_register(&usb_serial_driver);
//注册一个usb驱动
if (result < 0) {
err("%s - usb_register
failed", __FUNCTION__);
goto exit_tty;
}
/* register the generic driver, if we
should */
result =
usb_serial_generic_register(debug); //注册generic驱动程序
if (result < 0) {
err("%s - registering generic
driver failed", __FUNCTION__);
goto exit_generic;
}
info(DRIVER_DESC);
return result;
//失败时候的一些反向操作
exit_generic:
usb_deregister(&usb_serial_driver);
exit_tty:
tty_unregister_driver(usb_serial_tty_driver);
exit_reg_driver:
bus_unregister(&usb_serial_bus_type);
exit_bus:
err ("%s - returning with error
%d", __FUNCTION__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}
该函数先创建并初始化好了一个tty_driver的对象, 并把该对象注册进系统, 该对象就是tty设备的驱动程序, 后面我们会看到他是如何于具体tty设备绑定在一起的.
usb_serial.c:
static struct
tty_operations serial_ops = {
.open = serial_open,
.close = serial_close,
.write = serial_write,
.write_room = serial_write_room,
.ioctl = serial_ioctl,
.set_termios = serial_set_termios,
.throttle = serial_throttle,
.unthrottle = serial_unthrottle,
.break_ctl = serial_break,
.chars_in_buffer = serial_chars_in_buffer,
.read_proc = serial_read_proc,
.tiocmget = serial_tiocmget,
.tiocmset = serial_tiocmset,
};
这个就是tty设备文件对应的操作方法集合, 例如, 应用层调用open函数来打开该设备文件时将最终会走到serial_open里面.
usb_serial_init()
还注册了一条总线: usb_serial_bus_type, 这样当有设备连上系统时, 该总线上的驱动就有机会去匹配这个设备. 后面我们会看到generic的驱动就是注册在该总线上的.
bus.c:
struct bus_type
usb_serial_bus_type = {
.name = "usb-serial",
.match = usb_serial_device_match, //在设备匹配时会调用
.probe = usb_serial_device_probe,
.remove = usb_serial_device_remove,
};
关于设备匹配过程(probe)可以参考我的另一篇文章.
usb_serial_init()
在最后usb_serial_generic_register(debug)来注册generic驱动.
generic.c:
int
usb_serial_generic_register (int _debug)
{
int retval = 0;
debug = _debug;
#ifdef
CONFIG_USB_SERIAL_GENERIC
generic_device_ids[0].idVendor = vendor; //保存厂商ID
generic_device_ids[0].idProduct =
product; //保存产品ID
generic_device_ids[0].match_flags =
USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT; //匹配类型
/* register our generic driver with
ourselves */
retval = usb_serial_register
(&usb_serial_generic_device); //注册驱动
if (retval)
goto exit;
retval =
usb_register(&generic_driver); //注册驱动
if (retval)
usb_serial_deregister(&usb_serial_generic_device);
exit:
#endif
return retval;
}
该函数首先保存了命令通过命令行设备的vendor,product 用于以后设备匹配, 由此我们知道该驱动可以动态支持设备匹配. 接着该函数注册了usb_serial_generic_device驱动.
generic.c:
struct
usb_serial_driver usb_serial_generic_device = {
.driver = {
.owner = THIS_MODULE,
.name = "generic",
},
.id_table = generic_device_ids,
//匹配用的设备列表, 支持动态匹配
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
.num_ports = 1,
.shutdown = usb_serial_generic_shutdown,
};
Usb-serial.c:
int
usb_serial_register(struct usb_serial_driver *driver)
{
int retval;
fixup_generic(driver); //为driver赋上默认的操作函数
if (!driver->description)
driver->description =
driver->driver.name;
/* Add this device to our list of devices
*/
list_add(&driver->driver_list,
&usb_serial_driver_list); //加入驱动列表
retval = usb_serial_bus_register(driver); //把该驱动注册进usb serial bus下
if (retval) {
err("problem %d when
registering driver %s", retval, driver->description);
list_del(&driver->driver_list);
}
else
info("USB Serial support
registered for %s", driver->description);
return retval;
}
其中的fixup_generic()函数仅仅是为driver赋上默认的操作函数.
Usb-serial.c:
#define
set_to_generic_if_null(type, function) /
do { /
if (!type->function) { /
type->function =
usb_serial_generic_##function; /
dbg("Had to override
the " #function /
" usb serial operation with the generic
one.");/
} /
} while (0)
static void
fixup_generic(struct usb_serial_driver *device)
{
set_to_generic_if_null(device, open);
set_to_generic_if_null(device, write);
set_to_generic_if_null(device, close);
set_to_generic_if_null(device,
write_room);
set_to_generic_if_null(device,
chars_in_buffer);
set_to_generic_if_null(device, read_bulk_callback);
set_to_generic_if_null(device,
write_bulk_callback);
set_to_generic_if_null(device, shutdown);
}
即通过上面的usb_serial_register()函数后usb_serial_generic_device的函数集为:
usb_serial_generic_device.open
= usb_serial_generic_open;
usb_serial_generic_device.close
= usb_serial_generic_close
......
驱动usb_serial_generic_device将是以后操作tty设备的主要函数.我们会在后面分析.
bus.c:
int
usb_serial_bus_register(struct usb_serial_driver *driver)
{
int retval;
driver->driver.bus =
&usb_serial_bus_type; //注册到该bus下
retval = driver_register(&driver->driver);
return retval;
}
最后usb_serial_generic_register()函数注册了一个generic_driver驱动.
generic.c:
static struct
usb_driver generic_driver = {
.name = "usbserial_generic",
.probe = generic_probe, //匹配函数