快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
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函数。
在这个函数里,它首先取得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加到工程中后编译,搞定。