版权声明:免责声明: 本人在此发文(包括但不限于汉字、拼音、拉丁字母)均为随意敲击键盘所出,用于检验本人电脑键盘录入、屏幕显示的机械、光电性能,并不代表本人局部或全部同意、支持或者反对观点。如需要详查请直接与键盘生产厂商法人代表联系 .挖井挑水无水表,不会网购无快递
最近需要往TV上装一个触摸屏设备,现在比较常见的就是使用usb接口的触摸框,适用于各种平台,这里大体记录一下在Android上kernel中的usbtouchscreen驱动.
撰写不易,转载需注明出处:/jscese/article/details/41827495
驱动编译:
目前的kernel中都是自带了usbtouchscreen驱动的,我的版本3.1.10
源码位于:/kernel/drivers/input/touchscreen/usbtouchscreen.c
从这个路径可以看出所属驱动分支,我这边平台本身是没放开的,并没有编译进kernel,谁会想到触摸电视呢~
可以在make menuconfig之后,通过Device Drivers——>Input device support——>Touchscreens——>USB Touchscreen Driver 然后选取需要的touchscreen类型
通过查看相关目录下的的Kconfig Makefile,可参考:Kernel 编译配置机制
注册usb驱动:
熟悉Linux驱动的都知道模块入口:module_init(usbtouch_init) ,这里看下这个init:
staticint__initusbtouch_init(void){returnusb_register(&usbtouch_driver);//调用了usb核心的注册函数,传入的是一个usb_driver结构体指针}
usb_register实现在/kernel/include/linux/usb.h中:
staticinlineintusb_register(structusb_driver*driver){returnusb_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使用了部分接口:
staticstructusb_driverusbtouch_driver={.name="usbtouchscreen",//drivername.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列表,数据类型为:
structusb_device_id{/*whichfieldstomatchagainst?*/__u16match_flags;/*Usedforproductspecificmatches;rangeisinclusive*/__u16idVendor;__u16idProduct;__u16bcdDevice_lo;__u16bcdDevice_hi;/*Usedfordeviceclassmatches*/__u8bDeviceClass;__u8bDeviceSubClass;__u8bDeviceProtocol;/*Usedforinterfaceclassmatches*/__u8bInterfaceClass;__u8bInterfaceSubClass;__u8bInterfaceProtocol;/*notmatchedagainst*/kernel_ulong_tdriver_info;};
这些设备信息会被上面说到的usb bus 来匹配对应的驱动,只有这里的信息跟usb设备驱动那边收集到的设备信息匹配上,才会调用进这个驱动.
目前已有的id_table:
staticconststructusb_device_idusbtouch_devices[]={#ifdefCONFIG_TOUCHSCREEN_USB_EGALAX/*ignoretheHIDcapabledevices,handledbyusbhid*/{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 设置为枚举值:
/*devicetypes*/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.
[objc]view plaincopyprint? staticintusbtouch_probe(structusb_interface*intf,conststructusb_device_id*id){structusbtouch_usb*usbtouch;//usbtouch设备structinput_dev*input_dev;//输入设备structusb_endpoint_descriptor*endpoint;//usb的端点structusb_device*udev=interface_to_usbdev(intf);//从usb接口获取对应的设备structusbtouch_device_info*type;//这个就是上面说的真正的driverinfo了endpoint=usbtouch_get_input_endpoint(intf->cur_altsetting);//获取端点if(!endpoint)return-ENXIO;usbtouch=kzalloc(sizeof(structusbtouch_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的概念可参考usbdriverif(!usbtouch->irq){dbg("%s-usb_alloc_urbfailed:usbtouch->irq",__func__);gotoout_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);elseusb_fill_bulk_urb(usbtouch->irq,udev,usb_rcvbulkpipe(udev,endpoint->bEndpointAddress),usbtouch->data,type->rept_size,usbtouch_irq,usbtouch);//初始化urb的回调函数为usbtouch_irqusbtouch->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数组的第几元素.
[objc]view plaincopyprint? structusbtouch_device_info{intmin_xc,max_xc;intmin_yc,max_yc;intmin_press,max_press;intrept_size;/**AlwaysservicetheUSBdevicesirqnotjustwhentheinputdeviceis*open.Thisisusefulwhendeviceshaveawatchdogwhichpreventsus*fromperiodicallypollingthedevice.Leavethisunsetunlessyour*touchscreendevicerequiresit,asitdoesconsumemoreoftheUSB*bandwidth.*/boolirq_always;void(*process_pkt)(structusbtouch_usb*usbtouch,unsignedcharchar*pkt,intlen);//这个函数指针是用来接收处理中断的。/**usedtogetthepacketlen.possiblereturnvalues:*>0:packetlen*=0:skiponebyte*<0:-returnvaluemorebytesneeded*/int(*get_pkt_len)(unsignedcharchar*pkt,intlen);int(*read_data)(structusbtouch_usb*usbtouch,unsignedcharchar*pkt);int(*alloc)(structusbtouch_usb*usbtouch);int(*init)(structusbtouch_usb*usbtouch);void(*exit)(structusbtouch_usb*usbtouch);};
usbtouch_dev_info数组:[objc]view plaincopyprint? staticstructusbtouch_device_infousbtouch_dev_info[]={#ifdefCONFIG_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...#ifdefCONFIG_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中初始化,看下函数主要实现:
staticvoidusbtouch_irq(structurb*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的驱动部分就分析到这里。