调试环境------
CPU:freescale i.mx6
操作系统:Linux
内核版本号:3.0.35
最近在调试一个spi nor flash芯片的时候用到了gpio倒出的功能,就是可以在linux起来以后,通过串口倒出,并可以控制输入输出方向和高低电平的一种功能。配置好对应的pin后发现怎么都不能在/sys/class/gpio的目录下生成相应的接口目录,我要倒出的GPIO是gpio bank6的第11教,根据imx6平台gpio编号的计算方法IMX_GPIO_NR(6, 11),得到这个GPIO的编号是174(有兴趣的同学可以再imx6的平台代码上查看IMX_GPIO_NR这个宏的实现方法),通过如下命令实现倒出:
echo 174 > /sys/class/gpio/export
如果顺利倒出的话,会在gpio目录下生成gpio174这样的目录(/sys/class/gpio/gpio174),这个目录底下的接口可以对相应的gpio进行控制
执行了上述命令以后,没有生成gpio174这个目录,只好查看gpio_export函数实现,大概看了一下,整个倒出过程在关键步骤加了pr_debug调用,要用这个宏打印log可以通过DEBUG宏打开,具体打开方法如下:
1. 在内核根目录执行makemenuconfig
2. 进入内核配置图形界面以后查找
并选中
3. 退回到根菜单进入
选中
4. 重新编译uImage,烧录
5. 现在还不能实现pr_debug的打印,还要降低prink的打印级别:
echo 8 > /proc/sys/kernel/printk
再执行gpio倒出,于是打印了下面的log:
在代码/drivers/gpio/gpiolib.c中找到了这个错误打印的位置
if(status)
pr_debug("gpio_request:gpio-%d (%s) status %d\n",
gpio,label ? : "?", status);
在status不为0的情况下打印出来的,status是-16,经过查询-16是EBUSY的errorcode,EBUSY解释是:
#define EBUSY 16 /* Device or resource busy */
这个gpio可能是被哪个设备占用了,所以导致倒出失败,查bsp代码还真是,注释调以后让我来用,再编译运行果然成功了,看来这个pr_debug还是挺有用的。
但是printk的级别控制引起了我的好奇心,为什么echo比7大的数值才能使用pr_debug呢?带着这个疑问开始看printk相关的内核代码:
先从printk的读写控制入手,看看proc文件系统中是怎么实现对printk节点进行读写的:
/fs/proc/proc_sysctl.c中proc_sys_init函数建立/proc/sys/目录,kernel目录是在/kernel/sysctl.c中structctl_table root_table中定义的,具体定义如下:
static struct ctl_table root_table[] = {
{
.procname = "kernel",
.mode = 0555,
.child = kern_table,
},
kern_table包含了一对kernel目录下的子节点的定义,当然也包含了printk节点的定义:
{
/* 节点名称 */
.procname = "printk",
/* 控制台log等级 */
.data = &console_loglevel,
.maxlen = 4*sizeof(int),
/* 节点权限 */
.mode = 0644,
/* 操作节点的回调函数 */
.proc_handler = proc_dointvec,
},
其中有两个变量是比较重要的console_loglevel保存log级别,其中/kernel/printk.c源码中有如下定义:
#defineMINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#defineDEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
intconsole_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */
DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */
MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */
DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */
};
头文件/inclue/linux/printk.h对console_loglevel做了定义:
#defineconsole_loglevel (console_printk[0])
#definedefault_message_loglevel (console_printk[1])
#defineminimum_console_loglevel (console_printk[2])
#definedefault_console_loglevel (console_printk[3])
可以看到console_loglevel的值默认为7
proc_handler变量保存的函数指针proc_dointvec做了操作节点的工作,调用过程是 proc_dointvec->do_proc_dointvec->__do_proc_dointvec, 这个调用过程是多个kernel目录下的节点共用的,我以printk为例说明一下log级别是如何别改写的ÿ