diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-04-29 02:44:24 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-06-29 03:48:37 -0400 |
commit | 17bdc6c0e979ae61879806e4dd93ec3b169d0931 (patch) | |
tree | 35cc40c2a378d6abe696765bbf4b0bc7deff54ec /drivers | |
parent | 4d2bb3f5003617cb42b89faefd0009c505c3abd5 (diff) |
powerpc/pseries: Move hvsi support into a library
This will allow a different backend to share it
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tty/hvc/Makefile | 2 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_vio.c | 405 | ||||
-rw-r--r-- | drivers/tty/hvc/hvsi_lib.c | 426 |
3 files changed, 448 insertions, 385 deletions
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 69a444b71c63..e29205316376 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o | 1 | obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o |
2 | obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o | 2 | obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o |
3 | obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o | 3 | obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o |
4 | obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o | 4 | obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o |
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index d4e0850e8051..ade73fae816a 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c | |||
@@ -67,22 +67,10 @@ typedef enum hv_protocol { | |||
67 | HV_PROTOCOL_HVSI | 67 | HV_PROTOCOL_HVSI |
68 | } hv_protocol_t; | 68 | } hv_protocol_t; |
69 | 69 | ||
70 | #define HV_INBUF_SIZE 255 | ||
71 | |||
72 | struct hvterm_priv { | 70 | struct hvterm_priv { |
73 | u32 termno; /* HV term number */ | 71 | u32 termno; /* HV term number */ |
74 | hv_protocol_t proto; /* Raw data or HVSI packets */ | 72 | hv_protocol_t proto; /* Raw data or HVSI packets */ |
75 | unsigned int inbuf_len; /* Data in input buffer */ | 73 | struct hvsi_priv hvsi; /* HVSI specific data */ |
76 | unsigned char inbuf[HV_INBUF_SIZE]; | ||
77 | unsigned int inbuf_cur; /* Cursor in input buffer */ | ||
78 | unsigned int inbuf_pktlen; /* HVSI packet lenght from cursor */ | ||
79 | atomic_t seqno; /* HVSI packet sequence number */ | ||
80 | unsigned int opened:1; /* HVSI driver opened */ | ||
81 | unsigned int established:1; /* HVSI protocol established */ | ||
82 | unsigned int is_console:1; /* Used as a kernel console device */ | ||
83 | unsigned int mctrl_update:1; /* HVSI modem control updated */ | ||
84 | unsigned short mctrl; /* HVSI modem control */ | ||
85 | struct tty_struct *tty; /* TTY structure */ | ||
86 | }; | 74 | }; |
87 | static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; | 75 | static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; |
88 | 76 | ||
@@ -139,348 +127,24 @@ static const struct hv_ops hvterm_raw_ops = { | |||
139 | .notifier_hangup = notifier_hangup_irq, | 127 | .notifier_hangup = notifier_hangup_irq, |
140 | }; | 128 | }; |
141 | 129 | ||
142 | static int hvterm_hvsi_send_packet(struct hvterm_priv *pv, struct hvsi_header *packet) | ||
143 | { | ||
144 | packet->seqno = atomic_inc_return(&pv->seqno); | ||
145 | |||
146 | /* Assumes that always succeeds, works in practice */ | ||
147 | return hvc_put_chars(pv->termno, (char *)packet, packet->len); | ||
148 | } | ||
149 | |||
150 | static void hvterm_hvsi_start_handshake(struct hvterm_priv *pv) | ||
151 | { | ||
152 | struct hvsi_query q; | ||
153 | |||
154 | /* Reset state */ | ||
155 | pv->established = 0; | ||
156 | atomic_set(&pv->seqno, 0); | ||
157 | |||
158 | pr_devel("HVSI@%x: Handshaking started\n", pv->termno); | ||
159 | |||
160 | /* Send version query */ | ||
161 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
162 | q.hdr.len = sizeof(struct hvsi_query); | ||
163 | q.verb = VSV_SEND_VERSION_NUMBER; | ||
164 | hvterm_hvsi_send_packet(pv, &q.hdr); | ||
165 | } | ||
166 | |||
167 | static int hvterm_hvsi_send_close(struct hvterm_priv *pv) | ||
168 | { | ||
169 | struct hvsi_control ctrl; | ||
170 | |||
171 | pv->established = 0; | ||
172 | |||
173 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; | ||
174 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
175 | ctrl.verb = VSV_CLOSE_PROTOCOL; | ||
176 | return hvterm_hvsi_send_packet(pv, &ctrl.hdr); | ||
177 | } | ||
178 | |||
179 | static void hvterm_cd_change(struct hvterm_priv *pv, int cd) | ||
180 | { | ||
181 | if (cd) | ||
182 | pv->mctrl |= TIOCM_CD; | ||
183 | else { | ||
184 | pv->mctrl &= ~TIOCM_CD; | ||
185 | |||
186 | /* We copy the existing hvsi driver semantics | ||
187 | * here which are to trigger a hangup when | ||
188 | * we get a carrier loss. | ||
189 | * Closing our connection to the server will | ||
190 | * do just that. | ||
191 | */ | ||
192 | if (!pv->is_console && pv->opened) { | ||
193 | pr_devel("HVSI@%x Carrier lost, hanging up !\n", | ||
194 | pv->termno); | ||
195 | hvterm_hvsi_send_close(pv); | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | static void hvterm_hvsi_got_control(struct hvterm_priv *pv) | ||
201 | { | ||
202 | struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; | ||
203 | |||
204 | switch (pkt->verb) { | ||
205 | case VSV_CLOSE_PROTOCOL: | ||
206 | /* We restart the handshaking */ | ||
207 | hvterm_hvsi_start_handshake(pv); | ||
208 | break; | ||
209 | case VSV_MODEM_CTL_UPDATE: | ||
210 | /* Transition of carrier detect */ | ||
211 | hvterm_cd_change(pv, pkt->word & HVSI_TSCD); | ||
212 | break; | ||
213 | } | ||
214 | } | ||
215 | |||
216 | static void hvterm_hvsi_got_query(struct hvterm_priv *pv) | ||
217 | { | ||
218 | struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf; | ||
219 | struct hvsi_query_response r; | ||
220 | |||
221 | /* We only handle version queries */ | ||
222 | if (pkt->verb != VSV_SEND_VERSION_NUMBER) | ||
223 | return; | ||
224 | |||
225 | pr_devel("HVSI@%x: Got version query, sending response...\n", | ||
226 | pv->termno); | ||
227 | |||
228 | /* Send version response */ | ||
229 | r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; | ||
230 | r.hdr.len = sizeof(struct hvsi_query_response); | ||
231 | r.verb = VSV_SEND_VERSION_NUMBER; | ||
232 | r.u.version = HVSI_VERSION; | ||
233 | r.query_seqno = pkt->hdr.seqno; | ||
234 | hvterm_hvsi_send_packet(pv, &r.hdr); | ||
235 | |||
236 | /* Assume protocol is open now */ | ||
237 | pv->established = 1; | ||
238 | } | ||
239 | |||
240 | static void hvterm_hvsi_got_response(struct hvterm_priv *pv) | ||
241 | { | ||
242 | struct hvsi_query_response *r = (struct hvsi_query_response *)pv->inbuf; | ||
243 | |||
244 | switch(r->verb) { | ||
245 | case VSV_SEND_MODEM_CTL_STATUS: | ||
246 | hvterm_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); | ||
247 | pv->mctrl_update = 1; | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | static int hvterm_hvsi_check_packet(struct hvterm_priv *pv) | ||
253 | { | ||
254 | u8 len, type; | ||
255 | |||
256 | /* Check header validity. If it's invalid, we ditch | ||
257 | * the whole buffer and hope we eventually resync | ||
258 | */ | ||
259 | if (pv->inbuf[0] < 0xfc) { | ||
260 | pv->inbuf_len = pv->inbuf_pktlen = 0; | ||
261 | return 0; | ||
262 | } | ||
263 | type = pv->inbuf[0]; | ||
264 | len = pv->inbuf[1]; | ||
265 | |||
266 | /* Packet incomplete ? */ | ||
267 | if (pv->inbuf_len < len) | ||
268 | return 0; | ||
269 | |||
270 | pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n", | ||
271 | pv->termno, type, len); | ||
272 | |||
273 | /* We have a packet, yay ! Handle it */ | ||
274 | switch(type) { | ||
275 | case VS_DATA_PACKET_HEADER: | ||
276 | pv->inbuf_pktlen = len - 4; | ||
277 | pv->inbuf_cur = 4; | ||
278 | return 1; | ||
279 | case VS_CONTROL_PACKET_HEADER: | ||
280 | hvterm_hvsi_got_control(pv); | ||
281 | break; | ||
282 | case VS_QUERY_PACKET_HEADER: | ||
283 | hvterm_hvsi_got_query(pv); | ||
284 | break; | ||
285 | case VS_QUERY_RESPONSE_PACKET_HEADER: | ||
286 | hvterm_hvsi_got_response(pv); | ||
287 | break; | ||
288 | } | ||
289 | |||
290 | /* Swallow packet and retry */ | ||
291 | pv->inbuf_len -= len; | ||
292 | memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len); | ||
293 | return 1; | ||
294 | } | ||
295 | |||
296 | static int hvterm_hvsi_get_packet(struct hvterm_priv *pv) | ||
297 | { | ||
298 | /* If we have room in the buffer, ask HV for more */ | ||
299 | if (pv->inbuf_len < HV_INBUF_SIZE) | ||
300 | pv->inbuf_len += hvc_get_chars(pv->termno, | ||
301 | &pv->inbuf[pv->inbuf_len], | ||
302 | HV_INBUF_SIZE - pv->inbuf_len); | ||
303 | /* | ||
304 | * If we have at least 4 bytes in the buffer, check for | ||
305 | * a full packet and retry | ||
306 | */ | ||
307 | if (pv->inbuf_len >= 4) | ||
308 | return hvterm_hvsi_check_packet(pv); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) | 130 | static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) |
313 | { | 131 | { |
314 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | 132 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
315 | unsigned int tries, read = 0; | ||
316 | 133 | ||
317 | if (WARN_ON(!pv)) | 134 | if (WARN_ON(!pv)) |
318 | return 0; | 135 | return 0; |
319 | 136 | ||
320 | /* If we aren't open, dont do anything in order to avoid races | 137 | return hvsi_get_chars(&pv->hvsi, buf, count); |
321 | * with connection establishment. The hvc core will call this | ||
322 | * before we have returned from notifier_add(), and we need to | ||
323 | * avoid multiple users playing with the receive buffer | ||
324 | */ | ||
325 | if (!pv->opened) | ||
326 | return 0; | ||
327 | |||
328 | /* We try twice, once with what data we have and once more | ||
329 | * after we try to fetch some more from the hypervisor | ||
330 | */ | ||
331 | for (tries = 1; count && tries < 2; tries++) { | ||
332 | /* Consume existing data packet */ | ||
333 | if (pv->inbuf_pktlen) { | ||
334 | unsigned int l = min(count, (int)pv->inbuf_pktlen); | ||
335 | memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l); | ||
336 | pv->inbuf_cur += l; | ||
337 | pv->inbuf_pktlen -= l; | ||
338 | count -= l; | ||
339 | read += l; | ||
340 | } | ||
341 | if (count == 0) | ||
342 | break; | ||
343 | |||
344 | /* Data packet fully consumed, move down remaning data */ | ||
345 | if (pv->inbuf_cur) { | ||
346 | pv->inbuf_len -= pv->inbuf_cur; | ||
347 | memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], pv->inbuf_len); | ||
348 | pv->inbuf_cur = 0; | ||
349 | } | ||
350 | |||
351 | /* Try to get another packet */ | ||
352 | if (hvterm_hvsi_get_packet(pv)) | ||
353 | tries--; | ||
354 | } | ||
355 | if (!pv->established) { | ||
356 | pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno); | ||
357 | return -EPIPE; | ||
358 | } | ||
359 | return read; | ||
360 | } | 138 | } |
361 | 139 | ||
362 | static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) | 140 | static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) |
363 | { | 141 | { |
364 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | 142 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
365 | struct hvsi_data dp; | ||
366 | int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); | ||
367 | 143 | ||
368 | if (WARN_ON(!pv)) | 144 | if (WARN_ON(!pv)) |
369 | return 0; | 145 | return 0; |
370 | 146 | ||
371 | dp.hdr.type = VS_DATA_PACKET_HEADER; | 147 | return hvsi_put_chars(&pv->hvsi, buf, count); |
372 | dp.hdr.len = adjcount + sizeof(struct hvsi_header); | ||
373 | memcpy(dp.data, buf, adjcount); | ||
374 | rc = hvterm_hvsi_send_packet(pv, &dp.hdr); | ||
375 | if (rc <= 0) | ||
376 | return rc; | ||
377 | return adjcount; | ||
378 | } | ||
379 | |||
380 | static void maybe_msleep(unsigned long ms) | ||
381 | { | ||
382 | /* During early boot, IRQs are disabled, use mdelay */ | ||
383 | if (irqs_disabled()) | ||
384 | mdelay(ms); | ||
385 | else | ||
386 | msleep(ms); | ||
387 | } | ||
388 | |||
389 | static int hvterm_hvsi_read_mctrl(struct hvterm_priv *pv) | ||
390 | { | ||
391 | struct hvsi_query q; | ||
392 | int rc, timeout; | ||
393 | |||
394 | pr_devel("HVSI@%x: Querying modem control status...\n", | ||
395 | pv->termno); | ||
396 | |||
397 | pv->mctrl_update = 0; | ||
398 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
399 | q.hdr.len = sizeof(struct hvsi_query); | ||
400 | q.hdr.seqno = atomic_inc_return(&pv->seqno); | ||
401 | q.verb = VSV_SEND_MODEM_CTL_STATUS; | ||
402 | rc = hvterm_hvsi_send_packet(pv, &q.hdr); | ||
403 | if (rc <= 0) { | ||
404 | pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); | ||
405 | return rc; | ||
406 | } | ||
407 | |||
408 | /* Try for up to 1s */ | ||
409 | for (timeout = 0; timeout < 1000; timeout++) { | ||
410 | if (!pv->established) | ||
411 | return -ENXIO; | ||
412 | if (pv->mctrl_update) | ||
413 | return 0; | ||
414 | if (!hvterm_hvsi_get_packet(pv)) | ||
415 | maybe_msleep(1); | ||
416 | } | ||
417 | return -EIO; | ||
418 | } | ||
419 | |||
420 | static int hvterm_hvsi_write_mctrl(struct hvterm_priv *pv, int dtr) | ||
421 | { | ||
422 | struct hvsi_control ctrl; | ||
423 | |||
424 | pr_devel("HVSI@%x: %s DTR...\n", pv->termno, | ||
425 | dtr ? "Setting" : "Clearing"); | ||
426 | |||
427 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, | ||
428 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
429 | ctrl.verb = VSV_SET_MODEM_CTL; | ||
430 | ctrl.mask = HVSI_TSDTR; | ||
431 | ctrl.word = dtr ? HVSI_TSDTR : 0; | ||
432 | if (dtr) | ||
433 | pv->mctrl |= TIOCM_DTR; | ||
434 | else | ||
435 | pv->mctrl &= ~TIOCM_DTR; | ||
436 | return hvterm_hvsi_send_packet(pv, &ctrl.hdr); | ||
437 | } | ||
438 | |||
439 | static void hvterm_hvsi_establish(struct hvterm_priv *pv) | ||
440 | { | ||
441 | int timeout; | ||
442 | |||
443 | /* Try for up to 10ms, there can be a packet to | ||
444 | * start the process waiting for us... | ||
445 | */ | ||
446 | for (timeout = 0; timeout < 10; timeout++) { | ||
447 | if (pv->established) | ||
448 | goto established; | ||
449 | if (!hvterm_hvsi_get_packet(pv)) | ||
450 | maybe_msleep(1); | ||
451 | } | ||
452 | |||
453 | /* Failed, send a close connection packet just | ||
454 | * in case | ||
455 | */ | ||
456 | hvterm_hvsi_send_close(pv); | ||
457 | |||
458 | /* Then restart handshake */ | ||
459 | hvterm_hvsi_start_handshake(pv); | ||
460 | |||
461 | /* Try for up to 100ms */ | ||
462 | for (timeout = 0; timeout < 100; timeout++) { | ||
463 | if (pv->established) | ||
464 | goto established; | ||
465 | if (!hvterm_hvsi_get_packet(pv)) | ||
466 | maybe_msleep(1); | ||
467 | } | ||
468 | |||
469 | if (!pv->established) { | ||
470 | pr_devel("HVSI@%x: Timeout handshaking, giving up !\n", | ||
471 | pv->termno); | ||
472 | return; | ||
473 | } | ||
474 | established: | ||
475 | /* Query modem control lines */ | ||
476 | hvterm_hvsi_read_mctrl(pv); | ||
477 | |||
478 | /* Set our own DTR */ | ||
479 | hvterm_hvsi_write_mctrl(pv, 1); | ||
480 | |||
481 | /* Set the opened flag so reads are allowed */ | ||
482 | wmb(); | ||
483 | pv->opened = 1; | ||
484 | } | 148 | } |
485 | 149 | ||
486 | static int hvterm_hvsi_open(struct hvc_struct *hp, int data) | 150 | static int hvterm_hvsi_open(struct hvc_struct *hp, int data) |
@@ -494,46 +158,16 @@ static int hvterm_hvsi_open(struct hvc_struct *hp, int data) | |||
494 | if (rc) | 158 | if (rc) |
495 | return rc; | 159 | return rc; |
496 | 160 | ||
497 | /* Keep track of the tty data structure */ | 161 | return hvsi_open(&pv->hvsi, hp); |
498 | pv->tty = tty_kref_get(hp->tty); | ||
499 | |||
500 | hvterm_hvsi_establish(pv); | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static void hvterm_hvsi_shutdown(struct hvc_struct *hp, struct hvterm_priv *pv) | ||
505 | { | ||
506 | unsigned long flags; | ||
507 | |||
508 | if (!pv->is_console) { | ||
509 | pr_devel("HVSI@%x: Not a console, tearing down\n", | ||
510 | pv->termno); | ||
511 | |||
512 | /* Clear opened, synchronize with khvcd */ | ||
513 | spin_lock_irqsave(&hp->lock, flags); | ||
514 | pv->opened = 0; | ||
515 | spin_unlock_irqrestore(&hp->lock, flags); | ||
516 | |||
517 | /* Clear our own DTR */ | ||
518 | if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL)) | ||
519 | hvterm_hvsi_write_mctrl(pv, 0); | ||
520 | |||
521 | /* Tear down the connection */ | ||
522 | hvterm_hvsi_send_close(pv); | ||
523 | } | ||
524 | |||
525 | if (pv->tty) | ||
526 | tty_kref_put(pv->tty); | ||
527 | pv->tty = NULL; | ||
528 | } | 162 | } |
529 | 163 | ||
530 | static void hvterm_hvsi_close(struct hvc_struct *hp, int data) | 164 | static void hvterm_hvsi_close(struct hvc_struct *hp, int data) |
531 | { | 165 | { |
532 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | 166 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; |
533 | 167 | ||
534 | pr_devel("HVSI@%x: close !\n", pv->termno); | 168 | pr_devel("HVSI@%x: do close !\n", pv->termno); |
535 | 169 | ||
536 | hvterm_hvsi_shutdown(hp, pv); | 170 | hvsi_close(&pv->hvsi, hp); |
537 | 171 | ||
538 | notifier_del_irq(hp, data); | 172 | notifier_del_irq(hp, data); |
539 | } | 173 | } |
@@ -542,9 +176,9 @@ void hvterm_hvsi_hangup(struct hvc_struct *hp, int data) | |||
542 | { | 176 | { |
543 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | 177 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; |
544 | 178 | ||
545 | pr_devel("HVSI@%x: hangup !\n", pv->termno); | 179 | pr_devel("HVSI@%x: do hangup !\n", pv->termno); |
546 | 180 | ||
547 | hvterm_hvsi_shutdown(hp, pv); | 181 | hvsi_close(&pv->hvsi, hp); |
548 | 182 | ||
549 | notifier_hangup_irq(hp, data); | 183 | notifier_hangup_irq(hp, data); |
550 | } | 184 | } |
@@ -555,7 +189,7 @@ static int hvterm_hvsi_tiocmget(struct hvc_struct *hp) | |||
555 | 189 | ||
556 | if (!pv) | 190 | if (!pv) |
557 | return -EINVAL; | 191 | return -EINVAL; |
558 | return pv->mctrl; | 192 | return pv->hvsi.mctrl; |
559 | } | 193 | } |
560 | 194 | ||
561 | static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, | 195 | static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, |
@@ -567,9 +201,9 @@ static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, | |||
567 | pv->termno, set, clear); | 201 | pv->termno, set, clear); |
568 | 202 | ||
569 | if (set & TIOCM_DTR) | 203 | if (set & TIOCM_DTR) |
570 | hvterm_hvsi_write_mctrl(pv, 1); | 204 | hvsi_write_mctrl(&pv->hvsi, 1); |
571 | else if (clear & TIOCM_DTR) | 205 | else if (clear & TIOCM_DTR) |
572 | hvterm_hvsi_write_mctrl(pv, 0); | 206 | hvsi_write_mctrl(&pv->hvsi, 0); |
573 | 207 | ||
574 | return 0; | 208 | return 0; |
575 | } | 209 | } |
@@ -633,6 +267,8 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, | |||
633 | pv->termno = vdev->unit_address; | 267 | pv->termno = vdev->unit_address; |
634 | pv->proto = proto; | 268 | pv->proto = proto; |
635 | hvterm_privs[termno] = pv; | 269 | hvterm_privs[termno] = pv; |
270 | hvsi_init(&pv->hvsi, hvc_get_chars, hvc_put_chars, | ||
271 | pv->termno, 0); | ||
636 | } | 272 | } |
637 | 273 | ||
638 | hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS); | 274 | hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS); |
@@ -770,7 +406,6 @@ void __init hvc_vio_init_early(void) | |||
770 | if (termno == NULL) | 406 | if (termno == NULL) |
771 | goto out; | 407 | goto out; |
772 | hvterm_priv0.termno = *termno; | 408 | hvterm_priv0.termno = *termno; |
773 | hvterm_priv0.is_console = 1; | ||
774 | hvterm_privs[0] = &hvterm_priv0; | 409 | hvterm_privs[0] = &hvterm_priv0; |
775 | 410 | ||
776 | /* Check the protocol */ | 411 | /* Check the protocol */ |
@@ -781,8 +416,10 @@ void __init hvc_vio_init_early(void) | |||
781 | else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { | 416 | else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { |
782 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | 417 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; |
783 | ops = &hvterm_hvsi_ops; | 418 | ops = &hvterm_hvsi_ops; |
419 | hvsi_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, | ||
420 | hvterm_priv0.termno, 1); | ||
784 | /* HVSI, perform the handshake now */ | 421 | /* HVSI, perform the handshake now */ |
785 | hvterm_hvsi_establish(&hvterm_priv0); | 422 | hvsi_establish(&hvterm_priv0.hvsi); |
786 | } else | 423 | } else |
787 | goto out; | 424 | goto out; |
788 | udbg_putc = udbg_hvc_putc; | 425 | udbg_putc = udbg_hvc_putc; |
@@ -810,7 +447,6 @@ void __init udbg_init_debug_lpar(void) | |||
810 | hvterm_privs[0] = &hvterm_priv0; | 447 | hvterm_privs[0] = &hvterm_priv0; |
811 | hvterm_priv0.termno = 0; | 448 | hvterm_priv0.termno = 0; |
812 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | 449 | hvterm_priv0.proto = HV_PROTOCOL_RAW; |
813 | hvterm_priv0.is_console = 1; | ||
814 | udbg_putc = udbg_hvc_putc; | 450 | udbg_putc = udbg_hvc_putc; |
815 | udbg_getc = udbg_hvc_getc; | 451 | udbg_getc = udbg_hvc_getc; |
816 | udbg_getc_poll = udbg_hvc_getc_poll; | 452 | udbg_getc_poll = udbg_hvc_getc_poll; |
@@ -823,10 +459,11 @@ void __init udbg_init_debug_lpar_hvsi(void) | |||
823 | hvterm_privs[0] = &hvterm_priv0; | 459 | hvterm_privs[0] = &hvterm_priv0; |
824 | hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO; | 460 | hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO; |
825 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | 461 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; |
826 | hvterm_priv0.is_console = 1; | ||
827 | udbg_putc = udbg_hvc_putc; | 462 | udbg_putc = udbg_hvc_putc; |
828 | udbg_getc = udbg_hvc_getc; | 463 | udbg_getc = udbg_hvc_getc; |
829 | udbg_getc_poll = udbg_hvc_getc_poll; | 464 | udbg_getc_poll = udbg_hvc_getc_poll; |
830 | hvterm_hvsi_establish(&hvterm_priv0); | 465 | hvsi_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, |
466 | hvterm_priv0.termno, 1); | ||
467 | hvsi_establish(&hvterm_priv0.hvsi); | ||
831 | } | 468 | } |
832 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */ | 469 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */ |
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c new file mode 100644 index 000000000000..9401fcb556f0 --- /dev/null +++ b/drivers/tty/hvc/hvsi_lib.c | |||
@@ -0,0 +1,426 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include <linux/init.h> | ||
3 | #include <linux/delay.h> | ||
4 | #include <linux/slab.h> | ||
5 | #include <linux/console.h> | ||
6 | #include <asm/hvsi.h> | ||
7 | |||
8 | #include "hvc_console.h" | ||
9 | |||
10 | static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet) | ||
11 | { | ||
12 | packet->seqno = atomic_inc_return(&pv->seqno); | ||
13 | |||
14 | /* Assumes that always succeeds, works in practice */ | ||
15 | return pv->put_chars(pv->termno, (char *)packet, packet->len); | ||
16 | } | ||
17 | |||
18 | static void hvsi_start_handshake(struct hvsi_priv *pv) | ||
19 | { | ||
20 | struct hvsi_query q; | ||
21 | |||
22 | /* Reset state */ | ||
23 | pv->established = 0; | ||
24 | atomic_set(&pv->seqno, 0); | ||
25 | |||
26 | pr_devel("HVSI@%x: Handshaking started\n", pv->termno); | ||
27 | |||
28 | /* Send version query */ | ||
29 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
30 | q.hdr.len = sizeof(struct hvsi_query); | ||
31 | q.verb = VSV_SEND_VERSION_NUMBER; | ||
32 | hvsi_send_packet(pv, &q.hdr); | ||
33 | } | ||
34 | |||
35 | static int hvsi_send_close(struct hvsi_priv *pv) | ||
36 | { | ||
37 | struct hvsi_control ctrl; | ||
38 | |||
39 | pv->established = 0; | ||
40 | |||
41 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; | ||
42 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
43 | ctrl.verb = VSV_CLOSE_PROTOCOL; | ||
44 | return hvsi_send_packet(pv, &ctrl.hdr); | ||
45 | } | ||
46 | |||
47 | static void hvsi_cd_change(struct hvsi_priv *pv, int cd) | ||
48 | { | ||
49 | if (cd) | ||
50 | pv->mctrl |= TIOCM_CD; | ||
51 | else { | ||
52 | pv->mctrl &= ~TIOCM_CD; | ||
53 | |||
54 | /* We copy the existing hvsi driver semantics | ||
55 | * here which are to trigger a hangup when | ||
56 | * we get a carrier loss. | ||
57 | * Closing our connection to the server will | ||
58 | * do just that. | ||
59 | */ | ||
60 | if (!pv->is_console && pv->opened) { | ||
61 | pr_devel("HVSI@%x Carrier lost, hanging up !\n", | ||
62 | pv->termno); | ||
63 | hvsi_send_close(pv); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static void hvsi_got_control(struct hvsi_priv *pv) | ||
69 | { | ||
70 | struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; | ||
71 | |||
72 | switch (pkt->verb) { | ||
73 | case VSV_CLOSE_PROTOCOL: | ||
74 | /* We restart the handshaking */ | ||
75 | hvsi_start_handshake(pv); | ||
76 | break; | ||
77 | case VSV_MODEM_CTL_UPDATE: | ||
78 | /* Transition of carrier detect */ | ||
79 | hvsi_cd_change(pv, pkt->word & HVSI_TSCD); | ||
80 | break; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | static void hvsi_got_query(struct hvsi_priv *pv) | ||
85 | { | ||
86 | struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf; | ||
87 | struct hvsi_query_response r; | ||
88 | |||
89 | /* We only handle version queries */ | ||
90 | if (pkt->verb != VSV_SEND_VERSION_NUMBER) | ||
91 | return; | ||
92 | |||
93 | pr_devel("HVSI@%x: Got version query, sending response...\n", | ||
94 | pv->termno); | ||
95 | |||
96 | /* Send version response */ | ||
97 | r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; | ||
98 | r.hdr.len = sizeof(struct hvsi_query_response); | ||
99 | r.verb = VSV_SEND_VERSION_NUMBER; | ||
100 | r.u.version = HVSI_VERSION; | ||
101 | r.query_seqno = pkt->hdr.seqno; | ||
102 | hvsi_send_packet(pv, &r.hdr); | ||
103 | |||
104 | /* Assume protocol is open now */ | ||
105 | pv->established = 1; | ||
106 | } | ||
107 | |||
108 | static void hvsi_got_response(struct hvsi_priv *pv) | ||
109 | { | ||
110 | struct hvsi_query_response *r = | ||
111 | (struct hvsi_query_response *)pv->inbuf; | ||
112 | |||
113 | switch(r->verb) { | ||
114 | case VSV_SEND_MODEM_CTL_STATUS: | ||
115 | hvsi_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); | ||
116 | pv->mctrl_update = 1; | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static int hvsi_check_packet(struct hvsi_priv *pv) | ||
122 | { | ||
123 | u8 len, type; | ||
124 | |||
125 | /* Check header validity. If it's invalid, we ditch | ||
126 | * the whole buffer and hope we eventually resync | ||
127 | */ | ||
128 | if (pv->inbuf[0] < 0xfc) { | ||
129 | pv->inbuf_len = pv->inbuf_pktlen = 0; | ||
130 | return 0; | ||
131 | } | ||
132 | type = pv->inbuf[0]; | ||
133 | len = pv->inbuf[1]; | ||
134 | |||
135 | /* Packet incomplete ? */ | ||
136 | if (pv->inbuf_len < len) | ||
137 | return 0; | ||
138 | |||
139 | pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n", | ||
140 | pv->termno, type, len); | ||
141 | |||
142 | /* We have a packet, yay ! Handle it */ | ||
143 | switch(type) { | ||
144 | case VS_DATA_PACKET_HEADER: | ||
145 | pv->inbuf_pktlen = len - 4; | ||
146 | pv->inbuf_cur = 4; | ||
147 | return 1; | ||
148 | case VS_CONTROL_PACKET_HEADER: | ||
149 | hvsi_got_control(pv); | ||
150 | break; | ||
151 | case VS_QUERY_PACKET_HEADER: | ||
152 | hvsi_got_query(pv); | ||
153 | break; | ||
154 | case VS_QUERY_RESPONSE_PACKET_HEADER: | ||
155 | hvsi_got_response(pv); | ||
156 | break; | ||
157 | } | ||
158 | |||
159 | /* Swallow packet and retry */ | ||
160 | pv->inbuf_len -= len; | ||
161 | memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len); | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | static int hvsi_get_packet(struct hvsi_priv *pv) | ||
166 | { | ||
167 | /* If we have room in the buffer, ask HV for more */ | ||
168 | if (pv->inbuf_len < HVSI_INBUF_SIZE) | ||
169 | pv->inbuf_len += pv->get_chars(pv->termno, | ||
170 | &pv->inbuf[pv->inbuf_len], | ||
171 | HVSI_INBUF_SIZE - pv->inbuf_len); | ||
172 | /* | ||
173 | * If we have at least 4 bytes in the buffer, check for | ||
174 | * a full packet and retry | ||
175 | */ | ||
176 | if (pv->inbuf_len >= 4) | ||
177 | return hvsi_check_packet(pv); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | int hvsi_get_chars(struct hvsi_priv *pv, char *buf, int count) | ||
182 | { | ||
183 | unsigned int tries, read = 0; | ||
184 | |||
185 | if (WARN_ON(!pv)) | ||
186 | return 0; | ||
187 | |||
188 | /* If we aren't open, don't do anything in order to avoid races | ||
189 | * with connection establishment. The hvc core will call this | ||
190 | * before we have returned from notifier_add(), and we need to | ||
191 | * avoid multiple users playing with the receive buffer | ||
192 | */ | ||
193 | if (!pv->opened) | ||
194 | return 0; | ||
195 | |||
196 | /* We try twice, once with what data we have and once more | ||
197 | * after we try to fetch some more from the hypervisor | ||
198 | */ | ||
199 | for (tries = 1; count && tries < 2; tries++) { | ||
200 | /* Consume existing data packet */ | ||
201 | if (pv->inbuf_pktlen) { | ||
202 | unsigned int l = min(count, (int)pv->inbuf_pktlen); | ||
203 | memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l); | ||
204 | pv->inbuf_cur += l; | ||
205 | pv->inbuf_pktlen -= l; | ||
206 | count -= l; | ||
207 | read += l; | ||
208 | } | ||
209 | if (count == 0) | ||
210 | break; | ||
211 | |||
212 | /* Data packet fully consumed, move down remaning data */ | ||
213 | if (pv->inbuf_cur) { | ||
214 | pv->inbuf_len -= pv->inbuf_cur; | ||
215 | memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], | ||
216 | pv->inbuf_len); | ||
217 | pv->inbuf_cur = 0; | ||
218 | } | ||
219 | |||
220 | /* Try to get another packet */ | ||
221 | if (hvsi_get_packet(pv)) | ||
222 | tries--; | ||
223 | } | ||
224 | if (!pv->established) { | ||
225 | pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno); | ||
226 | return -EPIPE; | ||
227 | } | ||
228 | return read; | ||
229 | } | ||
230 | |||
231 | int hvsi_put_chars(struct hvsi_priv *pv, const char *buf, int count) | ||
232 | { | ||
233 | struct hvsi_data dp; | ||
234 | int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); | ||
235 | |||
236 | if (WARN_ON(!pv)) | ||
237 | return 0; | ||
238 | |||
239 | dp.hdr.type = VS_DATA_PACKET_HEADER; | ||
240 | dp.hdr.len = adjcount + sizeof(struct hvsi_header); | ||
241 | memcpy(dp.data, buf, adjcount); | ||
242 | rc = hvsi_send_packet(pv, &dp.hdr); | ||
243 | if (rc <= 0) | ||
244 | return rc; | ||
245 | return adjcount; | ||
246 | } | ||
247 | |||
248 | static void maybe_msleep(unsigned long ms) | ||
249 | { | ||
250 | /* During early boot, IRQs are disabled, use mdelay */ | ||
251 | if (irqs_disabled()) | ||
252 | mdelay(ms); | ||
253 | else | ||
254 | msleep(ms); | ||
255 | } | ||
256 | |||
257 | int hvsi_read_mctrl(struct hvsi_priv *pv) | ||
258 | { | ||
259 | struct hvsi_query q; | ||
260 | int rc, timeout; | ||
261 | |||
262 | pr_devel("HVSI@%x: Querying modem control status...\n", | ||
263 | pv->termno); | ||
264 | |||
265 | pv->mctrl_update = 0; | ||
266 | q.hdr.type = VS_QUERY_PACKET_HEADER; | ||
267 | q.hdr.len = sizeof(struct hvsi_query); | ||
268 | q.hdr.seqno = atomic_inc_return(&pv->seqno); | ||
269 | q.verb = VSV_SEND_MODEM_CTL_STATUS; | ||
270 | rc = hvsi_send_packet(pv, &q.hdr); | ||
271 | if (rc <= 0) { | ||
272 | pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); | ||
273 | return rc; | ||
274 | } | ||
275 | |||
276 | /* Try for up to 200ms */ | ||
277 | for (timeout = 0; timeout < 20; timeout++) { | ||
278 | if (!pv->established) | ||
279 | return -ENXIO; | ||
280 | if (pv->mctrl_update) | ||
281 | return 0; | ||
282 | if (!hvsi_get_packet(pv)) | ||
283 | maybe_msleep(10); | ||
284 | } | ||
285 | return -EIO; | ||
286 | } | ||
287 | |||
288 | int hvsi_write_mctrl(struct hvsi_priv *pv, int dtr) | ||
289 | { | ||
290 | struct hvsi_control ctrl; | ||
291 | unsigned short mctrl; | ||
292 | |||
293 | mctrl = pv->mctrl; | ||
294 | if (dtr) | ||
295 | mctrl |= TIOCM_DTR; | ||
296 | else | ||
297 | mctrl &= ~TIOCM_DTR; | ||
298 | if (mctrl == pv->mctrl) | ||
299 | return 0; | ||
300 | pv->mctrl = mctrl; | ||
301 | |||
302 | pr_devel("HVSI@%x: %s DTR...\n", pv->termno, | ||
303 | dtr ? "Setting" : "Clearing"); | ||
304 | |||
305 | ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, | ||
306 | ctrl.hdr.len = sizeof(struct hvsi_control); | ||
307 | ctrl.verb = VSV_SET_MODEM_CTL; | ||
308 | ctrl.mask = HVSI_TSDTR; | ||
309 | ctrl.word = dtr ? HVSI_TSDTR : 0; | ||
310 | return hvsi_send_packet(pv, &ctrl.hdr); | ||
311 | } | ||
312 | |||
313 | void hvsi_establish(struct hvsi_priv *pv) | ||
314 | { | ||
315 | int timeout; | ||
316 | |||
317 | pr_devel("HVSI@%x: Establishing...\n", pv->termno); | ||
318 | |||
319 | /* Try for up to 200ms, there can be a packet to | ||
320 | * start the process waiting for us... | ||
321 | */ | ||
322 | for (timeout = 0; timeout < 20; timeout++) { | ||
323 | if (pv->established) | ||
324 | goto established; | ||
325 | if (!hvsi_get_packet(pv)) | ||
326 | maybe_msleep(10); | ||
327 | } | ||
328 | |||
329 | /* Failed, send a close connection packet just | ||
330 | * in case | ||
331 | */ | ||
332 | pr_devel("HVSI@%x: ... sending close\n", pv->termno); | ||
333 | |||
334 | hvsi_send_close(pv); | ||
335 | |||
336 | /* Then restart handshake */ | ||
337 | |||
338 | pr_devel("HVSI@%x: ... restarting handshake\n", pv->termno); | ||
339 | |||
340 | hvsi_start_handshake(pv); | ||
341 | |||
342 | pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); | ||
343 | |||
344 | /* Try for up to 200s */ | ||
345 | for (timeout = 0; timeout < 20; timeout++) { | ||
346 | if (pv->established) | ||
347 | goto established; | ||
348 | if (!hvsi_get_packet(pv)) | ||
349 | maybe_msleep(10); | ||
350 | } | ||
351 | |||
352 | if (!pv->established) { | ||
353 | pr_devel("HVSI@%x: Timeout handshaking, giving up !\n", | ||
354 | pv->termno); | ||
355 | return; | ||
356 | } | ||
357 | established: | ||
358 | /* Query modem control lines */ | ||
359 | |||
360 | pr_devel("HVSI@%x: ... established, reading mctrl\n", pv->termno); | ||
361 | |||
362 | hvsi_read_mctrl(pv); | ||
363 | |||
364 | /* Set our own DTR */ | ||
365 | |||
366 | pr_devel("HVSI@%x: ... setting mctrl\n", pv->termno); | ||
367 | |||
368 | hvsi_write_mctrl(pv, 1); | ||
369 | |||
370 | /* Set the opened flag so reads are allowed */ | ||
371 | wmb(); | ||
372 | pv->opened = 1; | ||
373 | } | ||
374 | |||
375 | int hvsi_open(struct hvsi_priv *pv, struct hvc_struct *hp) | ||
376 | { | ||
377 | pr_devel("HVSI@%x: open !\n", pv->termno); | ||
378 | |||
379 | /* Keep track of the tty data structure */ | ||
380 | pv->tty = tty_kref_get(hp->tty); | ||
381 | |||
382 | hvsi_establish(pv); | ||
383 | |||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | void hvsi_close(struct hvsi_priv *pv, struct hvc_struct *hp) | ||
388 | { | ||
389 | unsigned long flags; | ||
390 | |||
391 | pr_devel("HVSI@%x: close !\n", pv->termno); | ||
392 | |||
393 | if (!pv->is_console) { | ||
394 | pr_devel("HVSI@%x: Not a console, tearing down\n", | ||
395 | pv->termno); | ||
396 | |||
397 | /* Clear opened, synchronize with khvcd */ | ||
398 | spin_lock_irqsave(&hp->lock, flags); | ||
399 | pv->opened = 0; | ||
400 | spin_unlock_irqrestore(&hp->lock, flags); | ||
401 | |||
402 | /* Clear our own DTR */ | ||
403 | if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL)) | ||
404 | hvsi_write_mctrl(pv, 0); | ||
405 | |||
406 | /* Tear down the connection */ | ||
407 | hvsi_send_close(pv); | ||
408 | } | ||
409 | |||
410 | if (pv->tty) | ||
411 | tty_kref_put(pv->tty); | ||
412 | pv->tty = NULL; | ||
413 | } | ||
414 | |||
415 | void hvsi_init(struct hvsi_priv *pv, | ||
416 | int (*get_chars)(uint32_t termno, char *buf, int count), | ||
417 | int (*put_chars)(uint32_t termno, const char *buf, | ||
418 | int count), | ||
419 | int termno, int is_console) | ||
420 | { | ||
421 | memset(pv, 0, sizeof(*pv)); | ||
422 | pv->get_chars = get_chars; | ||
423 | pv->put_chars = put_chars; | ||
424 | pv->termno = termno; | ||
425 | pv->is_console = is_console; | ||
426 | } | ||