OrangePi|48 使用linux内核源码里的按键轮询驱动
这个设备驱动适用于,每个按键是连接到一个io口, 驱动里是通过定时查询io口的电平来确定按键的状态的,不使用io口的中断功能/
需要在linux内核配置里选上相关的配置。在内核源码目录下:
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-Device Drivers--->
Input device support--->
[*]Keyboards--->
<*>Polled GPIO buttons
选择上后,再编内核,再使用新的内核镜像启动系统
使用新内核启动后,可以查看出设备驱动是否已选择上:
/sys/bus/platform/drivers/目录下应有”gpio-keys-polled”目录
【OrangePi|48 使用linux内核源码里的按键轮询驱动】驱动源码在”drivers/input/keyboard/gpio_keys_polled.c”, 里面是一个平台驱动,我们只要写平台设备描述硬件的资源与此驱动匹配即可.
236 static struct platform_driver gpio_keys_polled_driver = {
237.probe= gpio_keys_polled_probe,
238.remove = __devexit_p(gpio_keys_polled_remove),
239.driver = {
240.name= DRV_NAME, // "gpio-keys-polled"
241.owner= THIS_MODULE,
242},
243 };
244 module_platform_driver(gpio_keys_polled_driver);
//通过阅读平台驱动的probe函数,可得知我们写的平台设备应提供具本哪些硬件信息.
103 static int __devinit gpio_keys_polled_probe(struct platform_device *pdev)
104 {
105struct gpio_keys_platform_data *pdata = https://www.it610.com/article/pdev->dev.platform_data;
106struct device *dev = &pdev->dev;
107struct gpio_keys_polled_dev *bdev;
108struct input_polled_dev *poll_dev;
109struct input_dev *input;
...
//轮询型输入设备对象的初始化//注册轮询型的输入设备
error = input_register_polled_device(poll_dev);
...
215 }
/
//通过probe函数,可以确定我们写平台设备时只需通过platform_data成员提供平台驱动所需的信息,无需再提供resource.
//再确定结构体gpio_keys_platform_data的每个成员的作用即可,如不清楚具体用途,可以在驱动代码里通过查看对成员值的访问推出用途.
//平台设备的写法与前一个按键驱动方法完全一样, 注意平台设备的名字需与现平台驱动的名字一致.
//还有platform_data数据里需要设poll_interval轮询间隔时间
///
轮询型的输入设备的工作原理分析:
1) 轮询的实现是用工作队列的方式来实现的.
"include/linux/workqueue.h"
300 extern struct workqueue_struct *system_freezable_wq;
//有工作队列头指针"kernel/workqueue.c"
256 struct workqueue_struct *system_freezable_wq __read_mostly;
//声明指针变量
...
262 EXPORT_SYMBOL_GPL(system_freezable_wq);
//导出指针变量3914system_freezable_wq = alloc_workqueue("events_freezable",
3915WQ_FREEZABLE, 0);
//分配工作队列头的空间
2) 每个struct input_polled_dev对象表示轮询型的输入设备对象
struct input_polled_dev {
...
void (*poll)(struct input_polled_dev *dev);
//设备驱动实现的轮询函数,在工作任务里触发调用
...
struct input_dev *input;
//基于输入设备扩展而来的input_polled_dev类型
...
struct delayed_work work;
//用于加入工作队列的工作任务
};
3) 在设备驱动的probe函数里对轮询输入设备的初始化
poll_dev = input_allocate_polled_device();
...
poll_dev->poll = gpio_keys_polled_poll;
// poll函数里检查按键io口的状态,并汇报按键.
poll_dev->poll_interval = pdata->poll_interval;
...
input = poll_dev->input;
//输入设备的初始化
...input_register_polled_device(poll_dev);
//注册轮询输入设备
4) 注册轮询输入设备时所作的工作
int input_register_polled_device(struct input_polled_dev *dev) //传入轮询输入设备对象的地址
{
struct input_dev *input = dev->input;
//获取输入设备对象的地址
int error;
input_set_drvdata(input, dev);
INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
//初始化用于工作队列的延时工作任务,当dev->work工作任务得到调用时, input_polled_device_work函数就会得到调用
//前面我们用的工作队列,只要工作任务加入队列后,等待条件满足并唤醒队列才会得到调用. 但这里相当于定时执行一次.
...
input->open = input_open_polled_device;
//在输入设备的设备文件open时,被触发调用
input->close = input_close_polled_device;
//在设备文件close时,被触发调用.error = input_register_device(input);
//注册输入设备
...
}
5) 轮询输入设备里的工作队列被调用的过程
static int input_open_polled_device(struct input_dev *input) //当设备文件open时,此函数会被调用
{
struct input_polled_dev *dev = input_get_drvdata(input);
...
input_polldev_queue_work(dev);
// 把轮询输入设备的工作任务加入队列
...
return 0;
}static void input_polldev_queue_work(struct input_polled_dev *dev)
{
unsigned long delay;
delay = msecs_to_jiffies(dev->poll_interval);
if (delay >= HZ)
delay = round_jiffies_relative(delay);
queue_delayed_work(system_freezable_wq, &dev->work, delay);
//把工作任务加入队列,并指定多久时间后执行. 经过delay时间后,input_polled_device_work函数就会被调用.
}static void input_polled_device_work(struct work_struct *work)
{
struct input_polled_dev *dev =
container_of(work, struct input_polled_dev, work.work);
dev->poll(dev);
//调用轮询输入设备对象的poll函数,即gpio_keys_polled_poll函数
input_polldev_queue_work(dev);
//重新把轮询输入设备对象的工作任务加入工作队列里,实现按间隔时间重复调用
}
推荐阅读
- 由浅入深理解AOP
- 【译】20个更有效地使用谷歌搜索的技巧
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- iOS中的Block
- Linux下面如何查看tomcat已经使用多少线程
- 使用composer自动加载类文件
- Beego打包部署到Linux
- android|android studio中ndk的使用