之前追踪代码用的grep命令效率太低了,所以这次下载C代码阅读跳转利器ctags、cscope用于分析代码

因为用的是Centos6.7所以需要用到yum install安装软件

[wuyujun@wuyujunlocalhost ~]$ sudo yum install ctags cscope

[wuyujun@wuyujunlocalhost ~]$ vim ~/.bashrc

#在最后加上两行,做命令的别名,建立索引文件只需输入tag即可

alias tag='cscope -Rbq && ctags --c-kinds=+defglmnstuvx --langmap=c:.c.h.ho.hem.het.hec.hev.him.hit.hic.hiv -R .'

alias tagclean='rm -f cscope.* tags'

[wuyujun@wuyujunlocalhost ~]$ source ~/.bashrc

[wuyujun@wuyujunlocalhost linux-3.0]$ tag

[wuyujun@wuyujunlocalhost linux-3.0]$ ls tags cscope*

cscope.in.out  cscope.out  cscope.po.out  tags

这样tag索引文件就建立好了

用法:

ctags主要是用来创建Vim可以使用的tag索引文件,使用ctags -R 即可对当前目录下的代码递归建立索引文件,文件保存在当前目录下,默认文件名为tags,文件大小和你要索引的代码量有关。

Vim在运行过程中需要知道tag的索引文件位置,如果不指定的话,Vim会在当前目录下寻找名为tags的文件作为tag索引文件。如果想使用某个目录下的索引文件,在该目录下启动Vim即可。也可以在启动后通过如下命令设置或改变索引文件的位置:set tags=索引文件路径

  1. F4 左侧分屏列出该C文件中所有的变量,函数列表;再按一下取消左侧小窗口;
  2. Ctrl+WW 左右两侧之间切换,左侧函数列表窗口在某个函数上按回车就会回到右侧该函数的定义处;
  3. Ctrl+] 光标放在某个宏,变量,函数上,按这两个键可以查看他们的定义
  4. CTRL+T 返回刚才的查找
  5. CTRL+\然后迅速按S 光标放在某个宏,变量,函数上,快速按这三个组合键可以查看它们所有出现的地方

USB转串口驱动分析

USB驱动存在于不同的子系统,块设备(U盘)、字符设备层(键盘)、TTY层(USB转串口)...

在这里要分析的是USB转串口的驱动,首先,要知道usbserial模块由哪些文件编译而成,这样才能有目的性的去分析其代码. 而要知道其组成当然是去其目录下看Makefile了,它位于内核源码目录下的./drivers/usb/serial/

1 #

2 # Makefile for the USB serial device drivers.

3 #

4

5 # Object file lists.

6

7 obj-$(CONFIG_USB_SERIAL)            += usbserial.o  #编译内核时如何编译该模块

8

9 usbserial-y := usb-serial.o generic.o bus.o #usbserial模块的组成

10

11 usbserial-$(CONFIG_USB_SERIAL_CONSOLE)  += console.o

12 usbserial-$(CONFIG_USB_EZUSB)       += ezusb.o

13

14 obj-$(CONFIG_USB_SERIAL_AIRCABLE)       += aircable.o

15 obj-$(CONFIG_USB_SERIAL_ARK3116)        += ark3116.o

16 obj-$(CONFIG_USB_SERIAL_BELKIN)         += belkin_sa.o

.......

重点需要看的是usb-serial.c,  generic.c, bus.c

1、usb-serial.c 就是usbserial模块的核心, 它主要用来接收设备端发来的数据并传送到上层, 同时也接收来自上层应用的数据,并组装成urb包发送给设备.

2、generic.c 对特定设备单独的操作,相当于是设备自己的驱动程序, 由于很多设备具有通用性, 所以对于没有特殊要求的设备都可以使用这个驱动来作为自己设备的驱动程序. 它有两个参数vendor和product,厂商ID和设备ID, 用于匹配设备.

3、bus.c  每个usb驱动和设备都必须要归入某一条总线上, 即都是归属于某条总线的,只有这样系统才能从特定一条总线开始找到每个驱动和设备并为他们匹配. 这个文件就是用来模拟一条总线, 而usbserial的每个驱动和设备都会注册到这条总线上来.

按照惯例,看驱动代码都是先从module_init开始看,虽说安装了ctags阅读代码利器,但是一开始也需要先找到对应的c代码才能进行结构体、变量、函数的跳转追踪,还是熟悉的grep

