linux usb触摸屏设备实现指导

USB Touchscreen 属于一种HID设备,linux 2.6.31 目前已经支持多种USB touchscreen设备,source code位于/kernel/driver/input/ 的usbtouchscreen.c

增加新的设备需要更改usbtouchscreen.c

/******************************************************************************/

/* device types */
enum {

.....
DEVTYPE_ID,
};
/*****************************************************************************/

/* usb devices id */

static struct usb_device_id usbtouch_devices[] = {

.....
{USB_DEVICE(VID, PID), .driver_info = DEVTYPE_ID},
}

/****************************************************************************/

XXX_read_data()

{// read data from hid touchscreen

dev->x = xxx; //x data;

dev->y = yyy; //y data;

.....

}

/**************************************************************************/

/*the different device descriptors*/
static struct usbtouch_device_info usbtouch_dev_info[] = {

........

[DEVTYPE_ID] = {
.min_xc = 0x0,
.max_xc = 0xxxxx,
.min_yc = 0x0,
.max_yc = 0xxxxx,
.rept_size = xx,
.process_pkt = xxxxxxx,
.get_pkt_len = xxxxxxx,
.read_data = xxxx_read_data,
},

}

/****************************************************************************/

然后把其编译成module加载内核,make 模板

ifneq ($(KERNELRELEASE),)
obj-m := xxx.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.mod.* *.o *.ko .*.ko.* .tmp* .*.mod.o.* .*.o.*
其中xxx是源文件的文件名,在linux下直接执行make就可以生成驱动模块(xxx.ko)了。生成驱动模块后使用insmod xxx.ko就可以插入到内核中运行了,用lsmod可以看到你插入到内核中的模块,也可以从系统中用命令rmmod xxx把模块卸载掉;如果把编译出来的驱动模块拷贝到/lib/modules/~/kernel/drivers/usb/下,然后depmod一下,那么你在插入USB设备的时候,系统就会自动为你加载驱动模块的;当然这个得有hotplug的支持;加载驱动模块成功后就会在/dev/下生成设备文件了,如果用命令cat /proc/bus/usb/devices,我们可以看到驱动程序已经绑定到接口上了:
T: Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
D: Ver= 1.10 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
P: Vendor=xxx ProdID=xxx Rev= 1.10
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 0mA
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=usbtouchscreen_driver /*我们的驱动*/
E: Ad=01(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms
E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms
此框架程序生成的是skel0(可以自由修改)的设备文件,现在就可以对这个设备文件进行打开、读写、关闭等的操作了。

注释:linux kernel内hid_core 同样会识别HID设备,并使用usbhid驱动,故需要更改hid_core.c中的ignore_list,将VID PID加入list,内核的usbhid驱动将忽略此VID PID 的设备,而被用户usbtouchscreen驱动识别。

 

 

######################################################################################

Linux usbtouchscreen驱动分析

在Linux内核中自带USB触摸屏驱动,以linux-2.6.33.3\drivers\input\touchscreen.c为例,进行解析:

1.驱动加载:

static int __init usbtouch_init(void)
{
return usb_register(&usbtouch_driver); //驱动注册
}

其中usbtouch_driver定义为:

static struct usb_driver usbtouch_driver = {
.name = "usbtouchscreen", //驱动名称
.probe = usbtouch_probe, //probe 函数
.disconnect = usbtouch_disconnect, //disconnect时调用的函数
.id_table = usbtouch_devices, //usbtouch 支持的设备列表
};

static 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},

/* normal device IDs */
{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},

....

0x0123, 0x0001分别为VID和PID,driver_info 包含驱动信息,其类型为usbtouch_device_info。

例如DEVTYPE_EGALAX]定义为:

static struct usbtouch_device_info usbtouch_dev_info[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
[DEVTYPE_EGALAX] = {
.min_xc = 0x0, //X轴最小坐标
.max_xc = 0x07ff, //X轴最大坐标
.min_yc = 0x0, //Y轴最小坐标
.max_yc = 0x07ff, //Y轴最大坐标
.rept_size = 16, //
.process_pkt = usbtouch_process_multi, //用于中断回调函数,用于上传数据
.get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data, //用于中断回调函数,用于读取数据
},
#endif

所以在注册的时候,就注入了设备的信息,如果注册成功(usb_device_id中包含对应的硬件),就会调用usbtouch_probe方法。

2.probe方法:

static int usbtouch_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usbtouch_usb *usbtouch; //a usbtouch device
struct input_dev *input_dev; //a input device
struct usb_host_interface *interface;
/* usb 设备有一个configuration 的概念,表示配置,一个设备可以有多个配置,
但只能同时激活一个,如:一些设备可以下载固件,或可以设置不同的全局模式,
cur_altsetting 就是表示的当前的这个setting,或者说设置。可以查看原码中
usb_interface 结构定义的说明部分。从说明中可以看到一个接口可以有多种setting*/
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf); //获取接口对应的设备

