只分析tty层驱动,涉及到到console和uart相关的部分暂时只简略介绍。
内核串口部分需要用到tty部分,tty包含了tty内核和tty线路规程。这些是在串口代码之前初始化。
1.tty_ldisc线路规程的初始化
只要是函数
driver/tty/tty_io.c
console_init()
void __init console_init(void) { initcall_t *call; /* Setup the default TTY line discipline. */ tty_ldisc_begin(); /* * set up the console device so that later boot sequences can * inform about problems etc.. */ call = __con_initcall_start; while (call < __con_initcall_end) { (*call)(); call++; } }
此处和tty相关的就是函数tty_ldisc_begin(),内核通过此函数来初始化tty线路规程的相关操作。
void tty_ldisc_begin(void) { /* Setup the default TTY line discipline. */ (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); }
此处先看函数调用的两个参数,其中第一个参数N_TTY是一个宏,第二个参数是内核tty线路规程的操作方法集
#define N_TTY 0 #define N_SLIP 1 #define N_MOUSE 2
struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", .open = n_tty_open, .close = n_tty_close, .flush_buffer = n_tty_flush_buffer, .chars_in_buffer = n_tty_chars_in_buffer, .read = n_tty_read, .write = n_tty_write, .ioctl = n_tty_ioctl, .set_termios = n_tty_set_termios, .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup };
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) { unsigned long flags; int ret = 0; if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; spin_lock_irqsave(&tty_ldisc_lock, flags); tty_ldiscs[disc] = new_ldisc; new_ldisc->num = disc; new_ldisc->refcount = 0; spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; }可以发现tty线路规程的操作很好理解。内核定义一个tty_ldiscs数组,然后根据数组下标来存放对应的线路规程的操作集,而这里的数组下标表示的就是具体的协议,在头文件中已经通过宏定义好了。例如N_TTY 0。
所以可以发现
ldisc[0] 存放的是N_TTY对应的线路规程操作集
ldisc[1]存放的是N_SLIP对应的线路规程操作集
ldisc[2]存放的就是N_MOUSE对应的线路规程操作集
依次类推。
此处就是ldisc[N_TTY] = tty_ldisc_N_TTY。
然后函数退出,最后返回到console_init()函数。
假如串口作为console,则console_init()会通过剩余的代码调用s3c_serial_console_init()执行console相关的初始化了。
while (call < __con_initcall_end) { (*call)(); call++; }由于本文只分析不做console的串口驱动,所以这部分代码和串口无关。console_init()函数和串口相关的操作就是
之前调用的tty_ldisc_begin函数了。这个tty_ldisc初始化到这一步就暂时放在一边,待到后面通过open函数中再来调用
此处已注册的的tty_disc线路规程
2.tty_driver初始化 uart_driver中uart_state的初始化
2.1tty_driver相关部分的初始化
初始化线路规程后,系统开始初始化tty_driver。
而tty_driver需要初始化很多字段例如
tty_driver.owner
tty_driver.driver_name
tty_driver.name
tty_driver.major
tty_driver.minor_start
tty_driver.type
这些字段根据不同的tty_driver需要初始化成不同的参数。
根据LDD3的解释,内核的tty_driver有三种:控制台、串口、pty。这三种对应着三种不同的tty_driver,而这三种tty_driver对应的上述字段是不同的。
那么这些参数来源哪里呢?来源于程序员的定义,编译之初确定好这些参数后,内核最后会将其赋值给tty_driver。
那这些参数定义在哪里呢?定义在uart_driver中,这个就是驱动需要修改的一个数据结构。
这就相当于在tty_driver外面就加了一层uart_driver。而uart_driver中的部分字段和tty_driver中的部分字段数值是一样的。
此处的uart_driver定义如下:
static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .dev_name = "s3c2410_serial", .nr = CONFIG_SERIAL_SAMSUNG_UARTS, .cons = S3C24XX_SERIAL_CONSOLE, .driver_name = S3C24XX_SERIAL_NAME, .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR, };
内核通过
driver/serial/samsung.c文件中的
s3c24xx_modinit()函数来进行tty_driver的初始化,参数就是韦uart_driver结构的s3c24xx_uart_drv。
static int __init s3c24xx_serial_modinit(void) { int ret; ret = uart_register_driver(&s3c24xx_uart_drv); if (ret < 0) { printk(KERN_ERR "failed to register UART driver\n"); return -1; } return 0; }uart_register_driver函数如下:
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 = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); if (!drv->state) goto out; normal = alloc_tty_driver(drv->nr); if (!normal) goto out_kfree; drv->tty_driver = normal; 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_set_operations(normal, &uart_ops); /* * Initialise the UART state(s). */ for (i = 0; i < drv->nr; i++) { struct uart_state *state = drv->state + i; struct tty_port *port = &state->port; tty_port_init(port); port->ops = &uart_port_ops; port->close_delay = 500; /* .5 seconds */ port->closing_wait = 30000; /* 30 seconds */ tasklet_init(&state->tlet, uart_tasklet_action, (unsigned long)state); } retval = tty_register_driver(normal); if (retval >= 0) return retval; put_tty_driver(normal); out_kfree: kfree(drv->state); out: return -ENOMEM; }
这个函数就是用来初始化tty_drive的,并且可以看见tty_driver中的字段初始化的数值就是函数传递进来的uart_driver中的对应字段。
此外此函数还需要注意的地方都已经标红。
其中,通过 tty_set_operations(normal, &uart_ops);
指定了tty_driver的操作集tty_operation为uart_ops,假如是其他的tty_driver相信这个ops也是需要修改的。
uart_ops定义如下
static const struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, .write = uart_write, .put_char = uart_put_char, .flush_chars = uart_flush_chars, .write_room = uart_write_room, .chars_in_buffer= uart_chars_in_buffer, .flush_buffer = uart_flush_buffer, .ioctl = uart_ioctl, .throttle = uart_throttle, .unthrottle = uart_unthrottle, .send_xchar = uart_send_xchar, .set_termios = uart_set_termios, .set_ldisc = uart_set_ldisc, .stop = uart_stop, .start = uart_start, .hangup = uart_hangup, .break_ctl = uart_break_ctl, .wait_until_sent= uart_wait_until_sent, #ifdef CONFIG_PROC_FS .proc_fops = &uart_proc_fops, #endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset, .get_icount = uart_get_icount, #ifdef CONFIG_CONSOLE_POLL .poll_init = uart_poll_init, .poll_get_char = uart_poll_get_char, .poll_put_char = uart_poll_put_char, #endif };然后函数通过
retval = tty_register_driver(normal);
实现tty_driver的"注册"。具体的注册函数如下:
int tty_register_driver(struct tty_driver *driver) { int error; int i; dev_t dev; void **p = NULL; struct device *d; if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL); //注意这一句,为tty_driver分配了一个tty_struct *指针数组,并且使用的kzalloc方式也就是说。初始化的时候该数组内容是零!这点在tty_open判断的时候会用到! if (!p) return -ENOMEM; } 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) { kfree(p); return error; } if (p) { driver->ttys = (struct tty_struct **)p; driver->termios = (struct ktermios **)(p + driver->num); } else { driver->ttys = NULL; driver->termios = NULL; } cdev_init(&driver->cdev, &tty_fops); driver->cdev.owner = driver->owner; error = cdev_add(&driver->cdev, dev, driver->num); if (error) { unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; driver->termios = NULL; kfree(p); return error; } 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; } } } proc_tty_register_driver(driver); driver->flags |= TTY_DRIVER_INSTALLED; return 0; err: for (i--; i >= 0; i--) tty_unregister_device(driver, i); mutex_lock(&tty_mutex); list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; driver->termios = NULL; kfree(p); return error; }该“注册”主要包含了如下工作:
1.根据主设备号、次设备号,注册一个字符驱动,其操作集为tty_fops
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, };
2.将tty_driver添加到tty_driver链表tty_drivers中,内核通过维护tty_drivers链表来维护tty_driver。
前面提到了内核的tty_driver主要包含三大类console、串口、pty。
完成之后tty_driver初始化也结束,然后进去s3c24xx的uart相关的初始化。
2.uart_driver的部分初始化
此函数关于uart_driver部分的初始化主要是初始化了uart_driver中的uart_state这个成员。
uart_state也是一个结构体,其中包含uart_pot、tty_port两个成员。而系统中对具体硬件的操作就是通过uart_port.ops
来操作的,这个uart_port需要重点留意下,但是在此函数中就只初始化了tty_port这个成员,uart_port的初始化
在后面的platform_driver.probe函数中初始化,这也正好说明了uart_port是和具体硬件精密相关的。
3.uart_port初始化
uart_port的初始化是通过platform_driver中的probe函数来完成的。如何调用到probe函数的就不说了。
probe函数如下static int s3c2440_serial_probe(struct platform_device *dev) { dbg("s3c2440_serial_probe: dev=%p\n", dev); return s3c24xx_serial_probe(dev, &s3c2440_uart_inf); }其第二个参数s3c2440_uart_inf如下
static struct s3c24xx_uart_info s3c2440_uart_inf = { .name = "Samsung S3C2440 UART", .type = PORT_S3C2440, .fifosize = 64, .rx_fifomask = S3C2440_UFSTAT_RXMASK, .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, .rx_fifofull = S3C2440_UFSTAT_RXFULL, .tx_fifofull = S3C2440_UFSTAT_TXFULL, .tx_fifomask = S3C2440_UFSTAT_TXMASK, .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, .get_clksrc = s3c2440_serial_getsource, .set_clksrc = s3c2440_serial_setsource, .reset_port = s3c2440_serial_resetport, };
先简单介绍下uart_port结构的初始化。uart_port和系统中具体的硬件对应,每个uart都有自己对应的uart_port。而这个结构体的初始化由点类似于
前面说的tty_driver,并不是直接初始化tty_driver的,而是在tty_driver外面包了一层uart_driver。此处也是的在uart_port外面包了一层struct s3c24xx_uart_port 。
这样s3c24xx_uart_port除了包含uart_port这个重要结构外还可以存放s3c24xx系列串口的特有信息。
uart_port的初始化信息主要来源于struct s3c24xx_uart_info,如上所示。这些数值会在probe函数中赋值给uart_port用于初始化。
初始化uart_port之后就是调用函数将这个初始化好的uart_port和前面的uart_driver关联上。因为uart_driver中即有tty相关的tty_driver又有好友uart_port信息的uart_state
,这样tty和uart就有可能关联上了。下面从probe函数开始分析具体代码。
s3c24xx_serial_probe函数如下:
int s3c24xx_serial_probe(struct platform_device *dev, struct s3c24xx_uart_info *info) { struct s3c24xx_uart_port *ourport; int ret; dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); ourport = &s3c24xx_serial_ports[probe_index]; probe_index++; dbg("%s: initialising port %p...\n", __func__, ourport); ret = s3c24xx_serial_init_port(ourport, info, dev); if (ret < 0) goto probe_err; dbg("%s: adding port\n", __func__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(dev, &ourport->port); ret = device_create_file(&dev->dev, &dev_attr_clock_source); if (ret < 0) printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) dev_err(&dev->dev, "failed to add cpufreq notifier\n"); return 0; probe_err: return ret; }
函数总重要的语句全部标红了。先分析第一句
ourport = &s3c24xx_serial_ports[probe_index];
这个主要是从串口数组s3c24xx_serial_ports中选定初始化的串口。这里需要注意的uart_port中的ops,标红的那句。这里是具体操作s3c24xx串口的操作函数集。
后面具体的write read等函数最终调用的是这里操作集,都是最底层的硬件操作。
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
}
},
[1] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX1,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
[2] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX2,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
[3] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX3,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 3,
}
}
#endif
};
确定将要初始化的uart后,通过函数
ret = s3c24xx_serial_init_port(ourport, info, dev);
uart_port函数具体的初始化就不展开分析了,里面都是些赋值的操作。
接着分析uart_port和uart_driver关联的操作。这一步是通过函数
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);实现的。可以发现这个函数的第一个参数uart_driver是s3c24xx_uart_drv,和前面tty_driver初始化中提到的通过uart_register_driver注册的uart_driver是一样的!
这样uart_driver中的uart_state也就初始化好了,其中uart_state包含了uart_port的信息。这个uart_port是和具体的串口硬件相关的。
总结
到这一步所有的tty和uart初始化的部分算是完成了。但是目前还不能进行read、write操作。
因为还有一个重要的函数tty_open没有分析,作为tty设备的uart,这个tty_open函数也是非常重要的,下篇分析。
初始化的工作主要是
1.初始化uart_driver结构体,包括初始化uart_driver结构体中的tty_driver uart_state。
2.uart_state部分的初始化主要是初始化其中的uart_port,这部分的初始化在probe函数总完成
修改驱动需要设计的数据结构
1.uart_driver
uart_driver中的数据用于初始化tty_driver
2.s3c24xx_uart_info
用于初始化uart_port
3.s3c24xx_uart_port或者说uart_port。
uart_port的初始化,在s3c24xx的uart驱动中uart_port是内嵌在s3c24xx_uart_port中的
4.uart_ops
最底层的硬件操作。