驱动调试之打印

发布者:EtherealBeauty最新更新时间:2024-08-19 来源: cnblogs关键字:驱动调试  打印  ARM 手机看文章 扫描二维码
随时随地手机看文章

在编写驱动过程分析中会遇到许多难找的问题,这时候调试的方法就很重要了,下面介绍的是利用打印的方法调试驱动,这种方法同样可以用在应用的调试过程中,而且很有效。


1、prink的原理


首先介绍一下打印的函数prink的原理,printk的原理是最终打印在终端上的。所以只要是能成为终端的设备均可被打印,比如串口、网络、LCD等等。


在u-boot的启动参数中,有这么一项console=ttySAC0,其中ttySAC0就是最终printk打印到的设备。


bootargs=console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.101:/work/nfs_andy/first_fs ip=192.168.1.18:192.168.1.101:192.168.1.1:255.255.255.0::eth0:off

 


1.1、__setup调用过程分析


为了分析prink,可以在内核源码中搜索“console=”,最终可以在在kernelprintk.c中找到__setup('console=', console_setup);这种形式的定义在Linux内核源码阅读记录一之分析存储在不同段中的函数调用过程中已经介绍过,下面介绍一遍调用过程,__setup被定义在includelinuxinit.h中:


171 #define __setup(str, fn)                   

172     __setup_param(str, fn, fn, 0)


160 #define __setup_param(str, unique_id, fn, early)           

161    static char __setup_str_##unique_id[] __initdata = str;   

162     static struct obs_kernel_param __setup_##unique_id   

163        __attribute_used__               

164        __attribute__((__section__('.init.setup')))   

165        __attribute__((aligned((sizeof(long)))))   

166        = { __setup_str_##unique_id, fn, early }



148 struct obs_kernel_param {

149    const char *str;

150    int (*setup_func)(char *);

151    int early;

152 };


最终把__setup('console=', console_setup);展开可以得到:从展开的函数可以知道,最终定义了一个位于.init.setup段的结构体__setup_console_setup,并且初始化了它的各个成员,有函数,有名称等等。


static char __setup_str_console_setup[] __initdata = 'console=';  


 static struct obs_kernel_param __setup_console_setup    

 __attribute_used__                

 __attribute__((__section__('.init.setup')))   

  __attribute__((aligned((sizeof(long)))))   

    __setup_str_console_setup, 

    console_setup,

     0

}


接着在arch/arm/kernel/vmlinux.lds中搜索.init.setup,可以得到这个段的初始化地址与结束地址__setup_start、__setup_end


  __setup_start = .;

   *(.init.setup)

  __setup_end = .;

为了得到调用这个段的时机,我们继续接着在内核源码中搜索__setup_start,在initmain.c中的obsolete_checksetup函数搜索到了它,obsolete_checksetup这个函数的第6行开始会根据__setup_console_setup结构体中的str字符串值与传入的line字符串值是否相等以及early参数来决定是否调用__setup_console_setup结构体中的函数。


 1 static int __init obsolete_checksetup(char *line)

 2 {

 3     struct obs_kernel_param *p;

 4     int had_early_param = 0;

 5 

 6     p = __setup_start;//.init.setup的首地址

 7     do {

 8         int n = strlen(p->str);

 9         if (!strncmp(line, p->str, n)) {//在.init.setup中寻找相符的命令行参数

10             if (p->early) {//如果early大于0,那么这个参数在前面已经处理过了

11                 /* Already done in parse_early_param?

12                  * (Needs exact match on param part).

13                  * Keep iterating, as we can have early

14                  * params and __setups of same names 8( */

15                 if (line[n] == '' || line[n] == '=')

16                     had_early_param = 1;

17             } else if (!p->setup_func) {//如果处理函数不存在,则报错

18                 printk(KERN_WARNING 'Parameter %s is obsolete,'

19                        ' ignoredn', p->str);

20                 return 1;

21             } else if (p->setup_func(line + n))//调用处理函数处理

22                 return 1;

23         }

24         p++;

25     } while (p < __setup_end);

26 

27     return had_early_param;

28 }


