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)
,該函數用來獲取設備所匹配的模塊,即設備的驅動,
未完待續......``