二 console 设备驱动

来源:互联网 发布:淘宝店铺图片尺寸要求 编辑:程序博客网 时间:2024/06/08 11:35

一.结构体

1.console

struct console {charname[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方法shortflags;shortindex;intcflag;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_TTYnew_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_driversif (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_CONSOLEif (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;}#endifif (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_consoleif (!(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_CONSOLEif (console->flags & CON_BRL)return braille_unregister_console(console);#endifacquire_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_VTvty_init(&console_fops);#endifreturn 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-driverif (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对应的consolecontinue;driver = c->device(c, index);if (driver)break;}release_console_sem();return driver;}






 

原创粉丝点击