资源描述:
《通用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.ousbserial-obj-$(CONFIG_USB_EZUSB) +=ezusb.o usbserial-objs :=usb-serial.ogeneric.obus.o$(usbserial-obj-y) #OK, 就是usbserial模块的组成了. obj-$(CONFIG_USB_SERIAL_AIRPRIME) +=airprime.oobj-$(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;iowner=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= 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:staticintgeneric_probe(structusb_interface*interface, conststructusb_device_id*id){ conststructusb_device_id*id_pattern; id_pattern=usb_match_id(interface, generic_device_ids); //设备匹配 if(id_pattern!=NULL) returnusb_serial_probe(interface,id); //进一步匹配 return-ENODEV;}如果接入系统的设备的vendor和product与我们驱动支持的设备列表匹配则调用usb_serial_probe来进一步匹配.usb_serial_probe函数比较长, 我们一段段的来看usb-serial.c: intusb_serial_probe(structusb_interface*interface, conststructusb_device_id*id){ structusb_device*dev=interface_to_usbdev(interface); structusb_serial*serial=NULL; structusb_serial_port*port; structusb_host_interface*iface_desc; structusb_endpoint_descriptor*endpoint; structusb_endpoint_descriptor*interrupt_in_endpoint[MAX_NUM_PORTS]; structusb_endpoint_descriptor*interrupt_out_endpoint[MAX_NUM_PORTS]; structusb_endpoint_descriptor*bulk_in_endpoint[MAX_NUM_PORTS]; structusb_endpoint_descriptor*bulk_out_endpoint[MAX_NUM_PORTS]; structusb_serial_driver*type=NULL; intretval; intminor; intbuffer_size; inti; intnum_interrupt_in=0; intnum_interrupt_out=0; intnum_bulk_in=0; intnum_bulk_out=0; intnum_ports=0; intmax_endpoints; type=search_serial_device(interface); //获取该设备匹配的驱动 if(!type){ dbg("nonematched"); return-ENODEV; }......}首先是找到合适的驱动程序.usb-serial.c:staticstructusb_serial_driver*search_serial_device(structusb_interface*iface){ structlist_head*p; conststructusb_device_id*id; structusb_serial_driver*t; /*Checkiftheusbidmatchesaknowndevice*/ list_for_each(p,&usb_serial_driver_list){ t=list_entry(p,structusb_serial_driver,driver_list); id=usb_match_id(iface,t->id_table); //看设备列表是否匹配 if(id!=NULL){ dbg("descriptormatches"); returnt; //返回匹配的驱动 } } returnNULL;}实际上这边的匹配和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-outofmemory ",__FUNCTION__); return-ENOMEM; } /*ifthisdevicetypehasaprobefunction,callit*/ if(type->probe){ //从上面分析的代码可知这里的probe函数没有赋值 conststructusb_device_id*id; if(!try_module_get(type->driver.owner)){ dev_err(&interface->dev,"modulegetfailed,exiting "); 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("subdriverrejecteddevice"); kfree(serial); returnretval; } }....这段代码可知, 主要是创建一个usb_serial的对象, 用于保存该设备的详细信息, 一般的驱动程序都会为自己匹配的设备创建一个描用于描述该设备的对象. 在以后的所有操作中如读写等都会直接从这个对象里获取相应的信息.usb-serial.c:staticstructusb_serial*create_serial(structusb_device*dev, structusb_interface*interface, structusb_serial_driver*driver){ structusb_serial*serial; serial=kmalloc(sizeof(*serial),GFP_KERNEL); //闯将该对象 if(!serial){ dev_err(&dev->dev,"%s-outofmemory ",__FUNCTION__); returnNULL; } //初始化该对象 memset(serial,0,sizeof(*serial)); serial->dev=usb_get_dev(dev); //增加dev的引用计数 serial->type=driver; serial->interface=interface; kref_init(&serial->kref); returnserial;}这个函数就是用来创建usb_serial对象的,并把相关信息保存在里面.继续看usb_serial_probe()usb-serial.c:.... /*descriptormatches,let'sfindtheendpointsneeded*/ /*checkouttheendpoints*/ //查找该设备使用的endpoint的描述符, 并检查是否正确 iface_desc=interface->cur_altsetting; //接口描述符 for(i=0;idesc.bNumEndpoints;++i){ endpoint=&iface_desc->endpoint[i].desc; //端点描述符 if((endpoint->bEndpointAddress&0x80)&& ((endpoint->bmAttributes&3)==0x02)){ /*wefoundabulkinendpoint*/ //bulkin 的端点 dbg("foundbulkinonendpoint%d",i); bulk_in_endpoint[num_bulk_in]=endpoint; ++num_bulk_in; } if(((endpoint->bEndpointAddress&0x80)==0x00)&& ((endpoint->bmAttributes&3)==0x02)){ /*wefoundabulkoutendpoint*/ //bulkout的端点 dbg("foundbulkoutonendpoint%d",i); bulk_out_endpoint[num_bulk_out]=endpoint; ++num_bulk_out; } if((endpoint->bEndpointAddress&0x80)&& ((endpoint->bmAttributes&3)==0x03)){ /*wefoundainterruptinendpoint*/ //中断 in 端点 dbg("foundinterruptinonendpoint%d",i); interrupt_in_endpoint[num_interrupt_in]=endpoint; ++num_interrupt_in; } if(((endpoint->bEndpointAddress&0x80)==0x00)&& ((endpoint->bmAttributes&3)==0x03)){ /*wefoundaninterruptoutendpoint*///中断 out 端点 dbg("foundinterruptoutonendpoint%d",i); interrupt_out_endpoint[num_interrupt_out]=endpoint; ++num_interrupt_out; } }.....该段代码主要是获取该设备使用的各个类型及方向的端点描述府, 并保存起来, 关于端点的类型与方向可以参考USB的规范.继续看usb_serial_probe()usb-serial.c:....#ifdefined(CONFIG_USB_SERIAL_PL2303)||defined(CONFIG_USB_SERIAL_PL2303_MODULE) /*BEGINHORRIBLEHACKFORPL2303*/ /*thisisneededduetothelooneywayitsendpointsaresetup*/ 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]){ /*checkouttheendpointsoftheotherinterface*/ iface_desc=dev->actconfig->interface[0]->cur_altsetting; for(i=0;idesc.bNumEndpoints;++i){ endpoint=&iface_desc->endpoint[i].desc; if((endpoint->bEndpointAddress&0x80)&& ((endpoint->bmAttributes&3)==0x03)){ /*wefoundainterruptinendpoint*/ dbg("foundinterruptinforProlificdeviceonseparateinterface"); interrupt_in_endpoint[num_interrupt_in]=endpoint; ++num_interrupt_in; } } } /*NowmakesurethePL-2303isconfiguredcorrectly. *Ifnot,giveupnowandhopethishackwillwork *properlyduringalaterinvocationofusb_serial_probe */ if(num_bulk_in==0||num_bulk_out==0){ dev_info(&interface->dev,"PL-2303hack:descriptorsmatchedbutendpointsdidnot "); kfree(serial); return-ENODEV; } } /*ENDHORRIBLEHACKFORPL2303*/#endif上面这段代码主要是用于特定类型设备的(PL2303), 这里我们不用管他.接着看usb_serial_probe()usb-serial.c:.... /*foundallthatweneed*/ dev_info(&interface->dev,"%sconverterdetected ",type->description); #ifdefCONFIG_USB_SERIAL_GENERIC //这个宏定义了, 因为我们使用的是通用USB驱动. if(type==&usb_serial_generic_device){ //这个if为TRUE(上面分析过了) num_ports=num_bulk_out; if(num_ports==0){ //bulkout端点必须要有 dev_err(&interface->dev,"Genericdevicewithnobulkout,notallowed. "); kfree(serial); return-EIO; } }#endif if(!num_ports){ //由于走到了上面那个if段,因此这里的num_ports肯定不为0 /*ifthisdevicetypehasacalc_num_portsfunction,callit*/ if(type->calc_num_ports){ if(!try_module_get(type->driver.owner)){ dev_err(&interface->dev,"modulegetfailed,exiting "); 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,"Nomorefreeserialdevices "); kfree(serial); return-ENOMEM; }usbserial模块总共支持SERIAL_TTY_MINORS个设备, 它为每个设备都分配了一个serial_table项, 用于保存usb_serial对象, 方便以后直接通过minor号获取usb_serial对象.usb-serial.c:staticstructusb_serial*get_free_serial(structusb_serial*serial,intnum_ports,unsignedint*minor){ unsignedinti,j; intgood_spot; dbg("%s%d",__FUNCTION__,num_ports); *minor=0; for(i=0;i=SERIAL_TTY_MINORS)||(serial_table[i+j])){ good_spot=0; i+=j; break; } if(good_spot==0) continue; *minor=i; //获取minor号 dbg("%s-minorbase=%d",__FUNCTION__,*minor); for(i=*minor;(i<(*minor+num_ports))&&(iminor=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; /*createourports,weneedasmanyasthemaxendpoints*/ /*wedon'tusenum_portsherecauzsomedeviceshavemoreendpointpairsthanports*/ //对于generic的驱动来说一般都只有一个bulkin,一个bulkout,一个interruptin,一个interruptout 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-settingup%dportstructuresforthisdevice",__FUNCTION__,max_endpoints); for(i=0;inumber=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间互不干扰 /*setuptheendpointinformation*/ for(i=0;iport[i]; port->read_urb=usb_alloc_urb(0,GFP_KERNEL); //分配urb if(!port->read_urb){ dev_err(&interface->dev,"Nofreeurbsavailable "); gotoprobe_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'tallocatebulk_in_buffer "); gotoprobe_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;iport[i]; port->write_urb=usb_alloc_urb(0,GFP_KERNEL); if(!port->write_urb){ dev_err(&interface->dev,"Nofreeurbsavailable "); gotoprobe_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'tallocatebulk_out_buffer "); gotoprobe_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;iport[i]; port->interrupt_in_urb=usb_alloc_urb(0,GFP_KERNEL); if(!port->interrupt_in_urb){ dev_err(&interface->dev,"Nofreeurbsavailable "); gotoprobe_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'tallocateinterrupt_in_buffer "); gotoprobe_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); } }elseif(num_interrupt_in){ //如果有interruptin 而没有read_int_callback则是错误的 dbg("thedeviceclaimstosupportinterruptintransfers,butread_int_callbackisnotdefined"); } //从上面这个if可以看出generic驱动的设备不应该有interruptin 端点, 有也可以但是这个端点将//不起作用.//下面这个if和上面的这个if功能一模一样. if(serial->type->write_int_callback){ for(i=0;iport[i]; port->interrupt_out_urb=usb_alloc_urb(0,GFP_KERNEL); if(!port->interrupt_out_urb){ dev_err(&interface->dev,"Nofreeurbsavailable "); gotoprobe_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'tallocateinterrupt_out_buffer "); gotoprobe_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); } }elseif(num_interrupt_out){ dbg("thedeviceclaimstosupportinterruptouttransfers,butwrite_int_callbackisnotdefined"); } /*ifthisdevicetypehasanattachfunction,callit*/ if(type->attach){ //对于generic驱动, 没有设置这个attach函数 if(!try_module_get(type->driver.owner)){ dev_err(&interface->dev,"modulegetfailed,exiting "); gotoprobe_error; } retval=type->attach(serial); module_put(type->driver.owner); if(retval<0) gotoprobe_error; if(retval>0){ /*quietlyacceptthisdevice,butdon'tbindtoaserialport *asit'sabouttodisappear*/ gotoexit; } } /*registeralloftheindividualportswiththedrivercore*/ for(i=0;iport[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对象地址,以方便以后使用 return0; //错误处理probe_error: for(i=0;iport[i]; if(!port) continue; if(port->read_urb) usb_free_urb(port->read_urb); kfree(port->bulk_in_buffer); } for(i=0;iport[i]; if(!port) continue; if(port->write_urb) usb_free_urb(port->write_urb); kfree(port->bulk_out_buffer); } for(i=0;iport[i]; if(!port) continue; if(port->interrupt_in_urb) usb_free_urb(port->interrupt_in_urb); kfree(port->interrupt_in_buffer); } for(i=0;iport[i]; if(!port) continue; if(port->interrupt_out_urb) usb_free_urb(port->interrupt_out_urb); kfree(port->interrupt_out_buffer); } /*returntheminorrangethatthisdevicehad*/ return_serial(serial); /*freeupanymemorythatweallocated*/ for(i=0;inum_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的调用顺序: 1device_add(): 把device注册到相应的bus上去,并创建相应的devicefile,最后调用bus_attach_device()2bus_attach_device()调用device_attach(dev)3device_attach(): 调用bus_for_each_drv()遍历bus上的每个driver,当找到一个driver则用__device_attach()来判断是否匹配4__device_attach(): 直接调用driver_probe_device(drv,dev)5driver_probe_device(): 首先如果driver所在总线有match函数则先调用这个match来匹配,如不匹配则直接返回错误,否则接着调用really_probe(dev,drv)6really_probe(): 先判断dev所在总线是否有probe函数,有则调用它来匹配,失败则返回,正确则成功,如果总线没有probe则判断drv是否有probe函数,有则调用并匹配它.7drv->prob(): 一般它是一类设备的probe,在它里面它会调用具体某个drv的probe函数,这个函数是在我们的驱动程序里面注册的. device_register()里会调用device_add(). 因此由 5 可知 系统会先调用总线上的match函数来匹配. 对于我们的总线就是usb_serial_bus, 它的match函数就是usb_serial_device_match(), 至于为什么是这条总线, 通过前面的代码分析, 我们知道我们的设备也好, 我们的驱动也好都是注册在这条总线上的.下面我们就来分析usb_serial_device_match()Bus.c:staticintusb_serial_device_match(structdevice*dev,structdevice_driver*drv){ structusb_serial_driver*driver; conststructusb_serial_port*port; /* *driversarealreadyassignedtoportsinserial_probesoit's *asimplecheckhere. */ port=to_usb_serial_port(dev); //获取usb_serial_port对象 if(!port) return0; driver=to_usb_serial_driver(drv); //获取usb_serial_driver对象 if(driver==port->serial->type) //匹配否? return1; return0;}很显然,通过前面的分析可知, 这里肯定是匹配的.接着通过上面probe过程的6可知会调用总线的probe函数,这里就是usb_serial_device_probeBus.c:staticintusb_serial_device_probe(structdevice*dev){ structusb_serial_driver*driver; structusb_serial_port*port; intretval=0; intminor; port=to_usb_serial_port(dev); if(!port){ retval=-ENODEV; gotoexit; } driver=port->serial->type; if(driver->port_probe){ if(!try_module_get(driver->driver.owner)){ dev_err(dev,"modulegetfailed,exiting "); retval=-EIO; gotoexit; } retval=driver->port_probe(port); module_put(driver->driver.owner); if(retval) gotoexit; } minor=port->number; tty_register_device(usb_serial_tty_driver,minor,dev); //呵呵, 这里总算把tty_driver和device绑定//起来了 dev_info(&port->serial->dev->dev, "%sconverternowattachedtottyUSB%d ", driver->description,minor); exit: returnretval;}到了这一步该设备就和tty_driver绑定在了一起了, 同时在/dev下也创建了相应的设备文件了, 也就是说应用层可以使用这个设备了.这样整个对设备的probe过程才算真的完成了.OK, 设备从USB口连上系统, 并被系统认出及probe的真个过程就基本完成了, 从此以后设备就进入了就绪状态. 下面我们就开始分析对设备的操作流程了. 要使用设备当然要先打开这个设备了, 应用层调用open系统调用来打开这个设备, 它最终会跑到我们tty_driver的open函数里面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,};这里就是serial_open了.usb_serial.c:staticintserial_open(structtty_struct*tty,structfile*filp){ structusb_serial*serial; structusb_serial_port*port; unsignedintportNumber; intretval; dbg("%s",__FUNCTION__); /*gettheserialobjectassociatedwiththisttypointer*/ 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){ /*setupourportstructuremakingthettydriver *rememberourportobject,andusit*/ tty->driver_data=port; //赋值, 为以后的操作方便引用 port->tty=tty; /*lockthismodulebeforewecallit *thismayfail,whichmeanswemustbailout, *safebecausewearecalledwithBKLheld*/ if(!try_module_get(serial->type->driver.owner)){ retval=-ENODEV; gotobailout_kref_put; } /*onlycallthedevicespecificopenifthis *isthefirsttimetheportisopened*/ retval=serial->type->open(port,filp); //调用usb_serial_driver的open函数, 在前面分析过了 if(retval) gotobailout_module_put; } up(&port->sem); return0; 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); returnretval;} 可以看到这个open动作主要就是保存一些信息,以方便后面使用, 接着调用usb_serial_driver的open函数, 这里该open是usb_serial_generic_open, 前面分析过了generic.c:intusb_serial_generic_open(structusb_serial_port*port,structfile*filp){ structusb_serial*serial=port->serial; intresult=0; dbg("%s-port%d",__FUNCTION__,port->number); /*forcelow_latencyonsothatourtty_pushactuallyforcesthedatathrough, otherwiseitisscheduled,andwithhighdatarates(likewithOHCI)data cangetlost.*/ if(port->tty) port->tty->low_latency=1; /*ifwehaveabulkinterrupt,startreadingfromit*/ //如果有bulkin的端点的话, 就提交这个端点的urb, 即让系统开始在这个端点上接收来自设备段//发过来的数据, 当数据收到后会调用serial->type->read_bulk_callback函数. if(serial->num_bulk_in){ /*Startreadingfromthedevice*/ 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-failedresubmittingreadurb,error%d ",__FUNCTION__,result); } returnresult;}这个函数后, 系统就开始在bulkin的端点上接收数据了, 如果有数据到来的话就会调用我们的回调函数.接下来我们来看这个回调函数:Generic.c:voidusb_serial_generic_read_bulk_callback(structurb*urb,structpt_regs*regs){ structusb_serial_port*port=(structusb_serial_port*)urb->context; structusb_serial*serial=port->serial; structtty_struct*tty; unsignedchar*data=urb->transfer_buffer; intresult; dbg("%s-port%d",__FUNCTION__,port->number); if(urb->status){ //这次通信的状态 dbg("%s-nonzeroreadbulkstatusreceived:%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); } /*Continuetryingtoalwaysread */ //继续下一个接收操作. 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-failedresubmittingreadurb,error%d ",__FUNCTION__,result);}很明显, 这个回调就是把从设备端收到的数据push到相对上层的tty驱动中去, 随后tty子系统会把这些数据返回给用户.OK, 看完接收过程后我们在来看下发送过程. 发送过程由用户发起, 用户可以调用write系统调用来发数据, 最后会跑到tty驱动的write函数中去.Usb-serial.c:staticintserial_write(structtty_struct*tty,constunsignedchar*buf,intcount){ structusb_serial_port*port=tty->driver_data; intretval=-EINVAL; if(!port) gotoexit; dbg("%s-port%d,%dbyte(s)",__FUNCTION__,port->number,count); if(!port->open_count){ dbg("%s-portnotopened",__FUNCTION__); gotoexit; } /*passontothedriverspecificversionofthisfunction*/ retval=port->serial->type->write(port,buf,count); //仅仅是调用usb_serial_driver的write函数 exit: returnretval;}这个函数,直接调用了usb_serial_driver的write函数.Generic.c:intusb_serial_generic_write(structusb_serial_port*port,constunsignedchar*buf,intcount) { structusb_serial*serial=port->serial; intresult; unsignedchar*data; dbg("%s-port%d",__FUNCTION__,port->number); if(count==0){ dbg("%s-writerequestof0bytes",__FUNCTION__); return(0); } /*onlydosomethingifwehaveabulkoutendpoint*/ //仅在有bulkout的端点上才能发送 if(serial->num_bulk_out){ spin_lock(&port->lock); if(port->write_urb_busy){ spin_unlock(&port->lock); dbg("%s-alreadywriting",__FUNCTION__); return0; } 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); /*setupoururb*/ 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); /*sendthedataoutthebulkport*/ port->write_urb_busy=1; result=usb_submit_urb(port->write_urb,GFP_ATOMIC); //提交一个发送过程 if(result){ dev_err(&port->dev,"%s-failedsubmittingwriteurb,error%d ",__FUNCTION__,result); /*don'thavetograbthelockhere,aswewillretryif!=0*/ port->write_urb_busy=0; }else result=count; returnresult; } /*nobulkout,soreturn0byteswritten*/ return0;}该函数把用户要发送的数据封装到一个urb中去, 并把该urb提交给系统以执行一个发送的操作, 发送完成后会调用serial->type->write_bulk_callback函数.接着看这个回调函数:Generic.c:voidusb_serial_generic_write_bulk_callback(structurb*urb,structpt_regs*regs){ structusb_serial_port*port=(structusb_serial_port*)urb->context; dbg("%s-port%d",__FUNCTION__,port->number); port->write_urb_busy=0; if(urb->status){ dbg("%s-nonzerowritebulkstatusreceived:%d",__FUNCTION__,urb->status); return; } usb_serial_port_softint((void*)port); //请求传输更多的数据 schedule_work(&port->work); //调度一个工作}Usb-serial.c:voidusb_serial_port_softint(void*private){ structusb_serial_port*port=private; structtty_struct*tty; dbg("%s-port%d",__FUNCTION__,port->number); if(!port) return; tty=port->tty; if(!tty) return; tty_wakeup(tty); //请求传输更多的数据}其实很简单,其他的函数就不多做分析了,看懂了这个模块后, 实际上对USB设备的驱动也就大体知道了其流程.