欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux2.6.22.6串口驱动分析

程序员文章站 2022-03-07 13:21:18
...

//kernel启动第二阶段=======================
start_kernel
setup_arch(&command_line);
paging_init(&meminfo, mdesc);

mdesc->map_io(); //映射IO
<=>smdk2440_map_io

..处理参数u-boot传进来的参数.用 __setup 和 early_param 标记...
....
console_init
	tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);	//(int disc, struct tty_ldisc *new_ldisc)	//注册行规程
		tty_ldiscs[disc] = *new_ldisc;		//static struct tty_ldisc tty_ldiscs[17];	/* line disc dispatch table */
	call = __con_initcall_start;
	while (call < __con_initcall_end) {		//调用__con_initcall_start和__con_initcall_end质检的所有函数,包含了console_initcall函数
		(	*call)();
	call++;
	}

.......
rest_init(); 
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //创建kernel_init

===>kernel_init
prepare_namespace();
init_post();
sys_open((const char __user *) “/dev/console”, O_RDWR, 0) < 0) //标准输入设备为/dev/console
(void) sys_dup(0); //标准输出
(void) sys_dup(0); //标准错误

		run_init_process(execute_command);
		run_init_process("/sbin/init");					//运行init进程,只要有一个成功就一去不复返
		run_init_process("/etc/init");
		run_init_process("/bin/init");
		run_init_process("/bin/sh");
		panic("No init found.  Try passing init= option to kernel.");

//=======================================================
在内核第二阶段时会处理u-boot传入的bootargs参数,分为early和非early
对于 “console = ttySAC0,115200”,使用console_setup来处理

__setup(“console=”, console_setup); //使用console_setup函数来处理参数“console=ttySAC0”,设备名为ttySAC,索引为0,
//这些信息被保存在类型为console_cmdline 、名称为console_cmdline的全局结构中
kernel/printk.c L655:
static int __init console_setup(char *str)
{
char name[sizeof(console_cmdline[0].name)];
char *s, *options;
int idx;

	/*
	 * Decode str into name, index, options.
	 */
	if (str[0] >= '0' && str[0] <= '9') {////如果以数字0-9开头
		strcpy(name, "ttyS");
		strncpy(name + 4, str, sizeof(name) - 5);
	} else {
		strncpy(name, str, sizeof(name) - 1);		//name = ttySAC0
	}
	name[sizeof(name) - 1] = 0;
	if ((options = strchr(str, ',')) != NULL)	////如果参数中存在,的话。说明带波特率参数
		*(options++) = 0;
#ifdef __sparc__
	if (!strcmp(str, "ttya"))
		strcpy(name, "ttyS0");
	if (!strcmp(str, "ttyb"))
		strcpy(name, "ttyS1");
#endif
	for (s = name; *s; s++)
		if ((*s >= '0' && *s <= '9') || *s == ',')
			break;
	idx = simple_strtoul(s, NULL, 10);	////取出波特率参数,转换成整形
	*s = 0;
	
	//name = ttySAC,idx=0,options=115200
	add_preferred_console(name, idx, options);			//若已存在,则确定selected_console 并将参数保存在 console_cmdline结构体数组 中,见下,并确定selected_console
		for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
		 if (strcmp(console_cmdline[i].name, name) == 0 && console_cmdline[i].index == idx)
		 {
			selected_console = i;				//static int selected_console = -1;selected_console是静态全局的
			return 0;
		 }
		 if (i == MAX_CMDLINECONSOLES)		//代表满了
			return -E2BIG;
		selected_console = i;
		c = &console_cmdline[i];
		memcpy(c->name, name, sizeof(c->name));
		c->options = options;
		c->index = idx;
	return 1;
}

__setup(“console=”, console_setup); //使用console_setup函数来处理参数“console=ttySAC0”,设备名为ttySAC,索引为0

kernel/printk.c L101:
struct console_cmdline
{
char name[8]; /* Name of the driver /
int index; /
Minor dev. to use */
char options; / Options for the driver */
};

#define MAX_CMDLINECONSOLES 8
static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; //8

//kernel/printk.c==========
printk(const char *fmt, …)
va_start(args, fmt);
r = vprintk(fmt, args);
char printk_buf[1024];
vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); //先把信息放入临时buffer
for (p = printk_buf; *p; p++)
{
char tbuf[50], *tp;
…//判断打印级别 若没有,则默认级别是4
for (tp = tbuf; tp < tbuf + tlen; tp++)
emit_log_char(*tp); //(char c)写入log_buf
LOG_BUF(log_end) = c;
}
release_console_sem(); //register_console时也会调用这个函数
call_console_drivers(_con_start, _log_end)
…获取msg的打印级别
_call_console_drivers(start_print, cur_index, msg_level); //msg够格则打印,默认小于7打印
if((msg_log_level < console_loglevel || ignore_loglevel) &&console_drivers && start != end)
__call_console_drivers
for (con = console_drivers; con; con = con->next) //调用struct console *console_drivers;里链表结构的每一个console来打印
if ((con->flags & CON_ENABLED) && con->write…) //如果console使能了
con->write(con, &LOG_BUF(start), end - start); //打印
==>如 s3c24xx_serial_console_write
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar)
s3c24xx_serial_console_putchar
…见L153…
wr_regb(cons_uart, S3C2410_UTXH, ch);

