在内核中添加显示屏开关的设定
需求
dts文件中添加一个指定的gpio口来作为显示屏开关的控制,如果dts中该设备信息生效,在内核启动后给用户开启一个接口,让应用程序来完成对显示屏开关的控制(以达到随开随关的目的)。
起因:在实际应用中发现一种屏在使用时如果最开始没有确定的显示数据给到时,直接上电会出现随机的白点,且每次白点的数量和位置都随机。
实现要点
- 基本的gpio的操作,设计到gpio的注册,方向设定和导出(到用户空间)等;
- 到用户空间就是通过
sysfs
来进行连接的 - 在dts中需要创建内核代码能够搜寻到的路径,搜寻路径需要用到
of_
设备树的API - 以上添加的入口可以设置在板级设备信息的初始化过程中,即
arch/arm/mach-xxx/mach-yyyy.c
具体代码梳理
- DT_MACHINE_START
DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)")
/*
* i.MX6Q/DL maps system memory at 0x10000000 (offset 256MiB), and
* GPU has a limit on physical address that it accesses, which must
* be below 2GiB.
*/
.dma_zone_size = (SZ_2G - SZ_256M),
.smp = smp_ops(imx_smp_ops),
.map_io = imx6q_map_io,
.init_irq = imx6q_init_irq,
.init_time = imx6q_timer_init,
.init_machine = imx6q_init_machine, /* 已有,从这里进入 */
.init_late = imx6q_init_late,
.dt_compat = imx6q_dt_compat,
.restart = mxc_restart,
MACHINE_END
- imx6q_init_machine()
static void __init imx6q_init_machine(void)
{
struct device *parent;
mxc_arch_reset_init_dt();
parent = imx_soc_device_init();
if (parent == NULL)
pr_warn("failed to initialize soc device\n");
of_platform_populate(NULL, of_default_bus_match_table,
imx6q_auxdata_lookup, parent);
imx6q_enet_init();
imx_anatop_init();
imx6_pm_init();
imx6q_csi_mux_init();
my_display_sw_init(); /* 添加的点,具体的实现 */
}
- my_display_sw_init()
static int __init my_display_sw_init(void)
{
int ret_value = 0;
struct device_node *np = NULL;
int sw_gpio = 0;
int sw_gpio_flag = OF_GPIO_ACTIVE_LOW;
np = of_find_node_by_path("/display-switch"); /* dts中路径的寻找 */
if (!np) {
ret_value = -1;
goto label_out;
}
ret_value = of_device_is_available(np);
if (1 != ret_value) {
ret_value = -1;
goto label_out;
}
sw_gpio = of_get_named_gpio_flags(np, "display-sw-gpio", 0, &sw_gpio_flag); /* 获取节点属性,
gpio口编号、初始值 */
if (!gpio_is_valid(sw_gpio)) {
ret_value = -1;
goto label_out;
}
/*
ret_value = gpio_request_one(sw_gpio, GPIOF_DIR_OUT, "display-switch"); // 申请gpio资源并设置方向,
// 作用等同于下面两条
if (ret_value) {
printk("[ERROR] gpio_request_one\n");
goto label_out;
}*/
ret_value = gpio_request(sw_gpio, "display-switch"); // 申请gpio资源
if (ret_value) {
ret_value = -1;
goto label_out;
}
ret_value = gpio_direction_output(sw_gpio, sw_gpio_flag); // 设置方向为output,值为sw_gpio_flag
if (ret_value) {
ret_value = -1;
goto label_out;
}
ret_value = gpio_export(sw_gpio, 1); /* 在sysfs中导出为具体的文件,使可操作 */
if (ret_value) {
ret_value = -1;
goto label_out;
}
/*
gpio_set_value(sw_gpio, sw_gpio_flag); // 测试用
ret_value = gpio_get_value(sw_gpio); // 测试用
printk("***************ret_value: %d\n", ret_value);*/
label_out:
if (-1 != ret_value)
return 0;
else
return (-1);
}
- dts中书写
/ {
......
displaysw: display-switch {
display-sw-gpio = <&gpio1 20 GPIO_ACTIVE_HIGH>;
status = "okay";
};
......
};
总结
代码不多,涉及的文件不多,主要花的时间在具体事情的定位,努力,以及规避问题的解决方案确立上,后面的稳定性测试,极端测试那就是软件开发、嵌入式开发(题外话:估计任何一个行业,再简单一个事情,真正要做好,反复的检测都应该是必不可少的)的正常步骤了。