DSP

uclinux内核的console(3):console驱动初始化

2019-07-13 16:51发布

rev 0.2   快乐虾 http://blog.csdn.net/lights_joy/ lights@hb165.com    本文适用于 ADI bf561 DSP 优视BF561EVB开发板 uclinux-2008r1.5-rc3 (smp patch) Visual DSP++ 5.0 (update 5)   欢迎转载,但请保留作者信息  

1.1    console_init

uclinux初始化过程中,除非启用了early console,否则直到console_init调用之前是没有任何输出的,它们的输出都放在__log_buf这个缓冲内的,在console_init调用时再将这个缓冲区内的数据一次性输出。 那么console_init又做了什么工作呢?这个函数体在drivers/char/tty_io.c中: /*  * Initialize the console device. This is called *early*, so  * we can't necessarily depend on lots of kernel help here.  * Just do some early initializations, and do the complex setup  * later.  */ void __init console_init(void) {      initcall_t *call;        /* Setup the default TTY line discipline. */      (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);        /*       * set up the console device so that later boot sequences can       * inform about problems etc..       */      call = __con_initcall_start;      while (call < __con_initcall_end) {          (*call)();          call++;      } } 从这个函数大致可以猜出,console和串口的初始化操作应该是由__con_initcall_start__con_initcall_end之间的函数调用来完成的。那么放到这段空间中的函数是怎么来的呢?又是什么函数可以放在这段空间中呢? 让我们看看vmlinux.lds.s文件中对这两个符号的定义吧:        .con_initcall.init :        {               ___con_initcall_start = .;               *(.con_initcall.init)               ___con_initcall_end = .;        } 原来,这段空间中存放的是.con_initcall.init这个段的内容,也就是说只要在数据代码中声明.con_initcall.init就可以了。

1.2    console_initcall

uclinux的头文件中搜索initcall,发现了这样一个定义(include/linux/init.h) #define console_initcall(fn) /      static initcall_t __initcall_##fn /      __attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn 在此函数中的所有操作都是与硬件无关的,据此可以猜测,应该还有一个与硬件相关的文件,且在此文件中应该使用console_initcall这个宏。 在内核源码中搜索console_initcall,发现了一大堆结果: 查找全部"console_initcall", 大小写匹配, 全字匹配, 子文件夹, 查找结果1, "D:/uc2008/linux-2.6.x", "*.c"   D:/uc2008/linux-2.6.x/drivers/char/amiserial.c(2132)://console_initcall(amiserial_console_init);   D:/uc2008/linux-2.6.x/drivers/char/decserial.c(66)://console_initcall(decserial_console_init);   D:/uc2008/linux-2.6.x/drivers/char/hvc_beat.c(135)://console_initcall(hvc_beat_console_init);   D:/uc2008/linux-2.6.x/drivers/char/hvc_console.c(242)://console_initcall(hvc_console_init);   D:/uc2008/linux-2.6.x/drivers/char/hvc_iseries.c(594)://console_initcall(hvc_find_vtys);   D:/uc2008/linux-2.6.x/drivers/char/hvc_rtas.c(134)://console_initcall(hvc_rtas_console_init);   D:/uc2008/linux-2.6.x/drivers/char/hvc_vio.c(169)://console_initcall(hvc_find_vtys);   D:/uc2008/linux-2.6.x/drivers/char/hvsi.c(1317)://console_initcall(hvsi_console_init);   D:/uc2008/linux-2.6.x/drivers/char/serial167.c(2602)://console_initcall(serial167_console_init);   D:/uc2008/linux-2.6.x/drivers/char/viocons.c(1167)://console_initcall(viocons_init);   D:/uc2008/linux-2.6.x/drivers/char/vme_scc.c(1044)://console_initcall(vme_scc_console_init);   D:/uc2008/linux-2.6.x/drivers/char/vt.c(2823)://console_initcall(con_init);   D:/uc2008/linux-2.6.x/drivers/s390/char/con3215.c(890)://console_initcall(con3215_init);   D:/uc2008/linux-2.6.x/drivers/s390/char/con3270.c(632)://console_initcall(con3270_init);   D:/uc2008/linux-2.6.x/drivers/s390/char/sclp_con.c(252)://console_initcall(sclp_console_init);   D:/uc2008/linux-2.6.x/drivers/s390/char/sclp_vt220.c(774)://console_initcall(sclp_vt220_con_init);   D:/uc2008/linux-2.6.x/drivers/serial/21285.c(471)://console_initcall(rs285_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/68328serial.c(1577)://console_initcall(m68328_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/8250.c(2539)://console_initcall(serial8250_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/8250_early.c(210)://console_initcall(early_uart_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/atmel_serial.c(863)://console_initcall(atmel_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/bfin_5xx.c(1103)://console_initcall(bfin_serial_rs_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/clps711x.c(534)://console_initcall(clps711xuart_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/dz.c(765)://console_initcall(dz_serial_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/imx.c(1022)://console_initcall(imx_rs_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/m32r_sio.c(1121)://console_initcall(m32r_sio_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/mcfserial.c(1974)://console_initcall(mcfrs_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/mpc52xx_uart.c(800)://console_initcall(mpc52xx_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/netx-serial.c(644)://console_initcall(netx_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/pmac_zilog.c(2024)://console_initcall(pmz_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/pnx8xxx_uart.c(747)://console_initcall(pnx8xxx_rs_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/pxa.c(691)://console_initcall(serial_pxa_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/s3c2410.c(1932)://console_initcall(s3c24xx_serial_initconsole);   D:/uc2008/linux-2.6.x/drivers/serial/sa1100.c(808)://console_initcall(sa1100_rs_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/serial_ks8695.c(611)://console_initcall(ks8695_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/serial_lh7a40x.c(631)://console_initcall (lh7a40xuart_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/serial_txx9.c(922)://console_initcall(serial_txx9_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/sh-sci.c(1280)://console_initcall(sci_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/sh-sci.c(1361)://console_initcall(kgdb_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/sn_console.c(1086)://console_initcall(sn_sal_serial_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/uartlite.c(400)://console_initcall(ulite_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/v850e_uart.c(234)://console_initcall(v850e_uart_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/vr41xx_siu.c(903)://console_initcall(siu_console_init);   D:/uc2008/linux-2.6.x/drivers/serial/cpm_uart/cpm_uart_core.c(1242)://console_initcall(cpm_uart_console_init);   匹配行: 44    匹配文件: 43    合计搜索文件: 6311 果然不出所料,从目录和文件名中可以看出内核为许多不同的设备提供了硬件驱动。因为我用的是BF561,因此使用上述文件中的drivers/serial/bfin_5xx.c文件。 drivers/serial/bfin_5xx.c中有这样一行: console_initcall(bfin_serial_rs_console_init); 展开这个宏:      static initcall_t __initcall_bfin_serial_rs_console_init      __attribute_used__ __attribute__((__section__(".con_initcall.init")))= bfin_serial_rs_console_init 上式中initcall_t定义为: typedef int (*initcall_t)(void); 可以看出它声明了一个函数指针变量,这个变量就放在.con_initcall.init这个数据段中,且这个指针的值指向bfin_serial_rs_console_init函数。这样在console_init函数中就可以通过这个函数指针调用bfin_serial_rs_console_init函数。

