------------------------------------------
本文系本站原创,欢迎转载!
------------------------------------------我们从具体的uart驱动开始追踪它的层次,我的系统是mx51,故从mxc_uart.c进行。
1,初始化注册过程:
static int __init mxcuart_init(void)
{
int ret = 0;
printk(KERN_INFO "Serial: MXC Internal UART driver\n");
ret = uart_register_driver(&mxc_reg); //注册tty驱动接口
if (ret == 0) {
/* Register the device driver structure. */
ret = platform_driver_register(&mxcuart_driver);
if (ret != 0) {
uart_unregister_driver(&mxc_reg);
}
}
return ret;
}
我们先看uart_register_driver(&mxc_reg),mxc_reg的定义如下:
static struct uart_driver mxc_reg = {
.owner = THIS_MODULE,
.driver_name = "ttymxc",
.dev_name = "ttymxc",
.major = SERIAL_MXC_MAJOR,
.minor = SERIAL_MXC_MINOR,
.nr = MXC_UART_NR,
.cons = MXC_CONSOLE,
};
uart_register_driver定义在serial_core中
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); //申请一个有nr根lines的drvier
if (!normal)
goto out;
drv->tty_driver = normal; //赋值给uart_driver的私有变量,drv对应mxc_uart中的mxc_reg
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; //私有数据,linux经常有这样的用法
tty_set_operations(normal, &uart_ops); //注意这边的uart_ops,最后调用到了mxc_uart中的mxc_ops
//上面主要是相关结构体的初始化和一些赋值
/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i; //开始的时候申请了drv->nr个state,现在逐个初始化
state->close_delay = 500; /* .5 seconds */
state->closing_wait = 30000; /* 30 seconds */
mutex_init(&state->mutex);
tty_port_init(&state->info.port);
init_waitqueue_head(&state->info.delta_msr_wait);
tasklet_init(&state->info.tlet, uart_tasklet_action,
(unsigned long)state);
}
retval = tty_register_driver(normal); //注册到tty core
out:
if (retval < 0) {
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
我们再看下tty_register_driver这个定义,它定义在tty_core中:
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
void **p = NULL;
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
if (!p)
return -ENOMEM;
}
if (!driver->major) {//根据主设备号决定是动态还是静态创建字符设备编号
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
}
if (error < 0) {
kfree(p);
return error;
}
if (p) {
driver->ttys = (struct tty_struct **)p;
driver->termios = (struct ktermios **)(p + driver->num);
} else {
driver->ttys = NULL;
driver->termios = NULL;
}
//注册字符设备
cdev_init(&driver->cdev, &tty_fops);
driver->cdev.owner = driver->owner;
error = cdev_add(&driver->cdev, dev, driver->num); //将字符设备添加到系统中
if (error) {
unregister_chrdev_region(dev, driver->num);
driver->ttys = NULL;
driver->termios = NULL;
kfree(p);
return error;
}
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers); //添加到tty core的drivers链表里面,tty_core维护着这个链表
mutex_unlock(&tty_mutex);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { /*根据动态设备标记决定是否此时注册tty设备,就是上面uart_register_driver里的normal->flags,在这里mxc_uart中有设置,
它是在mxcuart_probe中的mxcuart_probe里注册的,device注册后将在sys中形成层次结构,系统启动后,udev系统(android中为vold)将在/dev/下面
生成节点文件,这些节点文件操作都是按照该字符设备的tty_fops进行*/
for (i = 0; i < driver->num; i++)
tty_register_device(driver, i, NULL);
}
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED; //设置完成标记
return 0;
}
我们看到这个tty_core中的注册函数主要工作是添加了字符设备。我们可以简单地看到注册一个uart驱动架构:
mxc_uart---->serial_core---->tty_core
我们下面再看mxcuart_init中的platform_driver_register(&mxcuart_driver)。要读懂这个过程需要理解platform bus的工作流程。
2,platform_driver_register(&mxcuart_driver)
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;
return driver_register(&drv->driver);
}
platform_bus_type定义如下:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
我们还记得driver_register分析中用的bus macth函数吗?platform总线的match就是对应这里的platform_match。我们下面看下它的定义
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL; //如果有id_table,去匹配id
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0); //匹配设备的名字和驱动的名字
}
我们看下platform_match_id()定义:
static const struct platform_device_id *platform_match_id(
struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) { //匹配设备的名字和id的名字
pdev->id_entry = id;
return id; //返回匹配的的id
}
id++;
}
return NULL;
}
所以我们看到platform匹配过程是先匹配设备的名字和id的名字,然后去匹配设备的名字和驱动的名字。
我们由driver_register分析知道,如果匹配成功的话,将会执行probe函数。
好了,我们知道platform工作流程以后,我们继续我们的uart分析platform_driver_register(&mxcuart_driver),先看下mxcuart_driver定义:
static struct platform_driver mxcuart_driver = {
.driver = {
.name = "mxcintuart",
},
.probe = mxcuart_probe,
.remove = mxcuart_remove,
.suspend = mxcuart_suspend,
.resume = mxcuart_resume,
};
我们发现它没有定义驱动的id_table,只有他的名字是"mxcintuart",所以会根据这个名字去匹配设备,我们在arch/arm/mach-mx5/serial.c发现了它的设备定义:
static struct platform_device mxc_uart_device1 = {
.name = "mxcintuart",
.id = 0,
.num_resources = ARRAY_SIZE(mxc_uart_resources1),
.resource = mxc_uart_resources1,
.dev = {
.platform_data = &mxc_ports[0],
},
};
这里只贴出第一个口子的设备定义,其它的都类似,驱动会一一去匹配的,所以这里系统初始化的时候会成功匹配,匹配完了会执行probe函数!
我们下面看他的probe函数:
static int mxcuart_probe(struct platform_device *pdev)
{
int id = pdev->id;//编号,从设备定义中获得
struct resource *res;
void __iomem *base;
mxc_ports[id] = pdev->dev.platform_data; //也是从设备定义中获得,从上面看就是port口子
mxc_ports[id]->port.ops = &mxc_ops; //对应的口子操作
/* Do not use UARTs that are disabled during integration */
if (mxc_ports[id]->enabled == 1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //从设备那边获得资源,从设备定义那边看,这些都一目了然
if (!res)
return -ENODEV;
base = ioremap(res->start, res->end - res->start + 1);
if (!base)
return -ENOMEM;
mxc_ports[id]->port.membase = base;
mxc_ports[id]->port.mapbase = res->start;
mxc_ports[id]->port.dev = &pdev->dev;
mxc_ports[id]->port.irq = platform_get_irq(pdev, 0);
mxc_ports[id]->irqs[0] = platform_get_irq(pdev, 1);
mxc_ports[id]->irqs[1] = platform_get_irq(pdev, 2);
spin_lock_init(&mxc_ports[id]->port.lock);
/* Enable the low latency flag for DMA UART ports */
if (mxc_ports[id]->dma_enabled == 1) { //这个在设备定义中可以查看的,有的打开,有的没打开
mxc_ports[id]->port.flags |= UPF_LOW_LATENCY;
}
mxc_ports[id]->clk = clk_get(&pdev->dev, "uart_clk");
if (mxc_ports[id]->clk == NULL)
return -1;
uart_add_one_port(&mxc_reg, &mxc_ports[id]->port);
platform_set_drvdata(pdev, mxc_ports[id]); //设置私有数据
}
return 0;
}
probe主要工作转入uart_add_one_port(&mxc_reg, &mxc_ports[id]->port):
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state;
int ret = 0;
struct device *tty_dev;
BUG_ON(in_interrupt());
if (port->line >= drv->nr)
return -EINVAL;
state = drv->state + port->line;
mutex_lock(&port_mutex);
mutex_lock(&state->mutex);
if (state->port) {
ret = -EINVAL;
goto out;
}
state->port = port; //将port和state联系起来
state->pm_state = -1;
port->cons = drv->cons;
port->info = &state->info;
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
spin_lock_init(&port->lock);
lockdep_set_class(&port->lock, &port_lock_key);
}
uart_configure_port(drv, state, port); //配置口子类型
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this ports parameters.
*/
tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev); //注册到tty core
if (likely(!IS_ERR(tty_dev))) {
device_init_wakeup(tty_dev, 1); //pm部分
device_set_wakeup_enable(tty_dev, 0);
} else
printk(KERN_ERR "Cannot register tty device on line %d\n",
port->line);
/*
* Ensure UPF_DEAD is not set.
*/
port->flags &= ~UPF_DEAD;
out:
mutex_unlock(&state->mutex);
mutex_unlock(&port_mutex);
return ret;
}
我们看下tty_register_device(drv->tty_driver, port->line, port->dev):
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
struct device *device)
{
char name[64];
dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
if (index >= driver->num) {
printk(KERN_ERR "Attempt to register invalid tty line number "
" (%d).\n", index);
return ERR_PTR(-EINVAL);
}
if (driver->type == TTY_DRIVER_TYPE_PTY)
pty_line_name(driver, index, name); //虚拟终端的名字
else
tty_line_name(driver, index, name);//生成tty*的名字
return device_create(tty_class, device, dev, NULL, name); //创建属于tty的设备,最终将进入以前分析的device_register,也就是添加到sys层次结构中
}
这样整个probe的工作就完了,其实从上面看下来,主要就是注册tty device。同样的的注册流程也是:mxc_uart---->serial_core---->tty_core