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

 

快乐虾

http://blog.csdn.net/lights_joy/

[email protected]

   

 

本文适用于

ADI bf561 DSP

优视BF561EVB开发板

uclinux-2008r1-rc8 (移植到vdsp5)

Visual DSP++ 5.0

   

 

欢迎转载,但请保留作者信息

 

碰到一个郁闷的问题,提示 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函数。

在这个函数里,它首先取得consoletty_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_driverconsole并返回。在内核中,实际使用的consolebfin_serial_consoledrivers/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;

}

在这里consoledata指向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_opendrivers/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",

     },

};

由此猜测,内核中应该要调用这个driverprobe回调函数的,但是由于某种原因没有调用。

在内核驱动初始化的时候,将通过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加到工程中后编译,搞定。

 

 

 

 

你可能感兴趣的:(bf561-uclinux)