/*struct usb_device 中有一个成员struct usb_device_descriptor,而struct usb_device_descriptor 中的成员__u16 bcdDevice,表示的是制造商指定的产品的版本号,制造商id 和产品id 来标志一个设备.bcdDevice 一共16 位,是以bcd码的方式保存的信息,也就是说,每4 位代表一个十进制的数,比如0011 0110 1001 0111 就代表的3697.*/


struct usbtouch_device_info *type;
int err = -ENOMEM;

/* some devices are ignored */
if (id->driver_info == DEVTYPE_IGNORE)
return -ENODEV;

interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
//端点0描述符,此处的0表示控制端点

usbtouch = kzalloc(sizeof(struct usbtouch_usb), GFP_KERNEL); //为设备分配内存
input_dev = input_allocate_device(); //申请input_dev结构
if (!usbtouch || !input_dev)
goto out_free;

type = &usbtouch_dev_info[id->driver_info]; //提取设备对应的Info
usbtouch->type = type;
if (!type->process_pkt)
type->process_pkt = usbtouch_process_pkt; //用于中断回调函数,上传数据到应用层

usbtouch->data = usb_buffer_alloc(udev, type->rept_size,
GFP_KERNEL, &usbtouch->data_dma);

/*usbtouch->data:记录了用于普通传输用的内存指针*/

/*usb_buffer_alloc(),这个函数是usbcore提供的,我们只管调用即可.从名字上就能知道它是用来申请内存的,第一个参数就是struct usb_device结构体的指针,所以我们要传递一个udev,第三个参数,GFP_KERNEL,是一个内存申请的flag,通常内存申请都用这个flag,除非是中断上下文,不能睡眠,那就得用GPF_ATOMIC,这里没那么多要求.第二个参数申请的buffer的大小,第四个参数,这个数据类型是Linux内核中专门为dma传输而准备的.为了支持dma传输,usb_buffer_alloc不仅仅是申请了地址,并且建立了dma映射.usb_buffer_alloc申请的内存空间需要用它的搭档usb_buffer_free()来释放.*/
if (!usbtouch->data)
goto out_free;

if (type->get_pkt_len) {
usbtouch->buffer = kmalloc(type->rept_size, GFP_KERNEL);

/*usbtouch->buffer:记录了用于存储读取到的数据的内存指针*/


if (!usbtouch->buffer)
goto out_free_buffers;
}

usbtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!usbtouch->irq) {
dbg("%s - usb_alloc_urb failed: usbtouch->irq", __func__);
goto out_free_buffers;
}

usbtouch->udev = udev;
usbtouch->input = input_dev;

if (udev->manufacturer)
strlcpy(usbtouch->name, udev->manufacturer, sizeof(usbtouch->name));

if (udev->product) {
if (udev->manufacturer)
strlcat(usbtouch->name, " ", sizeof(usbtouch->name));
strlcat(usbtouch->name, udev->product, sizeof(usbtouch->name));
}

if (!strlen(usbtouch->name))
snprintf(usbtouch->name, sizeof(usbtouch->name),
"USB Touchscreen %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));

usb_make_path(udev, usbtouch->phys, sizeof(usbtouch->phys));

/*填充设备结构体中的节点名。用来获取 USB 设备在 Sysfs 中的路径,格式为:usb-usb 总线号-路径名。*/
strlcat(usbtouch->phys, "/input0", sizeof(usbtouch->phys));

/* 将设备的名称赋给设备内嵌的输入子系统结构体 */

input_dev->name = usbtouch->name;

/* 将设备的设备节点名赋给设备内嵌的输入子系统结构体 */
input_dev->phys = usbtouch->phys;

/*
* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号,这个函数是将设备描述符
* 中的编号赋给内嵌的输入子系统结构体
*/
usb_to_input_id(udev, &input_dev->id);


input_dev->dev.parent = &intf->dev;

input_set_drvdata(input_dev, usbtouch);

/* 填充输入设备打开函数指针 */

input_dev->open = usbtouch_open;

/* 填充输入设备关闭函数指针 */
input_dev->close = usbtouch_close;

/* evbit 用来描述事件,EV_KEY 是按键事件,EV_ABS是绝对坐标事件,EV_REL 是相对坐标事件 */
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

/* keybit 表示键值 */
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 (type->max_press)
input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
type->max_press, 0, 0);

