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 + -
显示快捷键?