900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 分析USB鼠标——usbmouse.c

分析USB鼠标——usbmouse.c

时间:2019-04-10 01:16:16

相关推荐

分析USB鼠标——usbmouse.c

声明:本文章是看完韦东山老师的USB鼠标驱动的视频后,结合usb鼠标的驱动程序锁写的自己对usbmouse.c的分析,如果有与您重复的地方,敬请原谅。同时希望大家可以通过本文了解到usb驱动鼠标。而我认为我们不应该只是学习老师所教的课程,而自己独立的分析内核代码也是我们所要学习的内容。

下面就要讲到对内核中usbmouse.c的分析,而在我分析之前,我要几个要点是希望大家可以明白的,因为只有明白了这些知识点,你才可以更好的了解我对usbmouse.c的分析。

1.usb是主从结构的,而我们缩写的驱动程序是为匹配从设备所写的驱动程序

2.说到usb驱动就要讲到usb_bus_type,要了解USB总线类型,只有了解这些才可以对下面的分析充分了解。否则只能是雾里看花

3.对usb的插入拔出主设备的过程有所了解,

4.还有就是对usb的描述符有所了解,

以上这些如果有不明白的我建议你可以看一下我写的文章:

USB原理以及对照hub.c的代码分析

嵌入式Linux —— usb鼠标驱动

而在分析这代码之前我们要介绍一下usb驱动代码的大致格式:

1.分配设置:usb_driver

2.在入口函数中注册,在出口函数中注销

下面我们就进行代码分析,分析一个驱动程序就是要从他的入口程序开始分析:

static int __init usb_mouse_init(void){int retval = usb_register(&usb_mouse_driver); //注册usb_driver结构体if (retval == 0)info(DRIVER_VERSION ":" DRIVER_DESC);return retval;}

我们可以看出这个入口函数中注册了usb_driver结构体,而这个结构体做了什么那?我们看一下他的设置:

static struct usb_driver usb_mouse_driver = {.name= "usbmouse",.probe= usb_mouse_probe,.disconnect= usb_mouse_disconnect,.id_table= usb_mouse_id_table, };

我们可以看到这个结构体中定义了name,probe函数,disconnect函数,以及id_table结构体,而后面的三项是并不可少的,而id_table就是用来与device匹配时进行比较的。只有匹配成功才可以调用probe函数。我们先来看一下这个id_table中定义了什么:

static struct usb_device_id usb_mouse_id_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },{ }/* Terminating entry */};

代码中的USB_INTERFACE_INFO是个结构体:

/*** USB_INTERFACE_INFO - macro used to describe a class of usb interfaces * @cl: bInterfaceClass value* @sc: bInterfaceSubClass value* @pr: bInterfaceProtocol value** This macro is used to create a struct usb_device_id that matches a* specific class of interfaces.*/#define USB_INTERFACE_INFO(cl,sc,pr) \.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

通过注解可以知道这个宏是用来比较特定接口的结构体,而结合上边的id_table我们就可以知道,这个是通过比较接口类,接口子类以及接口协议来与设备进行匹配的。如果匹配成功那就将调用probe函数了:

static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)

内核中的probe函数喜欢先做初始化相关的工作:

struct usb_device *dev = interface_to_usbdev(intf); //定义一个usb_device的机构体struct usb_host_interface *interface; //定义一个usb主机接口struct usb_endpoint_descriptor *endpoint; //定义一个端点描述符struct usb_mouse *mouse; //定义一个usb_mouse结构体struct input_dev *input_dev; //定义一个input_dev结构体 int pipe, maxp;//定义源和最大包数量int error = -ENOMEM;interface = intf->cur_altsetting; //获得当前的接口if (interface->desc.bNumEndpoints != 1)//对当前接口的端点的判断return -ENODEV;endpoint = &interface->endpoint[0].desc; //获得端点,此处的endpoint[0]不是端点0,而是端点1if (!usb_endpoint_is_int_in(endpoint)) //判断此端点是否为输入中断端点(鼠标是输入端点)return -ENODEV;pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //获得源地址,这个宏还将端点的类型以及将端点的地址与设备地址绑定maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));//获得最大包的大小mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);//为usb_mouse分配空间input_dev = input_allocate_device(); //分配input_dev结构体if (!mouse || !input_dev)goto fail1;mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); //获得目的地址if (!mouse->data)goto fail1;mouse->irq = usb_alloc_urb(0, GFP_KERNEL);//分配urb(usb request block),用于传输数据if (!mouse->irq)goto fail2;mouse->usbdev = dev;//将usb_device设备放入usb_mouse中mouse->dev = input_dev;// 将input_dev放入usb_mouse中

从上面的代码可以看出,到这里probe主要做的就是定义各种将要用到的设备结构体,设备描述符结构体,并为其分配空间。同时还将通信时的源以及目标定义并获得相应的地址,并且定义了urb用于通信。

而下面这些则是通过usb_device和input_dev向usb_mouse这个结构体中添加数据:

if (dev->manufacturer) strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));//生产商if (dev->product) {if (dev->manufacturer)strlcat(mouse->name, " ", sizeof(mouse->name));strlcat(mouse->name, dev->product, sizeof(mouse->name)); //产品}if (!strlen(mouse->name))snprintf(mouse->name, sizeof(mouse->name), "USB HIDBP Mouse %04x:%04x",le16_to_cpu(dev->descriptor.idVendor), //设备IDle16_to_cpu(dev->descriptor.idProduct)); //产品IDusb_make_path(dev, mouse->phys, sizeof(mouse->phys));//??strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); input_dev->name = mouse->name; //设备名字input_dev->phys = mouse->phys; //设备的物理地址usb_to_input_id(dev, &input_dev->id); //input_dev 的IDinput_dev->dev.parent = &intf->dev; //设备的父类

