Android内核中判别启动模式
问题出现在驱动组描述了一个问题是内核的一个驱动中设置的电压值在正常模式和其它模式启动时电压值不能设置成一样,在正常模式中3.3v,其它模式3.0v。正常模式设置成3.3v是为了解决问题,但是这样也带来了一个问题在非正常模式(Recovery)启动时因此有附带的问题。
总归一句话:解决起来就是怎么让内核驱动时知道启动模式,来动态的设置电压值。
先从启动模式判别来说,知道Android判别启动方式一般是靠在Bootloader启动时检测 魔键(magic key)是否按下,然后传递给kernel不同的参数。知道一般kernel的参数在/proc/cmdline中存着呢?要想找出区别只能看它。但是在Recovery模式下没有cat等等命令可以用,那么cmdline中的内核就不好取出来。还好以前有调试内核的经验,知道在内核启动时会在串口中打印出来cmdline中的内容。有了内容就不差找出区别了。对比区别如下:
文字说明一下就是非正常模式启动时参数mtdparts中会多一个“[email protected](parameter),”;
如此一来,有这个参数的就是非正常模式,反之是正常模式。但是在内核驱动中如何判断呢,关于这个问题求助于了Linuxdrv群,群主给了方案,针对这个情况内核是有处理方案的,用__setup。在驱动中添加一个函数,注册到__setup中,这样内核在解析 内核参数列表 的时候,内核会将mtdparts参数的内容传递过来,在这个函数中做一下字符串匹配,设置一个全局变量normal_boot。等到加载驱动的时候,根据这个全局变量的来设置电压值,这样整体方案就有了。
方案理清交付驱动组。以下是我给出的demo:
/*
* Virtual TouchScreen driver
*
* Copyright (C) 2011 Niu Tao <[email protected]>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#define DRIVER_DESC "Virtual TouchScreen"
static struct input_dev *vts_dev;
#define VTS_MIN_XC 0
#define VTS_MAX_XC 320
#define VTS_MIN_YC 0
#define VTS_MAX_YC 480
static int __initdata vts_max_xc = VTS_MAX_XC;
static int __initdata vts_max_yc = VTS_MAX_YC;
#ifndef MODULE
static int __initdata vts_use = 0;
/**
* parse options,format must be vts=widthxheigth
*/
static int __init vts_setup(char *str)
{
char buf[64];
char *p;
int xc, yc;
vts_use = 1;
/* 本代码参考自:http://blog.chinaunix.net/uid-20729605-id-2876040.html
* 启动模式。正常模式:不包含"parameter";其它模式:包含"parameter".
* 使用内核中的__setup方法,注册后内核检测到有没有内容,如果有直接调用
* 判断str中有无"parameter"关键字,无说明正常启动,有说明其它模式启动
* if(isHad)
* normal_boot = 0;
* else
* normal_boot = 1;
* 在设置电压处时行normal_boot这个变量值的判断如下:
* if(normal_boot)
* 电压=3.3v;
*/
/*
strncpy(buf, str, sizeof(buf));
p = strchr(buf, 'x');
if (!p)
goto out;
*p = '\0';
xc = simple_strtoul(buf, NULL, 0);
yc = simple_strtoul(p + 1, NULL, 0);
if (!xc || !yc)
goto out;
vts_max_xc = xc;
vts_max_yc = yc;
return 1;
out:
printk(KERN_WARNING "vts: option format must be like 'vts=widthxheigth'. "
"use default config vts=%dx%d\n", vts_max_xc, vts_max_yc);
*/
return 0;
}
__setup("mtdparts=", vts_setup);
#endif
static int __init vts_init(void)
{
int err;
#ifndef MODULE
if (!vts_use)
return -ENODEV;
#endif
vts_dev = input_allocate_device();
if (!vts_dev) {
printk(KERN_ERR "vts: not enough memory\n");
err = -ENOMEM;
goto fail1;
}
vts_dev->name = DRIVER_DESC;
vts_dev->phys = "vts/input0";
vts_dev->id.bustype = BUS_VIRTUAL;
vts_dev->id.vendor = 0x0000;
vts_dev->id.product = 0x0000;
vts_dev->id.version = 0x0100;
vts_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
vts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(vts_dev, ABS_X, VTS_MIN_XC, vts_max_xc, 0, 0);
input_set_abs_params(vts_dev, ABS_Y, VTS_MIN_YC, vts_max_yc, 0, 0);
err = input_register_device(vts_dev);
if (err)
goto fail2;
return 0;
fail2:
input_free_device(vts_dev);
fail1:
return err;
}
module_init(vts_init);
#ifdef MODULE
static void __exit vts_exit(void)
{
input_unregister_device(vts_dev);
input_free_device(vts_dev);
}
module_exit(vts_exit);
#endif
MODULE_AUTHOR("Niu Tao <[email protected]>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
关于__setup见《__setup 在内核中的作用》。
关于__setup实例见《虚拟触摸屏驱动+如何通过启动参数给驱动程序传递参数 》。