diff options
Diffstat (limited to 'drivers/char/hvsi.c')
| -rw-r--r-- | drivers/char/hvsi.c | 1314 | 
1 files changed, 0 insertions, 1314 deletions
diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c deleted file mode 100644 index a2bc885ce60..00000000000 --- a/drivers/char/hvsi.c +++ /dev/null @@ -1,1314 +0,0 @@ -/* - * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA - */ - -/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS - * and the service processor on IBM pSeries servers. On these servers, there - * are no serial ports under the OS's control, and sometimes there is no other - * console available either. However, the service processor has two standard - * serial ports, so this over-complicated protocol allows the OS to control - * those ports by proxy. - * - * Besides data, the procotol supports the reading/writing of the serial - * port's DTR line, and the reading of the CD line. This is to allow the OS to - * control a modem attached to the service processor's serial port. Note that - * the OS cannot change the speed of the port through this protocol. - */ - -#undef DEBUG - -#include <linux/console.h> -#include <linux/ctype.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/major.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/sysrq.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <asm/hvcall.h> -#include <asm/hvconsole.h> -#include <asm/prom.h> -#include <asm/uaccess.h> -#include <asm/vio.h> -#include <asm/param.h> - -#define HVSI_MAJOR	229 -#define HVSI_MINOR	128 -#define MAX_NR_HVSI_CONSOLES 4 - -#define HVSI_TIMEOUT (5*HZ) -#define HVSI_VERSION 1 -#define HVSI_MAX_PACKET 256 -#define HVSI_MAX_READ 16 -#define HVSI_MAX_OUTGOING_DATA 12 -#define N_OUTBUF 12 - -/* - * we pass data via two 8-byte registers, so we would like our char arrays - * properly aligned for those loads. - */ -#define __ALIGNED__	__attribute__((__aligned__(sizeof(long)))) - -struct hvsi_struct { -	struct delayed_work writer; -	struct work_struct handshaker; -	wait_queue_head_t emptyq; /* woken when outbuf is emptied */ -	wait_queue_head_t stateq; /* woken when HVSI state changes */ -	spinlock_t lock; -	int index; -	struct tty_struct *tty; -	int count; -	uint8_t throttle_buf[128]; -	uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ -	/* inbuf is for packet reassembly. leave a little room for leftovers. */ -	uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; -	uint8_t *inbuf_end; -	int n_throttle; -	int n_outbuf; -	uint32_t vtermno; -	uint32_t virq; -	atomic_t seqno; /* HVSI packet sequence number */ -	uint16_t mctrl; -	uint8_t state;  /* HVSI protocol state */ -	uint8_t flags; -#ifdef CONFIG_MAGIC_SYSRQ -	uint8_t sysrq; -#endif /* CONFIG_MAGIC_SYSRQ */ -}; -static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; - -static struct tty_driver *hvsi_driver; -static int hvsi_count; -static int (*hvsi_wait)(struct hvsi_struct *hp, int state); - -enum HVSI_PROTOCOL_STATE { -	HVSI_CLOSED, -	HVSI_WAIT_FOR_VER_RESPONSE, -	HVSI_WAIT_FOR_VER_QUERY, -	HVSI_OPEN, -	HVSI_WAIT_FOR_MCTRL_RESPONSE, -	HVSI_FSP_DIED, -}; -#define HVSI_CONSOLE 0x1 - -#define VS_DATA_PACKET_HEADER           0xff -#define VS_CONTROL_PACKET_HEADER        0xfe -#define VS_QUERY_PACKET_HEADER          0xfd -#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc - -/* control verbs */ -#define VSV_SET_MODEM_CTL    1 /* to service processor only */ -#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ -#define VSV_CLOSE_PROTOCOL   3 - -/* query verbs */ -#define VSV_SEND_VERSION_NUMBER 1 -#define VSV_SEND_MODEM_CTL_STATUS 2 - -/* yes, these masks are not consecutive. */ -#define HVSI_TSDTR 0x01 -#define HVSI_TSCD  0x20 - -struct hvsi_header { -	uint8_t  type; -	uint8_t  len; -	uint16_t seqno; -} __attribute__((packed)); - -struct hvsi_data { -	uint8_t  type; -	uint8_t  len; -	uint16_t seqno; -	uint8_t  data[HVSI_MAX_OUTGOING_DATA]; -} __attribute__((packed)); - -struct hvsi_control { -	uint8_t  type; -	uint8_t  len; -	uint16_t seqno; -	uint16_t verb; -	/* optional depending on verb: */ -	uint32_t word; -	uint32_t mask; -} __attribute__((packed)); - -struct hvsi_query { -	uint8_t  type; -	uint8_t  len; -	uint16_t seqno; -	uint16_t verb; -} __attribute__((packed)); - -struct hvsi_query_response { -	uint8_t  type; -	uint8_t  len; -	uint16_t seqno; -	uint16_t verb; -	uint16_t query_seqno; -	union { -		uint8_t  version; -		uint32_t mctrl_word; -	} u; -} __attribute__((packed)); - - - -static inline int is_console(struct hvsi_struct *hp) -{ -	return hp->flags & HVSI_CONSOLE; -} - -static inline int is_open(struct hvsi_struct *hp) -{ -	/* if we're waiting for an mctrl then we're already open */ -	return (hp->state == HVSI_OPEN) -			|| (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); -} - -static inline void print_state(struct hvsi_struct *hp) -{ -#ifdef DEBUG -	static const char *state_names[] = { -		"HVSI_CLOSED", -		"HVSI_WAIT_FOR_VER_RESPONSE", -		"HVSI_WAIT_FOR_VER_QUERY", -		"HVSI_OPEN", -		"HVSI_WAIT_FOR_MCTRL_RESPONSE", -		"HVSI_FSP_DIED", -	}; -	const char *name = (hp->state < ARRAY_SIZE(state_names)) -		? state_names[hp->state] : "UNKNOWN"; - -	pr_debug("hvsi%i: state = %s\n", hp->index, name); -#endif /* DEBUG */ -} - -static inline void __set_state(struct hvsi_struct *hp, int state) -{ -	hp->state = state; -	print_state(hp); -	wake_up_all(&hp->stateq); -} - -static inline void set_state(struct hvsi_struct *hp, int state) -{ -	unsigned long flags; - -	spin_lock_irqsave(&hp->lock, flags); -	__set_state(hp, state); -	spin_unlock_irqrestore(&hp->lock, flags); -} - -static inline int len_packet(const uint8_t *packet) -{ -	return (int)((struct hvsi_header *)packet)->len; -} - -static inline int is_header(const uint8_t *packet) -{ -	struct hvsi_header *header = (struct hvsi_header *)packet; -	return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; -} - -static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) -{ -	if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) -		return 0; /* don't even have the packet header */ - -	if (hp->inbuf_end < (packet + len_packet(packet))) -		return 0; /* don't have the rest of the packet */ - -	return 1; -} - -/* shift remaining bytes in packetbuf down */ -static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) -{ -	int remaining = (int)(hp->inbuf_end - read_to); - -	pr_debug("%s: %i chars remain\n", __func__, remaining); - -	if (read_to != hp->inbuf) -		memmove(hp->inbuf, read_to, remaining); - -	hp->inbuf_end = hp->inbuf + remaining; -} - -#ifdef DEBUG -#define dbg_dump_packet(packet) dump_packet(packet) -#define dbg_dump_hex(data, len) dump_hex(data, len) -#else -#define dbg_dump_packet(packet) do { } while (0) -#define dbg_dump_hex(data, len) do { } while (0) -#endif - -static void dump_hex(const uint8_t *data, int len) -{ -	int i; - -	printk("    "); -	for (i=0; i < len; i++) -		printk("%.2x", data[i]); - -	printk("\n    "); -	for (i=0; i < len; i++) { -		if (isprint(data[i])) -			printk("%c", data[i]); -		else -			printk("."); -	} -	printk("\n"); -} - -static void dump_packet(uint8_t *packet) -{ -	struct hvsi_header *header = (struct hvsi_header *)packet; - -	printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, -			header->seqno); - -	dump_hex(packet, header->len); -} - -static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) -{ -	unsigned long got; - -	got = hvc_get_chars(hp->vtermno, buf, count); - -	return got; -} - -static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, -	struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) -{ -	struct hvsi_control *header = (struct hvsi_control *)packet; - -	switch (header->verb) { -		case VSV_MODEM_CTL_UPDATE: -			if ((header->word & HVSI_TSCD) == 0) { -				/* CD went away; no more connection */ -				pr_debug("hvsi%i: CD dropped\n", hp->index); -				hp->mctrl &= TIOCM_CD; -				/* If userland hasn't done an open(2) yet, hp->tty is NULL. */ -				if (hp->tty && !(hp->tty->flags & CLOCAL)) -					*to_hangup = hp->tty; -			} -			break; -		case VSV_CLOSE_PROTOCOL: -			pr_debug("hvsi%i: service processor came back\n", hp->index); -			if (hp->state != HVSI_CLOSED) { -				*to_handshake = hp; -			} -			break; -		default: -			printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", -				hp->index); -			dump_packet(packet); -			break; -	} -} - -static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) -{ -	struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; - -	switch (hp->state) { -		case HVSI_WAIT_FOR_VER_RESPONSE: -			__set_state(hp, HVSI_WAIT_FOR_VER_QUERY); -			break; -		case HVSI_WAIT_FOR_MCTRL_RESPONSE: -			hp->mctrl = 0; -			if (resp->u.mctrl_word & HVSI_TSDTR) -				hp->mctrl |= TIOCM_DTR; -			if (resp->u.mctrl_word & HVSI_TSCD) -				hp->mctrl |= TIOCM_CD; -			__set_state(hp, HVSI_OPEN); -			break; -		default: -			printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); -			dump_packet(packet); -			break; -	} -} - -/* respond to service processor's version query */ -static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) -{ -	struct hvsi_query_response packet __ALIGNED__; -	int wrote; - -	packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; -	packet.len = sizeof(struct hvsi_query_response); -	packet.seqno = atomic_inc_return(&hp->seqno); -	packet.verb = VSV_SEND_VERSION_NUMBER; -	packet.u.version = HVSI_VERSION; -	packet.query_seqno = query_seqno+1; - -	pr_debug("%s: sending %i bytes\n", __func__, packet.len); -	dbg_dump_hex((uint8_t*)&packet, packet.len); - -	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -	if (wrote != packet.len) { -		printk(KERN_ERR "hvsi%i: couldn't send query response!\n", -			hp->index); -		return -EIO; -	} - -	return 0; -} - -static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) -{ -	struct hvsi_query *query = (struct hvsi_query *)packet; - -	switch (hp->state) { -		case HVSI_WAIT_FOR_VER_QUERY: -			hvsi_version_respond(hp, query->seqno); -			__set_state(hp, HVSI_OPEN); -			break; -		default: -			printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); -			dump_packet(packet); -			break; -	} -} - -static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) -{ -	int i; - -	for (i=0; i < len; i++) { -		char c = buf[i]; -#ifdef CONFIG_MAGIC_SYSRQ -		if (c == '\0') { -			hp->sysrq = 1; -			continue; -		} else if (hp->sysrq) { -			handle_sysrq(c); -			hp->sysrq = 0; -			continue; -		} -#endif /* CONFIG_MAGIC_SYSRQ */ -		tty_insert_flip_char(hp->tty, c, 0); -	} -} - -/* - * We could get 252 bytes of data at once here. But the tty layer only - * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow - * it. Accordingly we won't send more than 128 bytes at a time to the flip - * buffer, which will give the tty buffer a chance to throttle us. Should the - * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be - * revisited. - */ -#define TTY_THRESHOLD_THROTTLE 128 -static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, -		const uint8_t *packet) -{ -	const struct hvsi_header *header = (const struct hvsi_header *)packet; -	const uint8_t *data = packet + sizeof(struct hvsi_header); -	int datalen = header->len - sizeof(struct hvsi_header); -	int overflow = datalen - TTY_THRESHOLD_THROTTLE; - -	pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); - -	if (datalen == 0) -		return NULL; - -	if (overflow > 0) { -		pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); -		datalen = TTY_THRESHOLD_THROTTLE; -	} - -	hvsi_insert_chars(hp, data, datalen); - -	if (overflow > 0) { -		/* -		 * we still have more data to deliver, so we need to save off the -		 * overflow and send it later -		 */ -		pr_debug("%s: deferring overflow\n", __func__); -		memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); -		hp->n_throttle = overflow; -	} - -	return hp->tty; -} - -/* - * Returns true/false indicating data successfully read from hypervisor. - * Used both to get packets for tty connections and to advance the state - * machine during console handshaking (in which case tty = NULL and we ignore - * incoming data). - */ -static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, -		struct tty_struct **hangup, struct hvsi_struct **handshake) -{ -	uint8_t *packet = hp->inbuf; -	int chunklen; - -	*flip = NULL; -	*hangup = NULL; -	*handshake = NULL; - -	chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); -	if (chunklen == 0) { -		pr_debug("%s: 0-length read\n", __func__); -		return 0; -	} - -	pr_debug("%s: got %i bytes\n", __func__, chunklen); -	dbg_dump_hex(hp->inbuf_end, chunklen); - -	hp->inbuf_end += chunklen; - -	/* handle all completed packets */ -	while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { -		struct hvsi_header *header = (struct hvsi_header *)packet; - -		if (!is_header(packet)) { -			printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); -			/* skip bytes until we find a header or run out of data */ -			while ((packet < hp->inbuf_end) && (!is_header(packet))) -				packet++; -			continue; -		} - -		pr_debug("%s: handling %i-byte packet\n", __func__, -				len_packet(packet)); -		dbg_dump_packet(packet); - -		switch (header->type) { -			case VS_DATA_PACKET_HEADER: -				if (!is_open(hp)) -					break; -				if (hp->tty == NULL) -					break; /* no tty buffer to put data in */ -				*flip = hvsi_recv_data(hp, packet); -				break; -			case VS_CONTROL_PACKET_HEADER: -				hvsi_recv_control(hp, packet, hangup, handshake); -				break; -			case VS_QUERY_RESPONSE_PACKET_HEADER: -				hvsi_recv_response(hp, packet); -				break; -			case VS_QUERY_PACKET_HEADER: -				hvsi_recv_query(hp, packet); -				break; -			default: -				printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", -						hp->index, header->type); -				dump_packet(packet); -				break; -		} - -		packet += len_packet(packet); - -		if (*hangup || *handshake) { -			pr_debug("%s: hangup or handshake\n", __func__); -			/* -			 * we need to send the hangup now before receiving any more data. -			 * If we get "data, hangup, data", we can't deliver the second -			 * data before the hangup. -			 */ -			break; -		} -	} - -	compact_inbuf(hp, packet); - -	return 1; -} - -static void hvsi_send_overflow(struct hvsi_struct *hp) -{ -	pr_debug("%s: delivering %i bytes overflow\n", __func__, -			hp->n_throttle); - -	hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); -	hp->n_throttle = 0; -} - -/* - * must get all pending data because we only get an irq on empty->non-empty - * transition - */ -static irqreturn_t hvsi_interrupt(int irq, void *arg) -{ -	struct hvsi_struct *hp = (struct hvsi_struct *)arg; -	struct tty_struct *flip; -	struct tty_struct *hangup; -	struct hvsi_struct *handshake; -	unsigned long flags; -	int again = 1; - -	pr_debug("%s\n", __func__); - -	while (again) { -		spin_lock_irqsave(&hp->lock, flags); -		again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); -		spin_unlock_irqrestore(&hp->lock, flags); - -		/* -		 * we have to call tty_flip_buffer_push() and tty_hangup() outside our -		 * spinlock. But we also have to keep going until we've read all the -		 * available data. -		 */ - -		if (flip) { -			/* there was data put in the tty flip buffer */ -			tty_flip_buffer_push(flip); -			flip = NULL; -		} - -		if (hangup) { -			tty_hangup(hangup); -		} - -		if (handshake) { -			pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); -			schedule_work(&handshake->handshaker); -		} -	} - -	spin_lock_irqsave(&hp->lock, flags); -	if (hp->tty && hp->n_throttle -			&& (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { -		/* we weren't hung up and we weren't throttled, so we can deliver the -		 * rest now */ -		flip = hp->tty; -		hvsi_send_overflow(hp); -	} -	spin_unlock_irqrestore(&hp->lock, flags); - -	if (flip) { -		tty_flip_buffer_push(flip); -	} - -	return IRQ_HANDLED; -} - -/* for boot console, before the irq handler is running */ -static int __init poll_for_state(struct hvsi_struct *hp, int state) -{ -	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; - -	for (;;) { -		hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ - -		if (hp->state == state) -			return 0; - -		mdelay(5); -		if (time_after(jiffies, end_jiffies)) -			return -EIO; -	} -} - -/* wait for irq handler to change our state */ -static int wait_for_state(struct hvsi_struct *hp, int state) -{ -	int ret = 0; - -	if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) -		ret = -EIO; - -	return ret; -} - -static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) -{ -	struct hvsi_query packet __ALIGNED__; -	int wrote; - -	packet.type = VS_QUERY_PACKET_HEADER; -	packet.len = sizeof(struct hvsi_query); -	packet.seqno = atomic_inc_return(&hp->seqno); -	packet.verb = verb; - -	pr_debug("%s: sending %i bytes\n", __func__, packet.len); -	dbg_dump_hex((uint8_t*)&packet, packet.len); - -	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -	if (wrote != packet.len) { -		printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, -			wrote); -		return -EIO; -	} - -	return 0; -} - -static int hvsi_get_mctrl(struct hvsi_struct *hp) -{ -	int ret; - -	set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); -	hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); - -	ret = hvsi_wait(hp, HVSI_OPEN); -	if (ret < 0) { -		printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); -		set_state(hp, HVSI_OPEN); -		return ret; -	} - -	pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); - -	return 0; -} - -/* note that we can only set DTR */ -static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) -{ -	struct hvsi_control packet __ALIGNED__; -	int wrote; - -	packet.type = VS_CONTROL_PACKET_HEADER, -	packet.seqno = atomic_inc_return(&hp->seqno); -	packet.len = sizeof(struct hvsi_control); -	packet.verb = VSV_SET_MODEM_CTL; -	packet.mask = HVSI_TSDTR; - -	if (mctrl & TIOCM_DTR) -		packet.word = HVSI_TSDTR; - -	pr_debug("%s: sending %i bytes\n", __func__, packet.len); -	dbg_dump_hex((uint8_t*)&packet, packet.len); - -	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -	if (wrote != packet.len) { -		printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); -		return -EIO; -	} - -	return 0; -} - -static void hvsi_drain_input(struct hvsi_struct *hp) -{ -	uint8_t buf[HVSI_MAX_READ] __ALIGNED__; -	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; - -	while (time_before(end_jiffies, jiffies)) -		if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) -			break; -} - -static int hvsi_handshake(struct hvsi_struct *hp) -{ -	int ret; - -	/* -	 * We could have a CLOSE or other data waiting for us before we even try -	 * to open; try to throw it all away so we don't get confused. (CLOSE -	 * is the first message sent up the pipe when the FSP comes online. We -	 * need to distinguish between "it came up a while ago and we're the first -	 * user" and "it was just reset before it saw our handshake packet".) -	 */ -	hvsi_drain_input(hp); - -	set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); -	ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); -	if (ret < 0) { -		printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); -		return ret; -	} - -	ret = hvsi_wait(hp, HVSI_OPEN); -	if (ret < 0) -		return ret; - -	return 0; -} - -static void hvsi_handshaker(struct work_struct *work) -{ -	struct hvsi_struct *hp = -		container_of(work, struct hvsi_struct, handshaker); - -	if (hvsi_handshake(hp) >= 0) -		return; - -	printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); -	if (is_console(hp)) { -		/* -		 * ttys will re-attempt the handshake via hvsi_open, but -		 * the console will not. -		 */ -		printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); -	} -} - -static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) -{ -	struct hvsi_data packet __ALIGNED__; -	int ret; - -	BUG_ON(count > HVSI_MAX_OUTGOING_DATA); - -	packet.type = VS_DATA_PACKET_HEADER; -	packet.seqno = atomic_inc_return(&hp->seqno); -	packet.len = count + sizeof(struct hvsi_header); -	memcpy(&packet.data, buf, count); - -	ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -	if (ret == packet.len) { -		/* return the number of chars written, not the packet length */ -		return count; -	} -	return ret; /* return any errors */ -} - -static void hvsi_close_protocol(struct hvsi_struct *hp) -{ -	struct hvsi_control packet __ALIGNED__; - -	packet.type = VS_CONTROL_PACKET_HEADER; -	packet.seqno = atomic_inc_return(&hp->seqno); -	packet.len = 6; -	packet.verb = VSV_CLOSE_PROTOCOL; - -	pr_debug("%s: sending %i bytes\n", __func__, packet.len); -	dbg_dump_hex((uint8_t*)&packet, packet.len); - -	hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -} - -static int hvsi_open(struct tty_struct *tty, struct file *filp) -{ -	struct hvsi_struct *hp; -	unsigned long flags; -	int line = tty->index; -	int ret; - -	pr_debug("%s\n", __func__); - -	if (line < 0 || line >= hvsi_count) -		return -ENODEV; -	hp = &hvsi_ports[line]; - -	tty->driver_data = hp; - -	mb(); -	if (hp->state == HVSI_FSP_DIED) -		return -EIO; - -	spin_lock_irqsave(&hp->lock, flags); -	hp->tty = tty; -	hp->count++; -	atomic_set(&hp->seqno, 0); -	h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); -	spin_unlock_irqrestore(&hp->lock, flags); - -	if (is_console(hp)) -		return 0; /* this has already been handshaked as the console */ - -	ret = hvsi_handshake(hp); -	if (ret < 0) { -		printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); -		return ret; -	} - -	ret = hvsi_get_mctrl(hp); -	if (ret < 0) { -		printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); -		return ret; -	} - -	ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); -	if (ret < 0) { -		printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); -		return ret; -	} - -	return 0; -} - -/* wait for hvsi_write_worker to empty hp->outbuf */ -static void hvsi_flush_output(struct hvsi_struct *hp) -{ -	wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); - -	/* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ -	cancel_delayed_work(&hp->writer); -	flush_scheduled_work(); - -	/* -	 * it's also possible that our timeout expired and hvsi_write_worker -	 * didn't manage to push outbuf. poof. -	 */ -	hp->n_outbuf = 0; -} - -static void hvsi_close(struct tty_struct *tty, struct file *filp) -{ -	struct hvsi_struct *hp = tty->driver_data; -	unsigned long flags; - -	pr_debug("%s\n", __func__); - -	if (tty_hung_up_p(filp)) -		return; - -	spin_lock_irqsave(&hp->lock, flags); - -	if (--hp->count == 0) { -		hp->tty = NULL; -		hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ - -		/* only close down connection if it is not the console */ -		if (!is_console(hp)) { -			h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ -			__set_state(hp, HVSI_CLOSED); -			/* -			 * any data delivered to the tty layer after this will be -			 * discarded (except for XON/XOFF) -			 */ -			tty->closing = 1; - -			spin_unlock_irqrestore(&hp->lock, flags); - -			/* let any existing irq handlers finish. no more will start. */ -			synchronize_irq(hp->virq); - -			/* hvsi_write_worker will re-schedule until outbuf is empty. */ -			hvsi_flush_output(hp); - -			/* tell FSP to stop sending data */ -			hvsi_close_protocol(hp); - -			/* -			 * drain anything FSP is still in the middle of sending, and let -			 * hvsi_handshake drain the rest on the next open. -			 */ -			hvsi_drain_input(hp); - -			spin_lock_irqsave(&hp->lock, flags); -		} -	} else if (hp->count < 0) -		printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", -		       hp - hvsi_ports, hp->count); - -	spin_unlock_irqrestore(&hp->lock, flags); -} - -static void hvsi_hangup(struct tty_struct *tty) -{ -	struct hvsi_struct *hp = tty->driver_data; -	unsigned long flags; - -	pr_debug("%s\n", __func__); - -	spin_lock_irqsave(&hp->lock, flags); - -	hp->count = 0; -	hp->n_outbuf = 0; -	hp->tty = NULL; - -	spin_unlock_irqrestore(&hp->lock, flags); -} - -/* called with hp->lock held */ -static void hvsi_push(struct hvsi_struct *hp) -{ -	int n; - -	if (hp->n_outbuf <= 0) -		return; - -	n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); -	if (n > 0) { -		/* success */ -		pr_debug("%s: wrote %i chars\n", __func__, n); -		hp->n_outbuf = 0; -	} else if (n == -EIO) { -		__set_state(hp, HVSI_FSP_DIED); -		printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); -	} -} - -/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ -static void hvsi_write_worker(struct work_struct *work) -{ -	struct hvsi_struct *hp = -		container_of(work, struct hvsi_struct, writer.work); -	unsigned long flags; -#ifdef DEBUG -	static long start_j = 0; - -	if (start_j == 0) -		start_j = jiffies; -#endif /* DEBUG */ - -	spin_lock_irqsave(&hp->lock, flags); - -	pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); - -	if (!is_open(hp)) { -		/* -		 * We could have a non-open connection if the service processor died -		 * while we were busily scheduling ourselves. In that case, it could -		 * be minutes before the service processor comes back, so only try -		 * again once a second. -		 */ -		schedule_delayed_work(&hp->writer, HZ); -		goto out; -	} - -	hvsi_push(hp); -	if (hp->n_outbuf > 0) -		schedule_delayed_work(&hp->writer, 10); -	else { -#ifdef DEBUG -		pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, -				jiffies - start_j); -		start_j = 0; -#endif /* DEBUG */ -		wake_up_all(&hp->emptyq); -		tty_wakeup(hp->tty); -	} - -out: -	spin_unlock_irqrestore(&hp->lock, flags); -} - -static int hvsi_write_room(struct tty_struct *tty) -{ -	struct hvsi_struct *hp = tty->driver_data; - -	return N_OUTBUF - hp->n_outbuf; -} - -static int hvsi_chars_in_buffer(struct tty_struct *tty) -{ -	struct hvsi_struct *hp = tty->driver_data; - -	return hp->n_outbuf; -} - -static int hvsi_write(struct tty_struct *tty, -		     const unsigned char *buf, int count) -{ -	struct hvsi_struct *hp = tty->driver_data; -	const char *source = buf; -	unsigned long flags; -	int total = 0; -	int origcount = count; - -	spin_lock_irqsave(&hp->lock, flags); - -	pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); - -	if (!is_open(hp)) { -		/* we're either closing or not yet open; don't accept data */ -		pr_debug("%s: not open\n", __func__); -		goto out; -	} - -	/* -	 * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf -	 * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls -	 * will see there is no room in outbuf and return. -	 */ -	while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { -		int chunksize = min(count, hvsi_write_room(hp->tty)); - -		BUG_ON(hp->n_outbuf < 0); -		memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); -		hp->n_outbuf += chunksize; - -		total += chunksize; -		source += chunksize; -		count -= chunksize; -		hvsi_push(hp); -	} - -	if (hp->n_outbuf > 0) { -		/* -		 * we weren't able to write it all to the hypervisor. -		 * schedule another push attempt. -		 */ -		schedule_delayed_work(&hp->writer, 10); -	} - -out: -	spin_unlock_irqrestore(&hp->lock, flags); - -	if (total != origcount) -		pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, -			total); - -	return total; -} - -/* - * I have never seen throttle or unthrottle called, so this little throttle - * buffering scheme may or may not work. - */ -static void hvsi_throttle(struct tty_struct *tty) -{ -	struct hvsi_struct *hp = tty->driver_data; - -	pr_debug("%s\n", __func__); - -	h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); -} - -static void hvsi_unthrottle(struct tty_struct *tty) -{ -	struct hvsi_struct *hp = tty->driver_data; -	unsigned long flags; -	int shouldflip = 0; - -	pr_debug("%s\n", __func__); - -	spin_lock_irqsave(&hp->lock, flags); -	if (hp->n_throttle) { -		hvsi_send_overflow(hp); -		shouldflip = 1; -	} -	spin_unlock_irqrestore(&hp->lock, flags); - -	if (shouldflip) -		tty_flip_buffer_push(hp->tty); - -	h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); -} - -static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) -{ -	struct hvsi_struct *hp = tty->driver_data; - -	hvsi_get_mctrl(hp); -	return hp->mctrl; -} - -static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, -		unsigned int set, unsigned int clear) -{ -	struct hvsi_struct *hp = tty->driver_data; -	unsigned long flags; -	uint16_t new_mctrl; - -	/* we can only alter DTR */ -	clear &= TIOCM_DTR; -	set &= TIOCM_DTR; - -	spin_lock_irqsave(&hp->lock, flags); - -	new_mctrl = (hp->mctrl & ~clear) | set; - -	if (hp->mctrl != new_mctrl) { -		hvsi_set_mctrl(hp, new_mctrl); -		hp->mctrl = new_mctrl; -	} -	spin_unlock_irqrestore(&hp->lock, flags); - -	return 0; -} - - -static const struct tty_operations hvsi_ops = { -	.open = hvsi_open, -	.close = hvsi_close, -	.write = hvsi_write, -	.hangup = hvsi_hangup, -	.write_room = hvsi_write_room, -	.chars_in_buffer = hvsi_chars_in_buffer, -	.throttle = hvsi_throttle, -	.unthrottle = hvsi_unthrottle, -	.tiocmget = hvsi_tiocmget, -	.tiocmset = hvsi_tiocmset, -}; - -static int __init hvsi_init(void) -{ -	int i; - -	hvsi_driver = alloc_tty_driver(hvsi_count); -	if (!hvsi_driver) -		return -ENOMEM; - -	hvsi_driver->owner = THIS_MODULE; -	hvsi_driver->driver_name = "hvsi"; -	hvsi_driver->name = "hvsi"; -	hvsi_driver->major = HVSI_MAJOR; -	hvsi_driver->minor_start = HVSI_MINOR; -	hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; -	hvsi_driver->init_termios = tty_std_termios; -	hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; -	hvsi_driver->init_termios.c_ispeed = 9600; -	hvsi_driver->init_termios.c_ospeed = 9600; -	hvsi_driver->flags = TTY_DRIVER_REAL_RAW; -	tty_set_operations(hvsi_driver, &hvsi_ops); - -	for (i=0; i < hvsi_count; i++) { -		struct hvsi_struct *hp = &hvsi_ports[i]; -		int ret = 1; - -		ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); -		if (ret) -			printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", -				hp->virq, ret); -	} -	hvsi_wait = wait_for_state; /* irqs active now */ - -	if (tty_register_driver(hvsi_driver)) -		panic("Couldn't register hvsi console driver\n"); - -	printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); - -	return 0; -} -device_initcall(hvsi_init); - -/***** console (not tty) code: *****/ - -static void hvsi_console_print(struct console *console, const char *buf, -		unsigned int count) -{ -	struct hvsi_struct *hp = &hvsi_ports[console->index]; -	char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; -	unsigned int i = 0, n = 0; -	int ret, donecr = 0; - -	mb(); -	if (!is_open(hp)) -		return; - -	/* -	 * ugh, we have to translate LF -> CRLF ourselves, in place. -	 * copied from hvc_console.c: -	 */ -	while (count > 0 || i > 0) { -		if (count > 0 && i < sizeof(c)) { -			if (buf[n] == '\n' && !donecr) { -				c[i++] = '\r'; -				donecr = 1; -			} else { -				c[i++] = buf[n++]; -				donecr = 0; -				--count; -			} -		} else { -			ret = hvsi_put_chars(hp, c, i); -			if (ret < 0) -				i = 0; -			i -= ret; -		} -	} -} - -static struct tty_driver *hvsi_console_device(struct console *console, -	int *index) -{ -	*index = console->index; -	return hvsi_driver; -} - -static int __init hvsi_console_setup(struct console *console, char *options) -{ -	struct hvsi_struct *hp; -	int ret; - -	if (console->index < 0 || console->index >= hvsi_count) -		return -1; -	hp = &hvsi_ports[console->index]; - -	/* give the FSP a chance to change the baud rate when we re-open */ -	hvsi_close_protocol(hp); - -	ret = hvsi_handshake(hp); -	if (ret < 0) -		return ret; - -	ret = hvsi_get_mctrl(hp); -	if (ret < 0) -		return ret; - -	ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); -	if (ret < 0) -		return ret; - -	hp->flags |= HVSI_CONSOLE; - -	return 0; -} - -static struct console hvsi_console = { -	.name		= "hvsi", -	.write		= hvsi_console_print, -	.device		= hvsi_console_device, -	.setup		= hvsi_console_setup, -	.flags		= CON_PRINTBUFFER, -	.index		= -1, -}; - -static int __init hvsi_console_init(void) -{ -	struct device_node *vty; - -	hvsi_wait = poll_for_state; /* no irqs yet; must poll */ - -	/* search device tree for vty nodes */ -	for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); -			vty != NULL; -			vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { -		struct hvsi_struct *hp; -		const uint32_t *vtermno, *irq; - -		vtermno = of_get_property(vty, "reg", NULL); -		irq = of_get_property(vty, "interrupts", NULL); -		if (!vtermno || !irq) -			continue; - -		if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { -			of_node_put(vty); -			break; -		} - -		hp = &hvsi_ports[hvsi_count]; -		INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); -		INIT_WORK(&hp->handshaker, hvsi_handshaker); -		init_waitqueue_head(&hp->emptyq); -		init_waitqueue_head(&hp->stateq); -		spin_lock_init(&hp->lock); -		hp->index = hvsi_count; -		hp->inbuf_end = hp->inbuf; -		hp->state = HVSI_CLOSED; -		hp->vtermno = *vtermno; -		hp->virq = irq_create_mapping(NULL, irq[0]); -		if (hp->virq == NO_IRQ) { -			printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", -				__func__, irq[0]); -			continue; -		} - -		hvsi_count++; -	} - -	if (hvsi_count) -		register_console(&hvsi_console); -	return 0; -} -console_initcall(hvsi_console_init);  | 
