您的位置:首页 > 其它

通用USB设备驱动源码分析

2010-08-16 10:23 441 查看
通用

USB

设备驱动源码分析


Author:aaron


前段时间写了篇
<qualcomm usb modem
驱动小结
>
的文章
,
描述了自己如何为高通的一个
usb modem
设备写驱动的过程
,
最近发现实际上可以使用
linux
自带的一个叫
usbserial
的模块作为这个
modem
的驱动并能良好的工作
,
所以写了这片文章来详细的分析下
usbserial
模块的源码
(2.6.16.3).

应该来说
,
对于那些仅仅是用
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,
//
匹配函数

.disconnect =
usb_serial_disconnect,

.id_table =
generic_serial_ids,
//
匹配用的设备列表

.no_dynamic_id =
1,
//
不支持动态匹配

};

整个初始化过程
,
乍一看一下子注册了几个驱动程序
,
几个驱动列表
,
有的支持动态匹配有的不支持
,
感觉很复杂
,
其实注册
generic_driver
驱动主要是为了注册一个
generic_probe
函数
,
而该函数将会在设备连上系统后被调用以来匹配设备
.
除此之外该驱动没什么用
,
而在这个初始化函数中把
vendor,product
都保存在了
generic_device_ids

,
因此可以肯定以后的匹配将用这个设备列表
,
而不是
generic_serial_ids,
说的更直白些
generic_serial_ids
其实根本也没什么用
.

真正有用的是
usb_serial_generic_device
驱动
,

generic.c:

static int generic_probe(struct usb_interface *interface,

const struct usb_device_id *id)

{

const struct usb_device_id *id_pattern;

id_pattern = usb_match_id(interface,
generic_device_ids);
//
设备匹配

if (id_pattern != NULL)

return usb_serial_probe(interface, id);
//
进一步匹配

return -ENODEV;

}

如果接入系统的设备的
vendor

product
与我们驱动支持的设备列表匹配则调用
usb_serial_probe
来进一步匹配
.

usb_serial_probe
函数比较长
,
我们一段段的来看

usb-serial.c:

int usb_serial_probe(struct usb_interface *interface,

const struct usb_device_id *id)

{

struct usb_device *dev = interface_to_usbdev (interface);

struct usb_serial *serial = NULL;

struct usb_serial_port *port;

struct usb_host_interface *iface_desc;

struct usb_endpoint_descriptor *endpoint;

struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];

struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];

struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];

struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];

struct usb_serial_driver *type = NULL;

int retval;

int minor;

int buffer_size;

int i;

int num_interrupt_in = 0;

int num_interrupt_out = 0;

int num_bulk_in = 0;

int num_bulk_out = 0;

int num_ports = 0;

int max_endpoints;

type = search_serial_device(interface);
//
获取该设备匹配的驱动

if (!type) {

dbg("none matched");

return -ENODEV;

}

......

}

首先是找到合适的驱动程序
.

usb-serial.c:

static struct usb_serial_driver *search_serial_device(struct usb_interface *iface)

{

struct list_head *p;

const struct usb_device_id *id;

struct usb_serial_driver *t;

/* Check if the usb id matches a known device */

list_for_each(p, &usb_serial_driver_list) {

t = list_entry(p, struct usb_serial_driver, driver_list);

id = usb_match_id(iface, t->id_table);
//
看设备列表是否匹配

if (id != NULL) {

dbg("descriptor matches");

return t;
//
返回匹配的驱动

}

}

return NULL;

}

实际上这边的匹配和
generic_probe
里的匹配重复了
,
因为他们的匹配的设备列表是同一个
,

这边主要是为了得到匹配的驱动程序
,
根据上面的代码分析我们可以知道这里匹配的驱动是
usb_serial_generic_device.

接着看
usb_serial_probe()

usb-serial.c:

....

serial = create_serial (dev, interface, type);
//
为该设备创建一个
usb_serial
对象

if (!serial) {

dev_err(&interface->dev, "%s - out of memory/n", __FUNCTION__);

return -ENOMEM;

}

/* if this device type has a probe function, call it */