/*
* 填充构建 urb,将刚才填充好的input_dev结构体的数据填充进urb 结构体中,在 open 中递交urb。
* 当 urb 包含一个即将传输的 DMA 缓冲区时应该设置 URB_NO_TRANSFER_DMA_MAP。USB核心使用
* transfer_dma变量所指向的缓冲区,而不是transfer_buffer变量所指向的。
* URB_NO_SETUP_DMA_MAP 用于 Setup 包,URB_NO_TRANSFER_DMA_MAP 用于所有 Data 包。
*/

usb_fill_int_urb(usbtouch->irq, usbtouch->udev,
usb_rcvintpipe(usbtouch->udev, endpoint->bEndpointAddress),
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch, endpoint->bInterval);

usbtouch->irq->dev = usbtouch->udev;
usbtouch->irq->transfer_dma = usbtouch->data_dma;
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

/* device specific init */
if (type->init) {
err = type->init(usbtouch);
if (err) {
dbg("%s - type->init() failed, err: %d", __func__, err);
goto out_free_buffers;
}
}

err = input_register_device(usbtouch->input);
if (err) {
dbg("%s - input_register_device failed, err: %d", __func__, err);
goto out_free_buffers;
}

/*
* 一般在 probe 函数中,都需要将设备相关信息保存在一个 usb_interface 结构体中,以便以后通过
* usb_get_intfdata 获取使用。这里设备结构体信息将保存在 intf 接口结构体内嵌的设备结构体中
* 的 driver_data 数据成员中,即 intf->dev->dirver_data = ...。
*/
usb_set_intfdata(intf, usbtouch);

if (usbtouch->type->irq_always)
usb_submit_urb(usbtouch->irq, GFP_KERNEL);

return 0;

out_free_buffers:
usbtouch_free_buffers(udev, usbtouch);
out_free:
input_free_device(input_dev);
kfree(usbtouch);
return err;
}

3.回调函数:

/*
* urb 回调函数,在完成提交 urb 后,urb 回调函数将被调用。
* 此函数作为 usb_fill_int_urb 函数的形参,为构建的 urb 制定的回调函数。
*/
static void usbtouch_irq(struct urb *urb)
{
struct usbtouch_usb *usbtouch = urb->context;
int retval;

switch (urb->status) {
case 0:
/* success */
break;
case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__func__);
return;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d",
__func__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d",
__func__, urb->status);
goto exit;
}

usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);

exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err("%s - usb_submit_urb failed with result: %d",
__func__, retval);
}

 

4. input_sync

用于事件同步,它告知事件的接收者驱动已经发出了一个完整的报告

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统中,实现触摸屏的滑动需要通过以下步骤: 1. 打开触摸屏设备:首先,需要打开触摸屏设备文件,通常在`/dev/input/eventX`路径下,其中X为设备号。可以使用`open()`函数打开设备文件,并进行错误检查。 2. 读取输入事件:使用`read()`函数从触摸屏设备文件中读取输入事件。输入事件是一个结构体,包含了触摸屏的各种信息,如触摸点的坐标、事件类型等。 3. 解析输入事件:根据读取到的输入事件,解析出触摸点的坐标和事件类型。通常,滑动操作会涉及到按下、移动和释放三种事件类型。 4. 处理滑动操作:根据解析到的事件类型和坐标信息,进行相应的滑动操作处理。可以使用算法来计算滑动的方向和距离,并根据需要执行相应的操作。 5. 关闭触摸屏设备:在程序结束时,需要关闭触摸屏设备文件,释放资源。使用`close()`函数关闭设备文件。 下面是一个简单的示例代码,演示了如何实现触摸屏的滑动功能: ```cpp #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <linux/input.h> int main() { int fd; struct input_event ev; // 打开触摸屏设备文件 fd = open("/dev/input/eventX", O_RDONLY); if (fd == -1) { perror("Failed to open touch screen device"); return -1; } while (1) { // 读取输入事件 if (read(fd, &ev, sizeof(struct input_event)) == -1) { perror("Failed to read touch screen event"); break; } // 解析输入事件 if (ev.type == EV_ABS && ev.code == ABS_X) { // 处理X轴坐标变化 // ... } else if (ev.type == EV_ABS && ev.code == ABS_Y) { // 处理Y轴坐标变化 // ... } else if (ev.type == EV_KEY && ev.code == BTN_TOUCH) { if (ev.value == 1) { // 处理按下事件 // ... } else if (ev.value == 0) { // 处理释放事件 // ... } } } // 关闭触摸屏设备文件 close(fd); return 0; } ``` 请注意,以上代码只是一个简单示例,实际的实现可能会更加复杂,需要根据具体的触摸屏设备和需求进行适当的调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值