Commit bc44ddca authored by Anton Blanchard's avatar Anton Blanchard

add pSeries hypervisor console

parent 8af480ec
...@@ -1153,3 +1153,7 @@ CONFIG_977_WATCHDOG ...@@ -1153,3 +1153,7 @@ CONFIG_977_WATCHDOG
Not sure? It's safe to say N. Not sure? It's safe to say N.
CONFIG_HVC_CONSOLE
pSeries machines when partitioned support a hypervisor virtual
console. This driver allows each pSeries partition to have a console
which is accessed via the HMC.
...@@ -104,6 +104,9 @@ if [ "$CONFIG_PARPORT" != "n" ]; then ...@@ -104,6 +104,9 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
fi fi
dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT dep_tristate 'Support for user-space parallel port device drivers' CONFIG_PPDEV $CONFIG_PARPORT
fi fi
if [ "$CONFIG_PPC_PSERIES" = "y" ]; then
bool 'pSeries Hypervisor Virtual Console support' CONFIG_HVC_CONSOLE
fi
source drivers/i2c/Config.in source drivers/i2c/Config.in
......
...@@ -164,6 +164,7 @@ obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o ...@@ -164,6 +164,7 @@ obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o
obj-$(CONFIG_HVC_CONSOLE) += hvc_console.o
subdir-$(CONFIG_RIO) += rio subdir-$(CONFIG_RIO) += rio
......
/*
* Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
* Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM
*
* 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/init.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/major.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/sched.h>
#include <linux/kbd_kern.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
extern int hvc_count(int *);
extern int hvc_get_chars(int index, char *buf, int count);
extern int hvc_put_chars(int index, const char *buf, int count);
#define HVC_MAJOR 229
#define HVC_MINOR 0
#define MAX_NR_HVC_CONSOLES 4
#define TIMEOUT ((HZ + 99) / 100)
struct tty_driver hvc_driver;
static int hvc_refcount;
static struct tty_struct *hvc_table[MAX_NR_HVC_CONSOLES];
static struct termios *hvc_termios[MAX_NR_HVC_CONSOLES];
static struct termios *hvc_termios_locked[MAX_NR_HVC_CONSOLES];
static int hvc_offset;
#define N_OUTBUF 16
#define __ALIGNED__ __attribute__((__aligned__(8)))
struct hvc_struct {
spinlock_t lock;
int index;
struct tty_struct *tty;
unsigned int count;
int do_wakeup;
char outbuf[N_OUTBUF] __ALIGNED__;
int n_outbuf;
};
struct hvc_struct hvc_struct[MAX_NR_HVC_CONSOLES];
static int hvc_open(struct tty_struct *tty, struct file * filp)
{
int line = minor(tty->device) - tty->driver.minor_start;
struct hvc_struct *hp;
if (line < 0 || line >= MAX_NR_HVC_CONSOLES)
return -ENODEV;
hp = &hvc_struct[line];
tty->driver_data = hp;
spin_lock(&hp->lock);
hp->tty = tty;
hp->count++;
spin_unlock(&hp->lock);
return 0;
}
static void hvc_close(struct tty_struct *tty, struct file * filp)
{
struct hvc_struct *hp = tty->driver_data;
if (tty_hung_up_p(filp))
return;
spin_lock(&hp->lock);
if (--hp->count == 0)
hp->tty = NULL;
else if (hp->count < 0)
printk(KERN_ERR "hvc_close %lu: oops, count is %d\n",
hp - hvc_struct, hp->count);
spin_unlock(&hp->lock);
}
/* called with hp->lock held */
static void hvc_push(struct hvc_struct *hp)
{
int n;
n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf);
if (n <= 0) {
if (n == 0)
return;
/* throw away output on error; this happens when
there is no session connected to the vterm. */
hp->n_outbuf = 0;
} else
hp->n_outbuf -= n;
if (hp->n_outbuf > 0)
memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);
else
hp->do_wakeup = 1;
}
static int hvc_write(struct tty_struct *tty, int from_user,
const unsigned char *buf, int count)
{
struct hvc_struct *hp = tty->driver_data;
char *p;
int todo, written = 0;
spin_lock(&hp->lock);
while (count > 0 && (todo = N_OUTBUF - hp->n_outbuf) > 0) {
if (todo > count)
todo = count;
p = hp->outbuf + hp->n_outbuf;
if (from_user) {
todo -= copy_from_user(p, buf, todo);
if (todo == 0) {
if (written == 0)
written = -EFAULT;
break;
}
} else
memcpy(p, buf, todo);
count -= todo;
buf += todo;
hp->n_outbuf += todo;
written += todo;
hvc_push(hp);
}
spin_unlock(&hp->lock);
return written;
}
static int hvc_write_room(struct tty_struct *tty)
{
struct hvc_struct *hp = tty->driver_data;
return N_OUTBUF - hp->n_outbuf;
}
static int hvc_chars_in_buffer(struct tty_struct *tty)
{
struct hvc_struct *hp = tty->driver_data;
return hp->n_outbuf;
}
static void hvc_poll(int index)
{
struct hvc_struct *hp = &hvc_struct[index];
struct tty_struct *tty;
int i, n;
char buf[16] __ALIGNED__;
spin_lock(&hp->lock);
if (hp->n_outbuf > 0)
hvc_push(hp);
tty = hp->tty;
if (tty) {
for (;;) {
if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf))
break;
n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf));
if (n <= 0)
break;
for (i = 0; i < n; ++i)
tty_insert_flip_char(tty, buf[i], 0);
}
if (tty->flip.count)
tty_schedule_flip(tty);
if (hp->do_wakeup) {
hp->do_wakeup = 0;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
&& tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
}
}
spin_unlock(&hp->lock);
}
int khvcd(void *unused)
{
int i;
daemonize();
reparent_to_init();
strcpy(current->comm, "khvcd");
sigfillset(&current->blocked);
for (;;) {
for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i)
hvc_poll(i);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(TIMEOUT);
}
}
int __init hvc_init(void)
{
int i;
memset(&hvc_driver, 0, sizeof(struct tty_driver));
hvc_driver.magic = TTY_DRIVER_MAGIC;
hvc_driver.driver_name = "hvc";
hvc_driver.name = "hvc/%d";
hvc_driver.major = HVC_MAJOR;
hvc_driver.minor_start = HVC_MINOR;
hvc_driver.num = hvc_count(&hvc_offset);
if (hvc_driver.num > MAX_NR_HVC_CONSOLES)
hvc_driver.num = MAX_NR_HVC_CONSOLES;
hvc_driver.type = TTY_DRIVER_TYPE_SYSTEM;
hvc_driver.init_termios = tty_std_termios;
hvc_driver.flags = TTY_DRIVER_REAL_RAW;
hvc_driver.refcount = &hvc_refcount;
hvc_driver.table = hvc_table;
hvc_driver.termios = hvc_termios;
hvc_driver.termios_locked = hvc_termios_locked;
hvc_driver.open = hvc_open;
hvc_driver.close = hvc_close;
hvc_driver.write = hvc_write;
hvc_driver.write_room = hvc_write_room;
hvc_driver.chars_in_buffer = hvc_chars_in_buffer;
for (i = 0; i < hvc_driver.num; i++) {
hvc_struct[i].lock = SPIN_LOCK_UNLOCKED;
hvc_struct[i].index = i;
tty_register_devfs(&hvc_driver, 0, hvc_driver.minor_start + i);
}
if (tty_register_driver(&hvc_driver))
panic("Couldn't register hvc console driver\n");
if (hvc_driver.num > 0)
kernel_thread(khvcd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
return 0;
}
static void __exit hvc_exit(void)
{
}
void hvc_console_print(struct console *co, const char *b, unsigned count)
{
char c[16] __ALIGNED__;
unsigned i, n;
int r, donecr = 0;
i = n = 0;
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 = hvc_put_chars(co->index + hvc_offset, 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 kdev_t hvc_console_device(struct console *c)
{
return mk_kdev(HVC_MAJOR, HVC_MINOR + c->index);
}
static int __init hvc_console_setup(struct console *co, char *options)
{
if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES
|| co->index >= hvc_count(&hvc_offset))
return -1;
return 0;
}
struct console hvc_con_driver = {
name: "hvc",
write: hvc_console_print,
device: hvc_console_device,
setup: hvc_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
int __init hvc_console_init(void)
{
register_console(&hvc_con_driver);
return 0;
}
module_init(hvc_init);
module_exit(hvc_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment