http://blog.chinaunix.net/u3/118615/showart_2320858.html
1概述
这里的uart设备是指物理设备,例如MPC8xx中CPM中的SMC,这个设备可以工作在uart模式下。
在linux中,uart设备可以抽象成两类设备:serial设备和console设备。在cpm_uart_core.c中可以清楚的发现,对uart设备实现了两套驱动,分别针对这两类设备。Serial设备的抽象定义在serial_core.c中,而console设备的抽象定义在vt.c中。这两种抽象设备又统一于tty设备。tty设备是一种字符型设备。
Console设备只用于内核空间的printk打印;而serial设备则可用于用户空间。
2 serial设备驱动分析
2.1核心数据结构是:uart_driver
struct uart_driver {
struct
module*owner;
const
char*driver_name;
const
char*dev_name;
int major;
int minor;
int nr;
struct
console*cons;
struct
uart_state*state;
struct
tty_driver*tty_driver;
};
其中tty_driver结构用于向tty层注册,而uart_state结构用于管理物理设备。
在cpm_uart_core.c中有如下定义:
static struct uart_driver cpm_reg = {
.owner= THIS_MODULE,
.driver_name= "ttyCPM",
.dev_name= "ttyCPM",
.major= SERIAL_CPM_MAJOR,
.minor= SERIAL_CPM_MINOR,
.cons= CPM_UART_CONSOLE,
.nr= UART_NR,
};
2.2 cpm_uart_init
这个是模块的初始化函数,从这里开始分析。
2.2.1uart_register_driver()
传入的参数就是上面提到的cpm_reg。
(1)申请数据结构
申请UART_NR个uart_state,并使cpm_reg.state指针指向新申请的空间;
申请UART_NR个tty_driver,并使cpm_reg.tty_driver指针指向新申请的空间;
UART_NR,其值的实际大小定义在cpm_uart.h中:
#define UART_NRfs_uart_nr
fs_uart_nr定义在fs_uart_pd.h中:
enum fs_uart_id
{
fsid_smc1_uart,
fsid_smc2_uart,
fsid_scc1_uart,
fsid_scc2_uart,
fsid_scc3_uart,
fsid_scc4_uart,
fs_uart_nr,
};
(2)初始化cpm_reg.tty_driver
1)结构中的变量;
2)使cpm_reg.tty_driver->driver_state指针指向cpm_reg;
3)tty_set_operations(normal,
&uart_ops);
设定tty_driver的操作函数集为uart_ops。Uart_ops是定义在serial_core.c中静态变量:
static
const struct tty_operations uart_ops = {
.open= uart_open,
.close= uart_close,
.write= uart_write,
.put_char= uart_put_char,
……
};
从这里可以看出,很明显serial设备层是对各种uart物理设备的一种抽象,统一使用同样的操作函数。
(3)初始化cpm_reg.state
1)结构变量值
2)cpm_reg.state->uart_info
3)tasklet下半部注册
2.2.2tty_register_driver
传入的参数是cpm_reg->tty_driver。
(1)将tty_driver代表的tty设备注册为字符设备
(2)设定字符设备的操作函数集为静态定义的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,
};
从这里看出,tty设备层已经完成了对所有tty设备的抽象,提供了统一的操作函数集。
(3)将tty_driver加到系统的tty_driver链表
2.3 of_register_platform_driver
2.2中,主要完成了uart层、tty层、chardev层三层的注册工作,但是,经过层层调用,最终一定要调用到具体的硬件设备操作函数才行。现在就是初始化这一部分内容。
首先明确一点,硬件设备的驱动和cpm_reg->state->port相关连。很多硬件驱动函数的入参就是uart_port结构。数据结构的联系是:
Uart_driver
+
uart_state
+
uart_port
在2.2中,uart_port结构没有进行任何初始化,而这个工作就是下面要描述的过程。经过of_register_platform_driver的调用,会回调执行cpm_uart_probe函数,这个函数就是对uart_port数据结构的完善。
Uart_port结构中有一个操作函数集指针:
const
struct uart_ops*ops;
这个指针应指向对应的物理设备的操作函数集,在cpm_uart_core.c中,就是指:
static struct uart_ops cpm_uart_pops = {
.tx_empty= cpm_uart_tx_empty,
.set_mctrl= cpm_uart_set_mctrl,
.get_mctrl= cpm_uart_get_mctrl,
.stop_tx= cpm_uart_stop_tx,
.start_tx= cpm_uart_start_tx,
.stop_rx= cpm_uart_stop_rx,
.enable_ms= cpm_uart_enable_ms,
.break_ctl= cpm_uart_break_ctl,
.startup= cpm_uart_startup,
.shutdown= cpm_uart_shutdown,
.set_termios= cpm_uart_set_termios,
.type= cpm_uart_type,
.release_port= cpm_uart_release_port,
.request_port= cpm_uart_request_port,
.config_port= cpm_uart_config_port,
.verify_port= cpm_uart_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char
= cpm_get_poll_char,
.poll_put_char
= cpm_put_poll_char,
#endif
};
2.3.1cpm_uart_ports[UART_NR]
这个数据结构代表了物理设备,它在uart_port结构的基础上,增加很多物理设备自有的特性。cpm_reg->state->port最终会指向这里面的uart_port port。这样uart_driver层就和物理驱动层建立了联系。
struct uart_cpm_port {
struct
uart_portport;
u16rx_nrfifos;
u16rx_fifosize;
u16tx_nrfifos;
…….
u32command;
intgpios[NUM_GPIOS];
};
struct uart_cpm_port
cpm_uart_ports[UART_NR];
2.3.2cpm_uart_init_port
这个函数用于初始化cpm_uart_ports及其中的uart_port结构。这个地方会根据open firmware中的fdt树对uart接口的描述来进行初始化。
值得注意的是,用户对uart的一些初始化就可以写在这里。
2.3.3uart_add_one_port
uart_add_one_port(&cpm_reg,
&pinfo->port);
这样其入参,cpm_reg自然是uart_driver,而pinfo是指cpm_uart_ports,其内部的port自然是uart_port。
这个函数最大的作用就是使cpm_reg->state->port指向这里面的uart_port port。
3 console设备驱动分析
3.1 vty_init
3.1.1入参console_fops
tty_io.c中的tty_init函数会调用vt.c中的vty_init函数。所以初始化的起点可以定为vty_init函数。
该函数的入参是
static const struct file_operations
console_fops = {
.llseek= no_llseek,
.read= tty_read,
.write= redirected_tty_write,
.poll= tty_poll,
.unlocked_ioctl= tty_ioctl,
.compat_ioctl= tty_compat_ioctl,
.open= tty_open,
.release= tty_release,
.fasync= tty_fasync,
};
3.1.2注册为字符设备
注册时,使用的操作函数集正是console_fops。这个console_fops和前面提到的tty_fops同样定义在tty_io.c中。仔细对比就会发现二者几乎相同。所以说,serial设备和console设备统一与tty设备。
3.1.3设定tty驱动操作函数集
tty_set_operations(console_driver,
&con_ops);
这个con_ops定义在vt.c,
static const struct tty_operations con_ops
= {
.open
= con_open,
.close
= con_close,
.write
= con_write,
.write_room
= con_write_room,
.put_char
= con_put_char,
.flush_chars
= con_flush_chars,
.chars_in_buffer
= con_chars_in_buffer,
.ioctl
= vt_ioctl,
.stop
= con_stop,
.start
= con_start,
.throttle
= con_throttle,
.unthrottle
= con_unthrottle,
.resize
= vt_resize,
.shutdown
= con_shutdown
};
其可类比于定义于serial_core.c中的uart_ops。很明显这是两个不同的tty驱动集。
3.2 cpm_uart_console_init
在cpm_uart_core.c中,通过声明
console_initcall(cpm_uart_console_init);
那么,cpm_uart_console_init函数就会被调用。cpm_uart_console_init调用了register_console函数,调用形式如下:
register_console(&cpm_scc_uart_console);
其中cpm_scc_uart_console是console类型的静态数据结构变量:
static struct console cpm_scc_uart_console
= {
.name= "ttyCPM",
.write= cpm_uart_console_write,
.device= uart_console_device,
.setup= cpm_uart_console_setup,
.flags= CON_PRINTBUFFER,
.index= -1,
.data= &cpm_reg,
};
很明显,里面的驱动函数是对uart硬件的又一套驱动。可以类比于static struct uart_ops
cpm_uart_pops定义。