Linux串口编程

一 uboot传递参数'console=ttyXXX'的作用

linux启动时uboot传递进console=ttyS0,115200n8的参数

内核中用__setup()宏声明参数处理的方法

关于__setup宏参考 early_param和__setup宏

__setup("console=", console_setup);

console_setup函数处理

1.console_cmdline结构体

struct console_cmdline
{
	char	name[8];		//驱动名
	int	index;		//次设备号
	char	*options;		//选项
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	char	*brl_options;	
#endif
};

2.console_setup

static int __init console_setup(char *str)
{
	char buf[sizeof(console_cmdline[0].name) + 4]; //分配驱动名+index的缓冲区
	char *s, *options, *brl_options = NULL;
	int idx;

#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	if (!memcmp(str, "brl,", 4)) {
		brl_options = "";
		str += 4;
	} else if (!memcmp(str, "brl=", 4)) {
		brl_options = str + 4;
		str = strchr(brl_options, ',');
		if (!str) {
			printk(KERN_ERR "need port name after brl=\n");
			return 1;
		}
		*(str++) = 0;
	}
#endif
	if (str[0] >= '0' && str[0] <= '9') {	//第一个参数属于[0,9]
		strcpy(buf, "ttyS");	//则将其驱动名设为ttyS
		strncpy(buf + 4, str, sizeof(buf) - 5);//将次设备号放其后面
	} else {
		strncpy(buf, str, sizeof(buf) - 1);	//放设备号到其后面
	}
	buf[sizeof(buf) - 1] = 0;
	if ((options = strchr(str, ',')) != NULL)	//获取options
		*(options++) = 0;
#ifdef __sparc__
	if (!strcmp(str, "ttya"))
		strcpy(buf, "ttyS0");
	if (!strcmp(str, "ttyb"))
		strcpy(buf, "ttyS1");
#endif
	for (s = buf; *s; s++)
		if ((*s >= '0' && *s <= '9') || *s == ',')
			break;
	idx = simple_strtoul(s, NULL, 10);	//获取次设备号
	*s = 0;

	__add_preferred_console(buf, idx, options, brl_options);
	console_set_on_cmdline = 1;
	return 1;
}

__add_preferred_console函数

static int __add_preferred_console(char *name, int idx, char *options,char *brl_options)
{
	struct console_cmdline *c;
	int i;
	for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)	//可以最多8个console
		if (strcmp(console_cmdline[i].name, name) == 0 && console_cmdline[i].index == idx) {
			//比较已注册的console_cmdline数组中的项的名字及次设备号,若console_cmdline已经存在
				if (!brl_options)
					selected_console = i;	//设置全局selected_console索引号
				return 0;		//则返回
		}
	if (i == MAX_CMDLINECONSOLES)	//判断console_cmdline数组是否满了
		return -E2BIG;
	if (!brl_options)
		selected_console = i;	//设置全局selected_console索引号
	c = &console_cmdline[i];	//获取全局console_cmdline数组的第i项地址
	strlcpy(c->name, name, sizeof(c->name));	//填充全局console_cmdline的驱动名
	c->options = options;	//填充配置选项115200n8
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	c->brl_options = brl_options;
#endif
	c->index = idx;	//填充索引号0
	return 0;
}

整体的作用是根据uboot传递的参数设置全局console_cmdline数组
该数组及全局selected_console,在register_console中会使用到
二 console 设备驱动

 

二 console 设备驱动

一.结构体

1.console

struct console {
	char	name[16];		//console名
	void	(*write)(struct console *, const char *, unsigned);	//写方法
	int	(*read)(struct console *, char *, unsigned);	//读方法
	struct tty_driver *(*device)(struct console *, int *);	//tty驱动
	void	(*unblank)(void);
	int	(*setup)(struct console *, char *);	//setup方法
	int	(*early_setup)(void);	//early_setup方法
	short	flags;
	short	index;
	int	cflag;
	void	*data;
	struct	 console *next;
};

2.标志

#define CON_PRINTBUFFER	(1)
#define CON_CONSDEV	(2) /* Last on the command line */
#define CON_ENABLED	(4)	//使能标志位
#define CON_BOOT		(8)	//内核引导使用的console
#define CON_ANYTIME	(16) /* Safe to call when cpu is offline */
#define CON_BRL		(32) /* Used for a braille device */


 二.初始化

在Vmlinux.lds.h中定义

#define CON_INITCALL					\
		VMLINUX_SYMBOL(__con_initcall_start) = .;	\
		*(.con_initcall.init)			\
		VMLINUX_SYMBOL(__con_initcall_end) = .;

在init.h中定义

#define console_initcall(fn) \
	static initcall_t __initcall_##fn \
	__used __section(.con_initcall.init) = fn

在console_init初始化(start_kernel中调用了console_init函数)

void __init console_init(void)
{
	initcall_t *call;
	tty_ldisc_begin();
	call = __con_initcall_start;
	while (call < __con_initcall_end) {	//遍历__con_initcall_end段的函数
		(*call)();	//调用该函数
		call++;
	}
}

tty线路规程开始

void tty_ldisc_begin(void)
{
	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);	//N_TTY=0
}

tty注册默认的线路规程

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;	//设置全局tty_ldiscs[0]=tty_ldisc_N_TTY
	new_ldisc->num = disc;	//设置num域
	new_ldisc->refcount = 0;	//参考计数
	spin_unlock_irqrestore(&tty_ldisc_lock, flags);
	return ret;
}
EXPORT_SYMBOL(tty_register_ldisc);

 该全局数组在tty_ldisc_init函数中使用

三.console的注册和注销

1.注册console

void register_console(struct console *newcon)
{
	int i;
	unsigned long flags;
	struct console *bcon = NULL;
	if (console_drivers && newcon->flags & CON_BOOT) {	//注册的是引导控制台
		for_each_console(bcon) {	//遍历全局console_drivers数组
			if (!(bcon->flags & CON_BOOT)) {	//判断是否已经有引导控制台了,有了的话就直接退出
				printk(KERN_INFO "Too late to register bootconsole %s%d\n",newcon->name, newcon->index);
				return;
			}
		}
	}

	if (console_drivers && console_drivers->flags & CON_BOOT)	//如果注册的是引导控制台
		bcon = console_drivers;	//让bcon指向全局console_drivers
	if (preferred_console < 0 || bcon || !console_drivers)
		preferred_console = selected_console;	//设置preferred_console为uboot命令选择的selected_console(索引)
	if (newcon->early_setup)	//存在early_setup函数
		newcon->early_setup();	//则调用early_setup函数
	if (preferred_console < 0) {
		if (newcon->index < 0)
			newcon->index = 0;
		if (newcon->setup == NULL ||newcon->setup(newcon, NULL) == 0) {
			newcon->flags |= CON_ENABLED;
			if (newcon->device) {
				newcon->flags |= CON_CONSDEV;
				preferred_console = 0;
			}
		}
	}	//遍历全局console_cmdline找到匹配的
	for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];i++) {
		if (strcmp(console_cmdline[i].name, newcon->name) != 0)	//比较名字
			continue;
		if (newcon->index >= 0 &&newcon->index != console_cmdline[i].index)	//比较次设备号
			continue;
		if (newcon->index < 0)	//若没指定设备号
			newcon->index = console_cmdline[i].index;
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
		if (console_cmdline[i].brl_options) {
			newcon->flags |= CON_BRL;
			braille_register_console(newcon,console_cmdline[i].index,console_cmdline[i].options,console_cmdline[i].brl_options);
			return;
		}
#endif
		if (newcon->setup &&newcon->setup(newcon, console_cmdline[i].options) != 0)	//存在setup方法调用setup方法
			break;
		newcon->flags |= CON_ENABLED;	//设置标志为CON_ENABLE(这个在printk调用中使用到)
		newcon->index = console_cmdline[i].index;	//设置索引号
		if (i == selected_console) {	//索引号和uboot指定的console的一样
			newcon->flags |= CON_CONSDEV;	//设置标志CON_CONSDEV(全局console_drivers链表中靠前)
			preferred_console = selected_console;	//设置preferred
		}
		break;
	}	//for循环作用大致是查看注册的console是否是uboot知道的引导console是则设置相关标志和preferred_console
	if (!(newcon->flags & CON_ENABLED))
		return;
	if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))	//防止重复打印
		newcon->flags &= ~CON_PRINTBUFFER;
	acquire_console_sem();
	if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {	//如果是preferred控制台
		newcon->next = console_drivers;
		console_drivers = newcon;	//添加进全局console_drivers链表前面位置(printk中会遍历该表调用合适的console的write方法打印信息)
		if (newcon->next)
			newcon->next->flags &= ~CON_CONSDEV;
	} else {	//如果不是preferred控制台
		newcon->next = console_drivers->next;
		console_drivers->next = newcon;	//添加进全局console_drivers链表后面位置
	}
	if (newcon->flags & CON_PRINTBUFFER) {
		spin_lock_irqsave(&logbuf_lock, flags);
		con_start = log_start;
		spin_unlock_irqrestore(&logbuf_lock, flags);
	}
	release_console_sem();
	if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV)) {
		printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",newcon->name, newcon->index);
		for_each_console(bcon)
			if (bcon->flags & CON_BOOT)
				unregister_console(bcon);
	} else {
		printk(KERN_INFO "%sconsole [%s%d] enabled\n",(newcon->flags & CON_BOOT) ? "boot" : "" ,newcon->name, newcon->index);
	}
}
EXPORT_SYMBOL(register_console);

注册console主要是刷选preferred_console放置在全局console_drivers链表前面,剩下的console放置链表靠后的位置,并设置相应的flags,

console_drivers最终会在printk函数的层层调用中遍历到,并调用console的write方法将信息打印出来

2.注销console

int unregister_console(struct console *console)
{
        struct console *a, *b;
	int res = 1;

#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
	if (console->flags & CON_BRL)
		return braille_unregister_console(console);
#endif

	acquire_console_sem();
	if (console_drivers == console) {
		console_drivers=console->next;
		res = 0;
	} else if (console_drivers) {
		for (a=console_drivers->next, b=console_drivers ;
		     a; b=a, a=b->next) {
			if (a == console) {
				b->next = a->next;
				res = 0;
				break;
			}
		}
	}

	/*
	 * If this isn't the last console and it has CON_CONSDEV set, we
	 * need to set it on the next preferred console.
	 */
	if (console_drivers != NULL && console->flags & CON_CONSDEV)
		console_drivers->flags |= CON_CONSDEV;

	release_console_sem();
	return res;
}
EXPORT_SYMBOL(unregister_console);

3./dev/console

在tty_init函数中

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), NULL,"tty");
	cdev_init(&console_cdev, &console_fops);	//初始化字符设备并捆绑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), NULL,"console");	//创建设备文件
