嵌入式Linux驅動學習之路(二十)USB設備驅動


USB在接入系統的時候,以0的設備ID和主機通信,然后由主機為其分配新的ID。

在主機端,D+和D-都是下拉接地的。而設備端的D-接上拉時,表明此設備為高速設備:12M/s。 D+接上拉時則是全速設備:480M/S。

PC的USB口中的D+D-有15K的下拉電阻,未接USB設備時,皆為低電平。

而設備中的D+D-則是1.5K的上拉電阻。一旦接入PC中,電腦就會知道有設備接入。

 

USB是主從結構。所有的傳輸都是由主機發起的,即USB設備沒有主動通知USB主機的能力。

 

USB的傳輸類型:

    控制傳輸。 可靠,時間有保證。 例如:USB設備的識別。

      批量傳輸。 可靠,但時間沒有保證。 例如:U盤。

    中斷傳輸。 可靠,實時傳輸。  例如:USB鼠標。

    實時傳輸。 不可靠。實時傳輸。  例如:USB攝像頭。

 USB傳輸的對象為端點。比如讀U盤和寫U盤,可以形容為從端點1讀數據,從端點2寫數據。

    除了端點0以外,每個端點只支持一個方向的數據傳輸。

    端點0用於控制傳輸,既能輸出也能輸入。

 

每一個端點都有傳輸類型和方向。

程序和術語中說的輸入輸出 都是基於USB主機的立場說的。

  比如鼠標是數據從鼠標傳輸到PC機的,鼠標對應的端點稱為輸入端點。

 

 
USB總線驅動程序的作用: 1.識別設備  2. 查找並安裝對應的設備驅動程序  3.提供USB讀寫函數。

 

USB驅動程序框架:

app:

--------------------------------------------------------------------

          USB設備驅動程序  (拿到數據處理,驅動開發人員編寫)

--------------------------------------------------------------------

            USB總線驅動程序  //1.識別  2.找到匹配的設備驅動  3.提供讀寫函數(不知數據含義)

--------------------------------------------------------------------

          主機控制器

         UHCI  OHCI  EHCI

--------------------------------------------------------------------

          USB設備

 

UHCI  intel 適用於低速和全速

OHCI  Microsoft 適用於低速和全速

EHCI   高速 

 

1.識別設備  

1.1分配地址,並告訴USB設備(set address)。

1.2 發出命令獲取描述符

 

2. 查找並安裝對應的設備驅動程序  

 

3.提供USB讀寫函數。

 

 

把USB設備接到開發板上,看輸出信息:

usb 1-1: new full speed USB device using s3c2410-ohci and address 2           
usb
1-1: configuration #1 chosen from 1 choice

  scsi0 : SCSI emulation for USB Mass Storage devices
  scsi 0:0:0:0: Direct-Access Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
  sd 0:0:0:0: [sda] 30277632 512-byte hardware sectors (15502 MB)
  sd 0:0:0:0: [sda] Write Protect is off
  sd 0:0:0:0: [sda] Assuming drive cache: write through
  sd 0:0:0:0: [sda] 30277632 512-byte hardware sectors (15502 MB)
  sd 0:0:0:0: [sda] Write Protect is off
  sd 0:0:0:0: [sda] Assuming drive cache: write through
  sda: sda1
  sd 0:0:0:0: [sda] Attached SCSI removable disk

斷開USB設備

usb 1-1: USB disconnect, address 2

再接上:

usb 1-1: new full speed USB device using s3c2410-ohci and address 3             
usb 1-1: configuration #1 chosen from 1 choice                                  
scsi1 : SCSI emulation for USB Mass Storage devices                             
scsi 1:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6    
sd 1:0:0:0: [sda] 30277632 512-byte hardware sectors (15502 MB)                 
sd 1:0:0:0: [sda] Write Protect is off                                          
sd 1:0:0:0: [sda] Assuming drive cache: write through                           
sd 1:0:0:0: [sda] 30277632 512-byte hardware sectors (15502 MB)                 
sd 1:0:0:0: [sda] Write Protect is off                                          
sd 1:0:0:0: [sda] Assuming drive cache: write through                           
 sda: sda1                                                                      
sd 1:0:0:0: [sda] Attached SCSI removable disk 

 然后在內核中搜索USB device using這個字符。最后發現在 drivers/usb/core/hub.c(+2186)中的hub_port_init 函數中。 

逆推則發現如下調用關系:

hub_irq()

  -->> kick_khubd()

    -->> hub_thread()

      -->> hub_events()

        -->> hub_port_connect_change()

          -->>choose_address()   //分配新的USB地址

          -->> hub_port_init()

            -->> hub_set_address()  //把地址告訴USB設備

              -->> usb_get_device_descriptor()   //獲取USB設備描述符

            -->> usb_get_device_descriptor()   //再獲取一次USB設備描述符

            -->> usb_new_device()     

               -->> usb_get_configuration()   //讀取所有的設備描述符並解析       

               -->> device_add()        //把設備放入總線的dev鏈表,從總線的driver鏈表中取出driver一一比較。

                                //usb_interface和usb_driver的id_table比較。

                                //如果匹配上了,則調用對應driver的probe函數。

    -->> 進入USB總線驅動