[wuyujun@wuyujunlocalhost linux-3.0]$ grep -n usb_serial_init -r ./

./tags:1682029:usb_serial_init  drivers/usb/serial/usb-serial.c /^module_init(usb_serial_init);$/;"     v

./tags:1682030:usb_serial_init  drivers/usb/serial/usb-serial.c /^static int __init usb_serial_init(void)$/;"   f       file:

匹配到二进制文件 ./.tmp_vmlinux1

匹配到二进制文件 ./vmlinux

匹配到二进制文件 ./arch/arm/boot/Image

./.tmp_System.map:518:c0018a2c t usb_serial_init

./.tmp_System.map:974:c001ec40 t __initcall_usb_serial_init6

./System.map:518:c0018a2c t usb_serial_init

./System.map:974:c001ec40 t __initcall_usb_serial_init6

匹配到二进制文件 ./vmlinux.o

匹配到二进制文件 ./drivers/usb/serial/usbserial.o

匹配到二进制文件 ./drivers/usb/serial/built-in.o

./drivers/usb/serial/usb-serial.c:1218:static int __init usb_serial_init(void)

./drivers/usb/serial/usb-serial.c:1310:module_init(usb_serial_init);

./drivers/usb/serial/ftdi_sio.c:1808:   /* Termios defaults are set by usb_serial_init. We don't change

匹配到二进制文件 ./drivers/usb/serial/usb-serial.o

匹配到二进制文件 ./drivers/usb/built-in.o

匹配到二进制文件 ./drivers/built-in.o

匹配到二进制文件 ./.tmp_vmlinux2

module_init(usb_serial_init);在./drivers/usb/serial/usb-serial.c

struct tty_driver *usb_serial_tty_driver; //usb_serial_tty_driver是tty_driver结构体类型的指针, 对应的tty设备的驱动.

static int __init usb_serial_init(void)

{

int i;

int result;

usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS)//创建tty结构函数

if (!usb_serial_tty_driver)

return -ENOMEM;

/* Initialize our global data */

for (i = 0; i < SERIAL_TTY_MINORS; ++i) //该模块共支持SERIAL_TTY_MINORS个该类型设备.

serial_table[i] = NULL;

result = bus_register(&usb_serial_bus_type);//注册usb_serial_bus_type类型的总线

if (result) {

printk(KERN_ERR "usb-serial: %s - registering bus driver "

"failed\n", __func__);

goto exit_bus;

}

usb_serial_tty_driver->owner = THIS_MODULE; //驱动模块拥有者

usb_serial_tty_driver->driver_name = "usbserial"; //用来在/proc/tty/drivers文件中向用户描述驱动程序的状态,并且在sysfs的tty类目录中显示当前被加载的tty驱动程序

usb_serial_tty_driver->name =   "ttyUSB"; //分配给单独tty结点的名字,通过在该名字末尾添加tty设备序号来创建tty设备

usb_serial_tty_driver->major = SERIAL_TTY_MAJOR; //设置主设备号

usb_serial_tty_driver->minor_start = 0; //次设备号开始的序号

usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; //设置tty驱动类型,可分控制台、串口和pty,这里是串口驱动

usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;//描述向tty核心注册的是何种tty驱动,SERIAL_TYPE_NORMAL可以被串行类设备使用

usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |

TTY_DRIVER_DYNAMIC_DEV; //表明驱动状态和类型

usb_serial_tty_driver->init_termios = tty_std_termios; //termios提供一系列串口的设置值    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD

| HUPCL | CLOCAL; //设置串口默认配置

usb_serial_tty_driver->init_termios.c_ispeed = 9600; //串口输入波特率

usb_serial_tty_driver->init_termios.c_ospeed = 9600; //串口输出波特率

tty_set_operations(usb_serial_tty_driver, &serial_ops); //赋值tty设备的操作集合,操作函数在serial_ops中定义

result = tty_register_driver(usb_serial_tty_driver); //串口驱动注册,在之前的博客串口驱动分析说到过

if (result) {

printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",

__func__);

goto exit_reg_driver;

}

/* register the USB driver */

result = usb_register(&usb_serial_driver); //用于往USB核心注册usb驱动程序

if (result < 0) {

printk(KERN_ERR "usb-serial: %s - usb_register failed\n",

__func__);

goto exit_tty;

}

