usbmouse分析

Leesagacious 原创,欢迎转载,未完待续

/**
    入口函数,注册了一个usb_driver
*/
static int __init usb_mouse_init(void)
{
  /**所有驱动的注册,都要经过driver_register()这个函数, 
     同样会遍历挂在usb总线上的所有驱动,查看有没有同名的驱动,
     然后将该驱动挂到总线上(bus_type_private的driver_kset容器)。设备驱动模型的详细信息请看另一篇博文 
  */
  int retval = usb_register(&usb_mouse_driver);
  if(retval == 0){
      printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
                DRIVER_DESC "\n");
  }
  return retval;
}   
看一下这个usb_register()函数:
static inline int usb_register(struct usb_driver *driver)
{    //调用了这个函数来注册
    return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}

int usb_register_driver(struct usb_driver * new_driver,struct module * owner,const char * mod_name)
{
    int retval = 0;
    //判断内核在启动的时候有没有禁止usb子系统启动
    if(usb_disabled()){
        return - ENODEV;
    }
    /**
        驱动在usb的世界里分成了设备驱动和接口驱动两种,
        为了区分这两种驱动,就中间加了一层,添加了for_device标志来判断
        struct usbdrv_wrap{
            struct device_driver driver;
            int for_device;  //0 接口驱动, 非0,设备驱动
        }
    */
    /**
    下面的代码是对 driver的一些设置,
    */
    new_driver->drvwrap.for_devices = 0; //表示它是接口驱动
    new_driver->drvwrap.driver.name = (char *) new_driver->name; //驱动的名字
    new_driver->drvwrap.driver.bus = &usb_bus_type;    //所挂在的总线
    new_driver->drvwrap.driver.probe = usb_probe_interface;//探测函数
    new_driver->drvwrap.driver.remove = usb_unbind_interface;//移除的时候出发的函数
    new_driver->drvwrap.driver.owner = owner;   //所属的模块
    new_driver->drvwrap.driver.mod_name = mod_name;//驱动模块的名字
    spin_lock_init(&new_driver->dynids.lock);
    INIT_LIST_HEAD(&new_driver->dynids.list);

    if (!retval) {
        pr_info("%s: registered new interface driver %s\n",
            usbcore_name, new_driver->name);
        usbfs_update_special();
        /**
            if (usb_drv->probe != NULL)
        error = driver_create_file(&usb_drv->drvwrap.driver,
&driver_attr_new_id);
          上面是这个函数的源码,
          如果驱动提供了probe函数,就为这个驱动创建属性文件              
        */
        usb_create_newid_file(new_driver);
    } else {
        printk(KERN_ERR "%s: error %d registering interface "
            "   driver %s\n",
            usbcore_name, retval, new_driver->name);
    }
    return retval;
}
看看usb_mouse_driver usb接口驱动
static struct usb_driver usb_mouse_driver = {
    .name = "usbmouse",   //驱动名字
    .probe = usb_mouse_probe,//匹配成功后调用的方法
    .disconnect = usb_mouse_disconnect,//移除设备时出发的方法
    .id_table = usb_mouse_id_table,//驱动支持的设备列表
}
匹配的依据

当插入usb鼠标的时候,会根据usb驱动中成员.id_table来匹配,id_table中存放了该驱动支持的设备列表

 static struct usb_device_id usb_mouse_id_table[] = {
/**
    #define USB_INTERFACE_INFO(cl,sc,pr)\
            match_flags = USB_DEVICE_ID_MATCH_DEV_INFO,\
            bDeviceClass = (cl),\  //设备的类型
            bDeviceSubclass = (src),\//设备的子类型
            bDeviceProtocol = (pr)//设备使用的协议
    这个宏用来创建要给struct usb_device_id结构体,这个结构体存储了设备的类型和协议信息     
*/  
 //设备的类型是 人机交互类设备,子类型是一种boot设备 遵循鼠标协议,那么就支持
 {USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE)},
   {} //空项表示结束
 }

如果匹配成功,那么会调用驱动提供的probe函数,会动态的创建一个usb_device,并调用device_add()将它放入到linux设备驱动模型中。其实如果总线提供了probe函数的话,内核是优先调用bus的probe函数的(详细请见另外一片博文),

