因为年后工作需要,预先对Android 驱动做一些了解,以下以 I2C 接口的 多点电容触摸屏 驱动为例,简单总结下Android 驱动
1.首先编写驱动模块的 Kconfig 和 Makefile 两个文件
################################# lfl add 20160819 ####################################
obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx.o
obj-$(CONFIG_TOUCHSCREEN_SSD254X) += ssd254x.o
################################# lfl add 20160819 ####################################
如上的makefile 文件 同时支持两种I2C 接口的触摸屏 ,编译时依据 如下 Kconfig 文件的配置 选择一种硬件
################################# lfl add 20160819 ####################################
config TOUCHSCREEN_GT9XX
tristate "GT9XX I2C Touchscreen"
depends on I2C
help
Say Y here if you have GT9XX series I2C touchscreen,
connected to your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called gt9xx_ts.
config TOUCHSCREEN_SSD254X
tristate "SSD254X I2C Touchscreen"
depends on I2C
help
Say Y here if you have SSD254X series I2C touchscreen,
connected to your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called ssd254x_ts.
################################# lfl add 20160819 ####################################
系统在编译时选择其中一种有效, ./kernel_imx/arch/arm/configs/imx6_android_defconfig 对应项目的配置文件
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_GT9XX=y
CONFIG_TOUCHSCREEN_SSD254X=y所以 ssd254x.o 参加编译。
2. 打开 ssd254x.c 最下部 都有模块的
module_init(ssd254x_ts_init);
module_exit(ssd254x_ts_exit);注册模块的初始化和退出函数
static int __init ssd254x_ts_init(void)
static void __exit ssd254x_ts_exit(void)
其中 linux就是这样做的,对只需要初始化运行一次的函数都加上__init属性,__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,module_exit的参数卸载时同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作,显然__init和__exit对动态加载的模块是无效的,只支持完全编译进内核。
3. ssd254x_ts_init
3.1 ssd254x_ts_init
//创建内核工作线程
ssd254x_wq = create_singlethread_workqueue("ssd254x_wq");
//依据设备信息 创建设备
client = i2c_new_device(adapter, i2c_board_info);
//设置
Ssd254x_ts->client = client;
i2c_set_clientdata(client, Ssd254x_ts);//添加 I2C 驱动 驱动和设备通过相同的 ssd254x_ts_id 相关联
ret=i2c_add_driver(&ssd254x_ts_driver);
3.2 ssd254x_ts_exit 基本是 对应 init 操作的反向,即各种资源的释放 主要步骤如下
i2c_del_driver(&ssd254x_ts_driver);
if (ssd254x_wq) {
destroy_workqueue(ssd254x_wq);
}
4. 驱动结构体的 定义如下
static struct i2c_driver ssd254x_ts_driver = {
. driver = {
.owner = THIS_MODULE,
.name = TYPE_NAME,
},
.probe = ssd254x_ts_probe,
.remove = ssd254x_ts_remove,
.id_table = ssd254x_ts_id,
};其中 最关键的是 黑色加粗的两个函数指针 系统会在初始化过程中 调用 ssd254x_ts_probe,所以这才是驱动真正的初始化
static int ssd254x_ts_probe(struct i2c_client *client,const struct i2c_device_id *idp)
{if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{........}
//申请资源 并初始化
ssl_priv = kzalloc(sizeof(*ssl_priv), GFP_KERNEL);
dev_set_drvdata(&client->dev, ssl_priv);
//申请资源 并初始化
ssl_input = input_allocate_device();input_set_drvdata(ssl_input, ssl_priv);
//申请硬件资源 GPIO ,并且设置
ret = gpio_request(ON_TOUCH_INT, "ssd254x-irq");
gpio_direction_input(ON_TOUCH_INT);
//注册主工作函数
INIT_WORK(&ssl_priv->ssl_work, ssd254x_ts_work);
//注册硬件中断或者 定时器,特定条件下运行一下驱动主函数 ,不能像单片机那样独占CPU资源
if((ssl_priv->use_irq==1)||(ssl_priv->use_irq==2))
{
ssl_priv->irq = gpio_to_irq(ON_TOUCH_INT);
error = request_irq(ssl_priv->irq, ssd254x_ts_isr, IRQF_TRIGGER_LOW, client->name,ssl_priv);}
if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2))
{
hrtimer_init(&ssl_priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ssl_priv->timer.function = ssd254x_ts_timer;}
}
ssd254x_ts_remove的工作与 ssd254x_ts_probe 基本反向 ,
static int ssd254x_ts_remove(struct i2c_client *client)
{
struct ssl_ts_priv *ssl_priv = dev_get_drvdata(&client->dev);
#ifdef CONFIG_TOUCHSCREEN_SSL_DEBUG
printk("+-----------------------------------------+\n");
printk("| ssd254x_ts_remove ! |\n");
printk("+-----------------------------------------+\n");
#endif
if((ssl_priv->use_irq==0)||(ssl_priv->use_irq==2)) hrtimer_cancel(&ssl_priv->timer);
//if((ssl_priv->use_irq==1)||(ssl_priv->use_irq==2)) free_irq(ssl_priv->irq, ssl_priv);
input_unregister_device(ssl_priv->input);
input_free_device(ssl_priv->input);
kfree(ssl_priv);
dev_set_drvdata(&client->dev, NULL);
return 0;
}
5. 无论是上面注册中断处理函数还是 定时器函数 ,所做的工作都一样 ,让本模块的内核线程运行一次
中断处理函数,先关中断,启动线程运行驱动程序,驱动程序中再次打开中断,定时器类似
static irqreturn_t ssd254x_ts_isr(int irq, void *dev_id)
{
struct ssl_ts_priv *ssl_priv = dev_id;
disable_irq_nosync(ssl_priv->irq);
queue_work(ssd254x_wq, &ssl_priv->ssl_work);
return IRQ_HANDLED;
}
static enum hrtimer_restart ssd254x_ts_timer(struct hrtimer *timer)
{
struct ssl_ts_priv *ssl_priv = container_of(timer, struct ssl_ts_priv, timer);
queue_work(ssd254x_wq, &ssl_priv->ssl_work);
if(ssl_priv->use_irq==0) hrtimer_start(&ssl_priv->timer, ktime_set(0, MicroTimeTInterupt), HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
6. 真正的驱动程序
static void ssd254x_ts_work(struct work_struct *work)
{
例如 触摸屏 会一直读取屏幕信息,然后反馈给系统
gtp_touch_down
gtp_touch_up
}