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 /drivers/char/tty_port.c | |
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>
Diffstat (limited to 'drivers/char/tty_port.c')
-rw-r--r-- | drivers/char/tty_port.c | 105 |
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 | } |
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 | |||