if (type->probe) {
//
从上面分析的代码可知这里的
probe
函数没有赋值

const struct usb_device_id *id;

if (!try_module_get(type->driver.owner)) {

dev_err(&interface->dev, "module get failed, exiting/n");

kfree (serial);

return -EIO;

}

id = usb_match_id(interface, type->id_table);

retval = type->probe(serial, id);

module_put(type->driver.owner);

if (retval) {

dbg ("sub driver rejected device");

kfree (serial);

return retval;

}

}

....

这段代码可知
,
主要是创建一个
usb_serial
的对象
,
用于保存该设备的详细信息
,
一般的驱动程序都会为自己匹配的设备创建一个描用于描述该设备的对象
.
在以后的所有操作中如读写等都会直接从这个对象里获取相应的信息
.

usb-serial.c:

static struct usb_serial * create_serial (struct usb_device *dev,

struct usb_interface *interface,

struct usb_serial_driver *driver)

{

struct usb_serial *serial;

serial = kmalloc (sizeof (*serial), GFP_KERNEL);
//
闯将该对象

if (!serial) {

dev_err(&dev->dev, "%s - out of memory/n", __FUNCTION__);

return NULL;

}

//
初始化该对象

memset (serial, 0, sizeof(*serial));

serial->dev = usb_get_dev(dev);
//
增加
dev
的引用计数

serial->type = driver;

serial->interface = interface;

kref_init(&serial->kref);

return serial;

}

这个函数就是用来创建
usb_serial
对象的
,
并把相关信息保存在里面
.

继续看
usb_serial_probe()

usb-serial.c:

....

/* descriptor matches, let's find the endpoints needed */

/* check out the endpoints */

//
查找该设备使用的
endpoint
的描述符
,
并检查是否正确

iface_desc = interface->cur_altsetting;
//
接口描述符

for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {

endpoint = &iface_desc->endpoint[i].desc;
//
端点描述符

if ((endpoint->bEndpointAddress & 0x80) &&

((endpoint->bmAttributes & 3) == 0x02)) {

/* we found a bulk in endpoint */
//bulk in
的端点

dbg("found bulk in on endpoint %d", i);

bulk_in_endpoint[num_bulk_in] = endpoint;

++num_bulk_in;

}

if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&

((endpoint->bmAttributes & 3) == 0x02)) {

/* we found a bulk out endpoint */
//bulk out
的端点

dbg("found bulk out on endpoint %d", i);

bulk_out_endpoint[num_bulk_out] = endpoint;

++num_bulk_out;

}

if ((endpoint->bEndpointAddress & 0x80) &&

((endpoint->bmAttributes & 3) == 0x03)) {

/* we found a interrupt in endpoint */
//
中断
in
端点

dbg("found interrupt in on endpoint %d", i);

interrupt_in_endpoint[num_interrupt_in] = endpoint;

++num_interrupt_in;

}

if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&

((endpoint->bmAttributes & 3) == 0x03)) {

/* we found an interrupt out endpoint */ //
中断
out
端点

dbg("found interrupt out on endpoint %d", i);

interrupt_out_endpoint[num_interrupt_out] = endpoint;

++num_interrupt_out;

}

}

.....

该段代码主要是获取该设备使用的各个类型及方向的端点描述府
,
并保存起来
,
关于端点的类型与方向可以参考
USB
的规范
.

继续看
usb_serial_probe()

usb-serial.c:

....

#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)

/* BEGIN HORRIBLE HACK FOR PL2303 */

/* this is needed due to the looney way its endpoints are set up */

if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&