#ifdef CONFIG_VT
	vty_init(&console_fops);
#endif
	return 0;
}

当操作/dev/console文件时会调用console_fops操作函数集的方法

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,
};

该函数集跟tty_fops基本一样不同的只是写方法

redirected_tty_write函数

ssize_t redirected_tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{
	struct file *p = NULL;
	spin_lock(&redirect_lock);
	if (redirect) {
		get_file(redirect);
		p = redirect;
	}
	spin_unlock(&redirect_lock);

	if (p) {
		ssize_t res;
		res = vfs_write(p, buf, count, &p->f_pos);	//多了个写文件的方法
		fput(p);
		return res;
	}
	return tty_write(file, buf, count, ppos);	//同样也会调用tty_write方法
}

在tty_open中有个分支专门处理/dev/console

	if (device == MKDEV(TTYAUX_MAJOR, 1)) {
		struct tty_driver *console_driver = console_device(&index);	//获取console对应的tty-driver
		if (console_driver) {
			driver = tty_driver_kref_get(console_driver);
			if (driver) {
				filp->f_flags |= O_NONBLOCK;
				noctty = 1;
				goto got_driver;
			}
		}
		tty_unlock();
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}

console_device

struct tty_driver *console_device(int *index)
{
	struct console *c;
	struct tty_driver *driver = NULL;

	acquire_console_sem();
	for_each_console(c) {
		if (!c->device)	//console存在tty_driver 说明是/dev/console对应的console
			continue;
		driver = c->device(c, index);
		if (driver)
			break;
	}
	release_console_sem();
	return driver;
}


 

三 printk如何打印信息

printk的定义

可变参数 参考va_list,va_start,va_arg,va_end可变参数

asmlinkage int printk(const char *fmt, ...)
{
	va_list args;	//可变参数链表
	int r;

#ifdef CONFIG_KGDB_KDB
	if (unlikely(kdb_trap_printk)) {
		va_start(args, fmt);
		r = vkdb_printf(fmt, args);
		va_end(args);
		return r;
	}
#endif
	va_start(args, fmt);	//获取第一个可变参数
	r = vprintk(fmt, args);	//调用vprintk函数
	va_end(args);	//释放可变参数链表指针

	return r;
}

vprintk函数

asmlinkage int vprintk(const char *fmt, va_list args)
{
	int printed_len = 0;
	int current_log_level = default_message_loglevel;
	unsigned long flags;
	int this_cpu;
	char *p;

	boot_delay_msec();
	printk_delay();
	preempt_disable();
	raw_local_irq_save(flags);
	this_cpu = smp_processor_id();
	if (unlikely(printk_cpu == this_cpu)) {
		if (!oops_in_progress) {
			recursion_bug = 1;
			goto out_restore_irqs;
		}
		zap_locks();
	}

	lockdep_off();
	spin_lock(&logbuf_lock);
	printk_cpu = this_cpu;

	if (recursion_bug) {
		recursion_bug = 0;
		strcpy(printk_buf, recursion_bug_msg);
		printed_len = strlen(recursion_bug_msg);
	}
	printed_len += vscnprintf(printk_buf + printed_len,sizeof(printk_buf) - printed_len, fmt, args);
	p = printk_buf;
	if (p[0] == '<') {		//处理打印级别字段
		unsigned char c = p[1];
		if (c && p[2] == '>') {
			switch (c) {
			case '0' ... '7': /* loglevel */
				current_log_level = c - '0';
			case 'd': /* KERN_DEFAULT */
				if (!new_text_line) {
					emit_log_char('\n');
					new_text_line = 1;
				}
			case 'c': /* KERN_CONT */
				p += 3;
				break;
			}
		}
	}
	for ( ; *p; p++) {
		if (new_text_line) {
			/* Always output the token */
			emit_log_char('<');
			emit_log_char(current_log_level + '0');
			emit_log_char('>');
			printed_len += 3;
			new_text_line = 0;

			if (printk_time) {		//打印时间信息
				/* Follow the token with the time */
				char tbuf[50], *tp;
				unsigned tlen;
				unsigned long long t;
				unsigned long nanosec_rem;

				t = cpu_clock(printk_cpu);
				nanosec_rem = do_div(t, 1000000000);
				tlen = sprintf(tbuf, "[%5lu.%06lu] ",(unsigned long) t,nanosec_rem / 1000);

				for (tp = tbuf; tp < tbuf + tlen; tp++)
					emit_log_char(*tp);
				printed_len += tlen;
			}

			if (!*p)
				break;
		}

		emit_log_char(*p);
		if (*p == '\n')
			new_text_line = 1;
	}
	if (acquire_console_semaphore_for_printk(this_cpu))
		release_console_sem();

	lockdep_on();
out_restore_irqs:
	raw_local_irq_restore(flags);

	preempt_enable();
	return printed_len;
}

接着调用release_console_sem函数

void release_console_sem(void)
{
	unsigned long flags;
	unsigned _con_start, _log_end;
	unsigned wake_klogd = 0;

	if (console_suspended) {
		up(&console_sem);
		return;
	}

	console_may_schedule = 0;

	for ( ; ; ) {
		spin_lock_irqsave(&logbuf_lock, flags);
		wake_klogd |= log_start - log_end;
		if (con_start == log_end)
			break;			/* Nothing to print */
		_con_start = con_start;
		_log_end = log_end;
		con_start = log_end;		/* Flush */
		spin_unlock(&logbuf_lock);
		stop_critical_timings();	/* don't trace print latency */
		call_console_drivers(_con_start, _log_end);
		start_critical_timings();
		local_irq_restore(flags);
	}
	console_locked = 0;
	up(&console_sem);
	spin_unlock_irqrestore(&logbuf_lock, flags);
	if (wake_klogd)
		wake_up_klogd();
}
EXPORT_SYMBOL(release_console_sem);

调用call_console_drivers函数

static void call_console_drivers(unsigned start, unsigned end)
{
	unsigned cur_index, start_print;
	static int msg_level = -1;

	BUG_ON(((int)(start - end)) > 0);

	cur_index = start;
	start_print = start;
	while (cur_index != end) {
		if (msg_level < 0 && ((end - cur_index) > 2) &&LOG_BUF(cur_index + 0) == '<' &&LOG_BUF(cur_index + 1) >= '0' &&LOG_BUF(cur_index + 1) <= '7' &&LOG_BUF(cur_index + 2) == '>') {
			msg_level = LOG_BUF(cur_index + 1) - '0';
			cur_index += 3;
			start_print = cur_index;
		}
		while (cur_index != end) {
			char c = LOG_BUF(cur_index);

			cur_index++;
			if (c == '\n') {
				if (msg_level < 0) {
					msg_level = default_message_loglevel;
				}
				_call_console_drivers(start_print, cur_index, msg_level);
				msg_level = -1;
				start_print = cur_index;
				break;
			}
		}
	}
	_call_console_drivers(start_print, end, msg_level);
}

_call_console_drivers函数

static void _call_console_drivers(unsigned start,unsigned end, int msg_log_level)
{
	if ((msg_log_level < console_loglevel || ignore_loglevel) && console_drivers && start != end) {
		if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
			/* wrapped write */
			__call_console_drivers(start & LOG_BUF_MASK,log_buf_len);
			__call_console_drivers(0, end & LOG_BUF_MASK);
		} else {
			__call_console_drivers(start, end);
		}
	}
}

__call_console_drivers函数

遍历console_drivers数组

#define for_each_console(con) \
	for (con = console_drivers; con != NULL; con = con->next)

调用console的写方法

static void __call_console_drivers(unsigned start, unsigned end)
{
	struct console *con;

	for_each_console(con) {
		if ((con->flags & CON_ENABLED) && con->write &&(cpu_online(smp_processor_id()) ||(con->flags & CON_ANYTIME)))
			con->write(con, &LOG_BUF(start), end - start);	//调用console的写方法
	}
}

console_drivers链表在register_console中会设置
二 console 设备驱动


四 linux tty驱动

分类: linux设备驱动 313人阅读 评论(0)收藏 举报

一. tty结构体

1.tty_driver

struct tty_driver {
	int	magic;
	struct kref kref;	//参考计数
	struct cdev cdev;	//字符设备
	struct module	*owner;	//模块所有者
	const char	*driver_name;	//驱动名
	const char	*name;	//设备名
	int	name_base;
	int	major;	//主设备号
	int	minor_start;	//起始次设备号
	int	minor_num;	//设备个数
	int	num;	//分配了的tty设备个数
	short	type;	//tty设备的类型
	short	subtype;	//tty设备子类型
	struct ktermios init_termios;	//初始化的ktermios
	int	flags;	//tty驱动标志
	struct proc_dir_entry *proc_entry;	//procfs入口
	struct tty_driver *other;
	struct tty_struct **ttys;
	struct ktermios **termios;
	struct ktermios **termios_locked;
	void *driver_state;
	const struct tty_operations *ops;	//操作函数集
	struct list_head tty_drivers;	//驱动链表
};

1.1 tty->flag

#define TTY_DRIVER_INSTALLED	0x0001
#define TTY_DRIVER_RESET_TERMIOS	0x0002
#define TTY_DRIVER_REAL_RAW		0x0004
#define TTY_DRIVER_DYNAMIC_DEV	0x0008
#define TTY_DRIVER_DEVPTS_MEM	0x0010
#define TTY_DRIVER_HARDWARE_BREAK	0x0020

1.2 tty->type tty设备类型

#define TTY_DRIVER_TYPE_SYSTEM	0x0001
#define TTY_DRIVER_TYPE_CONSOLE	0x0002
#define TTY_DRIVER_TYPE_SERIAL	0x0003
#define TTY_DRIVER_TYPE_PTY		0x0004
#define TTY_DRIVER_TYPE_SCC		0x0005	/* scc driver */
#define TTY_DRIVER_TYPE_SYSCONS	0x0006

2.ktermios结构体

struct ktermios {
	tcflag_t c_iflag;	/* input mode flags */	//输入模式标志
	tcflag_t c_oflag;	/* output mode flags */	//输出模式标志
	tcflag_t c_cflag;	/* control mode flags */	//控制模式标志
	tcflag_t c_lflag;	/* local mode flags */	//本地模式标志
	cc_t c_line;	/* line discipline */	//线路规程类型
	cc_t c_cc[NCCS];	/* control characters */	//控制字符
	speed_t c_ispeed;	/* input speed */		//输入速度
	speed_t c_ospeed;	/* output speed */		//输出速度
};

3.tty_struct

