一、设备名字的前缀的来源
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,
//...
};