zoukankan      html  css  js  c++  java
  • liunx驱动----USB驱动

    现象:把usb设备接入电脑

    1.Windows发现设备

    2.跳出一个对话框提示安装驱动程序

    问1:既然没有驱动程序,为什么了够知道是什么驱动了??

    答1:Windows里面已经有了usb总线驱动程序,接入usb设备后,是“总线驱动程序知道”是什么驱动。提示安装设备驱动程序

       usb总线驱动程序负责识别USB设备,给usb设备找到对应的驱动程序

    问2.usb设备种类多,为什么接入电脑就能够识别出来了?

    答2.PC和USB设备都的遵守一些规范。

      比如:USB接入电脑后PC机会发出,读取设备类型的命令(描述符)。然后USB设备就回答给PC机(描述符)。

    问3.PC机上接有非常多的USB设备,怎么分辨?

    答3.接在USB总线上的每个USB设备都有自己的编号(地址)PC机想访问某个USB设备的时候,发出的信息都有对方的编号(地址)

    问4.USB设备刚接入PC的时候还没有编号,那么PC怎么把分配的编号告诉

    答4.新接入的USB设备的默认编号为0,在没有分配新的编号前。PC使用0编号和USB通讯。

    问5.为什么一接入USB设备,PC机就能发现USB设备

    答5.PC的USB口内部。D-和D+接了下拉电阻,没有接入USB的时候为低电平,USB设备的USB口内部,D-或D+接了上啦电阻,接入时就有一个硬件的信号通知PC机有USB设备接入。  

    其它概念:

    1.USB是主从设备(主从结构)

      所有的USB传输都是从USB主机发起的。USB设备没有主动通知USB主机的能力。

      例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动的等待PC机来读取数据

    2.USB的传输类型

    a.控制传输:可靠,时间有保证,比如USB设备的识别过程

    b.批量传输:可靠,时间没有保证,U盘

    c.中断传输:可靠,实时,USB鼠标

    d.实时传输:不可靠,实时,USB摄像头

    3.USB传输的对象:端点(endpoint)

      读写U盘,可以细化为:把数据写到u盘的端点1,从u盘的端点2读出数据

      除了端点0以外,每个端点只支持一个方向的数据传输。

      端点0用于控制传输,既能输出也能输入

    4.每一个端点都有传输类型,传输方向。

    5.术语里,程序里说的输入(IN)输出(OUT)。都是基于USB主机的立场说的。

      比如说对于鼠标,数据是从鼠标传输到PC机的。对应的端点称为“输出端点”

    USB驱动

    设备驱动:          1.知道数据的含义。

    USB总线驱动程序(的作用)----> 1.识别设备

                   2.找到并安装对应的USB设备驱动

                   3.提供USB读写函数

    硬件:    USB主机控制器

    USB驱动程框架:

    APP:

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

        USB设备驱动程序

    内核--------------------------------------

        USB总线驱动程序

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

         USB主机控制器

      
    硬件   ---------------------

          USB设备

    /*****************************USB总线驱动程序****************************************/

     USB设备驱动程序编写:

    1.分配/设置usb_derver结构体

      .id_table  表示支持哪些usb设备

          .probe

      .disconnect  

    2.注册

    测试:

    1.make munconfig 去掉内核中原来的usb鼠标驱动

    -> Device Drivers 

    -> HID Devices

       < > USB Human Interface Device (full HID) support 

    2.make uImage  并使用新的内核启动

    使用新内核启动:nfs 30000000 192.168.1.102:/home/book/work/nfs_root/first_fs/uImage_nohid; 30000000

    挂接根文件系统:mount -t nfs -o nolock,vers=2 192.168.1.102:/home/book/work/nfs_root/first_fs  /mut

    3.insmod usbmonus_as_key.ko



    实验1:

    目的:通过控制台将数据数据移动按下数据打印出来。

    步骤1:得到  usb_host_interface  结构体

    interface = intf->cur_altsetting;//interface结构体

    步骤2:获取端点描述符

    endpoint = &interface->endpoint[0].desc;//端点描述符

     步骤3://1.分配一个input_dev 结构体

    uk_dev = input_allocate_device();

    步骤4:设置uk_dev结构体

    步骤5:注册input_dev结构体

    步骤6:硬件相关的操作 (使用usb总线设备驱动来收发数据)

    //数据传输的三要素: 源 、目的、长度
    //源:USB的某个端点

    代码分为:

    usbmouse_as_key_probe函数(初始化需要操作鼠标的设置)     

     usbmouse_as_key_irq函数(当鼠标有数据变化时将调用这个函数)       

    usbmouse_as_key_disconnect函数(鼠标的断开处理函数)

    usbmouse_as_key_probe代码如下:

    static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
    {
        /*打印厂家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;//interface结构体  
    
        
        endpoint = &interface->endpoint[0].desc;//端点描述符
    #if  0
        struct usb_device *dev = interface_to_usbdev(intf);//通过  interface_to_usbdev接口函数得到  usb_device          id
        printk("bcd_USB = %x
    ",dev->descriptor.bcdUSB);
        printk("vid = 0x%x
    ",dev->descriptor.idVendor);
        printk("pid = 0x%x
    ",dev->descriptor.idProduct);
        printk("found usbmonuse!
    ");
    #endif
    
        //1.分配一个input_dev 结构体
        uk_dev = input_allocate_device();
    
        //2.设置分配的这个结构体
        //2.1能产生那类事件
        set_bit(EV_KEY, uk_dev->evbit);//设置能够产生按键类事件uk_dev->evbit是表示能够产生那类事件
        set_bit(EV_REP, uk_dev->evbit);//设置能够产生重复类事件uk_dev->evbit是表示能够产生那类事件
        //2.2能产生哪些事件
        set_bit(KEY_L,uk_dev->keybit);//通过设置uk_dev->keybit   实现产生一个KEY_L的事件  (相当于按键按下 L 键)
        set_bit(KEY_S,uk_dev->keybit);//通过设置uk_dev->keybit   实现产生一个KEY_S的事件  (相当于按键按下 S 键)
        set_bit(KEY_ENTER,uk_dev->keybit);//通过设置uk_dev->keybit   实现产生一个KEY_ENTER的事件  (相当于按键按下 ENTER 键)
        //3.注册input_dev结构体
        input_register_device(uk_dev);
        //4.硬件相关的操作   (使用usb总线设备驱动来收发数据)
        //数据传输的三要素:  源  、目的、长度
        //源:USB的某个端点
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
    
        //长度:
        len =endpoint->wMaxPacketSize;//长度等于端点描述符的wMaxPacketSize(最大包大小)
    
        //目的:使用usb_buffer_alloc分配一个数据缓冲区
        usb_buf=usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_pyhs);
    
    
        //使用三要素  usb requset block
        uk_urb = usb_alloc_urb(0, GFP_KERNEL);
    
        //使用三要素  设置usb requset block
        /*             usb请求块, */
        usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
        uk_urb->transfer_dma = usb_buf_pyhs;//设置usb传输的目的 的物理地址
        uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//设置标记
        
        //使用urb
        usb_submit_urb (uk_urb, GFP_KERNEL);//提交数据 提交urb
        return 0;
    }

     usbmouse_as_key_irq代码如下:(当鼠标数据发生变化时执行此函数)

    static void usbmouse_as_key_irq(struct urb *urb)
    {
        int i;
        static int cnt=0;
        /*将鼠标数据打印出来*/
        printk("usb data %d: ",++cnt);
        for(i=0;i<len;i++)
            {
                printk("%02x ",usb_buf[i]);
            }
        printk("
    ");
        //重新提交urb    
        usb_submit_urb (uk_urb, GFP_KERNEL);//提交数据 提交urb
    }

    usbmouse_as_key_disconnect代码如下:其实就是usbmouse_as_key_probe的逆向操作

    static void usbmouse_as_key_disconnect(struct usb_interface *intf)
    {
        struct usb_device *dev = interface_to_usbdev(intf);
    
    //printk("dsiconnect usbmonuse!
    ");
        usb_kill_urb (uk_urb);//清除urb
        usb_free_urb(uk_urb);//释放urb
        usb_buffer_free(dev,len,usb_buf,usb_buf_pyhs);//去除  usb_buf
        input_unregister_device(uk_dev);
        input_free_device(uk_dev);
    }

    测试步骤: 

    1、insmod usbmonus_as_key.ko  挂载驱动

    2、ls /dev/event*        查看事件

     3、移动或者按下鼠标  

    数据1:按键值

    数据2:x方向位移 数据

    数据3:y方向位移 数据

    数据4:滚轮数据

    实验2:

    目的:使用鼠标上的按键实现  L  S 和ENTER 键值  在控制台上输出

    通过实验一,已经将鼠标数据读了出来,所以要实现按键的话只关心第一个数据就可以了

    所以只需要修改usbmouse_as_key_disconnect

    static void usbmouse_as_key_irq(struct urb *urb)
    {
        static int pre_val;//用来保存上次的数据
        
    #if 0
        int i;
        static int cnt=0;
        
        /*将鼠标数据打印出来*/
        printk("usb data %d: ",++cnt);
        for(i=0;i<len;i++)
            {
                printk("%02x ",usb_buf[i]);
            }
        printk("
    ");
    #endif
        
        /*
        * USB鼠标数据含义
        * data[0]:bit0-左键   1-按下   0-松开
        *          bit1-右键   1-按下   0-松开
        *          bit2-中键   1-按下   0-松开
        */
        if((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
            {
                //左键发生了变化
                input_event(uk_dev,EV_KEY,KEY_L,(usb_buf[0] & (1<<0))? 1 : 0);//使用input_event上报按键事件
                input_sync(uk_dev);//同步信号
            }
        if((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
            {
                //右键发生了变化
                input_event(uk_dev,EV_KEY,KEY_S,(usb_buf[0] & (1<<1))? 1 : 0);//使用input_event上报按键事件
                input_sync(uk_dev);//同步信号
            }
        if((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
            {
                //中键发生了变化
                input_event(uk_dev,EV_KEY,KEY_ENTER,(usb_buf[0] & (1<<2))? 1 : 0);//使用input_event上报按键事件
                input_sync(uk_dev);//同步信号
            }
        pre_val = usb_buf[0];
        //重新提交urb    
        usb_submit_urb (uk_urb, GFP_KERNEL);//提交数据 提交urb
    }

    使用/dev/tty1 测试:

    使用hexdump /dev/event1 测试:

  • 相关阅读:
    算法第4章实践报告
    避免商品超卖的4种方案
    PHP 之获取Windows下CPU、内存的使用率
    XunSearch(讯搜)的使用教程步骤
    curl传递二维数组,打印没有数据,只显示Array
    使用Postfix和Dovecot收发电子邮件
    Mybatis中使用association进行关联的几种方式
    两个服务器之间文件互传
    php-fpm 高并发 参数调整
    高并发linux内核网络参数调优
  • 原文地址:https://www.cnblogs.com/hjxzjp/p/10673896.html
Copyright © 2011-2022 走看看