hvsi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,320 行 · 第 1/3 页

C
1,320
字号
/* * 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. *//* TODO: * test FSP reset * add udbg support for xmon/kdb */#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/sched.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>#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 work_struct writer;	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;	unsigned 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,};#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  0x20struct 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_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",	};	const char *name = state_names[hp->state];	if (hp->state > (sizeof(state_names)/sizeof(char*)))		name = "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", __FUNCTION__, 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)#endifstatic 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);}/* can't use hvc_get_chars because that strips CRs */static int hvsi_read(struct hvsi_struct *hp, char *buf, int count){	unsigned long got;	if (plpar_hcall(H_GET_TERM_CHAR, hp->vtermno, 0, 0, 0, &got,			(unsigned long *)buf, (unsigned long *)buf+1) == H_Success)		return got;	return 0;}/* * we can't call tty_hangup() directly here because we need to call that * outside of our lock */static struct tty_struct *hvsi_recv_control(struct hvsi_struct *hp,		uint8_t *packet){	struct tty_struct *to_hangup = NULL;	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 (!(hp->tty->flags & CLOCAL))					to_hangup = hp->tty;			}			break;		case VSV_CLOSE_PROTOCOL:			printk(KERN_DEBUG				"hvsi%i: service processor closed connection!\n", hp->index);			__set_state(hp, HVSI_CLOSED);			to_hangup = hp->tty;			hp->tty = NULL;			break;		default:			printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ",				hp->index);			dump_packet(packet);			break;	}	return to_hangup;}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", __FUNCTION__, 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:			__set_state(hp, HVSI_OPEN);			hvsi_version_respond(hp, query->seqno);			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, NULL, hp->tty);			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 128static 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)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?