/* register the generic driver, if we should */

result = usb_serial_generic_register(debug); //注册generic驱动程序

if (result < 0) {

printk(KERN_ERR "usb-serial: %s - registering generic "

"driver failed\n", __func__);

goto exit_generic;

}

printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");

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:

printk(KERN_ERR "usb-serial: %s - returning with error %d\n",

__func__, result);

put_tty_driver(usb_serial_tty_driver);

return result;

}

module_init(usb_serial_init);

这里创建并初始化好了一个tty_driver类型结构体, 并把驱动注册进系统,重点看

usb_register(&usb_serial_driver); //用于往USB核心注册usb驱动程序usb_serial_generic_register(debug); //注册generic驱动程序以及提供给应用层的操作集合serial_ops结构体

先看usb_register(&usb_serial_driver):usb_register()-->usb_register_driver()-->driver_register()将 struct usb_driver usb_serial_driver注册到内核

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,

.supports_autosuspend = 1,

};

重点是usb_serial_probe这个匹配函数,之后还会说到

然后看usb_serial_generic_register(),因为下载好了ctags,并且建立了引索,这时候查找函数就非常高效了,Ctrl+],找到变量、结构体、函数定义的地方,光标移到看usb_serial_generic_register使用Ctrl+]很快就跳转到linux-3.0/drivers/usb/serial/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);//注册usb_serial_generic_device驱动.    

if (retval)

goto exit;

retval = usb_register(&generic_driver);

if (retval)

usb_serial_deregister(&usb_serial_generic_device); //卸载usb_serial_generic_device驱动.

exit:

#endif

return retval;

}

保存了设备的vendor,product 用于设备匹配, 由此知道该驱动可以动态支持设备匹配. 接着函数usb_serial_register(&usb_serial_generic_device); //注册usb_serial_generic_device驱动.     

int usb_serial_register(struct usb_serial_driver *driver)

{

/* must be called with BKL held */

int retval;

if (usb_disabled())

return -ENODEV;

fixup_generic(driver); //driver赋上默认的操作函数

if (!driver->description)

driver->description = driver->driver.name;

if (!driver->usb_driver) {

WARN(1, "Serial driver %s has no usb_driver\n",

driver->description);

return -EINVAL;

}

driver->usb_driver->supports_autosuspend = 1;

/* Add this device to our list of devices */

mutex_lock(&table_lock);

list_add(&driver->driver_list, &usb_serial_driver_list); //加入驱动列表

retval = usb_serial_bus_register(driver);//把该驱动注册进usb serial bus下

if (retval) {

printk(KERN_ERR "usb-serial: problem %d when registering "

"driver %s\n", retval, driver->description);

list_del(&driver->driver_list);

} else

printk(KERN_INFO "USB Serial support registered for %s\n",

driver->description);

mutex_unlock(&table_lock);

return retval;

}

这里看看fixup_generic()函数

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, disconnect);

set_to_generic_if_null(device, release);

set_to_generic_if_null(device, process_read_urb);

set_to_generic_if_null(device, prepare_write_buffer);

}

#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)

fixup_generic()函数仅给driver赋上默认的操作函数.所以fixup_generic(driver);最后得到的是

usb_serial_driver.open = usb_serial_generic_open ;

usb_serial_driver.write= usb_serial_generic_write;

......

然后看usb_serial_bus_register

int usb_serial_bus_register(struct usb_serial_driver *driver)

{

int retval;

driver->driver.bus = &usb_serial_bus_type; //注册到usb_serial_bus_type类型的总线下

spin_lock_init(&driver->dynids.lock);

INIT_LIST_HEAD(&driver->dynids.list);

retval = driver_register(&driver->driver); //注册驱动,

return retval;

}

usb_serial_generic_register()设置厂商id和设备id还有匹配类型等-->usb_serial_register(); 调用fixup_generic()函数给usb_serial_driver结构体赋上默认的操作函数list_add()加入驱动列表-->usb_serial_bus_register(driver);把该驱动注册进usb serial bus下-->driver_register(&driver->driver); 注册驱动。

简而言之就是usb_serial_generic_register()调用usb_serial_register(&usb_serial_generic_device);最后把struct usb_serial_driver usb_serial_generic_device这个驱动注册到内核,接下来就看看struct usb_serial_driver usb_serial_generic_device这个结构体

