1728674a7SGreg Kroah-Hartman /* 2728674a7SGreg Kroah-Hartman * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM 3728674a7SGreg Kroah-Hartman * 4728674a7SGreg Kroah-Hartman * This program is free software; you can redistribute it and/or modify 5728674a7SGreg Kroah-Hartman * it under the terms of the GNU General Public License as published by 6728674a7SGreg Kroah-Hartman * the Free Software Foundation; either version 2 of the License, or 7728674a7SGreg Kroah-Hartman * (at your option) any later version. 8728674a7SGreg Kroah-Hartman * 9728674a7SGreg Kroah-Hartman * This program is distributed in the hope that it will be useful, 10728674a7SGreg Kroah-Hartman * but WITHOUT ANY WARRANTY; without even the implied warranty of 11728674a7SGreg Kroah-Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12728674a7SGreg Kroah-Hartman * GNU General Public License for more details. 13728674a7SGreg Kroah-Hartman * 14728674a7SGreg Kroah-Hartman * You should have received a copy of the GNU General Public License 15728674a7SGreg Kroah-Hartman * along with this program; if not, write to the Free Software 16728674a7SGreg Kroah-Hartman * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17728674a7SGreg Kroah-Hartman */ 18728674a7SGreg Kroah-Hartman 19728674a7SGreg Kroah-Hartman /* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS 20728674a7SGreg Kroah-Hartman * and the service processor on IBM pSeries servers. On these servers, there 21728674a7SGreg Kroah-Hartman * are no serial ports under the OS's control, and sometimes there is no other 22728674a7SGreg Kroah-Hartman * console available either. However, the service processor has two standard 23728674a7SGreg Kroah-Hartman * serial ports, so this over-complicated protocol allows the OS to control 24728674a7SGreg Kroah-Hartman * those ports by proxy. 25728674a7SGreg Kroah-Hartman * 26728674a7SGreg Kroah-Hartman * Besides data, the procotol supports the reading/writing of the serial 27728674a7SGreg Kroah-Hartman * port's DTR line, and the reading of the CD line. This is to allow the OS to 28728674a7SGreg Kroah-Hartman * control a modem attached to the service processor's serial port. Note that 29728674a7SGreg Kroah-Hartman * the OS cannot change the speed of the port through this protocol. 30728674a7SGreg Kroah-Hartman */ 31728674a7SGreg Kroah-Hartman 32728674a7SGreg Kroah-Hartman #undef DEBUG 33728674a7SGreg Kroah-Hartman 34728674a7SGreg Kroah-Hartman #include <linux/console.h> 35728674a7SGreg Kroah-Hartman #include <linux/ctype.h> 36728674a7SGreg Kroah-Hartman #include <linux/delay.h> 37728674a7SGreg Kroah-Hartman #include <linux/init.h> 38728674a7SGreg Kroah-Hartman #include <linux/interrupt.h> 39728674a7SGreg Kroah-Hartman #include <linux/module.h> 40728674a7SGreg Kroah-Hartman #include <linux/major.h> 41728674a7SGreg Kroah-Hartman #include <linux/kernel.h> 42728674a7SGreg Kroah-Hartman #include <linux/spinlock.h> 43728674a7SGreg Kroah-Hartman #include <linux/sysrq.h> 44728674a7SGreg Kroah-Hartman #include <linux/tty.h> 45728674a7SGreg Kroah-Hartman #include <linux/tty_flip.h> 46728674a7SGreg Kroah-Hartman #include <asm/hvcall.h> 47728674a7SGreg Kroah-Hartman #include <asm/hvconsole.h> 48728674a7SGreg Kroah-Hartman #include <asm/prom.h> 49728674a7SGreg Kroah-Hartman #include <asm/uaccess.h> 50728674a7SGreg Kroah-Hartman #include <asm/vio.h> 51728674a7SGreg Kroah-Hartman #include <asm/param.h> 52725e789fSBenjamin Herrenschmidt #include <asm/hvsi.h> 53728674a7SGreg Kroah-Hartman 54728674a7SGreg Kroah-Hartman #define HVSI_MAJOR 229 55728674a7SGreg Kroah-Hartman #define HVSI_MINOR 128 56728674a7SGreg Kroah-Hartman #define MAX_NR_HVSI_CONSOLES 4 57728674a7SGreg Kroah-Hartman 58728674a7SGreg Kroah-Hartman #define HVSI_TIMEOUT (5*HZ) 59728674a7SGreg Kroah-Hartman #define HVSI_VERSION 1 60728674a7SGreg Kroah-Hartman #define HVSI_MAX_PACKET 256 61728674a7SGreg Kroah-Hartman #define HVSI_MAX_READ 16 62728674a7SGreg Kroah-Hartman #define HVSI_MAX_OUTGOING_DATA 12 63728674a7SGreg Kroah-Hartman #define N_OUTBUF 12 64728674a7SGreg Kroah-Hartman 65728674a7SGreg Kroah-Hartman /* 66728674a7SGreg Kroah-Hartman * we pass data via two 8-byte registers, so we would like our char arrays 67728674a7SGreg Kroah-Hartman * properly aligned for those loads. 68728674a7SGreg Kroah-Hartman */ 69728674a7SGreg Kroah-Hartman #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) 70728674a7SGreg Kroah-Hartman 71728674a7SGreg Kroah-Hartman struct hvsi_struct { 72728674a7SGreg Kroah-Hartman struct delayed_work writer; 73728674a7SGreg Kroah-Hartman struct work_struct handshaker; 74728674a7SGreg Kroah-Hartman wait_queue_head_t emptyq; /* woken when outbuf is emptied */ 75728674a7SGreg Kroah-Hartman wait_queue_head_t stateq; /* woken when HVSI state changes */ 76728674a7SGreg Kroah-Hartman spinlock_t lock; 77728674a7SGreg Kroah-Hartman int index; 78728674a7SGreg Kroah-Hartman struct tty_struct *tty; 79728674a7SGreg Kroah-Hartman int count; 80728674a7SGreg Kroah-Hartman uint8_t throttle_buf[128]; 81728674a7SGreg Kroah-Hartman uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ 82728674a7SGreg Kroah-Hartman /* inbuf is for packet reassembly. leave a little room for leftovers. */ 83728674a7SGreg Kroah-Hartman uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; 84728674a7SGreg Kroah-Hartman uint8_t *inbuf_end; 85728674a7SGreg Kroah-Hartman int n_throttle; 86728674a7SGreg Kroah-Hartman int n_outbuf; 87728674a7SGreg Kroah-Hartman uint32_t vtermno; 88728674a7SGreg Kroah-Hartman uint32_t virq; 89728674a7SGreg Kroah-Hartman atomic_t seqno; /* HVSI packet sequence number */ 90728674a7SGreg Kroah-Hartman uint16_t mctrl; 91728674a7SGreg Kroah-Hartman uint8_t state; /* HVSI protocol state */ 92728674a7SGreg Kroah-Hartman uint8_t flags; 93728674a7SGreg Kroah-Hartman #ifdef CONFIG_MAGIC_SYSRQ 94728674a7SGreg Kroah-Hartman uint8_t sysrq; 95728674a7SGreg Kroah-Hartman #endif /* CONFIG_MAGIC_SYSRQ */ 96728674a7SGreg Kroah-Hartman }; 97728674a7SGreg Kroah-Hartman static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; 98728674a7SGreg Kroah-Hartman 99728674a7SGreg Kroah-Hartman static struct tty_driver *hvsi_driver; 100728674a7SGreg Kroah-Hartman static int hvsi_count; 101728674a7SGreg Kroah-Hartman static int (*hvsi_wait)(struct hvsi_struct *hp, int state); 102728674a7SGreg Kroah-Hartman 103728674a7SGreg Kroah-Hartman enum HVSI_PROTOCOL_STATE { 104728674a7SGreg Kroah-Hartman HVSI_CLOSED, 105728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_VER_RESPONSE, 106728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_VER_QUERY, 107728674a7SGreg Kroah-Hartman HVSI_OPEN, 108728674a7SGreg Kroah-Hartman HVSI_WAIT_FOR_MCTRL_RESPONSE, 109728674a7SGreg Kroah-Hartman HVSI_FSP_DIED, 110728674a7SGreg Kroah-Hartman }; 111728674a7SGreg Kroah-Hartman #define HVSI_CONSOLE 0x1 112728674a7SGreg Kroah-Hartman 113728674a7SGreg Kroah-Hartman static inline int is_console(struct hvsi_struct *hp) 114728674a7SGreg Kroah-Hartman { 115728674a7SGreg Kroah-Hartman return hp->flags & HVSI_CONSOLE; 116728674a7SGreg Kroah-Hartman } 117728674a7SGreg Kroah-Hartman 118728674a7SGreg Kroah-Hartman static inline int is_open(struct hvsi_struct *hp) 119728674a7SGreg Kroah-Hartman { 120728674a7SGreg Kroah-Hartman /* if we're waiting for an mctrl then we're already open */ 121728674a7SGreg Kroah-Hartman return (hp->state == HVSI_OPEN) 122728674a7SGreg Kroah-Hartman || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); 123728674a7SGreg Kroah-Hartman } 124728674a7SGreg Kroah-Hartman 125728674a7SGreg Kroah-Hartman static inline void print_state(struct hvsi_struct *hp) 126728674a7SGreg Kroah-Hartman { 127728674a7SGreg Kroah-Hartman #ifdef DEBUG 128728674a7SGreg Kroah-Hartman static const char *state_names[] = { 129728674a7SGreg Kroah-Hartman "HVSI_CLOSED", 130728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_VER_RESPONSE", 131728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_VER_QUERY", 132728674a7SGreg Kroah-Hartman "HVSI_OPEN", 133728674a7SGreg Kroah-Hartman "HVSI_WAIT_FOR_MCTRL_RESPONSE", 134728674a7SGreg Kroah-Hartman "HVSI_FSP_DIED", 135728674a7SGreg Kroah-Hartman }; 136728674a7SGreg Kroah-Hartman const char *name = (hp->state < ARRAY_SIZE(state_names)) 137728674a7SGreg Kroah-Hartman ? state_names[hp->state] : "UNKNOWN"; 138728674a7SGreg Kroah-Hartman 139728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: state = %s\n", hp->index, name); 140728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 141728674a7SGreg Kroah-Hartman } 142728674a7SGreg Kroah-Hartman 143728674a7SGreg Kroah-Hartman static inline void __set_state(struct hvsi_struct *hp, int state) 144728674a7SGreg Kroah-Hartman { 145728674a7SGreg Kroah-Hartman hp->state = state; 146728674a7SGreg Kroah-Hartman print_state(hp); 147728674a7SGreg Kroah-Hartman wake_up_all(&hp->stateq); 148728674a7SGreg Kroah-Hartman } 149728674a7SGreg Kroah-Hartman 150728674a7SGreg Kroah-Hartman static inline void set_state(struct hvsi_struct *hp, int state) 151728674a7SGreg Kroah-Hartman { 152728674a7SGreg Kroah-Hartman unsigned long flags; 153728674a7SGreg Kroah-Hartman 154728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 155728674a7SGreg Kroah-Hartman __set_state(hp, state); 156728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 157728674a7SGreg Kroah-Hartman } 158728674a7SGreg Kroah-Hartman 159728674a7SGreg Kroah-Hartman static inline int len_packet(const uint8_t *packet) 160728674a7SGreg Kroah-Hartman { 161728674a7SGreg Kroah-Hartman return (int)((struct hvsi_header *)packet)->len; 162728674a7SGreg Kroah-Hartman } 163728674a7SGreg Kroah-Hartman 164728674a7SGreg Kroah-Hartman static inline int is_header(const uint8_t *packet) 165728674a7SGreg Kroah-Hartman { 166728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 167728674a7SGreg Kroah-Hartman return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; 168728674a7SGreg Kroah-Hartman } 169728674a7SGreg Kroah-Hartman 170728674a7SGreg Kroah-Hartman static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) 171728674a7SGreg Kroah-Hartman { 172728674a7SGreg Kroah-Hartman if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) 173728674a7SGreg Kroah-Hartman return 0; /* don't even have the packet header */ 174728674a7SGreg Kroah-Hartman 175728674a7SGreg Kroah-Hartman if (hp->inbuf_end < (packet + len_packet(packet))) 176728674a7SGreg Kroah-Hartman return 0; /* don't have the rest of the packet */ 177728674a7SGreg Kroah-Hartman 178728674a7SGreg Kroah-Hartman return 1; 179728674a7SGreg Kroah-Hartman } 180728674a7SGreg Kroah-Hartman 181728674a7SGreg Kroah-Hartman /* shift remaining bytes in packetbuf down */ 182728674a7SGreg Kroah-Hartman static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) 183728674a7SGreg Kroah-Hartman { 184728674a7SGreg Kroah-Hartman int remaining = (int)(hp->inbuf_end - read_to); 185728674a7SGreg Kroah-Hartman 186728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars remain\n", __func__, remaining); 187728674a7SGreg Kroah-Hartman 188728674a7SGreg Kroah-Hartman if (read_to != hp->inbuf) 189728674a7SGreg Kroah-Hartman memmove(hp->inbuf, read_to, remaining); 190728674a7SGreg Kroah-Hartman 191728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf + remaining; 192728674a7SGreg Kroah-Hartman } 193728674a7SGreg Kroah-Hartman 194728674a7SGreg Kroah-Hartman #ifdef DEBUG 195728674a7SGreg Kroah-Hartman #define dbg_dump_packet(packet) dump_packet(packet) 196728674a7SGreg Kroah-Hartman #define dbg_dump_hex(data, len) dump_hex(data, len) 197728674a7SGreg Kroah-Hartman #else 198728674a7SGreg Kroah-Hartman #define dbg_dump_packet(packet) do { } while (0) 199728674a7SGreg Kroah-Hartman #define dbg_dump_hex(data, len) do { } while (0) 200728674a7SGreg Kroah-Hartman #endif 201728674a7SGreg Kroah-Hartman 202728674a7SGreg Kroah-Hartman static void dump_hex(const uint8_t *data, int len) 203728674a7SGreg Kroah-Hartman { 204728674a7SGreg Kroah-Hartman int i; 205728674a7SGreg Kroah-Hartman 206728674a7SGreg Kroah-Hartman printk(" "); 207728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) 208728674a7SGreg Kroah-Hartman printk("%.2x", data[i]); 209728674a7SGreg Kroah-Hartman 210728674a7SGreg Kroah-Hartman printk("\n "); 211728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) { 212728674a7SGreg Kroah-Hartman if (isprint(data[i])) 213728674a7SGreg Kroah-Hartman printk("%c", data[i]); 214728674a7SGreg Kroah-Hartman else 215728674a7SGreg Kroah-Hartman printk("."); 216728674a7SGreg Kroah-Hartman } 217728674a7SGreg Kroah-Hartman printk("\n"); 218728674a7SGreg Kroah-Hartman } 219728674a7SGreg Kroah-Hartman 220728674a7SGreg Kroah-Hartman static void dump_packet(uint8_t *packet) 221728674a7SGreg Kroah-Hartman { 222728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 223728674a7SGreg Kroah-Hartman 224728674a7SGreg Kroah-Hartman printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, 225728674a7SGreg Kroah-Hartman header->seqno); 226728674a7SGreg Kroah-Hartman 227728674a7SGreg Kroah-Hartman dump_hex(packet, header->len); 228728674a7SGreg Kroah-Hartman } 229728674a7SGreg Kroah-Hartman 230728674a7SGreg Kroah-Hartman static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) 231728674a7SGreg Kroah-Hartman { 232728674a7SGreg Kroah-Hartman unsigned long got; 233728674a7SGreg Kroah-Hartman 234728674a7SGreg Kroah-Hartman got = hvc_get_chars(hp->vtermno, buf, count); 235728674a7SGreg Kroah-Hartman 236728674a7SGreg Kroah-Hartman return got; 237728674a7SGreg Kroah-Hartman } 238728674a7SGreg Kroah-Hartman 239728674a7SGreg Kroah-Hartman static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, 240728674a7SGreg Kroah-Hartman struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) 241728674a7SGreg Kroah-Hartman { 242728674a7SGreg Kroah-Hartman struct hvsi_control *header = (struct hvsi_control *)packet; 243728674a7SGreg Kroah-Hartman 244728674a7SGreg Kroah-Hartman switch (header->verb) { 245728674a7SGreg Kroah-Hartman case VSV_MODEM_CTL_UPDATE: 246728674a7SGreg Kroah-Hartman if ((header->word & HVSI_TSCD) == 0) { 247728674a7SGreg Kroah-Hartman /* CD went away; no more connection */ 248728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: CD dropped\n", hp->index); 249728674a7SGreg Kroah-Hartman hp->mctrl &= TIOCM_CD; 250728674a7SGreg Kroah-Hartman /* If userland hasn't done an open(2) yet, hp->tty is NULL. */ 251728674a7SGreg Kroah-Hartman if (hp->tty && !(hp->tty->flags & CLOCAL)) 252728674a7SGreg Kroah-Hartman *to_hangup = hp->tty; 253728674a7SGreg Kroah-Hartman } 254728674a7SGreg Kroah-Hartman break; 255728674a7SGreg Kroah-Hartman case VSV_CLOSE_PROTOCOL: 256728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: service processor came back\n", hp->index); 257728674a7SGreg Kroah-Hartman if (hp->state != HVSI_CLOSED) { 258728674a7SGreg Kroah-Hartman *to_handshake = hp; 259728674a7SGreg Kroah-Hartman } 260728674a7SGreg Kroah-Hartman break; 261728674a7SGreg Kroah-Hartman default: 262728674a7SGreg Kroah-Hartman printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", 263728674a7SGreg Kroah-Hartman hp->index); 264728674a7SGreg Kroah-Hartman dump_packet(packet); 265728674a7SGreg Kroah-Hartman break; 266728674a7SGreg Kroah-Hartman } 267728674a7SGreg Kroah-Hartman } 268728674a7SGreg Kroah-Hartman 269728674a7SGreg Kroah-Hartman static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) 270728674a7SGreg Kroah-Hartman { 271728674a7SGreg Kroah-Hartman struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; 272728674a7SGreg Kroah-Hartman 273728674a7SGreg Kroah-Hartman switch (hp->state) { 274728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_VER_RESPONSE: 275728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); 276728674a7SGreg Kroah-Hartman break; 277728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_MCTRL_RESPONSE: 278728674a7SGreg Kroah-Hartman hp->mctrl = 0; 279728674a7SGreg Kroah-Hartman if (resp->u.mctrl_word & HVSI_TSDTR) 280728674a7SGreg Kroah-Hartman hp->mctrl |= TIOCM_DTR; 281728674a7SGreg Kroah-Hartman if (resp->u.mctrl_word & HVSI_TSCD) 282728674a7SGreg Kroah-Hartman hp->mctrl |= TIOCM_CD; 283728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_OPEN); 284728674a7SGreg Kroah-Hartman break; 285728674a7SGreg Kroah-Hartman default: 286728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); 287728674a7SGreg Kroah-Hartman dump_packet(packet); 288728674a7SGreg Kroah-Hartman break; 289728674a7SGreg Kroah-Hartman } 290728674a7SGreg Kroah-Hartman } 291728674a7SGreg Kroah-Hartman 292728674a7SGreg Kroah-Hartman /* respond to service processor's version query */ 293728674a7SGreg Kroah-Hartman static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) 294728674a7SGreg Kroah-Hartman { 295728674a7SGreg Kroah-Hartman struct hvsi_query_response packet __ALIGNED__; 296728674a7SGreg Kroah-Hartman int wrote; 297728674a7SGreg Kroah-Hartman 298728674a7SGreg Kroah-Hartman packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; 299728674a7SGreg Kroah-Hartman packet.len = sizeof(struct hvsi_query_response); 300728674a7SGreg Kroah-Hartman packet.seqno = atomic_inc_return(&hp->seqno); 301728674a7SGreg Kroah-Hartman packet.verb = VSV_SEND_VERSION_NUMBER; 302728674a7SGreg Kroah-Hartman packet.u.version = HVSI_VERSION; 303728674a7SGreg Kroah-Hartman packet.query_seqno = query_seqno+1; 304728674a7SGreg Kroah-Hartman 305728674a7SGreg Kroah-Hartman pr_debug("%s: sending %i bytes\n", __func__, packet.len); 306728674a7SGreg Kroah-Hartman dbg_dump_hex((uint8_t*)&packet, packet.len); 307728674a7SGreg Kroah-Hartman 308728674a7SGreg Kroah-Hartman wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); 309728674a7SGreg Kroah-Hartman if (wrote != packet.len) { 310728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send query response!\n", 311728674a7SGreg Kroah-Hartman hp->index); 312728674a7SGreg Kroah-Hartman return -EIO; 313728674a7SGreg Kroah-Hartman } 314728674a7SGreg Kroah-Hartman 315728674a7SGreg Kroah-Hartman return 0; 316728674a7SGreg Kroah-Hartman } 317728674a7SGreg Kroah-Hartman 318728674a7SGreg Kroah-Hartman static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) 319728674a7SGreg Kroah-Hartman { 320728674a7SGreg Kroah-Hartman struct hvsi_query *query = (struct hvsi_query *)packet; 321728674a7SGreg Kroah-Hartman 322728674a7SGreg Kroah-Hartman switch (hp->state) { 323728674a7SGreg Kroah-Hartman case HVSI_WAIT_FOR_VER_QUERY: 324728674a7SGreg Kroah-Hartman hvsi_version_respond(hp, query->seqno); 325728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_OPEN); 326728674a7SGreg Kroah-Hartman break; 327728674a7SGreg Kroah-Hartman default: 328728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); 329728674a7SGreg Kroah-Hartman dump_packet(packet); 330728674a7SGreg Kroah-Hartman break; 331728674a7SGreg Kroah-Hartman } 332728674a7SGreg Kroah-Hartman } 333728674a7SGreg Kroah-Hartman 334728674a7SGreg Kroah-Hartman static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) 335728674a7SGreg Kroah-Hartman { 336728674a7SGreg Kroah-Hartman int i; 337728674a7SGreg Kroah-Hartman 338728674a7SGreg Kroah-Hartman for (i=0; i < len; i++) { 339728674a7SGreg Kroah-Hartman char c = buf[i]; 340728674a7SGreg Kroah-Hartman #ifdef CONFIG_MAGIC_SYSRQ 341728674a7SGreg Kroah-Hartman if (c == '\0') { 342728674a7SGreg Kroah-Hartman hp->sysrq = 1; 343728674a7SGreg Kroah-Hartman continue; 344728674a7SGreg Kroah-Hartman } else if (hp->sysrq) { 345728674a7SGreg Kroah-Hartman handle_sysrq(c); 346728674a7SGreg Kroah-Hartman hp->sysrq = 0; 347728674a7SGreg Kroah-Hartman continue; 348728674a7SGreg Kroah-Hartman } 349728674a7SGreg Kroah-Hartman #endif /* CONFIG_MAGIC_SYSRQ */ 350728674a7SGreg Kroah-Hartman tty_insert_flip_char(hp->tty, c, 0); 351728674a7SGreg Kroah-Hartman } 352728674a7SGreg Kroah-Hartman } 353728674a7SGreg Kroah-Hartman 354728674a7SGreg Kroah-Hartman /* 355728674a7SGreg Kroah-Hartman * We could get 252 bytes of data at once here. But the tty layer only 356728674a7SGreg Kroah-Hartman * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow 357728674a7SGreg Kroah-Hartman * it. Accordingly we won't send more than 128 bytes at a time to the flip 358728674a7SGreg Kroah-Hartman * buffer, which will give the tty buffer a chance to throttle us. Should the 359728674a7SGreg Kroah-Hartman * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be 360728674a7SGreg Kroah-Hartman * revisited. 361728674a7SGreg Kroah-Hartman */ 362728674a7SGreg Kroah-Hartman #define TTY_THRESHOLD_THROTTLE 128 363728674a7SGreg Kroah-Hartman static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, 364728674a7SGreg Kroah-Hartman const uint8_t *packet) 365728674a7SGreg Kroah-Hartman { 366728674a7SGreg Kroah-Hartman const struct hvsi_header *header = (const struct hvsi_header *)packet; 367728674a7SGreg Kroah-Hartman const uint8_t *data = packet + sizeof(struct hvsi_header); 368728674a7SGreg Kroah-Hartman int datalen = header->len - sizeof(struct hvsi_header); 369728674a7SGreg Kroah-Hartman int overflow = datalen - TTY_THRESHOLD_THROTTLE; 370728674a7SGreg Kroah-Hartman 371728674a7SGreg Kroah-Hartman pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); 372728674a7SGreg Kroah-Hartman 373728674a7SGreg Kroah-Hartman if (datalen == 0) 374728674a7SGreg Kroah-Hartman return NULL; 375728674a7SGreg Kroah-Hartman 376728674a7SGreg Kroah-Hartman if (overflow > 0) { 377728674a7SGreg Kroah-Hartman pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); 378728674a7SGreg Kroah-Hartman datalen = TTY_THRESHOLD_THROTTLE; 379728674a7SGreg Kroah-Hartman } 380728674a7SGreg Kroah-Hartman 381728674a7SGreg Kroah-Hartman hvsi_insert_chars(hp, data, datalen); 382728674a7SGreg Kroah-Hartman 383728674a7SGreg Kroah-Hartman if (overflow > 0) { 384728674a7SGreg Kroah-Hartman /* 385728674a7SGreg Kroah-Hartman * we still have more data to deliver, so we need to save off the 386728674a7SGreg Kroah-Hartman * overflow and send it later 387728674a7SGreg Kroah-Hartman */ 388728674a7SGreg Kroah-Hartman pr_debug("%s: deferring overflow\n", __func__); 389728674a7SGreg Kroah-Hartman memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); 390728674a7SGreg Kroah-Hartman hp->n_throttle = overflow; 391728674a7SGreg Kroah-Hartman } 392728674a7SGreg Kroah-Hartman 393728674a7SGreg Kroah-Hartman return hp->tty; 394728674a7SGreg Kroah-Hartman } 395728674a7SGreg Kroah-Hartman 396728674a7SGreg Kroah-Hartman /* 397728674a7SGreg Kroah-Hartman * Returns true/false indicating data successfully read from hypervisor. 398728674a7SGreg Kroah-Hartman * Used both to get packets for tty connections and to advance the state 399728674a7SGreg Kroah-Hartman * machine during console handshaking (in which case tty = NULL and we ignore 400728674a7SGreg Kroah-Hartman * incoming data). 401728674a7SGreg Kroah-Hartman */ 402728674a7SGreg Kroah-Hartman static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, 403728674a7SGreg Kroah-Hartman struct tty_struct **hangup, struct hvsi_struct **handshake) 404728674a7SGreg Kroah-Hartman { 405728674a7SGreg Kroah-Hartman uint8_t *packet = hp->inbuf; 406728674a7SGreg Kroah-Hartman int chunklen; 407728674a7SGreg Kroah-Hartman 408728674a7SGreg Kroah-Hartman *flip = NULL; 409728674a7SGreg Kroah-Hartman *hangup = NULL; 410728674a7SGreg Kroah-Hartman *handshake = NULL; 411728674a7SGreg Kroah-Hartman 412728674a7SGreg Kroah-Hartman chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); 413728674a7SGreg Kroah-Hartman if (chunklen == 0) { 414728674a7SGreg Kroah-Hartman pr_debug("%s: 0-length read\n", __func__); 415728674a7SGreg Kroah-Hartman return 0; 416728674a7SGreg Kroah-Hartman } 417728674a7SGreg Kroah-Hartman 418728674a7SGreg Kroah-Hartman pr_debug("%s: got %i bytes\n", __func__, chunklen); 419728674a7SGreg Kroah-Hartman dbg_dump_hex(hp->inbuf_end, chunklen); 420728674a7SGreg Kroah-Hartman 421728674a7SGreg Kroah-Hartman hp->inbuf_end += chunklen; 422728674a7SGreg Kroah-Hartman 423728674a7SGreg Kroah-Hartman /* handle all completed packets */ 424728674a7SGreg Kroah-Hartman while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { 425728674a7SGreg Kroah-Hartman struct hvsi_header *header = (struct hvsi_header *)packet; 426728674a7SGreg Kroah-Hartman 427728674a7SGreg Kroah-Hartman if (!is_header(packet)) { 428728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); 429728674a7SGreg Kroah-Hartman /* skip bytes until we find a header or run out of data */ 430728674a7SGreg Kroah-Hartman while ((packet < hp->inbuf_end) && (!is_header(packet))) 431728674a7SGreg Kroah-Hartman packet++; 432728674a7SGreg Kroah-Hartman continue; 433728674a7SGreg Kroah-Hartman } 434728674a7SGreg Kroah-Hartman 435728674a7SGreg Kroah-Hartman pr_debug("%s: handling %i-byte packet\n", __func__, 436728674a7SGreg Kroah-Hartman len_packet(packet)); 437728674a7SGreg Kroah-Hartman dbg_dump_packet(packet); 438728674a7SGreg Kroah-Hartman 439728674a7SGreg Kroah-Hartman switch (header->type) { 440728674a7SGreg Kroah-Hartman case VS_DATA_PACKET_HEADER: 441728674a7SGreg Kroah-Hartman if (!is_open(hp)) 442728674a7SGreg Kroah-Hartman break; 443728674a7SGreg Kroah-Hartman if (hp->tty == NULL) 444728674a7SGreg Kroah-Hartman break; /* no tty buffer to put data in */ 445728674a7SGreg Kroah-Hartman *flip = hvsi_recv_data(hp, packet); 446728674a7SGreg Kroah-Hartman break; 447728674a7SGreg Kroah-Hartman case VS_CONTROL_PACKET_HEADER: 448728674a7SGreg Kroah-Hartman hvsi_recv_control(hp, packet, hangup, handshake); 449728674a7SGreg Kroah-Hartman break; 450728674a7SGreg Kroah-Hartman case VS_QUERY_RESPONSE_PACKET_HEADER: 451728674a7SGreg Kroah-Hartman hvsi_recv_response(hp, packet); 452728674a7SGreg Kroah-Hartman break; 453728674a7SGreg Kroah-Hartman case VS_QUERY_PACKET_HEADER: 454728674a7SGreg Kroah-Hartman hvsi_recv_query(hp, packet); 455728674a7SGreg Kroah-Hartman break; 456728674a7SGreg Kroah-Hartman default: 457728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", 458728674a7SGreg Kroah-Hartman hp->index, header->type); 459728674a7SGreg Kroah-Hartman dump_packet(packet); 460728674a7SGreg Kroah-Hartman break; 461728674a7SGreg Kroah-Hartman } 462728674a7SGreg Kroah-Hartman 463728674a7SGreg Kroah-Hartman packet += len_packet(packet); 464728674a7SGreg Kroah-Hartman 465728674a7SGreg Kroah-Hartman if (*hangup || *handshake) { 466728674a7SGreg Kroah-Hartman pr_debug("%s: hangup or handshake\n", __func__); 467728674a7SGreg Kroah-Hartman /* 468728674a7SGreg Kroah-Hartman * we need to send the hangup now before receiving any more data. 469728674a7SGreg Kroah-Hartman * If we get "data, hangup, data", we can't deliver the second 470728674a7SGreg Kroah-Hartman * data before the hangup. 471728674a7SGreg Kroah-Hartman */ 472728674a7SGreg Kroah-Hartman break; 473728674a7SGreg Kroah-Hartman } 474728674a7SGreg Kroah-Hartman } 475728674a7SGreg Kroah-Hartman 476728674a7SGreg Kroah-Hartman compact_inbuf(hp, packet); 477728674a7SGreg Kroah-Hartman 478728674a7SGreg Kroah-Hartman return 1; 479728674a7SGreg Kroah-Hartman } 480728674a7SGreg Kroah-Hartman 481728674a7SGreg Kroah-Hartman static void hvsi_send_overflow(struct hvsi_struct *hp) 482728674a7SGreg Kroah-Hartman { 483728674a7SGreg Kroah-Hartman pr_debug("%s: delivering %i bytes overflow\n", __func__, 484728674a7SGreg Kroah-Hartman hp->n_throttle); 485728674a7SGreg Kroah-Hartman 486728674a7SGreg Kroah-Hartman hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); 487728674a7SGreg Kroah-Hartman hp->n_throttle = 0; 488728674a7SGreg Kroah-Hartman } 489728674a7SGreg Kroah-Hartman 490728674a7SGreg Kroah-Hartman /* 491728674a7SGreg Kroah-Hartman * must get all pending data because we only get an irq on empty->non-empty 492728674a7SGreg Kroah-Hartman * transition 493728674a7SGreg Kroah-Hartman */ 494728674a7SGreg Kroah-Hartman static irqreturn_t hvsi_interrupt(int irq, void *arg) 495728674a7SGreg Kroah-Hartman { 496728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = (struct hvsi_struct *)arg; 497728674a7SGreg Kroah-Hartman struct tty_struct *flip; 498728674a7SGreg Kroah-Hartman struct tty_struct *hangup; 499728674a7SGreg Kroah-Hartman struct hvsi_struct *handshake; 500728674a7SGreg Kroah-Hartman unsigned long flags; 501728674a7SGreg Kroah-Hartman int again = 1; 502728674a7SGreg Kroah-Hartman 503728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 504728674a7SGreg Kroah-Hartman 505728674a7SGreg Kroah-Hartman while (again) { 506728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 507728674a7SGreg Kroah-Hartman again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); 508728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 509728674a7SGreg Kroah-Hartman 510728674a7SGreg Kroah-Hartman /* 511728674a7SGreg Kroah-Hartman * we have to call tty_flip_buffer_push() and tty_hangup() outside our 512728674a7SGreg Kroah-Hartman * spinlock. But we also have to keep going until we've read all the 513728674a7SGreg Kroah-Hartman * available data. 514728674a7SGreg Kroah-Hartman */ 515728674a7SGreg Kroah-Hartman 516728674a7SGreg Kroah-Hartman if (flip) { 517728674a7SGreg Kroah-Hartman /* there was data put in the tty flip buffer */ 518728674a7SGreg Kroah-Hartman tty_flip_buffer_push(flip); 519728674a7SGreg Kroah-Hartman flip = NULL; 520728674a7SGreg Kroah-Hartman } 521728674a7SGreg Kroah-Hartman 522728674a7SGreg Kroah-Hartman if (hangup) { 523728674a7SGreg Kroah-Hartman tty_hangup(hangup); 524728674a7SGreg Kroah-Hartman } 525728674a7SGreg Kroah-Hartman 526728674a7SGreg Kroah-Hartman if (handshake) { 527728674a7SGreg Kroah-Hartman pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); 528728674a7SGreg Kroah-Hartman schedule_work(&handshake->handshaker); 529728674a7SGreg Kroah-Hartman } 530728674a7SGreg Kroah-Hartman } 531728674a7SGreg Kroah-Hartman 532728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 533728674a7SGreg Kroah-Hartman if (hp->tty && hp->n_throttle 534728674a7SGreg Kroah-Hartman && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { 535728674a7SGreg Kroah-Hartman /* we weren't hung up and we weren't throttled, so we can deliver the 536728674a7SGreg Kroah-Hartman * rest now */ 537728674a7SGreg Kroah-Hartman flip = hp->tty; 538728674a7SGreg Kroah-Hartman hvsi_send_overflow(hp); 539728674a7SGreg Kroah-Hartman } 540728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 541728674a7SGreg Kroah-Hartman 542728674a7SGreg Kroah-Hartman if (flip) { 543728674a7SGreg Kroah-Hartman tty_flip_buffer_push(flip); 544728674a7SGreg Kroah-Hartman } 545728674a7SGreg Kroah-Hartman 546728674a7SGreg Kroah-Hartman return IRQ_HANDLED; 547728674a7SGreg Kroah-Hartman } 548728674a7SGreg Kroah-Hartman 549728674a7SGreg Kroah-Hartman /* for boot console, before the irq handler is running */ 550728674a7SGreg Kroah-Hartman static int __init poll_for_state(struct hvsi_struct *hp, int state) 551728674a7SGreg Kroah-Hartman { 552728674a7SGreg Kroah-Hartman unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 553728674a7SGreg Kroah-Hartman 554728674a7SGreg Kroah-Hartman for (;;) { 555728674a7SGreg Kroah-Hartman hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ 556728674a7SGreg Kroah-Hartman 557728674a7SGreg Kroah-Hartman if (hp->state == state) 558728674a7SGreg Kroah-Hartman return 0; 559728674a7SGreg Kroah-Hartman 560728674a7SGreg Kroah-Hartman mdelay(5); 561728674a7SGreg Kroah-Hartman if (time_after(jiffies, end_jiffies)) 562728674a7SGreg Kroah-Hartman return -EIO; 563728674a7SGreg Kroah-Hartman } 564728674a7SGreg Kroah-Hartman } 565728674a7SGreg Kroah-Hartman 566728674a7SGreg Kroah-Hartman /* wait for irq handler to change our state */ 567728674a7SGreg Kroah-Hartman static int wait_for_state(struct hvsi_struct *hp, int state) 568728674a7SGreg Kroah-Hartman { 569728674a7SGreg Kroah-Hartman int ret = 0; 570728674a7SGreg Kroah-Hartman 571728674a7SGreg Kroah-Hartman if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) 572728674a7SGreg Kroah-Hartman ret = -EIO; 573728674a7SGreg Kroah-Hartman 574728674a7SGreg Kroah-Hartman return ret; 575728674a7SGreg Kroah-Hartman } 576728674a7SGreg Kroah-Hartman 577728674a7SGreg Kroah-Hartman static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) 578728674a7SGreg Kroah-Hartman { 579728674a7SGreg Kroah-Hartman struct hvsi_query packet __ALIGNED__; 580728674a7SGreg Kroah-Hartman int wrote; 581728674a7SGreg Kroah-Hartman 582728674a7SGreg Kroah-Hartman packet.type = VS_QUERY_PACKET_HEADER; 583728674a7SGreg Kroah-Hartman packet.len = sizeof(struct hvsi_query); 584728674a7SGreg Kroah-Hartman packet.seqno = atomic_inc_return(&hp->seqno); 585728674a7SGreg Kroah-Hartman packet.verb = verb; 586728674a7SGreg Kroah-Hartman 587728674a7SGreg Kroah-Hartman pr_debug("%s: sending %i bytes\n", __func__, packet.len); 588728674a7SGreg Kroah-Hartman dbg_dump_hex((uint8_t*)&packet, packet.len); 589728674a7SGreg Kroah-Hartman 590728674a7SGreg Kroah-Hartman wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); 591728674a7SGreg Kroah-Hartman if (wrote != packet.len) { 592728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, 593728674a7SGreg Kroah-Hartman wrote); 594728674a7SGreg Kroah-Hartman return -EIO; 595728674a7SGreg Kroah-Hartman } 596728674a7SGreg Kroah-Hartman 597728674a7SGreg Kroah-Hartman return 0; 598728674a7SGreg Kroah-Hartman } 599728674a7SGreg Kroah-Hartman 600728674a7SGreg Kroah-Hartman static int hvsi_get_mctrl(struct hvsi_struct *hp) 601728674a7SGreg Kroah-Hartman { 602728674a7SGreg Kroah-Hartman int ret; 603728674a7SGreg Kroah-Hartman 604728674a7SGreg Kroah-Hartman set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); 605728674a7SGreg Kroah-Hartman hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); 606728674a7SGreg Kroah-Hartman 607728674a7SGreg Kroah-Hartman ret = hvsi_wait(hp, HVSI_OPEN); 608728674a7SGreg Kroah-Hartman if (ret < 0) { 609728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); 610728674a7SGreg Kroah-Hartman set_state(hp, HVSI_OPEN); 611728674a7SGreg Kroah-Hartman return ret; 612728674a7SGreg Kroah-Hartman } 613728674a7SGreg Kroah-Hartman 614728674a7SGreg Kroah-Hartman pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); 615728674a7SGreg Kroah-Hartman 616728674a7SGreg Kroah-Hartman return 0; 617728674a7SGreg Kroah-Hartman } 618728674a7SGreg Kroah-Hartman 619728674a7SGreg Kroah-Hartman /* note that we can only set DTR */ 620728674a7SGreg Kroah-Hartman static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) 621728674a7SGreg Kroah-Hartman { 622728674a7SGreg Kroah-Hartman struct hvsi_control packet __ALIGNED__; 623728674a7SGreg Kroah-Hartman int wrote; 624728674a7SGreg Kroah-Hartman 625728674a7SGreg Kroah-Hartman packet.type = VS_CONTROL_PACKET_HEADER, 626728674a7SGreg Kroah-Hartman packet.seqno = atomic_inc_return(&hp->seqno); 627728674a7SGreg Kroah-Hartman packet.len = sizeof(struct hvsi_control); 628728674a7SGreg Kroah-Hartman packet.verb = VSV_SET_MODEM_CTL; 629728674a7SGreg Kroah-Hartman packet.mask = HVSI_TSDTR; 630728674a7SGreg Kroah-Hartman 631728674a7SGreg Kroah-Hartman if (mctrl & TIOCM_DTR) 632728674a7SGreg Kroah-Hartman packet.word = HVSI_TSDTR; 633728674a7SGreg Kroah-Hartman 634728674a7SGreg Kroah-Hartman pr_debug("%s: sending %i bytes\n", __func__, packet.len); 635728674a7SGreg Kroah-Hartman dbg_dump_hex((uint8_t*)&packet, packet.len); 636728674a7SGreg Kroah-Hartman 637728674a7SGreg Kroah-Hartman wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); 638728674a7SGreg Kroah-Hartman if (wrote != packet.len) { 639728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); 640728674a7SGreg Kroah-Hartman return -EIO; 641728674a7SGreg Kroah-Hartman } 642728674a7SGreg Kroah-Hartman 643728674a7SGreg Kroah-Hartman return 0; 644728674a7SGreg Kroah-Hartman } 645728674a7SGreg Kroah-Hartman 646728674a7SGreg Kroah-Hartman static void hvsi_drain_input(struct hvsi_struct *hp) 647728674a7SGreg Kroah-Hartman { 648728674a7SGreg Kroah-Hartman uint8_t buf[HVSI_MAX_READ] __ALIGNED__; 649728674a7SGreg Kroah-Hartman unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 650728674a7SGreg Kroah-Hartman 651728674a7SGreg Kroah-Hartman while (time_before(end_jiffies, jiffies)) 652728674a7SGreg Kroah-Hartman if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) 653728674a7SGreg Kroah-Hartman break; 654728674a7SGreg Kroah-Hartman } 655728674a7SGreg Kroah-Hartman 656728674a7SGreg Kroah-Hartman static int hvsi_handshake(struct hvsi_struct *hp) 657728674a7SGreg Kroah-Hartman { 658728674a7SGreg Kroah-Hartman int ret; 659728674a7SGreg Kroah-Hartman 660728674a7SGreg Kroah-Hartman /* 661728674a7SGreg Kroah-Hartman * We could have a CLOSE or other data waiting for us before we even try 662728674a7SGreg Kroah-Hartman * to open; try to throw it all away so we don't get confused. (CLOSE 663728674a7SGreg Kroah-Hartman * is the first message sent up the pipe when the FSP comes online. We 664728674a7SGreg Kroah-Hartman * need to distinguish between "it came up a while ago and we're the first 665728674a7SGreg Kroah-Hartman * user" and "it was just reset before it saw our handshake packet".) 666728674a7SGreg Kroah-Hartman */ 667728674a7SGreg Kroah-Hartman hvsi_drain_input(hp); 668728674a7SGreg Kroah-Hartman 669728674a7SGreg Kroah-Hartman set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); 670728674a7SGreg Kroah-Hartman ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); 671728674a7SGreg Kroah-Hartman if (ret < 0) { 672728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); 673728674a7SGreg Kroah-Hartman return ret; 674728674a7SGreg Kroah-Hartman } 675728674a7SGreg Kroah-Hartman 676728674a7SGreg Kroah-Hartman ret = hvsi_wait(hp, HVSI_OPEN); 677728674a7SGreg Kroah-Hartman if (ret < 0) 678728674a7SGreg Kroah-Hartman return ret; 679728674a7SGreg Kroah-Hartman 680728674a7SGreg Kroah-Hartman return 0; 681728674a7SGreg Kroah-Hartman } 682728674a7SGreg Kroah-Hartman 683728674a7SGreg Kroah-Hartman static void hvsi_handshaker(struct work_struct *work) 684728674a7SGreg Kroah-Hartman { 685728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = 686728674a7SGreg Kroah-Hartman container_of(work, struct hvsi_struct, handshaker); 687728674a7SGreg Kroah-Hartman 688728674a7SGreg Kroah-Hartman if (hvsi_handshake(hp) >= 0) 689728674a7SGreg Kroah-Hartman return; 690728674a7SGreg Kroah-Hartman 691728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); 692728674a7SGreg Kroah-Hartman if (is_console(hp)) { 693728674a7SGreg Kroah-Hartman /* 694728674a7SGreg Kroah-Hartman * ttys will re-attempt the handshake via hvsi_open, but 695728674a7SGreg Kroah-Hartman * the console will not. 696728674a7SGreg Kroah-Hartman */ 697728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); 698728674a7SGreg Kroah-Hartman } 699728674a7SGreg Kroah-Hartman } 700728674a7SGreg Kroah-Hartman 701728674a7SGreg Kroah-Hartman static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) 702728674a7SGreg Kroah-Hartman { 703728674a7SGreg Kroah-Hartman struct hvsi_data packet __ALIGNED__; 704728674a7SGreg Kroah-Hartman int ret; 705728674a7SGreg Kroah-Hartman 706728674a7SGreg Kroah-Hartman BUG_ON(count > HVSI_MAX_OUTGOING_DATA); 707728674a7SGreg Kroah-Hartman 708728674a7SGreg Kroah-Hartman packet.type = VS_DATA_PACKET_HEADER; 709728674a7SGreg Kroah-Hartman packet.seqno = atomic_inc_return(&hp->seqno); 710728674a7SGreg Kroah-Hartman packet.len = count + sizeof(struct hvsi_header); 711728674a7SGreg Kroah-Hartman memcpy(&packet.data, buf, count); 712728674a7SGreg Kroah-Hartman 713728674a7SGreg Kroah-Hartman ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); 714728674a7SGreg Kroah-Hartman if (ret == packet.len) { 715728674a7SGreg Kroah-Hartman /* return the number of chars written, not the packet length */ 716728674a7SGreg Kroah-Hartman return count; 717728674a7SGreg Kroah-Hartman } 718728674a7SGreg Kroah-Hartman return ret; /* return any errors */ 719728674a7SGreg Kroah-Hartman } 720728674a7SGreg Kroah-Hartman 721728674a7SGreg Kroah-Hartman static void hvsi_close_protocol(struct hvsi_struct *hp) 722728674a7SGreg Kroah-Hartman { 723728674a7SGreg Kroah-Hartman struct hvsi_control packet __ALIGNED__; 724728674a7SGreg Kroah-Hartman 725728674a7SGreg Kroah-Hartman packet.type = VS_CONTROL_PACKET_HEADER; 726728674a7SGreg Kroah-Hartman packet.seqno = atomic_inc_return(&hp->seqno); 727728674a7SGreg Kroah-Hartman packet.len = 6; 728728674a7SGreg Kroah-Hartman packet.verb = VSV_CLOSE_PROTOCOL; 729728674a7SGreg Kroah-Hartman 730728674a7SGreg Kroah-Hartman pr_debug("%s: sending %i bytes\n", __func__, packet.len); 731728674a7SGreg Kroah-Hartman dbg_dump_hex((uint8_t*)&packet, packet.len); 732728674a7SGreg Kroah-Hartman 733728674a7SGreg Kroah-Hartman hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); 734728674a7SGreg Kroah-Hartman } 735728674a7SGreg Kroah-Hartman 736728674a7SGreg Kroah-Hartman static int hvsi_open(struct tty_struct *tty, struct file *filp) 737728674a7SGreg Kroah-Hartman { 738728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 739728674a7SGreg Kroah-Hartman unsigned long flags; 740728674a7SGreg Kroah-Hartman int line = tty->index; 741728674a7SGreg Kroah-Hartman int ret; 742728674a7SGreg Kroah-Hartman 743728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 744728674a7SGreg Kroah-Hartman 745728674a7SGreg Kroah-Hartman if (line < 0 || line >= hvsi_count) 746728674a7SGreg Kroah-Hartman return -ENODEV; 747728674a7SGreg Kroah-Hartman hp = &hvsi_ports[line]; 748728674a7SGreg Kroah-Hartman 749728674a7SGreg Kroah-Hartman tty->driver_data = hp; 750728674a7SGreg Kroah-Hartman 751728674a7SGreg Kroah-Hartman mb(); 752728674a7SGreg Kroah-Hartman if (hp->state == HVSI_FSP_DIED) 753728674a7SGreg Kroah-Hartman return -EIO; 754728674a7SGreg Kroah-Hartman 755728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 756728674a7SGreg Kroah-Hartman hp->tty = tty; 757728674a7SGreg Kroah-Hartman hp->count++; 758728674a7SGreg Kroah-Hartman atomic_set(&hp->seqno, 0); 759728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 760728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 761728674a7SGreg Kroah-Hartman 762728674a7SGreg Kroah-Hartman if (is_console(hp)) 763728674a7SGreg Kroah-Hartman return 0; /* this has already been handshaked as the console */ 764728674a7SGreg Kroah-Hartman 765728674a7SGreg Kroah-Hartman ret = hvsi_handshake(hp); 766728674a7SGreg Kroah-Hartman if (ret < 0) { 767728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); 768728674a7SGreg Kroah-Hartman return ret; 769728674a7SGreg Kroah-Hartman } 770728674a7SGreg Kroah-Hartman 771728674a7SGreg Kroah-Hartman ret = hvsi_get_mctrl(hp); 772728674a7SGreg Kroah-Hartman if (ret < 0) { 773728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); 774728674a7SGreg Kroah-Hartman return ret; 775728674a7SGreg Kroah-Hartman } 776728674a7SGreg Kroah-Hartman 777728674a7SGreg Kroah-Hartman ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 778728674a7SGreg Kroah-Hartman if (ret < 0) { 779728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); 780728674a7SGreg Kroah-Hartman return ret; 781728674a7SGreg Kroah-Hartman } 782728674a7SGreg Kroah-Hartman 783728674a7SGreg Kroah-Hartman return 0; 784728674a7SGreg Kroah-Hartman } 785728674a7SGreg Kroah-Hartman 786728674a7SGreg Kroah-Hartman /* wait for hvsi_write_worker to empty hp->outbuf */ 787728674a7SGreg Kroah-Hartman static void hvsi_flush_output(struct hvsi_struct *hp) 788728674a7SGreg Kroah-Hartman { 789728674a7SGreg Kroah-Hartman wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); 790728674a7SGreg Kroah-Hartman 791728674a7SGreg Kroah-Hartman /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ 792728674a7SGreg Kroah-Hartman cancel_delayed_work_sync(&hp->writer); 793728674a7SGreg Kroah-Hartman flush_work_sync(&hp->handshaker); 794728674a7SGreg Kroah-Hartman 795728674a7SGreg Kroah-Hartman /* 796728674a7SGreg Kroah-Hartman * it's also possible that our timeout expired and hvsi_write_worker 797728674a7SGreg Kroah-Hartman * didn't manage to push outbuf. poof. 798728674a7SGreg Kroah-Hartman */ 799728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 800728674a7SGreg Kroah-Hartman } 801728674a7SGreg Kroah-Hartman 802728674a7SGreg Kroah-Hartman static void hvsi_close(struct tty_struct *tty, struct file *filp) 803728674a7SGreg Kroah-Hartman { 804728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 805728674a7SGreg Kroah-Hartman unsigned long flags; 806728674a7SGreg Kroah-Hartman 807728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 808728674a7SGreg Kroah-Hartman 809728674a7SGreg Kroah-Hartman if (tty_hung_up_p(filp)) 810728674a7SGreg Kroah-Hartman return; 811728674a7SGreg Kroah-Hartman 812728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 813728674a7SGreg Kroah-Hartman 814728674a7SGreg Kroah-Hartman if (--hp->count == 0) { 815728674a7SGreg Kroah-Hartman hp->tty = NULL; 816728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ 817728674a7SGreg Kroah-Hartman 818728674a7SGreg Kroah-Hartman /* only close down connection if it is not the console */ 819728674a7SGreg Kroah-Hartman if (!is_console(hp)) { 820728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ 821728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_CLOSED); 822728674a7SGreg Kroah-Hartman /* 823728674a7SGreg Kroah-Hartman * any data delivered to the tty layer after this will be 824728674a7SGreg Kroah-Hartman * discarded (except for XON/XOFF) 825728674a7SGreg Kroah-Hartman */ 826728674a7SGreg Kroah-Hartman tty->closing = 1; 827728674a7SGreg Kroah-Hartman 828728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 829728674a7SGreg Kroah-Hartman 830728674a7SGreg Kroah-Hartman /* let any existing irq handlers finish. no more will start. */ 831728674a7SGreg Kroah-Hartman synchronize_irq(hp->virq); 832728674a7SGreg Kroah-Hartman 833728674a7SGreg Kroah-Hartman /* hvsi_write_worker will re-schedule until outbuf is empty. */ 834728674a7SGreg Kroah-Hartman hvsi_flush_output(hp); 835728674a7SGreg Kroah-Hartman 836728674a7SGreg Kroah-Hartman /* tell FSP to stop sending data */ 837728674a7SGreg Kroah-Hartman hvsi_close_protocol(hp); 838728674a7SGreg Kroah-Hartman 839728674a7SGreg Kroah-Hartman /* 840728674a7SGreg Kroah-Hartman * drain anything FSP is still in the middle of sending, and let 841728674a7SGreg Kroah-Hartman * hvsi_handshake drain the rest on the next open. 842728674a7SGreg Kroah-Hartman */ 843728674a7SGreg Kroah-Hartman hvsi_drain_input(hp); 844728674a7SGreg Kroah-Hartman 845728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 846728674a7SGreg Kroah-Hartman } 847728674a7SGreg Kroah-Hartman } else if (hp->count < 0) 848728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", 849728674a7SGreg Kroah-Hartman hp - hvsi_ports, hp->count); 850728674a7SGreg Kroah-Hartman 851728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 852728674a7SGreg Kroah-Hartman } 853728674a7SGreg Kroah-Hartman 854728674a7SGreg Kroah-Hartman static void hvsi_hangup(struct tty_struct *tty) 855728674a7SGreg Kroah-Hartman { 856728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 857728674a7SGreg Kroah-Hartman unsigned long flags; 858728674a7SGreg Kroah-Hartman 859728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 860728674a7SGreg Kroah-Hartman 861728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 862728674a7SGreg Kroah-Hartman 863728674a7SGreg Kroah-Hartman hp->count = 0; 864728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 865728674a7SGreg Kroah-Hartman hp->tty = NULL; 866728674a7SGreg Kroah-Hartman 867728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 868728674a7SGreg Kroah-Hartman } 869728674a7SGreg Kroah-Hartman 870728674a7SGreg Kroah-Hartman /* called with hp->lock held */ 871728674a7SGreg Kroah-Hartman static void hvsi_push(struct hvsi_struct *hp) 872728674a7SGreg Kroah-Hartman { 873728674a7SGreg Kroah-Hartman int n; 874728674a7SGreg Kroah-Hartman 875728674a7SGreg Kroah-Hartman if (hp->n_outbuf <= 0) 876728674a7SGreg Kroah-Hartman return; 877728674a7SGreg Kroah-Hartman 878728674a7SGreg Kroah-Hartman n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); 879728674a7SGreg Kroah-Hartman if (n > 0) { 880728674a7SGreg Kroah-Hartman /* success */ 881728674a7SGreg Kroah-Hartman pr_debug("%s: wrote %i chars\n", __func__, n); 882728674a7SGreg Kroah-Hartman hp->n_outbuf = 0; 883728674a7SGreg Kroah-Hartman } else if (n == -EIO) { 884728674a7SGreg Kroah-Hartman __set_state(hp, HVSI_FSP_DIED); 885728674a7SGreg Kroah-Hartman printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); 886728674a7SGreg Kroah-Hartman } 887728674a7SGreg Kroah-Hartman } 888728674a7SGreg Kroah-Hartman 889728674a7SGreg Kroah-Hartman /* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ 890728674a7SGreg Kroah-Hartman static void hvsi_write_worker(struct work_struct *work) 891728674a7SGreg Kroah-Hartman { 892728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = 893728674a7SGreg Kroah-Hartman container_of(work, struct hvsi_struct, writer.work); 894728674a7SGreg Kroah-Hartman unsigned long flags; 895728674a7SGreg Kroah-Hartman #ifdef DEBUG 896728674a7SGreg Kroah-Hartman static long start_j = 0; 897728674a7SGreg Kroah-Hartman 898728674a7SGreg Kroah-Hartman if (start_j == 0) 899728674a7SGreg Kroah-Hartman start_j = jiffies; 900728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 901728674a7SGreg Kroah-Hartman 902728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 903728674a7SGreg Kroah-Hartman 904728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 905728674a7SGreg Kroah-Hartman 906728674a7SGreg Kroah-Hartman if (!is_open(hp)) { 907728674a7SGreg Kroah-Hartman /* 908728674a7SGreg Kroah-Hartman * We could have a non-open connection if the service processor died 909728674a7SGreg Kroah-Hartman * while we were busily scheduling ourselves. In that case, it could 910728674a7SGreg Kroah-Hartman * be minutes before the service processor comes back, so only try 911728674a7SGreg Kroah-Hartman * again once a second. 912728674a7SGreg Kroah-Hartman */ 913728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, HZ); 914728674a7SGreg Kroah-Hartman goto out; 915728674a7SGreg Kroah-Hartman } 916728674a7SGreg Kroah-Hartman 917728674a7SGreg Kroah-Hartman hvsi_push(hp); 918728674a7SGreg Kroah-Hartman if (hp->n_outbuf > 0) 919728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, 10); 920728674a7SGreg Kroah-Hartman else { 921728674a7SGreg Kroah-Hartman #ifdef DEBUG 922728674a7SGreg Kroah-Hartman pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, 923728674a7SGreg Kroah-Hartman jiffies - start_j); 924728674a7SGreg Kroah-Hartman start_j = 0; 925728674a7SGreg Kroah-Hartman #endif /* DEBUG */ 926728674a7SGreg Kroah-Hartman wake_up_all(&hp->emptyq); 927728674a7SGreg Kroah-Hartman tty_wakeup(hp->tty); 928728674a7SGreg Kroah-Hartman } 929728674a7SGreg Kroah-Hartman 930728674a7SGreg Kroah-Hartman out: 931728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 932728674a7SGreg Kroah-Hartman } 933728674a7SGreg Kroah-Hartman 934728674a7SGreg Kroah-Hartman static int hvsi_write_room(struct tty_struct *tty) 935728674a7SGreg Kroah-Hartman { 936728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 937728674a7SGreg Kroah-Hartman 938728674a7SGreg Kroah-Hartman return N_OUTBUF - hp->n_outbuf; 939728674a7SGreg Kroah-Hartman } 940728674a7SGreg Kroah-Hartman 941728674a7SGreg Kroah-Hartman static int hvsi_chars_in_buffer(struct tty_struct *tty) 942728674a7SGreg Kroah-Hartman { 943728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 944728674a7SGreg Kroah-Hartman 945728674a7SGreg Kroah-Hartman return hp->n_outbuf; 946728674a7SGreg Kroah-Hartman } 947728674a7SGreg Kroah-Hartman 948728674a7SGreg Kroah-Hartman static int hvsi_write(struct tty_struct *tty, 949728674a7SGreg Kroah-Hartman const unsigned char *buf, int count) 950728674a7SGreg Kroah-Hartman { 951728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 952728674a7SGreg Kroah-Hartman const char *source = buf; 953728674a7SGreg Kroah-Hartman unsigned long flags; 954728674a7SGreg Kroah-Hartman int total = 0; 955728674a7SGreg Kroah-Hartman int origcount = count; 956728674a7SGreg Kroah-Hartman 957728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 958728674a7SGreg Kroah-Hartman 959728674a7SGreg Kroah-Hartman pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 960728674a7SGreg Kroah-Hartman 961728674a7SGreg Kroah-Hartman if (!is_open(hp)) { 962728674a7SGreg Kroah-Hartman /* we're either closing or not yet open; don't accept data */ 963728674a7SGreg Kroah-Hartman pr_debug("%s: not open\n", __func__); 964728674a7SGreg Kroah-Hartman goto out; 965728674a7SGreg Kroah-Hartman } 966728674a7SGreg Kroah-Hartman 967728674a7SGreg Kroah-Hartman /* 968728674a7SGreg Kroah-Hartman * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf 969728674a7SGreg Kroah-Hartman * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls 970728674a7SGreg Kroah-Hartman * will see there is no room in outbuf and return. 971728674a7SGreg Kroah-Hartman */ 972728674a7SGreg Kroah-Hartman while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { 973728674a7SGreg Kroah-Hartman int chunksize = min(count, hvsi_write_room(hp->tty)); 974728674a7SGreg Kroah-Hartman 975728674a7SGreg Kroah-Hartman BUG_ON(hp->n_outbuf < 0); 976728674a7SGreg Kroah-Hartman memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); 977728674a7SGreg Kroah-Hartman hp->n_outbuf += chunksize; 978728674a7SGreg Kroah-Hartman 979728674a7SGreg Kroah-Hartman total += chunksize; 980728674a7SGreg Kroah-Hartman source += chunksize; 981728674a7SGreg Kroah-Hartman count -= chunksize; 982728674a7SGreg Kroah-Hartman hvsi_push(hp); 983728674a7SGreg Kroah-Hartman } 984728674a7SGreg Kroah-Hartman 985728674a7SGreg Kroah-Hartman if (hp->n_outbuf > 0) { 986728674a7SGreg Kroah-Hartman /* 987728674a7SGreg Kroah-Hartman * we weren't able to write it all to the hypervisor. 988728674a7SGreg Kroah-Hartman * schedule another push attempt. 989728674a7SGreg Kroah-Hartman */ 990728674a7SGreg Kroah-Hartman schedule_delayed_work(&hp->writer, 10); 991728674a7SGreg Kroah-Hartman } 992728674a7SGreg Kroah-Hartman 993728674a7SGreg Kroah-Hartman out: 994728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 995728674a7SGreg Kroah-Hartman 996728674a7SGreg Kroah-Hartman if (total != origcount) 997728674a7SGreg Kroah-Hartman pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, 998728674a7SGreg Kroah-Hartman total); 999728674a7SGreg Kroah-Hartman 1000728674a7SGreg Kroah-Hartman return total; 1001728674a7SGreg Kroah-Hartman } 1002728674a7SGreg Kroah-Hartman 1003728674a7SGreg Kroah-Hartman /* 1004728674a7SGreg Kroah-Hartman * I have never seen throttle or unthrottle called, so this little throttle 1005728674a7SGreg Kroah-Hartman * buffering scheme may or may not work. 1006728674a7SGreg Kroah-Hartman */ 1007728674a7SGreg Kroah-Hartman static void hvsi_throttle(struct tty_struct *tty) 1008728674a7SGreg Kroah-Hartman { 1009728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1010728674a7SGreg Kroah-Hartman 1011728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 1012728674a7SGreg Kroah-Hartman 1013728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); 1014728674a7SGreg Kroah-Hartman } 1015728674a7SGreg Kroah-Hartman 1016728674a7SGreg Kroah-Hartman static void hvsi_unthrottle(struct tty_struct *tty) 1017728674a7SGreg Kroah-Hartman { 1018728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1019728674a7SGreg Kroah-Hartman unsigned long flags; 1020728674a7SGreg Kroah-Hartman int shouldflip = 0; 1021728674a7SGreg Kroah-Hartman 1022728674a7SGreg Kroah-Hartman pr_debug("%s\n", __func__); 1023728674a7SGreg Kroah-Hartman 1024728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 1025728674a7SGreg Kroah-Hartman if (hp->n_throttle) { 1026728674a7SGreg Kroah-Hartman hvsi_send_overflow(hp); 1027728674a7SGreg Kroah-Hartman shouldflip = 1; 1028728674a7SGreg Kroah-Hartman } 1029728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 1030728674a7SGreg Kroah-Hartman 1031728674a7SGreg Kroah-Hartman if (shouldflip) 1032728674a7SGreg Kroah-Hartman tty_flip_buffer_push(hp->tty); 1033728674a7SGreg Kroah-Hartman 1034728674a7SGreg Kroah-Hartman h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 1035728674a7SGreg Kroah-Hartman } 1036728674a7SGreg Kroah-Hartman 103760b33c13SAlan Cox static int hvsi_tiocmget(struct tty_struct *tty) 1038728674a7SGreg Kroah-Hartman { 1039728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1040728674a7SGreg Kroah-Hartman 1041728674a7SGreg Kroah-Hartman hvsi_get_mctrl(hp); 1042728674a7SGreg Kroah-Hartman return hp->mctrl; 1043728674a7SGreg Kroah-Hartman } 1044728674a7SGreg Kroah-Hartman 104520b9d177SAlan Cox static int hvsi_tiocmset(struct tty_struct *tty, 1046728674a7SGreg Kroah-Hartman unsigned int set, unsigned int clear) 1047728674a7SGreg Kroah-Hartman { 1048728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = tty->driver_data; 1049728674a7SGreg Kroah-Hartman unsigned long flags; 1050728674a7SGreg Kroah-Hartman uint16_t new_mctrl; 1051728674a7SGreg Kroah-Hartman 1052728674a7SGreg Kroah-Hartman /* we can only alter DTR */ 1053728674a7SGreg Kroah-Hartman clear &= TIOCM_DTR; 1054728674a7SGreg Kroah-Hartman set &= TIOCM_DTR; 1055728674a7SGreg Kroah-Hartman 1056728674a7SGreg Kroah-Hartman spin_lock_irqsave(&hp->lock, flags); 1057728674a7SGreg Kroah-Hartman 1058728674a7SGreg Kroah-Hartman new_mctrl = (hp->mctrl & ~clear) | set; 1059728674a7SGreg Kroah-Hartman 1060728674a7SGreg Kroah-Hartman if (hp->mctrl != new_mctrl) { 1061728674a7SGreg Kroah-Hartman hvsi_set_mctrl(hp, new_mctrl); 1062728674a7SGreg Kroah-Hartman hp->mctrl = new_mctrl; 1063728674a7SGreg Kroah-Hartman } 1064728674a7SGreg Kroah-Hartman spin_unlock_irqrestore(&hp->lock, flags); 1065728674a7SGreg Kroah-Hartman 1066728674a7SGreg Kroah-Hartman return 0; 1067728674a7SGreg Kroah-Hartman } 1068728674a7SGreg Kroah-Hartman 1069728674a7SGreg Kroah-Hartman 1070728674a7SGreg Kroah-Hartman static const struct tty_operations hvsi_ops = { 1071728674a7SGreg Kroah-Hartman .open = hvsi_open, 1072728674a7SGreg Kroah-Hartman .close = hvsi_close, 1073728674a7SGreg Kroah-Hartman .write = hvsi_write, 1074728674a7SGreg Kroah-Hartman .hangup = hvsi_hangup, 1075728674a7SGreg Kroah-Hartman .write_room = hvsi_write_room, 1076728674a7SGreg Kroah-Hartman .chars_in_buffer = hvsi_chars_in_buffer, 1077728674a7SGreg Kroah-Hartman .throttle = hvsi_throttle, 1078728674a7SGreg Kroah-Hartman .unthrottle = hvsi_unthrottle, 1079728674a7SGreg Kroah-Hartman .tiocmget = hvsi_tiocmget, 1080728674a7SGreg Kroah-Hartman .tiocmset = hvsi_tiocmset, 1081728674a7SGreg Kroah-Hartman }; 1082728674a7SGreg Kroah-Hartman 1083728674a7SGreg Kroah-Hartman static int __init hvsi_init(void) 1084728674a7SGreg Kroah-Hartman { 1085728674a7SGreg Kroah-Hartman int i; 1086728674a7SGreg Kroah-Hartman 1087728674a7SGreg Kroah-Hartman hvsi_driver = alloc_tty_driver(hvsi_count); 1088728674a7SGreg Kroah-Hartman if (!hvsi_driver) 1089728674a7SGreg Kroah-Hartman return -ENOMEM; 1090728674a7SGreg Kroah-Hartman 1091728674a7SGreg Kroah-Hartman hvsi_driver->owner = THIS_MODULE; 1092728674a7SGreg Kroah-Hartman hvsi_driver->driver_name = "hvsi"; 1093728674a7SGreg Kroah-Hartman hvsi_driver->name = "hvsi"; 1094728674a7SGreg Kroah-Hartman hvsi_driver->major = HVSI_MAJOR; 1095728674a7SGreg Kroah-Hartman hvsi_driver->minor_start = HVSI_MINOR; 1096728674a7SGreg Kroah-Hartman hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; 1097728674a7SGreg Kroah-Hartman hvsi_driver->init_termios = tty_std_termios; 1098728674a7SGreg Kroah-Hartman hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; 1099728674a7SGreg Kroah-Hartman hvsi_driver->init_termios.c_ispeed = 9600; 1100728674a7SGreg Kroah-Hartman hvsi_driver->init_termios.c_ospeed = 9600; 1101728674a7SGreg Kroah-Hartman hvsi_driver->flags = TTY_DRIVER_REAL_RAW; 1102728674a7SGreg Kroah-Hartman tty_set_operations(hvsi_driver, &hvsi_ops); 1103728674a7SGreg Kroah-Hartman 1104728674a7SGreg Kroah-Hartman for (i=0; i < hvsi_count; i++) { 1105728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = &hvsi_ports[i]; 1106728674a7SGreg Kroah-Hartman int ret = 1; 1107728674a7SGreg Kroah-Hartman 1108728674a7SGreg Kroah-Hartman ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); 1109728674a7SGreg Kroah-Hartman if (ret) 1110728674a7SGreg Kroah-Hartman printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", 1111728674a7SGreg Kroah-Hartman hp->virq, ret); 1112728674a7SGreg Kroah-Hartman } 1113728674a7SGreg Kroah-Hartman hvsi_wait = wait_for_state; /* irqs active now */ 1114728674a7SGreg Kroah-Hartman 1115728674a7SGreg Kroah-Hartman if (tty_register_driver(hvsi_driver)) 1116728674a7SGreg Kroah-Hartman panic("Couldn't register hvsi console driver\n"); 1117728674a7SGreg Kroah-Hartman 1118728674a7SGreg Kroah-Hartman printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); 1119728674a7SGreg Kroah-Hartman 1120728674a7SGreg Kroah-Hartman return 0; 1121728674a7SGreg Kroah-Hartman } 1122728674a7SGreg Kroah-Hartman device_initcall(hvsi_init); 1123728674a7SGreg Kroah-Hartman 1124728674a7SGreg Kroah-Hartman /***** console (not tty) code: *****/ 1125728674a7SGreg Kroah-Hartman 1126728674a7SGreg Kroah-Hartman static void hvsi_console_print(struct console *console, const char *buf, 1127728674a7SGreg Kroah-Hartman unsigned int count) 1128728674a7SGreg Kroah-Hartman { 1129728674a7SGreg Kroah-Hartman struct hvsi_struct *hp = &hvsi_ports[console->index]; 1130728674a7SGreg Kroah-Hartman char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; 1131728674a7SGreg Kroah-Hartman unsigned int i = 0, n = 0; 1132728674a7SGreg Kroah-Hartman int ret, donecr = 0; 1133728674a7SGreg Kroah-Hartman 1134728674a7SGreg Kroah-Hartman mb(); 1135728674a7SGreg Kroah-Hartman if (!is_open(hp)) 1136728674a7SGreg Kroah-Hartman return; 1137728674a7SGreg Kroah-Hartman 1138728674a7SGreg Kroah-Hartman /* 1139728674a7SGreg Kroah-Hartman * ugh, we have to translate LF -> CRLF ourselves, in place. 1140728674a7SGreg Kroah-Hartman * copied from hvc_console.c: 1141728674a7SGreg Kroah-Hartman */ 1142728674a7SGreg Kroah-Hartman while (count > 0 || i > 0) { 1143728674a7SGreg Kroah-Hartman if (count > 0 && i < sizeof(c)) { 1144728674a7SGreg Kroah-Hartman if (buf[n] == '\n' && !donecr) { 1145728674a7SGreg Kroah-Hartman c[i++] = '\r'; 1146728674a7SGreg Kroah-Hartman donecr = 1; 1147728674a7SGreg Kroah-Hartman } else { 1148728674a7SGreg Kroah-Hartman c[i++] = buf[n++]; 1149728674a7SGreg Kroah-Hartman donecr = 0; 1150728674a7SGreg Kroah-Hartman --count; 1151728674a7SGreg Kroah-Hartman } 1152728674a7SGreg Kroah-Hartman } else { 1153728674a7SGreg Kroah-Hartman ret = hvsi_put_chars(hp, c, i); 1154728674a7SGreg Kroah-Hartman if (ret < 0) 1155728674a7SGreg Kroah-Hartman i = 0; 1156728674a7SGreg Kroah-Hartman i -= ret; 1157728674a7SGreg Kroah-Hartman } 1158728674a7SGreg Kroah-Hartman } 1159728674a7SGreg Kroah-Hartman } 1160728674a7SGreg Kroah-Hartman 1161728674a7SGreg Kroah-Hartman static struct tty_driver *hvsi_console_device(struct console *console, 1162728674a7SGreg Kroah-Hartman int *index) 1163728674a7SGreg Kroah-Hartman { 1164728674a7SGreg Kroah-Hartman *index = console->index; 1165728674a7SGreg Kroah-Hartman return hvsi_driver; 1166728674a7SGreg Kroah-Hartman } 1167728674a7SGreg Kroah-Hartman 1168728674a7SGreg Kroah-Hartman static int __init hvsi_console_setup(struct console *console, char *options) 1169728674a7SGreg Kroah-Hartman { 1170728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 1171728674a7SGreg Kroah-Hartman int ret; 1172728674a7SGreg Kroah-Hartman 1173728674a7SGreg Kroah-Hartman if (console->index < 0 || console->index >= hvsi_count) 1174728674a7SGreg Kroah-Hartman return -1; 1175728674a7SGreg Kroah-Hartman hp = &hvsi_ports[console->index]; 1176728674a7SGreg Kroah-Hartman 1177728674a7SGreg Kroah-Hartman /* give the FSP a chance to change the baud rate when we re-open */ 1178728674a7SGreg Kroah-Hartman hvsi_close_protocol(hp); 1179728674a7SGreg Kroah-Hartman 1180728674a7SGreg Kroah-Hartman ret = hvsi_handshake(hp); 1181728674a7SGreg Kroah-Hartman if (ret < 0) 1182728674a7SGreg Kroah-Hartman return ret; 1183728674a7SGreg Kroah-Hartman 1184728674a7SGreg Kroah-Hartman ret = hvsi_get_mctrl(hp); 1185728674a7SGreg Kroah-Hartman if (ret < 0) 1186728674a7SGreg Kroah-Hartman return ret; 1187728674a7SGreg Kroah-Hartman 1188728674a7SGreg Kroah-Hartman ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 1189728674a7SGreg Kroah-Hartman if (ret < 0) 1190728674a7SGreg Kroah-Hartman return ret; 1191728674a7SGreg Kroah-Hartman 1192728674a7SGreg Kroah-Hartman hp->flags |= HVSI_CONSOLE; 1193728674a7SGreg Kroah-Hartman 1194728674a7SGreg Kroah-Hartman return 0; 1195728674a7SGreg Kroah-Hartman } 1196728674a7SGreg Kroah-Hartman 1197728674a7SGreg Kroah-Hartman static struct console hvsi_console = { 1198728674a7SGreg Kroah-Hartman .name = "hvsi", 1199728674a7SGreg Kroah-Hartman .write = hvsi_console_print, 1200728674a7SGreg Kroah-Hartman .device = hvsi_console_device, 1201728674a7SGreg Kroah-Hartman .setup = hvsi_console_setup, 1202728674a7SGreg Kroah-Hartman .flags = CON_PRINTBUFFER, 1203728674a7SGreg Kroah-Hartman .index = -1, 1204728674a7SGreg Kroah-Hartman }; 1205728674a7SGreg Kroah-Hartman 1206728674a7SGreg Kroah-Hartman static int __init hvsi_console_init(void) 1207728674a7SGreg Kroah-Hartman { 1208728674a7SGreg Kroah-Hartman struct device_node *vty; 1209728674a7SGreg Kroah-Hartman 1210728674a7SGreg Kroah-Hartman hvsi_wait = poll_for_state; /* no irqs yet; must poll */ 1211728674a7SGreg Kroah-Hartman 1212728674a7SGreg Kroah-Hartman /* search device tree for vty nodes */ 1213728674a7SGreg Kroah-Hartman for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); 1214728674a7SGreg Kroah-Hartman vty != NULL; 1215728674a7SGreg Kroah-Hartman vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { 1216728674a7SGreg Kroah-Hartman struct hvsi_struct *hp; 1217728674a7SGreg Kroah-Hartman const uint32_t *vtermno, *irq; 1218728674a7SGreg Kroah-Hartman 1219728674a7SGreg Kroah-Hartman vtermno = of_get_property(vty, "reg", NULL); 1220728674a7SGreg Kroah-Hartman irq = of_get_property(vty, "interrupts", NULL); 1221728674a7SGreg Kroah-Hartman if (!vtermno || !irq) 1222728674a7SGreg Kroah-Hartman continue; 1223728674a7SGreg Kroah-Hartman 1224728674a7SGreg Kroah-Hartman if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { 1225728674a7SGreg Kroah-Hartman of_node_put(vty); 1226728674a7SGreg Kroah-Hartman break; 1227728674a7SGreg Kroah-Hartman } 1228728674a7SGreg Kroah-Hartman 1229728674a7SGreg Kroah-Hartman hp = &hvsi_ports[hvsi_count]; 1230728674a7SGreg Kroah-Hartman INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); 1231728674a7SGreg Kroah-Hartman INIT_WORK(&hp->handshaker, hvsi_handshaker); 1232728674a7SGreg Kroah-Hartman init_waitqueue_head(&hp->emptyq); 1233728674a7SGreg Kroah-Hartman init_waitqueue_head(&hp->stateq); 1234728674a7SGreg Kroah-Hartman spin_lock_init(&hp->lock); 1235728674a7SGreg Kroah-Hartman hp->index = hvsi_count; 1236728674a7SGreg Kroah-Hartman hp->inbuf_end = hp->inbuf; 1237728674a7SGreg Kroah-Hartman hp->state = HVSI_CLOSED; 1238728674a7SGreg Kroah-Hartman hp->vtermno = *vtermno; 1239728674a7SGreg Kroah-Hartman hp->virq = irq_create_mapping(NULL, irq[0]); 1240728674a7SGreg Kroah-Hartman if (hp->virq == NO_IRQ) { 1241728674a7SGreg Kroah-Hartman printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", 1242728674a7SGreg Kroah-Hartman __func__, irq[0]); 1243728674a7SGreg Kroah-Hartman continue; 1244728674a7SGreg Kroah-Hartman } 1245728674a7SGreg Kroah-Hartman 1246728674a7SGreg Kroah-Hartman hvsi_count++; 1247728674a7SGreg Kroah-Hartman } 1248728674a7SGreg Kroah-Hartman 1249728674a7SGreg Kroah-Hartman if (hvsi_count) 1250728674a7SGreg Kroah-Hartman register_console(&hvsi_console); 1251728674a7SGreg Kroah-Hartman return 0; 1252728674a7SGreg Kroah-Hartman } 1253728674a7SGreg Kroah-Hartman console_initcall(hvsi_console_init); 1254