UART驱动情景分析-open

一、设备名字的前缀的来源

struct device *tty_register_device_attr(struct tty_driver *driver,
				   unsigned index, struct device *device,
				   void *drvdata,
				   const struct attribute_group **attr_grp)
{
    
    
	//...
	if (driver->type == TTY_DRIVER_TYPE_PTY)
		pty_line_name(driver, index, name);
	else
		tty_line_name(driver, index, name);		//这个名字的前缀是从uart_driver->devname中获得的,后缀是从index+tty_driver->name_base来设置的,name_base没有设置,就完全根据序号来了

	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/xxx,这里的name是从tty_line_name函数设置的
	dev->groups = attr_grp;
	dev_set_drvdata(dev, drvdata);

	dev_set_uevent_suppress(dev, 1);

	retval = device_register(dev);
	//...
}

static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
{
    
    
	if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE)
		return sprintf(p, "%s", driver->name);
	else
		return sprintf(p, "%s%d", driver->name,
			       index + driver->name_base);		//从tty_driver->name中获得前缀
}

int uart_register_driver(struct uart_driver *drv)
{
    
    
	//...
	drv->tty_driver = normal;

	normal->driver_name	= drv->driver_name;
	normal->name		= drv->dev_name;		//这里初始化tty_driver时,是从uart_driver的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);
	//....
}

//uart_driver初始化时是固定的一个值,所以说设备的前缀名是ttymxc
#define DEV_NAME		"ttymxc"

static struct uart_driver imx_uart_uart_driver = {
    
    
	.owner          = THIS_MODULE,
	.driver_name    = DRIVER_NAME,
	.dev_name       = DEV_NAME,
	.major          = SERIAL_IMX_MAJOR,
	.minor          = MINOR_START,
	.nr             = ARRAY_SIZE(imx_uart_ports),
	.cons           = IMX_CONSOLE,
};

在设置设备文件名时,是从tty_driver->name里获取的,当tty_driver初始化时,是从uart_driver->dev_name里的值。完全一样,
uart_driver在初始化时就已经设置了固定的值,用宏定义设置好的。

设备文件的后缀的来源,这里根据设备树的别名来的

static int imx_uart_probe(struct platform_device *pdev)
{
    
    
	//...
    ret = imx_uart_probe_dt(sport, pdev);		//probe函数里有解析设备树的函数
	//...
}

static int imx_uart_probe_dt(struct imx_port *sport,
			     struct platform_device *pdev)
{
    
    
	//...
	ret = of_alias_get_id(np, "serial");			//获得别名的serial属性的值
	//...
	sport->port.line = ret;		//从返回的值确定序号
	//...
	return 0;
}


/ {
    
    
    aliases {
    
    
		//....
        serial0 = &uart1;
        serial1 = &uart2;
        serial2 = &uart3;
        serial3 = &uart4;
        serial4 = &uart5;
        serial5 = &uart6;
        serial6 = &uart7;
		//...
    };
}

二、tty设备open的过程

它肯定是从app端,一路调用到最底层的驱动。
硬件相关的驱动里面,驱动构造了一个uart_driver,然后向上注册tty_driver。
当tty_driver注册后,调用了uart_add_one_port函数,会给tty_driver分配cdev,cdev里面会有file_operations的操作函数,操作函数中的open函数,就可以让app端调用。

1. 找到tty_driver

struct tty_driver {
    
    
	//...
	const struct tty_operations *ops;		//底层的操作函数
	struct list_head tty_drivers;
}
//在tty_io.c中
static const struct file_operations tty_fops = {
    
    
	.llseek		= no_llseek,
	.read		= tty_read,
	.write		= tty_write,
	.poll		= tty_poll,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.release	= tty_release,
	.fasync		= tty_fasync,
	.show_fdinfo	= tty_show_fdinfo,
};

static int tty_open(struct inode *inode, struct file *filp)
{
    
    
	struct tty_struct *tty;
	int noctty, retval;
	dev_t device = inode->i_rdev;
	unsigned saved_flags = filp->f_flags;

	nonseekable_open(inode, filp);

retry_open:
	retval = tty_alloc_file(filp);
	if (retval)
		return -ENOMEM;
// 如果设备节点是(5,0)也就是/dev/tty, 表示当前TTY
// 对于普通串口, 第一次open时必定失败
	tty = tty_open_current_tty(device, filp);
	if (!tty)	// 第一次open串口时走这个分支
		tty = tty_open_by_driver(device, inode, filp);	// 通过driver来open tty,就是找到tty_driver,然后分配/设置tty_struct

	//...

// ops是tty_operations类型
// 对于串口ops就是serial_core.c中的uart_ops
// uart_open
	if (tty->ops->open)
		retval = tty->ops->open(tty, filp);
	else
		retval = -ENODEV;
	filp->f_flags = saved_flags;

	//...
	return 0;
}

static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
					     struct file *filp)
{
    
    
	struct tty_struct *tty;
	//...
	mutex_lock(&tty_mutex);
	driver = tty_lookup_driver(device, filp, &index);	// 1. 先找到对应的tty_driver
	//...
	/* check whether we're reopening an existing tty */
	tty = tty_driver_lookup_tty(driver, filp, index);	// 2. 如果曾经open过,会有对应的tty_struct
	//...
	if (tty) {
    
    
		//...
	} else {
    
     /* Returns with the tty_lock held for now */
		// 3. 第1打开这个串口时肯定没有对应的tty_struct
		// 所以使用下面的函数初始化设备
		tty = tty_init_dev(driver, index);
		mutex_unlock(&tty_mutex);
	}
out:
	tty_driver_kref_put(driver);
	return tty;
}
  • 找到tty_driver的时候
  • 构造一个tty_struct
  • 行规层相关的初始化。