va_end(args);

Q:那么console_drivers的结构里的struct console *next;链表在哪里注册呢?
A:使用register_console(struct console *console)来注册

//=drivers\serial\s3c2410.c==========

static struct console s3c24xx_serial_console =
{
.name = S3C24XX_SERIAL_NAME, //“ttySAC”
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
.index = -1, //表示使用命令行解析出来的索引
.write = s3c24xx_serial_console_write, //串口控制台的输出函数,见下
.setup = s3c24xx_serial_console_setup
//.data = &s3c24xx_uart_drv; 在s3c24xx_serial_initconsole函数中设置
};

s3c24xx_serial_initconsole(void) // s3c24xx_serial_initconsole在内核启动第二阶段的console_init()被调用
s3c24xx_serial_console.data = &s3c24xx_uart_drv; //console结构的成员data被指向uart_driver结构
s3c24xx_serial_init_ports(info);
register_console(&s3c24xx_serial_console);
if (preferred_console < 0) //一开始是static int preferred_console = -1;
{
console->setup(console, NULL)
==>s3c24xx_serial_console_setup(struct console *co, char *options)
if(co->index == -1)
co->index = 0;
struct uart_port *port = &s3c24xx_serial_ports[co->index].port; //设置了全局的 static struct uart_port *cons_uart;
cons_uart = port;
console->flags |= CON_ENABLED | CON_CONSDEV; //标记为使能|添加到链表前面
preferred_console = 0;
}
con_start = log_start;
release_console_sem(); //见L121
console_initcall(s3c24xx_serial_initconsole); //在内核代码执行的第二阶段被调用

s3c24xx_serial_console_write(struct console *co, const char *s,unsigned int count)
uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);//(struct uart_port *port, const char *s,unsigned int count,void (*putchar)(struct uart_port *, int))
for (i = 0; i < count; i++, s++) {
if (*s == ‘\n’)
putchar(port, ‘\r’);
putchar(port, *s);
//即 s3c24xx_serial_console_putchar(struct uart_port *port, int ch)

}

static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); //在register_console中设置cons_uart = &s3c24xx_serial_ports[co->index].port;
while (!s3c24xx_serial_console_txrdy(port, ufcon))
barrier();
wr_regb(cons_uart, S3C2410_UTXH, ch);
}
从上面代码得知,从串口输出printk打印信息时,是一个字符一个字符的发送、等待发送完成、发送、接着等待…效率很低

//====driver/char/tty_io.c=
对于sys_open((const char __user *) “/dev/console”, O_RDWR, 0) < 0) //标准输入设备为/dev/console
/dev/console设备的创造在driver/char/tty_io.c中
tty_class_init
tty_class = class_create(THIS_MODULE, “tty”);

tty_init
cdev_init(&console_cdev, &console_fops);
cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) //TTYAUX_MAJOR为5 /dev/console的主设备号为5,次设备号为1
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, “/dev/console”)
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), “console”);

sys_open((const char __user *) “/dev/console”, O_RDWR, 0) < 0)

tty_open(struct inode * inode, struct file * filp) //见下面具体的分析

//=======================================

module_init(s3c24xx_serial_modinit);
s3c24xx_serial_modinit
//注册 uart_driver 实际上是注册 tty_driver,因此与用户空间打交道的工作完全交给了 tty_driver ,
uart_register_driver(&s3c24xx_uart_drv); //(struct uart_driver *drv),实际上是根据 s3c24xx_uart_drv 注册了tty_driver
tty_driver *normal = alloc_tty_driver(drv->nr); //(int lines)
struct tty_driver *driver=kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
driver->num = lines;return driver;//次设备号的个数,每个设备文件都会对应一个line.
// 根据driver支持的最大设备数,申请n个 uart_state 空间,每一个 uart_state 都有一个 uart_port
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); //struct uart_state含一个uart_port,在uart_add_one_port中需要设置它,uart_port中含struct uart_ops *ops;
drv->tty_driver = normal //uart_driver结构的tty_driver成员被设置为了此normal
//设置tty_driver
normal->name = drv->dev_name; //“s3c2410_serial”
normal->major = drv->major; //204
normal->minor_start = drv->minor; //64
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;

tty_set_operations(normal, &uart_ops);//(struct tty_driver *driver,const struct tty_operations op)
driver->open = op->open; //tty_driver 结构里的操作函数实际上被赋值为了tty_operations里相应的读写函数
driver->write = op->write;
driver->put_char = op->put_char
driver->stop = op->stop;
driver->start = op->start

