diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/hvsi.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/hvsi.c')
-rw-r--r-- | drivers/char/hvsi.c | 1320 |
1 files changed, 1320 insertions, 0 deletions
diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c new file mode 100644 index 000000000000..f1f1192ba2b5 --- /dev/null +++ b/drivers/char/hvsi.c | |||
@@ -0,0 +1,1320 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | /* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS | ||
20 | * and the service processor on IBM pSeries servers. On these servers, there | ||
21 | * are no serial ports under the OS's control, and sometimes there is no other | ||
22 | * console available either. However, the service processor has two standard | ||
23 | * serial ports, so this over-complicated protocol allows the OS to control | ||
24 | * those ports by proxy. | ||
25 | * | ||
26 | * Besides data, the procotol supports the reading/writing of the serial | ||
27 | * port's DTR line, and the reading of the CD line. This is to allow the OS to | ||
28 | * control a modem attached to the service processor's serial port. Note that | ||
29 | * the OS cannot change the speed of the port through this protocol. | ||
30 | */ | ||
31 | |||
32 | #undef DEBUG | ||
33 | |||
34 | #include <linux/console.h> | ||
35 | #include <linux/ctype.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/interrupt.h> | ||
39 | #include <linux/module.h> | ||
40 | #include <linux/major.h> | ||
41 | #include <linux/kernel.h> | ||
42 | #include <linux/sched.h> | ||
43 | #include <linux/spinlock.h> | ||
44 | #include <linux/sysrq.h> | ||
45 | #include <linux/tty.h> | ||
46 | #include <linux/tty_flip.h> | ||
47 | #include <asm/hvcall.h> | ||
48 | #include <asm/hvconsole.h> | ||
49 | #include <asm/prom.h> | ||
50 | #include <asm/uaccess.h> | ||
51 | #include <asm/vio.h> | ||
52 | #include <asm/param.h> | ||
53 | |||
54 | #define HVSI_MAJOR 229 | ||
55 | #define HVSI_MINOR 128 | ||
56 | #define MAX_NR_HVSI_CONSOLES 4 | ||
57 | |||
58 | #define HVSI_TIMEOUT (5*HZ) | ||
59 | #define HVSI_VERSION 1 | ||
60 | #define HVSI_MAX_PACKET 256 | ||
61 | #define HVSI_MAX_READ 16 | ||
62 | #define HVSI_MAX_OUTGOING_DATA 12 | ||
63 | #define N_OUTBUF 12 | ||
64 | |||
65 | /* | ||
66 | * we pass data via two 8-byte registers, so we would like our char arrays | ||
67 | * properly aligned for those loads. | ||
68 | */ | ||
69 | #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) | ||
70 | |||
71 | struct hvsi_struct { | ||
72 | struct work_struct writer; | ||
73 | struct work_struct handshaker; | ||
74 | wait_queue_head_t emptyq; /* woken when outbuf is emptied */ | ||
75 | wait_queue_head_t stateq; /* woken when HVSI state changes */ | ||
76 | spinlock_t lock; | ||
77 | int index; | ||
78 | struct tty_struct *tty; | ||
79 | unsigned int count; | ||
80 | uint8_t throttle_buf[128]; | ||
81 | uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ | ||
82 | /* inbuf is for packet reassembly. leave a little room for leftovers. */ | ||
83 | uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; | ||
84 | uint8_t *inbuf_end; | ||
85 | int n_throttle; | ||
86 | int n_outbuf; | ||
87 | uint32_t vtermno; | ||
88 | uint32_t virq; | ||
89 | atomic_t seqno; /* HVSI packet sequence number */ | ||
90 | uint16_t mctrl; | ||
91 | uint8_t state; /* HVSI protocol state */ | ||
92 | uint8_t flags; | ||
93 | #ifdef CONFIG_MAGIC_SYSRQ | ||
94 | uint8_t sysrq; | ||
95 | #endif /* CONFIG_MAGIC_SYSRQ */ | ||
96 | }; | ||
97 | static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; | ||
98 | |||
99 | static struct tty_driver *hvsi_driver; | ||
100 | static int hvsi_count; | ||
101 | static int (*hvsi_wait)(struct hvsi_struct *hp, int state); | ||
102 | |||
103 | enum HVSI_PROTOCOL_STATE { | ||
104 | HVSI_CLOSED, | ||
105 | HVSI_WAIT_FOR_VER_RESPONSE, | ||
106 | HVSI_WAIT_FOR_VER_QUERY, | ||
107 | HVSI_OPEN, | ||
108 | HVSI_WAIT_FOR_MCTRL_RESPONSE, | ||
109 | HVSI_FSP_DIED, | ||
110 | }; | ||
111 | #define HVSI_CONSOLE 0x1 | ||
112 | |||
113 | #define VS_DATA_PACKET_HEADER 0xff | ||
114 | #define VS_CONTROL_PACKET_HEADER 0xfe | ||
115 | #define VS_QUERY_PACKET_HEADER 0xfd | ||
116 | #define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc | ||
117 | |||
118 | /* control verbs */ | ||
119 | #define VSV_SET_MODEM_CTL 1 /* to service processor only */ | ||
120 | #define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ | ||
121 | #define VSV_CLOSE_PROTOCOL 3 | ||
122 | |||
123 | /* query verbs */ | ||
124 | #define VSV_SEND_VERSION_NUMBER 1 | ||
125 | #define VSV_SEND_MODEM_CTL_STATUS 2 | ||
126 | |||
127 | /* yes, these masks are not consecutive. */ | ||
128 | #define HVSI_TSDTR 0x01 | ||
129 | #define HVSI_TSCD 0x20 | ||
130 | |||
131 | struct hvsi_header { | ||
132 | uint8_t type; | ||
133 | uint8_t len; | ||
134 | uint16_t seqno; | ||
135 | } __attribute__((packed)); | ||
136 | |||
137 | struct hvsi_data { | ||
138 | uint8_t type; | ||
139 | uint8_t len; | ||
140 | uint16_t seqno; | ||
141 | uint8_t data[HVSI_MAX_OUTGOING_DATA]; | ||
142 | } __attribute__((packed)); | ||
143 | |||
144 | struct hvsi_control { | ||
145 | uint8_t type; | ||
146 | uint8_t len; | ||
147 | uint16_t seqno; | ||
148 | uint16_t verb; | ||
149 | /* optional depending on verb: */ | ||
150 | uint32_t word; | ||
151 | uint32_t mask; | ||
152 | } __attribute__((packed)); | ||
153 | |||
154 | struct hvsi_query { | ||
155 | uint8_t type; | ||
156 | uint8_t len; | ||
157 | uint16_t seqno; | ||
158 | uint16_t verb; | ||
159 | } __attribute__((packed)); | ||
160 | |||
161 | struct hvsi_query_response { | ||
162 | uint8_t type; | ||
163 | uint8_t len; | ||
164 | uint16_t seqno; | ||
165 | uint16_t verb; | ||
166 | uint16_t query_seqno; | ||
167 | union { | ||
168 | uint8_t version; | ||
169 | uint32_t mctrl_word; | ||
170 | } u; | ||
171 | } __attribute__((packed)); | ||
172 | |||
173 | |||
174 | |||
175 | static inline int is_console(struct hvsi_struct *hp) | ||
176 | { | ||
177 | return hp->flags & HVSI_CONSOLE; | ||
178 | } | ||
179 | |||
180 | static inline int is_open(struct hvsi_struct *hp) | ||
181 | { | ||
182 | /* if we're waiting for an mctrl then we're already open */ | ||
183 | return (hp->state == HVSI_OPEN) | ||
184 | || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); | ||
185 | } | ||
186 | |||
187 | static inline void print_state(struct hvsi_struct *hp) | ||
188 | { | ||
189 | #ifdef DEBUG | ||
190 | static const char *state_names[] = { | ||
191 | "HVSI_CLOSED", | ||
192 | "HVSI_WAIT_FOR_VER_RESPONSE", | ||
193 | "HVSI_WAIT_FOR_VER_QUERY", | ||
194 | "HVSI_OPEN", | ||
195 | "HVSI_WAIT_FOR_MCTRL_RESPONSE", | ||
196 | "HVSI_FSP_DIED", | ||
197 | }; | ||
198 | const char *name = state_names[hp->state]; | ||
199 | |||
200 | if (hp->state > (sizeof(state_names)/sizeof(char*))) | ||
201 | name = "UNKNOWN"; | ||
202 | |||
203 | pr_debug("hvsi%i: state = %s\n", hp->index, name); | ||
204 | #endif /* DEBUG */ | ||
205 | } | ||
206 | |||
207 | static inline void __set_state(struct hvsi_struct *hp, int state) | ||
208 | { | ||
209 | hp->state = state; | ||
210 | print_state(hp); | ||
211 | wake_up_all(&hp->stateq); | ||
212 | } | ||
213 | |||
214 | static inline void set_state(struct hvsi_struct *hp, int state) | ||
215 | { | ||
216 | unsigned long flags; | ||
217 | |||
218 | spin_lock_irqsave(&hp->lock, flags); | ||
219 | __set_state(hp, state); | ||
220 | spin_unlock_irqrestore(&hp->lock, flags); | ||
221 | } | ||
222 | |||
223 | static inline int len_packet(const uint8_t *packet) | ||
224 | { | ||
225 | return (int)((struct hvsi_header *)packet)->len; | ||
226 | } | ||
227 | |||
228 | static inline int is_header(const uint8_t *packet) | ||
229 | { | ||
230 | struct hvsi_header *header = (struct hvsi_header *)packet; | ||
231 | return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; | ||
232 | } | ||
233 | |||
234 | static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) | ||
235 | { | ||
236 | if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) | ||
237 | return 0; /* don't even have the packet header */ | ||
238 | |||
239 | if (hp->inbuf_end < (packet + len_packet(packet))) | ||
240 | return 0; /* don't have the rest of the packet */ | ||
241 | |||
242 | return 1; | ||
243 | } | ||
244 | |||
245 | /* shift remaining bytes in packetbuf down */ | ||
246 | static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) | ||
247 | { | ||
248 | int remaining = (int)(hp->inbuf_end - read_to); | ||
249 | |||
250 | pr_debug("%s: %i chars remain\n", __FUNCTION__, remaining); | ||
251 | |||
252 | if (read_to != hp->inbuf) | ||
253 | memmove(hp->inbuf, read_to, remaining); | ||
254 | |||
255 | hp->inbuf_end = hp->inbuf + remaining; | ||
256 | } | ||
257 | |||
258 | #ifdef DEBUG | ||
259 | #define dbg_dump_packet(packet) dump_packet(packet) | ||
260 | #define dbg_dump_hex(data, len) dump_hex(data, len) | ||
261 | #else | ||
262 | #define dbg_dump_packet(packet) do { } while (0) | ||
263 | #define dbg_dump_hex(data, len) do { } while (0) | ||
264 | #endif | ||
265 | |||
266 | static void dump_hex(const uint8_t *data, int len) | ||
267 | { | ||
268 | int i; | ||
269 | |||
270 | printk(" "); | ||
271 | for (i=0; i < len; i++) | ||
272 | printk("%.2x", data[i]); | ||
273 | |||
274 | printk("\n "); | ||
275 | for (i=0; i < len; i++) { | ||
276 | if (isprint(data[i])) | ||
277 | printk("%c", data[i]); | ||
278 | else | ||
279 | printk("."); | ||
280 | } | ||
281 | printk("\n"); | ||
282 | } | ||
283 | |||
284 | static void dump_packet(uint8_t *packet) | ||
285 | { | ||
286 | struct hvsi_header *header = (struct hvsi_header *)packet; | ||
287 | |||
288 | printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, | ||
289 | header->seqno); | ||
290 | |||
291 | dump_hex(packet, header->len); | ||
292 | } | ||
293 | |||
294 | /* can't use hvc_get_chars because that strips CRs */ | ||
295 | static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) | ||
296 | { | ||
297 | unsigned long got; | ||
298 | |||
299 | if (plpar_hcall(H_GET_TERM_CHAR, hp->vtermno, 0, 0, 0, &got, | ||
300 | (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) | ||
301 | return got; | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, | ||
306 | struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) | ||
307 | { | ||
308 | struct hvsi_control *header = (struct hvsi_control *)packet; | ||
309 | |||
310 | switch (header->verb) { | ||
311 | case VSV_MODEM_CTL_UPDATE: | ||
312 | if ((header->word & HVSI_TSCD) == 0) { | ||
313 | /* CD went away; no more connection */ | ||
314 | pr_debug("hvsi%i: CD dropped\n", hp->index); | ||
315 | hp->mctrl &= TIOCM_CD; | ||
316 | if (!(hp->tty->flags & CLOCAL)) | ||
317 | *to_hangup = hp->tty; | ||
318 | } | ||
319 | break; | ||
320 | case VSV_CLOSE_PROTOCOL: | ||
321 | pr_debug("hvsi%i: service processor came back\n", hp->index); | ||
322 | if (hp->state != HVSI_CLOSED) { | ||
323 | *to_handshake = hp; | ||
324 | } | ||
325 | break; | ||
326 | default: | ||
327 | printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", | ||
328 | hp->index); | ||
329 | dump_packet(packet); | ||
330 | break; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) | ||
335 | { | ||
336 | struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; | ||
337 | |||
338 | switch (hp->state) { | ||
339 | case HVSI_WAIT_FOR_VER_RESPONSE: | ||
340 | __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); | ||
341 | break; | ||
342 | case HVSI_WAIT_FOR_MCTRL_RESPONSE: | ||
343 | hp->mctrl = 0; | ||
344 | if (resp->u.mctrl_word & HVSI_TSDTR) | ||
345 | hp->mctrl |= TIOCM_DTR; | ||
346 | if (resp->u.mctrl_word & HVSI_TSCD) | ||
347 | hp->mctrl |= TIOCM_CD; | ||
348 | __set_state(hp, HVSI_OPEN); | ||
349 | break; | ||
350 | default: | ||
351 | printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); | ||
352 | dump_packet(packet); | ||
353 | break; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | /* respond to service processor's version query */ | ||
358 | static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) | ||
359 | { | ||
360 | struct hvsi_query_response packet __ALIGNED__; | ||
361 | int wrote; | ||
362 | |||
363 | packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; | ||
364 | packet.len = sizeof(struct hvsi_query_response); | ||
365 | packet.seqno = atomic_inc_return(&hp->seqno); | ||
366 | packet.verb = VSV_SEND_VERSION_NUMBER; | ||
367 | packet.u.version = HVSI_VERSION; | ||
368 | packet.query_seqno = query_seqno+1; | ||
369 | |||
370 | pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); | ||
371 | dbg_dump_hex((uint8_t*)&packet, packet.len); | ||
372 | |||
373 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | ||
374 | if (wrote != packet.len) { | ||
375 | printk(KERN_ERR "hvsi%i: couldn't send query response!\n", | ||
376 | hp->index); | ||
377 | return -EIO; | ||
378 | } | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) | ||
384 | { | ||
385 | struct hvsi_query *query = (struct hvsi_query *)packet; | ||
386 | |||
387 | switch (hp->state) { | ||
388 | case HVSI_WAIT_FOR_VER_QUERY: | ||
389 | hvsi_version_respond(hp, query->seqno); | ||
390 | __set_state(hp, HVSI_OPEN); | ||
391 | break; | ||
392 | default: | ||
393 | printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); | ||
394 | dump_packet(packet); | ||
395 | break; | ||
396 | } | ||
397 | } | ||
398 | |||
399 | static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) | ||
400 | { | ||
401 | int i; | ||
402 | |||
403 | for (i=0; i < len; i++) { | ||
404 | char c = buf[i]; | ||
405 | #ifdef CONFIG_MAGIC_SYSRQ | ||
406 | if (c == '\0') { | ||
407 | hp->sysrq = 1; | ||
408 | continue; | ||
409 | } else if (hp->sysrq) { | ||
410 | handle_sysrq(c, NULL, hp->tty); | ||
411 | hp->sysrq = 0; | ||
412 | continue; | ||
413 | } | ||
414 | #endif /* CONFIG_MAGIC_SYSRQ */ | ||
415 | tty_insert_flip_char(hp->tty, c, 0); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | /* | ||
420 | * We could get 252 bytes of data at once here. But the tty layer only | ||
421 | * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow | ||
422 | * it. Accordingly we won't send more than 128 bytes at a time to the flip | ||
423 | * buffer, which will give the tty buffer a chance to throttle us. Should the | ||
424 | * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be | ||
425 | * revisited. | ||
426 | */ | ||
427 | #define TTY_THRESHOLD_THROTTLE 128 | ||
428 | static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, | ||
429 | const uint8_t *packet) | ||
430 | { | ||
431 | const struct hvsi_header *header = (const struct hvsi_header *)packet; | ||
432 | const uint8_t *data = packet + sizeof(struct hvsi_header); | ||
433 | int datalen = header->len - sizeof(struct hvsi_header); | ||
434 | int overflow = datalen - TTY_THRESHOLD_THROTTLE; | ||
435 | |||
436 | pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); | ||
437 | |||
438 | if (datalen == 0) | ||
439 | return NULL; | ||
440 | |||
441 | if (overflow > 0) { | ||
442 | pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __FUNCTION__); | ||
443 | datalen = TTY_THRESHOLD_THROTTLE; | ||
444 | } | ||
445 | |||
446 | hvsi_insert_chars(hp, data, datalen); | ||
447 | |||
448 | if (overflow > 0) { | ||
449 | /* | ||
450 | * we still have more data to deliver, so we need to save off the | ||
451 | * overflow and send it later | ||
452 | */ | ||
453 | pr_debug("%s: deferring overflow\n", __FUNCTION__); | ||
454 | memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); | ||
455 | hp->n_throttle = overflow; | ||
456 | } | ||
457 | |||
458 | return hp->tty; | ||
459 | } | ||
460 | |||
461 | /* | ||
462 | * Returns true/false indicating data successfully read from hypervisor. | ||
463 | * Used both to get packets for tty connections and to advance the state | ||
464 | * machine during console handshaking (in which case tty = NULL and we ignore | ||
465 | * incoming data). | ||
466 | */ | ||
467 | static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, | ||
468 | struct tty_struct **hangup, struct hvsi_struct **handshake) | ||
469 | { | ||
470 | uint8_t *packet = hp->inbuf; | ||
471 | int chunklen; | ||
472 | |||
473 | *flip = NULL; | ||
474 | *hangup = NULL; | ||
475 | *handshake = NULL; | ||
476 | |||
477 | chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); | ||
478 | if (chunklen == 0) { | ||
479 | pr_debug("%s: 0-length read\n", __FUNCTION__); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | pr_debug("%s: got %i bytes\n", __FUNCTION__, chunklen); | ||
484 | dbg_dump_hex(hp->inbuf_end, chunklen); | ||
485 | |||
486 | hp->inbuf_end += chunklen; | ||
487 | |||
488 | /* handle all completed packets */ | ||
489 | while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { | ||
490 | struct hvsi_header *header = (struct hvsi_header *)packet; | ||
491 | |||
492 | if (!is_header(packet)) { | ||
493 | printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); | ||
494 | /* skip bytes until we find a header or run out of data */ | ||
495 | while ((packet < hp->inbuf_end) && (!is_header(packet))) | ||
496 | packet++; | ||
497 | continue; | ||
498 | } | ||
499 | |||
500 | pr_debug("%s: handling %i-byte packet\n", __FUNCTION__, | ||
501 | len_packet(packet)); | ||
502 | dbg_dump_packet(packet); | ||
503 | |||
504 | switch (header->type) { | ||
505 | case VS_DATA_PACKET_HEADER: | ||
506 | if (!is_open(hp)) | ||
507 | break; | ||
508 | if (hp->tty == NULL) | ||
509 | break; /* no tty buffer to put data in */ | ||
510 | *flip = hvsi_recv_data(hp, packet); | ||
511 | break; | ||
512 | case VS_CONTROL_PACKET_HEADER: | ||
513 | hvsi_recv_control(hp, packet, hangup, handshake); | ||
514 | break; | ||
515 | case VS_QUERY_RESPONSE_PACKET_HEADER: | ||
516 | hvsi_recv_response(hp, packet); | ||
517 | break; | ||
518 | case VS_QUERY_PACKET_HEADER: | ||
519 | hvsi_recv_query(hp, packet); | ||
520 | break; | ||
521 | default: | ||
522 | printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", | ||
523 | hp->index, header->type); | ||
524 | dump_packet(packet); | ||
525 | break; | ||
526 | } | ||
527 | |||
528 | packet += len_packet(packet); | ||
529 | |||
530 | if (*hangup || *handshake) { | ||
531 | pr_debug("%s: hangup or handshake\n", __FUNCTION__); | ||
532 | /* | ||
533 | * we need to send the hangup now before receiving any more data. | ||
534 | * If we get "data, hangup, data", we can't deliver the second | ||
535 | * data before the hangup. | ||
536 | */ | ||
537 | break; | ||
538 | } | ||
539 | } | ||
540 | |||
541 | compact_inbuf(hp, packet); | ||
542 | |||
543 | return 1; | ||
544 | } | ||
545 | |||
546 | static void hvsi_send_overflow(struct hvsi_struct *hp) | ||
547 | { | ||
548 | pr_debug("%s: delivering %i bytes overflow\n", __FUNCTION__, | ||
549 | hp->n_throttle); | ||
550 | |||
551 | hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); | ||
552 | hp->n_throttle = 0; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * must get all pending data because we only get an irq on empty->non-empty | ||
557 | * transition | ||
558 | */ | ||
559 | static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs) | ||
560 | { | ||
561 | struct hvsi_struct *hp = (struct hvsi_struct *)arg; | ||
562 | struct tty_struct *flip; | ||
563 | struct tty_struct *hangup; | ||
564 | struct hvsi_struct *handshake; | ||
565 | unsigned long flags; | ||
566 | int again = 1; | ||
567 | |||
568 | pr_debug("%s\n", __FUNCTION__); | ||
569 | |||
570 | while (again) { | ||
571 | spin_lock_irqsave(&hp->lock, flags); | ||
572 | again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); | ||
573 | spin_unlock_irqrestore(&hp->lock, flags); | ||
574 | |||
575 | /* | ||
576 | * we have to call tty_flip_buffer_push() and tty_hangup() outside our | ||
577 | * spinlock. But we also have to keep going until we've read all the | ||
578 | * available data. | ||
579 | */ | ||
580 | |||
581 | if (flip) { | ||
582 | /* there was data put in the tty flip buffer */ | ||
583 | tty_flip_buffer_push(flip); | ||
584 | flip = NULL; | ||
585 | } | ||
586 | |||
587 | if (hangup) { | ||
588 | tty_hangup(hangup); | ||
589 | } | ||
590 | |||
591 | if (handshake) { | ||
592 | pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); | ||
593 | schedule_work(&handshake->handshaker); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | spin_lock_irqsave(&hp->lock, flags); | ||
598 | if (hp->tty && hp->n_throttle | ||
599 | && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { | ||
600 | /* we weren't hung up and we weren't throttled, so we can deliver the | ||
601 | * rest now */ | ||
602 | flip = hp->tty; | ||
603 | hvsi_send_overflow(hp); | ||
604 | } | ||
605 | spin_unlock_irqrestore(&hp->lock, flags); | ||
606 | |||
607 | if (flip) { | ||
608 | tty_flip_buffer_push(flip); | ||
609 | } | ||
610 | |||
611 | return IRQ_HANDLED; | ||
612 | } | ||
613 | |||
614 | /* for boot console, before the irq handler is running */ | ||
615 | static int __init poll_for_state(struct hvsi_struct *hp, int state) | ||
616 | { | ||
617 | unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; | ||
618 | |||
619 | for (;;) { | ||
620 | hvsi_interrupt(hp->virq, (void *)hp, NULL); /* get pending data */ | ||
621 | |||
622 | if (hp->state == state) | ||
623 | return 0; | ||
624 | |||
625 | mdelay(5); | ||
626 | if (time_after(jiffies, end_jiffies)) | ||
627 | return -EIO; | ||
628 | } | ||
629 | } | ||
630 | |||
631 | /* wait for irq handler to change our state */ | ||
632 | static int wait_for_state(struct hvsi_struct *hp, int state) | ||
633 | { | ||
634 | int ret = 0; | ||
635 | |||
636 | if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) | ||
637 | ret = -EIO; | ||
638 | |||
639 | return ret; | ||
640 | } | ||
641 | |||
642 | static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) | ||
643 | { | ||
644 | struct hvsi_query packet __ALIGNED__; | ||
645 | int wrote; | ||
646 | |||
647 | packet.type = VS_QUERY_PACKET_HEADER; | ||
648 | packet.len = sizeof(struct hvsi_query); | ||
649 | packet.seqno = atomic_inc_return(&hp->seqno); | ||
650 | packet.verb = verb; | ||
651 | |||
652 | pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); | ||
653 | dbg_dump_hex((uint8_t*)&packet, packet.len); | ||
654 | |||
655 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | ||
656 | if (wrote != packet.len) { | ||
657 | printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, | ||
658 | wrote); | ||
659 | return -EIO; | ||
660 | } | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static int hvsi_get_mctrl(struct hvsi_struct *hp) | ||
666 | { | ||
667 | int ret; | ||
668 | |||
669 | set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); | ||
670 | hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); | ||
671 | |||
672 | ret = hvsi_wait(hp, HVSI_OPEN); | ||
673 | if (ret < 0) { | ||
674 | printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); | ||
675 | set_state(hp, HVSI_OPEN); | ||
676 | return ret; | ||
677 | } | ||
678 | |||
679 | pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, hp->mctrl); | ||
680 | |||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | /* note that we can only set DTR */ | ||
685 | static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) | ||
686 | { | ||
687 | struct hvsi_control packet __ALIGNED__; | ||
688 | int wrote; | ||
689 | |||
690 | packet.type = VS_CONTROL_PACKET_HEADER, | ||
691 | packet.seqno = atomic_inc_return(&hp->seqno); | ||
692 | packet.len = sizeof(struct hvsi_control); | ||
693 | packet.verb = VSV_SET_MODEM_CTL; | ||
694 | packet.mask = HVSI_TSDTR; | ||
695 | |||
696 | if (mctrl & TIOCM_DTR) | ||
697 | packet.word = HVSI_TSDTR; | ||
698 | |||
699 | pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); | ||
700 | dbg_dump_hex((uint8_t*)&packet, packet.len); | ||
701 | |||
702 | wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | ||
703 | if (wrote != packet.len) { | ||
704 | printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); | ||
705 | return -EIO; | ||
706 | } | ||
707 | |||
708 | return 0; | ||
709 | } | ||
710 | |||
711 | static void hvsi_drain_input(struct hvsi_struct *hp) | ||
712 | { | ||
713 | uint8_t buf[HVSI_MAX_READ] __ALIGNED__; | ||
714 | unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; | ||
715 | |||
716 | while (time_before(end_jiffies, jiffies)) | ||
717 | if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) | ||
718 | break; | ||
719 | } | ||
720 | |||
721 | static int hvsi_handshake(struct hvsi_struct *hp) | ||
722 | { | ||
723 | int ret; | ||
724 | |||
725 | /* | ||
726 | * We could have a CLOSE or other data waiting for us before we even try | ||
727 | * to open; try to throw it all away so we don't get confused. (CLOSE | ||
728 | * is the first message sent up the pipe when the FSP comes online. We | ||
729 | * need to distinguish between "it came up a while ago and we're the first | ||
730 | * user" and "it was just reset before it saw our handshake packet".) | ||
731 | */ | ||
732 | hvsi_drain_input(hp); | ||
733 | |||
734 | set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); | ||
735 | ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); | ||
736 | if (ret < 0) { | ||
737 | printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); | ||
738 | return ret; | ||
739 | } | ||
740 | |||
741 | ret = hvsi_wait(hp, HVSI_OPEN); | ||
742 | if (ret < 0) | ||
743 | return ret; | ||
744 | |||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static void hvsi_handshaker(void *arg) | ||
749 | { | ||
750 | struct hvsi_struct *hp = (struct hvsi_struct *)arg; | ||
751 | |||
752 | if (hvsi_handshake(hp) >= 0) | ||
753 | return; | ||
754 | |||
755 | printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); | ||
756 | if (is_console(hp)) { | ||
757 | /* | ||
758 | * ttys will re-attempt the handshake via hvsi_open, but | ||
759 | * the console will not. | ||
760 | */ | ||
761 | printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); | ||
762 | } | ||
763 | } | ||
764 | |||
765 | static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) | ||
766 | { | ||
767 | struct hvsi_data packet __ALIGNED__; | ||
768 | int ret; | ||
769 | |||
770 | BUG_ON(count > HVSI_MAX_OUTGOING_DATA); | ||
771 | |||
772 | packet.type = VS_DATA_PACKET_HEADER; | ||
773 | packet.seqno = atomic_inc_return(&hp->seqno); | ||
774 | packet.len = count + sizeof(struct hvsi_header); | ||
775 | memcpy(&packet.data, buf, count); | ||
776 | |||
777 | ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | ||
778 | if (ret == packet.len) { | ||
779 | /* return the number of chars written, not the packet length */ | ||
780 | return count; | ||
781 | } | ||
782 | return ret; /* return any errors */ | ||
783 | } | ||
784 | |||
785 | static void hvsi_close_protocol(struct hvsi_struct *hp) | ||
786 | { | ||
787 | struct hvsi_control packet __ALIGNED__; | ||
788 | |||
789 | packet.type = VS_CONTROL_PACKET_HEADER; | ||
790 | packet.seqno = atomic_inc_return(&hp->seqno); | ||
791 | packet.len = 6; | ||
792 | packet.verb = VSV_CLOSE_PROTOCOL; | ||
793 | |||
794 | pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); | ||
795 | dbg_dump_hex((uint8_t*)&packet, packet.len); | ||
796 | |||
797 | hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); | ||
798 | } | ||
799 | |||
800 | static int hvsi_open(struct tty_struct *tty, struct file *filp) | ||
801 | { | ||
802 | struct hvsi_struct *hp; | ||
803 | unsigned long flags; | ||
804 | int line = tty->index; | ||
805 | int ret; | ||
806 | |||
807 | pr_debug("%s\n", __FUNCTION__); | ||
808 | |||
809 | if (line < 0 || line >= hvsi_count) | ||
810 | return -ENODEV; | ||
811 | hp = &hvsi_ports[line]; | ||
812 | |||
813 | tty->driver_data = hp; | ||
814 | tty->low_latency = 1; /* avoid throttle/tty_flip_buffer_push race */ | ||
815 | |||
816 | mb(); | ||
817 | if (hp->state == HVSI_FSP_DIED) | ||
818 | return -EIO; | ||
819 | |||
820 | spin_lock_irqsave(&hp->lock, flags); | ||
821 | hp->tty = tty; | ||
822 | hp->count++; | ||
823 | atomic_set(&hp->seqno, 0); | ||
824 | h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); | ||
825 | spin_unlock_irqrestore(&hp->lock, flags); | ||
826 | |||
827 | if (is_console(hp)) | ||
828 | return 0; /* this has already been handshaked as the console */ | ||
829 | |||
830 | ret = hvsi_handshake(hp); | ||
831 | if (ret < 0) { | ||
832 | printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | ret = hvsi_get_mctrl(hp); | ||
837 | if (ret < 0) { | ||
838 | printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); | ||
839 | return ret; | ||
840 | } | ||
841 | |||
842 | ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); | ||
843 | if (ret < 0) { | ||
844 | printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); | ||
845 | return ret; | ||
846 | } | ||
847 | |||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | /* wait for hvsi_write_worker to empty hp->outbuf */ | ||
852 | static void hvsi_flush_output(struct hvsi_struct *hp) | ||
853 | { | ||
854 | wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); | ||
855 | |||
856 | /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ | ||
857 | cancel_delayed_work(&hp->writer); | ||
858 | flush_scheduled_work(); | ||
859 | |||
860 | /* | ||
861 | * it's also possible that our timeout expired and hvsi_write_worker | ||
862 | * didn't manage to push outbuf. poof. | ||
863 | */ | ||
864 | hp->n_outbuf = 0; | ||
865 | } | ||
866 | |||
867 | static void hvsi_close(struct tty_struct *tty, struct file *filp) | ||
868 | { | ||
869 | struct hvsi_struct *hp = tty->driver_data; | ||
870 | unsigned long flags; | ||
871 | |||
872 | pr_debug("%s\n", __FUNCTION__); | ||
873 | |||
874 | if (tty_hung_up_p(filp)) | ||
875 | return; | ||
876 | |||
877 | spin_lock_irqsave(&hp->lock, flags); | ||
878 | |||
879 | if (--hp->count == 0) { | ||
880 | hp->tty = NULL; | ||
881 | hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ | ||
882 | |||
883 | /* only close down connection if it is not the console */ | ||
884 | if (!is_console(hp)) { | ||
885 | h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ | ||
886 | __set_state(hp, HVSI_CLOSED); | ||
887 | /* | ||
888 | * any data delivered to the tty layer after this will be | ||
889 | * discarded (except for XON/XOFF) | ||
890 | */ | ||
891 | tty->closing = 1; | ||
892 | |||
893 | spin_unlock_irqrestore(&hp->lock, flags); | ||
894 | |||
895 | /* let any existing irq handlers finish. no more will start. */ | ||
896 | synchronize_irq(hp->virq); | ||
897 | |||
898 | /* hvsi_write_worker will re-schedule until outbuf is empty. */ | ||
899 | hvsi_flush_output(hp); | ||
900 | |||
901 | /* tell FSP to stop sending data */ | ||
902 | hvsi_close_protocol(hp); | ||
903 | |||
904 | /* | ||
905 | * drain anything FSP is still in the middle of sending, and let | ||
906 | * hvsi_handshake drain the rest on the next open. | ||
907 | */ | ||
908 | hvsi_drain_input(hp); | ||
909 | |||
910 | spin_lock_irqsave(&hp->lock, flags); | ||
911 | } | ||
912 | } else if (hp->count < 0) | ||
913 | printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", | ||
914 | hp - hvsi_ports, hp->count); | ||
915 | |||
916 | spin_unlock_irqrestore(&hp->lock, flags); | ||
917 | } | ||
918 | |||
919 | static void hvsi_hangup(struct tty_struct *tty) | ||
920 | { | ||
921 | struct hvsi_struct *hp = tty->driver_data; | ||
922 | unsigned long flags; | ||
923 | |||
924 | pr_debug("%s\n", __FUNCTION__); | ||
925 | |||
926 | spin_lock_irqsave(&hp->lock, flags); | ||
927 | |||
928 | hp->count = 0; | ||
929 | hp->n_outbuf = 0; | ||
930 | hp->tty = NULL; | ||
931 | |||
932 | spin_unlock_irqrestore(&hp->lock, flags); | ||
933 | } | ||
934 | |||
935 | /* called with hp->lock held */ | ||
936 | static void hvsi_push(struct hvsi_struct *hp) | ||
937 | { | ||
938 | int n; | ||
939 | |||
940 | if (hp->n_outbuf <= 0) | ||
941 | return; | ||
942 | |||
943 | n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); | ||
944 | if (n > 0) { | ||
945 | /* success */ | ||
946 | pr_debug("%s: wrote %i chars\n", __FUNCTION__, n); | ||
947 | hp->n_outbuf = 0; | ||
948 | } else if (n == -EIO) { | ||
949 | __set_state(hp, HVSI_FSP_DIED); | ||
950 | printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); | ||
951 | } | ||
952 | } | ||
953 | |||
954 | /* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ | ||
955 | static void hvsi_write_worker(void *arg) | ||
956 | { | ||
957 | struct hvsi_struct *hp = (struct hvsi_struct *)arg; | ||
958 | unsigned long flags; | ||
959 | #ifdef DEBUG | ||
960 | static long start_j = 0; | ||
961 | |||
962 | if (start_j == 0) | ||
963 | start_j = jiffies; | ||
964 | #endif /* DEBUG */ | ||
965 | |||
966 | spin_lock_irqsave(&hp->lock, flags); | ||
967 | |||
968 | pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); | ||
969 | |||
970 | if (!is_open(hp)) { | ||
971 | /* | ||
972 | * We could have a non-open connection if the service processor died | ||
973 | * while we were busily scheduling ourselves. In that case, it could | ||
974 | * be minutes before the service processor comes back, so only try | ||
975 | * again once a second. | ||
976 | */ | ||
977 | schedule_delayed_work(&hp->writer, HZ); | ||
978 | goto out; | ||
979 | } | ||
980 | |||
981 | hvsi_push(hp); | ||
982 | if (hp->n_outbuf > 0) | ||
983 | schedule_delayed_work(&hp->writer, 10); | ||
984 | else { | ||
985 | #ifdef DEBUG | ||
986 | pr_debug("%s: outbuf emptied after %li jiffies\n", __FUNCTION__, | ||
987 | jiffies - start_j); | ||
988 | start_j = 0; | ||
989 | #endif /* DEBUG */ | ||
990 | wake_up_all(&hp->emptyq); | ||
991 | if (test_bit(TTY_DO_WRITE_WAKEUP, &hp->tty->flags) | ||
992 | && hp->tty->ldisc.write_wakeup) | ||
993 | hp->tty->ldisc.write_wakeup(hp->tty); | ||
994 | wake_up_interruptible(&hp->tty->write_wait); | ||
995 | } | ||
996 | |||
997 | out: | ||
998 | spin_unlock_irqrestore(&hp->lock, flags); | ||
999 | } | ||
1000 | |||
1001 | static int hvsi_write_room(struct tty_struct *tty) | ||
1002 | { | ||
1003 | struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; | ||
1004 | |||
1005 | return N_OUTBUF - hp->n_outbuf; | ||
1006 | } | ||
1007 | |||
1008 | static int hvsi_chars_in_buffer(struct tty_struct *tty) | ||
1009 | { | ||
1010 | struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; | ||
1011 | |||
1012 | return hp->n_outbuf; | ||
1013 | } | ||
1014 | |||
1015 | static int hvsi_write(struct tty_struct *tty, | ||
1016 | const unsigned char *buf, int count) | ||
1017 | { | ||
1018 | struct hvsi_struct *hp = tty->driver_data; | ||
1019 | const char *source = buf; | ||
1020 | unsigned long flags; | ||
1021 | int total = 0; | ||
1022 | int origcount = count; | ||
1023 | |||
1024 | spin_lock_irqsave(&hp->lock, flags); | ||
1025 | |||
1026 | pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); | ||
1027 | |||
1028 | if (!is_open(hp)) { | ||
1029 | /* we're either closing or not yet open; don't accept data */ | ||
1030 | pr_debug("%s: not open\n", __FUNCTION__); | ||
1031 | goto out; | ||
1032 | } | ||
1033 | |||
1034 | /* | ||
1035 | * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf | ||
1036 | * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls | ||
1037 | * will see there is no room in outbuf and return. | ||
1038 | */ | ||
1039 | while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { | ||
1040 | int chunksize = min(count, hvsi_write_room(hp->tty)); | ||
1041 | |||
1042 | BUG_ON(hp->n_outbuf < 0); | ||
1043 | memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); | ||
1044 | hp->n_outbuf += chunksize; | ||
1045 | |||
1046 | total += chunksize; | ||
1047 | source += chunksize; | ||
1048 | count -= chunksize; | ||
1049 | hvsi_push(hp); | ||
1050 | } | ||
1051 | |||
1052 | if (hp->n_outbuf > 0) { | ||
1053 | /* | ||
1054 | * we weren't able to write it all to the hypervisor. | ||
1055 | * schedule another push attempt. | ||
1056 | */ | ||
1057 | schedule_delayed_work(&hp->writer, 10); | ||
1058 | } | ||
1059 | |||
1060 | out: | ||
1061 | spin_unlock_irqrestore(&hp->lock, flags); | ||
1062 | |||
1063 | if (total != origcount) | ||
1064 | pr_debug("%s: wanted %i, only wrote %i\n", __FUNCTION__, origcount, | ||
1065 | total); | ||
1066 | |||
1067 | return total; | ||
1068 | } | ||
1069 | |||
1070 | /* | ||
1071 | * I have never seen throttle or unthrottle called, so this little throttle | ||
1072 | * buffering scheme may or may not work. | ||
1073 | */ | ||
1074 | static void hvsi_throttle(struct tty_struct *tty) | ||
1075 | { | ||
1076 | struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; | ||
1077 | |||
1078 | pr_debug("%s\n", __FUNCTION__); | ||
1079 | |||
1080 | h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); | ||
1081 | } | ||
1082 | |||
1083 | static void hvsi_unthrottle(struct tty_struct *tty) | ||
1084 | { | ||
1085 | struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; | ||
1086 | unsigned long flags; | ||
1087 | int shouldflip = 0; | ||
1088 | |||
1089 | pr_debug("%s\n", __FUNCTION__); | ||
1090 | |||
1091 | spin_lock_irqsave(&hp->lock, flags); | ||
1092 | if (hp->n_throttle) { | ||
1093 | hvsi_send_overflow(hp); | ||
1094 | shouldflip = 1; | ||
1095 | } | ||
1096 | spin_unlock_irqrestore(&hp->lock, flags); | ||
1097 | |||
1098 | if (shouldflip) | ||
1099 | tty_flip_buffer_push(hp->tty); | ||
1100 | |||
1101 | h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); | ||
1102 | } | ||
1103 | |||
1104 | static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) | ||
1105 | { | ||
1106 | struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; | ||
1107 | |||
1108 | hvsi_get_mctrl(hp); | ||
1109 | return hp->mctrl; | ||
1110 | } | ||
1111 | |||
1112 | static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, | ||
1113 | unsigned int set, unsigned int clear) | ||
1114 | { | ||
1115 | struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; | ||
1116 | unsigned long flags; | ||
1117 | uint16_t new_mctrl; | ||
1118 | |||
1119 | /* we can only alter DTR */ | ||
1120 | clear &= TIOCM_DTR; | ||
1121 | set &= TIOCM_DTR; | ||
1122 | |||
1123 | spin_lock_irqsave(&hp->lock, flags); | ||
1124 | |||
1125 | new_mctrl = (hp->mctrl & ~clear) | set; | ||
1126 | |||
1127 | if (hp->mctrl != new_mctrl) { | ||
1128 | hvsi_set_mctrl(hp, new_mctrl); | ||
1129 | hp->mctrl = new_mctrl; | ||
1130 | } | ||
1131 | spin_unlock_irqrestore(&hp->lock, flags); | ||
1132 | |||
1133 | return 0; | ||
1134 | } | ||
1135 | |||
1136 | |||
1137 | static struct tty_operations hvsi_ops = { | ||
1138 | .open = hvsi_open, | ||
1139 | .close = hvsi_close, | ||
1140 | .write = hvsi_write, | ||
1141 | .hangup = hvsi_hangup, | ||
1142 | .write_room = hvsi_write_room, | ||
1143 | .chars_in_buffer = hvsi_chars_in_buffer, | ||
1144 | .throttle = hvsi_throttle, | ||
1145 | .unthrottle = hvsi_unthrottle, | ||
1146 | .tiocmget = hvsi_tiocmget, | ||
1147 | .tiocmset = hvsi_tiocmset, | ||
1148 | }; | ||
1149 | |||
1150 | static int __init hvsi_init(void) | ||
1151 | { | ||
1152 | int i; | ||
1153 | |||
1154 | hvsi_driver = alloc_tty_driver(hvsi_count); | ||
1155 | if (!hvsi_driver) | ||
1156 | return -ENOMEM; | ||
1157 | |||
1158 | hvsi_driver->owner = THIS_MODULE; | ||
1159 | hvsi_driver->devfs_name = "hvsi/"; | ||
1160 | hvsi_driver->driver_name = "hvsi"; | ||
1161 | hvsi_driver->name = "hvsi"; | ||
1162 | hvsi_driver->major = HVSI_MAJOR; | ||
1163 | hvsi_driver->minor_start = HVSI_MINOR; | ||
1164 | hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; | ||
1165 | hvsi_driver->init_termios = tty_std_termios; | ||
1166 | hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; | ||
1167 | hvsi_driver->flags = TTY_DRIVER_REAL_RAW; | ||
1168 | tty_set_operations(hvsi_driver, &hvsi_ops); | ||
1169 | |||
1170 | for (i=0; i < hvsi_count; i++) { | ||
1171 | struct hvsi_struct *hp = &hvsi_ports[i]; | ||
1172 | int ret = 1; | ||
1173 | |||
1174 | ret = request_irq(hp->virq, hvsi_interrupt, SA_INTERRUPT, "hvsi", hp); | ||
1175 | if (ret) | ||
1176 | printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", | ||
1177 | hp->virq, ret); | ||
1178 | } | ||
1179 | hvsi_wait = wait_for_state; /* irqs active now */ | ||
1180 | |||
1181 | if (tty_register_driver(hvsi_driver)) | ||
1182 | panic("Couldn't register hvsi console driver\n"); | ||
1183 | |||
1184 | printk(KERN_INFO "HVSI: registered %i devices\n", hvsi_count); | ||
1185 | |||
1186 | return 0; | ||
1187 | } | ||
1188 | device_initcall(hvsi_init); | ||
1189 | |||
1190 | /***** console (not tty) code: *****/ | ||
1191 | |||
1192 | static void hvsi_console_print(struct console *console, const char *buf, | ||
1193 | unsigned int count) | ||
1194 | { | ||
1195 | struct hvsi_struct *hp = &hvsi_ports[console->index]; | ||
1196 | char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; | ||
1197 | unsigned int i = 0, n = 0; | ||
1198 | int ret, donecr = 0; | ||
1199 | |||
1200 | mb(); | ||
1201 | if (!is_open(hp)) | ||
1202 | return; | ||
1203 | |||
1204 | /* | ||
1205 | * ugh, we have to translate LF -> CRLF ourselves, in place. | ||
1206 | * copied from hvc_console.c: | ||
1207 | */ | ||
1208 | while (count > 0 || i > 0) { | ||
1209 | if (count > 0 && i < sizeof(c)) { | ||
1210 | if (buf[n] == '\n' && !donecr) { | ||
1211 | c[i++] = '\r'; | ||
1212 | donecr = 1; | ||
1213 | } else { | ||
1214 | c[i++] = buf[n++]; | ||
1215 | donecr = 0; | ||
1216 | --count; | ||
1217 | } | ||
1218 | } else { | ||
1219 | ret = hvsi_put_chars(hp, c, i); | ||
1220 | if (ret < 0) | ||
1221 | i = 0; | ||
1222 | i -= ret; | ||
1223 | } | ||
1224 | } | ||
1225 | } | ||
1226 | |||
1227 | static struct tty_driver *hvsi_console_device(struct console *console, | ||
1228 | int *index) | ||
1229 | { | ||
1230 | *index = console->index; | ||
1231 | return hvsi_driver; | ||
1232 | } | ||
1233 | |||
1234 | static int __init hvsi_console_setup(struct console *console, char *options) | ||
1235 | { | ||
1236 | struct hvsi_struct *hp = &hvsi_ports[console->index]; | ||
1237 | int ret; | ||
1238 | |||
1239 | if (console->index < 0 || console->index >= hvsi_count) | ||
1240 | return -1; | ||
1241 | |||
1242 | /* give the FSP a chance to change the baud rate when we re-open */ | ||
1243 | hvsi_close_protocol(hp); | ||
1244 | |||
1245 | ret = hvsi_handshake(hp); | ||
1246 | if (ret < 0) | ||
1247 | return ret; | ||
1248 | |||
1249 | ret = hvsi_get_mctrl(hp); | ||
1250 | if (ret < 0) | ||
1251 | return ret; | ||
1252 | |||
1253 | ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); | ||
1254 | if (ret < 0) | ||
1255 | return ret; | ||
1256 | |||
1257 | hp->flags |= HVSI_CONSOLE; | ||
1258 | |||
1259 | return 0; | ||
1260 | } | ||
1261 | |||
1262 | static struct console hvsi_con_driver = { | ||
1263 | .name = "hvsi", | ||
1264 | .write = hvsi_console_print, | ||
1265 | .device = hvsi_console_device, | ||
1266 | .setup = hvsi_console_setup, | ||
1267 | .flags = CON_PRINTBUFFER, | ||
1268 | .index = -1, | ||
1269 | }; | ||
1270 | |||
1271 | static int __init hvsi_console_init(void) | ||
1272 | { | ||
1273 | struct device_node *vty; | ||
1274 | |||
1275 | hvsi_wait = poll_for_state; /* no irqs yet; must poll */ | ||
1276 | |||
1277 | /* search device tree for vty nodes */ | ||
1278 | for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); | ||
1279 | vty != NULL; | ||
1280 | vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { | ||
1281 | struct hvsi_struct *hp; | ||
1282 | uint32_t *vtermno; | ||
1283 | uint32_t *irq; | ||
1284 | |||
1285 | vtermno = (uint32_t *)get_property(vty, "reg", NULL); | ||
1286 | irq = (uint32_t *)get_property(vty, "interrupts", NULL); | ||
1287 | if (!vtermno || !irq) | ||
1288 | continue; | ||
1289 | |||
1290 | if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { | ||
1291 | of_node_put(vty); | ||
1292 | break; | ||
1293 | } | ||
1294 | |||
1295 | hp = &hvsi_ports[hvsi_count]; | ||
1296 | INIT_WORK(&hp->writer, hvsi_write_worker, hp); | ||
1297 | INIT_WORK(&hp->handshaker, hvsi_handshaker, hp); | ||
1298 | init_waitqueue_head(&hp->emptyq); | ||
1299 | init_waitqueue_head(&hp->stateq); | ||
1300 | spin_lock_init(&hp->lock); | ||
1301 | hp->index = hvsi_count; | ||
1302 | hp->inbuf_end = hp->inbuf; | ||
1303 | hp->state = HVSI_CLOSED; | ||
1304 | hp->vtermno = *vtermno; | ||
1305 | hp->virq = virt_irq_create_mapping(irq[0]); | ||
1306 | if (hp->virq == NO_IRQ) { | ||
1307 | printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", | ||
1308 | __FUNCTION__, hp->virq); | ||
1309 | continue; | ||
1310 | } else | ||
1311 | hp->virq = irq_offset_up(hp->virq); | ||
1312 | |||
1313 | hvsi_count++; | ||
1314 | } | ||
1315 | |||
1316 | if (hvsi_count) | ||
1317 | register_console(&hvsi_con_driver); | ||
1318 | return 0; | ||
1319 | } | ||
1320 | console_initcall(hvsi_console_init); | ||