Commit | Line | Data |
---|---|---|
9e48565d AC |
1 | /* |
2 | * Tty port functions | |
3 | */ | |
4 | ||
5 | #include <linux/types.h> | |
6 | #include <linux/errno.h> | |
7 | #include <linux/tty.h> | |
8 | #include <linux/tty_driver.h> | |
9 | #include <linux/tty_flip.h> | |
3e61696b | 10 | #include <linux/serial.h> |
9e48565d AC |
11 | #include <linux/timer.h> |
12 | #include <linux/string.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/wait.h> | |
17 | #include <linux/bitops.h> | |
18 | #include <linux/delay.h> | |
19 | #include <linux/module.h> | |
20 | ||
21 | void tty_port_init(struct tty_port *port) | |
22 | { | |
23 | memset(port, 0, sizeof(*port)); | |
24 | init_waitqueue_head(&port->open_wait); | |
25 | init_waitqueue_head(&port->close_wait); | |
bdc04e31 | 26 | init_waitqueue_head(&port->delta_msr_wait); |
9e48565d | 27 | mutex_init(&port->mutex); |
4a90f09b | 28 | spin_lock_init(&port->lock); |
9e48565d AC |
29 | port->close_delay = (50 * HZ) / 100; |
30 | port->closing_wait = (3000 * HZ) / 100; | |
31 | } | |
32 | EXPORT_SYMBOL(tty_port_init); | |
33 | ||
34 | int tty_port_alloc_xmit_buf(struct tty_port *port) | |
35 | { | |
36 | /* We may sleep in get_zeroed_page() */ | |
37 | mutex_lock(&port->mutex); | |
38 | if (port->xmit_buf == NULL) | |
39 | port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); | |
40 | mutex_unlock(&port->mutex); | |
41 | if (port->xmit_buf == NULL) | |
42 | return -ENOMEM; | |
43 | return 0; | |
44 | } | |
45 | EXPORT_SYMBOL(tty_port_alloc_xmit_buf); | |
46 | ||
47 | void tty_port_free_xmit_buf(struct tty_port *port) | |
48 | { | |
49 | mutex_lock(&port->mutex); | |
50 | if (port->xmit_buf != NULL) { | |
51 | free_page((unsigned long)port->xmit_buf); | |
52 | port->xmit_buf = NULL; | |
53 | } | |
54 | mutex_unlock(&port->mutex); | |
55 | } | |
56 | EXPORT_SYMBOL(tty_port_free_xmit_buf); | |
57 | ||
58 | ||
4a90f09b AC |
59 | /** |
60 | * tty_port_tty_get - get a tty reference | |
61 | * @port: tty port | |
62 | * | |
63 | * Return a refcount protected tty instance or NULL if the port is not | |
64 | * associated with a tty (eg due to close or hangup) | |
65 | */ | |
66 | ||
67 | struct tty_struct *tty_port_tty_get(struct tty_port *port) | |
68 | { | |
69 | unsigned long flags; | |
70 | struct tty_struct *tty; | |
71 | ||
72 | spin_lock_irqsave(&port->lock, flags); | |
73 | tty = tty_kref_get(port->tty); | |
74 | spin_unlock_irqrestore(&port->lock, flags); | |
75 | return tty; | |
76 | } | |
77 | EXPORT_SYMBOL(tty_port_tty_get); | |
78 | ||
79 | /** | |
80 | * tty_port_tty_set - set the tty of a port | |
81 | * @port: tty port | |
82 | * @tty: the tty | |
83 | * | |
84 | * Associate the port and tty pair. Manages any internal refcounts. | |
85 | * Pass NULL to deassociate a port | |
86 | */ | |
87 | ||
88 | void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) | |
89 | { | |
90 | unsigned long flags; | |
91 | ||
92 | spin_lock_irqsave(&port->lock, flags); | |
93 | if (port->tty) | |
94 | tty_kref_put(port->tty); | |
cb4bca35 | 95 | port->tty = tty_kref_get(tty); |
4a90f09b AC |
96 | spin_unlock_irqrestore(&port->lock, flags); |
97 | } | |
98 | EXPORT_SYMBOL(tty_port_tty_set); | |
31f35939 | 99 | |
7ca0ff9a AC |
100 | static void tty_port_shutdown(struct tty_port *port) |
101 | { | |
102 | if (port->ops->shutdown && | |
1f5c13fa | 103 | test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) |
7ca0ff9a AC |
104 | port->ops->shutdown(port); |
105 | ||
106 | } | |
107 | ||
3e61696b AC |
108 | /** |
109 | * tty_port_hangup - hangup helper | |
110 | * @port: tty port | |
111 | * | |
112 | * Perform port level tty hangup flag and count changes. Drop the tty | |
113 | * reference. | |
114 | */ | |
115 | ||
116 | void tty_port_hangup(struct tty_port *port) | |
117 | { | |
118 | unsigned long flags; | |
119 | ||
120 | spin_lock_irqsave(&port->lock, flags); | |
121 | port->count = 0; | |
122 | port->flags &= ~ASYNC_NORMAL_ACTIVE; | |
123 | if (port->tty) | |
124 | tty_kref_put(port->tty); | |
125 | port->tty = NULL; | |
126 | spin_unlock_irqrestore(&port->lock, flags); | |
127 | wake_up_interruptible(&port->open_wait); | |
bdc04e31 | 128 | wake_up_interruptible(&port->delta_msr_wait); |
7ca0ff9a | 129 | tty_port_shutdown(port); |
3e61696b AC |
130 | } |
131 | EXPORT_SYMBOL(tty_port_hangup); | |
132 | ||
31f35939 AC |
133 | /** |
134 | * tty_port_carrier_raised - carrier raised check | |
135 | * @port: tty port | |
136 | * | |
137 | * Wrapper for the carrier detect logic. For the moment this is used | |
138 | * to hide some internal details. This will eventually become entirely | |
139 | * internal to the tty port. | |
140 | */ | |
141 | ||
142 | int tty_port_carrier_raised(struct tty_port *port) | |
143 | { | |
144 | if (port->ops->carrier_raised == NULL) | |
145 | return 1; | |
146 | return port->ops->carrier_raised(port); | |
147 | } | |
148 | EXPORT_SYMBOL(tty_port_carrier_raised); | |
5d951fb4 AC |
149 | |
150 | /** | |
fcc8ac18 | 151 | * tty_port_raise_dtr_rts - Raise DTR/RTS |
5d951fb4 AC |
152 | * @port: tty port |
153 | * | |
154 | * Wrapper for the DTR/RTS raise logic. For the moment this is used | |
155 | * to hide some internal details. This will eventually become entirely | |
156 | * internal to the tty port. | |
157 | */ | |
158 | ||
159 | void tty_port_raise_dtr_rts(struct tty_port *port) | |
160 | { | |
fcc8ac18 AC |
161 | if (port->ops->dtr_rts) |
162 | port->ops->dtr_rts(port, 1); | |
5d951fb4 AC |
163 | } |
164 | EXPORT_SYMBOL(tty_port_raise_dtr_rts); | |
36c621d8 | 165 | |
fcc8ac18 AC |
166 | /** |
167 | * tty_port_lower_dtr_rts - Lower DTR/RTS | |
168 | * @port: tty port | |
169 | * | |
170 | * Wrapper for the DTR/RTS raise logic. For the moment this is used | |
171 | * to hide some internal details. This will eventually become entirely | |
172 | * internal to the tty port. | |
173 | */ | |
174 | ||
175 | void tty_port_lower_dtr_rts(struct tty_port *port) | |
176 | { | |
177 | if (port->ops->dtr_rts) | |
178 | port->ops->dtr_rts(port, 0); | |
179 | } | |
180 | EXPORT_SYMBOL(tty_port_lower_dtr_rts); | |
181 | ||
36c621d8 AC |
182 | /** |
183 | * tty_port_block_til_ready - Waiting logic for tty open | |
184 | * @port: the tty port being opened | |
185 | * @tty: the tty device being bound | |
186 | * @filp: the file pointer of the opener | |
187 | * | |
188 | * Implement the core POSIX/SuS tty behaviour when opening a tty device. | |
189 | * Handles: | |
190 | * - hangup (both before and during) | |
191 | * - non blocking open | |
192 | * - rts/dtr/dcd | |
193 | * - signals | |
194 | * - port flags and counts | |
195 | * | |
196 | * The passed tty_port must implement the carrier_raised method if it can | |
fcc8ac18 | 197 | * do carrier detect and the dtr_rts method if it supports software |
36c621d8 AC |
198 | * management of these lines. Note that the dtr/rts raise is done each |
199 | * iteration as a hangup may have previously dropped them while we wait. | |
200 | */ | |
201 | ||
202 | int tty_port_block_til_ready(struct tty_port *port, | |
203 | struct tty_struct *tty, struct file *filp) | |
204 | { | |
205 | int do_clocal = 0, retval; | |
206 | unsigned long flags; | |
6af9a43d | 207 | DEFINE_WAIT(wait); |
36c621d8 AC |
208 | int cd; |
209 | ||
210 | /* block if port is in the process of being closed */ | |
211 | if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { | |
5fc5b42a JS |
212 | wait_event_interruptible(port->close_wait, |
213 | !(port->flags & ASYNC_CLOSING)); | |
36c621d8 AC |
214 | if (port->flags & ASYNC_HUP_NOTIFY) |
215 | return -EAGAIN; | |
216 | else | |
217 | return -ERESTARTSYS; | |
218 | } | |
219 | ||
220 | /* if non-blocking mode is set we can pass directly to open unless | |
221 | the port has just hung up or is in another error state */ | |
8627b96d AC |
222 | if (tty->flags & (1 << TTY_IO_ERROR)) { |
223 | port->flags |= ASYNC_NORMAL_ACTIVE; | |
224 | return 0; | |
225 | } | |
226 | if (filp->f_flags & O_NONBLOCK) { | |
4175f3e3 AC |
227 | /* Indicate we are open */ |
228 | if (tty->termios->c_cflag & CBAUD) | |
229 | tty_port_raise_dtr_rts(port); | |
36c621d8 AC |
230 | port->flags |= ASYNC_NORMAL_ACTIVE; |
231 | return 0; | |
232 | } | |
233 | ||
234 | if (C_CLOCAL(tty)) | |
235 | do_clocal = 1; | |
236 | ||
237 | /* Block waiting until we can proceed. We may need to wait for the | |
238 | carrier, but we must also wait for any close that is in progress | |
239 | before the next open may complete */ | |
240 | ||
241 | retval = 0; | |
36c621d8 AC |
242 | |
243 | /* The port lock protects the port counts */ | |
244 | spin_lock_irqsave(&port->lock, flags); | |
245 | if (!tty_hung_up_p(filp)) | |
246 | port->count--; | |
247 | port->blocked_open++; | |
248 | spin_unlock_irqrestore(&port->lock, flags); | |
249 | ||
250 | while (1) { | |
251 | /* Indicate we are open */ | |
7834909f AC |
252 | if (tty->termios->c_cflag & CBAUD) |
253 | tty_port_raise_dtr_rts(port); | |
36c621d8 | 254 | |
3e3b5c08 | 255 | prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); |
36c621d8 AC |
256 | /* Check for a hangup or uninitialised port. Return accordingly */ |
257 | if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { | |
258 | if (port->flags & ASYNC_HUP_NOTIFY) | |
259 | retval = -EAGAIN; | |
260 | else | |
261 | retval = -ERESTARTSYS; | |
262 | break; | |
263 | } | |
264 | /* Probe the carrier. For devices with no carrier detect this | |
265 | will always return true */ | |
266 | cd = tty_port_carrier_raised(port); | |
267 | if (!(port->flags & ASYNC_CLOSING) && | |
268 | (do_clocal || cd)) | |
269 | break; | |
270 | if (signal_pending(current)) { | |
271 | retval = -ERESTARTSYS; | |
272 | break; | |
273 | } | |
274 | schedule(); | |
275 | } | |
3e3b5c08 | 276 | finish_wait(&port->open_wait, &wait); |
36c621d8 AC |
277 | |
278 | /* Update counts. A parallel hangup will have set count to zero and | |
279 | we must not mess that up further */ | |
280 | spin_lock_irqsave(&port->lock, flags); | |
281 | if (!tty_hung_up_p(filp)) | |
282 | port->count++; | |
283 | port->blocked_open--; | |
284 | if (retval == 0) | |
285 | port->flags |= ASYNC_NORMAL_ACTIVE; | |
286 | spin_unlock_irqrestore(&port->lock, flags); | |
ecc2e05e | 287 | return retval; |
36c621d8 AC |
288 | |
289 | } | |
290 | EXPORT_SYMBOL(tty_port_block_til_ready); | |
291 | ||
a6614999 AC |
292 | int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) |
293 | { | |
294 | unsigned long flags; | |
295 | ||
296 | spin_lock_irqsave(&port->lock, flags); | |
297 | if (tty_hung_up_p(filp)) { | |
298 | spin_unlock_irqrestore(&port->lock, flags); | |
299 | return 0; | |
300 | } | |
301 | ||
302 | if( tty->count == 1 && port->count != 1) { | |
303 | printk(KERN_WARNING | |
304 | "tty_port_close_start: tty->count = 1 port count = %d.\n", | |
305 | port->count); | |
306 | port->count = 1; | |
307 | } | |
308 | if (--port->count < 0) { | |
309 | printk(KERN_WARNING "tty_port_close_start: count = %d\n", | |
310 | port->count); | |
311 | port->count = 0; | |
312 | } | |
313 | ||
314 | if (port->count) { | |
315 | spin_unlock_irqrestore(&port->lock, flags); | |
7ca0ff9a AC |
316 | if (port->ops->drop) |
317 | port->ops->drop(port); | |
a6614999 AC |
318 | return 0; |
319 | } | |
1f5c13fa | 320 | set_bit(ASYNCB_CLOSING, &port->flags); |
a6614999 AC |
321 | tty->closing = 1; |
322 | spin_unlock_irqrestore(&port->lock, flags); | |
fba85e01 AC |
323 | /* Don't block on a stalled port, just pull the chain */ |
324 | if (tty->flow_stopped) | |
325 | tty_driver_flush_buffer(tty); | |
7ca0ff9a | 326 | if (test_bit(ASYNCB_INITIALIZED, &port->flags) && |
6ed1dbae | 327 | port->closing_wait != ASYNC_CLOSING_WAIT_NONE) |
a6614999 | 328 | tty_wait_until_sent(tty, port->closing_wait); |
1ec739be AC |
329 | if (port->drain_delay) { |
330 | unsigned int bps = tty_get_baud_rate(tty); | |
331 | long timeout; | |
332 | ||
333 | if (bps > 1200) | |
334 | timeout = max_t(long, (HZ * 10 * port->drain_delay) / bps, | |
335 | HZ / 10); | |
336 | else | |
337 | timeout = 2 * HZ; | |
338 | schedule_timeout_interruptible(timeout); | |
339 | } | |
7ca0ff9a AC |
340 | /* Don't call port->drop for the last reference. Callers will want |
341 | to drop the last active reference in ->shutdown() or the tty | |
342 | shutdown path */ | |
a6614999 AC |
343 | return 1; |
344 | } | |
345 | EXPORT_SYMBOL(tty_port_close_start); | |
346 | ||
347 | void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) | |
348 | { | |
349 | unsigned long flags; | |
350 | ||
351 | tty_ldisc_flush(tty); | |
352 | ||
fcc8ac18 AC |
353 | if (tty->termios->c_cflag & HUPCL) |
354 | tty_port_lower_dtr_rts(port); | |
355 | ||
a6614999 AC |
356 | spin_lock_irqsave(&port->lock, flags); |
357 | tty->closing = 0; | |
358 | ||
359 | if (port->blocked_open) { | |
360 | spin_unlock_irqrestore(&port->lock, flags); | |
361 | if (port->close_delay) { | |
362 | msleep_interruptible( | |
363 | jiffies_to_msecs(port->close_delay)); | |
364 | } | |
365 | spin_lock_irqsave(&port->lock, flags); | |
366 | wake_up_interruptible(&port->open_wait); | |
367 | } | |
368 | port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); | |
369 | wake_up_interruptible(&port->close_wait); | |
370 | spin_unlock_irqrestore(&port->lock, flags); | |
371 | } | |
372 | EXPORT_SYMBOL(tty_port_close_end); | |
7ca0ff9a AC |
373 | |
374 | void tty_port_close(struct tty_port *port, struct tty_struct *tty, | |
375 | struct file *filp) | |
376 | { | |
377 | if (tty_port_close_start(port, tty, filp) == 0) | |
378 | return; | |
379 | tty_port_shutdown(port); | |
380 | tty_port_close_end(port, tty); | |
381 | tty_port_tty_set(port, NULL); | |
382 | } | |
383 | EXPORT_SYMBOL(tty_port_close); |