diff options
Diffstat (limited to 'drivers/tty/hvc/hvsi.c')
-rw-r--r-- | drivers/tty/hvc/hvsi.c | 128 |
1 files changed, 54 insertions, 74 deletions
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index a7488b748647..6f5bc49c441f 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c | |||
@@ -69,14 +69,13 @@ | |||
69 | #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) | 69 | #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) |
70 | 70 | ||
71 | struct hvsi_struct { | 71 | struct hvsi_struct { |
72 | struct tty_port port; | ||
72 | struct delayed_work writer; | 73 | struct delayed_work writer; |
73 | struct work_struct handshaker; | 74 | struct work_struct handshaker; |
74 | wait_queue_head_t emptyq; /* woken when outbuf is emptied */ | 75 | wait_queue_head_t emptyq; /* woken when outbuf is emptied */ |
75 | wait_queue_head_t stateq; /* woken when HVSI state changes */ | 76 | wait_queue_head_t stateq; /* woken when HVSI state changes */ |
76 | spinlock_t lock; | 77 | spinlock_t lock; |
77 | int index; | 78 | int index; |
78 | struct tty_struct *tty; | ||
79 | int count; | ||
80 | uint8_t throttle_buf[128]; | 79 | uint8_t throttle_buf[128]; |
81 | uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ | 80 | 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. */ | 81 | /* inbuf is for packet reassembly. leave a little room for leftovers. */ |
@@ -237,7 +236,7 @@ static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) | |||
237 | } | 236 | } |
238 | 237 | ||
239 | static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, | 238 | static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, |
240 | struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) | 239 | struct tty_struct *tty, struct hvsi_struct **to_handshake) |
241 | { | 240 | { |
242 | struct hvsi_control *header = (struct hvsi_control *)packet; | 241 | struct hvsi_control *header = (struct hvsi_control *)packet; |
243 | 242 | ||
@@ -247,9 +246,8 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, | |||
247 | /* CD went away; no more connection */ | 246 | /* CD went away; no more connection */ |
248 | pr_debug("hvsi%i: CD dropped\n", hp->index); | 247 | pr_debug("hvsi%i: CD dropped\n", hp->index); |
249 | hp->mctrl &= TIOCM_CD; | 248 | hp->mctrl &= TIOCM_CD; |
250 | /* If userland hasn't done an open(2) yet, hp->tty is NULL. */ | 249 | if (tty && !C_CLOCAL(tty)) |
251 | if (hp->tty && !(hp->tty->flags & CLOCAL)) | 250 | tty_hangup(tty); |
252 | *to_hangup = hp->tty; | ||
253 | } | 251 | } |
254 | break; | 252 | break; |
255 | case VSV_CLOSE_PROTOCOL: | 253 | case VSV_CLOSE_PROTOCOL: |
@@ -331,7 +329,8 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) | |||
331 | } | 329 | } |
332 | } | 330 | } |
333 | 331 | ||
334 | static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) | 332 | static void hvsi_insert_chars(struct hvsi_struct *hp, struct tty_struct *tty, |
333 | const char *buf, int len) | ||
335 | { | 334 | { |
336 | int i; | 335 | int i; |
337 | 336 | ||
@@ -347,7 +346,7 @@ static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) | |||
347 | continue; | 346 | continue; |
348 | } | 347 | } |
349 | #endif /* CONFIG_MAGIC_SYSRQ */ | 348 | #endif /* CONFIG_MAGIC_SYSRQ */ |
350 | tty_insert_flip_char(hp->tty, c, 0); | 349 | tty_insert_flip_char(tty, c, 0); |
351 | } | 350 | } |
352 | } | 351 | } |
353 | 352 | ||
@@ -360,7 +359,7 @@ static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) | |||
360 | * revisited. | 359 | * revisited. |
361 | */ | 360 | */ |
362 | #define TTY_THRESHOLD_THROTTLE 128 | 361 | #define TTY_THRESHOLD_THROTTLE 128 |
363 | static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, | 362 | static bool hvsi_recv_data(struct hvsi_struct *hp, struct tty_struct *tty, |
364 | const uint8_t *packet) | 363 | const uint8_t *packet) |
365 | { | 364 | { |
366 | const struct hvsi_header *header = (const struct hvsi_header *)packet; | 365 | const struct hvsi_header *header = (const struct hvsi_header *)packet; |
@@ -371,14 +370,14 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, | |||
371 | pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); | 370 | pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); |
372 | 371 | ||
373 | if (datalen == 0) | 372 | if (datalen == 0) |
374 | return NULL; | 373 | return false; |
375 | 374 | ||
376 | if (overflow > 0) { | 375 | if (overflow > 0) { |
377 | pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); | 376 | pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); |
378 | datalen = TTY_THRESHOLD_THROTTLE; | 377 | datalen = TTY_THRESHOLD_THROTTLE; |
379 | } | 378 | } |
380 | 379 | ||
381 | hvsi_insert_chars(hp, data, datalen); | 380 | hvsi_insert_chars(hp, tty, data, datalen); |
382 | 381 | ||
383 | if (overflow > 0) { | 382 | if (overflow > 0) { |
384 | /* | 383 | /* |
@@ -390,7 +389,7 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, | |||
390 | hp->n_throttle = overflow; | 389 | hp->n_throttle = overflow; |
391 | } | 390 | } |
392 | 391 | ||
393 | return hp->tty; | 392 | return true; |
394 | } | 393 | } |
395 | 394 | ||
396 | /* | 395 | /* |
@@ -399,14 +398,13 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, | |||
399 | * machine during console handshaking (in which case tty = NULL and we ignore | 398 | * machine during console handshaking (in which case tty = NULL and we ignore |
400 | * incoming data). | 399 | * incoming data). |
401 | */ | 400 | */ |
402 | static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, | 401 | static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, |
403 | struct tty_struct **hangup, struct hvsi_struct **handshake) | 402 | struct hvsi_struct **handshake) |
404 | { | 403 | { |
405 | uint8_t *packet = hp->inbuf; | 404 | uint8_t *packet = hp->inbuf; |
406 | int chunklen; | 405 | int chunklen; |
406 | bool flip = false; | ||
407 | 407 | ||
408 | *flip = NULL; | ||
409 | *hangup = NULL; | ||
410 | *handshake = NULL; | 408 | *handshake = NULL; |
411 | 409 | ||
412 | chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); | 410 | chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); |
@@ -440,12 +438,12 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, | |||
440 | case VS_DATA_PACKET_HEADER: | 438 | case VS_DATA_PACKET_HEADER: |
441 | if (!is_open(hp)) | 439 | if (!is_open(hp)) |
442 | break; | 440 | break; |
443 | if (hp->tty == NULL) | 441 | if (tty == NULL) |
444 | break; /* no tty buffer to put data in */ | 442 | break; /* no tty buffer to put data in */ |
445 | *flip = hvsi_recv_data(hp, packet); | 443 | flip = hvsi_recv_data(hp, tty, packet); |
446 | break; | 444 | break; |
447 | case VS_CONTROL_PACKET_HEADER: | 445 | case VS_CONTROL_PACKET_HEADER: |
448 | hvsi_recv_control(hp, packet, hangup, handshake); | 446 | hvsi_recv_control(hp, packet, tty, handshake); |
449 | break; | 447 | break; |
450 | case VS_QUERY_RESPONSE_PACKET_HEADER: | 448 | case VS_QUERY_RESPONSE_PACKET_HEADER: |
451 | hvsi_recv_response(hp, packet); | 449 | hvsi_recv_response(hp, packet); |
@@ -462,28 +460,26 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, | |||
462 | 460 | ||
463 | packet += len_packet(packet); | 461 | packet += len_packet(packet); |
464 | 462 | ||
465 | if (*hangup || *handshake) { | 463 | if (*handshake) { |
466 | pr_debug("%s: hangup or handshake\n", __func__); | 464 | pr_debug("%s: handshake\n", __func__); |
467 | /* | ||
468 | * we need to send the hangup now before receiving any more data. | ||
469 | * If we get "data, hangup, data", we can't deliver the second | ||
470 | * data before the hangup. | ||
471 | */ | ||
472 | break; | 465 | break; |
473 | } | 466 | } |
474 | } | 467 | } |
475 | 468 | ||
476 | compact_inbuf(hp, packet); | 469 | compact_inbuf(hp, packet); |
477 | 470 | ||
471 | if (flip) | ||
472 | tty_flip_buffer_push(tty); | ||
473 | |||
478 | return 1; | 474 | return 1; |
479 | } | 475 | } |
480 | 476 | ||
481 | static void hvsi_send_overflow(struct hvsi_struct *hp) | 477 | static void hvsi_send_overflow(struct hvsi_struct *hp, struct tty_struct *tty) |
482 | { | 478 | { |
483 | pr_debug("%s: delivering %i bytes overflow\n", __func__, | 479 | pr_debug("%s: delivering %i bytes overflow\n", __func__, |
484 | hp->n_throttle); | 480 | hp->n_throttle); |
485 | 481 | ||
486 | hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); | 482 | hvsi_insert_chars(hp, tty, hp->throttle_buf, hp->n_throttle); |
487 | hp->n_throttle = 0; | 483 | hp->n_throttle = 0; |
488 | } | 484 | } |
489 | 485 | ||
@@ -494,35 +490,20 @@ static void hvsi_send_overflow(struct hvsi_struct *hp) | |||
494 | static irqreturn_t hvsi_interrupt(int irq, void *arg) | 490 | static irqreturn_t hvsi_interrupt(int irq, void *arg) |
495 | { | 491 | { |
496 | struct hvsi_struct *hp = (struct hvsi_struct *)arg; | 492 | struct hvsi_struct *hp = (struct hvsi_struct *)arg; |
497 | struct tty_struct *flip; | ||
498 | struct tty_struct *hangup; | ||
499 | struct hvsi_struct *handshake; | 493 | struct hvsi_struct *handshake; |
494 | struct tty_struct *tty; | ||
500 | unsigned long flags; | 495 | unsigned long flags; |
501 | int again = 1; | 496 | int again = 1; |
502 | 497 | ||
503 | pr_debug("%s\n", __func__); | 498 | pr_debug("%s\n", __func__); |
504 | 499 | ||
500 | tty = tty_port_tty_get(&hp->port); | ||
501 | |||
505 | while (again) { | 502 | while (again) { |
506 | spin_lock_irqsave(&hp->lock, flags); | 503 | spin_lock_irqsave(&hp->lock, flags); |
507 | again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); | 504 | again = hvsi_load_chunk(hp, tty, &handshake); |
508 | spin_unlock_irqrestore(&hp->lock, flags); | 505 | spin_unlock_irqrestore(&hp->lock, flags); |
509 | 506 | ||
510 | /* | ||
511 | * we have to call tty_flip_buffer_push() and tty_hangup() outside our | ||
512 | * spinlock. But we also have to keep going until we've read all the | ||
513 | * available data. | ||
514 | */ | ||
515 | |||
516 | if (flip) { | ||
517 | /* there was data put in the tty flip buffer */ | ||
518 | tty_flip_buffer_push(flip); | ||
519 | flip = NULL; | ||
520 | } | ||
521 | |||
522 | if (hangup) { | ||
523 | tty_hangup(hangup); | ||
524 | } | ||
525 | |||
526 | if (handshake) { | 507 | if (handshake) { |
527 | pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); | 508 | pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); |
528 | schedule_work(&handshake->handshaker); | 509 | schedule_work(&handshake->handshaker); |
@@ -530,18 +511,15 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg) | |||
530 | } | 511 | } |
531 | 512 | ||
532 | spin_lock_irqsave(&hp->lock, flags); | 513 | spin_lock_irqsave(&hp->lock, flags); |
533 | if (hp->tty && hp->n_throttle | 514 | if (tty && hp->n_throttle && !test_bit(TTY_THROTTLED, &tty->flags)) { |
534 | && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { | 515 | /* we weren't hung up and we weren't throttled, so we can |
535 | /* we weren't hung up and we weren't throttled, so we can deliver the | 516 | * deliver the rest now */ |
536 | * rest now */ | 517 | hvsi_send_overflow(hp, tty); |
537 | flip = hp->tty; | 518 | tty_flip_buffer_push(tty); |
538 | hvsi_send_overflow(hp); | ||
539 | } | 519 | } |
540 | spin_unlock_irqrestore(&hp->lock, flags); | 520 | spin_unlock_irqrestore(&hp->lock, flags); |
541 | 521 | ||
542 | if (flip) { | 522 | tty_kref_put(tty); |
543 | tty_flip_buffer_push(flip); | ||
544 | } | ||
545 | 523 | ||
546 | return IRQ_HANDLED; | 524 | return IRQ_HANDLED; |
547 | } | 525 | } |
@@ -749,9 +727,9 @@ static int hvsi_open(struct tty_struct *tty, struct file *filp) | |||
749 | if (hp->state == HVSI_FSP_DIED) | 727 | if (hp->state == HVSI_FSP_DIED) |
750 | return -EIO; | 728 | return -EIO; |
751 | 729 | ||
730 | tty_port_tty_set(&hp->port, tty); | ||
752 | spin_lock_irqsave(&hp->lock, flags); | 731 | spin_lock_irqsave(&hp->lock, flags); |
753 | hp->tty = tty; | 732 | hp->port.count++; |
754 | hp->count++; | ||
755 | atomic_set(&hp->seqno, 0); | 733 | atomic_set(&hp->seqno, 0); |
756 | h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); | 734 | h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); |
757 | spin_unlock_irqrestore(&hp->lock, flags); | 735 | spin_unlock_irqrestore(&hp->lock, flags); |
@@ -808,8 +786,8 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp) | |||
808 | 786 | ||
809 | spin_lock_irqsave(&hp->lock, flags); | 787 | spin_lock_irqsave(&hp->lock, flags); |
810 | 788 | ||
811 | if (--hp->count == 0) { | 789 | if (--hp->port.count == 0) { |
812 | hp->tty = NULL; | 790 | tty_port_tty_set(&hp->port, NULL); |
813 | hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ | 791 | hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ |
814 | 792 | ||
815 | /* only close down connection if it is not the console */ | 793 | /* only close down connection if it is not the console */ |
@@ -841,9 +819,9 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp) | |||
841 | 819 | ||
842 | spin_lock_irqsave(&hp->lock, flags); | 820 | spin_lock_irqsave(&hp->lock, flags); |
843 | } | 821 | } |
844 | } else if (hp->count < 0) | 822 | } else if (hp->port.count < 0) |
845 | printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", | 823 | printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", |
846 | hp - hvsi_ports, hp->count); | 824 | hp - hvsi_ports, hp->port.count); |
847 | 825 | ||
848 | spin_unlock_irqrestore(&hp->lock, flags); | 826 | spin_unlock_irqrestore(&hp->lock, flags); |
849 | } | 827 | } |
@@ -855,12 +833,11 @@ static void hvsi_hangup(struct tty_struct *tty) | |||
855 | 833 | ||
856 | pr_debug("%s\n", __func__); | 834 | pr_debug("%s\n", __func__); |
857 | 835 | ||
858 | spin_lock_irqsave(&hp->lock, flags); | 836 | tty_port_tty_set(&hp->port, NULL); |
859 | 837 | ||
860 | hp->count = 0; | 838 | spin_lock_irqsave(&hp->lock, flags); |
839 | hp->port.count = 0; | ||
861 | hp->n_outbuf = 0; | 840 | hp->n_outbuf = 0; |
862 | hp->tty = NULL; | ||
863 | |||
864 | spin_unlock_irqrestore(&hp->lock, flags); | 841 | spin_unlock_irqrestore(&hp->lock, flags); |
865 | } | 842 | } |
866 | 843 | ||
@@ -888,6 +865,7 @@ static void hvsi_write_worker(struct work_struct *work) | |||
888 | { | 865 | { |
889 | struct hvsi_struct *hp = | 866 | struct hvsi_struct *hp = |
890 | container_of(work, struct hvsi_struct, writer.work); | 867 | container_of(work, struct hvsi_struct, writer.work); |
868 | struct tty_struct *tty; | ||
891 | unsigned long flags; | 869 | unsigned long flags; |
892 | #ifdef DEBUG | 870 | #ifdef DEBUG |
893 | static long start_j = 0; | 871 | static long start_j = 0; |
@@ -921,7 +899,11 @@ static void hvsi_write_worker(struct work_struct *work) | |||
921 | start_j = 0; | 899 | start_j = 0; |
922 | #endif /* DEBUG */ | 900 | #endif /* DEBUG */ |
923 | wake_up_all(&hp->emptyq); | 901 | wake_up_all(&hp->emptyq); |
924 | tty_wakeup(hp->tty); | 902 | tty = tty_port_tty_get(&hp->port); |
903 | if (tty) { | ||
904 | tty_wakeup(tty); | ||
905 | tty_kref_put(tty); | ||
906 | } | ||
925 | } | 907 | } |
926 | 908 | ||
927 | out: | 909 | out: |
@@ -966,8 +948,8 @@ static int hvsi_write(struct tty_struct *tty, | |||
966 | * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls | 948 | * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls |
967 | * will see there is no room in outbuf and return. | 949 | * will see there is no room in outbuf and return. |
968 | */ | 950 | */ |
969 | while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { | 951 | while ((count > 0) && (hvsi_write_room(tty) > 0)) { |
970 | int chunksize = min(count, hvsi_write_room(hp->tty)); | 952 | int chunksize = min(count, hvsi_write_room(tty)); |
971 | 953 | ||
972 | BUG_ON(hp->n_outbuf < 0); | 954 | BUG_ON(hp->n_outbuf < 0); |
973 | memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); | 955 | memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); |
@@ -1014,19 +996,16 @@ static void hvsi_unthrottle(struct tty_struct *tty) | |||
1014 | { | 996 | { |
1015 | struct hvsi_struct *hp = tty->driver_data; | 997 | struct hvsi_struct *hp = tty->driver_data; |
1016 | unsigned long flags; | 998 | unsigned long flags; |
1017 | int shouldflip = 0; | ||
1018 | 999 | ||
1019 | pr_debug("%s\n", __func__); | 1000 | pr_debug("%s\n", __func__); |
1020 | 1001 | ||
1021 | spin_lock_irqsave(&hp->lock, flags); | 1002 | spin_lock_irqsave(&hp->lock, flags); |
1022 | if (hp->n_throttle) { | 1003 | if (hp->n_throttle) { |
1023 | hvsi_send_overflow(hp); | 1004 | hvsi_send_overflow(hp, tty); |
1024 | shouldflip = 1; | 1005 | tty_flip_buffer_push(tty); |
1025 | } | 1006 | } |
1026 | spin_unlock_irqrestore(&hp->lock, flags); | 1007 | spin_unlock_irqrestore(&hp->lock, flags); |
1027 | 1008 | ||
1028 | if (shouldflip) | ||
1029 | tty_flip_buffer_push(hp->tty); | ||
1030 | 1009 | ||
1031 | h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); | 1010 | h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); |
1032 | } | 1011 | } |
@@ -1228,6 +1207,7 @@ static int __init hvsi_console_init(void) | |||
1228 | init_waitqueue_head(&hp->emptyq); | 1207 | init_waitqueue_head(&hp->emptyq); |
1229 | init_waitqueue_head(&hp->stateq); | 1208 | init_waitqueue_head(&hp->stateq); |
1230 | spin_lock_init(&hp->lock); | 1209 | spin_lock_init(&hp->lock); |
1210 | tty_port_init(&hp->port); | ||
1231 | hp->index = hvsi_count; | 1211 | hp->index = hvsi_count; |
1232 | hp->inbuf_end = hp->inbuf; | 1212 | hp->inbuf_end = hp->inbuf; |
1233 | hp->state = HVSI_CLOSED; | 1213 | hp->state = HVSI_CLOSED; |