//向上层注册 tty_driver
tty_register_driver(normal);//(struct tty_driver driver)
alloc_chrdev_region(&dev, driver->minor_start, driver->num,driver->name);
if § { /
为线路规程和termios分配空间 并使 tty_driver 相应的成员指向它们
/
driver->ttys = (struct tty_struct **)p;
driver->termios = (struct ktermios *)(p + driver->num);
} else {
driver->ttys = NULL;
driver->termios = NULL;
}
/
创建字符设备,使用 file_operations tty_fops /
cdev_init(&driver->cdev, &tty_fops); //const struct file_operations tty_fops
//另外在driver/char/tty_io.c中也有tty_fops被注册
cdev_add(&driver->cdev, dev, driver->num);
list_add(&driver->tty_drivers, &tty_drivers); //将tty_driver加入全局链表tty_drivers中// struct list_head tty_drivers;
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) ////如果没有指定TTY_DRIVER_DYNAMIC_DEV.即动态设备管理
for(i = 0; i < driver->num; i++) //L16:normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;故以下这些不会被调用
tty_register_device(driver, i, NULL);
pty_line_name(driver, index, name); //"s3c2410_serial"后加0 1 2
device_create(tty_class, device, dev, name); //tty_class在加载模块时被注册 postcore_initcall(tty_class_init); tty_class_init()-> tty_class = class_create(THIS_MODULE, “tty”);
proc_tty_register_driver(driver); /
proc 文件系统注册driver */
s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf); //name = “s3c2440-uart”,
->platform_driver_register(drv);
////有同名的平台设备就会调用.probe
drv.probe=s3c2440_serial_probe
->3c24xx_serial_probe(dev, &s3c2440_uart_inf);
struct s3c24xx_uart_port * ourport = &s3c24xx_serial_ports[probe_index]; //struct s3c24xx_uart_port内含struct uart_port ,uart_port内含struct uart_ops *ops;
s3c24xx_serial_init_port(ourport, info, dev);
struct uart_port *port = &ourport->port; //s3c24xx_serial_ports[0]里的port
struct s3c2410_uartcfg *cfg;
struct resource res;
res = platform_get_resource(platdev, IORESOURCE_MEM, 0); //获取dev资源,资源详见内核代码执行过程
port->mapbase = res->start;//设置物理地址,虚拟地址
port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
port->irq = platform_get_irq(platdev, 0); //中断号
ourport->clk = clk_get(&platdev->dev, “uart”);
s3c24xx_serial_resetport(port, cfg);/
reset the fifos (and setup the uart) */

					/* 将 uart_port 注册到 uart_driver */
					uart_add_one_port(&s3c24xx_uart_drv, &ourport->port)	//(struct uart_driver *drv, struct uart_port *port)
						struct uart_state * state = drv->state + port->line;/* 将 uart_prot 绑定到 uart_driver 对应的 state */
						state->port = port;					//uart_driver的state的port设置为s3c24xx_serial_ports[0].port
						uart_configure_port(drv, state, port);
						tty_register_device(drv->tty_driver, port->line, port->dev);//(struct tty_driver *driver, unsigned index,struct device *device)
							char name[64];
							dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
							tty_line_name(driver, index, name);
							device_create(tty_class, device, dev, name);	//"s3c2410_serial%d"
					platform_set_drvdata(dev, &ourport->port);

//同名的平台设备设置过程
paging_init

smdk2440_map_io
s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
unsigned long idcode = s3c24xx_read_idcode_v4();
cpu = s3c_lookup_cpu(idcode); //static struct cpu_table *cpu;在struct cpu_table cpu_ids[]中找
(cpu->map_io)(mach_desc, size);
=>s3c244x_map_io
s3c24xx_init_clocks(12000000); //xtal
(cpu->init_clocks)(xtal);
=>s3c244x_init_clocks //设置总线时钟
s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs)); //(struct s3c2410_uartcfg *cfg, int no)
(cpu->init_uarts)(cfg, no); //smdk2410_uartcfgs中存了寄存器的配置值
=>s3c244x_init_uarts
s3c24xx_init_uartdevs(“s3c2440-uart”, s3c2410_uart_resources, cfg, no); //(char *name,struct s3c24xx_uart_resources *res,struct s3c2410_uartcfg *cfg, int no)
struct platform_device *platdev;
struct s3c2410_uartcfg *cfgptr = uart_cfgs; //static struct s3c2410_uartcfg uart_cfgs[3];
struct s3c24xx_uart_resources *resp;
memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no); //把smdk2410_uartcfgs拷贝到uart_cfgs
for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
platdev = s3c24xx_uart_src[cfgptr->hwport];
resp = res + cfgptr->hwport;
s3c24xx_uart_devs[uart] = platdev; //一开始struct platform_device *s3c24xx_uart_devs[3]为空的
platdev->name = name; //.name = “s3c2440-uart”
platdev->resource = resp->resources;
platdev->num_resources = resp->nr_resources;

					platdev->dev.platform_data = cfgptr;
				}

s3c_arch_init
ret = (cpu->init)();
=>s3c2440_init()
s3c_device_wdt…
sysdev_register(&s3c2440_sysdev);
ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts); //这里将s3c24xx_uart_devs注册入内核了

