之前追踪代码用的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=索引文件路径
- F4 左侧分屏列出该C文件中所有的变量,函数列表;再按一下取消左侧小窗口;
- Ctrl+WW 左右两侧之间切换,左侧函数列表窗口在某个函数上按回车就会回到右侧该函数的定义处;
- Ctrl+] 光标放在某个宏,变量,函数上,按这两个键可以查看他们的定义
- CTRL+T 返回刚才的查找
- 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