文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>杂三

杂三

时间:2009-06-26  来源:qingfenglala

http://www.mjmwired.net/kernel/Documentation/kernel-doc-nano-HOWTO.txt

比如: drivers/rtc/rtc-pxa.c 中写:

/**

 * pxa_rtc_probe() - probe function for pxa micco driver

 * @pdev:      device structure from bus driver

 *

 * 1. set gpio0_2 as gpio mode and in direction.

 *

 * 2. register rtc driver to rtc framework

 *

 * 3. init work queue which will be used in interrupt handler

 *

 **/

static int pxa_rtc_probe(struct platform_device *pdev)

{

工具生成:

./scripts/kernel-doc -html drivers/rtc/rtc-pxa.c  > /mnt/hgfs/share1/rtc-pxa.htm

最后获得:

pxa_rtc_probe - probe function for pxa micco driver

int pxa_rtc_probe (struct platform_device * pdev)

Arguments

pdev

device structure from bus driver

Description

1. set gpio0_2 as gpio mode and in direction.

2. register rtc driver to rtc framework

3. init work queue which will be used in interrupt handler







天写 rtc 驱动, rtc 寄存器使用了 bcd 编码, 于是写了一个宏进行转换:

#define FROMBCD8(val)   (((val >> 4)  & 0xf) * 10 + (val & 0xf))

 

然后为了获取某个寄存器,比如小时, 是这么调用的:

tm->tm_hour     = FROMBCD8(micco_read(RTC_HOUR_REG));

 

但是, micco_read 的打印语句表明该函数被调用了两次!

 

仔细研究原来这是由于宏的副作用导致的!

宏扩展后变为:

tm->tm_hour     =  (((micco_read(RTC_HOUR_REG)>> 4)  & 0xf) * 10 + (micco_read(RTC_HOUR_REG)& 0xf))

 

这导致了低效(有时是错误)的代码!micco_read 可是使用低速 i2c 在访问寄存器!

 

修改为:

u8 val;

val = micco_read(RTC_HOUR_REG);

tm->tm_hour     = FROMBCD8(val);

 

即可!

 

该实例,表明宏的缺陷!正确的方法是应该使用 inline 函数, 少使用宏。

上面的宏改为:

static u8 inline frombcd8(u8 val)

{

        return ((val >> 4)  & 0xf) * 10 + (val & 0xf);

}

 

然后, 调用改为:

tm->tm_hour     = frombcd8 (micco_read(RTC_HOUR_REG));

也没有问题, 而且代码更加简洁。






开机过程中的内核打印

作者: zjujoe 转载请注明出处

Email:[email protected]

BLOG:http://blog.csdn.net/zjujoe

 

前言

嵌入式开发中, 通常使用串口输出调试信息,了解运行状态。 内核启动过程中,在不同阶段会通过不同的方式将调试信息输出到串口。 (注:以下内容针对 arm-linux.)

解压缩阶段

解压缩阶段内核会输出:

Uncompressing Linux................................ done, booting the kernel.

 

查找内核, 会发现如上输出是在如下语句中打印的:

 312        putstr("Uncompressing Linux...");
 313        gunzip();
 314        putstr(" done, booting the kernel\n");

 

putstr 会调用 uncompress.h 中的 putc 来完成任务。

 

通常, 嵌入式开发中,我们假定 bootloader 已经进行了串口初始化, 所以我们只需要对串口进行写操作就可以了。比如:

 

  18static void putc(char c)
  19{
  20        volatile u32 *uart = (volatile void *) DAVINCI_UART0_BASE;
  21
  22        while (!(uart[UART_LSR] & UART_LSR_THRE))
  23                barrier();
  24        uart[UART_TX] = c;
  25}

 

实现函数,就可以看到串口输出了。当然, 如果您是完美主义者, 还应该实现:

 

  27static inline void flush(void)
  28{
  29        volatile u32 *uart = (volatile void *) DAVINCI_UART0_BASE;
  30        while (!(uart[UART_LSR] & UART_LSR_THRE))
  31                barrier();
  32}

 

Image 启动早期

这里的“早期”定义为串口驱动初始化之前。 这里又分为两个阶段:汇编阶段与 C 语言阶段。

 

汇编阶段

在启动正常后一般不需要在此阶段输出信息。但是,如果系统还没有正常启动, 则需要在汇编里向串口输出一些信息。

 

内核提供了函数:
 116ENTRY(printascii)
 
为了使用它,我们需要实现:
addruart 获得调试串口的地址
senduart 发送一个字节
waituart 等待串口可用
三个汇编函数。 

 

可以参考某一平台来实现, 比如:

http://lxr.linux.no/linux+v2.6.27/arch/arm/mach-pxa/include/mach/debug-macro.S#L16

 

pxa 的实现表明, 由于 uart 通常是8250 兼容的, 所以很可能我们只需要实现:

addruart, 提供一下串口基地址,即可。

 

另外, 值得注意的是:汇编代码会经历实地址模式与虚拟地址模式两个阶段。 而上面的打印函数是可以同时用于两种模式的。

 

 

C 语言阶段

C 语言代码从 start_kernel 开始, 可以看到,内核很快就会调用 printk:
printk(KERN_NOTICE);
然而在 arm 平台上, 如上的打印要等 console_init()函数执行后,才会输出到串口。
 
如果有人有兴趣, 可以为arm平台实现一个 patch, 使得在打印内核 notice时就能够向串口输出。 当然, 对于 BSP 开发, 目前的实现基本够用, 因为驱动程序初始化时间较晚,在 console_init 之后。
 
我们看一下该函数的实现:

3644/*

3645 * Initialize the console device. This is called *early*, so

3646 * we can't necessarily depend on lots of kernel help here.

3647 * Just do some early initializations, and do the complex setup

3648 * later.

3649 */

3650void __init console_init(void)

3651{

3652        initcall_t *call;

3653

3654        /* Setup the default TTY line discipline. */

3655        tty_ldisc_begin();

3656

3657        /*

3658         * set up the console device so that later boot sequences can

3659         * inform about problems etc..

3660         */

3661        call = __con_initcall_start;

3662        while (call < __con_initcall_end) {

3663                (*call)();

3664                call++;

3665        }

3666}

 

 

原来该函数会调用 __con_initcall_start 段里的函数。而串口驱动正是在此注册了一个这样的函数, 而提供了 early printk 功能。

 

比如, http://lxr.linux.no/linux+v2.6.27/drivers/serial/8250.c 中,就实现了改功能:

2645static int __init serial8250_console_init(void)
2646{
2647        if (nr_uarts > UART_NR)
2648                nr_uarts = UART_NR;
2649
2650        serial8250_isa_init_ports();
2651        register_console(&serial8250_console);
2652        return 0;
2653}
2654console_initcall(serial8250_console_init);

 

本质上,就是提前注册串口终端,而不是在串口驱动初始化时才注册。

 

Image 启动后期

有了 前面实现的 early print, 内核打印就没有问题了, 如果没有实现early print, 则在串口驱动初始化时,会注册一个console 驱动, 从而实现内核打印。

 

当然, 前提是我们需要在串口驱动中实现并注册 static struct console 
具体细节可以参考样例驱动。 这里就不深究了。
 

内核打印机制浅析

内核函数调用 printk 后, printk 将 打印信息发送到一个 log_buffer. 然后调用如下函数, 试图进行后续处理。

 

如果已经注册了终端,则会调用终端的输出函数。

另外,终端注册函数也会调用 release_console_sem, 将此前所有的信息一次性打印到 console.

 

984 /**
985  * release_console_sem - unlock the console system
986  *
987  * Releases the semaphore which the caller holds on the console system
988  * and the console driver list.
989  *
990  * While the semaphore was held, console output may have been buffered
991  * by printk().  If this is the case, release_console_sem() emits
992  * the output prior to releasing the semaphore.
993  *
994  * If there is output waiting for klogd, we wake it up.
995  *
996  * release_console_sem() may be called from any context.
997  */
998 void release_console_sem(void)
999 {
1000         unsigned long flags;
1001         unsigned _con_start, _log_end;
1002         unsigned wake_klogd = 0;
1003 
1004         if (console_suspended) {
1005                 up(&console_sem);
1006                 return;
1007         }
1008 
1009         console_may_schedule = 0;
1010 
1011         for ( ; ; ) {
1012                 spin_lock_irqsave(&logbuf_lock, flags);
1013                 wake_klogd |= log_start - log_end;
1014                 if (con_start == log_end)
1015                         break;         /* Nothing to print */
1016                 _con_start = con_start;
1017                 _log_end = log_end;
1018                 con_start = log_end;   /* Flush */
1019                 spin_unlock(&logbuf_lock);
1020                 call_console_drivers(_con_start, _log_end);
1021                 local_irq_restore(flags);
1022         }
1023         console_locked = 0;
1024         up(&console_sem);
1025         spin_unlock_irqrestore(&logbuf_lock, flags);
1026         if (wake_klogd)
1027                 wake_up_klogd();
1028 }
1029 EXPORT_SYMBOL(release_console_sem);

相关阅读 更多 +
排行榜 更多 +
单挑幸存者安卓版

单挑幸存者安卓版

飞行射击 下载
决战战地指挥官

决战战地指挥官

飞行射击 下载
鸡仔幸存者最新版

鸡仔幸存者最新版

飞行射击 下载