/*******************************************************************************************
open(console_fops.open)
****************************************** ************************************************* /
static int tty_open(struct inode * inode, struct file * filp)
struct tty_driver *driver;
if (device == MKDEV(TTYAUX_MAJOR,0))
struct tty_struct *tty=get_current_tty()
goto got_driver:
if (device == MKDEV(TTY_MAJOR,0))

goto got_driver:
if (device == MKDEV(TTYAUX_MAJOR,1)){ //对于/dev/console是它
driver = console_device(&index);
for (c = console_drivers; c != NULL; c = c->next) //调用console_driver结构链表的每一个device成员函数
driver = c->device(c, index);
==>uart_console_device(struct console *co, int index) // 例如 s3c24xx_serial_console
struct uart_driver p = co->data; //console 的data里里放着 uart_driver 结构地址(s3c24xx_uart_drv)
return p->tty_driver; //这个tty_driver是我们之前在s3c24xx_serial_modinit分配的normal
return driver;
goto got_driver:
}
/
若是上面直接找到tty_driver,就不走这里了,跳到 got_driver
/
struct tty_driver *driver = get_tty_driver(device, &index); //从 tty_drivers 全局链表获取到前边我们注册进去的 tty_driver ,index被设为了次设备号
struct tty_driver *p;
list_for_each_entry(p, &tty_drivers, tty_drivers{
base = MKDEV(p->major, p->minor_start
*index = device - base;
return p;
}
got_driver:
retval = init_dev(driver, index, &tty);//(struct tty_driver *driver, int idx,struct tty_struct **ret_tty)//index表示它的次设备号
struct tty_struct *tty, o_tty;//要tty_driver 初始化一个 tty_struct
tty = alloc_tty_struct(); //分配 tty_struct 结构
initialize_tty_struct(tty); /
(struct tty_struct *tty)根据 tty_driver 初始化一个 tty_struct,设置线路规程 Ops 等 /
tty_ldisc_assign(tty, tty_ldisc_get(N_TTY) /
(struct tty_struct *tty, struct tty_ldisc *ld) 设置线路规程为 N_TTY /
----->tty_ldisc_get(N_TTY)
ld = &tty_ldiscs[disc]; //ld,即tty_ldisc_N_TTY
tty->ldisc = ld; //tty_struct 结构的行规程函数被设置为了tty_ldisc_N_TTY
tty_buffer_init(tty);//tty->buf.head = NULL;tty->buf.tail = NULL;
init_waitqueue_head(&tty->write_wait);//初始化等待队列头
init_waitqueue_head(&tty->read_wait);
INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);/
初始化延时工作队列 唤醒时调用flush_to_ldisc ,读函数时我们需要分析它
/
tty->driver = driver; //tty_struct结构的tty_driver被设置为了我们自己分配设置的那个tty_driver
tty->index = idx; //分配tty_struct结构设置为/dev/console的次设备号,即为1

		if (tty->ldisc.open) {
			retval = (tty->ldisc.open)(tty);	//调用行规程的open函数
	   即<=>int n_tty_open(struct tty_struct *tty) //里面不知道是啥意思,它应该是对线路规程如何“格式化数据”进行设置,太复杂了,忽略掉吧,跟我们没多大关系
				tty->read_buf = alloc_buf();		//为为tty_struct的read_buf成分配空间,为读取数据用的
				memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
				reset_buffer_flags(tty);
				tty->column = 0;
	filp->private_data = tty;			//私有数据设置为了这个 tty_struct	
	if (tty->driver->open)	//tty->driver = driver;
		retval = tty->driver->open(tty, filp);	//tty_set_operations 中设置了driver->open = op->open;
	<=>	uart_open(struct tty_struct *tty, struct file *filp)
			struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
			struct uart_state *state;
			state = uart_get(drv, line);	//(struct uart_driver *drv, int line)
				struct uart_state *state =drv->state + line;
				state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL);	//为uart_state的uart_info结构分配空间;
				init_waitqueue_head(&state->info->open_wait);
				init_waitqueue_head(&state->info->delta_msr_wait)
			tty->driver_data = state;			//tty_struct的driver_data设置为指向这个uart_state
			state->info->tty = tty;
			retval = uart_startup(state, 0);	//调用到最底层的ops里的startup 函数*
				struct uart_info *info = state->info;
				struct uart_port *port = state->port;
				unsigned long page = get_zeroed_page(GFP_KERNEL)	//分配一页数据,为发送缓冲区
				info->xmit.buf = (unsigned char *) page;
					retval = port->ops->startup(port);	//最终调用到最底层的startup函数
				<=>	s3c24xx_serial_ports[0].s3c24xx_serial_ops.startup
				<=> s3c24xx_serial_startup(struct uart_port *port)
						//其中s3c24xx_serial_rx_chars和s3c24xx_serial_tx_chars是中断处理函数
						request_irq(RX_IRQ(port),s3c24xx_serial_rx_chars, 0,s3c24xx_serial_portname(port), ourport);
						request_irq(TX_IRQ(port),s3c24xx_serial_tx_chars, 0,s3c24xx_serial_portname(port), ourport);
	//设置当前进程的终端
	if (!noctty &&current->signal->leader &&!current->signal->tty &&tty->session == NULL)
	  __proc_set_tty(current, tty);	

整个 tty_open 的工作:
1、获取 tty_driver

2、根据 tty_driver 初始化一个 tty_struct

2.1 设置 tty_struct 的线路规程为 N_TTY (不同类型的线路规程有不同的 ops)

2.2 初始化一个延时工作队列,唤醒时调用 flush_to_ldisc ,读函数时我们需要分析它。

2.3 初始化 tty_struct 里的两个等待队列头。

2.4 设置 tty_struct->ops == tty_driver->ops 。

3、在 tty_ldisc_setup 函数中调用到线路规程的open函数,对于 N_TTY 来说是 n_tty_open 。

4、如果 tty_struct->ops 也就是 tty_driver->ops 定义了 open 函数则调用,显然是有的 uart_open 。

/*******************************************************************************************
write
****************************************** ************************************************/