到这里我们直接罗列出obsolete_checksetup的调用过程,还是从start_kernel开始:


1 start_kernel

2     parse_args('Booting kernel', static_command_line, __start___param, __stop___param - __start___param,&unknown_bootoption);//后续的命令处理

3         unknown_bootoption

4             obsolete_checksetup

继续往下分析parse_args函数,这个函数会找出命令参数,然后调用parse_one函数处理


int parse_args(const char *name,

           char *args,

           struct kernel_param *params,

           unsigned num,

           int (*unknown)(char *param, char *val))

{

    char *param, *val;


    DEBUGP('Parsing ARGS: %sn', args);


    /* Chew leading spaces */

    while (*args == ' ')

        args++;


    while (*args) {//循环处理剩余的命令行,直到全部处理完成

        int ret;

        int irq_was_disabled;


        args = next_arg(args, ¶m, &val);//找出下一个命令行参数*param为命令名称,*val为参数值

        irq_was_disabled = irqs_disabled();

        ret = parse_one(param, val, params, num, unknown);//处理param为

        if (irq_was_disabled && !irqs_disabled()) {

            printk(KERN_WARNING 'parse_args(): option '%s' enabled '

                    'irq's!n', param);

        }

        switch (ret) {

        case -ENOENT:

            printk(KERN_ERR '%s: Unknown parameter `%s'n',

                   name, param);

            return ret;

        case -ENOSPC:

            printk(KERN_ERR

                   '%s: `%s' too large for parameter `%s'n',

                   name, val ?: '', param);

            return ret;

        case 0:

            break;

        default:

            printk(KERN_ERR

                   '%s: `%s' invalid for parameter `%s'n',

                   name, val ?: '', param);

            return ret;

        }

    }


    /* All parsed OK. */

    return 0;


继续分析parse_one函数,可以看到,最终调用了obsolete_checksetup处理函数。在obsolete_checksetup会处理相应的命令行参数


static int parse_one(char *param,

             char *val,

             struct kernel_param *params, 

             unsigned num_params,

             int (*handle_unknown)(char *param, char *val))

{

    unsigned int i;


    /* Find parameter */

    for (i = 0; i < num_params; i++) {//从__param段找出与命令行参数相同的名字

        if (parameq(param, params[i].name)) {

            DEBUGP('They are equal!  Calling %pn',

                   params[i].set);

            return params[i].set(val, ¶ms[i]);//如果是内核的参数,那么直接传给内核参数,然后返回。

        }

    }


    if (handle_unknown) {//如果不是内核的参数,并且处理函数存在

        DEBUGP('Unknown argument: calling %pn', handle_unknown);

        return handle_unknown(param, val);//调用处理函数处理

    }


    DEBUGP('Unknown argument `%s'n', param);

    return -ENOENT;

}


1.2、控制台设置函数console_setup分析


介绍完了__setup的调用过程吗,接下来分析console_setup函数:


static int __init console_setup(char *str)

{

    char name[sizeof(console_cmdline[0].name)];

    char *s, *options;

    int idx;


    /*

     * Decode str into name, index, options.

     */

    if (str[0] >= '0' && str[0] <= '9') {//如果以数字0-9开头

        strcpy(name, 'ttyS');

        strncpy(name + 4, str, sizeof(name) - 5);

    } else {

        strncpy(name, str, sizeof(name) - 1);//将str拷贝到name中,去除结束符

    }

    name[sizeof(name) - 1] = 0;

    if ((options = strchr(str, ',')) != NULL)//如果参数中存在,的话。说明带波特率参数

        *(options++) = 0;

#ifdef __sparc__

    if (!strcmp(str, 'ttya'))

        strcpy(name, 'ttyS0');

    if (!strcmp(str, 'ttyb'))

        strcpy(name, 'ttyS1');

#endif

    for (s = name; *s; s++)

        if ((*s >= '0' && *s <= '9') || *s == ',')

            break;

    idx = simple_strtoul(s, NULL, 10);//取出波特率参数,转换成整形

    *s = 0;


    add_preferred_console(name, idx, options);//将参数保存在console_cmdline中

    return 1;

}


在console_setup这个函数的最后会调用console_cmdline将参数保存在console_cmdline中。假设我想用名为'ttySAC0'的控制台,先记录下来放到console_cmdline这个全局变量中,接着搜索'console_cmdline'可以找到register_console这个函数。


分析到这里,大胆的假设,如果我想要printk打印到某个设备上,那么这个设备需要调用register_console注册控制台,并且注册的名字需要与命令行参数传入的名字相同。为了验证这个假设,接着搜索register_console这个函数,看看有哪些设备驱动调用了它。在driversserials3c2410.c文件中找到了它:可以看到,s3c24xx_serial_console 结构体中的名字与命令行传入的名字相符,所以prink最终可以调用s3c24xx_serial_console结构体中的write函数打印到串口输出。

[1] [2]
关键字:驱动调试  打印  ARM 引用地址:驱动调试之打印

上一篇:Linux内核源码阅读记录一之分析存储在不同段中的函数调用过程
下一篇:Linux驱动之一个简单的输入子系统程序编写

推荐阅读最新更新时间:2024-11-13 17:19

第1天-ARM汇编指令B/BL
第1天-ARM汇编指令 B : 分支 (Branch) B{条件} 地址 B 是最简单的分支。一旦遇到一个 B 指令,ARM 处理器将立即跳转到给定的地址,从那里继续执行。注意存储在分支指令中的实际的值是相对当前的 R15 的值的一个偏移量;而不是一个绝对地址。它的值由汇编器来计算,它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(+/- 32 M)。 在其他处理器上,你可能经常见到这样的指令: OPT 1 LDA &70 CMP #0 BEQ Zero STA &72 .Zero RTS (取自 Acorn Electron User Guide issue 1 page 213)
[单片机]
ARM CEO:物联网市场爆发 1,000亿枚芯片将采ARM设计
去年被软件银行收购的英国芯片设计大厂ARM执行官向日经新闻表示,拜物联网(IoT)相关需求大增所赐,未来四年以ARM技术打造的芯片出货量将达1,000亿枚,数量为公司有史以来之最。下面就随网络通信小编一起来了解一下相关内容吧。 ARM CEO:物联网市场爆发 1,000亿枚芯片将采ARM设计 ARM执行官席格斯向日经新闻透露,公司已转向专攻物联网技术,未来四年将1,000亿枚ARM技术的芯片将出货。 ARM执行官席格斯看好物联网相关产品的未来,正在将公司重心从智能手机相关产品转移到这方面。席格斯表示,目前正在和美国高通公司及中国华为技术公司一起开发服务器芯片。随着物联网的发展,这些产品需求已一飞冲天。 席格斯说:“我们打
[网络通信]
基于ARM和FPGA实时图像采集传输系统的设计
图像处理技术的快速发展,使得图像采集处理系统在提高农业生产自动化程度中的应用越来越广泛。目前的图像采集系统有的基于CCD摄像机、图像采集卡和计算 机,有的基于CCD摄像机、解码器、FPGA和DSP,而有的基于CMOS图像传感器芯片、FPGA和DSP,它们在实时性,灵活性,可维护性方面各有优 缺点。而在农业生产中,以基于CCD摄像机、图像采集卡和计算机的系统居多。本文结合实际系统中的前端图像处理和图像数据传输需要.充分利用ARM的灵活 性和FPGA的并行性特点,设计了一种基于ARM+FPGA的图像快速采集传输系统。所选的ARM (Ad-vanced RISC Machines)体系结构是32位嵌入式RISC微处理器结构,该微处理器拥
[单片机]
基于<font color='red'>ARM</font>和FPGA实时图像采集传输系统的设计
工程师3D打印丰田汽车引擎与变速箱
    加州一名机械工程师埃里克•哈勒尔,使用3D打印机打印了一台可以工作的丰田22RE引擎,其中包括可以真正工作的发动机和5速手动变速箱,这两个部件可以连接在一起。他现在所需要的是3D打印出一部迷你汽车,好和这台3D打印的发动机配套。他表示,5速手动变速箱需要约48小时打印出来并且进行组装。     埃里克·哈勒尔也将自己的打印图纸和文件放到互联网上与他人分享。他甚至会解答其他人在3D打印发动机和变速箱过程中遇到的问题,但是他表示,这些3D打印汽车引擎的人需要有实际汽车维修经验,才能将3D打印出来的零部件正确组装起来。     不过这种变速箱当中某些零部件3D打印无法完成,还是要买成品,比如传输用的专门毫米棒,623
[汽车电子]
玩转ARM 的基本功
我是稀里糊涂的走上了嵌入式开发的道路,回想起来,多亏了身边的几位朋友,是在与他们的闲侃中慢慢的走上了这条道路:从最早的电路设计,PCB打板,电路调试,模拟仿真,单片机开发到后来的ARM开发,回想起来,心里也是诚惶诚恐,如履薄冰。 我开始做电子是用的AVR单片机,从I/O、按键…到内存、外设玩了一通。工作之后,才开始玩ARM。在项目中逐渐对ARM开发有了一些感悟。 学习ARM有两个目的,看你是那种:1、长见识,顺便玩玩;2、工作需要,从事ARM开发。这就要分开对待了。 如果是长见识,随便玩玩,为日后的工作做准备,那么就应该挑选市场上很成熟的ARM7、ARM9等资料比较多,玩的人比较多,出现问题容易解决;如果是工作需要,就
[单片机]
玩转<font color='red'>ARM</font> 的基本功
首款ARM MacBook或用12英寸屏蝶式键盘也将回归
近日,围绕苹果第一台基于 ARM MacBook 的更多细节已被曝光。此前,有消息称这款机器将配备 12 英寸显示屏,起售价为 799 美元,这使其成为一款极具竞争力的机器。 推特爆料者 Komiya 提供了有关 ARM MacBook 的最新消息,其中包括带回蝶式键盘。根据他的最新推文,即将上市的 ARM MacBook 配备了 12 英寸的 Retina 显示屏,但只有一个 USB-C 端口。这可能表明苹果会使用与停产的 12 英寸 MacBook 相同的外壳。 此外,他之前还提到这款 12 英寸 MacBook 将搭载苹果的 A14X 仿生芯片,并具有惊人的 20 小时电池续航。不过最新的推文没有提及该机器会使用哪款
[手机便携]
DesignStart大升级:更便宜更好用
近日,在DesignStart大获成功之后,ARM宣布再度升级该项目,从之前只提供Cortex-M0内核扩展至Cortex-M3内核。“这也是为了相应软银总裁孙正义在2016年秋季举办的ARM TechCon上对未来的判断:既全球将会有一万亿个互联设备出现。”Phil Burr表示:“为了实现这样一个宏大愿景,我们确实需要一些支撑的基础,包括高效的技术,成熟的架构支撑以及广泛的生态系统。作为软银一部分,ARM迈出了更加大胆的一步,让外部的开发人员、厂商更加容易获得ARM技术,我们也期望借此帮助他们更好地开发出定制化、全新的方案。” DesignStart这一项目已持续几年,取得的成效也非常显著,短短两年时间在DesignStart
[半导体设计/制造]
DesignStart大升级:更便宜更好用
MarS Board- 你不知道的ARM DIY 计算机平台
随着Raspberry Pi的火热,各种卡片式电脑如雨后春笋的出现,CPU由AM11架构到Cortex-A8再到Cortex-A9架构,单核到多核,系统覆盖Linux、Ubuntu、Android,客户创意越来越多,需求越来越高。在这种环境下,超级ARM DIY计算机平台MarS Board应运而生。   MarS Board是深圳市英蓓特科技有限公司推出的基于飞思卡尔(Freescale Semiconductor)i.MX 6Dual处理器的超级ARM DIY计算机平台。i.MX 6Dual处理器集成了高达1GHz的 ARM Cortex™-A9 内核、2D和3D图形处理器和3D 1080p视频处理器。MarS Board具
[模拟电子]
MarS Board- 你不知道的<font color='red'>ARM</font> DIY 计算机平台
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved