歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

uclinux-2008R1-RC8(bf561)到VDSP5的移植(58)

uclinux-2008R1-RC8(bf561)到VDSP5的移植(58): unable to open an initial console blog.

碰到一個郁悶的問題,提示” unable to open an initial console”後再沒有下文了。

搜了下這個錯誤出現的位置,在init_post函數中:

if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
 printk(KERN_WARNING "Warning: unable to open an initial console./n");

剛開始以為是沒有正確建立/dev/console的緣故,但是跟蹤進去後才發現不是那麼回事,/dev/console已經正確的創建了,問題出現在tty_open函數。

在這個函數裡,它首先取得console的tty_driver:

if (device == MKDEV(TTYAUX_MAJOR,1)) {
 driver = console_device(&index);
 if (driver) {
  /* Don't let /dev/console block */
  filp->f_flags |= O_NONBLOCK;
  noctty = 1;
  goto got_driver;
 }
 mutex_unlock(&tty_mutex);
 return -ENODEV;
}

看下console_device的實現:

/*
 * Return the console tty driver structure and its associated index
 */
struct tty_driver *console_device(int *index)
{
     struct console *c;
     struct tty_driver *driver = NULL;

     acquire_console_sem();
     for (c = console_drivers; c != NULL; c = c->next) {
         if (!c->device)
              continue;
         driver = c->device(c, index);
         if (driver)
              break;
     }
     release_console_sem();
     return driver;
}

它將查找所有可用的console,找到一個提供了tty_driver的console並返回。在內核中,實際使用的console由bfin_serial_console(drivers/serial/bfin_5xx.c)這個全局變量來描述。在這個結構體中提供了device回調函數用以取得這個console所使用的tty_driver:

static struct console bfin_serial_console = {
     .name         = BFIN_SERIAL_NAME,
     .write        = bfin_serial_console_write,
     .device       = uart_console_device,
     .setup        = bfin_serial_console_setup,
     .flags        = CON_PRINTBUFFER,
     .index        = -1,
     .data         = &bfin_serial_reg,
};

看看uart_console_device函數(drivers/serial/serial_core.c)的代碼:

struct tty_driver *uart_console_device(struct console *co, int *index)
{
     struct uart_driver *p = co->data;
     *index = co->index;
     return p->tty_driver;
}

在這裡console的data指向bfin_serial_reg,這也是一個全局變量,其定義在drivers/serial/bfin_5xx.c文件中:

static struct uart_driver bfin_serial_reg = {
     .owner             = THIS_MODULE,
     .driver_name       = "bfin-uart",
     .dev_name     = BFIN_SERIAL_NAME,
     .major             = BFIN_SERIAL_MAJOR,
     .minor             = BFIN_SERIAL_MINOR,
     .nr           = NR_PORTS,
     .cons              = BFIN_SERIAL_CONSOLE,
};

這裡沒有明確指出它使用的tty_driver。它將在uart_register_driver函數中動態創建。

/**
 *   uart_register_driver - register a driver with the uart core layer
 *   @drv: low level driver structure
 *
 *   Register a uart driver with the core driver.  We in turn register
 *   with the tty layer, and initialise the core driver per-port state.
 *
 *   We have a proc file in /proc/tty/driver which is named after the
 *   normal driver.
 *
 *   drv->port should be NULL, and the per-port structures should be
 *   registered using uart_add_one_port after this call has succeeded.
 */
int uart_register_driver(struct uart_driver *drv)
{
     struct tty_driver *normal = NULL;
     int i, retval;

     BUG_ON(drv->state);

     /*
      * Maybe we should be using a slab cache for this, especially if
      * we have a large number of ports to handle.
      */
     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
     retval = -ENOMEM;
     if (!drv->state)
         goto out;

     normal  = alloc_tty_driver(drv->nr);
     if (!normal)
         goto out;

     drv->tty_driver = normal;

     normal->owner      = drv->owner;
     normal->driver_name    = drv->driver_name;
     normal->name       = drv->dev_name;
     normal->major      = drv->major;
     normal->minor_start    = drv->minor;
     normal->type       = TTY_DRIVER_TYPE_SERIAL;
     normal->subtype        = SERIAL_TYPE_NORMAL;
     normal->init_termios   = tty_std_termios;
     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
     normal->flags      = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
     normal->driver_state    = drv;
     tty_set_operations(normal, &uart_ops);

     /*
      * Initialise the UART state(s).
      */
     for (i = 0; i < drv->nr; i++) {
         struct uart_state *state = drv->state + i;

         state->close_delay     = 500;    /* .5 seconds */
         state->closing_wait    = 30000;  /* 30 seconds */

         mutex_init(&state->mutex);
     }

     retval = tty_register_driver(normal);
 out:
     if (retval < 0) {
         put_tty_driver(normal);
         kfree(drv->state);
     }
     return retval;
}

從這個函數知道,通過uart_driver->tty_driver可以取得一個uart_driver對應的tty_driver,而通過tty_driver->driver_state則可以取得相應的uart_driver,它們相互引用。通過tty_set_operations函數的調用,tty_driver取得了uart_ops這個結構體中的所有回調函數:

static const struct tty_operations uart_ops = {
     .open         = uart_open,
     .close        = uart_close,
     .write        = uart_write,
     .put_char = uart_put_char,
     .flush_chars  = uart_flush_chars,
     .write_room   = uart_write_room,
     .chars_in_buffer= uart_chars_in_buffer,
     .flush_buffer = uart_flush_buffer,
     .ioctl        = uart_ioctl,
     .throttle = uart_throttle,
     .unthrottle   = uart_unthrottle,
     .send_xchar   = uart_send_xchar,
     .set_termios  = uart_set_termios,
     .stop         = uart_stop,
     .start        = uart_start,
     .hangup       = uart_hangup,
     .break_ctl    = uart_break_ctl,
     .wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
     .read_proc    = uart_read_proc,
#endif
     .tiocmget = uart_tiocmget,
     .tiocmset = uart_tiocmset,
};

現在回到tty_open函數上來。

在取得console driver之後,tty_open接著進行如下調用:

retval = init_dev(driver, index, &tty);

在這個調用中,初始化了一個tty_struct,並將此結構體中的driver成員設置為指向bfin_serial_console這個全局變量指定的tty_driver,也就是在uart_register_driver函數中創建的那個結構體,接下來:

if (tty->driver->open)
  retval = tty->driver->open(tty, filp);
else
  retval = -ENODEV;

從uart_register_driver函數可知,tty->driver->open這個回調函數指向uart_ops中的open回調函數,也就是uart_open(drivers/serial/serial_core.c)。

/*
 * In 2.4.5, calls to uart_open are serialised by the BKL in
 *   linux/fs/devices.c:chrdev_open()
 * Note that if this fails, then uart_close() _will_ be called.
 *
 * In time, we want to scrap the "opening nonpresent ports"
 * behaviour and implement an alternative way for setserial
 * to set base addresses/ports/types.  This will allow us to
 * get rid of a certain amount of extra tests.
 */
static int uart_open(struct tty_struct *tty, struct file *filp)
{
     struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
     struct uart_state *state;
     int retval, line = tty->index;
…………………………………………

     /*
      * We take the semaphore inside uart_get to guarantee that we won't
      * be re-entered while allocating the info structure, or while we
      * request any IRQs that the driver may need.  This also has the nice
      * side-effect that it delays the action of uart_hangup, so we can
      * guarantee that info->tty will always contain something reasonable.
      */
     state = uart_get(drv, line);
     if (IS_ERR(state)) {
         retval = PTR_ERR(state);
         goto fail;
     }
…………………….
}

在這裡調用了uart_get函數:

static struct uart_state *uart_get(struct uart_driver *drv, int line)
{
     struct uart_state *state;
     int ret = 0;

