UART驱动情景分析-open
迪丽瓦拉
2025-06-01 09:48:41
0

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

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);elsetty_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);elsereturn 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_openif (tty->ops->open)retval = tty->ops->open(tty, filp);elseretval = -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函数elseretval = -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,打开某个portretval = 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) {		//设置各种buffstate->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->startupif (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中的函数

相关内容