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 架构下,函数的主要执行流程如下:

  1. serial8250_isa_init_ports
  2. uart_register_driver
  3. serial8250_pnp_init
  4. platform_device_alloc
  5. platform_device_add
  6. serial8250_register_ports
  7. 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_setupconsole_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_opsserial8250_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_portserial8250_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_portstruct 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 层的所有注册流程完成,主要涉及的函数包括

  1. serial8250_isa_init_ports —— 初始化 serial8250_ports
  2. uart_register_driver —— 初始化 struct tty_driverstruct uart_driver
  3. serial8250_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.
	},
};
posted @ 2020-08-07 11:30  glob  阅读(1287)  评论(0编辑  收藏  举报