struct tty_struct {
	int	magic;	//魔数
	struct kref kref;	//参考计数
	struct device *dev;	//设备文件
	struct tty_driver *driver;	//tty驱动
	const struct tty_operations *ops;	//tty操作函数集
	int index;	
	struct mutex ldisc_mutex;
	struct tty_ldisc *ldisc;	//线路规程
	struct mutex termios_mutex;
	spinlock_t ctrl_lock;
	struct ktermios *termios, *termios_locked;
	struct termiox *termiox;
	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;
	struct tty_struct *link;
	struct fasync_struct *fasync;
	struct tty_bufhead buf;
	int alt_speed;
	wait_queue_head_t write_wait;
	wait_queue_head_t read_wait;
	struct work_struct hangup_work;
	void *disc_data;
	void *driver_data;
	struct list_head tty_files;
#define N_TTY_BUF_SIZE 4096
	unsigned int column;
	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
	unsigned char closing:1;
	unsigned char echo_overrun: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))];
	unsigned char *echo_buf;
	unsigned int echo_pos;
	unsigned int echo_cnt;
	int canon_data;
	unsigned long canon_head;
	unsigned int canon_column;
	struct mutex atomic_read_lock;
	struct mutex atomic_write_lock;
	struct mutex output_lock;
	struct mutex echo_lock;
	unsigned char *write_buf;
	int write_cnt;
	spinlock_t read_lock;
	struct work_struct SAK_work;
	struct tty_port *port;
};

4.tty_ldisc线路规程

struct tty_ldisc {
	struct tty_ldisc_ops *ops;
	atomic_t users;
};

4.1 线路规程操作函数集

struct tty_ldisc_ops {
	int	magic;
	char	*name;
	int	num;
	int	flags;
	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);
	void	(*receive_buf)(struct tty_struct *, const unsigned char *cp,char *fp, int count);
	void	(*write_wakeup)(struct tty_struct *);
	void	(*dcd_change)(struct tty_struct *, unsigned int,struct timespec *);
	struct  module *owner;
	int refcount;
};


二.系统初始化

设备类初始化

static int __init tty_class_init(void)
{
	tty_class = class_create(THIS_MODULE, "tty");	//创建tty类
	if (IS_ERR(tty_class))
		return PTR_ERR(tty_class);
	tty_class->devnode = tty_devnode;	//指定tty设备节点
	return 0;
}

字符设备初始化

int __init tty_init(void)
{
	cdev_init(&tty_cdev, &tty_fops);	//初始化tty字符设备
	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||	//添加tty字符设备
	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)	//注册/dev/tty字符设备
		panic("Couldn't register /dev/tty driver\n");
	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,"tty");	//注册tty设备
	cdev_init(&console_cdev, &console_fops);	//初始化console字符设备
	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||	//添加console字符设备
	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)	//注册/dev/console设备
		panic("Couldn't register /dev/console driver\n");
	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,"console");	//创建console设备
#ifdef CONFIG_VT
	vty_init(&console_fops);
#endif
	return 0;
}

虚拟终端/dev/tty0初始化vty_init

int __init vty_init(const struct file_operations *console_fops)
{
	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), NULL, "tty0");	//创建/dev/tty0 (4,0)
	vcs_init();
	console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
	if (!console_driver)
		panic("Couldn't allocate console driver\n");
	console_driver->owner = THIS_MODULE;
	console_driver->name = "tty";
	console_driver->name_base = 1;
	console_driver->major = TTY_MAJOR;
	console_driver->minor_start = 1;
	console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
	console_driver->init_termios = tty_std_termios;
	if (default_utf8)
		console_driver->init_termios.c_iflag |= IUTF8;
	console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
	tty_set_operations(console_driver, &con_ops);
	if (tty_register_driver(console_driver))
		panic("Couldn't register console driver\n");
	kbd_init();
	console_map_init();
#ifdef CONFIG_MDA_CONSOLE
	mda_console_init();
#endif
	return 0;
}


 

三.tty初始化步骤

1.分配tty结构体

struct tty_driver *alloc_tty_driver(int lines)
{
	struct tty_driver *driver;
	driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);	//分配内存
	if (driver) {
		kref_init(&driver->kref);	//引用计数+1
		driver->magic = TTY_DRIVER_MAGIC;	//设置魔数0x5402
		driver->num = lines;	//设置tty line个数
	}
	return driver;
}


2.填充tty结构体成员

3.注册tty设备驱动

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);
		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;	//tty_struct
		driver->termios = (struct ktermios **)(p + driver->num);	//ktermios
k	} 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);		//添加到全局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);	//添加tty设备文件/dev/ttyXX
			if (IS_ERR(d)) {
				error = PTR_ERR(d);
				goto err;
			}
		}
	}
	proc_tty_register_driver(driver);	//设置tty在/proc下的接口
	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;
}

4.创建tty设备文件/dev/ttyXXX

struct device *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device)
{
	char name[64];
	dev_t dev = MKDEV(driver->major, driver->minor_start) + index;	//获取设备号
	if (index >= driver->num) {
		printk(KERN_ERR "Attempt to register invalid tty line number (%d).\n", index);
		return ERR_PTR(-EINVAL);
	}
	if (driver->type == TTY_DRIVER_TYPE_PTY)	//伪终端
		pty_line_name(driver, index, name);
	else					//"/dev/ttyXXX"
		tty_line_name(driver, index, name);
	return device_create(tty_class, device, dev, NULL, name);	//创建设备文件
}

5.tty设备文件捆绑的操作函数集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的操作

当打开/dev/ttyXX的时候会调用tty_open函数

tty_open操作

static int tty_open(struct inode *inode, struct file *filp)
{
	struct tty_struct *tty = NULL;
	int noctty, retval;
	struct tty_driver *driver;
	int index;
	dev_t device = inode->i_rdev;	//通过i节点获取设备号
	unsigned saved_flags = filp->f_flags;
	nonseekable_open(inode, filp);	//设置打开关联文件的模式
retry_open:
	noctty = filp->f_flags & O_NOCTTY;	//设置O_NOCTTY标志
	index  = -1;
	retval = 0;
	mutex_lock(&tty_mutex);
	tty_lock();
	if (device == MKDEV(TTYAUX_MAJOR, 0)) {	//打开的设备是/dev/tty (5,0)
		tty = get_current_tty();	//取当前进程tty_struct
		if (!tty) {
			tty_unlock();
			mutex_unlock(&tty_mutex);
			return -ENXIO;
		}
		driver = tty_driver_kref_get(tty->driver);	//获取tty_driver
		index = tty->index;
		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
		tty_kref_put(tty);		//引用计数
		goto got_driver;
	}
#ifdef CONFIG_VT	//虚拟终端设备 /dev/tty0
	if (device == MKDEV(TTY_MAJOR, 0)) {	//(4,0)
		extern struct tty_driver *console_driver;
		driver = tty_driver_kref_get(console_driver);	//获取tty_struct
		index = fg_console;
		noctty = 1;
		goto got_driver;
	}
#endif
	if (device == MKDEV(TTYAUX_MAJOR, 1)) {	//打开的是/dev/console (5,1)
		struct tty_driver *console_driver = console_device(&index);	//获取console对应的tty_driver
		if (console_driver) {
			driver = tty_driver_kref_get(console_driver);	//获取tty_struct
			if (driver) {
				filp->f_flags |= O_NONBLOCK;
				noctty = 1;
				goto got_driver;
			}
		}
		tty_unlock();
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}
	driver = get_tty_driver(device, &index);	//获取tty_driver,设置次设备号(索引值) 非特殊的tty
	if (!driver) {
		tty_unlock();
		mutex_unlock(&tty_mutex);
		return -ENODEV;
	}
got_driver:
	if (!tty) {
		tty = tty_driver_lookup_tty(driver, inode, index);	//获取tty_drivver的tty_struct
		if (IS_ERR(tty)) {
			tty_unlock();
			mutex_unlock(&tty_mutex);
			return PTR_ERR(tty);
		}
	}
	if (tty) {	//若tty_struct不为空
		retval = tty_reopen(tty);
		if (retval)
			tty = ERR_PTR(retval);
	} else	//若tty_struct为空
		tty = tty_init_dev(driver, index, 0);	//调用tty_init_dev函数(下面分析)
	mutex_unlock(&tty_mutex);
	tty_driver_kref_put(driver);	//增加引用计数
	if (IS_ERR(tty)) {
		tty_unlock();
		return PTR_ERR(tty);
	}
	retval = tty_add_file(tty, filp);
	if (retval) {
		tty_unlock();
		return retval;
	}
	check_tty_count(tty, "tty_open");
	if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER)
		noctty = 1;
#ifdef TTY_DEBUG_HANGUP
	printk(KERN_DEBUG "opening %s...", tty->name);
#endif
	if (!retval) {
		if (tty->ops->open)	//若tty_struct存在open方法
			retval = tty->ops->open(tty, filp);	//则调用其open方法
		else
			retval = -ENODEV;
	}
	filp->f_flags = saved_flags;
	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&!capable(CAP_SYS_ADMIN))
		retval = -EBUSY;
	if (retval) {
#ifdef TTY_DEBUG_HANGUP
		printk(KERN_DEBUG "error %d in opening %s...", retval,tty->name);
#endif
		tty_unlock(); /* need to call tty_release without BTM */
		tty_release(inode, filp);
		if (retval != -ERESTARTSYS)
			return retval
		if (signal_pending(current))
			return retval;
		schedule();
		tty_lock();
		if (filp->f_op == &hung_up_tty_fops)
			filp->f_op = &tty_fops;
		tty_unlock();
		goto retry_open;
	}
	tty_unlock();
	mutex_lock(&tty_mutex);
	tty_lock();
	spin_lock_irq(¤t->sighand->siglock);
	if (!noctty && current->signal->leader &&!current->signal->tty && tty->session == NULL)
		__proc_set_tty(current, tty);
	spin_unlock_irq(¤t->sighand->siglock);
	tty_unlock();
	mutex_unlock(&tty_mutex);
	return 0;
}

