本篇记录在友善之臂 mini2440 平台上挂载I2C接口触摸屏的驱动开发过程。
内核版本linux-2.6.32.2, 平台是ARM9 S3C2440+I2C接口的触摸屏
如上篇Linux的I2C驱动体系结构讲述 http://www.lupaworld.com/273398/viewspace-204237.html
要挂载新的I2C设备,需要实现3部分:
1) 适配器的硬件驱动: 内核中已经实现mini2440,i2c适配器驱动,可以在如下目录i2c-s3c2410.c中看到相关代码 linux-2.6.32.2/drivers/i2c/busses/i2c-s3c2410.c
2) I2C 设配器的algorithm 同样在inux-2.6.32.2/drivers/i2c/busses/i2c-s3c2410.c文件中实现。
以上两部分无须做任何更改
3) I2C设备驱动,可以以linux-2.6.32.2/drivers/input/touchscreen/migor_ts.c为例,分析如下: //-------------------------------------------------------------------// #include <linux/module.h> #include <linux/kernel.h> #include <linux/input.h> #include <linux/interrupt.h> #include <asm/io.h> #include <linux/i2c.h> #include <linux/timer.h> #include <linux/delay.h>
/*resolution definion according to touch screen */ #define MIN_X_COORDINATE 0 #define MAX_X_COORDINATE 1024 #define MIN_Y_COORDINATE 0 #define MAX_Y_COORDINATE 768
/* touch screen data structure */ struct i2c_ts_priv { struct i2c_client *client; struct input_dev *input; struct delayed_work work; int irq; };
static void i2c_ts_poscheck(struct work_struct *work) { struct i2c_ts_priv *priv = container_of(work, struct i2c_ts_priv, work.work); /* buffer for storing data */ char buf[6]; int number; int xpos, ypos;
memset(buf, 0, sizeof(buf));
/* Now do Page Read */ if (i2c_master_recv(priv->client, buf,6) != 6) { dev_err(&priv->client->dev, "Unable to read i2c page\n"); goto out; } /* convert coordinate */ number = buf[0]&0x07; xpos = ((buf[3] << 8) | buf[2]); ypos = ((buf[5] << 8) | buf[4]); /* report input event */ if ((number != 0) && (xpos != 0) && (ypos != 0)) { input_report_key(priv->input, BTN_TOUCH, 1); input_report_abs(priv->input, ABS_X, xpos); input_report_abs(priv->input, ABS_Y, ypos); input_sync(priv->input); } else if (number == 0) { input_report_key(priv->input, BTN_TOUCH, 0); input_sync(priv->input); }
out: enable_irq(priv->irq); }
/* read finger numbers and coordinate and report input event */ static irqreturn_t i2c_ts_isr(int irq, void *dev_id) { struct i2c_ts_priv *priv = dev_id; /* disable irq */ disable_irq_nosync(irq); schedule_delayed_work(&priv->work, HZ/100); return IRQ_HANDLED; }
static int i2c_ts_open(struct input_dev *dev) { return 0; }
static void i2c_ts_close(struct input_dev *dev) {
}
static int i2c_ts_probe(struct i2c_client *client, const struct i2c_device_id *idp) { struct i2c_ts_priv *priv; struct input_dev *input; int error; char buf[2];
priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "failed to allocate driver data\n"); error = -ENOMEM; goto err0; }
dev_set_drvdata(&client->dev, priv);
input = input_allocate_device(); if (!input) { dev_err(&client->dev, "Failed to allocate input device.\n"); error = -ENOMEM; goto err1; }
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input, ABS_X, MIN_X_COORDINATE, MAX_X_COORDINATE, 0, 0); input_set_abs_params(input, ABS_Y, MIN_Y_COORDINATE, MAX_Y_COORDINATE, 0, 0);
input->name = client->name; input->id.bustype = BUS_I2C; input->dev.parent = &client->dev;
input->open = i2c_ts_open; input->close = i2c_ts_close;
input_set_drvdata(input, priv);
priv->client = client; priv->input = input; INIT_DELAYED_WORK(&priv->work, i2c_ts_poscheck); priv->irq = client->irq;
error = input_register_device(input); if (error) goto err1;
error = request_irq(priv->irq, i2c_ts_isr, IRQF_TRIGGER_FALLING, client->name, priv); if (error) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); goto err2; }
device_init_wakeup(&client->dev,1); return 0;
err2: input_unregister_device(input); input = NULL; /* so we dont try to free it below */ err1: input_free_device(input); kfree(priv); err0: dev_set_drvdata(&client->dev, NULL); return error; }
static int i2c_ts_remove(struct i2c_client *client) { struct i2c_ts_priv *priv = dev_get_drvdata(&client->dev);
free_irq(priv->irq, priv); input_unregister_device(priv->input); kfree(priv);
dev_set_drvdata(&client->dev, NULL);
return 0; }
static int i2c_ts_suspend(struct i2c_client *client, pm_message_t mesg) { struct i2c_ts_priv *priv = dev_get_drvdata(&client->dev); if(device_may_wakeup(&client->dev)) enable_irq_wake(priv->irq); return 0; }
static int i2c_ts_resume(struct i2c_client *client) { struct i2c_ts_priv *priv = dev_get_drvdata(&client->dev); if(device_may_wakeup(&client->dev)) disable_irq_wake(priv->irq); return 0; }
static const struct i2c_device_id i2c_ts_id[] = { { "i2c-ts", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, i2c_ts_id);
static struct i2c_driver i2c_ts_driver = { .driver = { .name = "i2c-ts", }, .probe = i2c_ts_probe, .remove = i2c_ts_remove, .suspend = i2c_ts_suspend, .resume = i2c_ts_resume, .id_table = i2c_ts_id, };
static int __init i2c_ts_init(void) { return i2c_add_driver(&i2c_ts_driver); }
static void __exit i2c_ts_exit(void) { i2c_del_driver(&i2c_ts_driver); }
MODULE_DESCRIPTION("i2c Touchscreen driver"); MODULE_AUTHOR("ALlen <[email protected]>"); MODULE_LICENSE("GPL");
module_init(i2c_ts_init); module_exit(i2c_ts_exit);
4).实现如上步骤后,还需要创建和配置I2C 设备,设置文件位于 linux-2.6.32.2/arch/arm/mach-s3c2440/mach-mini2440.c中, 添加如下代码: .................................................. +/* I2C touch screen devices. */ +/* bus configuration */ +static struct s3c2410_platform_i2c i2c_touchscreen_cfg __initdata = { + .flags = 0, + .slave_addr = 0x5c, + .frequency = 100*1000, + .sda_delay = 2, +};
+/* i2c device name is "i2c_ts", address is 0x5c, interrupt is eint20 */ +static struct i2c_board_info touchscreen_i2c_devs[] __initdata = { + { + I2C_BOARD_INFO("i2c-ts", 0x5c), + .irq = IRQ_EINT20, + }, +}; ...................................................
static void __init mini2440_machine_init(void) { .................................................. + /* i2c touch screen devices */ + s3c_i2c0_set_platdata(&i2c_touchscreen_cfg); + i2c_register_board_info(0, touchscreen_i2c_devs, +ARRAY_SIZE(touchscreen_i2c_devs)); ................................................... }
此处I2C_BOARD_INFO("i2c-ts",0x5c), “i2c-ts” 要和i2c设备驱动中i2c_ts_id一致。 才能保证i2c设备驱动成功加载。
|
请发表评论