redirected_tty_write
struct file p=redirect //get_file(redirect);
if § /
不知道啥意思 */
{
res = vfs_write(p, buf, count, &p->f_pos);
file->f_op->write(file, buf, count, pos);
==>
fput§;
return res;
}
tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos)
struct tty_struct * tty=(struct tty_struct *)file->private_data; //open的时候filp->private_data = tty;
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_ldisc *ld; //线路规程,即行规程
ld = tty_ldisc_ref_wait(tty);
wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
&tty->ldisc; //
if (!ld->write)
ret = -EIO;
else
ret = do_tty_write(ld->write, tty, file, buf, count);//参数1:ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
for (;????
{
size_t size = count;
if(copy_from_user(tty->write_buf, buf, size)) break;
ret = write(tty, file, tty->write_buf, size); //线路规程在linux启动第二阶段的console_init函数里已将设置好了,为struct tty_ldisc tty_ldisc_N_TTY
<=> ssize_t write_chan(struct tty_struct * tty, struct file * file,const unsigned char * buf, size_t nr)//调用线路规程 write_chan 函数 ,
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&tty->write_wait, &wait);//将当前进程添加到等待队列。
while (1) { //任务死循环,会进行任务的调度
set_current_state(TASK_INTERRUPTIBLE); // 设置当前进程为可中断的
while (nr > 0) {
c = tty->driver->write(tty, b, nr);
<=> uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
struct uart_state *state = tty->driver_data;
struct circ_buf *circ = &state->info->xmit;;
while (1) { //填充待发送的缓冲区,uart_startup中分配了这个缓冲区
int c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)c = count;
if (c <= 0) break;
memcpy(circ->buf + circ->head, buf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c;count -= c;ret += c;
}

							uart_start(tty);
								__uart_start(tty);
									struct uart_state *state = tty->driver_data;
									struct uart_port *port = state->port;
									if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&
											!tty->stopped && !tty->hw_stopped)
										port->ops->start_tx(port);		/* 调用到最底层的 start_tx */
									<=> s3c24xx_serial_ports[0].s3c24xx_serial_ops.start_tx
									<=> s3c24xx_serial_start_tx((struct uart_port *port))
											s3c24xx_serial_rx_disable(port);
											enable_irq(TX_IRQ(port));
											tx_enabled(port) = 1;

						if (c < 0) goto break_out
						b += c;nr -= c;
					}
					schedule();	//进程调度 开始休眠
				}
				breakout:
					__set_current_state(TASK_RUNNING);
					remove_wait_queue(&tty->write_wait, &wait);  	//发送完了,把当前进程从队列里移除

			written += ret;buf += ret;count -= ret;
			if (!count) break;
			
		}

	tty_ldisc_deref(ld);	

/*******************************************************************************************
read
****************************************** ************************************************/
static ssize_t tty_read(struct file * file, char __user * buf, size_t count, loff_t *ppos)
struct tty_struct * tty=(struct tty_struct *)file->private_data;;
struct inode *inode = file->f_path.dentry->d_inode;
ld = tty_ldisc_ref_wait(tty);
if (ld->read)
i = (ld->read)(tty,file,buf,count);//调用线路规程的 read 函数
<=> ssize_t read_chan(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr)
add_wait_queue(&tty->read_wait, &wait); //将当前进程加入队列
while (nr){
set_current_state(TASK_INTERRUPTIBLE);
if (!input_available_p(tty, 0))
{
n_tty_set_room(tty);
timeout = schedule_timeout(timeout); //进程调度,休眠
continue;
}
__set_current_state(TASK_RUNNING);
if (tty->icanon) //如果是标准模式
{
while (nr && tty->read_cnt) {
eol = test_and_clear_bit(tty->read_tail,tty->read_flags);
//从 tty_struct 的read_buf中 获取数据 数据来源于s3c24xx_serial_rx_chars中的读取
c = tty->read_buf[tty->read_tail];
tty>read_tail = ((tty->read_tail+1) &N_TTY_BUF_SIZE-1));
tty->read_cnt–;
put_user(c, b++) //把数据考到用户空间

			}
		}
		else{...}	//非标准模式不关心
	}
	remove_wait_queue(&tty->read_wait, &wait);
	if (!waitqueue_active(&tty->read_wait))
	 tty->minimum_to_wake = minimum;

	__set_current_state(TASK_RUNNING);
	n_tty_set_room(tty);

else
 i = -EIO;
tty_ldisc_deref(ld);

“读”过程干了哪些事:
1、将当前进程加入等待队列

2、设置当前进程可中断

3、进程调度,当前进程进入休眠

4、在某处被唤醒

5、从 tty->read_buf 取出数据,通过 tty_put_user 拷贝到用户空间。
//========================================================

在何处唤醒?猜测因该是在中断处
在open时我们调用s3c24xx_serial_startup注册了两个中断函数s3c24xx_serial_tx_chars和s3c24xx_serial_rx_chars
①static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
//调用线路规程的…
tty_flip_buffer_push(tty);
//ty_flip_buffer_push 有两种方式调用到 flush_to_ldisc ,一种直接调用,另一种使用延时工作队列,在很久很久以前,我们初始化了这么一个工作队列~
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
struct tty_ldisc *disc=tty_ldisc_ref(tty);//找到线路规程
disc->receive_buf(tty, char_buf, flag_buf, count);调用线路规程中的receive_buf函数
<=>即n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,char *fp, int count)
memcpy(tty->read_buf + tty->read_head, cp, i); //将数据看拷贝到 tty_struct 结构的read_buf
wake_up_interruptible(&tty->read_wait); //唤醒队列
else
schedule_delayed_work(&tty->buf.work, 1);

②static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf xmit = &port->info->xmit
while (!uart_circ_empty(xmit) && count-- > 0) { /
try and drain the buffer…将数据写到出去 */
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;

	wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
	port->icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		
	uart_write_wakeup(port);	//(struct uart_port *port)
		struct uart_info *info = port->info;
		tasklet_schedule(&info->tlet);
			__tasklet_schedule(t);
				raise_softirq_irqoff(TASKLET_SOFTIRQ);
					if (!in_interrupt())
					   wakeup_softirqd();
							wake_up_process(tsk);	//唤醒“等待发送完毕的进程”

if (uart_circ_empty(xmit))
	s3c24xx_serial_stop_tx(port);	//如果已经发送完了,则停止超纽扣

另外在driver/char/tty_io.c中
static int __init tty_init(void)
{
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, “/dev/tty”) < 0)
panic(“Couldn’t register /dev/tty driver\n”);
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), “tty”);

cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
	panic("Couldn't register /dev/console driver\n");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), "console");

#ifdef CONFIG_UNIX98_PTYS
cdev_init(&ptmx_cdev, &ptmx_fops);
if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, “/dev/ptmx”) < 0)
panic(“Couldn’t register /dev/ptmx driver\n”);
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), “ptmx”);
#endif

#ifdef CONFIG_VT
cdev_init(&vc0_cdev, &console_fops);
if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, “/dev/vc/0”) < 0)
panic(“Couldn’t register /dev/tty0 driver\n”);
device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), “tty0”);

vty_init();

#endif
return 0;
}
module_init(tty_init);
同样有两个字符设备注册,file_operation一个是tty_fops,一个是console_fops(对应上面“四”中的结构体

/drivers/char/tty_io.c/

#define NR_LDISCS 17
static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */支持17类

void __init console_init(void)
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);//(int disc, struct tty_ldisc *new_ldisc)
tty_ldiscs[disc] = *new_ldisc;
tty_ldiscs[disc].num = disc;
tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
tty_ldiscs[disc].refcount = 0;

struct tty_ldisc 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 = read_chan,
.write = write_chan,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = normal_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};

struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;

/*
 * these are private; the low level driver should not
 * touch these; they should be initialised to NULL
 */
struct uart_state	*state;
struct tty_driver	*tty_driver;

};

static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = “s3c2410_serial”,
.nr = 3,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME, //“ttySAC”
.major = S3C24XX_SERIAL_MAJOR, //204
.minor = S3C24XX_SERIAL_MINOR, //64
//.tty_driver = normal 在s3c24xx_serial_modinit函数中被我们设置为了自己分配的tty_driver结构
//.state.port = s3c24xx_serial_ports[0].port 在s3c24xx_serial_modinit-----…------->uart_add_one_port中设置

};