匹配成功后会调用驱动提供的probe函数
    /**
        由于usb总线没有提供probe函数,那么就会调用驱动提供的probe函数
        这个probe函数可以划分位两部分: 
                    1  :  验证  
                            根据HID规范: 鼠标只能有一个端点(0号端点用于控制除外,),而且这个端点一定要是中断输入端点
                            是怎么验证的呢:
                                    if(interface->desc.bNumEndpoints != 1) 判断端点的数量
                                    if (!usb_endpoint_is_int_in(endpoint)) 是否是中断输入端点
                          初始化 : 就是分配了一个input_dev,然后set_bit()设置支持什么事件之类的
                                  初始化中断urb
                                  注册输入设备        
    */
    static int usb_mouse_probe(struct usb_interface * intf,const struct usb_device_id *id)
    {
        /**
            根据usb_interface获取动态创建的usb_device。
            那么,usb_device是怎么被动态创建的呢,下面简单的说一下
            在hub初始化的时候,会创建一个内核线程"khub",它用来监测hub状态的变化,
            Hub正常工作后,usb主机控制器就会定时的询问hub,如果hub端口上有usb设备的插入、拔出的时候,hub就会把端口的状态变化告诉usb主机控制器,..........最终urb的完成函数hub_irq()会被调用
            hub_irq()会调用kick_khubd(),kick_khubd()会调用wake_up(&khubd_wait);来唤醒上面的"khub"守护进程,这个守护进程会调用hub_event(),在这个函数中,最终会调用hub_port_connect_change()
            下面来看这个hub_port_connect_change()函数
            hub_port_connect_change( )
            {
                struct usb_device *udev;
                udev = usb_alloc_dev(hdev, hdev->bus, port1);
                usb_new_device(udev)
                {
                  /**
                      看最终会调用device_add()来向设备驱动模型中添加一个设备
                      所以,可以看出在usb插入后,系统会自动注册设备到驱动模型中
                      我们要做的只是usb_driver的事情了
                  */
                    err = device_add(&udev->dev) 
                }
            }
        */
        struct usb_device * dev = interface_to_usbdev(intf);
        struct usb_host_interface * interface;
        struct usb_endpoint_descriptor * endpoint;//定义usb端点描述符
        struct usb_mouse *mouse;      //usb鼠标结构体指针
        struct input_dev * input_dev ;// 代表一个输入设备

        int pipe,maxp;
        int error = -ENOMEM;
        /**
            获取当前正在使用的设置
        */
        interface = intf->cur->altsetting;

        /**
            interface->desc : usb接口描述符 usb_interface_descriptor
            bNumEndpoints 端点的数量(端点0除外)
        */
        if(interface->desc.bNumEndpoints != 1)
        {
            return -ENODEV;
        }
        /**
            获取端点描述符
        */
        endpoint = &interface->endpoint[0].desc;
        /**
            判断这个端点是不是中断输入端点
            那么是怎么判断的呢? 源码 : 
            static inline int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor*epd)
            {
                return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd))
                {
                    取bmAttributes的[1:0],判断它是不是 11
                    return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT); 
                }
            }
            usb端点描述符中的 bmAttributes 的低两位 用于 标示 端点传输类型
            #define USB_ENDPOINT_XFER_CONTROL   0
            #define USB_ENDPOINT_XFER_ISOC      1
            #define USB_ENDPOINT_XFER_BULK      2
            #define USB_ENDPOINT_XFER_INT       3   中断传输
        */
        if(!usb_endpoint_is_int_in(endpoint))
        {
            return -ENODEV;
        }
        /**
            创建中断管道
            实现代码:
                 #define usb_rcvintpipe(dev,endpoint)\
                         ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)    

                 static inline unsigned int __create_pipe(struct usb_device * dev,unsigned int endpoint)
                 {
                    return (dev->devnum << 8) | (endpoint << 15);               
                 }      

                 PIPE_INTERRUPT : 1, 表示是中断管道
                 USB_DIR_IN     :0x80 用来表示数据传输方向,由设备向主机

                pipe  : 32位,
                         8~14 : 设备号,
                         15~18: 端点号
                         7    : 方向

                bEndpointAddress : 
                                    8位
                                    0~3 :端点号,  bEndpointAddress &0x0f
                                    7   :端点的方向   使用宏USB_DIR_IN、USB_DIR_OUT来判断短点的额方向 
        */
        pipe = usb_rcvintpipe(dev,endpoint->bEndpointAddress);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值