/* All of the device info needed for the Generic Serial Converter */

struct usb_serial_driver usb_serial_generic_device = {

.driver = {

.owner =    THIS_MODULE,

.name =     "generic",

},

.id_table =     generic_device_ids,

.usb_driver =       &generic_driver,

.num_ports =        1,

.disconnect =       usb_serial_generic_disconnect,

.release =      usb_serial_generic_release,

.throttle =     usb_serial_generic_throttle,

.unthrottle =       usb_serial_generic_unthrottle,

.resume =       usb_serial_generic_resume,

};

之前可能会奇怪为什么注册驱动,而却叫做usb_serial_generic_device,USB串口通用设备,看完这个结构体就会发现结构体成员里.usb_driver = &generic_driver,其实是把generic_driver注册到内核

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_probe匹配函数,继续追踪,在linux-3.0/drivers/usb/serial/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_register(&usb_serial_driver)里的struct usb_driver usb_serial_driver这个结构体里就见到过,现在来看看usb_serial_probe,在linux-3.0/drivers/usb/serial/usb-serial.c

Usb_serial_probe是真长(ORZ跪了)...下面流程图有助于分析,转至:

https://blog.csdn.net/txxm520/article/details/8934706

int usb_serial_probe(struct usb_interface *interface,

const struct usb_device_id *id)

{

struct usb_device *dev = interface_to_usbdev(interface); //根据probe函数的参数interface获取usb_device接口。最终通过container_of宏获得结构体,总而言之就是通过 USB 接口来获得 usb 设备

struct usb_serial *serial = NULL;

struct usb_serial_port *port;

struct usb_host_interface *iface_desc;

struct usb_endpoint_descriptor *endpoint;

struct usb_serial_driver *type = NULL;

......

mutex_lock(&table_lock);

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

....

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

......

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

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

/* check out the endpoints */

iface_desc = interface->cur_altsetting; //设备接口的当前设置

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

endpoint = &iface_desc->endpoint[i].desc;//检查当前接口的端点类型,并保存,对于 usb-serial generic 设备,常见的端点只有 bulk out或 bulk in 端点

if (usb_endpoint_is_bulk_in(endpoint)) { //bulk in 的端点

/* we found a bulk in endpoint */

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

bulk_in_endpoint[num_bulk_in] = endpoint;

++num_bulk_in;

}

if (usb_endpoint_is_bulk_out(endpoint)) {  //bulk out的端点

/* we found a bulk out endpoint */

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

bulk_out_endpoint[num_bulk_out] = endpoint;

++num_bulk_out;

}

if (usb_endpoint_is_int_in(endpoint)) { //中断 in 端点

/* we found a interrupt in endpoint */

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

interrupt_in_endpoint[num_interrupt_in] = endpoint;

++num_interrupt_in;

}

if (usb_endpoint_is_int_out(endpoint)) { //中断out端点

/* we found an interrupt out endpoint */

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

interrupt_out_endpoint[num_interrupt_out] = endpoint;

++num_interrupt_out;

}

}

/*该段代码主要是获取该设备使用的各个类型及方向的端点描述府, 并保存起来*/

......

#ifdef CONFIG_USB_SERIAL_GENERIC

if (type == &usb_serial_generic_device) {

num_ports = num_bulk_out;

if (num_ports == 0) {

dev_err(&interface->dev,

"Generic device with no bulk out, not allowed.\n");

kfree(serial);

module_put(type->driver.owner);

return -EIO;

}

}

#endif

if (!num_ports) {

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

if (type->calc_num_ports)

num_ports = type->calc_num_ports(serial);

if (!num_ports)

num_ports = type->num_ports;

}

serial->num_ports = num_ports;

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;

/* found all that we need */

dev_info(&interface->dev, "%s converter detected\n",

type->description);

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

/* we don't use num_ports here because some devices have more

endpoint pairs than ports */

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);

serial->num_port_pointers = max_endpoints;

dbg("%s - setting up %d port structures for this device",

__func__, max_endpoints);

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

port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);//用struct usb_serial_port来描述一个 tty端口,每个端口都有自己的工作模式

if (!port)

goto probe_error;

tty_port_init(&port->port); //初始化端口

port->port.ops = &serial_port_ops; //设置操作集合,serial_port_ops,应用层调用open、read、write会调用到serial_port_ops这里面的函数,之后会说到