struct tty_driver { //!!!!注意: tty_driver 里的读写函数open、write等会被设置为uart_ops结构中的相应的读写函数!!!!
int magic; /* magic number for this structure */
struct cdev cdev;
struct module *owner;
const char *driver_name;
const char name;
int name_base; /
offset of printed name /
int major; /
major device number /
int minor_start; /
start of minor device number /
int minor_num; /
number of possible devices /
int num; /
number of devices allocated /
short type; /
type of tty driver /
short subtype; /
subtype of tty driver /
struct ktermios init_termios; /
Initial termios /
int flags; /
tty driver flags /
int refcount; /
for loadable tty drivers */
struct proc_dir_entry proc_entry; / /proc fs entry */
struct tty_driver other; / only used for the PTY driver */

/*
 * Pointer to the tty data structures
 */
struct tty_struct **ttys;
struct ktermios **termios;
struct ktermios **termios_locked;
void *driver_state;	/* only used for the PTY driver */

/*
 * Interface routines from the upper tty layer to the tty
 * driver.	Will be replaced with struct tty_operations.
 */
int  (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
int  (*write)(struct tty_struct * tty,
	      const unsigned char *buf, int count);
void (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int  (*write_room)(struct tty_struct *tty);
int  (*chars_in_buffer)(struct tty_struct *tty);
int  (*ioctl)(struct tty_struct *tty, struct file * file,
	    unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
		     unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
void (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*read_proc)(char *page, char **start, off_t off,
		  int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char __user *buffer,
		  unsigned long count, void *data);
int (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
		unsigned int set, unsigned int clear);

struct list_head tty_drivers;

};

static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
.set_mctrl = s3c24xx_serial_set_mctrl,
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
.enable_ms = s3c24xx_serial_enable_ms,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c24xx_serial_startup,
.shutdown = s3c24xx_serial_shutdown,
.set_termios = s3c24xx_serial_set_termios,
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port,
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
};

struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;

struct s3c24xx_uart_info	*info;
struct s3c24xx_uart_clksrc	*clksrc;
struct clk			*clk;
struct clk			*baudclk;
struct uart_port		port;

};

static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
[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, //struct uart_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 NR_PORTS > 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

static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};

static const struct file_operations console_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = redirected_tty_write,
.poll = tty_poll,
.ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};

struct uart_port {
spinlock_t lock; /* port lock /
unsigned int iobase; /
in/out[bwl] */
unsigned char __iomem membase; / read/write[bwl] /
unsigned int irq; /
irq number /
unsigned int uartclk; /
base uart clock /
unsigned int fifosize; /
tx fifo size /
unsigned char x_char; /
xon/xoff char /
unsigned char regshift; /
reg offset shift /
unsigned char iotype; /
io access style */
unsigned char unused1;

unsigned int		read_status_mask;	/* driver specific */
unsigned int		ignore_status_mask;	/* driver specific */
struct uart_info	*info;			/* pointer to parent info */
struct uart_icount	icount;			/* statistics */

struct console		*cons;			/* struct console, if any */

#ifdef CONFIG_SERIAL_CORE_CONSOLE
unsigned long sysrq; /* sysrq timeout */
#endif

upf_t			flags;

unsigned int		mctrl;			/* current modem ctrl settings */
unsigned int		timeout;		/* character-based timeout */
unsigned int		type;			/* port type */
const struct uart_ops	*ops;
unsigned int		custom_divisor;
unsigned int		line;			/* port index */
unsigned long		mapbase;		/* for ioremap */
struct device		*dev;			/* parent device */
unsigned char		hub6;			/* this should be in the 8250 driver */
unsigned char		unused[3];
void			*private_data;		/* generic platform data pointer */

};

struct circ_buf {
char *buf;
int head;
int tail;
};

struct uart_info {
struct tty_struct *tty;
struct circ_buf xmit;
uif_t flags;

int			blocked_open;

struct tasklet_struct	tlet;

wait_queue_head_t	open_wait;
wait_queue_head_t	delta_msr_wait;

};

struct uart_state {
unsigned int close_delay; /* msec /
unsigned int closing_wait; /
msec */

int			count;
int			pm_state;
struct uart_info	*info;
struct uart_port	*port;

struct mutex		mutex;

};

struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;

struct s3c24xx_uart_info	*info;
struct s3c24xx_uart_clksrc	*clksrc;
struct clk			*clk;
struct clk			*baudclk;
struct uart_port		port;

};