(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||

((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&

(le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {

if (interface != dev->actconfig->interface[0]) {

/* check out the endpoints of the other interface*/

iface_desc = dev->actconfig->interface[0]->cur_altsetting;

for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {

endpoint = &iface_desc->endpoint[i].desc;

if ((endpoint->bEndpointAddress & 0x80) &&

((endpoint->bmAttributes & 3) == 0x03)) {

/* we found a interrupt in endpoint */

dbg("found interrupt in for Prolific device on separate interface");

interrupt_in_endpoint[num_interrupt_in] = endpoint;

++num_interrupt_in;

}

}

}

/* Now make sure the PL-2303 is configured correctly.

* If not, give up now and hope this hack will work

* properly during a later invocation of usb_serial_probe

*/

if (num_bulk_in == 0 || num_bulk_out == 0) {

dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not/n");

kfree (serial);

return -ENODEV;

}

}

/* END HORRIBLE HACK FOR PL2303 */

#endif

上面这段代码主要是用于特定类型设备的
(PL2303),
这里我们不用管他
.

接着看
usb_serial_probe()

usb-serial.c:

....

/* found all that we need */

dev_info(&interface->dev, "%s converter detected/n", type->description);

#ifdef CONFIG_USB_SERIAL_GENERIC
//
这个宏定义了
,
因为我们使用的是通用
USB
驱动
.

if (type == &usb_serial_generic_device) {
//
这个
if

TRUE(
上面分析过了
)

num_ports = num_bulk_out;

if (num_ports == 0) {
//bulk out
端点必须要有

dev_err(&interface->dev, "Generic device with no bulk out, not allowed./n");

kfree (serial);

return -EIO;

}

}

#endif

if (!num_ports) {
//
由于走到了上面那个
if

,
因此这里的
num_ports
肯定不为
0

/* if this device type has a calc_num_ports function, call it */

if (type->calc_num_ports) {

if (!try_module_get(type->driver.owner)) {

dev_err(&interface->dev, "module get failed, exiting/n");

kfree (serial);

return -EIO;

}

num_ports = type->calc_num_ports (serial);

module_put(type->driver.owner);

}

if (!num_ports)

num_ports = type->num_ports;

}

//
获取一个空闲的
serial_table


if (get_free_serial (serial, num_ports, &minor) == NULL) {

dev_err(&interface->dev, "No more free serial devices/n");

kfree (serial);

return -ENOMEM;

}

usbserial
模块总共支持
SERIAL_TTY_MINORS
个设备
,
它为每个设备都分配了一个
serial_table

,
用于保存
usb_serial
对象
,
方便以后直接通过
minor
号获取
usb_serial
对象
.

usb-serial.c:

static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor)

{

unsigned int i, j;

int good_spot;

dbg("%s %d", __FUNCTION__, num_ports);

*minor = 0;

for (i = 0; i < SERIAL_TTY_MINORS; ++i) {

if (serial_table[i])

//
查找一个空闲的
serial_table

, serial_table
是个
usb_serial
的指针数组
.

continue;

good_spot = 1;

//
从上面代码可知
,
对于
generic
的驱动
, num_ports
就等于
num_bulk_out,
而一般的设备仅有
//
一个
bulk out
的端点
,
因此这个
for
循环不会执行
.

for (j = 1; j <= num_ports-1; ++j)

if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {

good_spot = 0;

i += j;

break;

}

if (good_spot == 0)

continue;

*minor = i;
//
获取
minor


dbg("%s - minor base = %d", __FUNCTION__, *minor);

for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)

serial_table[i] = serial;
//
获取空闲的
serial_table

,
并把我们的
usb_serial
对象地址保

//
存在其中
.

return serial;

}

return NULL;

}

通过这个函数我们找到了一个空闲的
serial_table

,
并把描述我们设备的
usb_serial
对象保存在其中
,
在以后对设备的使用中
,
我们可以轻易的通过
minor
号来找到这个
usb_serial.

接着看
usb_serial_probe()

usb-serial.c:

....

//
保存设备信息到
usb_serial
对象中
,

serial->minor = minor;

serial->num_ports = num_ports;
//
这里的
port
数量不是
endpoint
的数量
,

serial->num_bulk_in = num_bulk_in;

serial->num_bulk_out = num_bulk_out;

serial->num_interrupt_in = num_interrupt_in;

serial->num_interrupt_out = num_interrupt_out;

/* create our ports, we need as many as the max endpoints */

/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */

//
对于
generic
的驱动来说一般都只有一个
bulk in,
一个
bulk out,
一个
interrupt in,
一个
interrupt out

max_endpoints = max(num_bulk_in, num_bulk_out);

max_endpoints = max(max_endpoints, num_interrupt_in);

max_endpoints = max(max_endpoints, num_interrupt_out);

max_endpoints = max(max_endpoints, (int)serial->num_ports);

//
到这一步
,
对于
generic
来说大多数情况下
max_endpoints
还是为
1

serial->num_port_pointers = max_endpoints;

dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);

for (i = 0; i < max_endpoints; ++i) {

port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
//
分配一个
port
对象

if (!port)

goto probe_error;

//
初始化
port
对象

memset(port, 0x00, sizeof(struct usb_serial_port));

port->number = i + serial->minor;

port->serial = serial;
//
保存
usb_serial
对象
,
便于以后通过
port
对象访问到
usb_serial
对象

spin_lock_init(&port->lock);

sema_init(&port->sem, 1);

INIT_WORK(&port->work, usb_serial_port_softint, port);

serial->port[i] = port;

}

//
由上面的对
port
的初始化可知
,
每个
port
都有一套自己的工作机制
, port
间互不干扰

/* set up the endpoint information */

for (i = 0; i < num_bulk_in; ++i) {

//
初始化
bulk in
端点
,
并把它保存到相应的
port


endpoint = bulk_in_endpoint[i];

port = serial->port[i];

port->read_urb = usb_alloc_urb (0, GFP_KERNEL);
//
分配
urb

if (!port->read_urb) {

dev_err(&interface->dev, "No free urbs available/n");

goto probe_error;

}

buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

port->bulk_in_size = buffer_size;

port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
//
保存端点地址

port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
//
分配传输缓存

if (!port->bulk_in_buffer) {

dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer/n");

goto probe_error;

}

//
设置好该
urb.

usb_fill_bulk_urb (port->read_urb, dev,

usb_rcvbulkpipe (dev,

endpoint->bEndpointAddress),

port->bulk_in_buffer, buffer_size,

serial->type->read_bulk_callback,

port);

}

for (i = 0; i < num_bulk_out; ++i) {

//
初始化
bulk out
端点
,
并把它保存到相应的
port


endpoint = bulk_out_endpoint[i];

port = serial->port[i];

port->write_urb = usb_alloc_urb(0, GFP_KERNEL);

if (!port->write_urb) {

dev_err(&interface->dev, "No free urbs available/n");

goto probe_error;

}

buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

port->bulk_out_size = buffer_size;

port->bulk_out_endpointAddress = endpoint->bEndpointAddress;

port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);

if (!port->bulk_out_buffer) {

dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer/n");

goto probe_error;

}

usb_fill_bulk_urb (port->write_urb, dev,

usb_sndbulkpipe (dev,

endpoint->bEndpointAddress),

port->bulk_out_buffer, buffer_size,

serial->type->write_bulk_callback,

port);

}

