linux2.6.22.6串口驱动分析

//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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值