usb驅動測試程序:

 

/*************************************************************************
    > File Name: usb_mouse_as_key.c
    > Author: 
    > Mail: 
    > Created Time: 2016年11月04日 星期五 22時55分15秒
 ************************************************************************/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

struct input_dev *input_dev;
signed char *usb_buff;
static dma_addr_t usb_buf_phys;
struct urb *uk_urb;
int len;

static void usbmouse_as_key_irq(struct urb *urb)
{
    int i;
    static int cnt = 0;
#if 0

static int pr_lb,pr_rb,pr_mb;
// printk("data cnt %d:",++cnt);
// for( i=0;i<len;i++ )
// {
// printk("%2.2x ",usb_buff[i]);
// }
// printk("\n");
// printk("lb:%01x rb:%01x mb:%01x\n",(usb_buff[1])&0x01,(usb_buff[1]>>1)&0x01,(usb_buff[1]>>2)&0x01);
// printk("move x:%04d y:%04d\n",(usb_buff[2]),(usb_buff[3]>>4));
if( (usb_buff[1]&0x01) != pr_lb )
{
  input_event(input_dev,EV_KEY,KEY_L,!pr_lb); /* 有事件產生時上報事件 */
  input_sync(input_dev);
}

if( ((usb_buff[1]>>1)&0x01) != pr_rb )
{
  input_event(input_dev,EV_KEY,KEY_S,!pr_rb); /* 有事件產生時上報事件 */
  input_sync(input_dev);
}

 
         

if( ((usb_buff[1]>>2)&0x01) != pr_mb )
{
  input_event(input_dev,EV_KEY,KEY_ENTER,!pr_mb); /* 有事件產生時上報事件 */
  input_sync(input_dev);
}

pr_lb = usb_buff[1]&0x01;
pr_rb = (usb_buff[1]>>1)&0x01;
pr_mb = (usb_buff[1]>>2)&0x01;

#else
    printk("data cnt %d:",++cnt);
    for( i=0;i<len;i++ )
    {
        printk("%02x",usb_buff[i]);
    }
    printk("\n");
#endif usb_submit_urb(uk_urb,GFP_KERNEL); }
static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; int pipe; interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; printk("found usbmouse_as_key\n"); printk("bcdUSB = %x\n",dev->descriptor.bcdUSB); printk("VID = 0X%x\n",dev->descriptor.idVendor); printk("PID = 0X%x\n",dev->descriptor.idProduct); /* 分配一個input_dev結構體 */ input_dev = input_allocate_device(); /* 設置 */ /* 能產生哪類事件 */ set_bit(EV_KEY,input_dev->evbit); set_bit(EV_REP,input_dev->evbit); //重復類事件 /* 能產生哪些事件 */ set_bit(KEY_L,input_dev->keybit); set_bit(KEY_S,input_dev->keybit); set_bit(KEY_ENTER,input_dev->keybit); /* 注冊 */ input_register_device(input_dev); /* 硬件相關操作 */ /* 數據傳輸3要素: 源、目的、長度 */ /* 源: USB設備的某個節點 */ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 長度 */ len = endpoint->wMaxPacketSize; /* 目的: */ usb_buff = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &usb_buf_phys); /* 使用3要素 */ /* USB request block */ uk_urb = usb_alloc_urb(0, GFP_KERNEL); /* 設置URB */ usb_fill_int_urb(uk_urb, dev, pipe, usb_buff, len, usbmouse_as_key_irq, NULL, endpoint->bInterval); //bInterval 查詢頻率 uk_urb->transfer_dma = usb_buf_phys; uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* 使用urb */ usb_submit_urb(uk_urb,GFP_KERNEL); return 0; } static void usbmouse_as_key_disconnect(struct usb_interface *intf) { struct usb_device *dev = interface_to_usbdev(intf); printk("disconnect usbmouse_as_key\n"); usb_kill_urb(uk_urb); usb_free_urb(uk_urb); usb_buffer_free(dev, len, usb_buff,usb_buf_phys); input_unregister_device(input_dev); input_free_device(input_dev); } static struct usb_device_id usbmouse_as_key_id_table[] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) }, { }/* Terminating entry */ }; /* 分配 設置 */ static struct usb_driver usbmouse_as_key_driver = { .name= "usbmouse", .probe= usbmouse_as_key_probe, .disconnect= usbmouse_as_key_disconnect, .id_table= usbmouse_as_key_id_table, }; static int mouse_init(void) { /* 注冊 */ usb_register(&usbmouse_as_key_driver); return 0; } static void mouse_exit(void) { usb_deregister(&usbmouse_as_key_driver); } module_init(mouse_init); module_exit(mouse_exit); MODULE_LICENSE("GPL");

卸載掉內核中的鼠標usb驅動程序。

安裝我們的驅動程序,接入鼠標。

按下鼠標按鍵,將會在控制台看到相應的信息。

將打印信息的地方改成上報信息,即可

 

sd

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM