1.1输入子系统
在Linux中,输入子系统是由输入子系统驱动层,输入子系统核心层、输入子系统事件处理层组成。其中,设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转化为标准的输入事件,再通过核心层提交给事件处理层。而核心层向下提供了设备驱动层的编程接口,向上又提供了事件处理层的编程接口。而事件处理层就为用户空间的应用程序提供了统一访问设备的接口和处理驱动层提交的事件。所以这使得我们的输入设备驱动程序不必关心对设备文件的操作,而是需要对各硬件寄存器的操作和提交的事件的处理。
1.2输入子系统驱动层实现原理
在Linux中,input输入设备用input_dev结构体描述
1)在驱动模块加载函数设置input设备支持input子系统的哪些事件
2)将input设备注册到input子系统中
3)利用中断在input设备发生输入操作时,提交所发生的事件对应的键值或坐标
1.3相关API函数
struct input_dev *input_allocate_device(void) //分配input_dev结构体
int input_register_device(struct input_dev *dev) //注册设备
void fastcall init_timer(struct timer_list *timer) //初始化定时器
static inline void add_timer(struct timer_list *timer) //添加定时器
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *devname,void *dev_id) //注册中断
static inline void set_bit(int nr, volatile void * addr) //将地址addr的nr位置1
int mod_timer(struct timer_list *timer, unsigned long expires) //设置新的定时值
static inline void input_sync(struct input_dev *dev) //发送事件同步事件
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
/* case EV_KEY:
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;
if (value == 2)
break;
change_bit(code, dev->key); //按位取反
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
break;
*/
}
在分配结构体函数里
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); //先分配动态内存
if (dev) {
dev->cdev.class = &input_class; //创建类
dev->cdev.groups = input_dev_attr_groups;
class_device_initialize(&dev->cdev); //初始化类
mutex_init(&dev->mutex);
INIT_LIST_HEAD(&dev->h_list); //添加到链表中
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
在input_register_device函数中
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { //如果没有设置延时和周期
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250; //就使用默认设置
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode; //同理 没有设置使用默认设置
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
if (!dev->cdev.dev)
dev->cdev.dev = dev->dev.parent;
error = class_device_add(&dev->cdev); //添加类
if (error)
return error;
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
}
在初始化定时器时 添加定时器超时处理函数
在注册中断时 添加中断处理函数 并确定何时触发中断
添加完驱动之后需要exec 0</dev/tty1 使得 按键输入的显示在lcd屏幕上