我们通过Linux设备驱动模型摘抄(1)已经知道了Linux设备驱动的基本架构,但是这种介绍是比较抽象的,至少到目前为止,我们仍然不清楚struct device, struct device_driver, struct class和struct bus_type等等这些基本的结构体是如何和一个真正的设备发生关系的,下面我们就尝试来回答这个问题。
我们以driver/serial/amba_pl011.c中的serial console驱动为例来说明这个问题(unregister的问题不予考虑):
module_init(pl011_init)->pl011_init(void)->uart_register_driver(&amba_reg);
->amba_driver_register(&pl011_driver);
我们先来看看uart_register_driver(&amba_reg)这个函数的执行情况,相关信息如下:
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
static struct uart_driver amba_reg = {
.owner = THIS_MODULE,
.driver_name = "ttyAMA",
.dev_name = "ttyAMA",
.major = SERIAL_AMBA_MAJOR,
.minor = SERIAL_AMBA_MINOR,
.nr = UART_NR,
.cons = AMBA_CONSOLE,
};
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;
}
综上所述,tty_register_driver应该是最关键的函数, 如下:
tty_register_driver(struct tty_driver *driver)->
tty_register_device(struct tty_driver *driver, unsigned index, struct device *device) ->
device_create(tty_class, device, dev, name);
而device_create的原型是
struct device *device_create(struct class *class, struct device *parent, dev_t devt, const char *fmt, ...)
对比一下,就可以发现到了device_create的时候,uart这个protocol已经被屏蔽掉了,而产生了一个叫做tty_class的(struct class)代表了包括uart在内的一系列设备,dev_t则表示在sysfs中的位置。我们可以认为这一步最终产生了/sys/devices/XXX和/sys/class/XXX这2个目录。
下面我们再来分析amba_driver_register(&pl011_driver)的执行情况:
struct amba_driver {
struct device_driver drv;
int (*probe)(struct amba_device *, void *);
int (*remove)(struct amba_device *);
void (*shutdown)(struct amba_device *);
int (*suspend)(struct amba_device *, pm_message_t);
int (*resume)(struct amba_device *);
struct amba_id *id_table;
};
static struct amba_driver pl011_driver = {
.drv = {
.name = "uart-pl011",
},
.id_table = pl011_ids,
.probe = pl011_probe,
.remove = pl011_remove,
};
static struct bus_type amba_bustype = {
.name = "amba",
.dev_attrs = amba_dev_attrs,
.match = amba_match,
.uevent = amba_uevent,
.suspend = amba_suspend,
.resume = amba_resume,
};
int amba_driver_register(struct amba_driver *drv)
{
drv->drv.bus = &amba_bustype; //struct bus_type
#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
SETFN(probe); //amba_probe, 应该是默认值
SETFN(remove); //amba_remove, 应该是默认值
SETFN(shutdown); //amba_shutdown, 应该是默认值
return driver_register(&drv->drv);
}
分析到了这里,就到了driver_register这个函数,我们再来分析。
int driver_register(struct device_driver *drv)
{
int ret;
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);
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
我们从这里也可以看出来,到了dirver_register的时候,也没有具体的设备的信息,比较重要的2个函数是:
bus_add_driver和driver_add_groups
bus_add_driver: Add a driver to the bus
driver_add_groups->sysfs_create_group 目前没有功能(linux-2.6.25)
我们可以认为,这一步最终产生了/sys/bus/XXX目录
对于一个设备来说,/sys/bus/, /sys/devices/, /sys/class这3个目录足够了,加载驱动的过程也就是把一个物理上的设备放到/sys/bus/, /sys/devices/, /sys/class/这3个目录的某个确定的位置。