2. 调用ops结构体,这个结构体是在serial_core.c里提供的。所以还有更底层的操作函数

static int tty_open(struct inode *inode, struct file *filp)
{
    
    
	struct tty_struct *tty;
	//...

	if (tty->ops->open)
		retval = tty->ops->open(tty, filp);		//在这里调用tty_struct->tty_operations->open函数
	else
		retval = -ENODEV;
	filp->f_flags = saved_flags;
	//...
	return 0;
}

static const struct tty_operations uart_ops = {
    
    
	.open		= uart_open,			//这里从tty_driver->open调用到uart_open
	//...
};

static int uart_open(struct tty_struct *tty, struct file *filp)
{
    
    
	//uart下有多个port,打开某个port
	retval = tty_port_open(&state->port, tty, filp);
	//...
	return retval;
}

int tty_port_open(struct tty_port *port, struct tty_struct *tty,
							struct file *filp)
{
    
    
	spin_lock_irq(&port->lock);
	++port->count;
	spin_unlock_irq(&port->lock);
	tty_port_tty_set(port, tty);

	/*
	 * Do the device-specific open only if the hardware isn't
	 * already initialized. Serialize open and shutdown using the
	 * port mutex.
	 */

	mutex_lock(&port->mutex);

	if (!tty_port_initialized(port)) {
    
    
		clear_bit(TTY_IO_ERROR, &tty->flags);
		if (port->ops->activate) {
    
    
			int retval = port->ops->activate(port, tty);		//激活某个port,
			//这里的函数调用tty_port->tty_port_operations->actived
			//...
		}
		tty_port_set_initialized(port, 1);
	}
	mutex_unlock(&port->mutex);
	return tty_port_block_til_ready(port, tty, filp);
}

struct tty_port {
    
    
	//...
	const struct tty_port_operations *ops;	/* Port operations */
	//...
};

//在serial_core.c中找到了tty_port_operations
static const struct tty_port_operations uart_port_ops = {
    
    
	.carrier_raised = uart_carrier_raised,
	.dtr_rts	= uart_dtr_rts,
	.activate	= uart_port_activate,		//最后调用的是uart_port_activate
	.shutdown	= uart_tty_port_shutdown,
};

static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
{
    
    
	//...
	return uart_startup(tty, state, 0);	//调用了uart_startup,启动一个tty(串口)
}

static int uart_startup(struct tty_struct *tty, struct uart_state *state,
		int init_hw)
{
    
    
	//...
	retval = uart_port_startup(tty, state, init_hw);	//继续调用
	//...
	return retval;
}

static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
		int init_hw)
{
    
    
	struct uart_port *uport = uart_port_check(state);
	unsigned long page;
	unsigned long flags = 0;
	int retval = 0;

	if (uport->type == PORT_UNKNOWN)
		return 1;

	/*
	 * Make sure the device is in D0 state.
	 */
	uart_change_pm(state, UART_PM_STATE_ON);

	/*
	 * Initialise and allocate the transmit and temporary
	 * buffer.
	 */
	page = get_zeroed_page(GFP_KERNEL);
	if (!page)
		return -ENOMEM;

	uart_port_lock(state, flags);
	if (!state->xmit.buf) {
    
    		//设置各种buff
		state->xmit.buf = (unsigned char *) page;
		uart_circ_clear(&state->xmit);
		uart_port_unlock(uport, flags);
	} else {
    
    
		uart_port_unlock(uport, flags);
		/*
		 * Do not free() the page under the port lock, see
		 * uart_shutdown().
		 */
		free_page(page);
	}

	retval = uport->ops->startup(uport);		//调用的是uart_port->uart_ops->startup
	if (retval == 0) {
    
    
		if (uart_console(uport) && uport->cons->cflag) {
    
    
			tty->termios.c_cflag = uport->cons->cflag;
			uport->cons->cflag = 0;
		}
		/*
		 * Initialise the hardware port settings.
		 */
		uart_change_speed(tty, state, NULL);

		/*
		 * Setup the RTS and DTR signals once the
		 * port is open and ready to respond.
		 */
		if (init_hw && C_BAUD(tty))
			uart_port_dtr_rts(uport, 1);
	}

	/*
	 * This is to allow setserial on this port. People may want to set
	 * port/irq/type and then reconfigure the port properly if it failed
	 * now.
	 */
	if (retval && capable(CAP_SYS_ADMIN))
		return 1;

	return retval;
}

从tty_operations调用到tty_port_operations,然后又调用到uart_ops。
如果写代码时需要提供startup代码,在imx.c文件中:

static const struct uart_ops imx_uart_pops = {
    
    
	//...
	.break_ctl	= imx_uart_break_ctl,
	.startup	= imx_uart_startup,			//回到最底层的startup
	.shutdown	= imx_uart_shutdown,
	//...
};

3. 调用硬件ops中的函数

猜你喜欢

转载自blog.csdn.net/ch122633/article/details/129689546