drivers/input/input.c:
input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
问:怎么读按键?
input_open_file
struct input_handler *handler = input_table[iminor(inode) >> 5];
new_fops = fops_get(handler->fops) // =>&evdev_fops
file->f_op = new_fops;
err = new_fops->open(inode, file);
app: read > ... > file->f_op->read
input_table数组由谁构造?
input_register_handler
注册input_handler:
input_register_handler
// 放入数组
input_table[handler->minor >> 5] = handler;
// 放入链表
list_add_tail(&handler->node, &input_handler_list);
// 对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
注册输入设备:
input_register_device
// 放入链表
list_add_tail(&dev->node, &input_dev_list);
// 对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
input_attach_handler
id = input_match_device(handler->id_table, dev);
error = handler->connect(handler, dev, id);
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接"
怎么建立连接?
1. 分配一个input_handle结构体
2.
input_handle.dev = input_dev; // 指向左边的input_dev
input_handle.handler = input_handler; // 指向右边的input_handler
3. 注册:
input_handler->h_list = &input_handle;
inpu_dev->h_list = &input_handle;
evdev_connect
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle
// 设置
evdev->handle.dev = dev; // 指向左边的input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; // 指向右边的input_handler
evdev->handle.private = evdev;
// 注册
error = input_register_handle(&evdev->handle);
怎么读按键?
app: read
--------------------------
.......
evdev_read
// 无数据并且是非阻塞方式打开,则立刻返回
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
// 否则休眠
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
谁来唤醒?
evdev_event
wake_up_interruptible(&evdev->wait);
evdev_event被谁调用?
猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
gpio_keys_isr
// 上报事件
input_event(input, type, button->code, !!state);
input_sync(input);
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
struct input_handle *handle;
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件
unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];
测试:
1.
hexdump /dev/event1 (open(/dev/event1), read(), )
秒 微秒 类 code value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000
2. 如果没有启动QT:
cat /dev/tty1
按:s2,s3,s4
就可以得到ls
或者:
exec 0</dev/tty1
然后可以使用按键来输入
3. 如果已经启动了QT:
可以点开记事本
然后按:s2,s3,s4