--- linux-2.5/include/asm-ppc64/hvconsole.h 2004-03-01 10:43:39.000000000 -0600 +++ linuxppc64-2.5/include/asm-ppc64/hvconsole.h 2004-04-12 15:17:03.000000000 -0500 @@ -22,9 +22,20 @@ #ifndef _PPC64_HVCONSOLE_H #define _PPC64_HVCONSOLE_H -extern int hvc_get_chars(int index, char *buf, int count); -extern int hvc_put_chars(int index, const char *buf, int count); -extern int hvc_count(int *start_termno); +#define MAX_NR_HVC_CONSOLES 4 + +extern int hvc_arch_get_chars(int index, char *buf, int count); +extern int hvc_arch_put_chars(int index, const char *buf, int count); +extern int hvc_arch_tiocmset(int index, unsigned int set, unsigned int clear); +extern int hvc_arch_tiocmget(int index); +extern int hvc_arch_find_vterms(void); + +extern int hvc_instantiate(void); + +/* hvterm_get/put_chars() do not work with HVSI console protocol; present only + * for HVCS console server driver */ +extern int hvterm_get_chars(uint32_t vtermno, char *buf, int count); +extern int hvterm_put_chars(uint32_t vtermno, const char *buf, int count); #endif /* _PPC64_HVCONSOLE_H */ --- linux-2.5/drivers/char/hvc_console.c 2004-03-18 16:05:31.000000000 -0600 +++ linuxppc64-2.5/drivers/char/hvc_console.c 2004-04-12 09:40:04.000000000 -0500 @@ -27,23 +27,18 @@ #include #include #include -#include #include #include - -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); +#include +#include #define HVC_MAJOR 229 #define HVC_MINOR 0 -#define MAX_NR_HVC_CONSOLES 4 - #define TIMEOUT ((HZ + 99) / 100) static struct tty_driver *hvc_driver; -static int hvc_offset; +static int count; #ifdef CONFIG_MAGIC_SYSRQ static int sysrq_pressed; #endif @@ -52,6 +47,10 @@ #define __ALIGNED__ __attribute__((__aligned__(8))) +/* This driver speaks only in "indexes", i.e. logical consoles starting at 0. + * The ppc64 backend converts those indexes (e.g. hvc0) to whatever the + * ultimate "vterm number" that the platform understands. */ + struct hvc_struct { spinlock_t lock; int index; @@ -112,7 +111,7 @@ { int n; - n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf); + n = hvc_arch_put_chars(hp->index, hp->outbuf, hp->n_outbuf); if (n <= 0) { if (n == 0) return; @@ -226,9 +225,14 @@ for (;;) { if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf)) break; - n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf)); - if (n <= 0) + n = hvc_arch_get_chars(index, buf, sizeof(buf)); + if (n <= 0) { + if (n == -EPIPE) { + pr_debug("tty_hangup\n"); + tty_hangup(tty); + } break; + } for (i = 0; i < n; ++i) { #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (buf[i] == '\x0f') { /* ^O -- should support a sequence */ @@ -281,6 +285,30 @@ } } +static int hvc_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct hvc_struct *hp = tty->driver_data; + int ret = -EIO; + + if (!file || !tty_hung_up_p(file)) { + ret = hvc_arch_tiocmget(hp->index); + } + return ret; +} + +static int hvc_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct hvc_struct *hp = tty->driver_data; + int ret = -EIO; + + if (!file || !tty_hung_up_p(file)) { + ret = hvc_arch_tiocmset(hp->index, set, clear); + } + + return ret; +} + static struct tty_operations hvc_ops = { .open = hvc_open, .close = hvc_close, @@ -288,17 +316,13 @@ .hangup = hvc_hangup, .write_room = hvc_write_room, .chars_in_buffer = hvc_chars_in_buffer, + .tiocmget = hvc_tiocmget, + .tiocmset = hvc_tiocmset, }; int __init hvc_init(void) { - int num = hvc_count(&hvc_offset); - int i; - - if (num > MAX_NR_HVC_CONSOLES) - num = MAX_NR_HVC_CONSOLES; - - hvc_driver = alloc_tty_driver(num); + hvc_driver = alloc_tty_driver(count); if (!hvc_driver) return -ENOMEM; @@ -312,23 +336,20 @@ hvc_driver->init_termios = tty_std_termios; hvc_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(hvc_driver, &hvc_ops); - for (i = 0; i < num; i++) { - hvc_struct[i].lock = SPIN_LOCK_UNLOCKED; - hvc_struct[i].index = i; - } if (tty_register_driver(hvc_driver)) panic("Couldn't register hvc console driver\n"); - if (num > 0) + if (count > 0) kernel_thread(khvcd, NULL, CLONE_KERNEL); + else + printk(KERN_WARNING "no virtual consoles found\n"); return 0; } +device_initcall(hvc_init); -static void __exit hvc_exit(void) -{ -} +/***** console (not tty) code: *****/ void hvc_console_print(struct console *co, const char *b, unsigned count) { @@ -348,7 +369,7 @@ --count; } } else { - r = hvc_put_chars(co->index + hvc_offset, c, i); + r = hvc_arch_put_chars(co->index, c, i); if (r < 0) { /* throw away chars on error */ i = 0; @@ -370,7 +391,7 @@ 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)) + || co->index >= count) return -1; return 0; } @@ -384,12 +405,27 @@ .index = -1, }; +/* hvc_instantiate - called once per discovered vterm by hvc_arch_find_vterms */ +int hvc_instantiate(void) +{ + struct hvc_struct *hvc; + + if (count >= MAX_NR_HVC_CONSOLES) + return -1; + + hvc = &hvc_struct[count]; + hvc->lock = SPIN_LOCK_UNLOCKED; + hvc->index = count; + + count++; + + return 0; +} + static int __init hvc_console_init(void) { + hvc_arch_find_vterms(); /* populate hvc_struct[] early */ register_console(&hvc_con_driver); return 0; } console_initcall(hvc_console_init); - -module_init(hvc_init); -module_exit(hvc_exit); --- linux-2.5/arch/ppc64/kernel/hvconsole.c 2004-04-08 11:12:47.000000000 -0500 +++ linuxppc64-2.5/arch/ppc64/kernel/hvconsole.c 2004-04-12 14:34:05.000000000 -0500 @@ -19,16 +19,226 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* TODO: + * use #defines instead of "16" "12" etc + */ + #include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include + +#define __ALIGNED__ __attribute__((__aligned__(8))) + +struct vtty_struct { + uint32_t vtermno; + int (*get_chars)(struct vtty_struct *vtty, char *buf, int count); + int (*put_chars)(struct vtty_struct *vtty, const char *buf, int count); + int (*tiocmget)(struct vtty_struct *vtty); + int (*tiocmset)(struct vtty_struct *vtty, uint16_t set, uint16_t clear); + uint16_t seqno; /* HVSI packet sequence number */ + uint16_t mctrl; +}; +static struct vtty_struct vttys[MAX_NR_HVC_CONSOLES]; + +#define WAIT_LOOPS 10000 +#define WAIT_USECS 100 + +#define HVSI_VERSION 1 + +#define VS_DATA_PACKET_HEADER 0xff +#define VS_CONTROL_PACKET_HEADER 0xfe +#define VS_QUERY_PACKET_HEADER 0xfd +#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc + +/* control verbs */ +#define VSV_SET_MODEM_CTL 1 /* to service processor only */ +#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ +#define VSV_CLOSE_PROTOCOL 3 + +/* query verbs */ +#define VSV_SEND_VERSION_NUMBER 1 +#define VSV_SEND_MODEM_CTL_STATUS 2 + +/* yes, these masks are not consecutive. */ +#define HVSI_TSDTR 0x1 +#define HVSI_TSCD 0x20 + +struct hvsi_header { + uint8_t type; + uint8_t len; + uint16_t seqno; +} __attribute__((packed)); + +struct hvsi_control { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; + /* optional depending on verb: */ + uint32_t word; + uint32_t mask; +} __attribute__((packed)); + +struct hvsi_query { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; +} __attribute__((packed)); + +struct hvsi_query_response { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; + uint16_t query_seqno; + union { + uint8_t version; + uint32_t mctrl_word; + } u; +} __attribute__((packed)); -int hvc_get_chars(int index, char *buf, int count) +/* ring buffer stuff: */ +struct packet_desc { + union { + struct hvsi_header hdr; + char pkt[256]; /* console_initcall is pre-mem_init(), so no kmalloc */ + } data; + unsigned int want; + unsigned int got; +}; +#define N_PACKETS 4 +static struct packet_desc ring[N_PACKETS]; +static struct packet_desc *write=ring; /* next packet to write to */ +static struct packet_desc *read=ring; /* next packet to read from */ + +static struct packet_desc *next_desc(struct packet_desc *cur) +{ + if ((cur+1) > ring + (N_PACKETS-1)) + return ring; + return (cur+1); +} + +static int desc_hdr_done(struct packet_desc *desc) +{ + if (desc->got < sizeof(struct hvsi_header)) + return 0; + return 1; +} + +static unsigned int desc_want(struct packet_desc *desc) +{ + if (desc_hdr_done(desc)) + return desc->data.hdr.len; + else + return UINT_MAX; +} + +static int desc_done(struct packet_desc *desc) +{ + if (!desc_hdr_done(desc) || (desc->got < desc->want)) + return 0; + return 1; +} + +static int desc_overflow(struct packet_desc *desc) +{ + int overflow = desc->got - desc->want; + if (desc_hdr_done(desc) && (overflow > 0)) + return overflow; + return 0; +} + +static void desc_clear(struct packet_desc *desc) +{ + desc->got = desc->want = 0; +} + +/* these only work on well-formed and complete packets */ + +static inline int hdrlen(const struct hvsi_header *pkt) +{ + const int lengths[] = { 4, 6, 6, 8, }; + int index = VS_DATA_PACKET_HEADER - pkt->type; + + return lengths[index]; +} + +static inline uint8_t *payload(const struct hvsi_header *pkt) +{ + return (uint8_t *)pkt + hdrlen(pkt); +} + +static inline int len_packet(const struct hvsi_header *pkt) +{ + return (int)pkt->len; +} + +static inline int len_payload(const struct hvsi_header *pkt) +{ + return len_packet(pkt) - hdrlen(pkt); +} + +static void dump_packet(struct hvsi_header *pkt) +{ + int i; + char *data = payload(pkt); + + printk("type 0x%x, len %i, seqno %i:", pkt->type, pkt->len, pkt->seqno); + + if (len_payload(pkt)) + printk("\n "); + for (i=0; i < len_payload(pkt); i++) + printk("%.2x", data[i]); + + if (len_payload(pkt)) + printk("\n "); + for (i=0; i < len_payload(pkt); i++) { + if (isprint(data[i])) + printk(" %c", data[i]); + else + printk(".."); + } + printk("\n"); +} + +#ifdef DEBUG +static void dump_ring(void) +{ + int i; + for (i=0; i < N_PACKETS; i++) { + struct packet_desc *desc = &ring[i]; + if (read == desc) + printk("r"); + else + printk(" "); + if (write == desc) + printk("w"); + else + printk(" "); + printk(" "); + printk("desc %i: want %i got %i\n", i, desc->want, desc->got); + printk(" "); + dump_packet(&desc->data.hdr); + } +} +#endif /* DEBUG */ + +/* normal hypervisor virtual console code */ +int hvterm_get_chars(uint32_t vtermno, char *buf, int count) { unsigned long got; - if (plpar_hcall(H_GET_TERM_CHAR, index, 0, 0, 0, &got, + if (plpar_hcall(H_GET_TERM_CHAR, vtermno, 0, 0, 0, &got, (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) { /* * Work around a HV bug where it gives us a null @@ -49,40 +259,498 @@ } return 0; } +EXPORT_SYMBOL(hvterm_get_chars); + +/* wrapper exists just so that hvterm_get_chars() is callable by outside + * drivers without a vtty_struct */ +int hvc_hvterm_get_chars(struct vtty_struct *vtty, char *buf, int count) +{ + return hvterm_get_chars(vtty->vtermno, buf, count); +} -int hvc_put_chars(int index, const char *buf, int count) +int hvterm_put_chars(uint32_t vtermno, const char *buf, int count) { unsigned long *lbuf = (unsigned long *) buf; long ret; - ret = plpar_hcall_norets(H_PUT_TERM_CHAR, index, count, lbuf[0], + ret = plpar_hcall_norets(H_PUT_TERM_CHAR, vtermno, count, lbuf[0], lbuf[1]); if (ret == H_Success) return count; if (ret == H_Busy) return 0; - return -1; + return -EIO; +} +EXPORT_SYMBOL(hvterm_put_chars); + +/* wrapper exists just so that hvterm_put_chars() is callable by outside + * drivers without a vtty_struct */ +int hvc_hvterm_put_chars(struct vtty_struct *vtty, const char *buf, int count) +{ + return hvterm_put_chars(vtty->vtermno, buf, count); +} + +/* Host Virtual Serial Interface (HVSI) code */ + +static int hvsi_read(struct vtty_struct *vtty, char *buf, int count) +{ + unsigned long got; + + if (plpar_hcall(H_GET_TERM_CHAR, vtty->vtermno, 0, 0, 0, &got, + (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) { + return got; + } + return 0; +} + +/* like memcpy, but only copy at most a single packet from the src bytestream */ +static int copy_packet(uint8_t *dest, uint8_t *src, uint8_t len) +{ + int copylen; + + if (len == 1) { + /* we don't have the len header */ + *dest = *src; + return 1; + } + + /* if we have more than one packet here, only copy the first */ + copylen = min(len_packet((struct hvsi_header *)src), (int)len); + memcpy(dest, src, copylen); + return copylen; +} + +/* load up ring buffers */ +static int hvsi_load_chunk(struct vtty_struct *vtty) +{ + struct packet_desc *old = write; + unsigned int chunklen; + unsigned int overflow; + + /* copy up to 16 bytes into the write buffer */ + chunklen = hvsi_read(vtty, write->data.pkt + write->got, 16); + if (!chunklen) + return 0; + write->got += chunklen; + write->want = desc_want(write); + + overflow = desc_overflow(write); + while (overflow) { + /* copied too much into 'write'; memcpy it into the next buffers */ + int nextlen; + write = next_desc(write); + + nextlen = copy_packet(write->data.pkt, old->data.pkt + old->want, + overflow); + write->got = nextlen; + write->want = desc_want(write); + overflow -= nextlen; + } + if (desc_done(write)) + write = next_desc(write); + return 1; +} + +/* keep reading from hypervisor until there's no more */ +static void hvsi_load_buffers(struct vtty_struct *vtty) +{ + /* XXX perhaps we should limit this */ + while (hvsi_load_chunk(vtty)) { + if (write == read) { + /* we've filled all our ring buffers; let the hypervisor queue + * the rest for us */ + break; + } + } +} + +static int hvsi_recv_control(struct vtty_struct *vtty, struct hvsi_control *pkt) +{ + int ret = 0; + + //dump_packet((struct hvsi_header *)pkt); + + switch (pkt->verb) { + case VSV_MODEM_CTL_UPDATE: + if ((pkt->word & HVSI_TSCD) == 0) { + /* CD went away; no more connection */ + vtty->mctrl &= TIOCM_CD; + ret = -EPIPE; + } + break; + case VSV_CLOSE_PROTOCOL: + /* XXX handle this by reopening on open/read/write() ? */ + panic("%s: service processor closed HVSI connection!\n", __FUNCTION__); + break; + default: + printk(KERN_WARNING "unknown HVSI control packet: "); + dump_packet((struct hvsi_header *)pkt); + break; + } + return ret; +} + +/* transfer from ring buffers to caller's buffer */ +static int hvsi_deliver(struct vtty_struct *vtty, uint8_t *buf, int buflen) +{ + int written = 0; + int ret; + + for (; (read != write) && (buflen > 0); read = next_desc(read)) { + struct hvsi_header *pkt = &read->data.hdr; + int size; + +#ifdef DEBUG + dump_ring(); +#endif + + switch (pkt->type) { + case VS_DATA_PACKET_HEADER: + size = min(len_payload(pkt), buflen); + memcpy(buf, payload(pkt), size); + buf += size; + buflen -= size; + written += size; + break; + case VS_CONTROL_PACKET_HEADER: + ret = hvsi_recv_control(vtty, (struct hvsi_control *)pkt); + /* if we got an error (like CD dropped), stop now. + * otherwise keep dispatching packets */ + if (ret < 0) { + desc_clear(read); + read = next_desc(read); + return ret; + } + break; + default: + printk(KERN_WARNING "unknown HVSI packet: "); + dump_packet(pkt); + break; + } + desc_clear(read); + } + + return written; +} + +static int hvsi_get_chars(struct vtty_struct *vtty, char *databuf, int count) +{ + hvsi_load_buffers(vtty); /* get pending data */ + return hvsi_deliver(vtty, databuf, count); /* hand it up */ +} + +static struct hvsi_header *search_for_packet(struct vtty_struct *vtty, int type) +{ + /* bring in queued packets */ + hvsi_load_buffers(vtty); + + /* look for the version query response packet */ + for (; read != write; read = next_desc(read)) { + struct hvsi_header *pkt = &read->data.hdr; + + if (pkt->type == type) { + desc_clear(read); + read = next_desc(read); + return pkt; + } + printk("%s: ignoring packet while waiting for type 0x%x:\n", + __FUNCTION__, type); + dump_packet(pkt); + } + + return NULL; +} + +static int wait_for_packet(struct vtty_struct *vtty, struct hvsi_header **hdr, + int type) +{ + struct hvsi_header *found; + int count = 0; + + do { + if (count++ > WAIT_LOOPS) + return -EIO; + udelay(WAIT_USECS); + found = search_for_packet(vtty, type); + } while (!found); + + *hdr = found; + return 0; +} + +static int hvsi_query(struct vtty_struct *vtty, uint16_t verb) +{ + struct hvsi_query query __ALIGNED__ = { + .type = VS_QUERY_PACKET_HEADER, + .len = sizeof(struct hvsi_query), + }; + int wrote; + + query.seqno = vtty->seqno++; + query.verb = verb; + wrote = hvc_hvterm_put_chars(vtty, (char *)&query, query.len); + if (wrote != query.len) { + printk(KERN_ERR "%s: couldn't send query!\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +/* respond to service processor's version query */ +static int hvsi_version_respond(struct vtty_struct *vtty, uint16_t query_seqno) +{ + struct hvsi_query_response response __ALIGNED__ = { + .type = VS_QUERY_RESPONSE_PACKET_HEADER, + .len = sizeof(struct hvsi_query_response), + .verb = VSV_SEND_VERSION_NUMBER, + .u.version = HVSI_VERSION, + }; + int wrote; + + response.seqno = vtty->seqno++; + response.query_seqno = query_seqno+1, + wrote = hvc_hvterm_put_chars(vtty, (char *)&response, response.len); + if (wrote != response.len) { + printk(KERN_ERR "%s: couldn't send query response!\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +static int hvsi_get_mctrl(struct vtty_struct *vtty) +{ + struct hvsi_header *hdr; + int ret = 0; + uint16_t mctrl; + + if (hvsi_query(vtty, VSV_SEND_MODEM_CTL_STATUS)) { + ret = -EIO; + goto out; + } + if (wait_for_packet(vtty, &hdr, VS_QUERY_RESPONSE_PACKET_HEADER)) { + ret = -EIO; + goto out; + } + /* XXX see if it's the right response */ + + vtty->mctrl = 0; + + mctrl = ((struct hvsi_query_response *)hdr)->u.mctrl_word; + if (mctrl & HVSI_TSDTR) + vtty->mctrl |= TIOCM_DTR; + if (mctrl & HVSI_TSCD) + vtty->mctrl |= TIOCM_CD; + pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, vtty->mctrl); + +out: + return ret; +} + +static int hvsi_handshake(struct vtty_struct *vtty) +{ + struct hvsi_header *hdr; + int ret = 0; + + if (hvsi_query(vtty, VSV_SEND_VERSION_NUMBER)) { + ret = -EIO; + goto out; + } + if (wait_for_packet(vtty, &hdr, VS_QUERY_RESPONSE_PACKET_HEADER)) { + ret = -EIO; + goto out; + } + /* XXX see if it's the right response */ + + if (wait_for_packet(vtty, &hdr, VS_QUERY_PACKET_HEADER)) { + ret = -EIO; + goto out; + } + /* XXX see if it's the right query */ + if (hvsi_version_respond(vtty, hdr->seqno)) { + ret = -EIO; + goto out; + } + + if (hvsi_get_mctrl(vtty)) { + ret = -EIO; + goto out; + } + +out: + if (ret < 0) + printk(KERN_ERR "HVSI handshaking failed\n"); + return ret; +} + +static int hvsi_put_chars(struct vtty_struct *vtty, const char *buf, int count) +{ + char packet[16] __ALIGNED__; + uint64_t *lbuf = (uint64_t *)packet; + struct hvsi_header *hdr = (struct hvsi_header *)packet; + int ret; + + hdr->type = VS_DATA_PACKET_HEADER; + hdr->seqno = vtty->seqno++; + + if (count > 12) + count = 12; /* we'll leave some chars behind in buf */ + hdr->len = count + sizeof(struct hvsi_header); + memcpy(packet + sizeof(struct hvsi_header), buf, count); + + /* note: we can't use hvc_hvterm_put_chars() here, as it would return + * _packet_ length, not _payload_ length */ + ret = plpar_hcall_norets(H_PUT_TERM_CHAR, vtty->vtermno, hdr->len, + lbuf[0], lbuf[1]); + if (ret == H_Success) + return count; + if (ret == H_Busy) + return 0; + return -EIO; +} + +/* note that we can only set DTR */ +static int hvsi_set_mctrl(struct vtty_struct *vtty, uint16_t mctrl) +{ + struct hvsi_control command __ALIGNED__ = { + .type = VS_CONTROL_PACKET_HEADER, + .len = sizeof(struct hvsi_control), + .verb = VSV_SET_MODEM_CTL, + .mask = HVSI_TSDTR, + }; + int wrote; + + command.seqno = vtty->seqno++; + if (mctrl & TIOCM_DTR) + command.word = HVSI_TSDTR; + + wrote = hvc_hvterm_put_chars(vtty, (char *)&command, command.len); + if (wrote != command.len) { + printk(KERN_ERR "%s: couldn't set DTR!\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +static int hvsi_tiocmset(struct vtty_struct *vtty, uint16_t set, uint16_t clear) +{ + uint16_t old_mctrl; + + /* we can only set DTR */ + if (set & ~TIOCM_DTR) + return -EINVAL; + + old_mctrl = vtty->mctrl; + vtty->mctrl = (old_mctrl & ~clear) | set; + + pr_debug("%s: new mctrl 0x%x\n", __FUNCTION__, vtty->mctrl); + if (old_mctrl != vtty->mctrl) { + if (hvsi_set_mctrl(vtty, vtty->mctrl) < 0) + return -EIO; + } else { + pr_debug(" (not writing to SP)\n"); + } + + return 0; +} + +static int hvsi_tiocmget(struct vtty_struct *vtty) +{ + if (hvsi_get_mctrl(vtty)) + return -EIO; + pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, vtty->mctrl); + return vtty->mctrl; +} + +/* external (hvc_console.c) interface: */ + +int hvc_arch_get_chars(int index, char *buf, int count) +{ + struct vtty_struct *vtty = &vttys[index]; + + if (index >= MAX_NR_HVC_CONSOLES) + return -ENODEV; + + return vtty->get_chars(vtty, buf, count); } -/* return the number of client vterms present */ -/* XXX this requires an interface change to handle multiple discontiguous - * vterms */ -int hvc_count(int *start_termno) +int hvc_arch_put_chars(int index, const char *buf, int count) +{ + struct vtty_struct *vtty = &vttys[index]; + + if (index >= MAX_NR_HVC_CONSOLES) + return -ENODEV; + + return vtty->put_chars(vtty, buf, count); +} + +int hvc_arch_tiocmset(int index, unsigned int set, unsigned int clear) +{ + struct vtty_struct *vtty = &vttys[index]; + + if (index >= MAX_NR_HVC_CONSOLES) + return -ENODEV; + + if (vtty->tiocmset) + return vtty->tiocmset(vtty, set, clear); + return -EINVAL; +} + +int hvc_arch_tiocmget(int index) +{ + struct vtty_struct *vtty = &vttys[index]; + + if (index >= MAX_NR_HVC_CONSOLES) + return -ENODEV; + + if (vtty->tiocmset) + return vtty->tiocmget(vtty); + return -EINVAL; +} + +int hvc_arch_find_vterms(void) { struct device_node *vty; - int num_found = 0; + int count = 0; + + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + struct vtty_struct *vtty; + uint32_t *vtermno; - /* consider only the first vty node. - * we should _always_ be able to find one. */ - vty = of_find_node_by_name(NULL, "vty"); - if (vty && device_is_compatible(vty, "hvterm1")) { - u32 *termno = (u32 *)get_property(vty, "reg", 0); + vtermno = (uint32_t *)get_property(vty, "reg", NULL); + if (!vtermno) + continue; - if (termno && start_termno) - *start_termno = *termno; - num_found = 1; - of_node_put(vty); + if (count >= MAX_NR_HVC_CONSOLES) + break; + + vtty = &vttys[count]; + if (device_is_compatible(vty, "hvterm1")) { + vtty->vtermno = *vtermno; + vtty->get_chars = hvc_hvterm_get_chars; + vtty->put_chars = hvc_hvterm_put_chars; + vtty->tiocmget = NULL; + vtty->tiocmset = NULL; + hvc_instantiate(); + count++; + } else if (device_is_compatible(vty, "hvterm-protocol")) { + vtty->vtermno = *vtermno; + vtty->seqno = 0; + vtty->get_chars = hvsi_get_chars; + vtty->put_chars = hvsi_put_chars; + vtty->tiocmget = hvsi_tiocmget; + vtty->tiocmset = hvsi_tiocmset; + if (hvsi_handshake(vtty)) { + continue; + } + vtty->put_chars(vtty, "\nHVSI\n", 6); + hvc_instantiate(); + count++; + } } - return num_found; + return count; }