而下面的代码就是要设置input_dev结构体了:

input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); //定义产生按键类和相对位移类事件input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); //按键类中的左键,右键,中键input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); //相对位移类的X和Y方向的位移input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); //按键中的侧键以及附加键 input_dev->relbit[0] |= BIT(REL_WHEEL);//相对位移中的滚轮位移input_set_drvdata(input_dev, mouse); //设置input_dev中的私有数据input_dev->open = usb_mouse_open;//定义开函数:应用层打开鼠标设备时此函数被调用input_dev->close = usb_mouse_close; //定义关函数:应用层关闭鼠标设备时此函数被调用input_dev->close = usb_mouse_close; //定义关函数:应用层关闭鼠标设备时此函数被调用

而probe函数之后做的工作就是设置urb从而设置数据的传输:

/*** usb_fill_int_urb - macro to help initialize a interrupt urb* @urb: pointer to the urb to initialize.* @dev: pointer to the struct usb_device for this urb.* @pipe: the endpoint pipe* @transfer_buffer: pointer to the transfer buffer* @buffer_length: length of the transfer buffer* @complete_fn: pointer to the usb_complete_t function* @context: what to set the urb context to.* @interval: what to set the urb interval to, encoded like*the endpoint descriptor's bInterval value.** Initializes a interrupt urb with the proper information needed to submit* it to a device.* Note that high speed interrupt endpoints use a logarithmic encoding of* the endpoint interval, and express polling intervals in microframes* (eight per millisecond) rather than in frames (one per millisecond).*/ usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);mouse->irq->transfer_dma = mouse->data_dma; //告诉urb物理地址mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //告诉urbMAP标记位the endpoint descriptor's bInterval value.** Initializes a interrupt urb with the proper information needed to submit* it to a device.* Note that high speed interrupt endpoints use a logarithmic encoding of* the endpoint interval, and express polling intervals in microframes* (eight per millisecond) rather than in frames (one per millisecond).*/ usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,(maxp > 8 ? 8 : maxp),usb_mouse_irq, mouse, endpoint->bInterval);mouse->irq->transfer_dma = mouse->data_dma; //告诉urb物理地址mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //告诉urbMAP标记位

usb_fill_int_urb函数的参数分别为:urb结构体,usb_device结构体,pipe源,目的地址(此处为虚拟地址),数据长度,中断函数(或完成函数),上下文,以访问时间间隔。

最后probe会注册input_dev,并将usb_mouse这个结构体放入usb_interface中:

error = input_register_device(mouse->dev);

usb_set_intfdata(intf, mouse);

虽然probe函数讲完了,但是他里面的usb_mouse_irq,usb_mouse_open函数还没有完善。下面我们一边讲解一边完善。我们知道主机控制器会不断的查询usb设备,当获得数据后就会将数存储到buffer中并产生中断函数从而导致usb_mouse_irq函数被调用,下面我就将这个函数:

static void usb_mouse_irq(struct urb *urb){struct usb_mouse *mouse = urb->context; //定义usb_mousesigned char *data = mouse->data; //定义目的struct input_dev *dev = mouse->dev; //定义input_devint status;input_report_key(dev, BTN_LEFT, data[0] & 0x01); //上报左键状态input_report_key(dev, BTN_RIGHT, data[0] & 0x02); //上报右键状态input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); //上报中键状态input_report_key(dev, BTN_SIDE, data[0] & 0x08); //上报侧键状态input_report_key(dev, BTN_EXTRA, data[0] & 0x10); //上报附加建状态input_report_rel(dev, REL_X,data[1]); //上报相对位移中X方向位移input_report_rel(dev, REL_Y,data[2]);//上报相对位移中Y方向位移input_report_rel(dev, REL_WHEEL, data[3]);//上报相对位移中滚轮方向位移input_sync(dev); //上报同步事件resubmit:status = usb_submit_urb (urb, GFP_ATOMIC); //提交urbif (status)err ("can't resubmit intr, %s-%s/input0, status %d",mouse->usbdev->bus->bus_name,mouse->usbdev->devpath, status);}

我们可以看出,usb_mouse_irq函数主要的工作就是上报数据。我们说过在usb驱动中我们没有直接直接对硬件的读写函数,而是通过usb总线驱动程序获得读写的函数,但是驱动知道数据的含义,所以他会对数据进行处理。并将这些数据通过urb传输。

而接下来我们要介绍的就是usb_mouse_open函数。当应用层打开鼠标设备时,usb_mouse_open将被调用,这句话告诉我们这个函数什么时候被调用。 代码为:

static int usb_mouse_open(struct input_dev *dev){struct usb_mouse *mouse = input_get_drvdata(dev);mouse->irq->dev = mouse->usbdev;if (usb_submit_urb(mouse->irq, GFP_KERNEL)) //上报urbreturn -EIO;return 0;}

从上面的代码中我们知道,usb_mouse_open函数的主要工作就是上报urb,当应用层打开鼠标设备时,就上报urb,然后就回到上面中断函数中的不断将按键和位移上报。

讲到这里,这个驱动程序就讲完了,而我还是想说前面的那句话,作为要学习驱动的同学,有时候多看看内核自己的驱动代码还是有好处的,虽然有时候你会觉得很难看懂,这时候你可以去网上找一下,也许有很好的解释。

这里我贴一篇对我有帮助的文章:

嵌入式linux下如何使用usb键盘和鼠标

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。