serial8250_driver_analysis
1. serial8250_init
serial8250_init
函数是模块的入口,
static int __init serial8250_init(void)
{
int ret;
if (nr_uarts == 0)
return -ENODEV;
serial8250_isa_init_ports();
pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %sabled\n",
nr_uarts, share_irqs ? "en" : "dis");
#ifdef CONFIG_SPARC
ret = sunserial_register_minors(&serial8250_reg, UART_NR);
#else
serial8250_reg.nr = UART_NR;
ret = uart_register_driver(&serial8250_reg);
#endif
if (ret)
goto out;
ret = serial8250_pnp_init();
if (ret)
goto unreg_uart_drv;
serial8250_isa_devs = platform_device_alloc("serial8250",
PLAT8250_DEV_LEGACY);
if (!serial8250_isa_devs) {
ret = -ENOMEM;
goto unreg_pnp;
}
ret = platform_device_add(serial8250_isa_devs);
if (ret)
goto put_dev;
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
ret = platform_driver_register(&serial8250_isa_driver);
if (ret == 0)
goto out;
platform_device_del(serial8250_isa_devs);
put_dev:
platform_device_put(serial8250_isa_devs);
unreg_pnp:
serial8250_pnp_exit();
unreg_uart_drv:
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
#else
uart_unregister_driver(&serial8250_reg);
#endif
out:
return ret;
}
module_init(serial8250_init);
x86 架构下,函数的主要执行流程如下:
serial8250_isa_init_ports
uart_register_driver
serial8250_pnp_init
platform_device_alloc
platform_device_add
serial8250_register_ports
platform_driver_register
下面依次介绍这些函数具体执行的操作。
1.1. module_init
module_init
宏有两个定义:
#ifndef MODULE
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
/**
* module_exit() - driver exit entry point
* @x: function to be run when driver is removed
*
* module_exit() will wrap the driver clean-up code
* with cleanup_module() when used with rmmod when
* the driver is a module. If the driver is statically
* compiled into the kernel, module_exit() has no effect.
* There can only be one per module.
*/
#define module_exit(x) __exitcall(x);
#else /* MODULE */
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
/* This is only required if you want to be unloadable. */
#define module_exit(exitfn) \
static inline exitcall_t __maybe_unused __exittest(void) \
{ return exitfn; } \
void cleanup_module(void) __attribute__((alias(#exitfn)));
#endif
inbox 模式下, module_init
定义如下:
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
#define __define_initcall(fn, id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" #id ".init"))) = fn;
module_init(serial8250_init);
static initcall_t __initcall_serial8250_init_6 __used
___attribute__((__section__(".initcall6.init"))) = serial8250_init;
__define_initcall
定义的函数会在 do_initcalls
里依次调用,后者的函数调用路径为:
start_kernel
-> rest_init
-> kernel_init
-> kernel_init_freeable
-> do_basic_setup
-> do_initcalls
。
和 console_setup
, console_initcall
相比,三者的先后顺序为 console_setup
-> console_initcall
-> module_init
( inbox ) 。
2. serial8250_isa_init_ports
2.1. 变量介绍
2.1.1. uart 的数量
nr_uarts 变量的初始值为 CONFIG_SERIAL_8250_RUNTIME_UARTS 。如果大于 UART_NR ,即 CONFIG_SERIAL_8250_NR_UARTS ,设置为后者。
x86 下, nr_uarts 为 32 , UART_NR 为 48 。
2.1.2. serial8250_ports
static struct uart_8250_port serial8250_ports[UART_NR] 。
serial8250_ports 变量贯穿在 drivers/tty/serial/8250/8250_core.c 中, serial8250_isa_init_ports
会对变量进行初始化,初始化的数量为 nr_uarts 。
2.1.3. base_ops
static const struct uart_ops *base_ops 。
base_ops 在 serial8250_isa_init_ports
进行赋值,其余的地方都只是引用。
2.1.4. old_serial_port
x86 下, SERIAL_PORT_DFNS 定义如下:
static const struct old_serial_port old_serial_port[] = {
SERIAL_PORT_DFNS /* defined in asm/serial.h */
};
# define STD_COMX_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | 0 )
#define SERIAL_PORT_DFNS \
/* UART CLK PORT IRQ FLAGS */ \
{ .uart = 0, BASE_BAUD, 0x3F8, 4, STD_COMX_FLAGS }, /* ttyS0 */ \
{ .uart = 0, BASE_BAUD, 0x2F8, 3, STD_COMX_FLAGS }, /* ttyS1 */ \
{ .uart = 0, BASE_BAUD, 0x3E8, 4, STD_COMX_FLAGS }, /* ttyS2 */ \
{ .uart = 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
old_serial_port 供 serial8250_isa_init_ports
用于初始化变量 serial8250_ports 。
2.2. 函数解析
函数的主要功能可以分为两部分,第一部分对 serial8250_ports 进行一般性的初始化:
// 初始化所有的运行时 uart 结构体
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
port->line = i;
/*
* 初始化 up->port->ops = &serial8250_pops
* up->cur_iotype = 0xFF
*/
serial8250_init_port(up);
if (!base_ops)
// base_ops = &serial8250_pops
base_ops = port->ops;
port->ops = &univ8250_pops;
// up->timer.function = serial8250_timeout
timer_setup(&up->timer, serial8250_timeout, 0);
up->ops = &univ8250_driver_ops;
/*
* ALPHA_KLUDGE_MCR needs to be killed.
*/
up->mcr_mask = ~ALPHA_KLUDGE_MCR;
up->mcr_force = ALPHA_KLUDGE_MCR;
}
第二部分用 old_serial_port 变量初始化 serial8250_ports :
// univ8250_port_ops = &serial8250_pops
univ8250_port_ops = *base_ops;
/*
* CONFIG_SERIAL_8250_RSA=y 时
* serial8250_pops = {
* .config_port = univ8250_config_port,
* .request_port = univ8250_request_port,
* .release_port = univ8250_release_port,
*}
*/
univ8250_rsa_support(&univ8250_port_ops);
if (share_irqs)
irqflag = IRQF_SHARED;
for (i = 0, up = serial8250_ports;
i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
i++, up++) {
struct uart_port *port = &up->port;
port->iobase = old_serial_port[i].port;
port->irq = irq_canonicalize(old_serial_port[i].irq);
port->irqflags = 0;
port->uartclk = old_serial_port[i].baud_base * 16;
port->flags = old_serial_port[i].flags;
port->hub6 = 0;
port->membase = old_serial_port[i].iomem_base;
port->iotype = old_serial_port[i].io_type;
port->regshift = old_serial_port[i].iomem_reg_shift;
/*
* up->dl_read = default_serial_dl_read
* up->dl_write = default_serial_dl_write
* up->port->serial_in = io_serial_in
* up->port->serial_out = io_serial_out
* up->cur_iotype = up->port->iotype
* up->port->handle_irq = serial8250_default_handle_irq
*/
serial8250_set_defaults(up);
port->irqflags |= irqflag;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
}
两部分都是对 serial8250_ports 进行初始化。
3. uart_register_driver
serial8250_init
调用这个函数时传递的参数为 uart_register_driver(&serial8250_reg)
。
serial8250_reg 定义如下:
static struct uart_driver serial8250_reg = {
.owner = THIS_MODULE,
.driver_name = "serial",
.dev_name = "ttyS",
.major = TTY_MAJOR,
.minor = 64,
.cons = SERIAL8250_CONSOLE,
// serial8250_init 设置,48
.nr = UART_NR,
};
函数的定义如下:
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
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 保存所有 48 个 uart_state ,即 uart 运行时信息
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;
/*
* alloc_tty_driver 最终调用 __tty_alloc_drivers(48, THIS_MODULE, 0)
* 后者创建一个 struct tty_driver,初始化以下成员:
* .magic = TTY_DRIVER_MAGIC
* .num = 48
* .owner = THIS_MODULE
* .flags = 0
* .ttys = kcalloc(sizeof(struct tty_struct) * 48)
* .termios = kcalloc(sizeof(struct ktermios) * 48)
* .ports = kcalloc(sizeof(struct tty_port) * 48)
* .cdevs = kcalloc(sizeof(struct cdev) * 48)
* alloc_tty_driver 只会分配内存空间,并没有进行赋值
*/
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
drv->tty_driver = normal;
// 用 serial8250_reg 初始化 normal
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;
// normal->ops = &uart_ops
tty_set_operations(normal, &uart_ops);
/*
* Initialise the UART state(s).
*/
// 依次初始化 48 个 uart_state
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
/*
* 初始化 port 的下列成员
* .buf
* .open_wait
* .delta_msr_wait
* .mutex
* .buf_mutex
* .lock
* .close_delay = (50 * HZ) / 100
* .closing_wait = (3000 * HZ) / 100
* .client_ops = &default_client_ops
* .kref
*/
tty_port_init(port);
port->ops = &uart_port_ops;
}
/*
* 这个函数依次执行以下操作:
* 1. 根据 major 和 minor_start 调用 MKDEV 创建字符设备
* 2. 添加 normal->tty_drivers 到全局变量 tty_drivers
* 3. 调用 proc_tty_register_driver 注册驱动到 /proc
* 4. 设置 normal->flags |= TTY_DRIVER_INSTALLED
*/
retval = tty_register_driver(normal);
if (retval >= 0)
return retval;
for (i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
这个函数用 serial8250_reg 初始化一个 struct tty_driver ,注册到 driver model 。
初始化的 struct tty_driver 中,以下成员只是分配了所需要的内存空间,并没有初始化其成员:
struct tty_driver {
struct tty_struct **ttys;
struct tty_port **ports;
struct ktermios **termios;
}
4. serial8250_pnp_init
serial8250_pnp_init
直接调用 pnp_register_driver(&serial_pnp_driver)
注册 pnp 驱动到 driver model 。
注册的过程中,扫描系统中所有的 pnp 设备,根据名称将驱动和设备绑定。
serial_pnp_driver 的定义如下:
static struct pnp_driver serial_pnp_driver = {
.name = "serial",
.probe = serial_pnp_probe,
.remove = serial_pnp_remove,
.suspend = serial_pnp_suspend,
.resume = serial_pnp_resume,
.id_table = pnp_dev_table,
};
5. platform_device_alloc
platform_device_alloc
根据传入的 platform device 名和 id 创建一个 struct platform_device 对象。
struct platform_device *platform_device_alloc(const char *name, int id)
{
struct platform_object *pa;
pa = kzalloc(sizeof(*pa) + strlen(name) + 1, GFP_KERNEL);
if (pa) {
strcpy(pa->name, name);
// pa->pdev 是 struct platform_device 类型
pa->pdev.name = pa->name;
pa->pdev.id = id;
/*
* pa->pdev.dev 是 struct device 类型,和 WARN_ON 的触发条件相关
* device_initialize 调用 INIT_LIST_HEAD(&dev->devres_head)
* 初始化 dev->devres_head 为空表
*/
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release = platform_device_release;
arch_setup_pdev_archdata(&pa->pdev);
}
return pa ? &pa->pdev : NULL;
}
EXPORT_SYMBOL_GPL(platform_device_alloc);
serial8250_init 调用这个函数的结果是创建了一个 platform device serial8250_isa_devs ,并且初始化了 serial8250_isa_devs->dev 成员 。
6. platform_device_add
platform_device_add(serial8250_isa_devs)
将创建的 platform device,添加到 device hierarchy ,函数说明如下:
int platform_device_add(struct platform_device *pdev)
{
int i, ret;
if (!pdev)
return -EINVAL;
// true,设置 parent
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
// pdev->id = -1,即 PLATFORM_DEVID_NONE
switch (pdev->id) {
default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO:
/*
* Automatically allocated device ID. We mark it as such so
* that we remember it must be freed, and we append a suffix
* to avoid namespace collision with explicit IDs.
*/
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
/*
* device_add 完成真正的设备添加工作,包括在 sysfs 下创建对应的目录结构,
* 创建符号链接,给设备添加参数,然后通过 bus_add_device 将设备添加到对应的
* 总线。
* 设备添加到总线后,调用 bus_probe_device 给新的设备 probe 驱动
*/
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
if (r->parent)
release_resource(r);
}
err_out:
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
7. serial8250_register_ports
serial8250_init
的调用参数为 serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev)
:
static void __init
serial8250_register_ports(struct uart_driver *drv, struct device *dev)
{
int i;
// 初始化 32 个 uart 结构
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
// 条件不满足
if (up->port.type == PORT_8250_CIR)
continue;
// 条件不满足
if (up->port.dev)
continue;
// 设置 serial8250_ports 的 dev 指向 serial8250_isa_devs->dev
up->port.dev = dev;
serial8250_apply_quirks(up);
/*
* uart_add_one_port 将 drv->state 和对应的 up->port 关联,
* 调用 uart_configure_port(drv, state, &up->port) 配置
* &up->port ,最终调用 serial8250_config_port(&up->port, 0)
*/
uart_add_one_port(drv, &up->port);
}
}
7.1. uart_add_one_port
uart_add_one_port
完成 uart 层的初始化后,用 uart 设备初始化 tty 层的结构体: struct tty_port , struct tty_driver ,最终将 tty 层和 uart 层关联起来。
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
int ret = 0;
struct device *tty_dev;
int num_groups;
BUG_ON(in_interrupt());
// uport->line 在 serial8250_isa_init_ports 初始化
if (uport->line >= drv->nr)
return -EINVAL;
// drv->state 在 uart_register_driver 初始化
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
goto out;
}
/* Link the port to the driver state table and vice versa */
atomic_set(&state->refcount, 1);
init_waitqueue_head(&state->remove_wait);
state->uart_port = uport;
uport->state = state;
state->pm_state = UART_PM_STATE_UNDEFINED;
uport->cons = drv->cons;
// uart_register_driver 初始化 drv->tty_driver
uport->minor = drv->tty_driver->minor_start + uport->line;
uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name,
drv->tty_driver->name_base + uport->line);
if (!uport->name) {
ret = -ENOMEM;
goto out;
}
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
// console=ttyS1,115200n8 , uport->line = 1 时, if 条件就不满足
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
if (uport->cons && uport->dev)
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
/*
* uport->flags 设置了 UPF_BOOT_AUTOCONF 标志,调用
* serial8250_config_port 配置 uport ,包括请求 IO 资源,根据
* 串口具体型号设置 FCR ,配置 IRQ ;
* 如果设备类型已知, 调用 uart_report_port 报告端口信息;
* 调用 uart_change_pm 设置端口电源状态为打开,需要设备支持;
* 调用 serial8250_set_mctrl 设置 MCR ;
* 调用 uart_change_pm 关闭端口。
*/
uart_configure_port(drv, state, uport);
// 如果当前的 uport 是 console ,设置 port->console = 1
port->console = uart_console(uport);
num_groups = 2;
if (uport->attr_group)
num_groups++;
uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
GFP_KERNEL);
if (!uport->tty_groups) {
ret = -ENOMEM;
goto out;
}
uport->tty_groups[0] = &tty_dev_attr_group;
if (uport->attr_group)
uport->tty_groups[1] = uport->attr_group;
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this port's parameters.
*/
// 这个函数将 tty 层和 uart 层关联起来,下面进行详细介绍
tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
if (likely(!IS_ERR(tty_dev))) {
device_set_wakeup_capable(tty_dev, 1);
} else {
dev_err(uport->dev, "Cannot register tty device on line %d\n",
uport->line);
}
/*
* Ensure UPF_DEAD is not set.
*/
uport->flags &= ~UPF_DEAD;
out:
mutex_unlock(&port->mutex);
mutex_unlock(&port_mutex);
return ret;
}
struct serdev_device {
struct device dev;
int nr;
struct serdev_controller *ctrl;
const struct serdev_device_ops *ops;
struct completion write_comp;
struct mutex write_lock;
};
/*
* serdev_controller 是一个逻辑设备,创建时需要传递一个真实的 struct device
* 对象作为成员 dev.parent 存在;初始化时将 serdev_controller 对应的 struct
* serport 对象作为 dev.driver_data 保存
* @dev : .type = &serdev_ctrl_type
.bus = &serdev_bus_type
* @nr : ida_simple_get
* @serdev
* @ops
*/
struct serdev_controller {
struct device dev;
unsigned int nr;
struct serdev_device *serdev;
const struct serdev_controller_ops *ops;
};
/*
* 从 serport 的成员变量来看,这个结构体把 tty_port , tty_struct ,
* tty_driver 结合在一起
*/
struct serport {
struct tty_port *port;
struct tty_struct *tty;
struct tty_driver *tty_drv;
int tty_idx;
unsigned long flags;
};
uart_add_one_port
调用此函数时,各个参数的值为:
/*
* @port : serial8250_reg->state->port
* @driver : serial8250_reg->tty_driver , uart_register_driver 中的 normal
* @index : uport->port->line
* @device : serial8250_isa_dev->dev
* @drvdata : serial8250_reg->state->port
* @attr_grp :
*/
struct device *tty_port_register_device_attr_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp)
{
struct device *dev;
// driver->port[index] = port
tty_port_link_device(port, driver, index);
dev = serdev_tty_port_register(port, device, driver, index);
if (PTR_ERR(dev) != -ENODEV) {
/* Skip creating cdev if we registered a serdev device */
return dev;
}
return tty_register_device_attr(driver, index, device, drvdata,
attr_grp);
}
EXPORT_SYMBOL_GPL(tty_port_register_device_attr_serdev);
7.2.1. serdev_tty_port_register
serdev_tty_port_register
创建一个 struct serdev_controller 对象,添加到 device hierarchy 中。
struct device *serdev_tty_port_register(struct tty_port *port,
struct device *parent,
struct tty_driver *drv, int idx)
{
const struct tty_port_client_operations *old_ops;
struct serdev_controller *ctrl;
struct serport *serport;
int ret;
if (!port || !drv || !parent)
return ERR_PTR(-ENODEV);
/*
* 分配一个 serdev_controller 对象,初始化以下成员:
* .nr = ida_simple_get
* .dev = {
* .type = &serdev_ctrl_type
* .bus = &serdev_bus_type
* .parent = parent => serial8250_isa_dev->dev
* .of_node = parent->of_node
* .driver_data = &ctrl[1] , 即 struct serport 对象
* .dev->kobj->name = "serial$nr"
* }
*/
ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
if (!ctrl)
return ERR_PTR(-ENOMEM);
serport = serdev_controller_get_drvdata(ctrl);
serport->port = port;
serport->tty_idx = idx;
serport->tty_drv = drv;
ctrl->ops = &ctrl_ops;
old_ops = port->client_ops;
port->client_ops = &client_ops;
port->client_data = ctrl;
// 添加 ctrl 到 driver model
ret = serdev_controller_add(ctrl);
if (ret)
goto err_reset_data;
dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
return &ctrl->dev;
err_reset_data:
port->client_data = NULL;
port->client_ops = old_ops;
serdev_controller_put(ctrl);
return ERR_PTR(ret);
}
7.2.2. tty_register_device_attr
tty_register_device_attr
创建一个 struct device 对象,初始化其成员变量,然后通过 device_register
注册到系统。
struct device *tty_register_device_attr(struct tty_driver *driver,
unsigned index, struct device *device,
void *drvdata,
const struct attribute_group **attr_grp)
{
char name[64];
dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
struct ktermios *tp;
struct device *dev;
int retval;
if (index >= driver->num) {
pr_err("%s: Attempt to register invalid tty line number (%d)\n",
driver->name, index);
return ERR_PTR(-EINVAL);
}
if (driver->type == TTY_DRIVER_TYPE_PTY)
pty_line_name(driver, index, name);
else // name = "ttyS1"
tty_line_name(driver, index, name);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
dev->devt = devt;
dev->class = tty_class;
dev->parent = device;
dev->release = tty_device_create_release;
dev_set_name(dev, "%s", name);
dev->groups = attr_grp;
dev_set_drvdata(dev, drvdata);
dev_set_uevent_suppress(dev, 1);
retval = device_register(dev);
if (retval)
goto err_put;
// serial8250_init 执行时条件成立
if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
/*
* Free any saved termios data so that the termios state is
* reset when reusing a minor number.
*/
// alloc_tty_driver 分配了内存空间
tp = driver->termios[index];
if (tp) {
driver->termios[index] = NULL;
kfree(tp);
}
/*
* driver->cdevs[index] = cdev_alloc()
* driver->cdevs[index]->ops = &tty_fops
* driver->cdevs[index]->owner = driver->owner
* cdev_add(driver->cdevs[index], devt, 1)
* tty_cdev_add 初始化 driver->cdevs[index] 后,
* 将创建的字符设备添加到系统
*/
retval = tty_cdev_add(driver, devt, index, 1);
if (retval)
goto err_del;
}
dev_set_uevent_suppress(dev, 0);
kobject_uevent(&dev->kobj, KOBJ_ADD);
return dev;
err_del:
device_del(dev);
err_put:
put_device(dev);
return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(tty_register_device_attr);
7.3. 总结
至此,一个串口设备从 uart 层到 tty 层的所有注册流程完成,主要涉及的函数包括
serial8250_isa_init_ports
—— 初始化 serial8250_portsuart_register_driver
—— 初始化 struct tty_driver , struct uart_driverserial8250_register_ports
—— 初始化 tty_driver->cdevs
serial8250_init
的执行结果是初始化一个 struct tty_driver 对象,其中主要的成员变量介绍如下:
struct tty_driver {
unsigned int num; // num = 48
const char *driver_name; // driver_name = "serial"
const char *name; // name = "ttyS"
short type; // TTY_DRIVER_TYPE_SERIAL
short subtype; // SERIAL_TYPE_NORMAL
unsigned long flags; // TTY_DRIVER_RAW | TTY_DRIVER_DYNAMIC_DEV
const struct tty_operations *ops; // ops = uart_ops , serial_core.c
/*
* cdevs , ttys , ports , termios 都是长度为 num 的数组,
* 每个 tty_port 都有自己的字符设备文件 cdevs[index]
*/
struct cdev **cdevs;
struct tty_struct *ttys;
struct tty_port **ports;
struct ktermios **termios;
}
也就是说, serial8250_init
执行时初始化一个 tty_driver 。
8. platform_driver_register
serial8250_init
调用 platform_driver_register(&serial8250_isa_driver)
注册 platform driver 。后者调用 __platform_driver_register
,最终调用 driver_register(serial8250_isa_driver.driver)
注册驱动。
static struct platform_driver serial8250_isa_driver = {
.probe = serial8250_probe,
.remove = serial8250_remove,
.suspend = serial8250_suspend,
.resume = serial8250_resume,
.driver = {
.name = "serial8250",
// set by __platform_driver_register
.owner = THIS_MODULE,
.bus = &platform_bus_type,
.probe = platform_drv_probe,
.remove = platform_drv_remove,
.shutdown = platform_drv_shutdown.
},
};
作者:glob
出处:http://www.cnblogs.com/adera/
欢迎访问我的个人博客:https://blog.globs.site/
本文版权归作者和博客园共有,转载请注明出处。