     state = drv->state + line;
     if (mutex_lock_interruptible(&state->mutex)) {
         ret = -ERESTARTSYS;
         goto err;
     }

     state->count++;
     if (!state->port || state->port->flags & UPF_DEAD) {
         ret = -ENXIO;
         goto err_unlock;
     }
…………………….

 err_unlock:
     state->count--;
     mutex_unlock(&state->mutex);
 err:
     return ERR_PTR(ret);
}

在這裡,由於state->port指針為空,所以這個函數調用失敗,從而造成tty_open調用失敗,最後當然就提示unable to open an initial console了。

知道了錯誤發生的原因,開始解決這個問題,搜一下port這個關鍵字,最後發現唯一設置port成員的地方在uart_add_one_port函數中,這也是在uart_register_driver函數的注釋中所指出的,那麼又是在什麼地方會調用uart_add_one_port函數呢?

搜一下,有一個函數對它進行了調用,bfin_serial_probe函數(drivers/serial/bfin_5xx.c),而這個函數則是做為bfin_serial_driver這個結構體中定義的一個回調函數而存在。

static struct platform_driver bfin_serial_driver = {
     .probe        = bfin_serial_probe,
     .remove       = bfin_serial_remove,
     .suspend = bfin_serial_suspend,
     .resume       = bfin_serial_resume,
     .driver       = {
          .name    = "bfin-uart",
     },
};

由此猜測,內核中應該要調用這個driver的probe回調函數的,但是由於某種原因沒有調用。

在內核驅動初始化的時候,將通過platform_driver_register進行bfin_serial_driver的注冊,如下所示:

static int __init bfin_serial_init(void)
{
     int ret;

     pr_info("Serial: Blackfin serial driver/n");

     bfin_serial_init_ports();

     ret = uart_register_driver(&bfin_serial_reg);
     if (ret == 0) {
         ret = platform_driver_register(&bfin_serial_driver);
         if (ret) {
              pr_debug("uart register failed/n");
              uart_unregister_driver(&bfin_serial_reg);
         }
     }
     return ret;
}

瞧瞧platform_driver_register函數做了麼事:

/**
 *   platform_driver_register
 *   @drv: platform driver structure
 */
int platform_driver_register(struct platform_driver *drv)
{
 drv->driver.bus = &platform_bus_type;
 if (drv->probe)
   drv->driver.probe = platform_drv_probe;
 if (drv->remove)
   drv->driver.remove = platform_drv_remove;
 if (drv->shutdown)
   drv->driver.shutdown = platform_drv_shutdown;
 if (drv->suspend)
   drv->driver.suspend = platform_drv_suspend;
 if (drv->resume)
   drv->driver.resume = platform_drv_resume;
 return driver_register(&drv->driver);
}

嗯,調用了driver_register函數注冊一個device driver。注意,由於drv->driver指向的是一個結構體,而不是一個指針,因此只要知道drv->driver的地址,很容易就可以知道platform_driver這個結構體的頭指針。

在這裡,與probe相關的只有platform_drv_probe函數:

static int platform_drv_probe(struct device *_dev)
{
 struct platform_driver *drv = to_platform_driver(_dev->driver);
 struct platform_device *dev = to_platform_device(_dev);

 return drv->probe(dev);
}

據此可以猜測內核應該對每一個驅動都調用probe函數才對,為什麼不調用呢?

看看driver_register:

/**
 *   driver_register - register driver with bus
 *   @drv:    driver to register
 *
 *   We pass off most of the work to the bus_add_driver() call,
 *   since most of the things we have to do deal with the bus
 *   structures.
 */
int driver_register(struct device_driver * drv)
{
     if ((drv->bus->probe && drv->probe) ||
         (drv->bus->remove && drv->remove) ||
         (drv->bus->shutdown && drv->shutdown)) {
         printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods/n", drv->name);
     }
     klist_init(&drv->klist_devices, NULL, NULL);
     return bus_add_driver(drv);
}

