USB设备驱动程序用来驱动相应的USB设备,USB设备驱动用usb_driver表示,它主要用来将USB设备挂接到USB核心中,并启动USB设备,让其正常工作。对于USB设备的具体读写操作由放在usb_driver设备中的usb_class_drivers成员来实现,该成员定义了一个file_operations结构体,用来对设备进行读写操作。
1. USB设备驱动模型
设备需要驱动才能正常工作,所以当系统检测到设备时,应该将其与对应的驱动程序绑定。设备与驱动的绑定,只能够在同一总线上的设备与驱动之间进行。总线与设备和驱动的连接,需要相应总线的核心代码来实现。对于USB总线,实现总线与驱动和设备的连接,是通过USB核心(USB core)来完成的。
- USB core会完成总线的初始化工作,然后再扫描USB总线,看USB总线上连接了那一些设备。当USB core发现设备的时候,会为其分配一个struct device结构体,并将其连接到总线上。当发现所有的设备以后,USB总线上的设备链表就建立好了。
- 相比设备的连接,将驱动连接到总线上就更简单。每当驱动注册的时候,会将自己在总线上注册,并连接到总线的驱动链表中。这时,驱动会遍历总线的设备链表,寻找合适的设备,并将其通过内部指针联系起来。
2.USB驱动结构
在USB设备驱动模型中,USB设备驱动使用usb_driver结构体来表示,该结构体中包含了与具体设备相关的核心函数,对于不同的设备,需要实现不同功能的函数。usb_driver结构体的定义如下:
在cp210x中该结构体的定义如下:
其中probe函数指向USB驱动的程序的探测函数,当有USB设备插入时,USB核心会调用该函数进行设备的初始化工作。
2.1 usb驱动注册函数usb_register()
在初始化函数中,首先要注册一个USB驱动,注册USB驱动的函数是usb_register()。需要注意的是,调用该函数之前应该要对usb_driver进行必要的初始化,使用MODULE_DEVICE_TABLE(usb,...)宏来展示设备信息。
2.2 设置USB转串口的线路规程
线路规程要为串口的使用定下数据交换的规程。Linux内核中已经存在了许多的规程,如PPP、SLIP、TTY。缺省使用TTY,可以根据自己的需要将规程替换为Linux已经定义的规程结构,甚至是替换为自己的规程结构,当用户要改变线路设置,只需要调用多个termios用户函数中的一个,也可以直接对设备接点调用ioctl,tty核心将会把这两种接口转换为一系列的tty驱动程序的回调函数,或者是ioctl调用,set_termios回调函数需要知道要改变的是哪一个线路设置,然后在tty设备中进行改动。tty驱动程序必须能够对termios结构中所有不同的设置进行解码,并对任何需要的改变作出响应,所有的线路设置被封装在termios结构当中,在tty核心中定义如下:
该结构被用来为tty设备上的某一个特定的端口上保存当前 波特率、数据大小、数据流等参数。
框架函数
1. int usb_serial_register(struct usb_serial_device_type *new_device);//向核心注册USB转串口设备
void usb_serial_deregister(struct usb_serial_device_type *device);//向核心注销USB转串口设备。
3.USB设备驱动程序
USB设备驱动的实现,首先需要定义一个usb_driver结构变量作为要注册到USB核心的设备驱动,在USB转串口中该结构的定义如下:
- 定义了USB设备驱动的probe()函数,该函数由usb_serial_probe()函数实现。
- 定义了USB设备驱动的disconnect()函数,该函数由usb_serial_disconnect()函数实现,在设备驱动注销时被调用。
- 定义了USB设备驱动的id_table为id_table,表示该驱动支持的USB设备。
3.1 探测函数probe()的参数usb_interface
当USB设备插入插槽的时候,会引起一个电信号的变化,主机控制器捕获这个电信号,并命令USB核心处理对设备的加载工作。USB核心读取到USB设备固件中相关的信息,并与挂接到USB总线上的驱动程序相比较,如果找到合适的驱动程序usb_driver,就会调用驱动程序的probe()函数。该函数的原型如下:
int usb_serial_probe(struct usb_interface *interface,const struct usb_device_id *id);
第一个参数usb_interface是USB驱动中最重要的一个结构体,它代表着设备的一种功能,与一个usb_driver相对应。usb_interface在USB驱动中只有一个,由USB核心负责维护。USB核心调用probe()函数并传递进struct usb_interface和struct usb_device_id *类型的参数。
接口(interface):在USB协议中,接口(usb_interface)代表一个基本的功能。USB接口只处理一种USB逻辑连接,每一个USB驱动程序(usb_driver)控制一个接口。内核使用struct usb_interface结构体来表述USB接口。USB核心在设备插入的时候,会读取USB设备接口的信息,并创建一个usb_interface的结构体。接着USB核心在USB总线上找到合适的USB驱动程序,并调用驱动程序的probe()函数,将usb_interface传递给驱动程序。probe()函数的第一个参数就是指向USB核心分配的usb_interface结构体的指针,驱动程序从这里得到这个接口结构体,并且负责控制该结构体。因为一个接口代表一种基本的功能,所以驱动程序也只是负责该接口所代表的功能。probe()函数的第二个参数从设备读取usb_device_id的信息,用来与驱动程序匹配。
USB核心处理usb_interface中的大量成员,只有少数几个成员驱动程序会用到,usb_interface中的重要成员是:
- altsetting表示一组可选的设置,用这个指针指向一个可选设置数组。
- cur_altsetting表示当前正在使用的设置。
- num_altsetting表示可选设置altsetting的数量。
- minor表示分配给设备的次设备号。
- condition表示接口和驱动的绑定状态,在Linux的设备驱动模型中,设备和驱动是彼此关联相互依靠的。每一个设备或者是驱动都在USB总线中等待属于他的另一半,如果找到则彼此绑定在一起。这里的condition变量被定义为enum usb_interface_conditiion的类型,表示这个接口的状态。
enum usb_interface_condition{ USB_INTERFACE_UNBOUND = 0, //usb_interface为绑定状态 USB_INTERFACE_BINDING, //正在绑定中 USB_INTERFACE_BOUND, //已经绑定 USB_INTERFACE_UNBINDING, //在取消绑定这个过程中 };
- needs_remote_wakeup、pm_usage_cnt定义的是与电源管理有关的变量。
- struct device dev是设备驱动模型中device内嵌在usb_interface结构体中的设备。而struct device *usb_dev则不是内嵌的设备对象。当接口使用USB_MAJOR作为主设备号时,usb_dev才会被用到。在整个内核中,只有usb_register_dev()和usb_deregister_dev()两个函数里才会用到usb_dev变量,usb_dev指向的就是usb_register_dev()函数中创建的usb class device
3.2 设置
在上面所述的usb_interface结构体中介绍了altsetting和cur_altersetting,他们都是usb_host_interface的结构体,定义如下:
- desc是一个结构描述符。
- endpoint是一个数组,表示这个设置所用到的端点。USB协议中规定了端点的结构,在Linux中,使用struct usb_host_endpoint结构体来表示。
- string 字符串指针用来保存从设备固件中取出来的字符串描述信息,可有可无。
- extra和extralen表示额外的描述符。除了设备、配置、接口、端点这4个不能缺少的描述符和字符串描述符外,设备还可能有一些另外的字符信息描述符。这些信息有开发商自己指定需要的信息。
3.3 探测函数usb_serial_probe()
usb_serial_probe()函数作用是实现热插拔的机制,用来对设备进行识别和对设备进行最初的设置,使其能够进行正常的工作。要理解设备对设备进行了怎样的初始化工作,必须要详细的分析这一部分的代码:
struct usb_device *dev = interface_to_usbdev(interface);
首先是将usb_interface结构转化为usb_device结构,然后是定义了一些结构体指针和变量.
type = search_serial_device(interface);
search_serial_device的定义如下:
该函数的作用是检查所插入的USB设备的id是否匹配一个在定义的设备表中的已知的设备,返回值是一个usb_serial_driver类型的结构体,如果返回的指针为空,则说明没有查找到该设备匹配的驱动则返回。
若是成功则执行try_module_get(type->driver.owner)
,该函数用来获取设备所匹配的模块,即设备的驱动,
未完待续......``