.....

上面这段代码主要是保存设备信息到
usb_serial
中去
,
并为每个口分配一个
port,
同时设置好
port
中的项
.

对于这里的
port,
我的理解是有的设备可能有多个口
,
而每个口都有自己的一套
endpoint
用于该口的功能实现
.
因此我们要为每个口分别分配
port
对象
,
并保存该口下的
endpoint
信息
.

接着看
usb_serial_probe()

usb-serial.c:

....

if (serial->type->read_int_callback) {
//
对于
generic
驱动
,
这里的
read_int_callback
为空

for (i = 0; i < num_interrupt_in; ++i) {

//
初始化中断端点

endpoint = interrupt_in_endpoint[i];

port = serial->port[i];

port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);

if (!port->interrupt_in_urb) {

dev_err(&interface->dev, "No free urbs available/n");

goto probe_error;

}

buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;

port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);

if (!port->interrupt_in_buffer) {

dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer/n");

goto probe_error;

}

usb_fill_int_urb (port->interrupt_in_urb, dev,

usb_rcvintpipe (dev,

endpoint->bEndpointAddress),

port->interrupt_in_buffer, buffer_size,

serial->type->read_int_callback, port,

endpoint->bInterval);

}

} else if (num_interrupt_in) {
//
如果有
interrupt in
而没有
read_int_callback
则是错误的

dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined");

}

