这段时间看嘞AD7879-I2C的touchscreen驱动,并在3.3内核中模拟在三星平台上做移植动作。下面首先来分析下3.3.3内核中这部分代码代码。 首先看下驱动函数的入口和出口, static int __init ad7879_i2c_init(void) { return i2c_add_driver(&ad7879_i2c_driver); } module_init(ad7879_i2c_init); static void __exit ad7879_i2c_exit(void) { i2c_del_driver(&ad7879_i2c_driver); } module_exit(ad7879_i2c_exit); 在函数的入口和出口,分别进行了I2C驱动的注册和注销。 static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879",// .owner = THIS_MODULE, .pm = &ad7879_pm_ops, }, .probe = ad7879_i2c_probe,//探测函数 .remove = __devexit_p(ad7879_i2c_remove), .id_table = ad7879_id,//描述了这个I2C驱动支持那些设备 }; 在这个里面内嵌了一个driver的结构体,其中name成员,再匹配设备的时候有用到,.owner= THIS_MODULE表示这个模块在使用的时候,不能被remmod。 ad7879_pm_ops一个电源管理方面的操作函数集。 SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume); #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \ const struct dev_pm_ops name = { \ SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ } #ifdef CONFIG_PM_SLEEP #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ .suspend = suspend_fn, \ .resume = resume_fn, \ .freeze = suspend_fn, \ .thaw = resume_fn, \ .poweroff = suspend_fn, \ .restore = resume_fn, #else #define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif 可以看出,如果设备支持睡眠的,提供了 ad7879_suspend和ad7879_resume两个操作函数。 #ifdef CONFIG_PM_SLEEP static int ad7879_suspend(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); mutex_lock(&ts->input->mutex); if (!ts->suspended && !ts->disabled && ts->input->users) __ad7879_disable(ts); ts->suspended = true; mutex_unlock(&ts->input->mutex); return 0; } static int ad7879_resume(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); mutex_lock(&ts->input->mutex); if (ts->suspended && !ts->disabled && ts->input->users) __ad7879_enable(ts); ts->suspended = false; mutex_unlock(&ts->input->mutex); return 0; } #endif 这两个函数主要完成touchscreen的挂起和恢复操作。 static const struct i2c_device_id ad7879_id[] = { { "ad7879", 0 }, { "ad7889", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7879_id); 这里是驱动所支持的设备数组,{}表示结束。 接下来主要看probe 探测函数,要明白探测函数试怎么被调用到的,这就是linux2.6以后最主要的设备模型,由于touchscreen并不是热插拔设备,我们会在对应平台上设置设备信息。以AD7879以I2C为例在三星的64系列板子上,假设AD7879的I2C接口接在I2C1上面的,我们会在/arch/arm/mach-s3c64XX/math-smdk6410.c中增加设备描述, 这里主要包含设备的名字和地址。这里AD7879的设备地址为0X59,因为再AD7879芯片手册上说到它的7位地址前5位固定为01011后两位可配置,我配置为00,最后一位我们配置为1,表示由主控器件向被控器件读数据所以地址为0X59. static struct i2c_board_info i2c_devs1[] __initdata = { { I2C_BOARD_INFO("24c128", 0x57), I2C_BOARD_INFO("ad7879", 0x59)}, /* Samsung S524AD0XD1 */ }; 当I2C驱动注册的时候,如果 ad7879_id表中设备名字有和i2c_devs1表中支持设备的名字相同的化,调用match会成功,然后调用ad7879_i2c_probe函数 static int __devinit ad7879_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ad7879 *ts; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_err(&client->dev, "SMBUS Word Data not Supported\n"); return -EIO; } ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq, &ad7879_i2c_bus_ops); if (IS_ERR(ts)) return PTR_ERR(ts); i2c_set_clientdata(client, ts); return 0; } 在探测函数里面首先完成了I2C功能的检测,然后主要由ad7879_probe完成具体的探测,注意其中有一个参数ad7879_i2c_bus_ops,表示提供给用户空间的操作函数集,最后由 i2c_set_clientdata把ts挂再I2C总线上。 static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { .bustype = BUS_I2C, .read = ad7879_i2c_read, .multi_read = ad7879_i2c_multi_read, .write = ad7879_i2c_write, }; bustype表示挂在什么总线上。 static int ad7879_i2c_read(struct device *dev, u8 reg) { struct i2c_client *client = to_i2c_client(dev); return i2c_smbus_read_word_swapped(client, reg); } 首先获得I2C上的设备,然后寄存器获得设备的信息,完成读。 static int ad7879_i2c_multi_read(struct device *dev, u8 first_reg, u8 count, u16 *buf) { struct i2c_client *client = to_i2c_client(dev); u8 idx; i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf); for (idx = 0; idx < count; ++idx) buf[idx] = swab16(buf[idx]); return 0; } 实现批量数据的读。 static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val) { struct i2c_client *client = to_i2c_client(dev); return i2c_smbus_write_word_swapped(client, reg, val); } 首先获得I2C上的设备,然后设备获得寄存器信息,完成写。 struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, const struct ad7879_bus_ops *bops) { struct ad7879_platform_data *pdata = dev->platform_data; struct ad7879 *ts; struct input_dev *input_dev; int err; u16 revid; if (!irq) { dev_err(dev, "no IRQ?\n"); err = -EINVAL; goto err_out; } if (!pdata) { dev_err(dev, "no platform data?\n"); err = -EINVAL; goto err_out; } ts = kzalloc(sizeof(*ts), GFP_KERNEL);//给TS分配空间 input_dev = input_allocate_device();//动态分配input设备 if (!ts || !input_dev) { err = -ENOMEM; goto err_free_mem; } /*初始化ts结构体*/ ts->bops = bops; ts->dev = dev; ts->input = input_dev; ts->irq = irq; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);//设置定时器 ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; ts->first_conversion_delay = pdata->first_conversion_delay; ts->acquisition_time = pdata->acquisition_time; ts->averaging = pdata->averaging; ts->pen_down_acc_interval = pdata->pen_down_acc_interval; ts->median = pdata->median; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));//保存用户空间的设备节点 /*初始化input_dev*/ input_dev->name = "AD7879 Touchscreen"; input_dev->phys = ts->phys; input_dev->dev.parent = dev; input_dev->id.bustype = bops->bustype; input_dev->open = ad7879_open; input_dev->close = ad7879_close; input_set_drvdata(input_dev, ts); /*设置事件*/ __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); __set_bit(ABS_PRESSURE, input_dev->absbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); /*设置事件参数*/ input_set_abs_params(input_dev, ABS_X, pdata->x_min ? : 0, pdata->x_max ? : MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_Y, pdata->y_min ? : 0, pdata->y_max ? : MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, pdata->pressure_min, pdata->pressure_max, 0, 0); /*配置AD7879控制寄存器2的第4位,完成复位*/ err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { dev_err(dev, "Failed to write %s\n", input_dev->name); goto err_free_mem; } /*获得AD7879的产品号和厂商号*/ revid = ad7879_read(ts, AD7879_REG_REVID); input_dev->id.product = (revid & 0xff); input_dev->id.version = revid >> 8; if (input_dev->id.product != devid) { dev_err(dev, "Failed to probe %s (%x vs %x)\n", input_dev->name, devid, revid); err = -ENODEV; goto err_free_mem; } /*配置AD7879的控制寄存器3,使其不予许温度/AUX/VBAT测量引起中断,屏蔽GPIO,测量X,Y,及压力*/ ts->cmd_crtl3 = AD7879_YPLUS_BIT | AD7879_XPLUS_BIT | AD7879_Z2_BIT | AD7879_Z1_BIT | AD7879_TEMPMASK_BIT | AD7879_AUXVBATMASK_BIT | AD7879_GPIOALERTMASK_BIT; /*配置AD7879寄存器2,模拟模块掉电,平均值基础,中值滤波器,首次转换延迟时间*/ ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR | AD7879_AVG(ts->averaging) | AD7879_MFS(ts->median) | AD7879_FCD(ts->first_conversion_delay); /*配置AD7879控制寄存器3,禁用INT始能,设置为主机模式,设置ADC采集时间,设置转换间隔定时器*/ ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 | AD7879_ACQ(ts->acquisition_time) | AD7879_TMR(ts->pen_down_acc_interval); /*注册中断,这里和注册普通中断由什么区别还不是很清除*/ err = request_threaded_irq(ts->irq, NULL, ad7879_irq, IRQF_TRIGGER_FALLING, dev_name(dev), ts); if (err) { dev_err(dev, "irq %d busy?\n", ts->irq); goto err_free_mem; } __ad7879_disable(ts);//让touchscreen不可用 err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);//生成系统属性文件 if (err) goto err_free_irq; err = ad7879_gpio_add(ts, pdata); if (err) goto err_remove_attr; err = input_register_device(input_dev); if (err) goto err_remove_gpio; return ts; err_remove_gpio: ad7879_gpio_remove(ts); err_remove_attr: sysfs_remove_group(&dev->kobj, &ad7879_attr_group); err_free_irq: free_irq(ts->irq, ts); err_free_mem: input_free_device(input_dev); kfree(ts); err_out: return ERR_PTR(err); } 上面由几个地方没有说清除,比如设置定时器 setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);//设置定时器 当时间溢出后,进入中断处理例程 static void ad7879_timer(unsigned long handle) { struct ad7879 *ts = (void *)handle; ad7879_ts_event_release(ts); } static void ad7879_ts_event_release(struct ad7879 *ts) { struct input_dev *input_dev = ts->input; input_report_abs(input_dev, ABS_PRESSURE, 0); input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } 由这里可以看出,定时溢出以后,主要完成,touchscreen按键的释放 让touchscreen不可用的函数需要说明一下 static void __ad7879_disable(struct ad7879 *ts) { u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) | AD7879_PM(AD7879_PM_SHUTDOWN);//实现ADC的完全关断 disable_irq(ts->irq); if (del_timer_sync(&ts->timer)) ad7879_ts_event_release(ts); ad7879_write(ts, AD7879_REG_CTRL2, reg); } #define AD7879_PM(x) ((x & 0x3) << 14) #define AD7879_PM_SHUTDOWN (0) 可以看出这个函数主要屏蔽中断,完全关闭AD7879 和它对应的一个函数,始能AD7879 static void __ad7879_enable(struct ad7879 *ts) { ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); enable_irq(ts->irq); } static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) { return ts->bops->write(ts->dev, reg, val); } 首先这里说明一下吧,在配置内核的时候AD7879-I2C这个驱动,它依赖AD7879驱动,从/drivers/input/touchscreen/kconfig也可以看到 config TOUCHSCREEN_AD7879 tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface" help Say Y here if you want to support a touchscreen interface using the AD7879-1/AD7889-1 controller. You should select a bus connection too. To compile this driver as a module, choose M here: the module will be called ad7879. config TOUCHSCREEN_AD7879_I2C tristate "support I2C bus connection" depends on TOUCHSCREEN_AD7879 && I2C help Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. To compile this driver as a module, choose M here: the module will be called ad7879-i2c. 所以AD7879实现了这块ADC的完全封装,具体使用SPI,还是I2C总线完成数据传输的驱动,只是实现数据传输的读写,而具体与AD7879的信息则不用关心,所以在ad7879_probe中会传入bops的操作函数集接口,用于AD7879使用。 现在来看一下在初始化input_dev的时候,输入子系统的打开和关闭函数的实现。 static int ad7879_open(struct input_dev *input) { struct ad7879 *ts = input_get_drvdata(input); /* protected by input->mutex */ if (!ts->disabled && !ts->suspended) __ad7879_enable(ts); return 0; } static void ad7879_close(struct input_dev* input) { struct ad7879 *ts = input_get_drvdata(input); /* protected by input->mutex */ if (!ts->disabled && !ts->suspended) __ad7879_disable(ts); } 其实在input_dev打开和关闭中主要完成的开启和关闭AD7879,在开启和关闭之前有判断ts是否是不可用,是否是挂起的,这里主要防止竞态。 static irqreturn_t ad7879_irq(int irq, void *handle) { struct ad7879 *ts = handle; ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); if (!ad7879_report(ts)) mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; } 中断处理例程中,主要完成事件的上报,如果上报成功,设置定时器。 static int ad7879_report(struct ad7879 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; u16 x, y, z1, z2; x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT; y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT; z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; /* * The samples processed here are already preprocessed by the AD7879. * The preprocessing function consists of a median and an averaging * filter. The combination of these two techniques provides a robust * solution, discarding the spurious noise in the signal and keeping * only the data of interest. The size of both filters is * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other * user-programmable conversion controls include variable acquisition * time, and first conversion delay. Up to 16 averages can be taken * per conversion. */ if (likely(x && z1)) { /* compute touch pressure resistance using equation #1 */ /*更具AD7879提供的公式,求出按下的压力*/ Rt = (z2 - z1) * x * ts->x_plate_ohms; Rt /= z1; Rt = (Rt + 2047) >> 12; /* * Sample found inconsistent, pressure is beyond * the maximum. Don't report it to user space. */ if (Rt > ts->pressure_max) return -EINVAL; /* * Note that we delay reporting events by one sample. * This is done to avoid reporting last sample of the * touch sequence, which may be incomplete if finger * leaves the surface before last reading is taken. */ /*如果定时器没有溢出,上报事件*/ if (timer_pending(&ts->timer)) { /* Touch continues */ input_report_key(input_dev, BTN_TOUCH, 1); input_report_abs(input_dev, ABS_X, ts->x); input_report_abs(input_dev, ABS_Y, ts->y); input_report_abs(input_dev, ABS_PRESSURE, ts->Rt); input_sync(input_dev); } ts->x = x; ts->y = y; ts->Rt = Rt; return 0; } return -EINVAL; } 再touchscreen中关于input的将在后面分析,现在总结一下AD7879-I2C touchscreen在驱动中的流程吧。 1.驱动的入口注册I2C 2.实现ad7879_i2c_bus_ops操作函数集中的操作,基于不同总线有不同的读写操作。 3.初始化AD7879 touchsreen 4.分配input_dev,初始化input_dev 5.设置事件及对应事件的参数 6.配置AD7879的控制寄存器 7.在中断处理例程里面完成AD转换,上报数据 8.实现定时器溢出函数来释放touchscreen