• 嵌入式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(" ");
    // printk("lb:%01x rb:%01x mb:%01x ",(usb_buff[1])&0x01,(usb_buff[1]>>1)&0x01,(usb_buff[1]>>2)&0x01);
    // printk("move x:%04d y:%04d ",(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("
    ");
    #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 "); printk("bcdUSB = %x ",dev->descriptor.bcdUSB); printk("VID = 0X%x ",dev->descriptor.idVendor); printk("PID = 0X%x ",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 "); 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

  • 相关阅读:
    实验十四
    2
    解一元二次方程
    第一题
    输入四个人的年龄和姓名,排序后,按年龄,从小到大输出人员年龄及其姓名
    实验九
    实验8数组2 1.程序调试
    实验七4编写程序,输入一批学生的成绩,遇0或负数则输入结束,要求统计并输出优秀(大于85)、通过(60~84)和不及格(小于60)的学生人数。
    实验10 指针2
    作业4
  • 原文地址:https://www.cnblogs.com/ynxf/p/6030792.html
Copyright © 2020-2023  润新知