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

 找回密碼
 注冊會員

QQ登錄

只需一步,快速開始

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

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

[復(fù)制鏈接]
1#
發(fā)表于 2017-11-3 10:58:05 | 只看該作者 |正序瀏覽 |閱讀模式
編寫與一個USB設(shè)備驅(qū)動程序的方法和其他總線驅(qū)動方式類似,驅(qū)動程序把驅(qū)動程序?qū)ο笞缘?/font>USB子系統(tǒng)中,稍后再使用制造商和設(shè)備標(biāo)識來判斷是否安裝了硬件。當(dāng)然,這些制造商和設(shè)備標(biāo)識需要我們編寫進(jìn)USB 驅(qū)動程序中。
USB 驅(qū)動程序依然遵循設(shè)備模型 —— 總線、設(shè)備、驅(qū)動。和I2C 總線設(shè)備驅(qū)動編寫一樣,所有的USB驅(qū)動程序都必須創(chuàng)建的主要結(jié)構(gòu)體是 struct usb_driver,它們向USB 核心代碼描述了USB 驅(qū)動程序。但這是個外殼,只是實現(xiàn)設(shè)備和總線的掛接,具體的USB 設(shè)備是什么樣的,如何實現(xiàn)的,比如一個字符設(shè)備,我們還需填寫相應(yīng)的文件操作接口 ,下面我們從外到里進(jìn)行剖析,學(xué)習(xí)如何搭建這樣的一個USB驅(qū)動外殼框架:
一、注冊USB驅(qū)動程序
Linux的設(shè)備驅(qū)動,特別是這種hotplugUSB設(shè)備驅(qū)動,會被編譯成模塊,然后在需要時掛在到內(nèi)核。所以USB驅(qū)動和注冊與正常的模塊注冊、卸載是一樣的,下面是USB驅(qū)動的注冊與卸載:
[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ū)動的模塊加載函數(shù)通用的方法是在I2C設(shè)備驅(qū)動的模塊加載函數(shù)中使用usb_registerstruct *usb_driver)函數(shù)添加usb_driver的工作,而在模塊卸載函數(shù)中利用usb_deregisterstruct *usb_driver)做相反的工作。 對比I2C設(shè)備驅(qū)動中的 i2c_add_driver(&i2c_driver)i2c_del_driver(&i2c_driver)
struct usb_driverUSB設(shè)備驅(qū)動,我們需要實現(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需要初始化五個字段:
模塊的所有者 THIS_MODULE
& M! `0 L3 B& i0 f+ D: M$ x模塊的名字  skeleton
, l/ g" S( z1 e- Qprobe函數(shù)   skel_probe: n4 h. [( l) y* i$ t
disconnect函數(shù)skel_disconnect) y6 j) m+ p9 Z
id_table
最重要的當(dāng)然是probe函數(shù)與disconnect函數(shù),這個在后面詳細(xì)介紹,先談一下id_table
id_table struct usb_device_id 類型,包含了一列該驅(qū)動程序可以支持的所有不同類型的USB設(shè)備。如果沒有設(shè)置該變量,USB驅(qū)動程序中的探測回調(diào)該函數(shù)將不會被調(diào)用。對比I2Cstruct i2c_device_id *id_table,一個驅(qū)動程序可以對應(yīng)多個設(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è)備,并調(diào)用相關(guān)的驅(qū)動程序作處理。我們可以看看這個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的第一個參數(shù)是設(shè)備的類型,如果是USB設(shè)備,那自然是usb。后面一個參數(shù)是設(shè)備表,這個設(shè)備表的最后一個元素是空的,用于標(biāo)識結(jié)束。代碼定義了USB_SKEL_VENDOR_ID0xfff0USB_SKEL_PRODUCT_ID0xfff0,也就是說,當(dāng)有一個設(shè)備接到集線器時,usb子系統(tǒng)就會檢查這個設(shè)備的vendor IDproduct ID,如果它們的值是0xfff0時,那么子系統(tǒng)就會調(diào)用這個skeleton模塊作為設(shè)備的驅(qū)動。
當(dāng)USB設(shè)備接到USB控制器接口時,usb_core就檢測該設(shè)備的一些信息,例如生產(chǎn)廠商ID和產(chǎn)品的ID,或者是設(shè)備所屬的classsubclassprotocol,以便確定應(yīng)該調(diào)用哪一個驅(qū)動處理該設(shè)備。  
9 Z' j( ^" l+ S) p" q- S  e
我們下面所要做的就是對probe函數(shù)與disconnect函數(shù)的填充了,但是在對probe函數(shù)與disconnect函數(shù)填充之前,有必要先學(xué)習(xí)三個重要的數(shù)據(jù)結(jié)構(gòu),這在我們后面probe函數(shù)與disconnect函數(shù)中有很大的作用:
二、USB驅(qū)動程序中重要數(shù)據(jù)結(jié)構(gòu)
1usb-skeleton
usb-skeleton 是一個局部結(jié)構(gòu)體,用于與端點進(jìn)行通信。下面先看一下Linux內(nèi)核源碼中的一個usb-skeleton(就是usb驅(qū)動的骨架咯),其定義的設(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
- |7 ]% h- g8 A9 y一個接口interface2 F* y5 [- t. ?. m9 P( }
用于并發(fā)訪問控制的semaphore(信號量) limit_sem
! N3 a5 B- j0 G( t% ^用于接收數(shù)據(jù)的緩沖bulk_in_buffer; F/ ?( S( _& L& z, N+ z
用于接收數(shù)據(jù)的緩沖尺寸bulk_in_size
( L- G) W/ n" J1 ?* y批量輸入端口地址bulk_in_endpointAddr
4 e/ U5 R/ A6 m" W' L批量輸出端口地址bulk_out_endpointAddr
/ |5 O, {7 B1 M: q9 }# h4 {內(nèi)核使用的引用計數(shù)器
從開發(fā)人員的角度看,每一個usb設(shè)備有若干個配置(configuration)組成,每個配置又可以有多個接口(interface)(我理解就是USB設(shè)備的一項功能),每個接口又有多個設(shè)置,而接口本身可能沒有端點或者多個端點(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. };   
在邏輯上,一個USB設(shè)備的功能劃分是通過接口來完成的。比如說一個USB揚(yáng)聲器,可能會包括有兩個接口:一個用于鍵盤控制,另外一個用于音頻流傳輸。而事實上,這種設(shè)備需要用到不同的兩個驅(qū)動程序來操作,一個控制鍵盤,一個控制音頻流。但也有例外,比如藍(lán)牙設(shè)備,要求有兩個接口,第一用于ACLEVENT的傳輸,另外一個用于SCO鏈路,但兩者通過一個驅(qū)動控制。在Linux上,接口使用struct usb_interface來描述,以下是該結(jié)構(gòu)體中比較重要的字段:
a -- struct usb_host_interface *altsetting(注意不是usb_interface
其實據(jù)我理解,他應(yīng)該是每個接口的設(shè)置,雖然名字上有點奇怪。該字段是一個設(shè)置的數(shù)組(一個接口可以有多個設(shè)置),每個usb_host_interface都包含一套由struct usb_host_endpoint定義的端點配置。但這些配置次序是不定的。
. ^- y  X; s6 w" ]8 E
b -- struct usb_host_interface *cur_altsetting
       當(dāng)前活動的設(shè)置,指向altsetting數(shù)組中的一個
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è)備描述符,配置描述符,接口描述符和端點描述符,協(xié)議里規(guī)定一個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;//這個設(shè)置所使用的端點   
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ù)組的元素個數(shù)
d -- int minor
當(dāng)捆綁到該接口的USB驅(qū)動程序使用USB主設(shè)備號時,USB core分配的次設(shè)備號。僅在成功調(diào)用usb_register_dev之后才有效。
3USB 端點 struct usb_host_endpoint
Linux中用struct usb_host_endpoint 來描述USB端點
[cpp] view plain copy
1. struct usb_host_endpoint     
2. {   
3.          struct usb_endpoint_descriptor desc;   
4.          struct list_head                urb_list;//端點要處理的urb隊列.urbusb通信的主角,設(shè)備中的每個端點都可以處理一個urb隊列.要想和你的usb通信,就得創(chuàng)建一個urb,并且為它賦好值,   
5.                                    //交給咱們的usb core,它會找到合適的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. };   
每個usb_host_endpoint中包含一個struct usb_endpoint_descriptor結(jié)構(gòu)體,當(dāng)中包含該端點的信息以及設(shè)備自定義的各種信息,這些信息包括:
a -- bEndpointAddressb for byte
8位端點地址,其地址還隱藏了端點方向的信息(之前說過,端點是單向的),可以用掩碼USB_DIR_OUTUSB_DIR_IN來確定。
b -- bmAttributes
端點的類型,結(jié)合USB_ENDPOINT_XFERTYPE_MASK可以確定端點是USB_ENDPOINT_XFER_ISOC(等時)、USB_ENDPOINT_XFER_BULK(批量)還是USB_ENDPOINT_XFER_INT(中斷)。
c -- wMaxPacketSize
端點一次處理的最大字節(jié)數(shù)。發(fā)送的BULK包可以大于這個數(shù)值,但會被分割傳送。
d -- bInterval
如果端點是中斷類型,該值是端點的間隔設(shè)置,以毫秒為單位
三、探測和斷開函數(shù)分析
USB驅(qū)動程序指定了兩個USB核心在適當(dāng)時間調(diào)用的函數(shù)。
1、探測函數(shù)
當(dāng)一個設(shè)備被安裝而USB核心認(rèn)為該驅(qū)動程序應(yīng)該處理時,探測函數(shù)被調(diào)用;
探測函數(shù)應(yīng)該檢查傳遞給他的設(shè)備信息,確定驅(qū)動程序是否真的適合該設(shè)備。當(dāng)驅(qū)動程序因為某種原因不應(yīng)控制設(shè)備時,斷開函數(shù)被調(diào)用,它可以做一些清潔的工作。
系統(tǒng)會傳遞給探測函數(shù)的信息是什么呢?一個usb_interface * 跟一個struct usb_device_id *作為參數(shù)。他們分別是該USB設(shè)備的接口描述(一般會是該設(shè)備的第0號接口,該接口的默認(rèn)設(shè)置也是第0號設(shè)置)跟它的設(shè)備ID描述(包括Vendor IDProduction ID等)。
USB驅(qū)動程序應(yīng)該初始化任何可能用于控制USB設(shè)備的局部結(jié)構(gòu)體,它還應(yīng)該把所需的任何設(shè)備相關(guān)信息保存到局部結(jié)構(gòu)體中。例如,USB驅(qū)動程序通常需要探測設(shè)備對的端點地址和緩沖區(qū)大小,因為需要他們才能和端點通信。
下面具體分析探測函數(shù)做了哪些事情:
a -- 探測設(shè)備的端點地址、緩沖區(qū)大小,初始化任何可能用于控制USB設(shè)備的數(shù)據(jù)結(jié)構(gòu)
下面是一個實例代碼,他們探測批量類型的INOUT端點,把相關(guān)信息保存到一個局部設(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 K" ~- X+ C; X6 ?
回復(fù)

使用道具 舉報

7#
發(fā)表于 2017-11-23 01:35:21 | 只看該作者
支持一下吧~~~~~~~~~~~~~~~~~~
6#
發(fā)表于 2017-11-21 10:28:42 | 只看該作者
( @$ }- h- P1 ^9 J+ C5 x
回復(fù)

使用道具 舉報

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

本版積分規(guī)則

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

GMT+8, 2025-7-25 03:48 , Processed in 0.075619 second(s), 15 queries , Gzip On.

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

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