tiny4412 串口驱动分析二 --- printk的实现

2023-06-25  

开发板:tiny4412ADK+S700 4GB Flash

主机:Wind7 64位

虚拟机:Vmware+Ubuntu12_04

u-boot:U-Boot 2010.12

Linux内核版本:linux-3.0.31

Android版本:android-4.1.2

 

源码:kernel/printk.c

asmlinkage int printk(const char *fmt, ...)

{

         va_list args;

         int r;


         va_start(args, fmt);

         r = vprintk(fmt, args);

         va_end(args);

         return r;

}


asmlinkage int vprintk(const char *fmt, va_list args)

{

         int printed_len = 0;

         int current_log_level = default_message_loglevel;


/*

在include/linux/printk.h中:

#define default_message_loglevel (console_printk[1])

在kernel/printk.c中:

int console_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

};


#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL


在.config中:

#define CONFIG_DEFAULT_MESSAGE_LOGLEVEL 4

*/


         unsigned long flags;

         int this_cpu;

         char *p;

         size_t plen;

         char special;


/*

 由于没有定义宏CONFIG_BOOT_PRINTK_DELAY,所以boot_delay_msec是空函数

*/

         boot_delay_msec();

/*

printk_delay这个函数中判断变量printk_delay_msec的值,它的初始值是0

static inline void printk_delay(void)

{

         if (unlikely(printk_delay_msec)) {

                   int m = printk_delay_msec;


                   while (m--) {

                            mdelay(1);

                            touch_nmi_watchdog();

                   }

         }

}


那么在什么地方设置这个值呢?通过分析代码发现,是通过写文件/proc/sys/kernel/printk_delay实现的,可以执行如echo “44” > /proc/sys/kernel/printk_delay 来改变这个变量的值

在文件kernel/sysctl.c中:

static struct ctl_table kern_table[] = {

……

         {

                   .procname        = "printk_delay",

                   .data                  = &printk_delay_msec,

                   .maxlen             = sizeof(int),

                   .mode                = 0644,

                   .proc_handler  = proc_dointvec_minmax,

                   .extra1              = &zero,

                   .extra2              = &ten_thousand,

         },

……

}


当向文件/proc/sys/kernel/printk_delay中写入数据,函数proc_dointvec_minmax会调用,然后将这个数字赋值给printk_delay_msec


static struct ctl_table root_table[] = {

         {

                   .procname        = "kernel",

                   .mode                = 0555,

                   .child                  = kern_table,

         },

……

}


内核在启动时会同一处理root_table,然后再/proc下生成相应的文件,方便内核调试

*/

         printk_delay();

         preempt_disable(); // 关抢占


         /* This stops the holder of console_sem just where we want him */

         raw_local_irq_save(flags);

         this_cpu = smp_processor_id();  //读取当前cpu核的id号

         /*

          * Ouch, printk recursed into itself! 处理printk中再次调用printk的情况

          */

         if (unlikely(printk_cpu == this_cpu)) {

                  /*

                    * If a crash is occurring during printk() on this CPU,

                    * then try to get the crash message out but make sure

                    * we can't deadlock. Otherwise just return to avoid the

                    * recursion and return - but flag the recursion so that

                    * it can be printed at the next appropriate moment:

                    */


                   if (!oops_in_progress) {

                            recursion_bug = 1;

                            goto out_restore_irqs;

                   }

                  zap_locks();

         }


         lockdep_off();

         spin_lock(&logbuf_lock);

         printk_cpu = this_cpu;


         if (recursion_bug) {

                   recursion_bug = 0;

/*

static const char recursion_bug_msg [] =

                   KERN_CRIT "BUG: recent printk recursion!n";

如果在printk中继续调用printk,上面这句就会不断出现,可以试试

*/

                   strcpy(printk_buf, recursion_bug_msg);  // 缓存入printk_buf,待下面处理

                   printed_len = strlen(recursion_bug_msg);

         }

         /* Emit the output into the temporary buffer

先将要打印的字符串缓存入printk_buf中

*/

         printed_len += vscnprintf(printk_buf + printed_len,

                                       sizeof(printk_buf) - printed_len, fmt, args);


/*

         如果配置和宏CONFIG_DEBUG_LL的话,

         这个以后分析,printascii是一个用汇编实现的函数,在文件arch/arm/kernel/debug.S中

他会直接将传入的printk_buf参数中的内容打印出来

*/

#ifdef        CONFIG_DEBUG_LL

        printascii(printk_buf);

#endif

         p = printk_buf;  // 将临时缓冲区的首地址赋给指针p


/*

Read log level and handle special printk prefix

这个函数会解析printk_buf的前三个字符,因为一般我们在驱动中使用printk的时候会加一个前缀如 printk(KERN_DEBUG “hello worldn”); 其中KERN_DEBUG宏展开后就是 “<7>”

这个函数的目的就是解析这里的 ”<7>”,会把解析出来的数字7赋值给current_log_level

如果是””或者””,这把字符c或者d赋值给special

上面两种情况下,返回值是3,也就是字符串””的长度,赋值返回0,同时也不给current_log_level和special赋值

*/


         plen = log_prefix(p, &current_log_level, &special);

         if (plen) {  // 如果是类似printk(“hello world”);则plen是0,这个if条件不成立

                   p += plen;  // 跳过printk_buf开头的 “

                   switch (special) {  // 一般不会是 ‘c’ 或者 ‘d’

                   case 'c': /* Strip KERN_CONT, continue line */

                            plen = 0;

                            break;

                   case 'd': /* Strip KERN_DEFAULT, start new line */

                            plen = 0;

                   default:

                            if (!new_text_line) {  // 这个变量初始值是1

                                     emit_log_char('n');


/*

#define CONFIG_LOG_BUF_SHIFT 17   在make menuconfig的时候可以修改

#define __LOG_BUF_LEN         (1 << CONFIG_LOG_BUF_SHIFT)

static char __log_buf[__LOG_BUF_LEN] __nosavedata; // 定义了128KB的log缓冲区

static int log_buf_len = __LOG_BUF_LEN;  // 128K

static char *log_buf = __log_buf;

#define LOG_BUF_MASK (log_buf_len-1)

#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])  // 获得log_buf中第idx个字符,这个技巧保证了log_buf中的内容可以以环形缓冲区的形式存放,不至于溢出

static void emit_log_char(char c) // 将c表示的字符存入log_buf中,并调整相关索引值

{

         LOG_BUF(log_end) = c; // 存入 log_buf[log_end&(128k-1)]

         log_end++;

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。