162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS 762306a36Sopenharmony_ci * and the service processor on IBM pSeries servers. On these servers, there 862306a36Sopenharmony_ci * are no serial ports under the OS's control, and sometimes there is no other 962306a36Sopenharmony_ci * console available either. However, the service processor has two standard 1062306a36Sopenharmony_ci * serial ports, so this over-complicated protocol allows the OS to control 1162306a36Sopenharmony_ci * those ports by proxy. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Besides data, the procotol supports the reading/writing of the serial 1462306a36Sopenharmony_ci * port's DTR line, and the reading of the CD line. This is to allow the OS to 1562306a36Sopenharmony_ci * control a modem attached to the service processor's serial port. Note that 1662306a36Sopenharmony_ci * the OS cannot change the speed of the port through this protocol. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#undef DEBUG 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/console.h> 2262306a36Sopenharmony_ci#include <linux/ctype.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/interrupt.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/major.h> 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/of_irq.h> 3062306a36Sopenharmony_ci#include <linux/spinlock.h> 3162306a36Sopenharmony_ci#include <linux/sysrq.h> 3262306a36Sopenharmony_ci#include <linux/tty.h> 3362306a36Sopenharmony_ci#include <linux/tty_flip.h> 3462306a36Sopenharmony_ci#include <asm/hvcall.h> 3562306a36Sopenharmony_ci#include <asm/hvconsole.h> 3662306a36Sopenharmony_ci#include <linux/uaccess.h> 3762306a36Sopenharmony_ci#include <asm/vio.h> 3862306a36Sopenharmony_ci#include <asm/param.h> 3962306a36Sopenharmony_ci#include <asm/hvsi.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define HVSI_MAJOR 229 4262306a36Sopenharmony_ci#define HVSI_MINOR 128 4362306a36Sopenharmony_ci#define MAX_NR_HVSI_CONSOLES 4 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define HVSI_TIMEOUT (5*HZ) 4662306a36Sopenharmony_ci#define HVSI_VERSION 1 4762306a36Sopenharmony_ci#define HVSI_MAX_PACKET 256 4862306a36Sopenharmony_ci#define HVSI_MAX_READ 16 4962306a36Sopenharmony_ci#define HVSI_MAX_OUTGOING_DATA 12 5062306a36Sopenharmony_ci#define N_OUTBUF 12 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * we pass data via two 8-byte registers, so we would like our char arrays 5462306a36Sopenharmony_ci * properly aligned for those loads. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct hvsi_struct { 5962306a36Sopenharmony_ci struct tty_port port; 6062306a36Sopenharmony_ci struct delayed_work writer; 6162306a36Sopenharmony_ci struct work_struct handshaker; 6262306a36Sopenharmony_ci wait_queue_head_t emptyq; /* woken when outbuf is emptied */ 6362306a36Sopenharmony_ci wait_queue_head_t stateq; /* woken when HVSI state changes */ 6462306a36Sopenharmony_ci spinlock_t lock; 6562306a36Sopenharmony_ci int index; 6662306a36Sopenharmony_ci uint8_t throttle_buf[128]; 6762306a36Sopenharmony_ci uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ 6862306a36Sopenharmony_ci /* inbuf is for packet reassembly. leave a little room for leftovers. */ 6962306a36Sopenharmony_ci uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; 7062306a36Sopenharmony_ci uint8_t *inbuf_end; 7162306a36Sopenharmony_ci int n_throttle; 7262306a36Sopenharmony_ci int n_outbuf; 7362306a36Sopenharmony_ci uint32_t vtermno; 7462306a36Sopenharmony_ci uint32_t virq; 7562306a36Sopenharmony_ci atomic_t seqno; /* HVSI packet sequence number */ 7662306a36Sopenharmony_ci uint16_t mctrl; 7762306a36Sopenharmony_ci uint8_t state; /* HVSI protocol state */ 7862306a36Sopenharmony_ci uint8_t flags; 7962306a36Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 8062306a36Sopenharmony_ci uint8_t sysrq; 8162306a36Sopenharmony_ci#endif /* CONFIG_MAGIC_SYSRQ */ 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_cistatic struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic struct tty_driver *hvsi_driver; 8662306a36Sopenharmony_cistatic int hvsi_count; 8762306a36Sopenharmony_cistatic int (*hvsi_wait)(struct hvsi_struct *hp, int state); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cienum HVSI_PROTOCOL_STATE { 9062306a36Sopenharmony_ci HVSI_CLOSED, 9162306a36Sopenharmony_ci HVSI_WAIT_FOR_VER_RESPONSE, 9262306a36Sopenharmony_ci HVSI_WAIT_FOR_VER_QUERY, 9362306a36Sopenharmony_ci HVSI_OPEN, 9462306a36Sopenharmony_ci HVSI_WAIT_FOR_MCTRL_RESPONSE, 9562306a36Sopenharmony_ci HVSI_FSP_DIED, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci#define HVSI_CONSOLE 0x1 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline int is_console(struct hvsi_struct *hp) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return hp->flags & HVSI_CONSOLE; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic inline int is_open(struct hvsi_struct *hp) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci /* if we're waiting for an mctrl then we're already open */ 10762306a36Sopenharmony_ci return (hp->state == HVSI_OPEN) 10862306a36Sopenharmony_ci || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic inline void print_state(struct hvsi_struct *hp) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci#ifdef DEBUG 11462306a36Sopenharmony_ci static const char *state_names[] = { 11562306a36Sopenharmony_ci "HVSI_CLOSED", 11662306a36Sopenharmony_ci "HVSI_WAIT_FOR_VER_RESPONSE", 11762306a36Sopenharmony_ci "HVSI_WAIT_FOR_VER_QUERY", 11862306a36Sopenharmony_ci "HVSI_OPEN", 11962306a36Sopenharmony_ci "HVSI_WAIT_FOR_MCTRL_RESPONSE", 12062306a36Sopenharmony_ci "HVSI_FSP_DIED", 12162306a36Sopenharmony_ci }; 12262306a36Sopenharmony_ci const char *name = (hp->state < ARRAY_SIZE(state_names)) 12362306a36Sopenharmony_ci ? state_names[hp->state] : "UNKNOWN"; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci pr_debug("hvsi%i: state = %s\n", hp->index, name); 12662306a36Sopenharmony_ci#endif /* DEBUG */ 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic inline void __set_state(struct hvsi_struct *hp, int state) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci hp->state = state; 13262306a36Sopenharmony_ci print_state(hp); 13362306a36Sopenharmony_ci wake_up_all(&hp->stateq); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void set_state(struct hvsi_struct *hp, int state) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci unsigned long flags; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 14162306a36Sopenharmony_ci __set_state(hp, state); 14262306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic inline int len_packet(const uint8_t *packet) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return (int)((struct hvsi_header *)packet)->len; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic inline int is_header(const uint8_t *packet) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct hvsi_header *header = (struct hvsi_header *)packet; 15362306a36Sopenharmony_ci return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) 15962306a36Sopenharmony_ci return 0; /* don't even have the packet header */ 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (hp->inbuf_end < (packet + len_packet(packet))) 16262306a36Sopenharmony_ci return 0; /* don't have the rest of the packet */ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 1; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* shift remaining bytes in packetbuf down */ 16862306a36Sopenharmony_cistatic void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int remaining = (int)(hp->inbuf_end - read_to); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci pr_debug("%s: %i chars remain\n", __func__, remaining); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (read_to != hp->inbuf) 17562306a36Sopenharmony_ci memmove(hp->inbuf, read_to, remaining); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hp->inbuf_end = hp->inbuf + remaining; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#ifdef DEBUG 18162306a36Sopenharmony_ci#define dbg_dump_packet(packet) dump_packet(packet) 18262306a36Sopenharmony_ci#define dbg_dump_hex(data, len) dump_hex(data, len) 18362306a36Sopenharmony_ci#else 18462306a36Sopenharmony_ci#define dbg_dump_packet(packet) do { } while (0) 18562306a36Sopenharmony_ci#define dbg_dump_hex(data, len) do { } while (0) 18662306a36Sopenharmony_ci#endif 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void dump_hex(const uint8_t *data, int len) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int i; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci printk(" "); 19362306a36Sopenharmony_ci for (i=0; i < len; i++) 19462306a36Sopenharmony_ci printk("%.2x", data[i]); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci printk("\n "); 19762306a36Sopenharmony_ci for (i=0; i < len; i++) { 19862306a36Sopenharmony_ci if (isprint(data[i])) 19962306a36Sopenharmony_ci printk("%c", data[i]); 20062306a36Sopenharmony_ci else 20162306a36Sopenharmony_ci printk("."); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci printk("\n"); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void dump_packet(uint8_t *packet) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct hvsi_header *header = (struct hvsi_header *)packet; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, 21162306a36Sopenharmony_ci header->seqno); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci dump_hex(packet, header->len); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int hvsi_read(struct hvsi_struct *hp, char *buf, int count) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci unsigned long got; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci got = hvc_get_chars(hp->vtermno, buf, count); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return got; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, 22662306a36Sopenharmony_ci struct tty_struct *tty, struct hvsi_struct **to_handshake) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct hvsi_control *header = (struct hvsi_control *)packet; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (be16_to_cpu(header->verb)) { 23162306a36Sopenharmony_ci case VSV_MODEM_CTL_UPDATE: 23262306a36Sopenharmony_ci if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) { 23362306a36Sopenharmony_ci /* CD went away; no more connection */ 23462306a36Sopenharmony_ci pr_debug("hvsi%i: CD dropped\n", hp->index); 23562306a36Sopenharmony_ci hp->mctrl &= TIOCM_CD; 23662306a36Sopenharmony_ci if (tty && !C_CLOCAL(tty)) 23762306a36Sopenharmony_ci tty_hangup(tty); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci case VSV_CLOSE_PROTOCOL: 24162306a36Sopenharmony_ci pr_debug("hvsi%i: service processor came back\n", hp->index); 24262306a36Sopenharmony_ci if (hp->state != HVSI_CLOSED) { 24362306a36Sopenharmony_ci *to_handshake = hp; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci default: 24762306a36Sopenharmony_ci printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", 24862306a36Sopenharmony_ci hp->index); 24962306a36Sopenharmony_ci dump_packet(packet); 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; 25762306a36Sopenharmony_ci uint32_t mctrl_word; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci switch (hp->state) { 26062306a36Sopenharmony_ci case HVSI_WAIT_FOR_VER_RESPONSE: 26162306a36Sopenharmony_ci __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci case HVSI_WAIT_FOR_MCTRL_RESPONSE: 26462306a36Sopenharmony_ci hp->mctrl = 0; 26562306a36Sopenharmony_ci mctrl_word = be32_to_cpu(resp->u.mctrl_word); 26662306a36Sopenharmony_ci if (mctrl_word & HVSI_TSDTR) 26762306a36Sopenharmony_ci hp->mctrl |= TIOCM_DTR; 26862306a36Sopenharmony_ci if (mctrl_word & HVSI_TSCD) 26962306a36Sopenharmony_ci hp->mctrl |= TIOCM_CD; 27062306a36Sopenharmony_ci __set_state(hp, HVSI_OPEN); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci default: 27362306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); 27462306a36Sopenharmony_ci dump_packet(packet); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* respond to service processor's version query */ 28062306a36Sopenharmony_cistatic int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct hvsi_query_response packet __ALIGNED__; 28362306a36Sopenharmony_ci int wrote; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; 28662306a36Sopenharmony_ci packet.hdr.len = sizeof(struct hvsi_query_response); 28762306a36Sopenharmony_ci packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 28862306a36Sopenharmony_ci packet.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); 28962306a36Sopenharmony_ci packet.u.version = HVSI_VERSION; 29062306a36Sopenharmony_ci packet.query_seqno = cpu_to_be16(query_seqno+1); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 29362306a36Sopenharmony_ci dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 29662306a36Sopenharmony_ci if (wrote != packet.hdr.len) { 29762306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: couldn't send query response!\n", 29862306a36Sopenharmony_ci hp->index); 29962306a36Sopenharmony_ci return -EIO; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct hvsi_query *query = (struct hvsi_query *)packet; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci switch (hp->state) { 31062306a36Sopenharmony_ci case HVSI_WAIT_FOR_VER_QUERY: 31162306a36Sopenharmony_ci hvsi_version_respond(hp, be16_to_cpu(query->hdr.seqno)); 31262306a36Sopenharmony_ci __set_state(hp, HVSI_OPEN); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci default: 31562306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); 31662306a36Sopenharmony_ci dump_packet(packet); 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci for (i=0; i < len; i++) { 32662306a36Sopenharmony_ci char c = buf[i]; 32762306a36Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 32862306a36Sopenharmony_ci if (c == '\0') { 32962306a36Sopenharmony_ci hp->sysrq = 1; 33062306a36Sopenharmony_ci continue; 33162306a36Sopenharmony_ci } else if (hp->sysrq) { 33262306a36Sopenharmony_ci handle_sysrq(c); 33362306a36Sopenharmony_ci hp->sysrq = 0; 33462306a36Sopenharmony_ci continue; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci#endif /* CONFIG_MAGIC_SYSRQ */ 33762306a36Sopenharmony_ci tty_insert_flip_char(&hp->port, c, 0); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* 34262306a36Sopenharmony_ci * We could get 252 bytes of data at once here. But the tty layer only 34362306a36Sopenharmony_ci * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow 34462306a36Sopenharmony_ci * it. Accordingly we won't send more than 128 bytes at a time to the flip 34562306a36Sopenharmony_ci * buffer, which will give the tty buffer a chance to throttle us. Should the 34662306a36Sopenharmony_ci * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be 34762306a36Sopenharmony_ci * revisited. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci#define TTY_THRESHOLD_THROTTLE 128 35062306a36Sopenharmony_cistatic bool hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci const struct hvsi_header *header = (const struct hvsi_header *)packet; 35362306a36Sopenharmony_ci const uint8_t *data = packet + sizeof(struct hvsi_header); 35462306a36Sopenharmony_ci int datalen = header->len - sizeof(struct hvsi_header); 35562306a36Sopenharmony_ci int overflow = datalen - TTY_THRESHOLD_THROTTLE; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (datalen == 0) 36062306a36Sopenharmony_ci return false; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (overflow > 0) { 36362306a36Sopenharmony_ci pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); 36462306a36Sopenharmony_ci datalen = TTY_THRESHOLD_THROTTLE; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci hvsi_insert_chars(hp, data, datalen); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (overflow > 0) { 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * we still have more data to deliver, so we need to save off the 37262306a36Sopenharmony_ci * overflow and send it later 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci pr_debug("%s: deferring overflow\n", __func__); 37562306a36Sopenharmony_ci memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); 37662306a36Sopenharmony_ci hp->n_throttle = overflow; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return true; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* 38362306a36Sopenharmony_ci * Returns true/false indicating data successfully read from hypervisor. 38462306a36Sopenharmony_ci * Used both to get packets for tty connections and to advance the state 38562306a36Sopenharmony_ci * machine during console handshaking (in which case tty = NULL and we ignore 38662306a36Sopenharmony_ci * incoming data). 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cistatic int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, 38962306a36Sopenharmony_ci struct hvsi_struct **handshake) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci uint8_t *packet = hp->inbuf; 39262306a36Sopenharmony_ci int chunklen; 39362306a36Sopenharmony_ci bool flip = false; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci *handshake = NULL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); 39862306a36Sopenharmony_ci if (chunklen == 0) { 39962306a36Sopenharmony_ci pr_debug("%s: 0-length read\n", __func__); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci pr_debug("%s: got %i bytes\n", __func__, chunklen); 40462306a36Sopenharmony_ci dbg_dump_hex(hp->inbuf_end, chunklen); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci hp->inbuf_end += chunklen; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* handle all completed packets */ 40962306a36Sopenharmony_ci while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { 41062306a36Sopenharmony_ci struct hvsi_header *header = (struct hvsi_header *)packet; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!is_header(packet)) { 41362306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); 41462306a36Sopenharmony_ci /* skip bytes until we find a header or run out of data */ 41562306a36Sopenharmony_ci while ((packet < hp->inbuf_end) && (!is_header(packet))) 41662306a36Sopenharmony_ci packet++; 41762306a36Sopenharmony_ci continue; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci pr_debug("%s: handling %i-byte packet\n", __func__, 42162306a36Sopenharmony_ci len_packet(packet)); 42262306a36Sopenharmony_ci dbg_dump_packet(packet); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci switch (header->type) { 42562306a36Sopenharmony_ci case VS_DATA_PACKET_HEADER: 42662306a36Sopenharmony_ci if (!is_open(hp)) 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci flip = hvsi_recv_data(hp, packet); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case VS_CONTROL_PACKET_HEADER: 43162306a36Sopenharmony_ci hvsi_recv_control(hp, packet, tty, handshake); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case VS_QUERY_RESPONSE_PACKET_HEADER: 43462306a36Sopenharmony_ci hvsi_recv_response(hp, packet); 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci case VS_QUERY_PACKET_HEADER: 43762306a36Sopenharmony_ci hvsi_recv_query(hp, packet); 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci default: 44062306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", 44162306a36Sopenharmony_ci hp->index, header->type); 44262306a36Sopenharmony_ci dump_packet(packet); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci packet += len_packet(packet); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (*handshake) { 44962306a36Sopenharmony_ci pr_debug("%s: handshake\n", __func__); 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci compact_inbuf(hp, packet); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (flip) 45762306a36Sopenharmony_ci tty_flip_buffer_push(&hp->port); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return 1; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void hvsi_send_overflow(struct hvsi_struct *hp) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci pr_debug("%s: delivering %i bytes overflow\n", __func__, 46562306a36Sopenharmony_ci hp->n_throttle); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); 46862306a36Sopenharmony_ci hp->n_throttle = 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * must get all pending data because we only get an irq on empty->non-empty 47362306a36Sopenharmony_ci * transition 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_cistatic irqreturn_t hvsi_interrupt(int irq, void *arg) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct hvsi_struct *hp = (struct hvsi_struct *)arg; 47862306a36Sopenharmony_ci struct hvsi_struct *handshake; 47962306a36Sopenharmony_ci struct tty_struct *tty; 48062306a36Sopenharmony_ci unsigned long flags; 48162306a36Sopenharmony_ci int again = 1; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pr_debug("%s\n", __func__); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci tty = tty_port_tty_get(&hp->port); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci while (again) { 48862306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 48962306a36Sopenharmony_ci again = hvsi_load_chunk(hp, tty, &handshake); 49062306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (handshake) { 49362306a36Sopenharmony_ci pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); 49462306a36Sopenharmony_ci schedule_work(&handshake->handshaker); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 49962306a36Sopenharmony_ci if (tty && hp->n_throttle && !tty_throttled(tty)) { 50062306a36Sopenharmony_ci /* we weren't hung up and we weren't throttled, so we can 50162306a36Sopenharmony_ci * deliver the rest now */ 50262306a36Sopenharmony_ci hvsi_send_overflow(hp); 50362306a36Sopenharmony_ci tty_flip_buffer_push(&hp->port); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci tty_kref_put(tty); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return IRQ_HANDLED; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* for boot console, before the irq handler is running */ 51362306a36Sopenharmony_cistatic int __init poll_for_state(struct hvsi_struct *hp, int state) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci for (;;) { 51862306a36Sopenharmony_ci hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (hp->state == state) 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mdelay(5); 52462306a36Sopenharmony_ci if (time_after(jiffies, end_jiffies)) 52562306a36Sopenharmony_ci return -EIO; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* wait for irq handler to change our state */ 53062306a36Sopenharmony_cistatic int wait_for_state(struct hvsi_struct *hp, int state) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci int ret = 0; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) 53562306a36Sopenharmony_ci ret = -EIO; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return ret; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int hvsi_query(struct hvsi_struct *hp, uint16_t verb) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct hvsi_query packet __ALIGNED__; 54362306a36Sopenharmony_ci int wrote; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci packet.hdr.type = VS_QUERY_PACKET_HEADER; 54662306a36Sopenharmony_ci packet.hdr.len = sizeof(struct hvsi_query); 54762306a36Sopenharmony_ci packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 54862306a36Sopenharmony_ci packet.verb = cpu_to_be16(verb); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 55162306a36Sopenharmony_ci dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 55462306a36Sopenharmony_ci if (wrote != packet.hdr.len) { 55562306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, 55662306a36Sopenharmony_ci wrote); 55762306a36Sopenharmony_ci return -EIO; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int hvsi_get_mctrl(struct hvsi_struct *hp) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci int ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); 56862306a36Sopenharmony_ci hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ret = hvsi_wait(hp, HVSI_OPEN); 57162306a36Sopenharmony_ci if (ret < 0) { 57262306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); 57362306a36Sopenharmony_ci set_state(hp, HVSI_OPEN); 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* note that we can only set DTR */ 58362306a36Sopenharmony_cistatic int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct hvsi_control packet __ALIGNED__; 58662306a36Sopenharmony_ci int wrote; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci packet.hdr.type = VS_CONTROL_PACKET_HEADER; 58962306a36Sopenharmony_ci packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 59062306a36Sopenharmony_ci packet.hdr.len = sizeof(struct hvsi_control); 59162306a36Sopenharmony_ci packet.verb = cpu_to_be16(VSV_SET_MODEM_CTL); 59262306a36Sopenharmony_ci packet.mask = cpu_to_be32(HVSI_TSDTR); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (mctrl & TIOCM_DTR) 59562306a36Sopenharmony_ci packet.word = cpu_to_be32(HVSI_TSDTR); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 59862306a36Sopenharmony_ci dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 60162306a36Sopenharmony_ci if (wrote != packet.hdr.len) { 60262306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); 60362306a36Sopenharmony_ci return -EIO; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void hvsi_drain_input(struct hvsi_struct *hp) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci uint8_t buf[HVSI_MAX_READ] __ALIGNED__; 61262306a36Sopenharmony_ci unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci while (time_before(end_jiffies, jiffies)) 61562306a36Sopenharmony_ci if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int hvsi_handshake(struct hvsi_struct *hp) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * We could have a CLOSE or other data waiting for us before we even try 62562306a36Sopenharmony_ci * to open; try to throw it all away so we don't get confused. (CLOSE 62662306a36Sopenharmony_ci * is the first message sent up the pipe when the FSP comes online. We 62762306a36Sopenharmony_ci * need to distinguish between "it came up a while ago and we're the first 62862306a36Sopenharmony_ci * user" and "it was just reset before it saw our handshake packet".) 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci hvsi_drain_input(hp); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); 63362306a36Sopenharmony_ci ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); 63462306a36Sopenharmony_ci if (ret < 0) { 63562306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ret = hvsi_wait(hp, HVSI_OPEN); 64062306a36Sopenharmony_ci if (ret < 0) 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic void hvsi_handshaker(struct work_struct *work) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct hvsi_struct *hp = 64962306a36Sopenharmony_ci container_of(work, struct hvsi_struct, handshaker); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (hvsi_handshake(hp) >= 0) 65262306a36Sopenharmony_ci return; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); 65562306a36Sopenharmony_ci if (is_console(hp)) { 65662306a36Sopenharmony_ci /* 65762306a36Sopenharmony_ci * ttys will re-attempt the handshake via hvsi_open, but 65862306a36Sopenharmony_ci * the console will not. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct hvsi_data packet __ALIGNED__; 66762306a36Sopenharmony_ci int ret; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci BUG_ON(count > HVSI_MAX_OUTGOING_DATA); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci packet.hdr.type = VS_DATA_PACKET_HEADER; 67262306a36Sopenharmony_ci packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 67362306a36Sopenharmony_ci packet.hdr.len = count + sizeof(struct hvsi_header); 67462306a36Sopenharmony_ci memcpy(&packet.data, buf, count); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 67762306a36Sopenharmony_ci if (ret == packet.hdr.len) { 67862306a36Sopenharmony_ci /* return the number of chars written, not the packet length */ 67962306a36Sopenharmony_ci return count; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci return ret; /* return any errors */ 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void hvsi_close_protocol(struct hvsi_struct *hp) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct hvsi_control packet __ALIGNED__; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci packet.hdr.type = VS_CONTROL_PACKET_HEADER; 68962306a36Sopenharmony_ci packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); 69062306a36Sopenharmony_ci packet.hdr.len = 6; 69162306a36Sopenharmony_ci packet.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); 69462306a36Sopenharmony_ci dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int hvsi_open(struct tty_struct *tty, struct file *filp) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct hvsi_struct *hp; 70262306a36Sopenharmony_ci unsigned long flags; 70362306a36Sopenharmony_ci int ret; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci pr_debug("%s\n", __func__); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci hp = &hvsi_ports[tty->index]; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci tty->driver_data = hp; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci mb(); 71262306a36Sopenharmony_ci if (hp->state == HVSI_FSP_DIED) 71362306a36Sopenharmony_ci return -EIO; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci tty_port_tty_set(&hp->port, tty); 71662306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 71762306a36Sopenharmony_ci hp->port.count++; 71862306a36Sopenharmony_ci atomic_set(&hp->seqno, 0); 71962306a36Sopenharmony_ci h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 72062306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (is_console(hp)) 72362306a36Sopenharmony_ci return 0; /* this has already been handshaked as the console */ 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci ret = hvsi_handshake(hp); 72662306a36Sopenharmony_ci if (ret < 0) { 72762306a36Sopenharmony_ci printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); 72862306a36Sopenharmony_ci return ret; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ret = hvsi_get_mctrl(hp); 73262306a36Sopenharmony_ci if (ret < 0) { 73362306a36Sopenharmony_ci printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); 73462306a36Sopenharmony_ci return ret; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 73862306a36Sopenharmony_ci if (ret < 0) { 73962306a36Sopenharmony_ci printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci/* wait for hvsi_write_worker to empty hp->outbuf */ 74762306a36Sopenharmony_cistatic void hvsi_flush_output(struct hvsi_struct *hp) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ 75262306a36Sopenharmony_ci cancel_delayed_work_sync(&hp->writer); 75362306a36Sopenharmony_ci flush_work(&hp->handshaker); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* 75662306a36Sopenharmony_ci * it's also possible that our timeout expired and hvsi_write_worker 75762306a36Sopenharmony_ci * didn't manage to push outbuf. poof. 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_ci hp->n_outbuf = 0; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic void hvsi_close(struct tty_struct *tty, struct file *filp) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 76562306a36Sopenharmony_ci unsigned long flags; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci pr_debug("%s\n", __func__); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (tty_hung_up_p(filp)) 77062306a36Sopenharmony_ci return; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (--hp->port.count == 0) { 77562306a36Sopenharmony_ci tty_port_tty_set(&hp->port, NULL); 77662306a36Sopenharmony_ci hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* only close down connection if it is not the console */ 77962306a36Sopenharmony_ci if (!is_console(hp)) { 78062306a36Sopenharmony_ci h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ 78162306a36Sopenharmony_ci __set_state(hp, HVSI_CLOSED); 78262306a36Sopenharmony_ci /* 78362306a36Sopenharmony_ci * any data delivered to the tty layer after this will be 78462306a36Sopenharmony_ci * discarded (except for XON/XOFF) 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ci tty->closing = 1; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* let any existing irq handlers finish. no more will start. */ 79162306a36Sopenharmony_ci synchronize_irq(hp->virq); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* hvsi_write_worker will re-schedule until outbuf is empty. */ 79462306a36Sopenharmony_ci hvsi_flush_output(hp); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* tell FSP to stop sending data */ 79762306a36Sopenharmony_ci hvsi_close_protocol(hp); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * drain anything FSP is still in the middle of sending, and let 80162306a36Sopenharmony_ci * hvsi_handshake drain the rest on the next open. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci hvsi_drain_input(hp); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci } else if (hp->port.count < 0) 80862306a36Sopenharmony_ci printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", 80962306a36Sopenharmony_ci hp - hvsi_ports, hp->port.count); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void hvsi_hangup(struct tty_struct *tty) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 81762306a36Sopenharmony_ci unsigned long flags; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci pr_debug("%s\n", __func__); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci tty_port_tty_set(&hp->port, NULL); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 82462306a36Sopenharmony_ci hp->port.count = 0; 82562306a36Sopenharmony_ci hp->n_outbuf = 0; 82662306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/* called with hp->lock held */ 83062306a36Sopenharmony_cistatic void hvsi_push(struct hvsi_struct *hp) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int n; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (hp->n_outbuf <= 0) 83562306a36Sopenharmony_ci return; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); 83862306a36Sopenharmony_ci if (n > 0) { 83962306a36Sopenharmony_ci /* success */ 84062306a36Sopenharmony_ci pr_debug("%s: wrote %i chars\n", __func__, n); 84162306a36Sopenharmony_ci hp->n_outbuf = 0; 84262306a36Sopenharmony_ci } else if (n == -EIO) { 84362306a36Sopenharmony_ci __set_state(hp, HVSI_FSP_DIED); 84462306a36Sopenharmony_ci printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ 84962306a36Sopenharmony_cistatic void hvsi_write_worker(struct work_struct *work) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct hvsi_struct *hp = 85262306a36Sopenharmony_ci container_of(work, struct hvsi_struct, writer.work); 85362306a36Sopenharmony_ci unsigned long flags; 85462306a36Sopenharmony_ci#ifdef DEBUG 85562306a36Sopenharmony_ci static long start_j = 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (start_j == 0) 85862306a36Sopenharmony_ci start_j = jiffies; 85962306a36Sopenharmony_ci#endif /* DEBUG */ 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (!is_open(hp)) { 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * We could have a non-open connection if the service processor died 86862306a36Sopenharmony_ci * while we were busily scheduling ourselves. In that case, it could 86962306a36Sopenharmony_ci * be minutes before the service processor comes back, so only try 87062306a36Sopenharmony_ci * again once a second. 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_ci schedule_delayed_work(&hp->writer, HZ); 87362306a36Sopenharmony_ci goto out; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci hvsi_push(hp); 87762306a36Sopenharmony_ci if (hp->n_outbuf > 0) 87862306a36Sopenharmony_ci schedule_delayed_work(&hp->writer, 10); 87962306a36Sopenharmony_ci else { 88062306a36Sopenharmony_ci#ifdef DEBUG 88162306a36Sopenharmony_ci pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, 88262306a36Sopenharmony_ci jiffies - start_j); 88362306a36Sopenharmony_ci start_j = 0; 88462306a36Sopenharmony_ci#endif /* DEBUG */ 88562306a36Sopenharmony_ci wake_up_all(&hp->emptyq); 88662306a36Sopenharmony_ci tty_port_tty_wakeup(&hp->port); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ciout: 89062306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic unsigned int hvsi_write_room(struct tty_struct *tty) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return N_OUTBUF - hp->n_outbuf; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic unsigned int hvsi_chars_in_buffer(struct tty_struct *tty) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci return hp->n_outbuf; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic ssize_t hvsi_write(struct tty_struct *tty, const u8 *source, 90862306a36Sopenharmony_ci size_t count) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 91162306a36Sopenharmony_ci unsigned long flags; 91262306a36Sopenharmony_ci size_t total = 0; 91362306a36Sopenharmony_ci size_t origcount = count; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (!is_open(hp)) { 92062306a36Sopenharmony_ci /* we're either closing or not yet open; don't accept data */ 92162306a36Sopenharmony_ci pr_debug("%s: not open\n", __func__); 92262306a36Sopenharmony_ci goto out; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* 92662306a36Sopenharmony_ci * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf 92762306a36Sopenharmony_ci * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls 92862306a36Sopenharmony_ci * will see there is no room in outbuf and return. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci while ((count > 0) && (hvsi_write_room(tty) > 0)) { 93162306a36Sopenharmony_ci size_t chunksize = min_t(size_t, count, hvsi_write_room(tty)); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci BUG_ON(hp->n_outbuf < 0); 93462306a36Sopenharmony_ci memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); 93562306a36Sopenharmony_ci hp->n_outbuf += chunksize; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci total += chunksize; 93862306a36Sopenharmony_ci source += chunksize; 93962306a36Sopenharmony_ci count -= chunksize; 94062306a36Sopenharmony_ci hvsi_push(hp); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (hp->n_outbuf > 0) { 94462306a36Sopenharmony_ci /* 94562306a36Sopenharmony_ci * we weren't able to write it all to the hypervisor. 94662306a36Sopenharmony_ci * schedule another push attempt. 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_ci schedule_delayed_work(&hp->writer, 10); 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ciout: 95262306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (total != origcount) 95562306a36Sopenharmony_ci pr_debug("%s: wanted %zu, only wrote %zu\n", __func__, 95662306a36Sopenharmony_ci origcount, total); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return total; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* 96262306a36Sopenharmony_ci * I have never seen throttle or unthrottle called, so this little throttle 96362306a36Sopenharmony_ci * buffering scheme may or may not work. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_cistatic void hvsi_throttle(struct tty_struct *tty) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci pr_debug("%s\n", __func__); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic void hvsi_unthrottle(struct tty_struct *tty) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 97762306a36Sopenharmony_ci unsigned long flags; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci pr_debug("%s\n", __func__); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 98262306a36Sopenharmony_ci if (hp->n_throttle) { 98362306a36Sopenharmony_ci hvsi_send_overflow(hp); 98462306a36Sopenharmony_ci tty_flip_buffer_push(&hp->port); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic int hvsi_tiocmget(struct tty_struct *tty) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci hvsi_get_mctrl(hp); 99762306a36Sopenharmony_ci return hp->mctrl; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic int hvsi_tiocmset(struct tty_struct *tty, 100162306a36Sopenharmony_ci unsigned int set, unsigned int clear) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci struct hvsi_struct *hp = tty->driver_data; 100462306a36Sopenharmony_ci unsigned long flags; 100562306a36Sopenharmony_ci uint16_t new_mctrl; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* we can only alter DTR */ 100862306a36Sopenharmony_ci clear &= TIOCM_DTR; 100962306a36Sopenharmony_ci set &= TIOCM_DTR; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci spin_lock_irqsave(&hp->lock, flags); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci new_mctrl = (hp->mctrl & ~clear) | set; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (hp->mctrl != new_mctrl) { 101662306a36Sopenharmony_ci hvsi_set_mctrl(hp, new_mctrl); 101762306a36Sopenharmony_ci hp->mctrl = new_mctrl; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci spin_unlock_irqrestore(&hp->lock, flags); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic const struct tty_operations hvsi_ops = { 102662306a36Sopenharmony_ci .open = hvsi_open, 102762306a36Sopenharmony_ci .close = hvsi_close, 102862306a36Sopenharmony_ci .write = hvsi_write, 102962306a36Sopenharmony_ci .hangup = hvsi_hangup, 103062306a36Sopenharmony_ci .write_room = hvsi_write_room, 103162306a36Sopenharmony_ci .chars_in_buffer = hvsi_chars_in_buffer, 103262306a36Sopenharmony_ci .throttle = hvsi_throttle, 103362306a36Sopenharmony_ci .unthrottle = hvsi_unthrottle, 103462306a36Sopenharmony_ci .tiocmget = hvsi_tiocmget, 103562306a36Sopenharmony_ci .tiocmset = hvsi_tiocmset, 103662306a36Sopenharmony_ci}; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic int __init hvsi_init(void) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct tty_driver *driver; 104162306a36Sopenharmony_ci int i, ret; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci driver = tty_alloc_driver(hvsi_count, TTY_DRIVER_REAL_RAW); 104462306a36Sopenharmony_ci if (IS_ERR(driver)) 104562306a36Sopenharmony_ci return PTR_ERR(driver); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci driver->driver_name = "hvsi"; 104862306a36Sopenharmony_ci driver->name = "hvsi"; 104962306a36Sopenharmony_ci driver->major = HVSI_MAJOR; 105062306a36Sopenharmony_ci driver->minor_start = HVSI_MINOR; 105162306a36Sopenharmony_ci driver->type = TTY_DRIVER_TYPE_SYSTEM; 105262306a36Sopenharmony_ci driver->init_termios = tty_std_termios; 105362306a36Sopenharmony_ci driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; 105462306a36Sopenharmony_ci driver->init_termios.c_ispeed = 9600; 105562306a36Sopenharmony_ci driver->init_termios.c_ospeed = 9600; 105662306a36Sopenharmony_ci tty_set_operations(driver, &hvsi_ops); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci for (i=0; i < hvsi_count; i++) { 105962306a36Sopenharmony_ci struct hvsi_struct *hp = &hvsi_ports[i]; 106062306a36Sopenharmony_ci int ret = 1; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci tty_port_link_device(&hp->port, driver, i); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp); 106562306a36Sopenharmony_ci if (ret) 106662306a36Sopenharmony_ci printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", 106762306a36Sopenharmony_ci hp->virq, ret); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci hvsi_wait = wait_for_state; /* irqs active now */ 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ret = tty_register_driver(driver); 107262306a36Sopenharmony_ci if (ret) { 107362306a36Sopenharmony_ci pr_err("Couldn't register hvsi console driver\n"); 107462306a36Sopenharmony_ci goto err_free_irq; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci hvsi_driver = driver; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return 0; 108262306a36Sopenharmony_cierr_free_irq: 108362306a36Sopenharmony_ci hvsi_wait = poll_for_state; 108462306a36Sopenharmony_ci for (i = 0; i < hvsi_count; i++) { 108562306a36Sopenharmony_ci struct hvsi_struct *hp = &hvsi_ports[i]; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci free_irq(hp->virq, hp); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci tty_driver_kref_put(driver); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return ret; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_cidevice_initcall(hvsi_init); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/***** console (not tty) code: *****/ 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic void hvsi_console_print(struct console *console, const char *buf, 109862306a36Sopenharmony_ci unsigned int count) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci struct hvsi_struct *hp = &hvsi_ports[console->index]; 110162306a36Sopenharmony_ci char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; 110262306a36Sopenharmony_ci unsigned int i = 0, n = 0; 110362306a36Sopenharmony_ci int ret, donecr = 0; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci mb(); 110662306a36Sopenharmony_ci if (!is_open(hp)) 110762306a36Sopenharmony_ci return; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * ugh, we have to translate LF -> CRLF ourselves, in place. 111162306a36Sopenharmony_ci * copied from hvc_console.c: 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci while (count > 0 || i > 0) { 111462306a36Sopenharmony_ci if (count > 0 && i < sizeof(c)) { 111562306a36Sopenharmony_ci if (buf[n] == '\n' && !donecr) { 111662306a36Sopenharmony_ci c[i++] = '\r'; 111762306a36Sopenharmony_ci donecr = 1; 111862306a36Sopenharmony_ci } else { 111962306a36Sopenharmony_ci c[i++] = buf[n++]; 112062306a36Sopenharmony_ci donecr = 0; 112162306a36Sopenharmony_ci --count; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci } else { 112462306a36Sopenharmony_ci ret = hvsi_put_chars(hp, c, i); 112562306a36Sopenharmony_ci if (ret < 0) 112662306a36Sopenharmony_ci i = 0; 112762306a36Sopenharmony_ci i -= ret; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic struct tty_driver *hvsi_console_device(struct console *console, 113362306a36Sopenharmony_ci int *index) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci *index = console->index; 113662306a36Sopenharmony_ci return hvsi_driver; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic int __init hvsi_console_setup(struct console *console, char *options) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci struct hvsi_struct *hp; 114262306a36Sopenharmony_ci int ret; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (console->index < 0 || console->index >= hvsi_count) 114562306a36Sopenharmony_ci return -EINVAL; 114662306a36Sopenharmony_ci hp = &hvsi_ports[console->index]; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* give the FSP a chance to change the baud rate when we re-open */ 114962306a36Sopenharmony_ci hvsi_close_protocol(hp); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci ret = hvsi_handshake(hp); 115262306a36Sopenharmony_ci if (ret < 0) 115362306a36Sopenharmony_ci return ret; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci ret = hvsi_get_mctrl(hp); 115662306a36Sopenharmony_ci if (ret < 0) 115762306a36Sopenharmony_ci return ret; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); 116062306a36Sopenharmony_ci if (ret < 0) 116162306a36Sopenharmony_ci return ret; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci hp->flags |= HVSI_CONSOLE; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci return 0; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic struct console hvsi_console = { 116962306a36Sopenharmony_ci .name = "hvsi", 117062306a36Sopenharmony_ci .write = hvsi_console_print, 117162306a36Sopenharmony_ci .device = hvsi_console_device, 117262306a36Sopenharmony_ci .setup = hvsi_console_setup, 117362306a36Sopenharmony_ci .flags = CON_PRINTBUFFER, 117462306a36Sopenharmony_ci .index = -1, 117562306a36Sopenharmony_ci}; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic int __init hvsi_console_init(void) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct device_node *vty; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci hvsi_wait = poll_for_state; /* no irqs yet; must poll */ 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* search device tree for vty nodes */ 118462306a36Sopenharmony_ci for_each_compatible_node(vty, "serial", "hvterm-protocol") { 118562306a36Sopenharmony_ci struct hvsi_struct *hp; 118662306a36Sopenharmony_ci const __be32 *vtermno, *irq; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci vtermno = of_get_property(vty, "reg", NULL); 118962306a36Sopenharmony_ci irq = of_get_property(vty, "interrupts", NULL); 119062306a36Sopenharmony_ci if (!vtermno || !irq) 119162306a36Sopenharmony_ci continue; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { 119462306a36Sopenharmony_ci of_node_put(vty); 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci hp = &hvsi_ports[hvsi_count]; 119962306a36Sopenharmony_ci INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); 120062306a36Sopenharmony_ci INIT_WORK(&hp->handshaker, hvsi_handshaker); 120162306a36Sopenharmony_ci init_waitqueue_head(&hp->emptyq); 120262306a36Sopenharmony_ci init_waitqueue_head(&hp->stateq); 120362306a36Sopenharmony_ci spin_lock_init(&hp->lock); 120462306a36Sopenharmony_ci tty_port_init(&hp->port); 120562306a36Sopenharmony_ci hp->index = hvsi_count; 120662306a36Sopenharmony_ci hp->inbuf_end = hp->inbuf; 120762306a36Sopenharmony_ci hp->state = HVSI_CLOSED; 120862306a36Sopenharmony_ci hp->vtermno = be32_to_cpup(vtermno); 120962306a36Sopenharmony_ci hp->virq = irq_create_mapping(NULL, be32_to_cpup(irq)); 121062306a36Sopenharmony_ci if (hp->virq == 0) { 121162306a36Sopenharmony_ci printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", 121262306a36Sopenharmony_ci __func__, be32_to_cpup(irq)); 121362306a36Sopenharmony_ci tty_port_destroy(&hp->port); 121462306a36Sopenharmony_ci continue; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci hvsi_count++; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (hvsi_count) 122162306a36Sopenharmony_ci register_console(&hvsi_console); 122262306a36Sopenharmony_ci return 0; 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ciconsole_initcall(hvsi_console_init); 1225