tty_init_dev函数

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok)
{
	struct tty_struct *tty;
	int retval;

	if (driver->subtype == PTY_TYPE_MASTER &&(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
		return ERR_PTR(-EIO);
	}
	if (!try_module_get(driver->owner))
		return ERR_PTR(-ENODEV);

	tty = alloc_tty_struct();	//分配tty_struct
	if (!tty)
		goto fail_no_mem;
	initialize_tty_struct(tty, driver, idx);	//初始化tty_struct

	retval = tty_driver_install_tty(driver, tty);	//调用tty_driver的install方法,并初始化termios
	if (retval < 0) {
		free_tty_struct(tty);
		module_put(driver->owner);
		return ERR_PTR(retval);
	}

	retval = tty_ldisc_setup(tty, tty->link);	//调用线路规程的open方法,及线路规程link的open方法
	if (retval)
		goto release_mem_out;
	return tty;

fail_no_mem:
	module_put(driver->owner);
	return ERR_PTR(-ENOMEM);

release_mem_out:
	if (printk_ratelimit())
		printk(KERN_INFO "tty_init_dev: ldisc open failed,clearing slot %d\n", idx);
	release_tty(tty, idx);
	return ERR_PTR(retval);
}

tty_init_dev>>>initialize_tty_struct

void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx)
{
	memset(tty, 0, sizeof(struct tty_struct));	//初始化tty_struct
	kref_init(&tty->kref);
	tty->magic = TTY_MAGIC;	//魔数
	tty_ldisc_init(tty);	//初始化线路规程
	tty->session = NULL;
	tty->pgrp = NULL;
	tty->overrun_time = jiffies;
	tty->buf.head = tty->buf.tail = NULL;
	tty_buffer_init(tty);
	mutex_init(&tty->termios_mutex);
	mutex_init(&tty->ldisc_mutex);
	init_waitqueue_head(&tty->write_wait);	//初始化写等待队列头
	init_waitqueue_head(&tty->read_wait);	//初始化读等待队列头
	INIT_WORK(&tty->hangup_work, do_tty_hangup);
	mutex_init(&tty->atomic_read_lock);
	mutex_init(&tty->atomic_write_lock);
	mutex_init(&tty->output_lock);
	mutex_init(&tty->echo_lock);
	spin_lock_init(&tty->read_lock);
	spin_lock_init(&tty->ctrl_lock);
	INIT_LIST_HEAD(&tty->tty_files);
	INIT_WORK(&tty->SAK_work, do_SAK_work);

	tty->driver = driver;	//tty_struct->driver=tty_driver 捆绑tty_struct和tty_driver
	tty->ops = driver->ops;	//tty_strcut的操作函数集=tty_driver的操作函数集
	tty->index = idx;	//设置索引值
	tty_line_name(driver, idx, tty->name);
	tty->dev = tty_get_device(tty);	//设置tty_struct的设备文件
}

tty_init_dev>>>initialize_tty_struct>>>tty_ldisc_init

void tty_ldisc_init(struct tty_struct *tty)
{
	struct tty_ldisc *ld = tty_ldisc_get(N_TTY);	//获取线路规程数组tty_ldiscs[N_TTY]项
	if (IS_ERR(ld))
		panic("n_tty: init_tty");
	tty_ldisc_assign(tty, ld);	//设置线路规程
}

在start_kernel-->console_init-->tty_ldisc_begin-->tty_register_ldisc将tty_ldiscs[N_TTY]=tty_ldisc_N_TTY

tty_driver_install_tty

static int tty_driver_install_tty(struct tty_driver *driver,struct tty_struct *tty)
{
	int idx = tty->index;
	int ret;

	if (driver->ops->install) {	//tty驱动操作函数集存在install方法
		ret = driver->ops->install(driver, tty);	//则调用install方法
		return ret;
	}

	if (tty_init_termios(tty) == 0) {	//初始化termios
		tty_driver_kref_get(driver);
		tty->count++;
		driver->ttys[idx] = tty;
		return 0;
	}
	return -ENOMEM;
}

tty_driver_install_tty>>>tty_init_termios

int tty_init_termios(struct tty_struct *tty)
{
	struct ktermios *tp;
	int idx = tty->index;

	tp = tty->driver->termios[idx];	//获取termios
	if (tp == NULL) {
		tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
		if (tp == NULL)
			return -ENOMEM;
		memcpy(tp, &tty->driver->init_termios,sizeof(struct ktermios));
		tty->driver->termios[idx] = tp;
	}
	tty->termios = tp;
	tty->termios_locked = tp + 1;

	/* Compatibility until drivers always set this */
	tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);	//设置接收波特率
	tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);	//设置波特率
	return 0;
}
EXPORT_SYMBOL_GPL(tty_init_termios);

tty_ldisc_setup

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
	struct tty_ldisc *ld = tty->ldisc;
	int retval;
	retval = tty_ldisc_open(tty, ld);	//调用tty_struct的open方法
	if (retval)
		return retval;
	if (o_tty) {
		retval = tty_ldisc_open(o_tty, o_tty->ldisc);	//调用tty_struct->link的open方法
		if (retval) {
			tty_ldisc_close(tty, ld);
			return retval;
		}
		tty_ldisc_enable(o_tty);	//使能tty_struct->kink
	}
	tty_ldisc_enable(tty);	//使能tty_struct
	return 0;
}

 

tty_read操作

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{
	int i;
	struct inode *inode = file->f_path.dentry->d_inode;
	struct tty_struct *tty = file_tty(file);	//获取tty_strcut
	struct tty_ldisc *ld;

	if (tty_paranoia_check(tty, inode, "tty_read"))
		return -EIO;
	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
		return -EIO;

	ld = tty_ldisc_ref_wait(tty);	//获取线路规程
	if (ld->ops->read)		//线路规程存在读方法
		i = (ld->ops->read)(tty, file, buf, count);	//调用其读方法
	else
		i = -EIO;
	tty_ldisc_deref(ld);	//引用计数
	if (i > 0)
		inode->i_atime = current_fs_time(inode->i_sb);
	return i;
}

 

tty_write操作

static ssize_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{
	struct inode *inode = file->f_path.dentry->d_inode;
	struct tty_struct *tty = file_tty(file);	//获取tty_struct
 	struct tty_ldisc *ld;
	ssize_t ret;

	if (tty_paranoia_check(tty, inode, "tty_write"))
		return -EIO;
	if (!tty || !tty->ops->write ||(test_bit(TTY_IO_ERROR, &tty->flags)))
			return -EIO;
	/* Short term debug to catch buggy drivers */
	if (tty->ops->write_room == NULL)
		printk(KERN_ERR "tty driver %s lacks a write_room method.\n",tty->driver->name);
	ld = tty_ldisc_ref_wait(tty);
	if (!ld->ops->write)	//线路规程存在写方法
		ret = -EIO;
	else
		ret = do_tty_write(ld->ops->write, tty, file, buf, count);	//调用线路规程写方法
	tty_ldisc_deref(ld);
	return ret;
}

do_tty_write函数

do_tty_write(ld->ops->write,

                       tty, file, buf, count);

这个函数第一个参数是调用线路规程的写方法,写方法函数的参数是后面四个参数

该写方法将数据写入tty_struct相关的file里

static inline ssize_t do_tty_write(ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
			struct tty_struct *tty,struct file *file,const char __user *buf,size_t count)
{
	ssize_t ret, written = 0;
	unsigned int chunk;

	ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
	if (ret < 0)
		return ret;
	chunk = 2048;
	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
		chunk = 65536;
	if (count < chunk)
		chunk = count;

	/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
	if (tty->write_cnt < chunk) {
		unsigned char *buf_chunk;

		if (chunk < 1024)
			chunk = 1024;

		buf_chunk = kmalloc(chunk, GFP_KERNEL);
		if (!buf_chunk) {
			ret = -ENOMEM;
			goto out;
		}
		kfree(tty->write_buf);
		tty->write_cnt = chunk;
		tty->write_buf = buf_chunk;
	}

	/* Do the write .. */
	for (;;) {
		size_t size = count;
		if (size > chunk)
			size = chunk;
		ret = -EFAULT;
		if (copy_from_user(tty->write_buf, buf, size))
			break;
		ret = write(tty, file, tty->write_buf, size);
		if (ret <= 0)
			break;
		written += ret;
		buf += ret;
		count -= ret;
		if (!count)
			break;
		ret = -ERESTARTSYS;
		if (signal_pending(current))
			break;
		cond_resched();
	}
	if (written) {
		struct inode *inode = file->f_path.dentry->d_inode;
		inode->i_mtime = current_fs_time(inode->i_sb);
		ret = written;
	}
out:
	tty_write_unlock(tty);
	return ret;
}

五.线路规程操作

tty_driver的open,read,write最终都会调用线路规程的对应操作方法

线路规程默认函数集

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
};

1.open方法

static int n_tty_open(struct tty_struct *tty)
{
	if (!tty)
		return -EINVAL;

	if (!tty->read_buf) {
		tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
		if (!tty->read_buf)
			return -ENOMEM;
	}
	if (!tty->echo_buf) {
		tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);

		if (!tty->echo_buf)
			return -ENOMEM;
	}
	reset_buffer_flags(tty);
	tty->column = 0;
	n_tty_set_termios(tty, NULL);
	tty->minimum_to_wake = 1;
	tty->closing = 0;
	return 0;
}

2.写方法

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr)
{
	const unsigned char *b = buf;
	DECLARE_WAITQUEUE(wait, current);
	int c;
	ssize_t retval = 0;

	/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
	if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
		retval = tty_check_change(tty);
		if (retval)
			return retval;
	}

	/* Write out any echoed characters that are still pending */
	process_echoes(tty);

	add_wait_queue(&tty->write_wait, &wait);
	while (1) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (signal_pending(current)) {
			retval = -ERESTARTSYS;
			break;
		}
		if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
			retval = -EIO;
			break;
		}
		if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
			while (nr > 0) {
				ssize_t num = process_output_block(tty, b, nr);
				if (num < 0) {
					if (num == -EAGAIN)
						break;
					retval = num;
					goto break_out;
				}
				b += num;
				nr -= num;
				if (nr == 0)
					break;
				c = *b;
				if (process_output(c, tty) < 0)
					break;
				b++; nr--;
			}
			if (tty->ops->flush_chars)
				tty->ops->flush_chars(tty);	//调用tty_struct操作函数集合的flush_chars方法
		} else {
			while (nr > 0) {
				c = tty->ops->write(tty, b, nr);	//调用tty_struct操作函数集合的write方法
				if (c < 0) {
					retval = c;
					goto break_out;
				}
				if (!c)
					break;
				b += c;
				nr -= c;
			}
		}
		if (!nr)
			break;
		if (file->f_flags & O_NONBLOCK) {
			retval = -EAGAIN;
			break;
		}
		schedule();
	}
break_out:
	__set_current_state(TASK_RUNNING);
	remove_wait_queue(&tty->write_wait, &wait);
	if (b - buf != nr && tty->fasync)
		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
	return (b - buf) ? b - buf : retval;
}

