【输入子系统01】USB触摸屏驱动
发布日期:2021-06-29 14:51:12 浏览次数:3 分类:技术文章

本文共 7781 字,大约阅读时间需要 25 分钟。

【输入子系统01】USB触摸屏驱动

驱动编译

目前的kernel中都是自带了usbtouchscreen驱动的,我的版本3.1.10

源码位于:/kernel/drivers/input/touchscreen/usbtouchscreen.c
从这个路径可以看出所属驱动分支,我这边平台本身是没放开的,并没有编译进kernel,谁会想到触摸电视呢~

可以在make menuconfig之后,通过Device Drivers——>Input device support——>Touchscreens——>USB Touchscreen Driver 然后选取需要的touchscreen类型

注册usb驱动

熟悉linux驱动的都知道模块入口:module_init(usbtouch_init) ,这里看下这个init:

static int __init usbtouch_init(void){
return usb_register(&usbtouch_driver); //调用了usb 核心的注册函数,传入的是一个usb_driver结构体指针}

usb_register实现在/kernel/include/linux/usb.h中:

static inline int usb_register(struct usb_driver *driver){
return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);//这里再往后就是usb核心驱动的事,注册这个module驱动到usb总线上}

这里必须是要先注册的总线,当一个USB设备被插入的时候,USB设备驱动,

也就是usb_generic_driver会跟USB设备交互,得到其所有的各种描述符,并为每个接口都定义成为一个device,
之后再加载到usb_bus上,让其去匹配其对应的接口驱动程序,有兴趣可以去看下/kernel/drivers/base/bus.c中的bus_for_each_drv函数。

这里注册到总线的接口驱动就是 usbtouch_driver

usbtouch_driver:

这个usb_driver类型的变量usbtouch_driver 就是整个usbtouchscreen的灵魂核心,可以在上面说到的usb.h中查看usb_driver结构原型,

这里usbtouch_driver使用了部分接口:

static struct usb_driver usbtouch_driver = {
.name = "usbtouchscreen", //driver name .probe = usbtouch_probe, //probe接口,用于总线上匹配检测到这个驱动对应的设备之后,/kernel/drivers/usb/core/driver.c中的usb_probe_interface调用到我们这个驱动的接口 .disconnect = usbtouch_disconnect, //与probe相反,断开的时候调用 .suspend = usbtouch_suspend, //usb 设备挂起 .resume = usbtouch_resume, // 和上面挂起相反,唤醒 .reset_resume = usbtouch_reset_resume, // 重置唤醒 .id_table = usbtouch_devices, //支持的设备ID表 .supports_autosuspend = 1,};

id_table:

首先可以关注一下 id_table 这个变量,代表支持的设备id列表,数据类型为:

struct usb_device_id {
/* which fields to match against? */ __u16 match_flags; /* Used for product specific matches; range is inclusive */ __u16 idVendor; __u16 idProduct; __u16 bcdDevice_lo; __u16 bcdDevice_hi; /* Used for device class matches */ __u8 bDeviceClass; __u8 bDeviceSubClass; __u8 bDeviceProtocol; /* Used for interface class matches */ __u8 bInterfaceClass; __u8 bInterfaceSubClass; __u8 bInterfaceProtocol; /* not matched against */ kernel_ulong_t driver_info;};

这些设备信息会被上面说到的usb bus 来匹配对应的驱动,只有这里的信息跟usb设备驱动那边收集到的设备信息匹配上,才会调用进这个驱动.

目前已有的id_table:

static const struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX /* ignore the HID capable devices, handled by usbhid */ {
USB_DEVICE_HID_CLASS(0x0eef, 0x0001), .driver_info = DEVTYPE_IGNORE}, {
USB_DEVICE_HID_CLASS(0x0eef, 0x0002), .driver_info = DEVTYPE_IGNORE},...#endif...};

其中可以看到 两个字节的十六进制数字,第一个代表idVendor 厂商ID,idProduct 产品ID ,这两个一般作为设备的标识.

driver_info

像上面的usbtouch_devices的数组中driver_info 设置为枚举值:

/* device types */enum {
DEVTYPE_IGNORE = -1, DEVTYPE_EGALAX, DEVTYPE_PANJIT, DEVTYPE_3M, DEVTYPE_ITM, DEVTYPE_ETURBO, DEVTYPE_GUNZE, DEVTYPE_DMC_TSC10, DEVTYPE_IRTOUCH, DEVTYPE_IDEALTEK, DEVTYPE_GENERAL_TOUCH, DEVTYPE_GOTOP, DEVTYPE_JASTEC, DEVTYPE_E2I, DEVTYPE_ZYTRONIC, DEVTYPE_TC45USB, DEVTYPE_NEXIO,};

那么这些driver 的真正的info保存在哪里呢?

在注册的时候,现在只是注册上去一个枚举数字而已,
真正有设备识别到的时候这些个枚举值就起到作用了! 在下面的 usbtouch_probe 会介绍!

usbtouch_probe:

在前面有稍微提到,usbtouchscreen驱动是怎么被映射到的,这个过程暂时不做深入,作为这个驱动中的第一个接入点就是usbtouch_probe.

static int usbtouch_probe(struct usb_interface *intf, const struct usb_device_id *id){
struct usbtouch_usb *usbtouch; //usbtouch 设备 struct input_dev *input_dev; //输入设备 struct usb_endpoint_descriptor *endpoint; //usb 的端点 struct usb_device *udev = interface_to_usbdev(intf); //从usb接口获取对应的设备 struct usbtouch_device_info *type; //这个就是上面说的真正的 driver info了 endpoint = usbtouch_get_input_endpoint(intf->cur_altsetting); //获取端点 if (!endpoint) return -ENXIO; usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL); input_dev = input_allocate_device(); //分配内存,申请input 设备结构 ... type = &usbtouch_dev_info[id->driver_info]; // 这里就用到了 上面说到的枚举值了, 真正的info 是放在这个数组里面的! ... usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL); //分配了一个urb 用于 获得触摸屏设备返回的触摸事件的数据,urb的概念可参考usb driver if (!usbtouch->irq) {
dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__); goto out_free_buffers; } ... //往下都是一些分配内存,input注册,初始化操作了 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); //这里是就是input设备触摸坐标的初始化赋值了,为ABS 绝对坐标 input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, type->min_xc, type->max_xc, 0, 0); input_set_abs_params(input_dev, ABS_Y, type->min_yc, type->max_yc, 0, 0); ... if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT) usb_fill_int_urb(usbtouch->irq, udev, usb_rcvintpipe(udev, endpoint->bEndpointAddress), usbtouch->data, type->rept_size, usbtouch_irq, usbtouch, endpoint->bInterval); else usb_fill_bulk_urb(usbtouch->irq, udev, usb_rcvbulkpipe(udev, endpoint->bEndpointAddress), usbtouch->data, type->rept_size, usbtouch_irq, usbtouch); //初始化urb的回调函数为 usbtouch_irq usbtouch->irq->dev = udev; usbtouch->irq->transfer_dma = usbtouch->data_dma; usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ...}

usbtouch_device_info

这个就是上面driver_info 以及usbtouch_probe 中抽取的驱动模块的info数组,

不同的usbtouchscreen 注册的时候就是注册了一个枚举值,这个值就是usbtouch_dev_info 数组的第几元素.

struct usbtouch_device_info {
int min_xc, max_xc; int min_yc, max_yc; int min_press, max_press; int rept_size; /* * Always service the USB devices irq not just when the input device is * open. This is useful when devices have a watchdog which prevents us * from periodically polling the device. Leave this unset unless your * touchscreen device requires it, as it does consume more of the USB * bandwidth. */ bool irq_always; void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len); //这个函数指针是用来接收处理中断的。 /* * used to get the packet len. possible return values: * > 0: packet len * = 0: skip one byte * < 0: -return value more bytes needed */ int (*get_pkt_len) (unsigned char *pkt, int len); int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt); int (*alloc) (struct usbtouch_usb *usbtouch); int (*init) (struct usbtouch_usb *usbtouch); void (*exit) (struct usbtouch_usb *usbtouch);};

usbtouch_dev_info 数组:

static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX [DEVTYPE_EGALAX] = {
.min_xc = 0x0, .max_xc = 0x07ff, .min_yc = 0x0, .max_yc = 0x07ff, .rept_size = 16, .process_pkt = usbtouch_process_multi,//用于中断回调函数,用于处理中断,得到input的event,上传数据 .get_pkt_len = egalax_get_pkt_len, .read_data = egalax_read_data, //用于中断回调函数,用于读取数据 },#endif...#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH [DEVTYPE_IRTOUCH] = {
.min_xc = 0x0, .max_xc = 0x0fff, .min_yc = 0x0, .max_yc = 0x0fff, .rept_size = 8, .read_data = irtouch_read_data, },#endif ...};

可以看到这个数组的成员都是以前面说到的注册枚举值来区分的!

这些x,y 参数以及回调函数,都在上面说到的 usbtouch_probe 中被抽离出来使用.

usbtouch_irq:

这个函数作为中断响应函数,在上面的 usbtouch_probe中初始化,看下函数主要实现:

static void usbtouch_irq(struct urb *urb){
... usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length); //这个type的类型就是 usbtouch_device_info,此时的process_pkt指针自然指向的是上面对应的函数,如果此时是触发的设备type为 DEVTYPE_EGALAX,那么这里调用的 usbtouch_process_multi //如果此时是DEVTYPE_IRTOUCH 那么就是执行 usbtouch_process_pkt函数,因为usbtouch_probe中: // if (!type->process_pkt)// type->process_pkt = usbtouch_process_pkt;..}

接下来的都会调用到usbtouch_process_pkt中,通过type->read_data,和上面一样的指针读取,然后调用input_report_key发送,input_sync用于同步.

关于usbtouchscreen的驱动部分就分析到这里。

本转自 https://blog.csdn.net/jscese/article/details/41827495

转载地址:https://ciellee.blog.csdn.net/article/details/104904189 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【输入子系统02】输入子系统Input_event 传递
下一篇:Android输入子系统之启动过程分析

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月17日 04时02分28秒