因为年后工作需要,预先对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
}