3.读方法

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr)
{
	unsigned char __user *b = buf;
	DECLARE_WAITQUEUE(wait, current);
	int c;
	int minimum, time;
	ssize_t retval = 0;
	ssize_t size;
	long timeout;
	unsigned long flags;
	int packet;

do_it_again:

	BUG_ON(!tty->read_buf);

	c = job_control(tty, file);
	if (c < 0)
		return c;

	minimum = time = 0;
	timeout = MAX_SCHEDULE_TIMEOUT;
	if (!tty->icanon) {
		time = (HZ / 10) * TIME_CHAR(tty);
		minimum = MIN_CHAR(tty);
		if (minimum) {
			if (time)
				tty->minimum_to_wake = 1;
			else if (!waitqueue_active(&tty->read_wait) ||
				 (tty->minimum_to_wake > minimum))
				tty->minimum_to_wake = minimum;
		} else {
			timeout = 0;
			if (time) {
				timeout = time;
				time = 0;
			}
			tty->minimum_to_wake = minimum = 1;
		}
	}

	if (file->f_flags & O_NONBLOCK) {
		if (!mutex_trylock(&tty->atomic_read_lock))
			return -EAGAIN;
	} else {
		if (mutex_lock_interruptible(&tty->atomic_read_lock))
			return -ERESTARTSYS;
	}
	packet = tty->packet;

	add_wait_queue(&tty->read_wait, &wait);
	while (nr) {
		/* First test for status change. */
		if (packet && tty->link->ctrl_status) {
			unsigned char cs;
			if (b != buf)
				break;
			spin_lock_irqsave(&tty->link->ctrl_lock, flags);
			cs = tty->link->ctrl_status;
			tty->link->ctrl_status = 0;
			spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
			if (tty_put_user(tty, cs, b++)) {
				retval = -EFAULT;
				b--;
				break;
			}
			nr--;
			break;
		}
		set_current_state(TASK_INTERRUPTIBLE);

		if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
		    ((minimum - (b - buf)) >= 1))
			tty->minimum_to_wake = (minimum - (b - buf));

		if (!input_available_p(tty, 0)) {
			if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
				retval = -EIO;
				break;
			}
			if (tty_hung_up_p(file))
				break;
			if (!timeout)
				break;
			if (file->f_flags & O_NONBLOCK) {
				retval = -EAGAIN;
				break;
			}
			if (signal_pending(current)) {
				retval = -ERESTARTSYS;
				break;
			}
			/* FIXME: does n_tty_set_room need locking ? */
			n_tty_set_room(tty);
			timeout = schedule_timeout(timeout);
			continue;
		}
		__set_current_state(TASK_RUNNING);

		/* Deal with packet mode. */
		if (packet && b == buf) {
			if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
				retval = -EFAULT;
				b--;
				break;
			}
			nr--;
		}

		if (tty->icanon && !L_EXTPROC(tty)) {
			/* N.B. avoid overrun if nr == 0 */
			while (nr && tty->read_cnt) {
				int eol;

				eol = test_and_clear_bit(tty->read_tail,tty->read_flags);
				c = tty->read_buf[tty->read_tail];
				spin_lock_irqsave(&tty->read_lock, flags);
				tty->read_tail = ((tty->read_tail+1) &(N_TTY_BUF_SIZE-1));
				tty->read_cnt--;
				if (eol) {
					if (--tty->canon_data < 0)
						tty->canon_data = 0;
				}
				spin_unlock_irqrestore(&tty->read_lock, flags);

				if (!eol || (c != __DISABLED_CHAR)) {
					if (tty_put_user(tty, c, b++)) {
						retval = -EFAULT;
						b--;
						break;
					}
					nr--;
				}
				if (eol) {
					tty_audit_push(tty);
					break;
				}
			}
			if (retval)
				break;
		} else {
			int uncopied;
			uncopied = copy_from_read_buf(tty, &b, &nr);
			uncopied += copy_from_read_buf(tty, &b, &nr);
			if (uncopied) {
				retval = -EFAULT;
				break;
			}
		}

		if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
			n_tty_set_room(tty);
			check_unthrottle(tty);
		}

		if (b - buf >= minimum)
			break;
		if (time)
			timeout = time;
	}
	mutex_unlock(&tty->atomic_read_lock);
	remove_wait_queue(&tty->read_wait, &wait);

	if (!waitqueue_active(&tty->read_wait))
		tty->minimum_to_wake = minimum;

	__set_current_state(TASK_RUNNING);
	size = b - buf;
	if (size) {
		retval = size;
		if (nr)
			clear_bit(TTY_PUSH, &tty->flags);
	} else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
		 goto do_it_again;

	n_tty_set_room(tty);
	return retval;
}

六.线路规程的操作函数集会调用对应的tty_struct的操作函数集
在initialize_tty_struct中tty->ops = driver->ops也就是说tty_struct的操作函数集就是tty_driver的操作函数集合

tty_driver的操作函数集设置在其register之前,例如串口的注册函数uart_register_driver中调用tty_set_operations(normal, &uart_ops)

将tty_driver->ops设置为uart_ops。

这样我们对/dev/ttyXXX的操作的调用顺序:字符设备的操作函数集tty_fops-->线路规程的操作函数集tty_ldisc_N_TTY-->串口核心或其他类型设备的操作函数集uart_ops
五 linux 串口驱动

 

分类: linux设备驱动 312人阅读 评论(0)收藏 举报

一.串口结构体

1.串口驱动结构体

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设备
};

2.串口端口结构体

struct uart_port {
	spinlock_t	lock;
	unsigned long	iobase;	//io端口基地址
	unsigned char __iomem	*membase;		//内存端口基地址
	unsigned int	(*serial_in)(struct uart_port *, int);
	void	(*serial_out)(struct uart_port *, int, int);
	void	(*set_termios)(struct uart_port *,struct ktermios *new,struct ktermios *old);
	void	(*pm)(struct uart_port *, unsigned int state,unsigned int old);
	unsigned int	irq;	//中断号
	unsigned long	irqflags;	//中断标志
	unsigned int	uartclk;
	unsigned int	fifosize;	//fifo大小	
	unsigned char	x_char;
	unsigned char	regshift;	//寄存器偏移值	
	unsigned char	iotype;	//io访问类型
	unsigned char	unused1;
	unsigned int	read_status_mask;
	unsigned int	ignore_status_mask;
	struct uart_state	*state;	//uart_state结构体	
	struct uart_icount	icount;	//串口使用计数
	struct console	*cons;	//console控制台
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
	unsigned long	sysrq;
#endif
	upf_t	flags;
	unsigned int	mctrl;
	unsigned int	timeout;
	unsigned int	type;
	const struct uart_ops	*ops;	//串口操作函数集
	unsigned int	custom_divisor;
	unsigned int	line;	//端口号
	resource_size_t	mapbase;
	struct device	*dev;	//设备文件
	unsigned char	hub6;
	unsigned char	suspended;
	unsigned char	irq_wake;
	unsigned char	unused[2];
	void	*private_data;
};

3.操作函数集

struct uart_ops {
	unsigned int	(*tx_empty)(struct uart_port *);	//发送缓冲区为空
	void	(*set_mctrl)(struct uart_port *, unsigned int mctrl);	//设置串口modem控制模式
	unsigned int	(*get_mctrl)(struct uart_port *);	//获取串口modem控制模式
	void	(*stop_tx)(struct uart_port *);	//停止发送
	void	(*start_tx)(struct uart_port *);	//开始发送
	void	(*send_xchar)(struct uart_port *, char ch);
	void	(*stop_rx)(struct uart_port *);	//停止接收
	void	(*enable_ms)(struct uart_port *);	//使能modem状态信息
	void	(*break_ctl)(struct uart_port *, int ctl);
	int	(*startup)(struct uart_port *);	//打开串口
	void	(*shutdown)(struct uart_port *);	//关闭串口
	void	(*flush_buffer)(struct uart_port *);
	void	(*set_termios)(struct uart_port *, struct ktermios *new,struct ktermios *old);	//设置串口参数
	void	(*set_ldisc)(struct uart_port *, int new);
	void	(*pm)(struct uart_port *, unsigned int state,unsigned int oldstate);
	int	(*set_wake)(struct uart_port *, unsigned int state);
	const char *(*type)(struct uart_port *);
	void	(*release_port)(struct uart_port *);	//释放端口
	int	(*request_port)(struct uart_port *);	//请求端口
	void	(*config_port)(struct uart_port *, int);	//配置端口
	int	(*verify_port)(struct uart_port *, struct serial_struct *);	//校验端口
	int	(*ioctl)(struct uart_port *, unsigned int, unsigned long);	//控制
#ifdef CONFIG_CONSOLE_POLL
	void	(*poll_put_char)(struct uart_port *, unsigned char);
	int	(*poll_get_char)(struct uart_port *);
#endif
};

4.uart_state

struct uart_state {
	struct tty_port	port;
	int		pm_state;
	struct circ_buf	xmit;
	struct tasklet_struct	tlet;
	struct uart_port	*uart_port;
};

 

二.串口驱动的注册与注销

注册

int uart_register_driver(struct uart_driver *drv)
{
	struct tty_driver *normal;
	int i, retval;
	BUG_ON(drv->state);
	drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);	//分配uart_state内存
	if (!drv->state)
		goto out;
	normal = alloc_tty_driver(drv->nr);	//分配tty_driver
	if (!normal)
		goto out_kfree;
	drv->tty_driver = normal;	//tty_driver和uart_driver捆绑

	normal->owner		= drv->owner;	//模块所有者
	normal->driver_name	= drv->driver_name;	//驱动名
	normal->name		= drv->dev_name;	//设备名
	normal->major		= drv->major;	//主设备号
	normal->minor_start	= drv->minor;	//次设备号
	normal->type		= TTY_DRIVER_TYPE_SERIAL;	//tty类型
	normal->subtype		= SERIAL_TYPE_NORMAL;	//tty子类型
	normal->init_termios	= tty_std_termios;		//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);	//设置tty操作函数集

	for (i = 0; i < drv->nr; i++) {	//初始化uart_state
		struct uart_state *state = drv->state + i;
		struct tty_port *port = &state->port;
		tty_port_init(port);	//初始化tty端口
		port->ops = &uart_port_ops;	//tty端口操作函数集
		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);	//注册tty驱动
	if (retval >= 0)
		return retval;

	put_tty_driver(normal);	//引用计数
out_kfree:
	kfree(drv->state);
out:
	return -ENOMEM;
}

 注销

void uart_unregister_driver(struct uart_driver *drv)
{
	struct tty_driver *p = drv->tty_driver;
	tty_unregister_driver(p);	//注销tty驱动
	put_tty_driver(p);	//减少引用计数
	kfree(drv->state);
	drv->tty_driver = NULL;
}

三.端口注册与注销

注册

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
	struct uart_state *state;
	struct tty_port *port;
	int ret = 0;
	struct device *tty_dev;
	BUG_ON(in_interrupt());
	if (uport->line >= drv->nr)
		return -EINVAL;
	state = drv->state + uport->line;	//获取uart_state
	port = &state->port;	//获取tty_port
	mutex_lock(&port_mutex);
	mutex_lock(&port->mutex);
	if (state->uart_port) {
		ret = -EINVAL;
		goto out;
	}
	state->uart_port = uport;	//设置uart_state->uart_port
	state->pm_state = -1;
	uport->cons = drv->cons;	//设置uart_port->cons控制台
	uport->state = state;	//设置uart_port->state
	if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
		spin_lock_init(&uport->lock);
		lockdep_set_class(&uport->lock, &port_lock_key);
	}
	uart_configure_port(drv, state, uport);	//配置端口
	tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);	//生成tty设备文件/dev/ttyXXX
	if (likely(!IS_ERR(tty_dev))) {
		device_init_wakeup(tty_dev, 1);
		device_set_wakeup_enable(tty_dev, 0);
	} else
		printk(KERN_ERR "Cannot register tty device on line %d\n",uport->line);
	uport->flags &= ~UPF_DEAD;
 out:
	mutex_unlock(&port->mutex);
	mutex_unlock(&port_mutex);

	return ret;
}

