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 + -
显示快捷键?