//我们在s3c24xx_serial_modinit->uart_register_driver->tty_set_operations中分配了一个tty_driver结构并将struct tty_operations uart_ops中的所有
操作函数成员都赋过去
struct tty_operations {
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
void (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
void (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char __user *buffer,
unsigned long count, void *data);
int (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
};

static const struct tty_operations uart_ops = { //它的所有操作函数会被设置到tty_driver结构中
.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,
.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
.read_proc = uart_read_proc,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
};

struct tty_ldisc { //行规程
int magic;
char *name;
int num;
int flags;

/*
 * The following routines are called from above.
 */
int	(*open)(struct tty_struct *);
void	(*close)(struct tty_struct *);
void	(*flush_buffer)(struct tty_struct *tty);
ssize_t	(*chars_in_buffer)(struct tty_struct *tty);
ssize_t	(*read)(struct tty_struct * tty, struct file * file,
		unsigned char __user * buf, size_t nr);
ssize_t	(*write)(struct tty_struct * tty, struct file * file,
		 const unsigned char * buf, size_t nr);	
int	(*ioctl)(struct tty_struct * tty, struct file * file,
		 unsigned int cmd, unsigned long arg);
long	(*compat_ioctl)(struct tty_struct * tty, struct file * file,
			unsigned int cmd, unsigned long arg);
void	(*set_termios)(struct tty_struct *tty, struct ktermios * old);
unsigned int (*poll)(struct tty_struct *, struct file *,
		     struct poll_table_struct *);
int	(*hangup)(struct tty_struct *tty);

/*
 * The following routines are called from below.
 */
void	(*receive_buf)(struct tty_struct *, const unsigned char *cp,
		       char *fp, int count);
void	(*write_wakeup)(struct tty_struct *);

struct  module *owner;

int refcount;

};

struct console {
char name[16];
void (*write)(struct console *, const char *, unsigned);
int (*read)(struct console *, char *, unsigned);
struct tty_driver *(*device)(struct console *, int *);
void (*unblank)(void);
int (*setup)(struct console *, char *);
short flags;
short index;
int cflag;
void *data; //放着 uart_driver 结构地址
struct console *next;
};

struct tty_struct { //tty_struct也是我们自己分配的,含tty_ldisc 行规程
int magic;
struct tty_driver *driver;
int index;
struct tty_ldisc ldisc;
struct mutex termios_mutex;
struct ktermios *termios, *termios_locked;
char name[64];
struct pid *pgrp;
struct pid session;
unsigned long flags;
int count;
struct winsize winsize;
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
unsigned char low_latency:1, warned:1;
unsigned char ctrl_status;
unsigned int receive_room; /
Bytes free for queue */

struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_bufhead buf;
int alt_speed;		/* For magic substitution of 38400 bps */
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;			//被设置为指向uart_state
struct list_head tty_files;

#define N_TTY_BUF_SIZE 4096

/*
 * The following is data for the N_TTY line discipline.  For
 * historical reasons, this is included in the tty structure.
 */
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1;
unsigned short minimum_to_wake;
unsigned long overrun_time;
int num_overrun;
unsigned long process_char_map[256/(8*sizeof(unsigned long))];
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
int canon_data;
unsigned long canon_head;
unsigned int canon_column;
struct mutex atomic_read_lock;
struct mutex atomic_write_lock;
unsigned char *write_buf;
int write_cnt;
spinlock_t read_lock;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;

};

static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x32410000,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410_init,
.name = name_s3c2410
},
{
.idcode = 0x32410002,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410_init,
.name = name_s3c2410a
},
{
.idcode = 0x32440000,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2440_init,
.name = name_s3c2440
},
{
.idcode = 0x32440001,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2440_init,
.name = name_s3c2440a
},
{
.idcode = 0x32440aaa,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2442_init,
.name = name_s3c2442
},
{
.idcode = 0x32412001,
.idmask = 0xffffffff,
.map_io = s3c2412_map_io,
.init_clocks = s3c2412_init_clocks,
.init_uarts = s3c2412_init_uarts,
.init = s3c2412_init,
.name = name_s3c2412,
},
{ /* a newer version of the s3c2412 /
.idcode = 0x32412003,
.idmask = 0xffffffff,
.map_io = s3c2412_map_io,
.init_clocks = s3c2412_init_clocks,
.init_uarts = s3c2412_init_uarts,
.init = s3c2412_init,
.name = name_s3c2412,
},
{
.idcode = 0x32443001,
.idmask = 0xffffffff,
.map_io = s3c2443_map_io,
.init_clocks = s3c2443_init_clocks,
.init_uarts = s3c2443_init_uarts,
.init = s3c2443_init,
.name = name_s3c2443,
},
{
.idcode = 0x0, /
S3C2400 doesn’t have an idcode */
.idmask = 0xffffffff,
.map_io = s3c2400_map_io,
.init_clocks = s3c2400_init_clocks,
.init_uarts = s3c2400_init_uarts,
.init = s3c2400_init,
.name = name_s3c2400
},
};

static struct platform_driver s3c2440_serial_drv = {
.probe = s3c2440_serial_probe,
.remove = s3c24xx_serial_remove,
.suspend = s3c24xx_serial_suspend,
.resume = s3c24xx_serial_resume,
.driver = {
.name = “s3c2440-uart”,
.owner = THIS_MODULE,
},
};

struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
[0] = {
.resources = s3c2410_uart0_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart0_resource),
},
[1] = {
.resources = s3c2410_uart1_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart1_resource),
},
[2] = {
.resources = s3c2410_uart2_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart2_resource),
},
};

static struct s3c2410_uartcfg smdk2410_uartcfgs[] = {
[0] = {
.hwport = 0,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
},
[1] = {
.hwport = 1,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
},
[2] = {
.hwport = 2,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
}
};

static struct platform_device s3c24xx_uart_device0 = {
.id = 0,
};

static struct platform_device s3c24xx_uart_device1 = {
.id = 1,
};

static struct platform_device s3c24xx_uart_device2 = {
.id = 2,
};

struct platform_device *s3c24xx_uart_src[3] = {
&s3c24xx_uart_device0,
&s3c24xx_uart_device1,
&s3c24xx_uart_device2,
};

struct platform_device *s3c24xx_uart_devs[3] = {
};

设备号:

ls /dev/console -l

crw-rw---- 1 0 0 5, 1 Jan 1 00:01 /dev/console

ls /dev/tty0 -l

crw-rw---- 1 0 0 4, 0 Jan 1 00:00 /dev/tty0

ls /dev/tty -l

crw-rw---- 1 0 0 5, 0 Jan 1 00:00 /dev/tty

相关标签: 内核