久久久国产一区二区_国产精品av电影_日韩精品中文字幕一区二区三区_精品一区二区三区免费毛片爱

 找回密碼
 注冊(cè)會(huì)員

QQ登錄

只需一步,快速開始

搜索
查看: 2742|回復(fù): 6

嵌入式Linux USB驅(qū)動(dòng)開發(fā)之教你一步步編寫USB驅(qū)動(dòng)程序

[復(fù)制鏈接]
1#
發(fā)表于 2017-11-3 10:58:05 | 只看該作者 |倒序?yàn)g覽 |閱讀模式
編寫與一個(gè)USB設(shè)備驅(qū)動(dòng)程序的方法和其他總線驅(qū)動(dòng)方式類似,驅(qū)動(dòng)程序把驅(qū)動(dòng)程序?qū)ο笞?cè)到USB子系統(tǒng)中,稍后再使用制造商和設(shè)備標(biāo)識(shí)來判斷是否安裝了硬件。當(dāng)然,這些制造商和設(shè)備標(biāo)識(shí)需要我們編寫進(jìn)USB 驅(qū)動(dòng)程序中。
USB 驅(qū)動(dòng)程序依然遵循設(shè)備模型 —— 總線、設(shè)備、驅(qū)動(dòng)。和I2C 總線設(shè)備驅(qū)動(dòng)編寫一樣,所有的USB驅(qū)動(dòng)程序都必須創(chuàng)建的主要結(jié)構(gòu)體是 struct usb_driver,它們向USB 核心代碼描述了USB 驅(qū)動(dòng)程序。但這是個(gè)外殼,只是實(shí)現(xiàn)設(shè)備和總線的掛接,具體的USB 設(shè)備是什么樣的,如何實(shí)現(xiàn)的,比如一個(gè)字符設(shè)備,我們還需填寫相應(yīng)的文件操作接口 ,下面我們從外到里進(jìn)行剖析,學(xué)習(xí)如何搭建這樣的一個(gè)USB驅(qū)動(dòng)外殼框架:
一、注冊(cè)USB驅(qū)動(dòng)程序
Linux的設(shè)備驅(qū)動(dòng),特別是這種hotplugUSB設(shè)備驅(qū)動(dòng),會(huì)被編譯成模塊,然后在需要時(shí)掛在到內(nèi)核。所以USB驅(qū)動(dòng)和注冊(cè)與正常的模塊注冊(cè)、卸載是一樣的,下面是USB驅(qū)動(dòng)的注冊(cè)與卸載:
[cpp] view plain copy
1. static int __init usb_skel_init(void)     
2. {     
3.      int result;     
4.      /* register this driver with the USB subsystem */     
5.      result = usb_register(&skel_driver);     
6.      if (result)     
7.          err("usb_register failed. Error number %d", result);     
8.     
9.      return result;     
10. }     
11.     
12. static void __exit usb_skel_exit(void)     
13. {     
14.      /* deregister this driver with the USB subsystem */     
15.      usb_deregister(&skel_driver);     
16. }     
17.     
18. module_init (usb_skel_init);     
19. module_exit (usb_skel_exit);     
20. MODULE_LICENSE("GPL");  
USB設(shè)備驅(qū)動(dòng)的模塊加載函數(shù)通用的方法是在I2C設(shè)備驅(qū)動(dòng)的模塊加載函數(shù)中使用usb_registerstruct *usb_driver)函數(shù)添加usb_driver的工作,而在模塊卸載函數(shù)中利用usb_deregisterstruct *usb_driver)做相反的工作。 對(duì)比I2C設(shè)備驅(qū)動(dòng)中的 i2c_add_driver(&i2c_driver)i2c_del_driver(&i2c_driver)
struct usb_driverUSB設(shè)備驅(qū)動(dòng),我們需要實(shí)現(xiàn)其成員函數(shù):
[cpp] view plain copy
1. static struct usb_driver skel_driver = {     
2.      .owner = THIS_MODULE,      
3.      .name = "skeleton",   
4.      .id_table = skel_table,         
5.      .probe = skel_probe,      
6.      .disconnect = skel_disconnect,      
7. };      
從代碼看來,usb_driver需要初始化五個(gè)字段:
模塊的所有者 THIS_MODULE
  `7 D2 }+ q. _% t# O模塊的名字  skeleton
  r. w: a! J) ?  I! g3 G$ _probe函數(shù)   skel_probe6 n9 x8 T3 F3 S
disconnect函數(shù)skel_disconnect  C  b* q- x  d/ ^
id_table
最重要的當(dāng)然是probe函數(shù)與disconnect函數(shù),這個(gè)在后面詳細(xì)介紹,先談一下id_table
id_table struct usb_device_id 類型,包含了一列該驅(qū)動(dòng)程序可以支持的所有不同類型的USB設(shè)備。如果沒有設(shè)置該變量,USB驅(qū)動(dòng)程序中的探測回調(diào)該函數(shù)將不會(huì)被調(diào)用。對(duì)比I2Cstruct i2c_device_id *id_table,一個(gè)驅(qū)動(dòng)程序可以對(duì)應(yīng)多個(gè)設(shè)備,i2c 示例:
[cpp] view plain copy
1. static const struct i2c_device_id mpu6050_id[] = {      
2.     { "mpu6050", 0},      
3.     {}      
4. };   
usb子系統(tǒng)通過設(shè)備的production IDvendor ID的組合或者設(shè)備的classsubclassprotocol的組合來識(shí)別設(shè)備,并調(diào)用相關(guān)的驅(qū)動(dòng)程序作處理。我們可以看看這個(gè)id_table到底是什么東西:
[cpp] view plain copy
1. static struct usb_device_id skel_table [] = {      
2.      { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },      
3.      { }                    /* Terminating entry */      
4. };      
5.      
6. MODULE_DEVICE_TABLE (usb, skel_table);     
MODULE_DEVICE_TABLE的第一個(gè)參數(shù)是設(shè)備的類型,如果是USB設(shè)備,那自然是usb。后面一個(gè)參數(shù)是設(shè)備表,這個(gè)設(shè)備表的最后一個(gè)元素是空的,用于標(biāo)識(shí)結(jié)束。代碼定義了USB_SKEL_VENDOR_ID0xfff0USB_SKEL_PRODUCT_ID0xfff0,也就是說,當(dāng)有一個(gè)設(shè)備接到集線器時(shí),usb子系統(tǒng)就會(huì)檢查這個(gè)設(shè)備的vendor IDproduct ID,如果它們的值是0xfff0時(shí),那么子系統(tǒng)就會(huì)調(diào)用這個(gè)skeleton模塊作為設(shè)備的驅(qū)動(dòng)。
當(dāng)USB設(shè)備接到USB控制器接口時(shí),usb_core就檢測該設(shè)備的一些信息,例如生產(chǎn)廠商ID和產(chǎn)品的ID,或者是設(shè)備所屬的classsubclassprotocol,以便確定應(yīng)該調(diào)用哪一個(gè)驅(qū)動(dòng)處理該設(shè)備。  8 K) ~4 P! S$ _6 k0 W
我們下面所要做的就是對(duì)probe函數(shù)與disconnect函數(shù)的填充了,但是在對(duì)probe函數(shù)與disconnect函數(shù)填充之前,有必要先學(xué)習(xí)三個(gè)重要的數(shù)據(jù)結(jié)構(gòu),這在我們后面probe函數(shù)與disconnect函數(shù)中有很大的作用:
二、USB驅(qū)動(dòng)程序中重要數(shù)據(jù)結(jié)構(gòu)
1usb-skeleton
usb-skeleton 是一個(gè)局部結(jié)構(gòu)體,用于與端點(diǎn)進(jìn)行通信。下面先看一下Linux內(nèi)核源碼中的一個(gè)usb-skeleton(就是usb驅(qū)動(dòng)的骨架咯),其定義的設(shè)備結(jié)構(gòu)體就叫做usb-skel
[cpp] view plain copy
1. struct usb_skel {     
2.      struct usb_device *udev;                 /* the usb device for this device */     
3.      struct usb_interface  *interface;            /* the interface for this device */     
4.      struct semaphore limit_sem;         /* limiting the number of writes in progress */     
5.      unsigned char *bulk_in_buffer;     /* the buffer to receive data */     
6.      size_t         bulk_in_size;                  /* the size of the receive buffer */     
7.      __u8          bulk_in_endpointAddr;        /* the address of the bulk in endpoint */     
8.      __u8          bulk_out_endpointAddr;      /* the address of the bulk out endpoint */     
9.      struct kref   kref;     
10. };   
他擁有:
描述usb設(shè)備的結(jié)構(gòu)體udev
1 @9 t8 k9 i( g) \. r' x$ B一個(gè)接口interface
) o  }# z6 c  c! V1 _用于并發(fā)訪問控制的semaphore(信號(hào)量) limit_sem9 I7 `- J4 a' Y1 z$ k
用于接收數(shù)據(jù)的緩沖bulk_in_buffer
( L6 A' n  n, T" B* |用于接收數(shù)據(jù)的緩沖尺寸bulk_in_size
; m2 Z8 \8 r6 s  ^# s批量輸入端口地址bulk_in_endpointAddr
3 l5 M' Y; n) {9 D5 h1 K; g批量輸出端口地址bulk_out_endpointAddr
1 S2 ~6 R0 q: E# c: X% R3 d內(nèi)核使用的引用計(jì)數(shù)器
從開發(fā)人員的角度看,每一個(gè)usb設(shè)備有若干個(gè)配置(configuration)組成,每個(gè)配置又可以有多個(gè)接口(interface)(我理解就是USB設(shè)備的一項(xiàng)功能),每個(gè)接口又有多個(gè)設(shè)置,而接口本身可能沒有端點(diǎn)或者多個(gè)端點(diǎn)(end point
2USB 接口數(shù)據(jù)結(jié)構(gòu) struct usb_interface
[cpp] view plain copy
1. struct usb_interface   
2. {            
3.          struct usb_host_interface *altsetting;     
4.          struct usb_host_interface *cur_altsetting;         
5.          unsigned num_altsetting;            
6.          int minor;                          
7.          enum usb_interface_condition condition;            
8.          unsigned is_active:1;               
9.          unsigned needs_remote_wakeup:1;      
10.          struct device dev;                  
11.          struct device *usb_dev;            
12.          int pm_usage_cnt;                  
13. };   
在邏輯上,一個(gè)USB設(shè)備的功能劃分是通過接口來完成的。比如說一個(gè)USB揚(yáng)聲器,可能會(huì)包括有兩個(gè)接口:一個(gè)用于鍵盤控制,另外一個(gè)用于音頻流傳輸。而事實(shí)上,這種設(shè)備需要用到不同的兩個(gè)驅(qū)動(dòng)程序來操作,一個(gè)控制鍵盤,一個(gè)控制音頻流。但也有例外,比如藍(lán)牙設(shè)備,要求有兩個(gè)接口,第一用于ACLEVENT的傳輸,另外一個(gè)用于SCO鏈路,但兩者通過一個(gè)驅(qū)動(dòng)控制。在Linux上,接口使用struct usb_interface來描述,以下是該結(jié)構(gòu)體中比較重要的字段:
a -- struct usb_host_interface *altsetting(注意不是usb_interface
其實(shí)據(jù)我理解,他應(yīng)該是每個(gè)接口的設(shè)置,雖然名字上有點(diǎn)奇怪。該字段是一個(gè)設(shè)置的數(shù)組(一個(gè)接口可以有多個(gè)設(shè)置),每個(gè)usb_host_interface都包含一套由struct usb_host_endpoint定義的端點(diǎn)配置。但這些配置次序是不定的。

( E" O. [% }! }$ m5 W) H2 hb -- struct usb_host_interface *cur_altsetting
       當(dāng)前活動(dòng)的設(shè)置,指向altsetting數(shù)組中的一個(gè)
struct usb_host_interface數(shù)據(jù)結(jié)構(gòu):
[cpp] view plain copy
1. struct usb_host_interface     
2. {   
3.          struct usb_interface_descriptor desc;//usb描述符,主要有四種usb描述符,設(shè)備描述符,配置描述符,接口描述符和端點(diǎn)描述符,協(xié)議里規(guī)定一個(gè)usb設(shè)備是必須支持這四大描述符的信盈達(dá)嵌入式要領(lǐng)吧五六零五四五吧   
4.                                  //usb描述符放在usb設(shè)備的eeprom里邊   
5.          /* array of desc.bNumEndpoint endpoints associated with this  
6.           * interface setting. these will be in no particular order.  
7.           */   
8.          struct usb_host_endpoint *endpoint;//這個(gè)設(shè)置所使用的端點(diǎn)   
9.     
10.          char *string;           /* iInterface string, if present */   
11.          unsigned char *extra;   /* Extra descriptors */關(guān)于額外描述符   
12.          int extralen;   
13. };   
c -- unsigned num_altstting
可選設(shè)置的數(shù)量,即altsetting所指數(shù)組的元素個(gè)數(shù)
d -- int minor
當(dāng)捆綁到該接口的USB驅(qū)動(dòng)程序使用USB主設(shè)備號(hào)時(shí),USB core分配的次設(shè)備號(hào)。僅在成功調(diào)用usb_register_dev之后才有效。
3USB 端點(diǎn) struct usb_host_endpoint
Linux中用struct usb_host_endpoint 來描述USB端點(diǎn)
[cpp] view plain copy
1. struct usb_host_endpoint     
2. {   
3.          struct usb_endpoint_descriptor desc;   
4.          struct list_head                urb_list;//端點(diǎn)要處理的urb隊(duì)列.urbusb通信的主角,設(shè)備中的每個(gè)端點(diǎn)都可以處理一個(gè)urb隊(duì)列.要想和你的usb通信,就得創(chuàng)建一個(gè)urb,并且為它賦好值,   
5.                                    //交給咱們的usb core,它會(huì)找到合適的host controller,從而進(jìn)行具體的數(shù)據(jù)傳輸   
6.          void                            *hcpriv;//這是提供給HCDhost controller driver)用的   
7.          struct ep_device                *ep_dev;        /* For sysfs info */   
8.     
9.          unsigned char *extra;   /* Extra descriptors */   
10.          int extralen;   
11. };   
每個(gè)usb_host_endpoint中包含一個(gè)struct usb_endpoint_descriptor結(jié)構(gòu)體,當(dāng)中包含該端點(diǎn)的信息以及設(shè)備自定義的各種信息,這些信息包括:
a -- bEndpointAddressb for byte
8位端點(diǎn)地址,其地址還隱藏了端點(diǎn)方向的信息(之前說過,端點(diǎn)是單向的),可以用掩碼USB_DIR_OUTUSB_DIR_IN來確定。
b -- bmAttributes
端點(diǎn)的類型,結(jié)合USB_ENDPOINT_XFERTYPE_MASK可以確定端點(diǎn)是USB_ENDPOINT_XFER_ISOC(等時(shí))、USB_ENDPOINT_XFER_BULK(批量)還是USB_ENDPOINT_XFER_INT(中斷)。
c -- wMaxPacketSize
端點(diǎn)一次處理的最大字節(jié)數(shù)。發(fā)送的BULK包可以大于這個(gè)數(shù)值,但會(huì)被分割傳送。
d -- bInterval
如果端點(diǎn)是中斷類型,該值是端點(diǎn)的間隔設(shè)置,以毫秒為單位
三、探測和斷開函數(shù)分析
USB驅(qū)動(dòng)程序指定了兩個(gè)USB核心在適當(dāng)時(shí)間調(diào)用的函數(shù)。
1、探測函數(shù)
當(dāng)一個(gè)設(shè)備被安裝而USB核心認(rèn)為該驅(qū)動(dòng)程序應(yīng)該處理時(shí),探測函數(shù)被調(diào)用;
探測函數(shù)應(yīng)該檢查傳遞給他的設(shè)備信息,確定驅(qū)動(dòng)程序是否真的適合該設(shè)備。當(dāng)驅(qū)動(dòng)程序因?yàn)槟撤N原因不應(yīng)控制設(shè)備時(shí),斷開函數(shù)被調(diào)用,它可以做一些清潔的工作。
系統(tǒng)會(huì)傳遞給探測函數(shù)的信息是什么呢?一個(gè)usb_interface * 跟一個(gè)struct usb_device_id *作為參數(shù)。他們分別是該USB設(shè)備的接口描述(一般會(huì)是該設(shè)備的第0號(hào)接口,該接口的默認(rèn)設(shè)置也是第0號(hào)設(shè)置)跟它的設(shè)備ID描述(包括Vendor IDProduction ID等)。
USB驅(qū)動(dòng)程序應(yīng)該初始化任何可能用于控制USB設(shè)備的局部結(jié)構(gòu)體,它還應(yīng)該把所需的任何設(shè)備相關(guān)信息保存到局部結(jié)構(gòu)體中。例如,USB驅(qū)動(dòng)程序通常需要探測設(shè)備對(duì)的端點(diǎn)地址和緩沖區(qū)大小,因?yàn)樾枰麄儾拍芎投它c(diǎn)通信。
下面具體分析探測函數(shù)做了哪些事情:
a -- 探測設(shè)備的端點(diǎn)地址、緩沖區(qū)大小,初始化任何可能用于控制USB設(shè)備的數(shù)據(jù)結(jié)構(gòu)
下面是一個(gè)實(shí)例代碼,他們探測批量類型的INOUT端點(diǎn),把相關(guān)信息保存到一個(gè)局部設(shè)備結(jié)構(gòu)體中:
[cpp] view plain copy
1. /* set up the endpoint information */     
2.      /* use only the first bulk-in and bulk-out endpoints */     
3.      iface_desc = interface->cur_altsetting;     
4.      for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {     
5.          endpoint = &iface_desc->endpoint.desc;   
6.     
7.          if ( !dev->bulk_in_endpointAddr &&     
8.                 ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) = = USB_DIR_IN) &&     
9.              ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) = = USB_ENDPOINT_XFER_BULK)) {     
10.              /* we found a bulk in endpoint */     
11.               buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);     
12.               dev->bulk_in_size = buffer_size;     
13.               dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;     
14.               dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);     
15.               if (!dev->bulk_in_buffer) {     
16.                   err("Could not allocate bulk_in_buffer");     
17.                    goto error;                  
18.               }  
19.          }     
20.     
21.          if (!dev->bulk_out_endpointAddr &&     
22.             ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)= =USB_DIR_OUT) &&     
23.                ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)= = USB_ENDPOINT_XFER_BULK)) {     
24.               /* we found a bulk out endpoint */     
25.               dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;     
26.          }     
27.      }     
28.     
29.      if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {     
30.          err("Could not find both bulk-in and bulk-out endpoints");     
31.          goto error;     
32.      }   