//
从上面这个
if
可以看出
generic
驱动的设备不应该有
interrupt in
端点
,
有也可以但是这个端点将

//
不起作用
.

//
下面这个
if
和上面的这个
if
功能一模一样
.

if (serial->type->write_int_callback) {

for (i = 0; i < num_interrupt_out; ++i) {

endpoint = interrupt_out_endpoint[i];

port = serial->port[i];

port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);

if (!port->interrupt_out_urb) {

dev_err(&interface->dev, "No free urbs available/n");

goto probe_error;

}

buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

port->interrupt_out_size = buffer_size;

port->interrupt_out_endpointAddress = endpoint->bEndpointAddress;

port->interrupt_out_buffer = kmalloc (buffer_size, GFP_KERNEL);

if (!port->interrupt_out_buffer) {

dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer/n");

goto probe_error;

}

usb_fill_int_urb (port->interrupt_out_urb, dev,

usb_sndintpipe (dev,

endpoint->bEndpointAddress),

port->interrupt_out_buffer, buffer_size,

serial->type->write_int_callback, port,

endpoint->bInterval);

}

} else if (num_interrupt_out) {

dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined");

}

/* if this device type has an attach function, call it */

if (type->attach) {
//
对于
generic
驱动
,
没有设置这个
attach
函数

if (!try_module_get(type->driver.owner)) {

dev_err(&interface->dev, "module get failed, exiting/n");

goto probe_error;

}

retval = type->attach (serial);

module_put(type->driver.owner);

if (retval < 0)

goto probe_error;

if (retval > 0) {

/* quietly accept this device, but don't bind to a serial port

* as it's about to disappear */

goto exit;

}

}

/* register all of the individual ports with the driver core */

for (i = 0; i < num_ports; ++i) {

port = serial->port[i];

port->dev.parent = &interface->dev;

port->dev.driver = NULL;

port->dev.bus = &usb_serial_bus_type;
//
注册到
usb_serial_bus
上去

port->dev.release = &port_release;

snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);

dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);

device_register (&port->dev);
//
把该设备注册到系统中去

}

usb_serial_console_init (debug, minor);

......

上面这段代码主要就是初始化
interrupt
的端点
,
并且把
port
设备挂到
usb_serial_bus
上注册进系统
,

接着看
usb_serial_probe()

usb-serial.c:

......

exit:

/* success */

usb_set_intfdata (interface, serial);
//

interface
对象里保存
usb_serial
对象地址
,
以方便以后使用

return 0;

//
错误处理

probe_error:

for (i = 0; i < num_bulk_in; ++i) {

port = serial->port[i];

if (!port)

continue;

if (port->read_urb)

usb_free_urb (port->read_urb);

kfree(port->bulk_in_buffer);

}

for (i = 0; i < num_bulk_out; ++i) {

port = serial->port[i];

if (!port)

continue;

if (port->write_urb)

usb_free_urb (port->write_urb);

kfree(port->bulk_out_buffer);

}

for (i = 0; i < num_interrupt_in; ++i) {

port = serial->port[i];

if (!port)

continue;

if (port->interrupt_in_urb)

usb_free_urb (port->interrupt_in_urb);

kfree(port->interrupt_in_buffer);

}

for (i = 0; i < num_interrupt_out; ++i) {

port = serial->port[i];

if (!port)

continue;

if (port->interrupt_out_urb)

usb_free_urb (port->interrupt_out_urb);

kfree(port->interrupt_out_buffer);

}

/* return the minor range that this device had */

return_serial (serial);

/* free up any memory that we allocated */

for (i = 0; i < serial->num_port_pointers; ++i)

kfree(serial->port[i]);

kfree (serial);

return -EIO;

这样整个
probe
过程就结束了
,
或许你会奇怪
,
好像
usb_serial_bus
都没什么用
,
而且好像没看见这个设备和
init
时的那个
tty_driver
绑定啊
,

不错
,
这个
probe
函数本身还没有作这些工作
,
我们接着分析
.

usb_serial_probe()
函数在最后调用了
device_register (&port->dev);
把设备注册进了系统
,
在这个注册过程中
,
系统会在次为这个注册的设备进行
probe
过程
.

