转自:http://blog.chinaunix.net/uid-29507718-id-4314013.html

分类: LINUX

linux下触摸屏驱动的移植主要包括这几个步骤:

(1)确定触摸屏IC接口,了解对应接口的API函数,注册设备并加入到相应总线上

(2)关联设备与驱动,并编写具体的驱动代码

(3)熟悉linux 输入设备驱动,在驱动代码中分配一个输入设备并初始化相应数据结构,在驱动实现中引用

这里对应上面几部分,分析I2C接口触摸屏驱动的实现。先介绍linux下I2C接口与输入子系统架构,然后基于FT5x0x源码逐层展开整个移植过程的实现。

一、I2C驱动架构

linux I2C驱动架构

具体的实现可分为四个层次:

1、提供adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层。主要数据结构struct i2c_adapter

  1. struct i2c_adapter {
  2. struct module *owner;
  3. unsigned int id;
  4. unsigned int class;
  5. struct i2c_algorithm *algo;/* the algorithm to access the bus   */
  6. void *algo_data;
  7. /* --- administration stuff. */
  8. int (*client_register)(struct i2c_client *);
  9. int (*client_unregister)(struct i2c_client *);
  10. /* data fields that are valid for all devices   */
  11. struct mutex bus_lock;
  12. struct mutex clist_lock;
  13. int timeout;
  14. int retries;
  15. struct device dev;      /* the adapter device */
  16. struct class_device class_dev;  /* the class device */
  17. int nr;
  18. struct list_head clients;
  19. struct list_head list;
  20. char name[I2C_NAME_SIZE];
  21. struct completion dev_released;
  22. struct completion class_dev_released;
  23. };

这个结构体对应一个控制器。其中包含了控制器名称,algorithm数据,控制器设备等。

2、提供adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层。主要数据结构struct
i2c_algorithm

  1. struct i2c_algorithm {
  2. int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,
  3. int num);
  4. int (*slave_send)(struct i2c_adapter *,char*,int);
  5. int (*slave_recv)(struct i2c_adapter *,char*,int);
  6. u32 (*functionality) (struct i2c_adapter *);
  7. };

这个结构体中定义了一套控制器使用的通信方法。其中关键函数是master_xfer()。实际工作中很重要一点就是要实现这个函数。

3、实现i2c设备驱动中的i2c_driver接口,由结构i2c_client中的数据填充,覆盖图中的driver驱动层。主要数据结构

  1. struct i2c_client {
  2. unsigned int flags;     /* div., see below      */
  3. unsigned short addr;        /* chip address - NOTE: 7bit    */
  4. /* addresses are stored in the  */
  5. /* _LOWER_ 7 bits       */
  6. struct i2c_adapter *adapter;    /* the adapter we sit on    */
  7. struct i2c_driver *driver;  /* and our access routines  */
  8. int usage_count;        /* How many accesses currently  */
  9. /* to the client        */
  10. struct device dev;      /* the device structure     */
  11. struct list_head list;
  12. char name[I2C_NAME_SIZE];
  13. struct completion released;
  14. };

这个结构体中的内容是描述设备的。包含了芯片地址,设备名称,设备使用的中断号,设备所依附的控制器,设备所依附的驱动等内容。

    4、实现i2c设备所对应的具体device的驱动。覆盖图中的driver驱动层。主要数据结构struct i2c_driver

  1. struct i2c_driver {
  2. int id;
  3. unsigned int class;
  4. int (*attach_adapter)(struct i2c_adapter *);
  5. int (*detach_adapter)(struct i2c_adapter *);
  6. int (*detach_client)(struct i2c_client *);
  7. int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
  8. struct device_driver driver;
  9. struct list_head list;
  10. };

这个结构体对应了驱动方法,重要成员函数有probe,remove,suspend,resume与中断处理函数,也是我们需要是实现的函数。另外包括一个重要的数据结构: struct i2c_device_id *id_table; 如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID。

第一层和第二层是i2c总线驱动,属于芯片内部的驱动,在linux驱动架构中已经实现,不需要我们编写或更改。第三第四属于i2c设备驱动,需要我们根据内核提供的接口去完成具体的代码。

另外就是i2c_core层,起到了承上启下的作用。源代码位于drivers/i2c/i2c-core.c中。在这里可以看到几个重要的函数。