1.3    bfin_serial_rs_console_init

bf561串口的初始化就是在bfin_serial_rs_console_init函数中完成的,看看: static int __init bfin_serial_rs_console_init(void) {      bfin_serial_init_ports();      register_console(&bfin_serial_console);      return 0; } 实际上做了两件事情,首先调用bfin_serial_init_ports函数初始化了uart结构体的部分成员,但是它并不初始化硬件,看起来似乎有点奇怪。然后注册一个struct console结构体变量,并将printk缓冲区中的字符输出。

1.4    register_console

此函数的实现在kernel/printk.c中: /*  * The console driver calls this routine during kernel initialization  * to register the console printing procedure with printk() and to  * print any messages that were printed by the kernel before the  * console driver was initialized.  */ void register_console(struct console *console) {      int i;      unsigned long flags;      struct console *bootconsole = NULL;        if (preferred_console < 0 || bootconsole || !console_drivers)          preferred_console = selected_console;        /*       *   See if we want to use this console driver. If we       *   didn't select a console we take the first one       *   that registers here.       */      if (preferred_console < 0) {          if (console->index < 0)               console->index = 0;          if (console->setup == NULL ||              console->setup(console, NULL) == 0) {               console->flags |= CON_ENABLED | CON_CONSDEV;               preferred_console = 0;          }      }        /*       *   See if this console matches one we selected on       *   the command line.       */      for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];               i++) {          if (strcmp(console_cmdline[i].name, console->name) != 0)               continue;          if (console->index >= 0 &&              console->index != console_cmdline[i].index)               continue;          if (console->index < 0)               console->index = console_cmdline[i].index;          if (console->setup &&              console->setup(console, console_cmdline[i].options) != 0)               break;          console->flags |= CON_ENABLED;          console->index = console_cmdline[i].index;          if (i == selected_console) {               console->flags |= CON_CONSDEV;               preferred_console = selected_console;          }          break;      }        if (!(console->flags & CON_ENABLED))          return;        /*       *   Put this console in the list - keep the       *   preferred driver at the head of the list.       */      acquire_console_sem();      if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {          console->next = console_drivers;          console_drivers = console;          if (console->next)               console->next->flags &= ~CON_CONSDEV;      } else {          console->next = console_drivers->next;          console_drivers->next = console;      }      if (console->flags & CON_PRINTBUFFER) {          /*           * release_console_sem() will print out the buffered messages           * for us.           */          spin_lock_irqsave(&logbuf_lock, flags);