port->serial = serial;

spin_lock_init(&port->lock);

/* Keep this for private driver use for the moment but

should probably go away */

INIT_WORK(&port->work, usb_serial_port_work); //初始化工作队列,其在 serial_write 的回调函数中被调用

serial->port[i] = port;//一个serial包含若干个port

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

port->dev.driver = NULL;

port->dev.bus = &usb_serial_bus_type;

port->dev.release = &port_release;

device_initialize(&port->dev);

}

/* set up the endpoint information */

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

endpoint = bulk_in_endpoint[i];

port = serial->port[i];

port->read_urb = usb_alloc_urb(0, GFP_KERNEL); //urb结构体不能静态创建,必须使用usb_alloc_urb来创

if (!port->read_urb) {

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

goto probe_error;

}

buffer_size = max_t(int, serial->type->bulk_in_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;

}

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); //批量Urb初始化,read_bulk_callback是读回调函数

}

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

int j;

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;

}

if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))

goto probe_error;

buffer_size = serial->type->bulk_out_size;

if (!buffer_size)

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); //批量Urb初始化,write_bulk_callback是写回调函数

......

if (get_free_serial(serial, num_ports, &minor) == NULL) { //获取一个空闲的serial_table

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

goto probe_error;

}

/* 通过get_free_serial这个函数找到了一个空闲的serial_table项, 并把描述设备的usb_serial对象保存在其中, 在以后对设备的使用中, 可以轻易的通过minor号来找到这个usb_serial. */

serial->minor = minor;

/* 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;

}

}

usb_serial_console_init(debug, minor);

exit:

/* success */

usb_set_intfdata(interface, serial); //interface对象里保存usb_serial对象地址,以方便以后使用

module_put(type->driver.owner);

return 0;

probe_error:

usb_serial_put(serial);

module_put(type->driver.owner);

return -EIO;

}

最后usb_serial_probe()会调用device_add(),device_add()在platform总线LED驱动分析时就见到过,流程如下:

device_add()-->bus_probe_device()-->device_attch()-->__device_attch()-->really_probe()-->probe()

这里要注意的是,在USB转串口总线下面与platform总线有点不同

__device_attch()

driver_match_device()

           return drv->bus->match ? drv->bus->match(dev, drv) : 1;

之前分析的platform总线上没有match函数所以返回1,接着下面直接调用really_probe()而USB转串口的总线是有match函数的,所以会调用USB转串口总线的match函数,下面看struct bus_type usb_serial_bus_type追踪USB转串口的match查找函数和probe匹配函数

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,

.drv_attrs =    drv_attrs,

};

看 usb_serial_device_match 函数

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) //判断struct usb_serial_device是否一致

return 1;

return 0;

}

match函数return 1之后接下来会调用总线的 probe 函数

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;

}

if (port->dev_state != PORT_REGISTERING)

goto exit;

driver = port->serial->type;

if (driver->port_probe) {

retval = driver->port_probe(port);

if (retval)

goto exit;

}

retval = device_create_file(dev, &dev_attr_port_number);

if (retval) {

if (driver->port_remove)

retval = driver->port_remove(port);

goto exit;

}

minor = port->number;

 tty_register_device(usb_serial_tty_driver, minor, dev);//将设备与驱动绑定到一块并注册

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

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

driver->description, minor);

exit:

return retval;

}

struct device *tty_register_device(struct tty_driver *driver, unsigned index,

struct device *device)

{

char name[64];

dev_t dev = MKDEV(driver->major, driver->minor_start) + index; //获取主次设备号

if (index >= driver->num) {

printk(KERN_ERR "Attempt to register invalid tty line number "

" (%d).\n", index);

return ERR_PTR(-EINVAL);

}

if (driver->type == TTY_DRIVER_TYPE_PTY)

pty_line_name(driver, index, name);

else

tty_line_name(driver, index, name);

return device_create(tty_class, device, dev, NULL, name); //创建设备

}

到了这一步该设备终于 tty_driver 绑定在了一起了,同时在/dev 目录下也创建了相应的设备文件了,应用层也就可以使用这个设备了。

以上是USB转串口驱动的初始化注册,打开、读、写等操作留到下一篇讲吧。。

参考:https://blog.csdn.net/aaronychen/article/details/3555885

USB转串口驱动分析(一)相关推荐

  1. linux下usb转串口驱动分析

    linux下usb转串口驱动分析 分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报 linux struct interface returni ...

  2. USB转串口驱动分析(二)

    在static int __init usb_serial_init(void)里 tty_set_operations(usb_serial_tty_driver, &serial_ops) ...

  3. Linux环境下使用 USB转串口驱动(二)

    minicom是linux下串口通信的软件,它的使用完全依靠键盘的操作,虽然没有"超级终端"那么易用,但是使用习惯之后读者将会体会到它的高效与便利,下面将讲解minicom的安装和 ...

  4. STM32 USB转串口驱动安装不成功出现黄色感叹号解决方法!

    STM32 USB转串口驱动安装不成功出现黄色感叹号解决方法! 参考文章: (1)STM32 USB转串口驱动安装不成功出现黄色感叹号解决方法! (2)https://www.cnblogs.com/ ...

  5. CH341SER CH340SER USB转串口驱动

    CH341SER CH340SER USB转串口驱动适用于同型号的设备,这个USB转串口设备我们可能用不到,但是对于专业的开发者来说可能会用到,将设备与电脑连接后安装一下小编提供的USB转串口驱动程序 ...

  6. ubuntu安装USB转串口驱动(PL2303)

    在Ubuntu下利用minicom进行嵌入式开发时可能会用到USB转串口,这时就会用到USB转串口驱动,以前的Ubuntu是直接将此驱动编译进内核,但不知道从哪个版本开始Ubuntu将其从内核去掉了, ...

  7. linux装pl2303驱动下载,Linux下安装USB转串口驱动(PL2303)

    主机:Gentoo Linux 11.2 内核版本:Linux 3.0.6 以前在Ubuntu下USB转串口驱动已经编译进内核,而编译的Gentoo内核没有编译进去,这里将内核中PL2303驱动 co ...

  8. pl2303hxa串口线驱动_PL2303 USB转串口驱动64位(非认证线缆可用)_下载_热门驱动_驱动精灵...

    Prolific PL2303 (串口线)USB转串口驱动3.3.2.102版For WinXP-64/Vista-64/Win7-64/Win8-64/Win8.1-64/Win10-64.目前主流 ...

  9. Linux下安装USB转串口驱动(PL2303)

    分类: 嵌入式ARM+Linux 2012-01-11 22:26  2077人阅读  评论(0)  收藏  举报 主机:Gentoo Linux 11.2 内核版本:Linux 3.0.6 原创作品 ...

最新文章

  1. ubuntu安装五笔输入法(ibus-table-wubi)
  2. UIAutomator 2
  3. 为什么微博“越改版越被骂“?数据却”越骂越好“?
  4. 女主播还是女主播???
  5. python基础入门(3)之字符串
  6. css背景从左到右颜色渐变,CSS:linear-gradient()背景颜色渐变
  7. linux查看nec进程状态,【linux】 /proc/PID/stat
  8. ServletContextListener使用详解
  9. matplotlib中改变字体的方法
  10. [黑苹果双系统]macOS 12.4正式版OpenCore/Clover/winPE原版镜像
  11. VS2008 ~ VS2019下载地址
  12. jspstudy启动mysql失败_JspStudy配置Jspxcms安装教程,以及数据库设置;
  13. 本地上传文件到服务器
  14. nnt第三代接任务软件
  15. 不要低估实现难度,聊聊当下热议的“元宇宙”是什么?
  16. dp怎么接显示器和主机_怎样设置显示器DP接口信号优先?
  17. 常见的27个电源符号
  18. 10步Navicat for Mysql12.1.20破解激活方法(适用于Navica12.1系列软件)
  19. C语言那些事之脉冲丢包率检测
  20. 比尔盖茨夫妇现身贫民窟

热门文章

  1. 【Unity】一些不错的unity插件
  2. PPT中建立打开Onenote特定笔记本的超链接
  3. 【转贴】关于开发数学软件的想法
  4. Android端的彩票开奖查询系统
  5. 2017-4-15,16
  6. 神经系统图 基本结构图,神经系统的组织结构图
  7. 小程序与H5及混合应用测试
  8. 计算机组成1046Q表示什么,计算器里的tanh是什么意思
  9. B站2020跨年晚会,被弹幕刷屏的节目是哪个,发弹幕最多的人又是谁?
  10. docker国内镜像加速配置