(1)增加/删除i2c控制器的函数

  1. int i2c_add_adapter(struct i2c_adapter *adapter)
  2. int i2c_del_adapter(struct i2c_adapter *adap)

(2)增加/删除i2c设备的函数

  1. struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
  2. void i2c_unregister_device(struct i2c_client *client)

(3)增加/删除设备驱动的函数

  1. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  2. void i2c_del_driver(struct i2c_driver *driver)

(4)I2C传输、发送和接收函数

  1. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
  2. int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
  3. int i2c_master_recv(struct i2c_client *client, char *buf ,int count);

send和receive分别都调用了transfer函数,而transfer也不是直接和硬件交互,而是调用algorithm中的master_xfer()函数。

二、linux input输入子系统架构

linux输入系统框架

linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(Event Handler)、输入子系统核心层(Input Core)和输入子系统设备驱动层。这些层次里的具体机制我们先不涉及,而我们要知道输入子系统里有事件处理层,我们完成驱动时很主要的一点就是为输入设备定义一个符合要求的事件集合。另外,在图中可以看出输入设备不止包括触摸屏,还有很多其他种类的输入设备,如键盘,鼠标等等,所以linux内核已经把各式各样的输入设备进行了抽象,提供了统一的接口,让我们把设备接入到系统中。而我们要做的工作就是申请一个输入设备结构体对象,并填充其里面的数据。

把设备注册进输入子系统里一般要经过以下步骤:

1、申请一个输入设备,定义事件集合。关于输入事件的介绍可以看这里:Linux 内核点触摸接口协议

2、上报从设备中读到的数据。可使用函数

  1. void input_report_key(struct input_dev *dev, unsigned int code, int value);      //上报按键事件
  2. void input_report_rel(struct input_dev *dev, unsigned int code, int value);       //上报相对坐标事件
  3. void input_report_abs(struct input_dev *dev, unsigned int code, int value);       //上报绝对坐标事件

三、基于OMAP FT5x0x驱动源码分析

1、板级初始化代码

板级初始化代码与具体的平台有关,负责相应平台的初始化工作,这里使用的pandaboard平台的代码,在目录arch/arm/mach-omap2/board-omap4panda.c中。触摸屏设备当然需要在这里先声明并注册进系统。具体代码如下:

  1. static struct i2c_board_info __initdata panda_i2c2_boardinfo[] = {
  2. {
  3. I2C_BOARD_INFO("ft5x06_ts", 0x38),
  4. .irq = OMAP_GPIO_IRQ(34),
  5. },
  6. };
  7. omap_register_i2c_bus(2, 100, panda_i2c2_boardinfo,ARRAY_SIZE(panda_i2c2_boardinfo));

声明一个i2c_board_info结构的数组,这个数组包含pandaboard板上i2c接口的信息。先使用宏I2C_BOARD_INFO宏声明设备名为ft5x06_ts,地址为0x38。要说明的是这里的地址并不是指处理器中i2c寄存器的硬件地址,而是在系统加载后,位于/sys/bus/i2c/devices下的设备名。再申请这个i2c接口的中断口为GPIO34,这从板的硬件连接图可以看出。

然后把这个结构体注册进i2c总线,使用omap_register_i2c_bus函数注册。

2、设备与驱动绑定

设备与驱动绑定的代码位于目录drivers/input/touchscreen/ft5x0x.c中,这个文件包含了i2c设备的具体代码,以模块的形式编译进内核。

  1. static const struct i2c_device_id ft5x0x_ts_id[] =
  2. {
  3. { FT5X0X_NAME, 0 },{ }
  4. };
  5. MODULE_DEVICE_TABLE(i2c, ft5x0x_ts_id);
  6. static struct i2c_driver ft5x0x_ts_driver =
  7. {
  8. .probe      = ft5x0x_ts_probe,
  9. .remove     = __devexit_p(ft5x0x_ts_remove),
  10. .id_table   = ft5x0x_ts_id,
  11. .driver =
  12. {
  13. .name   = FT5X0X_NAME,
  14. .owner  = THIS_MODULE,
  15. },
  16. };
  17. static int __init ft5x0x_ts_init(void)
  18. {
  19. return i2c_add_driver(&ft5x0x_ts_driver);
  20. }
  21. static void __exit ft5x0x_ts_exit(void)
  22. {
  23. i2c_del_driver(&ft5x0x_ts_driver);
  24. }
  25. module_init(ft5x0x_ts_init);
  26. module_exit(ft5x0x_ts_exit);
  27. MODULE_AUTHOR("");
  28. MODULE_DESCRIPTION("FocalTech ft5x0x TouchScreen driver");
  29. MODULE_LICENSE("GPL");