uart_configure_port

static void uart_configure_port(struct uart_driver *drv, struct uart_state *state,struct uart_port *port)
{
	unsigned int flags;
	if (!port->iobase && !port->mapbase && !port->membase)
		return;
	flags = 0;
	if (port->flags & UPF_AUTO_IRQ)	//设置可中断标志
		flags |= UART_CONFIG_IRQ;
	if (port->flags & UPF_BOOT_AUTOCONF) {	//设置自动配置标志
		if (!(port->flags & UPF_FIXED_TYPE)) {
			port->type = PORT_UNKNOWN;
			flags |= UART_CONFIG_TYPE;
		}
		port->ops->config_port(port, flags);	//调用uart_port的config_port方法
	}

	if (port->type != PORT_UNKNOWN) {
		unsigned long flags;
		uart_report_port(drv, port);
		uart_change_pm(state, 0);
		spin_lock_irqsave(&port->lock, flags);
		port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);	//调用uart_port的set_mctrl方法
		spin_unlock_irqrestore(&port->lock, flags);
		if (port->cons && !(port->cons->flags & CON_ENABLED))	//若console存在且设置了CON_ENABLED标志
			register_console(port->cons);	//注册控制台设备
		if (!uart_console(port))
			uart_change_pm(state, 3);
	}
}

 注销

int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
{
	struct uart_state *state = drv->state + uport->line;
	struct tty_port *port = &state->port;
	BUG_ON(in_interrupt());
	if (state->uart_port != uport)
		printk(KERN_ALERT "Removing wrong port: %p != %p\n",state->uart_port, uport);
	mutex_lock(&port_mutex);
	mutex_lock(&port->mutex);
	uport->flags |= UPF_DEAD;
	mutex_unlock(&port->mutex);
	tty_unregister_device(drv->tty_driver, uport->line);
	if (port->tty)
		tty_vhangup(port->tty);
	if (uport->type != PORT_UNKNOWN)
		uport->ops->release_port(uport);
	uport->type = PORT_UNKNOWN;
	tasklet_kill(&state->tlet);
	state->uart_port = NULL;
	mutex_unlock(&port_mutex);
	return 0;
}

四.串口对应的tty_driver的操作函数集

1.tty的操作函数集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
};

2.open方法

static int uart_open(struct tty_struct *tty, struct file *filp)
{
	struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
	struct uart_state *state;
	struct tty_port *port;
	int retval, line = tty->index;

	BUG_ON(!tty_locked());
	pr_debug("uart_open(%d) called\n", line);
	retval = -ENODEV;
	if (line >= tty->driver->num)
		goto fail;
	state = uart_get(drv, line);	//获取uart_state
	if (IS_ERR(state)) {
		retval = PTR_ERR(state);
		goto fail;
	}
	port = &state->port;	//获取tty_port
	tty->driver_data = state;
	state->uart_port->state = state;
	tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
	tty->alt_speed = 0;
	tty_port_tty_set(port, tty);
	if (tty_hung_up_p(filp)) {
		retval = -EAGAIN;
		port->count--;
		mutex_unlock(&port->mutex);
		goto fail;
	}
	if (port->count == 1)
		uart_change_pm(state, 0);
	retval = uart_startup(tty, state, 0);	//启动串口
	mutex_unlock(&port->mutex);
	if (retval == 0)
		retval = tty_port_block_til_ready(port, tty, filp);
fail:
	return retval;
}

uart_startup

static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
{
	struct uart_port *uport = state->uart_port;	//获取uart_port
	struct tty_port *port = &state->port;	//获取tty_port
	unsigned long page;
	int retval = 0;

	if (port->flags & ASYNC_INITIALIZED)
		return 0;
	set_bit(TTY_IO_ERROR, &tty->flags);
	if (uport->type == PORT_UNKNOWN)
		return 0;
	if (!state->xmit.buf) {
		/* This is protected by the per port mutex */
		page = get_zeroed_page(GFP_KERNEL);
		if (!page)
			return -ENOMEM;
		state->xmit.buf = (unsigned char *) page;
		uart_circ_clear(&state->xmit);
	}
	retval = uport->ops->startup(uport);	//调用uart_port的startup方法
	if (retval == 0) {
		if (init_hw) {
			uart_change_speed(tty, state, NULL);
			if (tty->termios->c_cflag & CBAUD)
				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
		}
		if (port->flags & ASYNC_CTS_FLOW) {
			spin_lock_irq(&uport->lock);
			if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
				tty->hw_stopped = 1;
			spin_unlock_irq(&uport->lock);
		}
		set_bit(ASYNCB_INITIALIZED, &port->flags);
		clear_bit(TTY_IO_ERROR, &tty->flags);
	}
	if (retval && capable(CAP_SYS_ADMIN))
		retval = 0;
	return retval;
}

3.写方法

static int uart_write(struct tty_struct *tty,const unsigned char *buf, int count)
{
	struct uart_state *state = tty->driver_data;	//获取uart_state
	struct uart_port *port;
	struct circ_buf *circ;
	unsigned long flags;
	int c, ret = 0;
	if (!state) {
		WARN_ON(1);
		return -EL3HLT;
	}
	port = state->uart_port;	//获取uart_port
	circ = &state->xmit;
	if (!circ->buf)
		return 0;

	spin_lock_irqsave(&port->lock, flags);
	while (1) {
		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;
	}
	spin_unlock_irqrestore(&port->lock, flags);
	uart_start(tty);	//调用uart_start方法
	return ret;
}

uart_start

static void uart_start(struct tty_struct *tty)
{
	struct uart_state *state = tty->driver_data;	//获取tty_state
	struct uart_port *port = state->uart_port;	//获取uart_port
	unsigned long flags;

	spin_lock_irqsave(&port->lock, flags);
	__uart_start(tty);		//调用__uart_start函数
	spin_unlock_irqrestore(&port->lock, flags);
}

uart_start>>>__uart_start

static void __uart_start(struct tty_struct *tty)
{
	struct uart_state *state = tty->driver_data;	//获取uart_state
	struct uart_port *port = state->uart_port;	//获取uart_port
	if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&!tty->stopped && !tty->hw_stopped)
		port->ops->start_tx(port);	//调用uart_port的start_tx方法
}



 

六 linux串口编程

一.结构体

1.termios

struct termios  {    
	tcflag_t c_iflag;		/* 输入模式标志 */    
	tcflag_t c_oflag;		/* 输出模式标志 */    
	tcflag_t c_cflag;		/* 控制模式标志 */    
	tcflag_t c_lflag;		/* 本地模式标志 */    
	cc_t c_line;			/* 线路规程 */    
	cc_t c_cc[NCCS];		/* 控制字符 */    
	speed_t c_ispeed;		/* 输入速率 */    
	speed_t c_ospeed;		/* 输出速率 */
};

二.定义

1.类型定义

typedef unsigned char	cc_t;
typedef unsigned int	speed_t;
typedef unsigned int	tcflag_t;

2.c_cc

#define 	VINTR 	0	//中断 Ctrl-C
#define 	VQUIT 	1	//退出 Ctrl-Z
#define 	VERASE 	2	//擦除 空格BS
#define 	VKILL 	3	//删行 Ctrl-U
#define 	VEOF 	4	//文件结尾 Ctrl-D
#define 	VTIME 	5	//等待时间
#define 	VMIN 	6	//读取的最小字符数
#define 	VSWTC 	7	//
#define 	VSTART 	8	//软件流开始
#define 	VSTOP 	9	//软件流停止
#define 	VSUSP 	10	//
#define 	VEOL 	11	//行结尾 回车CR
#define 	VREPRINT 	12	//
#define 	VDISCARD 	13	//
#define 	VWERASE 	14	//
#define 	VLNEXT 	15	//
#define 	VEOL2 	16	//第二行结尾 进行LF

3.c_iflag

#define 	IGNBRK	0000001	//忽略任何中断条件
#define 	BRKINT	0000002	//传送SIGINT,当检测到中断
#define 	IGNPAR	0000004	//忽略校验错误
#define 	PARMRK	0000010	//标记校验错误
#define 	INPCK	0000020	//校验有效
#define 	ISTRIP	0000040	//剥离校验位
#define 	INLCR	0000100	//映射NL到CR
#define 	IGNCR	0000200	//忽略CR
#define 	ICRNL	0000400	//映射CR到NL
#define 	IUCLC	0001000	//映射大写到小写
#define 	IXON	0002000	//软件流控制输出作用
#define 	IXANY	0004000	//允许任何字符再次启动流
#define 	IXOFF	0010000	//软件流控制输入作用
#define 	IMAXBEL	0020000	//在输入线上回显BEL过长
#define 	IUTF8	0040000

4.c_oflag

#define 	OPOST	0000001	//输出后处理(不设置=行式输出)
#define 	OLCUC	0000002	//映射小写到大写
#define 	ONLCR	0000004	//映射NL到CR-NL
#define 	OCRNL	0000010	//映射CR到NL
#define 	ONOCR	0000020	//
#define 	ONLRET	0000040	//NL实现CR功能
#define 	OFILL	0000100	//对于延时用填充字符
#define 	OFDEL	0000200	//填充字符DEL
#define 	NLDLY	0000400	//在行之间,需要掩蔽延时
#define 	NL0	0000000	//NL没有延时
#define 	NL1	0000400	//在换行后,新的输出前延时100ms
#define 	CRDLY	0003000	//回车到左边,需要掩蔽的延时
#define  	CR0	0000000	//CR没有延时
#define  	CR1	0001000	//CR后的延时,依赖于当前的列位置
#define  	CR2	0002000	//CR后延时100ms
#define  	CR3	0003000	//CR后延时150ms
#define 	TABDLY	0014000	//TAB后需要延时
#define  	TAB0	0000000	//TAB后没有延时
#define  	TAB1	0004000	//TAB后的延时根据当前的列位置
#define  	TAB2	0010000	//TAB后延时100ms
#define  	TAB3	0014000	//TAB扩展为空格
#define  	XTABS	0014000	//
#define 	BSDLY	0020000	//BS后需要延时掩蔽
#define  	BS0	0000000	//BS后无延时
#define  	BS1	0020000	//BS后延时50ms
#define 	VTDLY	0040000	//VT后需要延时掩蔽
#define  VT0	0000000	//VT后无延时
#define  	VT1	0040000	//VT后延时2秒
#define 	FFDLY	0100000	//FF后需要延时掩蔽
#define 	FF0	0000000	//FF后无延时
#define  FF1	0100000	//FF后延时2秒

