Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
* 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>
#include <asm/hvsi.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 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;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
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";
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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
Loading
Loading full blame...