共计 2926 个字符,预计需要花费 8 分钟才能阅读完成。
Linux 驱动 – GPIO Buttons 移植
这个设备驱动适用于,每个按键是连接到一个 io 口,而且这个 io 口还有中断功能的
驱动移植
需要在 linux 内核配置里选上相关的配置。在内核源码目录下:
# make menuconfig | |
Device Drivers ---> | |
Input device support ---> | |
[*] Keyboards ---> | |
<*> GPIO Buttons |
选择上后,再编内核,再使用新的内核镜像启动系统
使用新内核启动后,可以查看出设备驱动是否已选择上:
/sys/bus/platform/drivers/ 目录下应有”gpio-keys”目录
驱动源码分析
驱动源码在”drivers/input/keyboard/gpio_keys.c”, 里面是一个平台驱动,我们只要写平台设备描述硬件的资源与此驱动匹配即可.
static struct platform_driver gpio_keys_device_driver = { | |
.probe = gpio_keys_probe, | |
.remove = __devexit_p(gpio_keys_remove), | |
.driver = { | |
.name = "gpio-keys", // 可匹配名为 "gpio-keys" 的平台设备 | |
.owner = THIS_MODULE, | |
.pm = &gpio_keys_pm_ops, | |
.of_match_table = gpio_keys_of_match, // 按这个成员来匹配平台设备也是可以的,要求设备的名字为 "gpio-keys" | |
} | |
}; | |
// 通过阅读平台驱动的 probe 函数,可得知我们写的平台设备应提供具本哪些硬件信息. | |
static int __devinit gpio_keys_probe(struct platform_device *pdev) | |
{ | |
const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; // 这里可得知我们写的平台设备的 platform_data 成员应当提供 gpio_keys_platform_data 类型数据 | |
struct gpio_keys_drvdata *ddata; // 在设备驱动里对每个匹配上的设备都准备一个独立的数据 | |
struct device *dev = &pdev->dev; | |
struct gpio_keys_platform_data alt_pdata; | |
struct input_dev *input; | |
int i, error; | |
int wakeup = 0; | |
... | |
// 对 gpio_keys_drvdata 对象的初始化 | |
// 输入设备对象的初始化 | |
... | |
} |
///////////////////// | |
// 通过 probe 函数,可以确定我们写平台设备时只需通过 platform_data 成员提供平台驱动所需的信息,无需再提供 resource. | |
// 再确定结构体 gpio_keys_platform_data 的每个成员的作用即可, 如不清楚具体用途,可以在驱动代码里通过查看对成员值的访问推出用途. | |
//"include/linux/gpio_keys.h" | |
// 每个 struct gpio_key_button 的对象表示一个按键的具体信息 | |
struct gpio_keys_button { | |
// 此按键对应的键码 | |
unsigned int code; /* input event code (KEY_*, SW_*) */ | |
// 此按键对应的一个 io 口 | |
int gpio; /* -1 if this key does not support gpio */ | |
// 通过查看驱动代码,可得知表示是否按键按下是低电平,如是则设1. | |
int active_low; | |
// 就是申请 io 口,申请中断时使用的名字 | |
const char *desc; | |
// 输入设备的事件类型,按键用 EV_KEY | |
unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ | |
// 表示按键按下时是否唤醒系统, 这个需要 io 口硬件上有这功能 | |
int wakeup; /* configure the button as a wake-up source */ | |
// 防抖动用,间隔多久时间 | |
int debounce_interval; /* debounce ticks interval in msecs */ | |
... | |
}; | |
//gpio_keys_paltform_data 对象表示一个输入设备, 一个输入设备可有多个按键 | |
struct gpio_keys_platform_data { | |
// 多个按键需要用 gpio_keys_button 的变量数组才可以,buttons 成员用于装数组首地址 | |
struct gpio_keys_button *buttons; | |
// 在按键数组里的元素个数 | |
int nbuttons; | |
// 轮询的按键的平台驱动所用 | |
unsigned int poll_interval; /* polling interval in msecs - | |
for polling driver only */ | |
// 键按住时,是否重复提交按键 | |
unsigned int rep:1; /* enable input subsystem auto repeat */ | |
// 设备这边需在使用前所做的初始化工作,由设备驱动调用. 在输入设备产生的设备文件打开时触发调用 | |
int (*enable)(struct device *dev); | |
// 设备这边需在结束工作前所做的工作, 由设备驱动调用. 在输入设备产生的设备文件关闭时触发调用 | |
void (*disable)(struct device *dev); | |
const char *name; /* input device name */ | |
}; |
添加硬件信息
现用一个按键连接再板上,SIG 脚接到 PA20. 当键按下时,SIG 脚为高电平。键松开时,SIG 脚为低电平.
以下信息可以添加到 arch/arm/mach-board/mach-soc.c。
//mypdev.c | |
#include <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/platform_device.h> | |
#include <linux/gpio_keys.h> | |
#include <linux/input.h> | |
#include <mach/gpio.h> | |
struct gpio_keys_button btns[] = {{KEY_L, GPIOA(20), 0, "mygpio-keys", EV_KEY, 0, 100}, | |
}; | |
struct gpio_keys_platform_data pdata = { | |
.buttons = btns, | |
.nbuttons = ARRAY_SIZE(btns), | |
.rep = 1, | |
.name = "mygpio-keys", | |
}; | |
struct platform_device mypdev = { | |
.name = "gpio-keys", // 与平台驱动的名字一致才会匹配上 | |
.id = -1, | |
.dev = {.platform_data = &pdata,}, | |
}; | |
module_driver(mypdev, platform_device_register, platform_device_unregister); | |
MODULE_LICENSE("GPL"); |
正文完
发表至:无分类
2019-07-21