首先建立设备与驱动的映射表ft5x0x_ts_id,通过宏MODULE_DEVICE_TABLE关联起来,MODULE_DEVICE_TABLE第一个参数表示这个设备id是i2c总线上了,第二个参数是指设备,当系统找到这个设备时,就会通过FT5X0X_NAME把设备与这个模块关联起来。
     这是个i2c驱动模块,当然需要定义一个i2c_driver结构体来标识这个驱动,并说明实现的驱动函数有probe()和remove()函数。
     之后就是添加模块初始化和退出函数,分别是在模块初始化时调用i2c_add_driver()加入i2c驱动,在模块退出时调用i2c_del_driver删除i2c驱动

3、设备驱动程序实现
     从上面i2c_driver结构可以看出,ft5x0x实现了驱动的两个接口函数,分别为probe()和remove()函数。
     probe()具体实现函数为ft5x0x_ts_probe(),这个函数在内核初始化时,如果设备和驱动匹配,将调用,实现i2c设备的初始化,具体实现:

  1. static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
  2. {
  3. struct ft5x0x_ts_data *ft5x0x_ts;
  4. struct input_dev *input_dev;
  5. int err = 0;
  6. int rev_id = 0;
  7. if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
  8. err = -ENODEV;
  9. goto err_out;
  10. }
  11. ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);
  12. if (!ft5x0x_ts) {
  13. err = -ENOMEM;
  14. goto err_free_mem;
  15. }
  16. this_client = client;
  17. i2c_set_clientdata(client, ft5x0x_ts);
  18. rev_id = i2c_smbus_read_byte_data(client, FT5X0X_REG_FT5201ID);
  19. if (rev_id != FT5X06_ID)
  20. {
  21. err = -ENODEV;
  22. dev_err(&client->dev, "failed to probe FT5X0X touchscreen device\n");
  23. goto err_free_mem;
  24. }
  25. INIT_WORK(&ft5x0x_ts->pen_event_work, ft5x0x_ts_pen_irq_work);
  26. ft5x0x_ts->ts_workqueue = create_singlethread_workqueue(dev_name(&client->dev));
  27. if (!ft5x0x_ts->ts_workqueue)
  28. {
  29. err = -ESRCH;
  30. goto err_free_thread;
  31. }
  32. err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, "ft5x0x_ts", ft5x0x_ts);
  33. if (err < 0)
  34. {
  35. dev_err(&client->dev, "request irq failed\n");
  36. goto err_free_irq;
  37. }
  38. input_dev = input_allocate_device();
  39. if (!input_dev)
  40. {
  41. err = -ENOMEM;
  42. dev_err(&client->dev, "failed to allocate input device\n");
  43. goto err_free_input;
  44. }
  45. ft5x0x_ts->input_dev = input_dev;
  46. #ifdef CONFIG_FT5X0X_MULTITOUCH
  47. set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
  48. set_bit(ABS_MT_POSITION_X, input_dev->absbit);
  49. set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
  50. set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
  51. set_bit(ABS_PRESSURE, input_dev->absbit);
  52. input_set_abs_params(input_dev,
  53. ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
  54. input_set_abs_params(input_dev,
  55. ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
  56. input_set_abs_params(input_dev,
  57. ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
  58. input_set_abs_params(input_dev,
  59. ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
  60. #else
  61. set_bit(ABS_X, input_dev->absbit);
  62. set_bit(ABS_Y, input_dev->absbit);
  63. set_bit(ABS_PRESSURE, input_dev->absbit);
  64. set_bit(BTN_TOUCH, input_dev->keybit);
  65. input_set_abs_params(input_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
  66. input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
  67. input_set_abs_params(input_dev, ABS_PRESSURE, 0, PRESS_MAX, 0 , 0);
  68. #endif
  69. set_bit(EV_ABS, input_dev->evbit);
  70. set_bit(EV_KEY, input_dev->evbit);
  71. input_dev->name  = FT5X0X_NAME;
  72. err = input_register_device(input_dev);
  73. if (err)
  74. {
  75. dev_err(&client->dev, "failed to register input device: %s\n",
  76. dev_name(&client->dev));
  77. goto err_free_input;
  78. }
  79. #ifdef CONFIG_HAS_EARLYSUSPEND
  80. ft5x0x_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
  81. ft5x0x_ts->early_suspend.suspend = ft5x0x_ts_suspend;
  82. ft5x0x_ts->early_suspend.resume  = ft5x0x_ts_resume;
  83. register_early_suspend(&ft5x0x_ts->early_suspend);
  84. #endif
  85. return 0;
  86. err_free_input:
  87. input_free_device(input_dev);
  88. err_free_irq:
  89. free_irq(client->irq, ft5x0x_ts);
  90. err_free_thread:
  91. cancel_work_sync(&ft5x0x_ts->pen_event_work);
  92. destroy_workqueue(ft5x0x_ts->ts_workqueue);
  93. i2c_set_clientdata(client, NULL);
  94. err_free_mem:
  95. kfree(ft5x0x_ts);
  96. err_out:
  97. return err;
  98. }

这个函数代码比较长,但是可以分部分去看,每一部分对应不同的初始化工作.大概可以分为这几个部分:

(1)检测适配器是否支持I2C_FUNC_I2C的通信方式 :i2c_check_functionality(client->adapter, I2C_FUNC_I2C)

(2)申请内存存储驱动的数据:ft5x0x_ts = kzalloc(sizeof(*ft5x0x_ts), GFP_KERNEL);并把驱动数据赋值给系统传过来的i2c_client数据:this_client = client; i2c_set_clientdata(client, ft5x0x_ts);

(3)验证将要通信的设备ID:rev_id = i2c_smbus_read_byte_data(client, FT5X0X_REG_FT5201ID);

(4)创建触摸事件的工作队列并初始化工作队列的处理函数:INIT_WORK(&ft5x0x_ts->pen_event_work,
ft5x0x_ts_pen_irq_work); ft5x0x_ts->ts_workqueue =
create_singlethread_workqueue(dev_name(&client->dev));

(5)申请系统中断并声明中断处理函数;request_irq(client->irq, ft5x0x_ts_interrupt,
IRQF_DISABLED | IRQF_TRIGGER_RISING, "ft5x0x_ts", ft5x0x_ts);

(6)分配一个输入设备实例,初始化数据并注册进系统:input_dev = input_allocate_device(); input_register_device(input_dev);

(7)如果定义了earlysuspend,声明其处理函数。earlysuspend用于对触摸屏类设备的电源管理,降低功耗。

(8)最后对执行过程中可能出现的错误进行处理。

当有触摸动作时,触摸屏将会执行(5)中声明的中断处理函数ft5x0x_ts_interrupt,具体实现:

  1. static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id)
  2. {
  3. struct ft5x0x_ts_data *ft5x0x_ts = dev_id;
  4. if (!work_pending(&ft5x0x_ts->pen_event_work))
  5. queue_work(ft5x0x_ts->ts_workqueue, &ft5x0x_ts->pen_event_work);
  6. return IRQ_HANDLED;
  7. }

可以看到,中断处理就是在判断工作队列在没有被挂起的情况下在触摸时间加入到工作队列中去,等待工作队列处理函数ft5x0x_ts_pen_irq_work()的处理。ft5x0x_ts_pen_irq_work()的实现:

  1. static void ft5x0x_ts_pen_irq_work(struct work_struct *work)
  2. {
  3. int ret = -1;
  4. ret = ft5x0x_read_data();
  5. if (ret == 0)
  6. ft5x0x_report_value();
  7. }

概括来说这里只做了两件事:从设备中读取数据 ;把数据上报到输入子系统中。

(1)从设备中读取数据由函数ft5x0x_read_data()实现

  1. static int ft5x0x_read_data(void)
  2. {
  3. struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
  4. struct ts_event *event = &data->event;
  5. u8 buf[32] = {0};
  6. int ret = -1;
  7. int status = 0;
  8. #ifdef CONFIG_FT5X0X_MULTITOUCH
  9. ret = ft5x0x_i2c_rxdata(buf, 31);
  10. #else
  11. ret = ft5x0x_i2c_rxdata(buf, 7);
  12. #endif
  13. if (ret < 0)
  14. {
  15. printk("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
  16. return ret;
  17. }
  18. memset(event, 0, sizeof(struct ts_event));
  19. event->touch_point = buf[2] & 0x07;
  20. if (event->touch_point == 0)
  21. {
  22. ft5x0x_ts_inactivate();
  23. return 1;
  24. }
  25. #ifdef CONFIG_FT5X0X_MULTITOUCH
  26. switch (event->touch_point)
  27. {
  28. case 5:
  29. event->x5 = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c];
  30. event->y5 = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e];
  31. status = (s16)((buf[0x1b] & 0xc0) >> 6);
  32. event->touch_ID5=(s16)(buf[0x1D] & 0xF0)>>4;
  33. if (status == 1) ft5x0x_ts_release();
  34. case 4:
  35. event->x4 = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16];
  36. event->y4 = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18];
  37. status = (s16)((buf[0x15] & 0xc0) >> 6);
  38. event->touch_ID4=(s16)(buf[0x17] & 0xF0)>>4;
  39. if (status == 1) ft5x0x_ts_release();
  40. case 3:
  41. event->x3 = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10];
  42. event->y3 = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12];
  43. status = (s16)((buf[0x0f] & 0xc0) >> 6);
  44. event->touch_ID3=(s16)(buf[0x11] & 0xF0)>>4;
  45. if (status == 1) ft5x0x_ts_release();
  46. case 2:
  47. event->x2 = (s16)(buf[9] & 0x0F)<<8 | (s16)buf[10];
  48. event->y2 = (s16)(buf[11] & 0x0F)<<8 | (s16)buf[12];
  49. status = (s16)((buf[0x9] & 0xc0) >> 6);
  50. event->touch_ID2=(s16)(buf[0x0b] & 0xF0)>>4;
  51. if (status == 1) ft5x0x_ts_release();
  52. case 1:
  53. event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
  54. event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
  55. status = (s16)((buf[0x3] & 0xc0) >> 6);
  56. event->touch_ID1=(s16)(buf[0x05] & 0xF0)>>4;
  57. if (status == 1) ft5x0x_ts_release();
  58. break;
  59. default:
  60. return -1;
  61. }
  62. #else
  63. if (event->touch_point == 1)
  64. {
  65. event->x1 = (s16)(buf[3] & 0x0F)<<8 | (s16)buf[4];
  66. event->y1 = (s16)(buf[5] & 0x0F)<<8 | (s16)buf[6];
  67. }
  68. #endif
  69. event->pressure = 200;
  70. dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
  71. event->x1, event->y1, event->x2, event->y2);
  72. return 0;
  73. }

