1、概述

Android 和PC系统本身是支持 usb hid输入设备的。不过由于业务的发展,需要采用高精度触摸框。重新设计框架,改变原来   串口+usb_hid的方式。采用俩路usb,一路usb_buik+一路usb_hid方式。具体架构如下图:

2、触摸驱动

该驱动是基于Android 8.0 内核 4.9版本上调试的。驱动主要从俩个方面去分析:1) USB 驱动框架  2)input 驱动

2.1 USB驱动框架

USB驱动基于Linux USB总线完成的。主要注册USB 设备和填充USB设备结构体相关信息。

static int __init usb_touch_init(void)
{int retval = usb_register(&XX_driver); //注册usb driver到系统中printk(KERN_ALERT "usb touch init\r\n");if(retval)printk(KERN_ALERT "usb touch init error %d\r\n",retval);return retval;
}static void __exit usb_touch_exit(void)
{printk(KERN_ALERT "usb touch exit");usb_deregister(&XX_driver);
}module_init(usb_touch_init);
module_exit(usb_touch_exit);

Linux 内核驱动都是从init函数开始执行,相当于应用程序里面的main函数。在init函数中主要做了usb_register(),注册usb driver到

系统中。usb_register() 参数为struct usb_driver 结构。

static struct usb_driver XX_driver = {.name             =  "XXXXXX", //usb driver name.probe             =  XX_probe, //usb 匹配后执行probe.disconnect           =  XX_disconnect, //usb 设备断开.id_table              =  XX_id_table,   // 用于usb 匹配信息
};

在usb_driver结构中重要的几个参数为 .probe 该函数用于usb 设备匹配正确后执行的。匹配规则后面会讲到 ,.disconnect该函数用于usb设备断开后执行的,主要做一些资源的释放。id_table 用于usb设备匹配信息,下面会讲。.name 用于usb driver name

