diff options
author | Alan Cox <alan@linux.intel.com> | 2009-11-30 08:16:09 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 18:18:05 -0500 |
commit | 584abc3775e76c1a2abe725355915851ed23ed6c (patch) | |
tree | 5808522d561d6529d5b9de2a14f8157d4c033405 /drivers/mmc/card/sdio_uart.c | |
parent | 0a68f64febf365313987c570ad59c9069f61306d (diff) |
tty: sdio_uart: Switch to the open/close helpers
Gets us proper tty semantics, removes some code and fixes up a few corner
case races (hangup during open etc)
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/mmc/card/sdio_uart.c')
-rw-r--r-- | drivers/mmc/card/sdio_uart.c | 201 |
1 files changed, 119 insertions, 82 deletions
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 31f70233133d..95027b03c646 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c | |||
@@ -77,7 +77,6 @@ struct sdio_uart_port { | |||
77 | struct kref kref; | 77 | struct kref kref; |
78 | struct tty_struct *tty; | 78 | struct tty_struct *tty; |
79 | unsigned int index; | 79 | unsigned int index; |
80 | unsigned int opened; | ||
81 | struct sdio_func *func; | 80 | struct sdio_func *func; |
82 | struct mutex func_lock; | 81 | struct mutex func_lock; |
83 | struct task_struct *in_sdio_uart_irq; | 82 | struct task_struct *in_sdio_uart_irq; |
@@ -150,6 +149,7 @@ static void sdio_uart_port_put(struct sdio_uart_port *port) | |||
150 | static void sdio_uart_port_remove(struct sdio_uart_port *port) | 149 | static void sdio_uart_port_remove(struct sdio_uart_port *port) |
151 | { | 150 | { |
152 | struct sdio_func *func; | 151 | struct sdio_func *func; |
152 | struct tty_struct *tty; | ||
153 | 153 | ||
154 | BUG_ON(sdio_uart_table[port->index] != port); | 154 | BUG_ON(sdio_uart_table[port->index] != port); |
155 | 155 | ||
@@ -170,11 +170,10 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) | |||
170 | sdio_claim_host(func); | 170 | sdio_claim_host(func); |
171 | port->func = NULL; | 171 | port->func = NULL; |
172 | mutex_unlock(&port->func_lock); | 172 | mutex_unlock(&port->func_lock); |
173 | if (port->opened) { | 173 | tty = tty_port_tty_get(&port->port); |
174 | struct tty_struct *tty = tty_port_tty_get(&port->port); | 174 | /* tty_hangup is async so is this safe as is ?? */ |
175 | /* tty_hangup is async so is this safe as is ?? */ | 175 | if (tty) { |
176 | if (tty) | 176 | tty_hangup(tty); |
177 | tty_hangup(tty); | ||
178 | tty_kref_put(tty); | 177 | tty_kref_put(tty); |
179 | } | 178 | } |
180 | mutex_unlock(&port->port.mutex); | 179 | mutex_unlock(&port->port.mutex); |
@@ -560,13 +559,46 @@ static void sdio_uart_irq(struct sdio_func *func) | |||
560 | port->in_sdio_uart_irq = NULL; | 559 | port->in_sdio_uart_irq = NULL; |
561 | } | 560 | } |
562 | 561 | ||
563 | static int sdio_uart_startup(struct sdio_uart_port *port) | 562 | /** |
563 | * uart_dtr_rts - port helper to set uart signals | ||
564 | * @tport: tty port to be updated | ||
565 | * @onoff: set to turn on DTR/RTS | ||
566 | * | ||
567 | * Called by the tty port helpers when the modem signals need to be | ||
568 | * adjusted during an open, close and hangup. | ||
569 | */ | ||
570 | |||
571 | static void uart_dtr_rts(struct tty_port *tport, int onoff) | ||
564 | { | 572 | { |
565 | unsigned long page; | 573 | struct sdio_uart_port *port = |
566 | int ret = -ENOMEM; | 574 | container_of(tport, struct sdio_uart_port, port); |
567 | struct tty_struct *tty = tty_port_tty_get(&port->port); | 575 | if (onoff == 0) |
576 | sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); | ||
577 | else | ||
578 | sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); | ||
579 | } | ||
568 | 580 | ||
569 | /* FIXME: What if it is NULL ?? */ | 581 | /** |
582 | * sdio_uart_activate - start up hardware | ||
583 | * @tport: tty port to activate | ||
584 | * @tty: tty bound to this port | ||
585 | * | ||
586 | * Activate a tty port. The port locking guarantees us this will be | ||
587 | * run exactly once per set of opens, and if successful will see the | ||
588 | * shutdown method run exactly once to match. Start up and shutdown are | ||
589 | * protected from each other by the internal locking and will not run | ||
590 | * at the same time even during a hangup event. | ||
591 | * | ||
592 | * If we successfully start up the port we take an extra kref as we | ||
593 | * will keep it around until shutdown when the kref is dropped. | ||
594 | */ | ||
595 | |||
596 | static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty) | ||
597 | { | ||
598 | struct sdio_uart_port *port = | ||
599 | container_of(tport, struct sdio_uart_port, port); | ||
600 | unsigned long page; | ||
601 | int ret; | ||
570 | 602 | ||
571 | /* | 603 | /* |
572 | * Set the TTY IO error marker - we will only clear this | 604 | * Set the TTY IO error marker - we will only clear this |
@@ -577,7 +609,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port) | |||
577 | /* Initialise and allocate the transmit buffer. */ | 609 | /* Initialise and allocate the transmit buffer. */ |
578 | page = __get_free_page(GFP_KERNEL); | 610 | page = __get_free_page(GFP_KERNEL); |
579 | if (!page) | 611 | if (!page) |
580 | goto err0; | 612 | return -ENOMEM; |
581 | port->xmit.buf = (unsigned char *)page; | 613 | port->xmit.buf = (unsigned char *)page; |
582 | circ_clear(&port->xmit); | 614 | circ_clear(&port->xmit); |
583 | 615 | ||
@@ -631,7 +663,6 @@ static int sdio_uart_startup(struct sdio_uart_port *port) | |||
631 | sdio_uart_irq(port->func); | 663 | sdio_uart_irq(port->func); |
632 | 664 | ||
633 | sdio_uart_release_func(port); | 665 | sdio_uart_release_func(port); |
634 | tty_kref_put(tty); | ||
635 | return 0; | 666 | return 0; |
636 | 667 | ||
637 | err3: | 668 | err3: |
@@ -640,15 +671,25 @@ err2: | |||
640 | sdio_uart_release_func(port); | 671 | sdio_uart_release_func(port); |
641 | err1: | 672 | err1: |
642 | free_page((unsigned long)port->xmit.buf); | 673 | free_page((unsigned long)port->xmit.buf); |
643 | err0: | ||
644 | tty_kref_put(tty); | ||
645 | return ret; | 674 | return ret; |
646 | } | 675 | } |
647 | 676 | ||
648 | static void sdio_uart_shutdown(struct sdio_uart_port *port) | 677 | |
678 | /** | ||
679 | * sdio_uart_shutdown - stop hardware | ||
680 | * @tport: tty port to shut down | ||
681 | * | ||
682 | * Deactivate a tty port. The port locking guarantees us this will be | ||
683 | * run only if a successful matching activate already ran. The two are | ||
684 | * protected from each other by the internal locking and will not run | ||
685 | * at the same time even during a hangup event. | ||
686 | */ | ||
687 | |||
688 | static void sdio_uart_shutdown(struct tty_port *tport) | ||
649 | { | 689 | { |
690 | struct sdio_uart_port *port = | ||
691 | container_of(tport, struct sdio_uart_port, port); | ||
650 | int ret; | 692 | int ret; |
651 | struct tty_struct *tty; | ||
652 | 693 | ||
653 | ret = sdio_uart_claim_func(port); | 694 | ret = sdio_uart_claim_func(port); |
654 | if (ret) | 695 | if (ret) |
@@ -656,14 +697,6 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port) | |||
656 | 697 | ||
657 | sdio_uart_stop_rx(port); | 698 | sdio_uart_stop_rx(port); |
658 | 699 | ||
659 | /* TODO: wait here for TX FIFO to drain */ | ||
660 | |||
661 | tty = tty_port_tty_get(&port->port); | ||
662 | /* Turn off DTR and RTS early. */ | ||
663 | if (tty && (tty->termios->c_cflag & HUPCL)) | ||
664 | sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); | ||
665 | tty_kref_put(tty); | ||
666 | |||
667 | /* Disable interrupts from this port */ | 700 | /* Disable interrupts from this port */ |
668 | sdio_release_irq(port->func); | 701 | sdio_release_irq(port->func); |
669 | port->ier = 0; | 702 | port->ier = 0; |
@@ -688,74 +721,68 @@ skip: | |||
688 | free_page((unsigned long)port->xmit.buf); | 721 | free_page((unsigned long)port->xmit.buf); |
689 | } | 722 | } |
690 | 723 | ||
691 | static int sdio_uart_open(struct tty_struct *tty, struct file *filp) | 724 | /** |
692 | { | 725 | * sdio_uart_install - install method |
693 | struct sdio_uart_port *port; | 726 | * @driver: the driver in use (sdio_uart in our case) |
694 | int ret; | 727 | * @tty: the tty being bound |
695 | 728 | * | |
696 | port = sdio_uart_port_get(tty->index); | 729 | * Look up and bind the tty and the driver together. Initialize |
697 | if (!port) | 730 | * any needed private data (in our case the termios) |
698 | return -ENODEV; | 731 | */ |
699 | |||
700 | mutex_lock(&port->port.mutex); | ||
701 | 732 | ||
702 | /* | 733 | static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty) |
703 | * Make sure not to mess up with a dead port | 734 | { |
704 | * which has not been closed yet. | 735 | int idx = tty->index; |
705 | */ | 736 | struct sdio_uart_port *port = sdio_uart_port_get(idx); |
706 | if (tty->driver_data && tty->driver_data != port) { | 737 | int ret = tty_init_termios(tty); |
707 | mutex_unlock(&port->port.mutex); | 738 | |
739 | if (ret == 0) { | ||
740 | tty_driver_kref_get(driver); | ||
741 | tty->count++; | ||
742 | /* This is the ref sdio_uart_port get provided */ | ||
743 | tty->driver_data = port; | ||
744 | driver->ttys[idx] = tty; | ||
745 | } else | ||
708 | sdio_uart_port_put(port); | 746 | sdio_uart_port_put(port); |
709 | return -EBUSY; | 747 | return ret; |
710 | } | ||
711 | 748 | ||
712 | if (!port->opened) { | ||
713 | tty->driver_data = port; | ||
714 | tty_port_tty_set(&port->port, tty); | ||
715 | ret = sdio_uart_startup(port); | ||
716 | if (ret) { | ||
717 | tty->driver_data = NULL; | ||
718 | tty_port_tty_set(&port->port, NULL); | ||
719 | mutex_unlock(&port->port.mutex); | ||
720 | sdio_uart_port_put(port); | ||
721 | return ret; | ||
722 | } | ||
723 | } | ||
724 | port->opened++; | ||
725 | mutex_unlock(&port->port.mutex); | ||
726 | return 0; | ||
727 | } | 749 | } |
728 | 750 | ||
729 | static void sdio_uart_close(struct tty_struct *tty, struct file * filp) | 751 | /** |
752 | * sdio_uart_cleanup - called on the last tty kref drop | ||
753 | * @tty: the tty being destroyed | ||
754 | * | ||
755 | * Called asynchronously when the last reference to the tty is dropped. | ||
756 | * We cannot destroy the tty->driver_data port kref until this point | ||
757 | */ | ||
758 | |||
759 | static void sdio_uart_cleanup(struct tty_struct *tty) | ||
730 | { | 760 | { |
731 | struct sdio_uart_port *port = tty->driver_data; | 761 | struct sdio_uart_port *port = tty->driver_data; |
762 | tty->driver_data = NULL; /* Bug trap */ | ||
763 | sdio_uart_port_put(port); | ||
764 | } | ||
732 | 765 | ||
733 | if (!port) | 766 | /* |
734 | return; | 767 | * Open/close/hangup is now entirely boilerplate |
768 | */ | ||
735 | 769 | ||
736 | mutex_lock(&port->port.mutex); | 770 | static int sdio_uart_open(struct tty_struct *tty, struct file *filp) |
737 | BUG_ON(!port->opened); | 771 | { |
772 | struct sdio_uart_port *port = tty->driver_data; | ||
773 | return tty_port_open(&port->port, tty, filp); | ||
774 | } | ||
738 | 775 | ||
739 | /* | 776 | static void sdio_uart_close(struct tty_struct *tty, struct file * filp) |
740 | * This is messy. The tty layer calls us even when open() | 777 | { |
741 | * returned an error. Ignore this close request if tty->count | 778 | struct sdio_uart_port *port = tty->driver_data; |
742 | * is larger than port->count. | 779 | tty_port_close(&port->port, tty, filp); |
743 | */ | 780 | } |
744 | if (tty->count > port->opened) { | ||
745 | mutex_unlock(&port->port.mutex); | ||
746 | return; | ||
747 | } | ||
748 | 781 | ||
749 | if (--port->opened == 0) { | 782 | static void sdio_uart_hangup(struct tty_struct *tty) |
750 | tty->closing = 1; | 783 | { |
751 | sdio_uart_shutdown(port); | 784 | struct sdio_uart_port *port = tty->driver_data; |
752 | tty_ldisc_flush(tty); | 785 | tty_port_hangup(&port->port); |
753 | tty_port_tty_set(&port->port, NULL); | ||
754 | tty->driver_data = NULL; | ||
755 | tty->closing = 0; | ||
756 | } | ||
757 | mutex_unlock(&port->port.mutex); | ||
758 | sdio_uart_port_put(port); | ||
759 | } | 786 | } |
760 | 787 | ||
761 | static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf, | 788 | static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf, |
@@ -1021,6 +1048,12 @@ static const struct file_operations sdio_uart_proc_fops = { | |||
1021 | .release = single_release, | 1048 | .release = single_release, |
1022 | }; | 1049 | }; |
1023 | 1050 | ||
1051 | static const struct tty_port_operations sdio_uart_port_ops = { | ||
1052 | .dtr_rts = uart_dtr_rts, | ||
1053 | .shutdown = sdio_uart_shutdown, | ||
1054 | .activate = sdio_uart_activate, | ||
1055 | }; | ||
1056 | |||
1024 | static const struct tty_operations sdio_uart_ops = { | 1057 | static const struct tty_operations sdio_uart_ops = { |
1025 | .open = sdio_uart_open, | 1058 | .open = sdio_uart_open, |
1026 | .close = sdio_uart_close, | 1059 | .close = sdio_uart_close, |
@@ -1031,9 +1064,12 @@ static const struct tty_operations sdio_uart_ops = { | |||
1031 | .throttle = sdio_uart_throttle, | 1064 | .throttle = sdio_uart_throttle, |
1032 | .unthrottle = sdio_uart_unthrottle, | 1065 | .unthrottle = sdio_uart_unthrottle, |
1033 | .set_termios = sdio_uart_set_termios, | 1066 | .set_termios = sdio_uart_set_termios, |
1067 | .hangup = sdio_uart_hangup, | ||
1034 | .break_ctl = sdio_uart_break_ctl, | 1068 | .break_ctl = sdio_uart_break_ctl, |
1035 | .tiocmget = sdio_uart_tiocmget, | 1069 | .tiocmget = sdio_uart_tiocmget, |
1036 | .tiocmset = sdio_uart_tiocmset, | 1070 | .tiocmset = sdio_uart_tiocmset, |
1071 | .install = sdio_uart_install, | ||
1072 | .cleanup = sdio_uart_cleanup, | ||
1037 | .proc_fops = &sdio_uart_proc_fops, | 1073 | .proc_fops = &sdio_uart_proc_fops, |
1038 | }; | 1074 | }; |
1039 | 1075 | ||
@@ -1096,6 +1132,7 @@ static int sdio_uart_probe(struct sdio_func *func, | |||
1096 | port->func = func; | 1132 | port->func = func; |
1097 | sdio_set_drvdata(func, port); | 1133 | sdio_set_drvdata(func, port); |
1098 | tty_port_init(&port->port); | 1134 | tty_port_init(&port->port); |
1135 | port->port.ops = &sdio_uart_port_ops; | ||
1099 | 1136 | ||
1100 | ret = sdio_uart_add_port(port); | 1137 | ret = sdio_uart_add_port(port); |
1101 | if (ret) { | 1138 | if (ret) { |