hvc_console.c

来自「linux 内核源代码」· C语言 代码 · 共 908 行 · 第 1/2 页

C
908
字号
/* * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM * Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM * Copyright (C) 2004 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. * Copyright (C) 2004 IBM Corporation * * Additional Author(s): *  Ryan S. Arnold <rsa@us.ibm.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *  * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. *  * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */#include <linux/console.h>#include <linux/cpumask.h>#include <linux/init.h>#include <linux/kbd_kern.h>#include <linux/kernel.h>#include <linux/kobject.h>#include <linux/kthread.h>#include <linux/list.h>#include <linux/module.h>#include <linux/major.h>#include <linux/sysrq.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <linux/freezer.h>#include <asm/uaccess.h>#include "hvc_console.h"#define HVC_MAJOR	229#define HVC_MINOR	0/* * Wait this long per iteration while trying to push buffered data to the * hypervisor before allowing the tty to complete a close operation. */#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second *//* * These sizes are most efficient for vio, because they are the * native transfer size. We could make them selectable in the * future to better deal with backends that want other buffer sizes. */#define N_OUTBUF	16#define N_INBUF		16#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))static struct tty_driver *hvc_driver;static struct task_struct *hvc_task;/* Picks up late kicks after list walk but before schedule() */static int hvc_kicked;static int hvc_init(void);#ifdef CONFIG_MAGIC_SYSRQstatic int sysrq_pressed;#endifstruct hvc_struct {	spinlock_t lock;	int index;	struct tty_struct *tty;	unsigned int count;	int do_wakeup;	char *outbuf;	int outbuf_size;	int n_outbuf;	uint32_t vtermno;	struct hv_ops *ops;	int irq_requested;	int irq;	struct list_head next;	struct kobject kobj; /* ref count & hvc_struct lifetime */};/* dynamic list of hvc_struct instances */static struct list_head hvc_structs = LIST_HEAD_INIT(hvc_structs);/* * Protect the list of hvc_struct instances from inserts and removals during * list traversal. */static DEFINE_SPINLOCK(hvc_structs_lock);/* * This value is used to assign a tty->index value to a hvc_struct based * upon order of exposure via hvc_probe(), when we can not match it to * a console candidate registered with hvc_instantiate(). */static int last_hvc = -1;/* * Do not call this function with either the hvc_structs_lock or the hvc_struct * lock held.  If successful, this function increments the kobject reference * count against the target hvc_struct so it should be released when finished. */static struct hvc_struct *hvc_get_by_index(int index){	struct hvc_struct *hp;	unsigned long flags;	spin_lock(&hvc_structs_lock);	list_for_each_entry(hp, &hvc_structs, next) {		spin_lock_irqsave(&hp->lock, flags);		if (hp->index == index) {			kobject_get(&hp->kobj);			spin_unlock_irqrestore(&hp->lock, flags);			spin_unlock(&hvc_structs_lock);			return hp;		}		spin_unlock_irqrestore(&hp->lock, flags);	}	hp = NULL;	spin_unlock(&hvc_structs_lock);	return hp;}/* * Initial console vtermnos for console API usage prior to full console * initialization.  Any vty adapter outside this range will not have usable * console interfaces but can still be used as a tty device.  This has to be * static because kmalloc will not work during early console init. */static struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES];static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =	{[0 ... MAX_NR_HVC_CONSOLES - 1] = -1};/* * Console APIs, NOT TTY.  These APIs are available immediately when * hvc_console_setup() finds adapters. */static void hvc_console_print(struct console *co, const char *b,			      unsigned count){	char c[N_OUTBUF] __ALIGNED__;	unsigned i = 0, n = 0;	int r, donecr = 0, index = co->index;	/* Console access attempt outside of acceptable console range. */	if (index >= MAX_NR_HVC_CONSOLES)		return;	/* This console adapter was removed so it is not usable. */	if (vtermnos[index] < 0)		return;	while (count > 0 || i > 0) {		if (count > 0 && i < sizeof(c)) {			if (b[n] == '\n' && !donecr) {				c[i++] = '\r';				donecr = 1;			} else {				c[i++] = b[n++];				donecr = 0;				--count;			}		} else {			r = cons_ops[index]->put_chars(vtermnos[index], c, i);			if (r < 0) {				/* throw away chars on error */				i = 0;			} else if (r > 0) {				i -= r;				if (i > 0)					memmove(c, c+r, i);			}		}	}}static struct tty_driver *hvc_console_device(struct console *c, int *index){	if (vtermnos[c->index] == -1)		return NULL;	*index = c->index;	return hvc_driver;}static int __init hvc_console_setup(struct console *co, char *options){	if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES)		return -ENODEV;	if (vtermnos[co->index] == -1)		return -ENODEV;	return 0;}static struct console hvc_con_driver = {	.name		= "hvc",	.write		= hvc_console_print,	.device		= hvc_console_device,	.setup		= hvc_console_setup,	.flags		= CON_PRINTBUFFER,	.index		= -1,};/* * Early console initialization.  Precedes driver initialization. * * (1) we are first, and the user specified another driver * -- index will remain -1 * (2) we are first and the user specified no driver * -- index will be set to 0, then we will fail setup. * (3)  we are first and the user specified our driver * -- index will be set to user specified driver, and we will fail * (4) we are after driver, and this initcall will register us * -- if the user didn't specify a driver then the console will match * * Note that for cases 2 and 3, we will match later when the io driver * calls hvc_instantiate() and call register again. */static int __init hvc_console_init(void){	register_console(&hvc_con_driver);	return 0;}console_initcall(hvc_console_init);/* * hvc_instantiate() is an early console discovery method which locates * consoles * prior to the vio subsystem discovering them.  Hotplugged * vty adapters do NOT get an hvc_instantiate() callback since they * appear after early console init. */int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops){	struct hvc_struct *hp;	if (index < 0 || index >= MAX_NR_HVC_CONSOLES)		return -1;	if (vtermnos[index] != -1)		return -1;	/* make sure no no tty has been registered in this index */	hp = hvc_get_by_index(index);	if (hp) {		kobject_put(&hp->kobj);		return -1;	}	vtermnos[index] = vtermno;	cons_ops[index] = ops;	/* reserve all indices up to and including this index */	if (last_hvc < index)		last_hvc = index;	/* if this index is what the user requested, then register	 * now (setup won't fail at this point).  It's ok to just	 * call register again if previously .setup failed.	 */	if (index == hvc_con_driver.index)		register_console(&hvc_con_driver);	return 0;}/* Wake the sleeping khvcd */static void hvc_kick(void){	hvc_kicked = 1;	wake_up_process(hvc_task);}static int hvc_poll(struct hvc_struct *hp);/* * NOTE: This API isn't used if the console adapter doesn't support interrupts. * In this case the console is poll driven. */static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance){	/* if hvc_poll request a repoll, then kick the hvcd thread */	if (hvc_poll(dev_instance))		hvc_kick();	return IRQ_HANDLED;}static void hvc_unthrottle(struct tty_struct *tty){	hvc_kick();}/* * The TTY interface won't be used until after the vio layer has exposed the vty * adapter to the kernel. */static int hvc_open(struct tty_struct *tty, struct file * filp){	struct hvc_struct *hp;	unsigned long flags;	int irq = 0;	int rc = 0;	struct kobject *kobjp;	/* Auto increments kobject reference if found. */	if (!(hp = hvc_get_by_index(tty->index)))		return -ENODEV;	spin_lock_irqsave(&hp->lock, flags);	/* Check and then increment for fast path open. */	if (hp->count++ > 0) {		spin_unlock_irqrestore(&hp->lock, flags);		hvc_kick();		return 0;	} /* else count == 0 */	tty->driver_data = hp;	tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */	hp->tty = tty;	/* Save for request_irq outside of spin_lock. */	irq = hp->irq;	if (irq)		hp->irq_requested = 1;	kobjp = &hp->kobj;	spin_unlock_irqrestore(&hp->lock, flags);	/* check error, fallback to non-irq */	if (irq)		rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp);	/*	 * If the request_irq() fails and we return an error.  The tty layer	 * will call hvc_close() after a failed open but we don't want to clean	 * up there so we'll clean up here and clear out the previously set	 * tty fields and return the kobject reference.	 */	if (rc) {		spin_lock_irqsave(&hp->lock, flags);		hp->tty = NULL;		hp->irq_requested = 0;		spin_unlock_irqrestore(&hp->lock, flags);		tty->driver_data = NULL;		kobject_put(kobjp);		printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);	}	/* Force wakeup of the polling thread */	hvc_kick();	return rc;}static void hvc_close(struct tty_struct *tty, struct file * filp){	struct hvc_struct *hp;	struct kobject *kobjp;	int irq = 0;	unsigned long flags;	if (tty_hung_up_p(filp))		return;	/*	 * No driver_data means that this close was issued after a failed	 * hvc_open by the tty layer's release_dev() function and we can just	 * exit cleanly because the kobject reference wasn't made.	 */	if (!tty->driver_data)		return;	hp = tty->driver_data;	spin_lock_irqsave(&hp->lock, flags);	kobjp = &hp->kobj;	if (--hp->count == 0) {		if (hp->irq_requested)			irq = hp->irq;		hp->irq_requested = 0;		/* We are done with the tty pointer now. */		hp->tty = NULL;		spin_unlock_irqrestore(&hp->lock, flags);		/*		 * Chain calls chars_in_buffer() and returns immediately if		 * there is no buffered data otherwise sleeps on a wait queue		 * waking periodically to check chars_in_buffer().		 */		tty_wait_until_sent(tty, HVC_CLOSE_WAIT);		if (irq)			free_irq(irq, hp);	} else {		if (hp->count < 0)			printk(KERN_ERR "hvc_close %X: oops, count is %d\n",				hp->vtermno, hp->count);		spin_unlock_irqrestore(&hp->lock, flags);	}	kobject_put(kobjp);}static void hvc_hangup(struct tty_struct *tty){	struct hvc_struct *hp = tty->driver_data;	unsigned long flags;	int irq = 0;	int temp_open_count;	struct kobject *kobjp;	if (!hp)		return;	spin_lock_irqsave(&hp->lock, flags);	/*	 * The N_TTY line discipline has problems such that in a close vs	 * open->hangup case this can be called after the final close so prevent	 * that from happening for now.	 */	if (hp->count <= 0) {		spin_unlock_irqrestore(&hp->lock, flags);		return;	}	kobjp = &hp->kobj;	temp_open_count = hp->count;	hp->count = 0;	hp->n_outbuf = 0;	hp->tty = NULL;	if (hp->irq_requested)		/* Saved for use outside of spin_lock. */		irq = hp->irq;	hp->irq_requested = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?