从设备中读取数据使用ft5x0x_i2c_rxdata()函数:

  1. static int ft5x0x_i2c_rxdata(char *rxdata, int length)
  2. {
  3. int ret;
  4. struct i2c_msg msgs[] =
  5. {
  6. {
  7. .addr   = this_client->addr,
  8. .flags  = 0,
  9. .len    = 1,
  10. .buf    = rxdata,
  11. },
  12. {
  13. .addr   = this_client->addr,
  14. .flags  = I2C_M_RD,
  15. .len    = length,
  16. .buf    = rxdata,
  17. },
  18. };
  19. ret = i2c_transfer(this_client->adapter, msgs, 2);
  20. if (ret < 0)
  21. pr_err("msg %s i2c read error: %d\n", __func__, ret);
  22. return ret;
  23. }

这个函数里面会构建一个i2c_msg
msg结构去调用i2c_transfer()函数读取数据,在前面已经说过,i2c_transfer()函数最终调用的就是i2c_algorithm中master_xfer()函数进行实际的数据传输,这个函数在adapter驱动中实现。
    对读取到的数据处理涉及到IC里面的数据格式,查找芯片的数据手册可以看到寄存器的数据分步。


ft5x0x寄存器映射表

这里为缩小篇幅,只截取一部分寄存器,其余寄存器格式可参照既给出的寄存器信息。
结合图片可以看出,代码先根据是否多点触摸,从寄存器读取数据长度,若是只需要单点触摸就读取7个字节信息,若是多点触摸,就读取31个字节的信息,从代码可以看出最多支持五点触摸。读出信息的第三个字节的低4位为触摸点数,所以event->touch_point
= buf[2] &
0x07;判断触摸点数之后便分别计算触摸点的坐标,这里以第一个点为例,其余点可以依此类推。第一个点的坐标信息包含在地址03h~06h中,坐标信息由12bit组成,分布在两个字节中,另外还包含flag标志信息,用于表示触摸点的状态,还包含触摸点的ID识别。

(2)把数据上报到输入子系统中由函数ft5x0x_report_value()实现

  1. static void ft5x0x_report_value(void)
  2. {
  3. struct ft5x0x_ts_data *data = i2c_get_clientdata(this_client);
  4. struct ts_event *event = &data->event;
  5. #ifdef CONFIG_FT5X0X_MULTITOUCH
  6. switch(event->touch_point)
  7. {
  8. case 5:
  9. input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID5);
  10. input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
  11. input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x5);
  12. input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y5);
  13. input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
  14. input_mt_sync(data->input_dev);
  15. case 4:
  16. input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID4);
  17. input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
  18. input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x4);
  19. input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y4);
  20. input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
  21. input_mt_sync(data->input_dev);
  22. case 3:
  23. input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID3);
  24. input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
  25. input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x3);
  26. input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y3);
  27. input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
  28. input_mt_sync(data->input_dev);
  29. case 2:
  30. input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID2);
  31. input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
  32. input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x2);
  33. input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y2);
  34. input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
  35. input_mt_sync(data->input_dev);
  36. case 1:
  37. input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->touch_ID1);
  38. input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure);
  39. input_report_abs(data->input_dev, ABS_MT_POSITION_X, SCREEN_MAX_X - event->x1);
  40. input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y1);
  41. input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR, 1);
  42. input_mt_sync(data->input_dev);
  43. default:
  44. break;
  45. }
  46. #else   /* CONFIG_FT5X0X_MULTITOUCH*/
  47. if (event->touch_point == 1)
  48. {
  49. input_report_abs(data->input_dev, ABS_X, SCREEN_MAX_X - event->x1);
  50. input_report_abs(data->input_dev, ABS_Y, event->y1);
  51. input_report_abs(data->input_dev, ABS_PRESSURE, event->pressure);
  52. }
  53. input_report_key(data->input_dev, BTN_TOUCH, 1);
  54. #endif  /* CONFIG_FT5X0X_MULTITOUCH*/
  55. input_sync(data->input_dev);
  56. dev_dbg(&this_client->dev, "%s: 1:%d %d 2:%d %d \n", __func__,
  57. event->x1, event->y1, event->x2, event->y2);
  58. }

因为触摸屏使用的是绝对坐标系,上报数据使用input_report_abs()函数,参数要注意选择合适的事件集。上报玩数据要记得同步,使用input_mt_sync()表示单个手指信息结束,使用input_sync()表示整个触摸动作的结束。

remove()函数的具体实现是ft5x0x_ts_remove(),做与probe()相反的工作,释放内存。

  1. static int __devexit ft5x0x_ts_remove(struct i2c_client *client)
  2. {
  3. struct ft5x0x_ts_data *ft5x0x_ts = i2c_get_clientdata(client);
  4. unregister_early_suspend(&ft5x0x_ts->early_suspend);
  5. free_irq(client->irq, ft5x0x_ts);
  6. input_unregister_device(ft5x0x_ts->input_dev);
  7. kfree(ft5x0x_ts);
  8. cancel_work_sync(&ft5x0x_ts->pen_event_work);
  9. destroy_workqueue(ft5x0x_ts->ts_workqueue);
  10. i2c_set_clientdata(client, NULL);
  11. return 0;
  12. }

至此,我们从i2c接口及输入子系统的接口清楚了整个触摸屏驱动的结构,并详细分析触摸屏驱动中的具体实现。

 

i2c触摸屏驱动文件的实现的更多相关文章

  1. AM335x(TQ335x)学习笔记——触摸屏驱动编写

    前面几篇文章已经通过配置DTS的方式完成了多个驱动的移植,接下来我们解决TQ335x的触摸驱动问题.由于种种原因,TQ335x的触摸屏驱动是以模块方式提供的,且Linux官方内核中也没有带该触摸屏的驱 ...

  2. i2c总线驱动,总线设备(适配器),从设备,从设备驱动的注册以及匹配

    常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 随笔分类 ARM裸机(13) C(8) C++(8) GNU-ARM汇编 Linux驱动(24) Linux应用编程(5) Makefile ...

  3. 基于设备树的TQ2440触摸屏驱动移植

    平台 开发板:tq2440 内核:Linux-4.9 u-boot:u-boot-2015.04   概述 之前移植了LCD驱动,下面继续移植触摸屏驱动,然后将tslib也移植上去. 正文 一.移植触 ...

  4. I2C总线驱动框架详解

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,I2C通信方法(”algorithm”)上层的,与 ...

  5. 【6集iCore3_ADP触摸屏驱动讲解视频】6-1 工程及程序构架介绍

    视频简介: 该视频由银杏科技有限公司基于iCore3应用开发平台推出,包含 触摸屏驱动工程文件的介绍与程序构架的介绍等.   源视频包下载地址: http://pan.baidu.com/s/1dFz ...

  6. S3C2440触摸屏驱动实例开发讲解

    出处:http://www.embeddedlinux.org.cn/html/yingjianqudong/ 一.开发环境 主  机:VMWare--Fedora 9 开发板:Mini2440--6 ...

  7. mini2440触摸屏驱动分析

    mini2440驱动分析系列之 ---------------------------------------Mini2440触摸屏程序分析 By JeefJiang July,8th,2009 这是 ...

  8. Linux I2C设备驱动编写(二)

    在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_driver.i2c_client.三者的关系也在上一节进行了描述.应该已经算是对Linux I2C子系统有了初步 ...

  9. Linux I2C设备驱动编写(一)

    在Linux驱动中I2C系统中主要包含以下几个成员: I2C adapter 即I2C适配器 I2C driver 某个I2C设备的设备驱动,可以以driver理解. I2C client 某个I2C ...

随机推荐

  1. MD5加密和sha加密

    sha加密原理Algorithm)又叫安全哈希加密技术,是当今世界最先近的加密算法.主要用于文件身份识别.数字签名和口令加密等. 对于明文信息A,通过SHA1算法,生成一条160位长的识别码B.且明文 ...

  2. C#中委托

    委托是一种安全地封装方法的类型,它与 C 和 C++ 中的函数指针类似.与 C 中的函数指针不同,委托是面向对象的.类型安全的和保险的.一个委托类型是代表与特定参数列表和返回类型的方法的引用类型.实例 ...

  3. 函数的有用信息,装饰器 day12

    一 函数的有用信息 本函数的功能:绘图功能,实时接收数据并绘图.:return: 绘图需要的数据,返回给前端某标签 def f1(): ''' 本函数的功能:绘图功能,实时接收数据并绘图. :retu ...

  4. Linux移植之移植步骤

    在这里总结一下我在移植Linux2.6.22.6内核过程时的步骤.移植成功后最终能挂接做好的根文件系统,并且启动第一个init程序.移植的步骤如下: 1.将网上下载的内核源码文件linux-2.6.2 ...

  5. springMvc入门--初识springMvc

    springMvc是什么 springmvc是表现层的框架,是一个spring的表现层组件.是整个spring框架的一部分,但是也可以不使用springmvc.跟struts2框架功能类似.其中的mv ...

  6. 使用/\_ 打印正三角形 C/C++

    输入size,level,使用/\_,打印正三角形 #include<stdio.h> int main() { int level, size; printf("Enter l ...

  7. node.js 在使用child_process 模块时候,调试端口占用的问题解决方案(EADDRINUSE)

    在fork的时候,带参数{ execArgv: ['--debug=' + (process.debugPort +   1)] }

  8. GET与POST传递数据的长度分析

    在客户机和服务器之间进行请求-响应时,两种最常被用到的方法是:GET 和 POST.GET - 从指定的资源请求数据,POST - 向指定的资源提交要被处理的数据.本篇文章我们就来分析一下GET与PO ...

  9. 【已处理完】Centos 6.5版本,df -h出来的容量与du -sh的容量不对应是怎么会事呢?

    问题如题,df -h 出来的容量与du -sh 查看的容量信息不一样,是那里出了问题了吗? 下面分别是du -sh *与df -h出来的结果 [root@mail /]# du -sh * 6.2M ...

  10. pselect 函数

    <unix网络环境编程> 中20-7 的示例理解. #include "unp.h" static void recvfrom_alarm(int); void dg_ ...