赞
踩
许多驱动程序不是从一个字符设备开始写的,而是使用固定框架实现,该框架专门针对特定设备类型,如网络设备、串口设备、内存设备)(MTD)、实时时钟、工业IO等。框架的作用是把同类设备驱动程序的公共代码分离出来,以减少代码重复度。框架为每种类型设备提供与驱动无关的一致的用户接口。
驱动如何与框架交互并向用户应用程序,使用硬件资源的功能,如下:
输入设备常见的有鼠标、键盘、操作杆、触摸屏等。
输入设备驱动程序统一的格式——input_event数据结构捕获硬件信息,并报告给核心层,核心层对数据进行分类上报事件,最后通过事件层上报给用户态。
输入子系统可分两部分:驱动程序、事件处理程序。
输入子系统划分:event应用程序、输入子系统核心、输入事件驱动、输入轮询设备驱动、I2C驱动;
轮询输入设备头文件定义:
/include/linux/input-polldev.h
struct input_polled_dev;
input_polled_dev数据结构由poll()回调函数来处理,这个函数轮询设备并产生输入事件。poll()函数内部,驱动程序调用input_event()函数把事件发送到事件处理函数。
adxl345@1c {
compatible = "arrow,adxl345";
reg = <0x1d>;
};
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/input-polldev.h>
/* create private structure */
struct ioaccel_dev {
struct i2c_client * i2c_client;
struct input_polled_dev * polled_input;
};
#define POWER_CTL 0x2D
#define PCTL_MEASURE (1 << 3)
#define OUT_X_MSB 0x33
/* poll function */
static void ioaccel_poll(struct input_polled_dev * pl_dev)
{
struct ioaccel_dev * ioaccel = pl_dev->private;
int val = 0;
val = i2c_smbus_read_byte_data(ioaccel->i2c_client, OUT_X_MSB);
if ( (val > 0xc0) && (val < 0xff) ) {
input_event(ioaccel->polled_input->input, EV_KEY, KEY_1, 1);
} else {
input_event(ioaccel->polled_input->input, EV_KEY, KEY_1, 0);
}
input_sync(ioaccel->polled_input->input);
}
static int ioaccel_probe(struct i2c_client * client,
const struct i2c_device_id * id)
{
/* declare an instance of the private structure */
struct ioaccel_dev * ioaccel;
dev_info(&client->dev, "my_probe() function is called.\n");
/* allocate private structure for new device */
ioaccel = devm_kzalloc(&client->dev, sizeof(struct ioaccel_dev), GFP_KERNEL); //声明一个私有数据结构体、并分配空间
/* associate client->dev with ioaccel private structure */
i2c_set_clientdata(client, ioaccel);
/* enter measurement mode */
i2c_smbus_write_byte_data(client, POWER_CTL, PCTL_MEASURE);
/* allocate the struct input_polled_dev */
ioaccel->polled_input = devm_input_allocate_polled_device(&client->dev);
/* initialize polled input */
ioaccel->i2c_client = client;
ioaccel->polled_input->private = ioaccel;
ioaccel->polled_input->poll_interval = 50;
ioaccel->polled_input->poll = ioaccel_poll;
ioaccel->polled_input->input->dev.parent = &client->dev;
ioaccel->polled_input->input->name = "IOACCEL keyboard";
ioaccel->polled_input->input->id.bustype = BUS_I2C;
/* set event types */
set_bit(EV_KEY, ioaccel->polled_input->input->evbit);
set_bit(KEY_1, ioaccel->polled_input->input->keybit);
/* register the device, now the device is global until being unregistered*/
input_register_polled_device(ioaccel->polled_input);
return 0;
}
static int ioaccel_remove(struct i2c_client * client)
{
struct ioaccel_dev * ioaccel;
ioaccel = i2c_get_clientdata(client);
input_unregister_polled_device(ioaccel->polled_input);
dev_info(&client->dev, "ioaccel_remove()\n");
return 0;
}
/* add entries to device tree */
static const struct of_device_id ioaccel_dt_ids[] = {
{ .compatible = "arrow,adxl345", },
{ }
};
MODULE_DEVICE_TABLE(of, ioaccel_dt_ids);
static const struct i2c_device_id i2c_ids[] = {
{ "adxl345", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, i2c_ids);
/* create struct i2c_driver */
static struct i2c_driver ioaccel_driver = {
.driver = {
. = "adxl345",
.owner = THIS_MODULE,
.of_match_table = ioaccel_dt_ids,
},
.probe = ioaccel_probe,
.remove = ioaccel_remove,
.id_table = i2c_ids,
};
/* register to i2c bus as a driver */
module_i2c_driver(ioaccel_driver); //把驱动程序注册到I2C总线上
MODULE_LICENSE("GPL");
MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("This is an accelerometer INPUT framework platform driver");
感谢阅读,祝君成功!
-by aiziyou
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。