注意 :因为 tty_driver核心层 和 uart_driver 设备层 的注册是绑在一起的,
所以把 核心层和设备层一起分析
一:tty设备驱动关键步骤
设备驱动层的注册其实一共只有三步骤:
1 构造 uart设备相关驱动结构体
2 uart_register_driver{tty_register_driver}//同时注册了tty_driver
3 uart_add_one_port
/kenrel/drivers/tty/serial XXX_uart.c
1
struct XXX_port{
struct uart_port port;
.....
}
static struct uart_driver XXX_uart_driver{
owner: THIS_MODULE,
major: SERIAL_WK2XXX_MAJOR,
driver_name: "ttySWK",
dev_name: "ttysWK",
minor: MINOR_START,
nr: NR_PORTS,
cons: NULL//WK2Xxx_CONSOLE,
}
static struct uart_ops wk2xxx_pops = {
tx_empty: wk2xxx_tx_empty,
set_mctrl: wk2xxx_set_mctrl,
get_mctrl: wk2xxx_get_mctrl,
stop_tx: wk2xxx_stop_tx,
start_tx: wk2xxx_start_tx,
stop_rx: wk2xxx_stop_rx,
enable_ms: wk2xxx_enable_ms,
break_ctl: wk2xxx_break_ctl,
startup: wk2xxx_startup,
shutdown: wk2xxx_shutdown,
set_termios: wk2xxx_termios,
type: wk2xxx_type,
release_port: wk2xxx_release_port,
request_port: wk2xxx_request_port,
config_port: wk2xxx_config_port,
verify_port: wk2xxx_verify_port,
};
2
//其实 tty_driver 就是 uart_driver 的另一个封装
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
int i, retval;
//根据driver支持的最大设备数,申请n个 uart_state 空间,每一个 uart_state 都有一个uart_port
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
//tty层:分配一个 tty_driver ,并将drv->tty_driver 指向它 uart_driver->tty_driver
normal = alloc_tty_driver(drv->nr);
drv->tty_driver = normal;
//对 tty_driver 进行设置
normal->owner = drv->owner;
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;
//设置tty_driver 的操作函数集 为 uart_ops函数集
// tty_driver->ops = tty_operations uart_ops
tty_set_operations(normal, &uart_ops);
//初始化 uart_state,此时并没用和具体的 uart_port 对应起来;
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port; /* driver->state->tty_port */
tty_port_init(port);
port->close_delay = 500; /* .5 seconds */
port->closing_wait = 30000; /* 30 seconds */
/* 初始化 tasklet */
tasklet_init(&state->tlet, uart_tasklet_action,
(unsigned long)state);
}
//tty层:注册 driver->tty_driver */
retval = tty_register_driver(normal);
}
2.1
//为 tty_driver 创建字符设备、挂载tty_driver到链表、创建proc下的节点、
//此处注册了tty_driver驱动(添加链表)、但是还没有对接任何物理端口(uart_port)、
//也没有创建 /dev/ 下的任何设备节点;
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
struct device *d;
//申请主设备号
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
}
if (error < 0)
goto err;
//添加一个自字符设备,file_operations和设备号关联起来,
if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
error = tty_cdev_add(driver, dev, 0, driver->num);
if (error)
goto err_unreg_char;
}
//将该driver->tty_drivers 添加到 全局tty_drivers链表
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
mutex_unlock(&tty_mutex);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);
if (IS_ERR(d)) {
error = PTR_ERR(d);
goto err_unreg_devs;
}
}
}
/* proc 文件系统注册driver */
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED;
return 0;
.....
}
3
//为 uart_driver(tty_driver) 添加端口、
//最终上层可以通过 ->tty_driver->uart_driver->uart_port->uart_ops 操作底层
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;
....
//将 uart_port 绑定到 uart_driver 的 state
state->uart_port = uport;
uport->state = state;
....
//创建设备节点(有点特别)
tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
{
tty_line_name(driver, index, name);
....
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);
retval = device_register(dev);
}
....
}
二 :汇总:上述代码工作汇总如下:
2.1 :
uart_register_driver
{
1 // 根据平台支持的串口数目 申请 N 个uart_state 空间
2 // 分配一个tty_driver,并且绑定到 uart_driver->tty_driver
3 // 对 tty_driver 进行设置,拷贝 uart_driver 的参数(tty_driver == uart_driver)
4 // 设置 tty_driver 的操作函数为 struct tty_operations uart_ops
5 // 初始化uart_state,但是此时并没有和具体 uart_port 物理端口对应起来
6 // 注册 tty_driver tty_register_driver
tty_register_driver
{
6.1 //申请设备号
6.2 //添加一个字符设备,file_operations,但此时并没有设备节点的创建
6.3 //将 tty_drivers 添加到 全局tty_drivers链表
6.4 //向proc文件系统注册 tty_drivers
}
}
2.2:
uart_add_one_port
{
1 //将 uart_port 绑定到 uart_driver->uart_state
2 //创建设备节点
}
三 :总结
疑问:通过研究代码发现 tty_driver 和 uart_driver 两个结构体的成员很相似,并且在
注册 uart_driver 的时候,会将 uart_driver 的很多参数传递赋值给 tty_driver
的同名参数,这两个结构体至今有啥关系吗?
答案:前面说到,tty作为终端设备,按照LDD3的说法,tty类型的终端设备分为三种类型
串口终端、虚拟终端、控制台终端;tty_driver->type 参数就是终端驱动类型的标志
type 由 uart_driver->type 传入。所以可以理解为 tty_driver代表的是 串口终端
uart_driver 、虚拟终端uart_driver 、 控制台终端uart_driver 三种类型终端驱动
的一个抽象,这个抽象是给上层用的,用来区分出底层的设备驱动是哪一种类型的驱动
小结:在注册设备驱动 uart_driver 的同时 向上注册由 uart_driver 抽象出来的 tty_driver 驱动
向下 创建 uart_state 、 并且绑定 uart_port 到 uart_driver->uart_state;这样就将底层设备
驱动和核心层联系起来了;