赞
踩
本篇博客的内核基于linux2.6.22
先深入分析内核中自带usb鼠标驱动
入口函数注册一个usb_driver
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 */ }; static struct usb_driver usb_mouse_driver = { .name = "usbmouse", .probe = usb_mouse_probe, .disconnect = usb_mouse_disconnect, .id_table = usb_mouse_id_table, }; static int __init usb_mouse_init(void) { int retval = usb_register(&usb_mouse_driver); if (retval == 0) info(DRIVER_VERSION ":" DRIVER_DESC); return retval; }
当driver和device匹配成功后会调用probe函数
struct usb_mouse { char name[128]; char phys[64]; struct usb_device *usbdev; struct input_dev *dev; struct urb *irq; signed char *data; dma_addr_t data_dma; }; static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id) { /*根据usb_interface 来获取usb_device */ struct usb_device *dev = interface_to_usbdev(intf); struct usb_host_interface *interface; /*端点是USB通信的最基本方式*/ struct usb_endpoint_descriptor *endpoint; struct usb_mouse *mouse; struct input_dev *input_dev; int pipe, maxp; int error = -ENOMEM; /*从usb_interface 获取usb_host_interface */ interface = intf->cur_altsetting; if (interface->desc.bNumEndpoints != 1) return -ENODEV; /*获取endpoint*/ endpoint = &interface->endpoint[0].desc; if (!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); input_dev = input_allocate_device(); 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); if (!mouse->irq) goto fail2; mouse->usbdev = dev; mouse->dev = input_dev; 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), le16_to_cpu(dev->descriptor.idProduct)); usb_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->dev.parent = &intf->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); 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->open = usb_mouse_open; input_dev->close = usb_mouse_close; 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; mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; error = input_register_device(mouse->dev); if (error) goto fail3; usb_set_intfdata(intf, mouse); return 0; fail3: usb_free_urb(mouse->irq); fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); fail1: input_free_device(input_dev); kfree(mouse); return error; }
这个函数可以说是mouse驱动的主体,对于这个函数中,我们重点关注两个内容
1、usb_fill_int_urb 函数
2、在这里并没有定义全局变量,那是如何在不同函数中实现信息传递的呢?可以深入地分析并学习一下
/** 不必去分析这个函数底层是如何实现的,我也做不到,主要是分析如何使用这个函数 这个函数很像request_irq,向内核注册一个中断函数 看下这个函数的英文注释 * usb_fill_int_urb - macro to help initialize a interrupt urb * @urb: pointer to the urb to initialize. * 第一个参数是urb usb request block * @dev: pointer to the struct usb_device for this urb. * the endpoint pipe信息源res * @pipe: the endpoint pipe * transfer_buffer信息dest * @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 and SuperSpeed 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). * * Wireless USB also uses the logarithmic encoding, but specifies it in units of * 128us instead of 125us. For Wireless USB devices, the interval is passed * through to the host controller, rather than being translated into microframe * units. */ static inline void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval) struct urb *urb的设置 mouse->irq = usb_alloc_urb(0, GFP_KERNEL); struct usb_device *dev struct usb_device *dev = interface_to_usbdev(intf); unsigned int pipe pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); void *transfer_buffer mouse->data = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &mouse->data_dma); void *context mouse
现在来分析第二个问题
可以想象得到在irq函数中,也就是complete_fn中,必然会用mouse,因为input_dev和装有信息的data都在mouse中,而mouse又不是全局变量,那我们是不是要以传参的方式传递给complete_fn函数呢?显然不行,complete_fn属于内核已经存在的一个通用型接口,它的传参是urb结构体,而mouse是我们自己定义的,肯定不可以用参数传递的方式。
所以我们可以根据以往从file的private_data获得启示,看下urb有没有void *成员
发现有两个
struct urb {
void *transfer_buffer; /* (in) associated data buffer */
void *context; /* (in) context for completion */
};
transfer_buffer我们已经用来当作dest缓冲区了,所以只能使用context
事实上也确实是这样来处理的
static inline void usb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, unsigned char *setup_packet, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context) { urb->dev = dev; urb->pipe = pipe; urb->setup_packet = setup_packet; urb->transfer_buffer = transfer_buffer; urb->transfer_buffer_length = buffer_length; urb->complete = complete_fn; /*在我们的mouse驱动中,context是指向mouse的指针*/ urb->context = context; }
这是处理complete_fn函数,而对于这个驱动还有一个disconnect函数
它的传参是usb_interface,所以我们来看下它有哪些可以用来保存mouse的void *
然而我们发现并没有,这怎么办
我们可以找它的成员结构体中是否有void 指针
struct device {
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct device_private *p;
};
struct device_private {
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
struct list_head deferred_probe;
void *driver_data;
struct device *device;
};
这个并不行,因为注释已经说明了platform_data是专门给Platform specific data所使用的
usb_set_intfdata(intf, mouse); static inline void usb_set_intfdata(struct usb_interface *intf, void *data) { dev_set_drvdata(&intf->dev, data); } int dev_set_drvdata(struct device *dev, void *data) { int error; if (!dev->p) { error = device_private_init(dev); if (error) return error; } dev->p->driver_data = data; return 0; }
现在我们来看下complete_fn
static void usb_mouse_irq(struct urb *urb) { struct usb_mouse *mouse = urb->context; signed char *data = mouse->data; struct input_dev *dev = mouse->dev; int status; /*首先判断urb的状态位,如果没有设置(为0),则为成功*/ switch (urb->status) { case 0: /* success */ break; case -ECONNRESET: /* unlink */ case -ENOENT: case -ESHUTDOWN: return; /* -EPIPE: should clear the halt */ default: /* error */ goto resubmit; } /*上报数据的设置,value就是我们从在probe函数中调用的usb_alloc_coherent申请到的缓冲区 * data[0] data[1]等和坐标点击之间的关系,这需要对usb通信做进一步分析才可以理解 * 但是可以从这个函数中看出,一次是上传了所有相关的数据 */ 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]); input_report_rel(dev, REL_Y, data[2]); input_report_rel(dev, REL_WHEEL, data[3]); input_sync(dev); resubmit: status = usb_submit_urb (urb, GFP_ATOMIC); if (status) dev_err(&mouse->usbdev->dev, "can't resubmit intr, %s-%s/input0, status %d\n", mouse->usbdev->bus->bus_name, mouse->usbdev->devpath, status); }
现在看下我们自己写的程序,我这里仅仅只列出probe函数和complete函数
struct usbmouse_as_key { struct usb_device *usbdev; struct input_dev *dev; struct urb *irq; int len; signed char *data; dma_addr_t data_dma; }; static void usbmouse_as_key_irq(struct urb *urb) { struct usbmouse_as_key *usbmouse_as_key_t = urb->context; signed char *data = usbmouse_as_key_t->data; struct input_dev input_dev = usbmouse_as_key_t->dev; int status; switch (urb->status) { case 0: /* success */ break; case -ECONNRESET: /* unlink */ case -ENOENT: case -ESHUTDOWN: return; /* -EPIPE: should clear the halt */ default: /* error */ goto resubmit; } input_event(input_dev, EV_KEY, KEY_L, (data[0] & (1<<0)) ? 1 : 0); input_event(input_dev, EV_KEY, KEY_S, (data[0] & (1<<1)) ? 1 : 0); input_event(input_dev, EV_KEY, KEY_ENTER, (data[0] & (1<<2)) ? 1 : 0); input_sync(input_dev); resubmit: status = usb_submit_urb(urb, GFP_KERNEL); } static int usbmouse_as_key_probe(struct usb_interface *iface, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(iface); struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; struct input_dev *input_dev; struct usbmouse_as_key *usbmouse_as_key_t; int pipe, len; interface = iface->cur_altsetting; endpoint = &interface->endpoint[0].desc; /*分配input_dev*/ input_dev = input_allocate_device(); usbmouse_as_key_t = kzalloc(sizeof(struct usbmouse_as_key), GFP_KERNEL); usbmouse_as_key_t->usbdev = dev; usbmouse_as_key_t->dev = input_dev; /*事件类型为按键事件和可重复按键事件*/ 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(usbmouse_as_key_t->dev); usbmouse_as_key_t->irq = usb_alloc_urb(0, GFP_KERNEL); pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); usbmouse_as_key_t->len = endpoint->wMaxPacketSize; usbmouse_as_key_t->data = usb_buffer_alloc(dev, usbmouse_as_key_t->len, GFP_ATOMIC, &usbmouse_as_key_t->data_dma); usb_fill_int_urb(usbmouse_as_key_t->irq, dev, pipe, usbmouse_as_key_t->data, usbmouse_as_key_t->len, usbmouse_as_key_irq, usbmouse_as_key_t,endpoint->bInterval); usbmouse_as_key_t->irq->transfer_dma = usbmouse_as_key_t->data_dma; usbmouse_as_key_t->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_set_intfdata(iface, usbmouse_as_key_t); return 0; } 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_as_key", .probe = usbmouse_as_key_probe, .disconnect = usbmouse_as_key_disconnect, .id_table = usbmouse_as_key_id_table, }; static int usbmouse_as_key_init(void) { usb_register(&usbmouse_as_key_driver); return 0; } static void usbmouse_as_key_exit(void) { usb_deregister(&usbmouse_as_key_driver); } module_init(usbmouse_as_key_init); module_exit(usbmouse_as_key_exit); MODULE_LICENSE("GPL");
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。