static struct usb_device_id XX_id_table [] ={{.match_flags =  USB_DEVICE_ID_MATCH_VENDOR,//匹配规则,根据vid pid 去匹配设备.idVendor = XXXX,.idProduct = XXXXX,},{.match_flags =  USB_DEVICE_ID_MATCH_VENDOR, //可以匹配多个usb设备.idVendor = XXXXX,.idProduct = XXXXX,},//{ USB_DEVICE(USB_TOUCH_VENDOR_ID, USB_TOUCH_PRODUCT_ID) },{}
};MODULE_DEVICE_TABLE (usb, XX_id_table );

usb _device_id 写明usb 设备与驱动匹配的规则,这里采用的 是根据vid pid去匹配。每个usb设备的pid vid都是唯一的,当然也可以根据class Subclass (设备描述符)去匹配usb 设备.同时一个usb 驱动可以匹配多个usb 设备。MODULE_DEVICE_TABLE 宏将你写好的匹配规则带入到系统中.

注意 usb_device_id XX_id_table 要与 usb_driver 中的.id_table 关联起来,系统才可以识别相关的usb 设备。

static int usb_touch_probe(struct usb_interface *intf,const struct usb_device_id *id)
{//获取usb_device 通过结构体成员指针获取结构体首地址struct usb_device *dev = interface_to_usbdev(intf);struct usb_host_interface *interface;struct usb_endpoint_descriptor *endpoint_in=NULL,*endpoint_out;//将接口描述符赋为当前接口interface = intf->cur_altsetting;//获取端点个数endpoint_num=interface->desc.bNumEndpoints;//申请touch 结构体空间touch = kzalloc(sizeof(struct usb_touch), GFP_KERNEL);//查找端点的属性,In端点还是Out端点for(i=0;i<interface->desc.bNumEndpoints;i++){if(!touch->input_ep &&  usb_endpoint_dir_in(&interface->endpoint[i].desc)){endpoint_in=&interface->endpoint[i].desc;touch->input_ep = interface->endpoint[i].desc.bEndpointAddress;maxp= usb_maxpacket(dev, touch->input_ep, usb_pipeout(touch->input_ep));printk(KERN_ALERT "In   bDescriptorType -> %x bEndpointAddress -> %x                                                        bmAttributes -> %x   wMaxPacketSize  ->%x\r\n",endpoint_in->bDescriptorType,endpoint_in->bEndpointAddress,endpoint_in->bmAttributes,endpoint_in->wMaxPacketSize);}if(!touch->output_ep &&  usb_endpoint_dir_out(&interface->endpoint[i].desc)){endpoint_out=&interface->endpoint[i].desc;touch->output_ep = interface->endpoint[i].desc.bEndpointAddress;maxp= usb_maxpacket(dev, touch->output_ep, usb_pipeout(touch->output_ep));printk(KERN_ALERT "Out  bDescriptorType -> %x bEndpointAddress -> %x  bmAttributes -> %x   wMaxPacketSize  ->%x\r\n",endpoint_out->bDescriptorType,endpoint_out->bEndpointAddress,endpoint_out->bmAttributes,endpoint_out->wMaxPacketSize);}}//申请data空间和buff空间touch->data = usb_alloc_coherent(dev,512*20,GFP_KERNEL,&touch->data_dma);touch->buff = kmalloc(512*20, GFP_ATOMIC);if(!touch->data || !touch->buff)goto fail1;/* * 为 urb 结构体申请内存空间,第一个参数表示等时传输时需要传送包的数量,其它传输方式则为0。 * 申请的内存将通过下面即将见到的 usb_fill_int_urb 函数进行填充。  */ touch->irq = usb_alloc_urb(0,GFP_KERNEL);if(!touch->irq)goto fail2;/* 填充 usb 设备结构体和输入设备结构体 */  touch->usbdev=dev;touch->usb_touch_input_dev=input_dev;/* 获取Usb_hid设备的名称 */printk(KERN_ALERT "usb touch probe manufacturer %s\r\n",dev->manufacturer);  if(dev->manufacturer)strlcpy(touch->name, dev->manufacturer, sizeof(touch->name));printk(KERN_ALERT "usb touch probe touch->name %s\r\n",touch->name);printk(KERN_ALERT "usb touch probe product %s\r\n",dev->product);    if (dev->product) {if (dev->manufacturer)strlcat(touch->name, " ", sizeof(touch->name));strlcat(touch->name, dev->product, sizeof(touch->name));printk(KERN_ALERT "usb touch probe touch->name22 %s\r\n",touch->name);}if (!strlen(touch->name))snprintf(touch->name, sizeof(touch->name),"XXXX touch %04x:%04x",le16_to_cpu(dev->descriptor.idVendor),le16_to_cpu(dev->descriptor.idProduct));printk(KERN_ALERT "usb touch probe touch->name333 %s\r\n",touch->name);//初始化一个buik_urb块用于异步传输数据usb_fill_bulk_urb(touch->irq, dev, usb_rcvbulkpipe(dev, touch->input_ep), touch->data,512*20,usb_touch_irq, touch);touch->irq->transfer_dma = touch->data_dma;//dma数据缓冲区指向设备的data_dma成员touch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;// DMA 有效//提交urb到系统中retval = usb_submit_urb(touch->irq,GFP_KERNEL);if (retval) {printk(KERN_ALERT "retval %d\r\n",retval);}
}

在probe函数主要是一些初始化,首先通过interface_to_usbdev函数获取到usb_dev.这种手法在内核中很常用。申请相关的结构体的空间,判断usb设备端点的属性和初始化一个urb用于传输数据。在初始化urb时 usb_fill_bulk_urb() 第六个参数是一个回调函数,当有数据是系统会调用这个函数。类似于中断函数。第五个参数是参数数据的buff。

static void usb_touch_irq(struct urb* urb)
{//根据上下文可获取到usb_touch 结构体 urb的私有成员。不过在这里设置usb_touch为全局变量,//struct usb_touch *touch = urb->context;int status=0;//判断urb 块传输数据的状态switch (urb->status) {case 0: /*success */break;case -ECONNRESET: /* unlink */case -ENOENT:case -ESHUTDOWN:printk(KERN_ALERT "ESHUTDOWN\r\n");return;/* -EPIPE:  should clear the halt */default:       /* error */goto resubmit;}//接收数据的长度urb->actual_length;//touch->data 接收的数据。//urb 状态未知时,再次提交urb到系统中
resubmit:status = usb_submit_urb (urb, GFP_ATOMIC);if (status)printk(KERN_ALERT "can't resubmit intr, %s-%s/input0, status %d",touch->usbdev->bus->bus_name,touch->usbdev->devpath, status);
} 

在irq里判断urb的状态,然后就可以读到数据和数据的长度。

static void usb_touch_disconnect(struct usb_interface *intf)
{struct usb_touch *touch = usb_get_intfdata (intf);usb_set_intfdata(intf, NULL);if (touch) {usb_kill_urb(touch->irq);usb_free_urb(touch->irq);usb_free_coherent(interface_to_usbdev(intf), 512, touch->data, touch->data_dma);kfree(touch);}
}

在disconnect 中释放之前申请的资源。

2.2 input 部分

Input部分相对来讲会简单一些,在probe函数里面做相关初始化,在disconnect里面释放相关资源。然后再irq函数中拿到数据后上报数据给系统即可。Android系统本事的input子系统会识别相关的event 事件,触摸正常。

static int usb_touch_probe(struct usb_interface *intf,const struct usb_device_id *id)
{struct input_dev *input_dev;input_dev = input_allocate_device();//为input设备申请空间/* * 填充usb设备结构体中的节点名。usb_make_path 用来获取 USB 设备在 Sysfs 中的路径,格式 * 为:usb-usb 总线号-路径名。 */ usb_make_path(dev, touch->phys, sizeof(touch->phys));input_dev->name = touch->name;input_dev->phys=touch->phys;usb_to_input_id(dev, &input_dev->id);//设置输入设备的bustype,vendor,product,versioninput_dev->dev.parent = &intf->dev;//usb接口设备为输入设备的父设备//设备支持X Y ID 事件//支持同步事件set_bit(EV_SYN, input_dev->evbit);//设置绝对坐标,触摸屏采用绝对坐标,鼠标采用相对坐标set_bit(EV_ABS, input_dev->evbit);//设置点击事件set_bit(BTN_TOUCH, input_dev->keybit);//设置压力set_bit(ABS_MT_PRESSURE, input_dev->absbit);//点的ID ,多钱根据ID区分set_bit(ABS_MT_TRACKING_ID, input_dev->keybit);//设置X 事件set_bit(ABS_MT_POSITION_X, input_dev->absbit);//设置Y事件set_bit(ABS_MT_POSITION_Y, input_dev->absbit);//设置触摸笔的类型,粗细笔set_bit(ABS_MT_TOOL_TYPE, input_dev->absbit);//设置触摸屏设备set_bit(INPUT_PROP_DIRECT, input_dev->propbit); //设置压力的范围input_set_abs_params(input_dev, ABS_MT_PRESSURE,0,255,0,0);//设置X Y坐标的范围input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 0x7fff, 0, 0);input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 0x7fff, 0, 0);//设置多点的ID个数 10点input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);//设置面积的长宽input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);//设置粗细笔个数input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,0, 3, 0, 0);//关联input_dev与touch设备input_set_drvdata(input_dev, touch);//注册input驱动input_register_device(touch->usb_touch_input_dev);
}

在probe中做相关的初始化,到此input相关已经初始化完成,在irq函数中直接上报函数即可。

for (i = 0; i < pointnum; i++) {if (((data->points[i].mPresstype & 0x07) == 0x07)) {input_report_abs(dev, ABS_MT_TRACKING_ID, data->points[i].mID);input_report_abs(dev, ABS_MT_PRESSURE, 1);input_report_key(dev, BTN_TOUCH,1);input_report_abs(dev, ABS_MT_POSITION_X, data->points[i].mX);input_report_abs(dev, ABS_MT_POSITION_Y, data->points[i].mY);input_report_abs(dev, ABS_MT_TOUCH_MAJOR,max(data-     >points[i].mWidth,data->points[i].mHeight));input_report_abs(dev, ABS_MT_TOUCH_MINOR,min(data->points[i].mWidth,data->points[i].mHeight));input_report_abs(dev, ABS_MT_TOOL_TYPE,data->points[i].mPresstype>>5);   }input_mt_sync(dev);//以一个点的信息为结尾}input_sync(dev);//以一次事件为结尾}

这里只截取上报数据部分,直接上报相关数据即可。需要注意的是多点时,需要先上报每个点的数据,然后用 input_mt_sync()函数同步每个点数据,当多点都上报完以后还需要用input_sync()同步,对于系统来说,多点时同一个事件。

2.3 device_attribute的使用。

device_attribute 主要用于在sys子系统中,用户空间与内核空间的交互,在这里采用device_attribute 将版本信息输出到sys子系统中。用户空间用cat指令即可查看。

   //在sys子系统下Class目录下创建一个XXX目录touch->myclass = class_create(THIS_MODULE, "XXX");if(IS_ERR(touch->myclass)){printk("XXXtouchscreen class_create error\n");return 0;}//在XXX目录下创建一个XXX_touch目录touch->mydevice=device_create(touch->myclass, NULL, touch->chrdev_no, NULL, "XXX_touch");//在XXX_touch目录下创建drvinfo节点,通过读取节点即可获取信息版本device_create_file(touch->mydevice, &drvinfo);//device_attribute  结构体中重要俩个函数 这里只用了.show .attr包括模式和名字
static struct device_attribute drvinfo = {.attr = {.name = "drvinfo",.mode = 0444,},.show = drvinfo_show,
};在drvinfo_show中采用sprintf()直接将信息输出。用户层利用cat就可获取数据信息
static ssize_t drvinfo_show(struct device *dev,struct device_attribute * attr,char * buf)
{return sprintf();
}

3、驱动框架

整体触摸驱动的流程。

4、总结

驱动主要分为俩大部分,USB驱动和INPUT驱动。利用USB通信,采集数据然后通过算法算出数据后,通过INPUT子系统直接上报系统。在Linux 中USB 驱动和INPUT驱动的相关的API内核已经提供,只需按照相应的流程去操作就可以了。USB驱动前期会难一些,你要理解明白USB的枚举过程和四大描述符的作用。这样写起来就会很快。USB的触摸驱动到这里就结束了,后面还会跟新串口触摸驱动。

Android 基于USB_BUIK 触摸驱动相关推荐

  1. 基于rk3288平台的gt9xx 触摸驱动移植

                                              基于rk3288平台的gt9xx 触摸驱动移植 一.硬件介绍:     1 core-rk3288j 核心板     ...

  2. Android虚拟键盘和虚拟触摸驱动

    1.虚拟键盘驱动 文件位置:kernel/drivers/input/keyboard/zebra_key.c #include <linux/module.h> #include < ...

  3. Android 开发之 ---- 底层驱动开发(一) 【转】

    转自:http://blog.csdn.net/jmq_0000/article/details/7372783 版权声明:本文为博主原创文章,未经博主允许不得转载. 驱动概述 说到 Android  ...

  4. Android基于Socket无线遥控 - 模拟触摸按键篇framework jar

    Android基于Socket无线遥控(一)-Socket基本模型搭建 http://233.io/article/1017992.html 本篇主要内容涉及模拟系统按键消息,单击事件,触屏事件等,模 ...

  5. Android 开发之 ---- 底层驱动开发

    说到 android 驱动是离不开Linux驱动的.Android内核采用的是Linux2.6内核(最近Linux 3.3已经包含了一些Android代码).但Android并没有完全照搬Linux系 ...

  6. Android基于IIS的APK下载(五)IIS的配置

    这里使用的IIS是win7_64的. 步骤一:打开IIS.控制面板->管理工具(如果没有,请把查看方式调成大图标)->Internet 信息服务(IIS)管理器. 步骤二:配置网站目录 步 ...

  7. 中柏平板触摸驱动_工业平板电脑触摸屏种类及故障解决办法,赶紧收藏起来

    如需获得更多信息,文末点击"了解更多" 触摸屏又称为触控面板,一般和液晶显示器相结合做成触控显示设备.工业平板电脑是带有触摸显示设备的工控电脑,整机性能完善.区别在于内部的硬件,多 ...

  8. android 添加子view,Android基于Window.ID_ANDROID_CONTENT给定id添加子View

    Android基于Window.ID_ANDROID_CONTENT给定id添加子View 这一技术特点在一些视频播放器中比较有用. 例如代码: package zhangphil.demo; imp ...

  9. ACL 2021 | 基于依存句法驱动注意力图卷积神经网络的关系抽取

    ©作者 | 陈桂敏 来源 | QTrade AI研究中心 QTrade 的 AI研究中心是一支将近 30 人的团队,主要研究方向包括:预训练模型.信息抽取.对话机器人.内容推荐等.本文介绍的是一篇信息 ...

最新文章

  1. 科普丨数据中心、云计算、大数据之间有什么区别和联系?
  2. git -- 练习的笔记
  3. 第K个幸运排列 (51Nod-1635)
  4. 控制只读_用Python控制硬件44-四位半万用表UT61E
  5. FFMPEG结构体分析:AVIOContext
  6. idea导出Oracle表结构和数据
  7. 行政区划编码转换区域名工具类
  8. android中屏幕保护的实现的,Android 屏幕保护程序制做及源码
  9. python中function是什么意思_Python中的Function定义方法
  10. JavaScript 技术篇-js正则表达式匹配中英文数字
  11. 两个向量叉乘表示什么意思_为什么两个空间向量的向量积的摸等于以这两个 – 手机爱问...
  12. VM16-ubuntu16桥接网络频繁掉线
  13. c++无法启动程序,系统找不到指定文件的处理方法
  14. codelite交叉编译动态库学习记录
  15. 前后端开发的心得体会_前后端对接的思考及总结
  16. 三、Bugku----手机热点------流量分析题------obex
  17. 【电路理论】1-10 两类约束 KVL、KCL方程的独立性
  18. 通过Keras + LSTM训练天气污染程度预测模型
  19. 台式计算机搜不到无线,电脑搜不到5g频段wifi解决方法(图文)
  20. 盘点世上最牛的5篇博士论文,跪拜!

热门文章

  1. 品牌c602 芯片组服务器,驱动天空 - 品牌主板 - 服务器主板 SERVER
  2. linux ls和 ll 命令
  3. Metro UI CSS 的简介
  4. 使Windows 7任务栏更像Windows XP或Vista
  5. Revit插件快速提高建模的效率,分别都有那种功能revit插件
  6. html设置图片切割,JavaScript html js图片切割系统
  7. 程序员用 Python 破解同事的加密压缩包!不小心知道了……
  8. 论文阅读Generalizing A Person Retrieval Model Hetero-and Homogeneously
  9. es6知识总结--3
  10. GPS车辆监控系统中的电子围栏