1. 前言
在这篇文章仅仅是简单展示下一个gpio key driver是通过dts中定义的数据进行相关key的硬件初始化的,只是截图了部分相关的dts文件内容和驱动代码.
使用的平台:
MT8167
Android O
Kernel: 4.4
2. DTS中的gpio_keys 节点说明
gpio_keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&gpio_keys_default>;
lens_cover {
label = "lens_cover";
gpios = <&pio 10 GPIO_ACTIVE_LOW>;
linux,code = <KEY_F1>;
linux,input-type = <EV_KEY>;
};
};
关键属性的解释:
- 在Device Tree的Spec中有对于compatible的描述:
2.3.1. compatible
Property name: compatible
Value type: <stringlist>
Description:
The compatible property value consists of one or more strings that define the specific programming model for the device. This list of strings should be used by a client program for device driver selection. The property value consists of a concatenated list of null terminated strings, from most specific to most general. They allow a device to express its compatibility with a family of similar devices, potentially allowing a single device driver to match against several devices.
简单理解: 在这个案例中的compatible 是用于对应的driver和gpio_keys 节点定义设备的匹配,具体的原理在后面的文章中会讲述。
- gpio_keys下的子节点lens_cover解释
lens_cover {
label = "lens_cover";
# gpios这一属性定义了引脚和有效的电平,这在对应的驱动文件中会解析使用,进行gipo的初始化
gpios = <&pio 10 GPIO_ACTIVE_LOW>;
# linux,code 这一属性定义的是按键触发时对应需要上报的应用层的key code
linux,code = <KEY_F1>;
# linux,input-type这一属性定义了lens_cover这一按键注册到input时的按键类型
linux,input-type = <EV_KEY>;
};
另外对于gpio_keys 这一节点 你可能还会有疑问? 为什么还会有一个子节点lens_cover。
在这个案例中lens_cover要定义的是镜头盖的拨动开关(可以理解为一个按键),对于gpio_keys这一大类来说它下面可能会包含多种类型的按键,所以这里就将lens_cover定义为了gpio_keys的子节点。
3. gpio drirver 加载对应的dts数据进行硬件初始化
首先看下gpio_keys_probe中调用了gpio_keys_get_devtree_pdata进行对应device data的获取, gpio driver是如何识别到gpio_key driver所对应的dts 节点 在后续文章中会详细说明。
// kernel-4.4/drivers/input/keyboard/gpio_keys.c
static int gpio_keys_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct gpio_keys_drvdata *ddata;
struct input_dev *input;
size_t size;
int i, error;
int wakeup = 0;
if (!pdata) {
/*
在gpio_keys调用gpio_keys_probe进行驱动的初始化时,首先需要获取到driver所对应的platform data,在未支持dts前 platform data是固化在代码中的,而在支持dts后是通过读取的对应dts中定义的device data进行相关pdata的赋值。
*/
pdata = gpio_keys_get_devtree_pdata(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
...
}
在gpio_keys_get_devtree_pdata中会将加载到内存的dts数据转换为paltform_data, 对于系统是如何将dts转换为驱动可以解析的内容, 在后续文章中会详细说明。
/*
* Handlers for alternative sources of platform_data
*/
#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
struct device_node *node, *pp;
struct gpio_keys_platform_data *pdata;
struct gpio_keys_button *button;
int error;
int nbuttons;
int i;
// 这里所赋值的就是dts中定义的gpio_keys 节点
node = dev->of_node;
if (!node)
return ERR_PTR(-ENODEV);
//获取gpio_keys 下有多少个子节点,在这个案例只存在lens_cover 这一个设备
nbuttons = of_get_child_count(node);
if (nbuttons == 0)
return ERR_PTR(-ENODEV);
pdata = devm_kzalloc(dev,
sizeof(*pdata) + nbuttons * sizeof(*button),
GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
pdata->nbuttons = nbuttons;
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
i = 0;
// 这里就是循环去读取子节点中的属性
// 在这里就只是处理了lens_cover
for_each_child_of_node(node, pp) {
enum of_gpio_flags flags;
button = &pdata->buttons[i++];
button->gpio = of_get_gpio_flags(pp, 0, &flags);
if (button->gpio < 0) {
error = button->gpio;
if (error != -ENOENT) {
if (error != -EPROBE_DEFER)
dev_err(dev,
"Failed to get gpio flags, error: %d\n",
error);
return ERR_PTR(error);
}
} else {
button->active_low = flags & OF_GPIO_ACTIVE_LOW;
}
button->irq = irq_of_parse_and_map(pp, 0);
if (!gpio_is_valid(button->gpio) && !button->irq) {
dev_err(dev, "Found button without gpios or irqs\n");
return ERR_PTR(-EINVAL);
}
// 这里对应到了lens_cover 中的linux,code = <KEY_F1>;
if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n",
button->gpio);
return ERR_PTR(-EINVAL);
}
// 这里对应到了lens_cover 中的label = "lens_cover";
button->desc = of_get_property(pp, "label", NULL);
// 这里对应到了lens_cover 中的linux,input-type = <EV_KEY>;
if (of_property_read_u32(pp, "linux,input-type", &button->type))
button->type = EV_KEY;
button->wakeup = of_property_read_bool(pp, "wakeup-source") ||
/* legacy name */
of_property_read_bool(pp, "gpio-key,wakeup");
button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
if (of_property_read_u32(pp, "debounce-interval",
&button->debounce_interval))
button->debounce_interval = 5;
}
if (pdata->nbuttons == 0)
return ERR_PTR(-EINVAL);
return pdata;
}
通过上述的DTS中定义的节点(device node)与对应的驱动gpio_key的部分代码,可以更容易的理解:
所谓的dts实质上只是一个定义device data的规范,然后对应的驱动基于这个规范去读取加载到内存中的device data进行一系列的初始化动作。
对于Android的kernel是如何加载dts文件?driver是如何匹配到对应的device data的?在后续的文章中会详细的描述。