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加到工程中後編譯,搞定。
uclinux-2008R1-RC8(bf561)到VDSP5的移植(62)
uclinux-2008R1-RC8(bf561)到VDSP5的移植(60):current_text_addr
uclinux-2008R1-RC8(bf561)到VDSP5的移植(57)
uclinux-2008R1-RC8(bf561)到VDSP5的移植(56):__grab_cache_page
uclinux-2008R1-RC8(bf561)到VDSP5的移植(46):raw_rwlock_t
uclinux-2008R1-RC8(bf561)到VDSP5的移植(45):__delay