8 A( c. o! V( ~8 o7 [4 l
回復(fù)

使用道具 舉報(bào)

2#
發(fā)表于 2017-11-3 14:04:43 | 只看該作者
好長的廣告
3#
發(fā)表于 2017-11-3 19:42:05 | 只看該作者
把這么多代碼弄上去也是耐心
4#
發(fā)表于 2017-11-3 20:30:47 | 只看該作者
又是C語言,感覺像是火星文
5#
發(fā)表于 2017-11-3 21:10:30 | 只看該作者
連注解都是英文。
6#
發(fā)表于 2017-11-21 10:28:42 | 只看該作者

+ T  Y: g8 T' t/ B& U) Q0 l7 ^
回復(fù)

使用道具 舉報(bào)

7#
發(fā)表于 2017-11-23 01:35:21 | 只看該作者
支持一下吧~~~~~~~~~~~~~~~~~~

本版積分規(guī)則

Archiver|手機(jī)版|小黑屋|機(jī)械社區(qū) ( 京ICP備10217105號(hào)-1,京ICP證050210號(hào),浙公網(wǎng)安備33038202004372號(hào) )

GMT+8, 2025-7-27 07:08 , Processed in 0.073468 second(s), 14 queries , Gzip On.

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回復(fù) 返回頂部 返回列表