aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/tty_port.c
diff options
context:
space:
mode:
authorAlan Cox <alan@redhat.com>2009-01-02 08:46:10 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-02 13:19:39 -0500
commit36c621d82b956ff6ff72273f848af53e6c581aba (patch)
treeedd387d8275a8f25277d264ffed94e8d1c2ba048 /drivers/char/tty_port.c
parent3b6826b250633361f08a6427a4ac0035e5d88c72 (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>
Diffstat (limited to 'drivers/char/tty_port.c')
-rw-r--r--drivers/char/tty_port.c105
1 files changed, 105 insertions, 0 deletions
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}
153EXPORT_SYMBOL(tty_port_raise_dtr_rts); 153EXPORT_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
175int 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}
257EXPORT_SYMBOL(tty_port_block_til_ready);
258