我们先来了解下
probe
的过程

Driver

prob
的调用顺序
:

1 device_add():

device
注册到相应的
bus
上去
,
并创建相应的
device file,
最后调用
bus_attach_device()

2 bus_attach_device()
调用
device_attach(dev)

3 device_attach():
调用
bus_for_each_drv()
遍历
bus
上的每个
driver,
当找到一个
driver
则用
__device_attach()
来判断是否匹配

4 __device_attach():
直接调用
driver_probe_device(drv, dev)

5 driver_probe_device():
首先如果
driver
所在总线有
match
函数则先调用这个
match
来匹配
,
如不匹配则直接返回错误
,
否则接着调用
really_probe(dev,drv)

6 really_probe():
先判断
dev
所在总线是否有
probe
函数
,
有则调用它来匹配
,
失败则返回
,
正确则成功
,
如果总线没有
probe
则判断
drv
是否有
probe
函数
,
有则调用并匹配它
.

7 drv->prob():

一般它是一类设备的
probe,
在它里面它会调用具体某个
drv

probe
函数
,
这个函数是在我们的驱动程序里面注册的
.

device_register()
里会调用
device_add().
因此由
5
可知

系统会先调用总线上的
match
函数来匹配
.
对于我们的总线就是
usb_serial_bus,
它的
match
函数就是
usb_serial_device_match(),
至于为什么是这条总线
,
通过前面的代码分析
,
我们知道我们的设备也好
,
我们的驱动也好都是注册在这条总线上的
.

下面我们就来分析
usb_serial_device_match()

Bus.c:

static int usb_serial_device_match (struct device *dev, struct device_driver *drv)

{

struct usb_serial_driver *driver;

const struct usb_serial_port *port;

/*

* drivers are already assigned to ports in serial_probe so it's

* a simple check here.

*/

port = to_usb_serial_port(dev);
//
获取
usb_serial_port
对象

if (!port)

return 0;

driver = to_usb_serial_driver(drv);
//
获取
usb_serial_driver
对象

if (driver == port->serial->type)
//
匹配否
?

return 1;

return 0;

}

很显然
,
通过前面的分析可知
,
这里肯定是匹配的
.

接着通过上面
probe
过程的
6
可知会调用总线的
probe
函数
,
这里就是
usb_serial_device_probe

Bus.c:

static int usb_serial_device_probe (struct device *dev)

{

struct usb_serial_driver *driver;

struct usb_serial_port *port;

int retval = 0;

int minor;

port = to_usb_serial_port(dev);

if (!port) {

retval = -ENODEV;

goto exit;

}

driver = port->serial->type;

if (driver->port_probe) {

if (!try_module_get(driver->driver.owner)) {

dev_err(dev, "module get failed, exiting/n");

retval = -EIO;

goto exit;

}

retval = driver->port_probe (port);

module_put(driver->driver.owner);

if (retval)

goto exit;

}

minor = port->number;

tty_register_device (usb_serial_tty_driver, minor, dev);
//
呵呵
,
这里总算把
tty_driver

device
绑定

//
起来了

dev_info(&port->serial->dev->dev,

"%s converter now attached to ttyUSB%d/n",

driver->description, minor);

exit:

return retval;

}

到了这一步该设备就和
tty_driver
绑定在了一起了
,
同时在
/dev
下也创建了相应的设备文件了
,
也就是说应用层可以使用这个设备了
.

这样整个对设备的
probe
过程才算真的完成了
.

OK,
设备从
USB
口连上系统
,
并被系统认出及
probe
的真个过程就基本完成了
,
从此以后设备就进入了就绪状态
.
下面我们就开始分析对设备的操作流程了
.

要使用设备当然要先打开这个设备了
,
应用层调用
open
系统调用来打开这个设备
,
它最终会跑到我们
tty_driver

open
函数里面

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,

};

这里就是
serial_open

.

usb_serial.c:

static int serial_open (struct tty_struct *tty, struct file * filp)