最後調用了bus_add_driver,這個函數實際上是將drv放到它的bus的設備鏈表中,從platform_register函數可知,它將存在在platform_bus_type這個全局變量的鏈表中:

struct bus_type platform_bus_type = {
 .name         = "platform",
 .dev_attrs    = platform_dev_attrs,
 .match        = platform_match,
 .uevent       = platform_uevent,
 .suspend = platform_suspend,
 .suspend_late = platform_suspend_late,
 .resume_early = platform_resume_early,
 .resume       = platform_resume,
};

現在的問題變為內核是否會檢測這個bus_type中的設備鏈表,並調用每個設備的probe函數。但是從上面的分析都無法看出哪裡調用了probe函數,看來得逆向分析一下,直接在代碼中搜索probe關鍵字,猜測有一個比較可能的調用:

static int really_probe(struct device *dev, struct device_driver *drv)
{
……………………

     if (dev->bus->probe) {
         ret = dev->bus->probe(dev);
         if (ret)
              goto probe_failed;
     } else if (drv->probe) {
         ret = drv->probe(dev);
         if (ret)
              goto probe_failed;
     }
………………………..
}

再搜索哪個地方調用了real_probe:

int driver_probe_device(struct device_driver * drv, struct device * dev)
{
     int ret = 0;

     if (!device_is_registered(dev))
         return -ENODEV;
     if (drv->bus->match && !drv->bus->match(dev, drv))
         goto done;

     pr_debug("%s: Matched Device %s with Driver %s/n",
          drv->bus->name, dev->bus_id, drv->name);

     ret = really_probe(dev, drv);

done:
     return ret;
}

再搜索driver_probe_device:

static int __device_attach(struct device_driver * drv, void * data)
{
     struct device * dev = data;
     return driver_probe_device(drv, dev);
}

再搜索__device_attach:

int device_attach(struct device * dev)
{
     int ret = 0;

     down(&dev->sem);
     if (dev->driver) {
         ret = device_bind_driver(dev);
         if (ret == 0)
              ret = 1;
         else {
              dev->driver = NULL;
              ret = 0;
         }
     } else {
         ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
     }
     up(&dev->sem);
     return ret;
}

看來我們已經一步步接近目標了。搜索device_attach:

void bus_attach_device(struct device * dev)
{
     struct bus_type *bus = dev->bus;
     int ret = 0;

     if (bus) {
         dev->is_registered = 1;
         if (bus->drivers_autoprobe)
              ret = device_attach(dev);
         WARN_ON(ret < 0);
         if (ret >= 0)
              klist_add_tail(&dev->knode_bus, &bus->klist_devices);
         else
              dev->is_registered = 0;
     }
}

再找bus_attach_device:

int device_add(struct device *dev)
{
…………………………………………….
     bus_attach_device(dev);
……………………………………………
}

找device_add:

int platform_device_add(struct platform_device *pdev)
{
……………………………………………
     ret = device_add(&pdev->dev);
     if (ret == 0)
         return ret;
…………………………………………..
}

最後在arch/blackfin/mach-bf561/board/ezkit.c中發現了如下調用:

static int __init ezkit_init(void)
{
 int ret;

 printk(KERN_INFO "%s(): registering device resources/n", __func__);

 ret = platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices));
 if (ret < 0)
  return ret;

#if defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE)
     bfin_write_FIO0_DIR(bfin_read_FIO0_DIR() | (1 << 12));
     SSYNC();
#endif

#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE)
     spi_register_board_info(bfin_spi_board_info,
                   ARRAY_SIZE(bfin_spi_board_info));
#endif

#if defined(CONFIG_PATA_PLATFORM) || defined(CONFIG_PATA_PLATFORM_MODULE)
     irq_desc[PATA_INT].status |= IRQ_NOAUTOEN;
#endif
     return 0;
}

arch_initcall(ezkit_init);

哈哈哈,原來是這麼回事,將ezkit.c加到工程中後編譯,搞定。

Copyright © Linux教程網 All Rights Reserved