5.c_cflag

#define 	CBAUD	0010017		//波特率掩码
#define  	B0	0000000		//挂起
#define  	B50	0000001		//50 b/s
#define  	B75	0000002		//75 b/s
#define  	B110	0000003		//110 b/s
#define  	B134	0000004		//134.5 b/s
#define  	B150	0000005		//150 b/s
#define  	B200	0000006		//200 b/s
#define  	B300	0000007		//300 b/s
#define  	B600	0000010		//600 b/s
#define  	B1200	0000011		//1200 b/s
#define  	B1800	0000012		//1800 b/s
#define  	B2400	0000013		//2400 b/s
#define  	B4800	0000014		//4800 b/s
#define  	B9600	0000015		//9600 b/s
#define  	B19200	0000016		//19200 b/s
#define  	B38400	0000017		//38400 b/s
#define 	EXTA 	B19200		//外部时钟A
#define 	EXTB 	B38400		//外部时钟B
#define 	CSIZE	0000060		//位数据位掩码
#define 	CS5	0000000		//5位数据位
#define 	CS6	0000020		//6位数据位
#define 	CS7	0000040		//7位数据位
#define 	CS8	0000060		//8位数据位
#define 	CSTOPB	0000100		//2位停止位
#define 	CREAD	0000200		//接收有效
#define 	PARENB	0000400		//校验位有效
#define 	PARODD	0001000		//奇偶校验
#define 	HUPCL	0002000		//最后关闭后,挂起
#define 	CLOCAL	0004000		//本地线-不 改变端口的"拥有者"
#define 	CBAUDEX 	0010000		//
#define 	BOTHER 	0010000		//
#define 	B57600 	0010001		//57600 b/s
#define 	B115200 	0010002		//115200 b/s
#define 	B230400 	0010003		//230400 b/s
#define 	B460800 	0010004		//460800 b/s
#define 	B500000 	0010005		//500000 b/s
#define 	B576000 	0010006		//576000 b/s
#define 	B921600 	0010007		//921600 b/s
#define  B1000000 	0010010		//1000000 b/s
#define  B1152000 	0010011		//1152000 b/s
#define  B1500000 	0010012		//1500000 b/s
#define  B2000000 	0010013		//2000000 b/s
#define  B2500000 	0010014		//2500000 b/s
#define  B3000000 	0010015		//3000000 b/s
#define  B3500000 	0010016		//3500000 b/s
#define  B4000000 	0010017		//4000000 b/s
#define 	CIBAUD	002003600000	//输入波特率
#define 	CMSPAR  	010000000000	/* mark or space (stick) parity */
#define 	CRTSCTS	020000000000	/* flow control */

#define IBSHIFT	   16

6.c_lflag

#define ISIG	0000001	//使SIGINTR,SIGSUSP,SIGDSUSP,SIGQUIT等信号作用
#define ICANON	0000002	//设定规范canonical(或行式raw)
#define XCASE	0000004	//map uppercase\lowercase(废除)
#define ECHO	0000010	//回显输入字符
#define ECHOE	0000020	//回显擦除字符 BS-SP-BS
#define ECHOK	0000040	//在删除字符后,回显NL
#define ECHONL	0000100	//回显NL
#define NOFLSH	0000200	//不在中断后退出字符后刷新输入缓冲
#define TOSTOP	0000400	//传送SIGTTOU信号作为背景
#define ECHOCTL	0001000	//以^char方式回显控制字符和以~?方式显示删除字符
#define ECHOPRT	0002000	//回显提示有删除字符
#define ECHOKE	0004000	//BS-SP-BS整行,在有行删除时
#define FLUSHO	0010000	//刷新输出
#define PENDIN	0040000	//在下次读或输入时,将未决字符重打
#define IEXTEN	0100000	
#define EXTPROC	0200000

 7.其他

/* tcflow() and TCXONC use these */
#define	TCOOFF	0
#define	TCOON	1
#define	TCIOFF	2
#define	TCION	3

/* tcflush() and TCFLSH use these */
#define	TCIFLUSH		0
#define	TCOFLUSH		1
#define	TCIOFLUSH		2

/* tcsetattr 使用到这些参数*/
#define	TCSANOW		0	//立即改变,不等待数据结束
#define	TCSADRAIN		1	//等待直到所有的都传完
#define	TCSAFLUSH		2	//刷新输入输出缓冲,然后改变


三.API函数

1.基本操作

打开串口 O_NOCTTY表示不是系统终端,这样就不会受键盘Ctrl-C等操作影响

fd = open("/dev/ttyXXX", O_RDWR | O_NOCTTY | O_NDELAY);

写(返回写入字节数)

ssize_t write(int fildes, const void *buf, size_t nbyte);

读(返回读取字节数)

ssize_t read(int fildes, void *buf, size_t nbyte);

关闭串口

close(unsigned int fd)

控制

int fcntl(int fildes, int cmd, ...);
fcntl(fd,F_SETFD,FNDELAY);	//非阻塞
fcntl(fd,F_SETFD,0);		//阻塞

控制命令还有以下几个针对tty的(魔数为0x54xx或‘T’),具体使用参考tty_ioctl函数,

#define TCGETS		0x5401
#define TCSETS		0x5402
#define TCSETSW		0x5403
#define TCSETSF		0x5404
#define TCGETA		0x5405
#define TCSETA		0x5406
#define TCSETAW		0x5407
#define TCSETAF		0x5408
#define TCSBRK		0x5409
#define TCXONC		0x540A
#define TCFLSH		0x540B
#define TIOCEXCL	0x540C
#define TIOCNXCL	0x540D
#define TIOCSCTTY	0x540E
#define TIOCGPGRP	0x540F
#define TIOCSPGRP	0x5410
#define TIOCOUTQ	0x5411
#define TIOCSTI		0x5412
#define TIOCGWINSZ	0x5413
#define TIOCSWINSZ	0x5414
#define TIOCMGET	0x5415
#define TIOCMBIS	0x5416
#define TIOCMBIC	0x5417
#define TIOCMSET	0x5418
#define TIOCGSOFTCAR	0x5419
#define TIOCSSOFTCAR	0x541A
#define FIONREAD	0x541B
#define TIOCINQ		FIONREAD
#define TIOCLINUX	0x541C
#define TIOCCONS	0x541D
#define TIOCGSERIAL	0x541E
#define TIOCSSERIAL	0x541F
#define TIOCPKT		0x5420
#define FIONBIO		0x5421
#define TIOCNOTTY	0x5422
#define TIOCSETD	0x5423
#define TIOCGETD	0x5424
#define TCSBRKP		0x5425	/* Needed for POSIX tcsendbreak() */
#define TIOCSBRK	0x5427  /* BSD compatibility */
#define TIOCCBRK	0x5428  /* BSD compatibility */
#define TIOCGSID	0x5429  /* Return the session ID of FD */
#define TCGETS2		_IOR('T', 0x2A, struct termios2)
#define TCSETS2		_IOW('T', 0x2B, struct termios2)
#define TCSETSW2	_IOW('T', 0x2C, struct termios2)
#define TCSETSF2	_IOW('T', 0x2D, struct termios2)
#define TIOCGRS485	0x542E
#ifndef TIOCSRS485
#define TIOCSRS485	0x542F
#endif
#define TIOCGPTN	_IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK	_IOW('T', 0x31, int)  /* Lock/unlock Pty */
#define TCGETX		0x5432 /* SYS5 TCGETX compatibility */
#define TCSETX		0x5433
#define TCSETXF		0x5434
#define TCSETXW		0x5435
#define TIOCSIG		_IOW('T', 0x36, int)  /* pty: generate signal */

#define FIONCLEX	0x5450
#define FIOCLEX		0x5451
#define FIOASYNC	0x5452
#define TIOCSERCONFIG	0x5453
#define TIOCSERGWILD	0x5454
#define TIOCSERSWILD	0x5455
#define TIOCGLCKTRMIOS	0x5456
#define TIOCSLCKTRMIOS	0x5457
#define TIOCSERGSTRUCT	0x5458 /* For debugging only */
#define TIOCSERGETLSR   0x5459 /* Get line status register */
#define TIOCSERGETMULTI 0x545A /* Get multiport config  */
#define TIOCSERSETMULTI 0x545B /* Set multiport config */

#define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
#define TIOCGICOUNT	0x545D	/* read serial port __inline__ interrupt counts */

2.串口设置

int tcgetattr(int fildes, struct termios *termios_p);//获取串口参数
int tcsetattr(int fildes, int optional_actions,const struct termios *termios_p);//设置串口参数
speed_t cfgetispeed(const struct termios *termios_p);//获取输入波特率
int cfsetispeed(struct termios *termios_p, speed_t speed);//设置输入波特率
speed_t cfgetospeed(const struct termios *termios_p);//获取输出波特率
int cfsetospeed(struct termios *termios_p, speed_tspeed);//设置输出波特率

大致这样使用

struct termios options;

tcgetattr(fd, &options);	//--------获取属性

//--------设置波特率
if(cfgetispeed(&options)!=B115200)	//获取输入波特率
	cfsetispeed(&options, B19200);	//设置输入波特率

cfsetospeed(&options, B19200);	//设置输出波特率
if(cfgetospeed(&options)!=B19200)	//获取输出波特率
	perror("set baud rates fail!\n");

//--------本地模式|串行数据接收
options.c_cflag!=(CLOCAL|CREAD);	

//--------设置数据位格式
ifdef UART_8N1
//8N1 - 无校验
	options.c_cflag &= ~PARENB;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;
#endif 

ifdef UART_7E1
//7E1 - 偶校验
	options.c_cflag |= PARENB;
	options.c_cflag &= ~PARODD;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS7;
#endif 

ifdef UART_7O1
//7O1 - 奇校验
	options.c_cflag |= PARENB;
	options.c_cflag |= PARODD;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS7;
#endif 

ifdef UART_7S1
//7S1 - 空校验设置同无校验
	options.c_cflag &= ~PARENB;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;
#endif 

//--------硬件流控制
#ifdef CTSRTS
	options.c_cflag |= CNEW_RTSCTS;
#else
	options.c_cflag &= ~CNEW_RTSCTS;
#endif

tcsetattr(fd, TCSANOW, &options);	//--------设置属性
3.其他API函数