{

struct usb_serial *serial;

struct usb_serial_port *port;

unsigned int portNumber;

int retval;

dbg("%s", __FUNCTION__);

/* get the serial object associated with this tty pointer */

serial = usb_serial_get_by_index(tty->index);
//
获取
usb_serial
对象
,
根据上面的分析
,
不难理解

if (!serial) {

tty->driver_data = NULL;

return -ENODEV;

}

portNumber = tty->index - serial->minor;

port = serial->port[portNumber];
//
获取设备对应的
port
对象
,
这也不难理解了

if (!port)

return -ENODEV;

if (down_interruptible(&port->sem))

return -ERESTARTSYS;

++port->open_count;
//
跟踪打开次数

if (port->open_count == 1) {

/* set up our port structure making the tty driver

* remember our port object, and us it */

tty->driver_data = port;
//
赋值
,
为以后的操作方便引用

port->tty = tty;

/* lock this module before we call it

* this may fail, which means we must bail out,

* safe because we are called with BKL held */

if (!try_module_get(serial->type->driver.owner)) {

retval = -ENODEV;

goto bailout_kref_put;

}

/* only call the device specific open if this

* is the first time the port is opened */

retval = serial->type->open(port, filp);
//
调用
usb_serial_driver

open
函数
,
在前面分析过了

if (retval)

goto bailout_module_put;

}

up(&port->sem);

return 0;

bailout_module_put:

module_put(serial->type->driver.owner);

bailout_kref_put:

kref_put(&serial->kref, destroy_serial);

port->open_count = 0;

up(&port->sem);

return retval;

}

可以看到这个
open
动作主要就是保存一些信息
,
以方便后面使用
,
接着调用
usb_serial_driver

open
函数
,
这里该
open

usb_serial_generic_open,
前面分析过了

generic.c:

int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)

{

struct usb_serial *serial = port->serial;

int result = 0;

dbg("%s - port %d", __FUNCTION__, port->number);

/* force low_latency on so that our tty_push actually forces the data through,

otherwise it is scheduled, and with high data rates (like with OHCI) data

can get lost. */

if (port->tty)

port->tty->low_latency = 1;

/* if we have a bulk interrupt, start reading from it */

//
如果有
bulk in
的端点的话
,
就提交这个端点的
urb,
即让系统开始在这个端点上接收来自设备段

//
发过来的数据
,
当数据收到后会调用
serial->type->read_bulk_callback
函数
.

if (serial->num_bulk_in) {

/* Start reading from the device */

usb_fill_bulk_urb (port->read_urb, serial->dev,

usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),

port->read_urb->transfer_buffer,

port->read_urb->transfer_buffer_length,

((serial->type->read_bulk_callback) ?

serial->type->read_bulk_callback :

usb_serial_generic_read_bulk_callback),

port);

result = usb_submit_urb(port->read_urb, GFP_KERNEL);
//
提交

if (result)

dev_err(&port->dev, "%s - failed resubmitting read urb, error %d/n", __FUNCTION__, result);

}

return result;

}

这个函数后
,
系统就开始在
bulk in
的端点上接收数据了
,
如果有数据到来的话就会调用我们的回调函数
.

接下来我们来看这个回调函数
:

Generic.c:

void usb_serial_generic_read_bulk_callback (struct urb *urb, struct pt_regs *regs)

{

struct usb_serial_port *port = (struct usb_serial_port *)urb->context;

struct usb_serial *serial = port->serial;

struct tty_struct *tty;

unsigned char *data = urb->transfer_buffer;

int result;

dbg("%s - port %d", __FUNCTION__, port->number);

if (urb->status) {
//
这次通信的状态

dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);

return;

}

usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);

tty = port->tty;

if (tty && urb->actual_length) {

//
如果有数据接收到的话
,
就把它存入
tty
驱动的缓冲中去

tty_buffer_request_room(tty, urb->actual_length);

tty_insert_flip_string(tty, data, urb->actual_length);

tty_flip_buffer_push(tty);

}

/* Continue trying to always read
*/

//
继续下一个接收操作
.

usb_fill_bulk_urb (port->read_urb, serial->dev,

usb_rcvbulkpipe (serial->dev,

port->bulk_in_endpointAddress),

port->read_urb->transfer_buffer,

port->read_urb->transfer_buffer_length,

((serial->type->read_bulk_callback) ?

serial->type->read_bulk_callback :

usb_serial_generic_read_bulk_callback), port);

