通用USB设备驱动源码分析.docx

上传人:b****6 文档编号:8249618 上传时间:2023-01-30 格式:DOCX 页数:40 大小:38.65KB
下载 相关 举报
通用USB设备驱动源码分析.docx_第1页
第1页 / 共40页
通用USB设备驱动源码分析.docx_第2页
第2页 / 共40页
通用USB设备驱动源码分析.docx_第3页
第3页 / 共40页
通用USB设备驱动源码分析.docx_第4页
第4页 / 共40页
通用USB设备驱动源码分析.docx_第5页
第5页 / 共40页
点击查看更多>>
下载资源
资源描述

通用USB设备驱动源码分析.docx

《通用USB设备驱动源码分析.docx》由会员分享,可在线阅读,更多相关《通用USB设备驱动源码分析.docx(40页珍藏版)》请在冰豆网上搜索。

通用USB设备驱动源码分析.docx

通用USB设备驱动源码分析

通用USB设备驱动源码分析

Author:

aaron

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

应该来说, 对于那些仅仅是用USB来通信, 在上层可看作tty设备, 不属于任何USB设备类型, 没有什么流控等的普通USB设备来说都可以使用这个驱动来作为设备驱动程序.下面就来对这样一种通用的驱动程序来进行详细的分析. 不对之处敬请指正!

 

为了能让usbserail模块支持我的设备, 我必须在命令行上输入如下命令:

sudomodprobeusbserialvendor=0x12d1product=0x1003

该命令用特权用户来加载usbserial模块,并把该模块依赖的模块一并加载进系统, 同时它还设置了usbserial的两个参数:

vendor,product,  很显然这两个参数是厂商ID和设备ID, 而作用就是用于匹配设备.

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

./drivers/usb/serial/Makefile:

#

#MakefilefortheUSBserialdevicedrivers.

#

 

#Objectfilelists.

 

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.ogeneric.obus.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:

structtty_driver*usb_serial_tty_driver;

staticint__initusb_serial_init(void)

{

       inti;

       intresult;

 

    //创建一个tty_driver对象, 对应的就是tty设备的驱动.

       usb_serial_tty_driver=alloc_tty_driver(SERIAL_TTY_MINORS); 

       if(!

usb_serial_tty_driver)

              return-ENOMEM;

 

       /*Initializeourglobaldata*/

       for(i=0;i

              serial_table[i]=NULL;   //该模块共支持SERIAL_TTY_MINORS个该类型设备.

       }

 

       result=bus_register(&usb_serial_bus_type);   //注册这条serialbus.

       if(result){

              err("%s-registeringbusdriverfailed",__FUNCTION__);

              gotoexit_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_driverfailed",__FUNCTION__);

              gotoexit_reg_driver;

       }

 

       /*registertheUSBdriver*/

       result=usb_register(&usb_serial_driver);   //注册一个usb驱动

       if(result<0){

              err("%s-usb_registerfailed",__FUNCTION__);

              gotoexit_tty;

       }

 

       /*registerthegenericdriver,ifweshould*/

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

       if(result<0){

              err("%s-registeringgenericdriverfailed",__FUNCTION__);

              gotoexit_generic;

       }

 

       info(DRIVER_DESC);

 

       returnresult;

   //失败时候的一些反向操作

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-returningwitherror%d",__FUNCTION__,result);

       put_tty_driver(usb_serial_tty_driver);

       returnresult;

}

该函数先创建并初始化好了一个tty_driver的对象, 并把该对象注册进系统, 该对象就是tty设备的驱动程序, 后面我们会看到他是如何于具体tty设备绑定在一起的.

usb_serial.c:

staticstructtty_operationsserial_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:

structbus_typeusb_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:

intusb_serial_generic_register(int_debug)

{

       intretval=0;

 

       debug=_debug;

#ifdefCONFIG_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;     //匹配类型

 

       /*registerourgenericdriverwithourselves*/

       retval=usb_serial_register(&usb_serial_generic_device);   //注册驱动

       if(retval)

              gotoexit;

       retval=usb_register(&generic_driver);     //注册驱动

       if(retval)

              usb_serial_deregister(&usb_serial_generic_device);

exit:

#endif

       returnretval;

}

该函数首先保存了命令通过命令行设备的vendor,product 用于以后设备匹配, 由此我们知道该驱动可以动态支持设备匹配. 接着该函数注册了usb_serial_generic_device驱动.

generic.c:

structusb_serial_driverusb_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:

intusb_serial_register(structusb_serial_driver*driver)

{

       intretval;

 

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

 

       if(!

driver->description)

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

 

       /*Addthisdevicetoourlistofdevices*/

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

 

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

       if(retval){

              err("problem%dwhenregisteringdriver%s",retval,driver->description);

              list_del(&driver->driver_list);

       }

       else

              info("USBSerialsupportregisteredfor%s",driver->description);

 

       returnretval;

}

其中的fixup_generic()函数仅仅是为driver赋上默认的操作函数.

Usb-serial.c:

#defineset_to_generic_if_null(type,function)                         \

       do{                                                  \

              if(!

type->function){                                \

                     type->function=usb_serial_generic_##function; \

                     dbg("Hadtooverridethe"#function          \

                             "usbserialoperationwiththegenericone.");\

                     }                                        \

       }while(0)

staticvoidfixup_generic(structusb_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:

intusb_serial_bus_register(structusb_serial_driver*driver)

{

       intretval;

 

       driver->driver.bus=&usb_serial_bus_type;    //注册到该bus下

       retval=driver_register(&driver->driver);

 

       returnretval;

}

 

最后usb_serial_generic_register()函数注册了一个generic_driver驱动.

generic.c:

staticstructusb_drivergeneric_driver={

       .name=         "usbserial_generic",

       .probe=  generic_probe,    //匹配函数

       .disconnect=  

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1