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






 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值