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++;

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

相关文章

    总线接口,通过对嵌入式Linux系统RS232驱动程序的修改,使的在通过该修改后的串口驱动程序发送数据时,自动控制IO来实现RS485通信的方向控制,从而简化了RS485通信的控制流程,Linux下......
    低廉等优点在各种工业场合得到广泛的应用。设计使用ARM9处理器S3C2440内部集成的UART外设和RSM485模块构建具有电源隔离、电气隔离、总线保护的RS485总线接口,通过对嵌入式Linux系统RS232驱动程序的修改,使的在通过该修改后的串口驱动程序......
    linux的串口驱动分析;1、串口驱动中的数据结构 • UART驱动程序结构:struct uart_driver  驱动 • UART端口结构: struct uart_port  串口......
    电缆是八针的选型接头,与 RS232 的 DB9 接线如下图所示: 将自制电缆与PLC连接。 3.参数设置 GX Developer编程软件下载参数如下图所示,串口与插入PC的串口驱动......
    这样交叉着连接,如果电脑没有RS232口 只有USB口,可以用串口转接线转出串口。 这个时候在电脑上位机上需要安装串口驱动程序。 注意,这个驱动程序驱动的是PL2303芯片(在上图的大头里面) 使得......
    。 这个时候在电脑上位机上需要安装串口驱动程序。 注意,这个驱动程序驱动的是PL2303芯片(在上图的大头里面) 使得RS232信息转换成USB信息。 下图......
    交叉着连接,如果电脑没有RS232口 只有USB口,可以用串口转接线转出串口,如下图所示。 这个时候在电脑上位机上需要安装串口驱动程序。 注意,这个驱动程序驱动的是PL2303芯片(在上图的大头里面) 使得......
    电脑没有RS232口 只有USB口,可以用串口转接线转出串口,如下图所示。 这个时候在电脑上位机上需要安装串口驱动程序。 注意,这个驱动程序驱动的是PL2303芯片(在上图的大头里面) 使得......
    控制器的通信协议的具体分析 由于公司的商业原因,不提供源程序和通信协议,也没有HMS30C7202的驱动程序,我们借助串口调试助手和存储示波器分析,得出了串口触摸屏控制器的通信协议,其传输速率为9600,一帧......
    现:① 为温度变送器编写内核驱动程序;② 编写温度数据采集应用程序,通过串口获取温度数据并进行相应的EPA报文打包处理;③ 利用无线网络将处理数据发送给上位机。前面提到系统平台上运行的是ARM Linux......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>