result = usb_submit_urb(port->read_urb, GFP_ATOMIC);

if (result)

dev_err(&port->dev, "%s - failed resubmitting read urb, error %d/n", __FUNCTION__, result);

}

很明显
,
这个回调就是把从设备端收到的数据
push
到相对上层的
tty
驱动中去
,
随后
tty
子系统会把这些数据返回给用户
.

OK,
看完接收过程后我们在来看下发送过程
.

发送过程由用户发起
,
用户可以调用
write
系统调用来发数据
,
最后会跑到
tty
驱动的
write
函数中去
.

Usb-serial.c:

static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count)

{

struct usb_serial_port *port = tty->driver_data;

int retval = -EINVAL;

if (!port)

goto exit;

dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);

if (!port->open_count) {

dbg("%s - port not opened", __FUNCTION__);

goto exit;

}

/* pass on to the driver specific version of this function */

retval = port->serial->type->write(port, buf, count);
//
仅仅是调用
usb_serial_driver

write
函数

exit:

return retval;

}

这个函数
,
直接调用了
usb_serial_driver

write
函数
.

Generic.c:

int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *buf, int count)

{

struct usb_serial *serial = port->serial;

int result;

unsigned char *data;

dbg("%s - port %d", __FUNCTION__, port->number);

if (count == 0) {

dbg("%s - write request of 0 bytes", __FUNCTION__);

return (0);

}

/* only do something if we have a bulk out endpoint */

//
仅在有
bulk out
的端点上才能发送

if (serial->num_bulk_out) {

spin_lock(&port->lock);

if (port->write_urb_busy) {

spin_unlock(&port->lock);

dbg("%s - already writing", __FUNCTION__);

return 0;

}

port->write_urb_busy = 1;

spin_unlock(&port->lock);

//
把要发送的数据
,
发送的字节数存入
urb


count = (count > port->bulk_out_size) ? port->bulk_out_size : count;

memcpy (port->write_urb->transfer_buffer, buf, count);

data = port->write_urb->transfer_buffer;

usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, data);

/* set up our urb */

usb_fill_bulk_urb (port->write_urb, serial->dev,

usb_sndbulkpipe (serial->dev,

port->bulk_out_endpointAddress),

port->write_urb->transfer_buffer, count,

((serial->type->write_bulk_callback) ?

serial->type->write_bulk_callback :

usb_serial_generic_write_bulk_callback), port);

/* send the data out the bulk port */

port->write_urb_busy = 1;

result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
//
提交一个发送过程

if (result) {

dev_err(&port->dev, "%s - failed submitting write urb, error %d/n", __FUNCTION__, result);

/* don't have to grab the lock here, as we will retry if != 0 */

port->write_urb_busy = 0;

} else

result = count;

return result;

}

/* no bulk out, so return 0 bytes written */

return 0;

}

该函数把用户要发送的数据封装到一个
urb
中去
,
并把该
urb
提交给系统以执行一个发送的操作
,
发送完成后会调用
serial->type->write_bulk_callback
函数
.

接着看这个回调函数
:

Generic.c:

void usb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *regs)

{

struct usb_serial_port *port = (struct usb_serial_port *)urb->context;

dbg("%s - port %d", __FUNCTION__, port->number);

port->write_urb_busy = 0;

if (urb->status) {

dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);

return;

}

usb_serial_port_softint((void *)port);
//
请求传输更多的数据

schedule_work(&port->work);
//
调度一个工作

}

Usb-serial.c:

void usb_serial_port_softint(void *private)

{

struct usb_serial_port *port = private;

struct tty_struct *tty;

dbg("%s - port %d", __FUNCTION__, port->number);

if (!port)

return;

tty = port->tty;

if (!tty)

return;

tty_wakeup(tty);
//
请求传输更多的数据

}

其实很简单
,

其他的函数就不多做分析了
,

看懂了这个模块后
,
实际上对
USB
设备的驱动也就大体知道了其流程
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: