本文共 4513 字,大约阅读时间需要 15 分钟。
一:前言
之前看过s3c2440上的触摸屏驱动,不过那个是电阻的。现在手机上用的都是电容式的。触摸屏相对来说还是属于比较简单的模块,拿龙歌的话来说都是属于入门级的。我大概看了一下代码,总的代码好像是不多,但里面所涉及到的东西好像还挺多的。1.I2C驱动;2.中断-工作队列;3.input子系统
二:I2C驱动回顾
按照华清的那本《linux设备驱动开发详解》上面讲的来看I2C驱动分 I2C核心;I2C总线驱动;I2C设备驱动。在我所处的环境当中I2C核心和总线驱动已经是做好了,我所要添加的触摸屏属于一个I2C设备驱动。
1.所涉及数据结构: i2c_driver,i2c_client ,i2c_adapter ,i2c_algorithm ,他们的具体定义都在i2c.h当中。i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少 i2c_algorithm 的 i2c_adapter 什么也做不了,因此 i2c_adapter 中包含其使用的i2c_algorithm的指针。 i2c_algorithm则是I2C适配器与I2C设备通信方法的描述。i2c_driver 对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个 I2C设备都需要一个i2c_client来描述。 i2c_client 则是对应一个具体的i2c设备,它里面还包含i2c_driver 和i2c_adapter ,这样三者也都联系在一起了。 struct i2c_driver { unsigned int class; int (*attach_adapter)(struct i2c_adapter *); int (*detach_adapter)(struct i2c_adapter *); /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); void (*alert)(struct i2c_client *, unsigned int data); int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); struct device_driver driver; const struct i2c_device_id *id_table; /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; }; struct i2c_client { unsigned short flags;/* div., see below */ unsigned short addr;/* chip address - NOTE: 7bit */ /* addresses are stored in the*/ /* _LOWER_ 7 bits*/ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter;/* the adapter we sit on */ struct i2c_driver *driver;/* and our access routines */ struct device dev;/* the device structure */ int irq; /* irq issued by device */ struct list_head detected; }; struct i2c_adapter { struct module *owner; unsigned int id; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices*/ struct rt_mutex bus_lock; int timeout; /* in jiffies */ int retries; struct device dev;/* the adapter device */ int nr; char name[48]; struct completion dev_released; struct list_head userspace_clients; }; struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); }; 2-1 : board-xxx.c里面定义i2c_board_info static struct i2c_board_info i2c_devices[] = { #ifdef CONFIG_QT602240 { I2C_BOARD_INFO(ATMEL_QT602240_NAME, 0x94 >> 1), .platform_data = &supersonic_atmel_ts_data, .irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_TP_INT_N) }, #endif }可以看到这里面包含设备名字,设备地址,所用的中断引脚,还有一个platform_data的成员,这个所含的信息就多了,由我们自己定义。 xxx-init函数中将i2c_board_info注册上。 i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); 查看i2c_register_board_info的实现可知它创建了一个devinfo然后将它挂在了__i2c_board_list链表上。 2-2: touch-xxx.c具体的TP设备驱动文件。 定义i2c_driver。 static struct i2c_driver atmel_ts_driver = { .id_table = atml_ts_i2c_id, .probe = atmel_ts_probe, .remove = atmel_ts_remove, #ifndef CONFIG_HAS_EARLYSUSPEND .suspend = atmel_ts_suspend, .resume = atmel_ts_resume, #endif .driver = { .name = ATMEL_QT602240_NAME, }, }; 首先当然是从module_init函数看起: static int __devinit xxx_init(void) { printk(KERN_INFO "atmel_ts_init():\n"); return i2c_add_driver(&atmel_ts_driver); } 通常看完init函数就看它的probe函数,这是从平台设备驱动的架构得来的经验,至于这其间的具体过程就留到后面来分析吧!
三:中断-工作队列 相关
3.1 创建一个单个工作者线程的工作队列
ts->atmel_wq = create_singlethread_workqueue("atmel_wq"); 3-2 初始化一个工作 INIT_WORK(&ts->work, atmel_ts_work_func); atmel_ts_work_func这个函数就是最后给用户空间报数据的。 3-3 申请中断 ret = request_irq(client->irq, atmel_ts_irq_handler, IRQF_TRIGGER_LOW, client->name, ts); 当触屏有人按下的时候,便会触发中断,从而调用 atmel_ts_irq_handler函数。 static irqreturn_t atmel_ts_irq_handler(int irq, void *dev_id) { struct atmel_ts_data *ts = dev_id; // printk(KERN_INFO "LiuQi in irq enter %s %d\n", __func__, __LINE__); disable_irq_nosync(ts->client->irq); queue_work(ts->atmel_wq, &ts->work); //printk(KERN_INFO "LiuQi in irq out %s %d\n", __func__, __LINE__); return IRQ_HANDLED; } 这个函数里面就是将上面所创建的工作加入到工作队列中,此后工作对应的函数atmel_ts_work_func便会被调用。
转载地址:http://lzbsi.baihongyu.com/