int tcflow(int fildes, int action);//软件流控制
int tcflush(int fildes, int queue_selector);//丢弃要写入引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据
int tcdrain(int fildes);//等待直到所有写入
int tcsendbreak(int fildes, int duration);//传送连续的0值比特流,持续一段时间
pid_t tcgetpgrp(int fildes);	//获取组id
int tcsetpgrp(int fildes, pid_t pgid_id);	//设置组id
pid_t tcgetsid(int fildes);	//获取sid
int isatty(int fildes);	//判断使用的是不是tty终端
char *ttyname(int fildes);	//获取tty路径名

四.例子

网上找来个好的例子

/************************Copyright(c)******************************* 
**                       西安邮电学院 
**                       graduate school 
**                       XNMS项目组 
**                       WebSite :blog.csdn.net/tigerjb 
**------------------------------------------FileInfo------------------------------------------------------- 
** File name:                 main.c 
** Last modified Date:  2011-01-31 
** Last Version:              1.0 
** Descriptions:             
**------------------------------------------------------------------------------------------------------ 
** Created by:               冀博 
** Created date:            2011-01-31 
** Version:                            1.0 
** Descriptions:             The original version 
**------------------------------------------------------------------------------------------------------ 
** Modified by: 
** Modified date: 
** Version: 
** Descriptions: 
*******************************************************************/  
   
   
//串口相关的头文件   
#include<stdio.h>      /*标准输入输出定义*/   
#include<stdlib.h>     /*标准函数库定义*/   
#include<unistd.h>     /*Unix 标准函数定义*/   
#include<sys/types.h>    
#include<sys/stat.h>      
#include<fcntl.h>      /*文件控制定义*/   
#include<termios.h>    /*PPSIX 终端控制定义*/   
#include<errno.h>      /*错误号定义*/   
#include<string.h>   
   
//宏定义   
#define FALSE  -1   
#define TRUE   0   
   
/******************************************************************* 
* 名称:                  UART0_Open 
* 功能:                打开串口并返回串口设备文件描述 
* 入口参数:        fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2) 
* 出口参数:        正确返回为1,错误返回为0 
*******************************************************************/  
int UART0_Open(int fd,char* port)  
{  
	fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);  
	if(FALSE == fd){  
		perror("Can't Open Serial Port");  
		return(FALSE);  
	}  
	//恢复串口为阻塞状态                                  
	if(fcntl(fd, F_SETFL, 0) < 0){  
		printf("fcntl failed!\n");  
		return(FALSE);  
	}       
	else{  
		printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));  
	}  
	//测试是否为终端设备       
	if(0 == isatty(STDIN_FILENO)){  
		printf("standard input is not a terminal device\n");  
		return(FALSE);  
	}  
	else{  
		printf("isatty success!\n");  
	}                
	printf("fd->open=%d\n",fd);  
	return fd;  
}  
/******************************************************************* 
* 名称:                UART0_Close 
* 功能:                关闭串口并返回串口设备文件描述 
* 入口参数:        fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2) 
* 出口参数:        void 
*******************************************************************/  
   
void UART0_Close(int fd)  
{  
	close(fd);  
}  
   
/******************************************************************* 
* 名称:                UART0_Set 
* 功能:               设置串口数据位,停止位和效验位 
* 入口参数:fd       串口文件描述符 
* 	speed     串口速度 
*        	flow_ctrl 数据流控制 0不使用 1硬件流控制 2软件流控制
*        databits   数据位   取值为 7 或者8 
*        stopbits   停止位   取值为 1 或者2 
*        parity     效验类型 取值为N,E,O,,S 
*出口参数:   	正确返回为1,错误返回为0 
*******************************************************************/  
int UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)  
{  
	int   i;  
	int   status;  
	int   speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};  
	int   name_arr[] = {115200,  19200,  9600,  4800,  2400,  1200,  300};  
    	struct termios options;  
	/*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将其保存于options,该函数还可以测试配置是否正确,
		该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1*/  
	if (tcgetattr( fd,&options)!= 0){  
		perror("SetupSerial 1");      
		return(FALSE);   
	}  
    
    //设置串口输入波特率和输出波特率   
	for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++){  
		if(speed == name_arr[i]){               
			cfsetispeed(&options, speed_arr[i]);   
			cfsetospeed(&options, speed_arr[i]);    
		}  
	}       
     
    //修改控制模式,保证程序不会占用串口   
	options.c_cflag |= CLOCAL;  
    //修改控制模式,使得能够从串口中读取输入数据   
	options.c_cflag |= CREAD;  
    
    //设置数据流控制   
	switch(flow_ctrl)  
    {      
	case 0://不使用流控制   
		options.c_cflag &= ~CRTSCTS;  
		break;     
        
	case 1://使用硬件流控制   
		options.c_cflag |= CRTSCTS;  
		break;  
	case 2://使用软件流控制   
		options.c_cflag |= IXON | IXOFF | IXANY;  
		break;  
    }  
    //设置数据位   
    //屏蔽其他标志位   
    options.c_cflag &= ~CSIZE;  
    switch (databits)  
    {    
	case 5:  
		options.c_cflag |= CS5;  
		break;  
	case 6:  
		options.c_cflag |= CS6;  
		break;  
	case 7:      
		options.c_cflag |= CS7;  
		break;  
	case 8:      
		options.c_cflag |= CS8;  
		break;    
	default:     
		fprintf(stderr,"Unsupported data size\n");  
		return (FALSE);   
    }  
    //设置校验位   
    switch (parity)  
    {    
	case 'n':  
	case 'N': //无奇偶校验位。   
		options.c_cflag &= ~PARENB;   
		options.c_iflag &= ~INPCK;      
		break;   
	case 'o':    
	case 'O'://设置为奇校验       
		options.c_cflag |= (PARODD | PARENB);   
		options.c_iflag |= INPCK;               
		break;   
	case 'e':   
	case 'E'://设置为偶校验     
		options.c_cflag |= PARENB;         
		options.c_cflag &= ~PARODD;         
		options.c_iflag |= INPCK;        
		break;  
	case 's':  
	case 'S': //设置为空格    
		options.c_cflag &= ~PARENB;  
		options.c_cflag &= ~CSTOPB;  
		break;   
	default:    
		fprintf(stderr,"Unsupported parity\n");      
		return (FALSE);   
    }   
    // 设置停止位    
	switch (stopbits)  
    {    
	case 1:     
		options.c_cflag &= ~CSTOPB; break;   
	case 2:     
		options.c_cflag |= CSTOPB; break;  
	default:     
		fprintf(stderr,"Unsupported stop bits\n");   
		return (FALSE);  
    }  
     
	//修改输出模式,原始数据输出   
	options.c_oflag &= ~OPOST;  
    
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);//我加的   
	//options.c_lflag &= ~(ISIG | ICANON);   
     
    //设置等待时间和最小接收字符   
    options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */    
    options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */  
     
    //如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读   
    tcflush(fd,TCIFLUSH);  
     
    //激活配置 (将修改后的termios数据设置到串口中)   
    if (tcsetattr(fd,TCSANOW,&options) != 0){  
	perror("com set error!\n");    
	return (FALSE);   
	}  
    return (TRUE);   
}  
/******************************************************************* 
* 名称:		UART0_Init() 
* 功能:		串口初始化 
* 入口参数:fd	:  文件描述符    
*		speed  	:  串口速度 
*		flow_ctrl  数据流控制 
*		databits   数据位   取值为 7 或者8 
*		stopbits   停止位   取值为 1 或者2 
*		parity     效验类型 取值为N,E,O,,S 
*                       
* 出口参数:        正确返回为1,错误返回为0 
*******************************************************************/  
int UART0_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)  
{  
    int err;  
    //设置串口数据帧格式   
    if (UART0_Set(fd,19200,0,8,1,'N') == FALSE){                                                           
		return FALSE;  
	}  
    else{  
		return  TRUE;  
	}  
}  
   
/******************************************************************* 
* 名称:		UART0_Recv 
* 功能:		接收串口数据 
* 入口参数:	fd	:文件描述符     
*		rcv_buf	:接收串口中数据存入rcv_buf缓冲区中 
*		data_len	:一帧数据的长度 
* 出口参数:	正确返回为1,错误返回为0 
*******************************************************************/  
int UART0_Recv(int fd, char *rcv_buf,int data_len)  
{  
    int len,fs_sel;  
    fd_set fs_read;  
     
    struct timeval time;  
     
    FD_ZERO(&fs_read);  
    FD_SET(fd,&fs_read);  
     
    time.tv_sec = 10;  
    time.tv_usec = 0;  
     
    //使用select实现串口的多路通信   
    fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);  
    if(fs_sel){  
		len = read(fd,rcv_buf,data_len);  
		printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel);  
			return len;  
	}  
    else{  
		printf("Sorry,I am wrong!");  
		return FALSE;  
	}       
}  
/******************************************************************** 
* 名称:		UART0_Send 
* 功能:		发送数据 
* 入口参数:fd	:文件描述符     
*	send_buf :存放串口发送数据 
*	data_len :一帧数据的个数 
* 出口参数:正确返回为1,错误返回为0 
*******************************************************************/  
int UART0_Send(int fd, char *send_buf,int data_len)  
{  
    int len = 0;  
     
    len = write(fd,send_buf,data_len);  
    if (len == data_len )  
              {  
                     return len;  
              }       
    else     
        {  
                 
                tcflush(fd,TCOFLUSH);  
                return FALSE;  
        }  
     
}  
   
int main(int argc, char **argv)  
{  
    int fd;		//文件描述符   
    int err;	//返回调用函数的状态   
    int len;                          
    int i;  
    char rcv_buf[100];         
    char send_buf[20]="tiger john";  
    if(argc != 3){  
		printf("Usage: %s /dev/ttySn 0(send data)/1 (receive data) \n",argv[0]);  
		return FALSE;  
	}  
    fd = UART0_Open(fd,argv[1]); //打开串口,返回文件描述符   
    do{  
		err = UART0_Init(fd,19200,0,8,1,'N');  
		printf("Set Port Exactly!\n");  
	}while(FALSE == err || FALSE == fd);  
     
    if(0 == strcmp(argv[2],"0")){  
		for(i = 0;i < 10;i++){  
			len = UART0_Send(fd,send_buf,10);  
			if(len > 0)  
				printf(" %d send data successful\n",i);  
			else  
				printf("send data failed!\n");  
			sleep(2);  
		}  
		UART0_Close(fd);               
	}  
    else{  
		while (1){    //循环读取数据 
			len = UART0_Recv(fd, rcv_buf,9);  
			if(len > 0){  
				rcv_buf[len] = '\0';  
				printf("receive data is %s\n",rcv_buf);  
				printf("len = %d\n",len);  
			}  
			else{  
				printf("cannot receive data\n");  
			}  
			sleep(2);  
		}              
		UART0_Close(fd);   
	}  
}  
    
/*********************************************************************
                            End Of File                          
*********************************************************************/  


 

必须的表

 


 






 

 

 






 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值