diff options
author | Alan Cox <alan@redhat.com> | 2009-01-02 08:46:10 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-02 13:19:39 -0500 |
commit | 36c621d82b956ff6ff72273f848af53e6c581aba (patch) | |
tree | edd387d8275a8f25277d264ffed94e8d1c2ba048 | |
parent | 3b6826b250633361f08a6427a4ac0035e5d88c72 (diff) |
tty: Introduce a tty_port generic block_til_ready
Start sucking more commonality out of the drivers into a single piece of
core code.
Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/char/isicom.c | 79 | ||||
-rw-r--r-- | drivers/char/mxser.c | 71 | ||||
-rw-r--r-- | drivers/char/riscom8.c | 86 | ||||
-rw-r--r-- | drivers/char/synclink.c | 1 | ||||
-rw-r--r-- | drivers/char/tty_port.c | 105 | ||||
-rw-r--r-- | drivers/char/vme_scc.c | 6 | ||||
-rw-r--r-- | include/linux/tty.h | 2 |
7 files changed, 115 insertions, 235 deletions
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index db53db91ae4a..bac55cf44243 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c | |||
@@ -838,82 +838,6 @@ static int isicom_carrier_raised(struct tty_port *port) | |||
838 | return (ip->status & ISI_DCD)?1 : 0; | 838 | return (ip->status & ISI_DCD)?1 : 0; |
839 | } | 839 | } |
840 | 840 | ||
841 | static int block_til_ready(struct tty_struct *tty, struct file *filp, | ||
842 | struct isi_port *ip) | ||
843 | { | ||
844 | struct tty_port *port = &ip->port; | ||
845 | int do_clocal = 0, retval; | ||
846 | unsigned long flags; | ||
847 | DECLARE_WAITQUEUE(wait, current); | ||
848 | int cd; | ||
849 | |||
850 | /* block if port is in the process of being closed */ | ||
851 | |||
852 | if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { | ||
853 | pr_dbg("block_til_ready: close in progress.\n"); | ||
854 | interruptible_sleep_on(&port->close_wait); | ||
855 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
856 | return -EAGAIN; | ||
857 | else | ||
858 | return -ERESTARTSYS; | ||
859 | } | ||
860 | |||
861 | /* if non-blocking mode is set ... */ | ||
862 | |||
863 | if ((filp->f_flags & O_NONBLOCK) || | ||
864 | (tty->flags & (1 << TTY_IO_ERROR))) { | ||
865 | pr_dbg("block_til_ready: non-block mode.\n"); | ||
866 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | if (C_CLOCAL(tty)) | ||
871 | do_clocal = 1; | ||
872 | |||
873 | /* block waiting for DCD to be asserted, and while | ||
874 | callout dev is busy */ | ||
875 | retval = 0; | ||
876 | add_wait_queue(&port->open_wait, &wait); | ||
877 | |||
878 | spin_lock_irqsave(&port->lock, flags); | ||
879 | if (!tty_hung_up_p(filp)) | ||
880 | port->count--; | ||
881 | port->blocked_open++; | ||
882 | spin_unlock_irqrestore(&port->lock, flags); | ||
883 | |||
884 | while (1) { | ||
885 | tty_port_raise_dtr_rts(port); | ||
886 | |||
887 | set_current_state(TASK_INTERRUPTIBLE); | ||
888 | if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { | ||
889 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
890 | retval = -EAGAIN; | ||
891 | else | ||
892 | retval = -ERESTARTSYS; | ||
893 | break; | ||
894 | } | ||
895 | cd = tty_port_carrier_raised(port); | ||
896 | if (!(port->flags & ASYNC_CLOSING) && | ||
897 | (do_clocal || cd)) | ||
898 | break; | ||
899 | if (signal_pending(current)) { | ||
900 | retval = -ERESTARTSYS; | ||
901 | break; | ||
902 | } | ||
903 | schedule(); | ||
904 | } | ||
905 | set_current_state(TASK_RUNNING); | ||
906 | remove_wait_queue(&port->open_wait, &wait); | ||
907 | spin_lock_irqsave(&port->lock, flags); | ||
908 | if (!tty_hung_up_p(filp)) | ||
909 | port->count++; | ||
910 | port->blocked_open--; | ||
911 | if (retval == 0) | ||
912 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
913 | spin_unlock_irqrestore(&port->lock, flags); | ||
914 | return 0; | ||
915 | } | ||
916 | |||
917 | static int isicom_open(struct tty_struct *tty, struct file *filp) | 841 | static int isicom_open(struct tty_struct *tty, struct file *filp) |
918 | { | 842 | { |
919 | struct isi_port *port; | 843 | struct isi_port *port; |
@@ -940,12 +864,13 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) | |||
940 | 864 | ||
941 | isicom_setup_board(card); | 865 | isicom_setup_board(card); |
942 | 866 | ||
867 | /* FIXME: locking on port.count etc */ | ||
943 | port->port.count++; | 868 | port->port.count++; |
944 | tty->driver_data = port; | 869 | tty->driver_data = port; |
945 | tty_port_tty_set(&port->port, tty); | 870 | tty_port_tty_set(&port->port, tty); |
946 | error = isicom_setup_port(tty); | 871 | error = isicom_setup_port(tty); |
947 | if (error == 0) | 872 | if (error == 0) |
948 | error = block_til_ready(tty, filp, port); | 873 | error = tty_port_block_til_ready(&port->port, tty, filp); |
949 | return error; | 874 | return error; |
950 | } | 875 | } |
951 | 876 | ||
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index e2471cf1ee95..08ba6eb1a380 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c | |||
@@ -558,75 +558,6 @@ static void mxser_raise_dtr_rts(struct tty_port *port) | |||
558 | spin_unlock_irqrestore(&mp->slock, flags); | 558 | spin_unlock_irqrestore(&mp->slock, flags); |
559 | } | 559 | } |
560 | 560 | ||
561 | static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, | ||
562 | struct mxser_port *mp) | ||
563 | { | ||
564 | DECLARE_WAITQUEUE(wait, current); | ||
565 | int retval; | ||
566 | int do_clocal = 0; | ||
567 | unsigned long flags; | ||
568 | int cd; | ||
569 | struct tty_port *port = &mp->port; | ||
570 | |||
571 | /* | ||
572 | * If non-blocking mode is set, or the port is not enabled, | ||
573 | * then make the check up front and then exit. | ||
574 | */ | ||
575 | if ((filp->f_flags & O_NONBLOCK) || | ||
576 | test_bit(TTY_IO_ERROR, &tty->flags)) { | ||
577 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | if (tty->termios->c_cflag & CLOCAL) | ||
582 | do_clocal = 1; | ||
583 | |||
584 | /* | ||
585 | * Block waiting for the carrier detect and the line to become | ||
586 | * free (i.e., not in use by the callout). While we are in | ||
587 | * this loop, port->count is dropped by one, so that | ||
588 | * mxser_close() knows when to free things. We restore it upon | ||
589 | * exit, either normal or abnormal. | ||
590 | */ | ||
591 | retval = 0; | ||
592 | add_wait_queue(&port->open_wait, &wait); | ||
593 | |||
594 | spin_lock_irqsave(&port->lock, flags); | ||
595 | if (!tty_hung_up_p(filp)) | ||
596 | port->count--; | ||
597 | port->blocked_open++; | ||
598 | spin_unlock_irqrestore(&port->lock, flags); | ||
599 | while (1) { | ||
600 | tty_port_raise_dtr_rts(port); | ||
601 | set_current_state(TASK_INTERRUPTIBLE); | ||
602 | if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { | ||
603 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
604 | retval = -EAGAIN; | ||
605 | else | ||
606 | retval = -ERESTARTSYS; | ||
607 | break; | ||
608 | } | ||
609 | cd = tty_port_carrier_raised(port); | ||
610 | if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) | ||
611 | break; | ||
612 | if (signal_pending(current)) { | ||
613 | retval = -ERESTARTSYS; | ||
614 | break; | ||
615 | } | ||
616 | schedule(); | ||
617 | } | ||
618 | set_current_state(TASK_RUNNING); | ||
619 | remove_wait_queue(&port->open_wait, &wait); | ||
620 | spin_lock_irqsave(&port->lock, flags); | ||
621 | if (!tty_hung_up_p(filp)) | ||
622 | port->count++; | ||
623 | port->blocked_open--; | ||
624 | if (retval == 0) | ||
625 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
626 | spin_unlock_irqrestore(&port->lock, flags); | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int mxser_set_baud(struct tty_struct *tty, long newspd) | 561 | static int mxser_set_baud(struct tty_struct *tty, long newspd) |
631 | { | 562 | { |
632 | struct mxser_port *info = tty->driver_data; | 563 | struct mxser_port *info = tty->driver_data; |
@@ -1110,7 +1041,7 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) | |||
1110 | if (retval) | 1041 | if (retval) |
1111 | return retval; | 1042 | return retval; |
1112 | 1043 | ||
1113 | retval = mxser_block_til_ready(tty, filp, info); | 1044 | retval = tty_port_block_til_ready(&info->port, tty, filp); |
1114 | if (retval) | 1045 | if (retval) |
1115 | return retval; | 1046 | return retval; |
1116 | 1047 | ||
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 14662d7aa628..af34c2054a09 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c | |||
@@ -874,90 +874,6 @@ static int carrier_raised(struct tty_port *port) | |||
874 | return CD; | 874 | return CD; |
875 | } | 875 | } |
876 | 876 | ||
877 | static int block_til_ready(struct tty_struct *tty, struct file *filp, | ||
878 | struct riscom_port *rp) | ||
879 | { | ||
880 | DECLARE_WAITQUEUE(wait, current); | ||
881 | int retval; | ||
882 | int do_clocal = 0; | ||
883 | int CD; | ||
884 | unsigned long flags; | ||
885 | struct tty_port *port = &rp->port; | ||
886 | |||
887 | /* | ||
888 | * If the device is in the middle of being closed, then block | ||
889 | * until it's done, and then try again. | ||
890 | */ | ||
891 | if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { | ||
892 | interruptible_sleep_on(&port->close_wait); | ||
893 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
894 | return -EAGAIN; | ||
895 | else | ||
896 | return -ERESTARTSYS; | ||
897 | } | ||
898 | |||
899 | /* | ||
900 | * If non-blocking mode is set, or the port is not enabled, | ||
901 | * then make the check up front and then exit. | ||
902 | */ | ||
903 | if ((filp->f_flags & O_NONBLOCK) || | ||
904 | (tty->flags & (1 << TTY_IO_ERROR))) { | ||
905 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | if (C_CLOCAL(tty)) | ||
910 | do_clocal = 1; | ||
911 | |||
912 | /* | ||
913 | * Block waiting for the carrier detect and the line to become | ||
914 | * free (i.e., not in use by the callout). While we are in | ||
915 | * this loop, info->count is dropped by one, so that | ||
916 | * rs_close() knows when to free things. We restore it upon | ||
917 | * exit, either normal or abnormal. | ||
918 | */ | ||
919 | retval = 0; | ||
920 | add_wait_queue(&port->open_wait, &wait); | ||
921 | |||
922 | spin_lock_irqsave(&port->lock, flags); | ||
923 | if (!tty_hung_up_p(filp)) | ||
924 | port->count--; | ||
925 | port->blocked_open++; | ||
926 | spin_unlock_irqrestore(&port->lock, flags); | ||
927 | |||
928 | while (1) { | ||
929 | |||
930 | CD = tty_port_carrier_raised(port); | ||
931 | set_current_state(TASK_INTERRUPTIBLE); | ||
932 | if (tty_hung_up_p(filp) || | ||
933 | !(port->flags & ASYNC_INITIALIZED)) { | ||
934 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
935 | retval = -EAGAIN; | ||
936 | else | ||
937 | retval = -ERESTARTSYS; | ||
938 | break; | ||
939 | } | ||
940 | if (!(port->flags & ASYNC_CLOSING) && | ||
941 | (do_clocal || CD)) | ||
942 | break; | ||
943 | if (signal_pending(current)) { | ||
944 | retval = -ERESTARTSYS; | ||
945 | break; | ||
946 | } | ||
947 | schedule(); | ||
948 | } | ||
949 | __set_current_state(TASK_RUNNING); | ||
950 | remove_wait_queue(&port->open_wait, &wait); | ||
951 | spin_lock_irqsave(&port->lock, flags); | ||
952 | if (!tty_hung_up_p(filp)) | ||
953 | port->count++; | ||
954 | port->blocked_open--; | ||
955 | if (retval == 0) | ||
956 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
957 | spin_unlock_irqrestore(&port->lock, flags); | ||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | static int rc_open(struct tty_struct *tty, struct file *filp) | 877 | static int rc_open(struct tty_struct *tty, struct file *filp) |
962 | { | 878 | { |
963 | int board; | 879 | int board; |
@@ -984,7 +900,7 @@ static int rc_open(struct tty_struct *tty, struct file *filp) | |||
984 | 900 | ||
985 | error = rc_setup_port(bp, port); | 901 | error = rc_setup_port(bp, port); |
986 | if (error == 0) | 902 | if (error == 0) |
987 | error = block_til_ready(tty, filp, port); | 903 | error = tty_port_block_til_ready(&port->port, tty, filp); |
988 | return error; | 904 | return error; |
989 | } | 905 | } |
990 | 906 | ||
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index ac9f21e18c3f..0ded4ed3da3c 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c | |||
@@ -3401,6 +3401,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, | |||
3401 | set_current_state(TASK_RUNNING); | 3401 | set_current_state(TASK_RUNNING); |
3402 | remove_wait_queue(&port->open_wait, &wait); | 3402 | remove_wait_queue(&port->open_wait, &wait); |
3403 | 3403 | ||
3404 | /* FIXME: Racy on hangup during close wait */ | ||
3404 | if (extra_count) | 3405 | if (extra_count) |
3405 | port->count++; | 3406 | port->count++; |
3406 | port->blocked_open--; | 3407 | port->blocked_open--; |
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 9f418bca4a22..ff94182b3813 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c | |||
@@ -151,3 +151,108 @@ void tty_port_raise_dtr_rts(struct tty_port *port) | |||
151 | port->ops->raise_dtr_rts(port); | 151 | port->ops->raise_dtr_rts(port); |
152 | } | 152 | } |
153 | EXPORT_SYMBOL(tty_port_raise_dtr_rts); | 153 | EXPORT_SYMBOL(tty_port_raise_dtr_rts); |
154 | |||
155 | /** | ||
156 | * tty_port_block_til_ready - Waiting logic for tty open | ||
157 | * @port: the tty port being opened | ||
158 | * @tty: the tty device being bound | ||
159 | * @filp: the file pointer of the opener | ||
160 | * | ||
161 | * Implement the core POSIX/SuS tty behaviour when opening a tty device. | ||
162 | * Handles: | ||
163 | * - hangup (both before and during) | ||
164 | * - non blocking open | ||
165 | * - rts/dtr/dcd | ||
166 | * - signals | ||
167 | * - port flags and counts | ||
168 | * | ||
169 | * The passed tty_port must implement the carrier_raised method if it can | ||
170 | * do carrier detect and the raise_dtr_rts method if it supports software | ||
171 | * management of these lines. Note that the dtr/rts raise is done each | ||
172 | * iteration as a hangup may have previously dropped them while we wait. | ||
173 | */ | ||
174 | |||
175 | int tty_port_block_til_ready(struct tty_port *port, | ||
176 | struct tty_struct *tty, struct file *filp) | ||
177 | { | ||
178 | int do_clocal = 0, retval; | ||
179 | unsigned long flags; | ||
180 | DECLARE_WAITQUEUE(wait, current); | ||
181 | int cd; | ||
182 | |||
183 | /* block if port is in the process of being closed */ | ||
184 | if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { | ||
185 | interruptible_sleep_on(&port->close_wait); | ||
186 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
187 | return -EAGAIN; | ||
188 | else | ||
189 | return -ERESTARTSYS; | ||
190 | } | ||
191 | |||
192 | /* if non-blocking mode is set we can pass directly to open unless | ||
193 | the port has just hung up or is in another error state */ | ||
194 | if ((filp->f_flags & O_NONBLOCK) || | ||
195 | (tty->flags & (1 << TTY_IO_ERROR))) { | ||
196 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | if (C_CLOCAL(tty)) | ||
201 | do_clocal = 1; | ||
202 | |||
203 | /* Block waiting until we can proceed. We may need to wait for the | ||
204 | carrier, but we must also wait for any close that is in progress | ||
205 | before the next open may complete */ | ||
206 | |||
207 | retval = 0; | ||
208 | add_wait_queue(&port->open_wait, &wait); | ||
209 | |||
210 | /* The port lock protects the port counts */ | ||
211 | spin_lock_irqsave(&port->lock, flags); | ||
212 | if (!tty_hung_up_p(filp)) | ||
213 | port->count--; | ||
214 | port->blocked_open++; | ||
215 | spin_unlock_irqrestore(&port->lock, flags); | ||
216 | |||
217 | while (1) { | ||
218 | /* Indicate we are open */ | ||
219 | tty_port_raise_dtr_rts(port); | ||
220 | |||
221 | set_current_state(TASK_INTERRUPTIBLE); | ||
222 | /* Check for a hangup or uninitialised port. Return accordingly */ | ||
223 | if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { | ||
224 | if (port->flags & ASYNC_HUP_NOTIFY) | ||
225 | retval = -EAGAIN; | ||
226 | else | ||
227 | retval = -ERESTARTSYS; | ||
228 | break; | ||
229 | } | ||
230 | /* Probe the carrier. For devices with no carrier detect this | ||
231 | will always return true */ | ||
232 | cd = tty_port_carrier_raised(port); | ||
233 | if (!(port->flags & ASYNC_CLOSING) && | ||
234 | (do_clocal || cd)) | ||
235 | break; | ||
236 | if (signal_pending(current)) { | ||
237 | retval = -ERESTARTSYS; | ||
238 | break; | ||
239 | } | ||
240 | schedule(); | ||
241 | } | ||
242 | set_current_state(TASK_RUNNING); | ||
243 | remove_wait_queue(&port->open_wait, &wait); | ||
244 | |||
245 | /* Update counts. A parallel hangup will have set count to zero and | ||
246 | we must not mess that up further */ | ||
247 | spin_lock_irqsave(&port->lock, flags); | ||
248 | if (!tty_hung_up_p(filp)) | ||
249 | port->count++; | ||
250 | port->blocked_open--; | ||
251 | if (retval == 0) | ||
252 | port->flags |= ASYNC_NORMAL_ACTIVE; | ||
253 | spin_unlock_irqrestore(&port->lock, flags); | ||
254 | return 0; | ||
255 | |||
256 | } | ||
257 | EXPORT_SYMBOL(tty_port_block_til_ready); | ||
258 | |||
diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index d4e1534c0e03..2d9242a45a0d 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c | |||
@@ -631,8 +631,8 @@ static void scc_enable_rx_interrupts(void *ptr) | |||
631 | 631 | ||
632 | static int scc_carrier_raised(struct tty_port *port) | 632 | static int scc_carrier_raised(struct tty_port *port) |
633 | { | 633 | { |
634 | struct scc_port *scc = container_of(port, struct scc_port, gs.port); | 634 | struct scc_port *sc = container_of(port, struct scc_port, gs.port); |
635 | unsigned channel = port->channel; | 635 | unsigned channel = sc->channel; |
636 | 636 | ||
637 | return !!(scc_last_status_reg[channel] & SR_DCD); | 637 | return !!(scc_last_status_reg[channel] & SR_DCD); |
638 | } | 638 | } |
@@ -643,7 +643,7 @@ static void scc_shutdown_port(void *ptr) | |||
643 | struct scc_port *port = ptr; | 643 | struct scc_port *port = ptr; |
644 | 644 | ||
645 | port->gs.port.flags &= ~ GS_ACTIVE; | 645 | port->gs.port.flags &= ~ GS_ACTIVE; |
646 | if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) { | 646 | if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) { |
647 | scc_setsignals (port, 0, 0); | 647 | scc_setsignals (port, 0, 0); |
648 | } | 648 | } |
649 | } | 649 | } |
diff --git a/include/linux/tty.h b/include/linux/tty.h index a1a93140e6e4..61a0ab32cf11 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -439,6 +439,8 @@ extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); | |||
439 | extern int tty_port_carrier_raised(struct tty_port *port); | 439 | extern int tty_port_carrier_raised(struct tty_port *port); |
440 | extern void tty_port_raise_dtr_rts(struct tty_port *port); | 440 | extern void tty_port_raise_dtr_rts(struct tty_port *port); |
441 | extern void tty_port_hangup(struct tty_port *port); | 441 | extern void tty_port_hangup(struct tty_port *port); |
442 | extern int tty_port_block_til_ready(struct tty_port *port, | ||
443 | struct tty_struct *tty, struct file *filp); | ||
442 | 444 | ||
443 | extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); | 445 | extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); |
444 | extern int tty_unregister_ldisc(int disc); | 446 | extern int tty_unregister_ldisc(int disc); |