diff options
Diffstat (limited to 'drivers/char/tty_port.c')
-rw-r--r-- | drivers/char/tty_port.c | 97 |
1 files changed, 79 insertions, 18 deletions
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index c63f3d33914a..be492dd66437 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c | |||
@@ -25,19 +25,21 @@ void tty_port_init(struct tty_port *port) | |||
25 | init_waitqueue_head(&port->close_wait); | 25 | init_waitqueue_head(&port->close_wait); |
26 | init_waitqueue_head(&port->delta_msr_wait); | 26 | init_waitqueue_head(&port->delta_msr_wait); |
27 | mutex_init(&port->mutex); | 27 | mutex_init(&port->mutex); |
28 | mutex_init(&port->buf_mutex); | ||
28 | spin_lock_init(&port->lock); | 29 | spin_lock_init(&port->lock); |
29 | port->close_delay = (50 * HZ) / 100; | 30 | port->close_delay = (50 * HZ) / 100; |
30 | port->closing_wait = (3000 * HZ) / 100; | 31 | port->closing_wait = (3000 * HZ) / 100; |
32 | kref_init(&port->kref); | ||
31 | } | 33 | } |
32 | EXPORT_SYMBOL(tty_port_init); | 34 | EXPORT_SYMBOL(tty_port_init); |
33 | 35 | ||
34 | int tty_port_alloc_xmit_buf(struct tty_port *port) | 36 | int tty_port_alloc_xmit_buf(struct tty_port *port) |
35 | { | 37 | { |
36 | /* We may sleep in get_zeroed_page() */ | 38 | /* We may sleep in get_zeroed_page() */ |
37 | mutex_lock(&port->mutex); | 39 | mutex_lock(&port->buf_mutex); |
38 | if (port->xmit_buf == NULL) | 40 | if (port->xmit_buf == NULL) |
39 | port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); | 41 | port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); |
40 | mutex_unlock(&port->mutex); | 42 | mutex_unlock(&port->buf_mutex); |
41 | if (port->xmit_buf == NULL) | 43 | if (port->xmit_buf == NULL) |
42 | return -ENOMEM; | 44 | return -ENOMEM; |
43 | return 0; | 45 | return 0; |
@@ -46,15 +48,32 @@ EXPORT_SYMBOL(tty_port_alloc_xmit_buf); | |||
46 | 48 | ||
47 | void tty_port_free_xmit_buf(struct tty_port *port) | 49 | void tty_port_free_xmit_buf(struct tty_port *port) |
48 | { | 50 | { |
49 | mutex_lock(&port->mutex); | 51 | mutex_lock(&port->buf_mutex); |
50 | if (port->xmit_buf != NULL) { | 52 | if (port->xmit_buf != NULL) { |
51 | free_page((unsigned long)port->xmit_buf); | 53 | free_page((unsigned long)port->xmit_buf); |
52 | port->xmit_buf = NULL; | 54 | port->xmit_buf = NULL; |
53 | } | 55 | } |
54 | mutex_unlock(&port->mutex); | 56 | mutex_unlock(&port->buf_mutex); |
55 | } | 57 | } |
56 | EXPORT_SYMBOL(tty_port_free_xmit_buf); | 58 | EXPORT_SYMBOL(tty_port_free_xmit_buf); |
57 | 59 | ||
60 | static void tty_port_destructor(struct kref *kref) | ||
61 | { | ||
62 | struct tty_port *port = container_of(kref, struct tty_port, kref); | ||
63 | if (port->xmit_buf) | ||
64 | free_page((unsigned long)port->xmit_buf); | ||
65 | if (port->ops->destruct) | ||
66 | port->ops->destruct(port); | ||
67 | else | ||
68 | kfree(port); | ||
69 | } | ||
70 | |||
71 | void tty_port_put(struct tty_port *port) | ||
72 | { | ||
73 | if (port) | ||
74 | kref_put(&port->kref, tty_port_destructor); | ||
75 | } | ||
76 | EXPORT_SYMBOL(tty_port_put); | ||
58 | 77 | ||
59 | /** | 78 | /** |
60 | * tty_port_tty_get - get a tty reference | 79 | * tty_port_tty_get - get a tty reference |
@@ -99,10 +118,11 @@ EXPORT_SYMBOL(tty_port_tty_set); | |||
99 | 118 | ||
100 | static void tty_port_shutdown(struct tty_port *port) | 119 | static void tty_port_shutdown(struct tty_port *port) |
101 | { | 120 | { |
121 | mutex_lock(&port->mutex); | ||
102 | if (port->ops->shutdown && | 122 | if (port->ops->shutdown && |
103 | test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) | 123 | test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) |
104 | port->ops->shutdown(port); | 124 | port->ops->shutdown(port); |
105 | 125 | mutex_unlock(&port->mutex); | |
106 | } | 126 | } |
107 | 127 | ||
108 | /** | 128 | /** |
@@ -120,8 +140,10 @@ void tty_port_hangup(struct tty_port *port) | |||
120 | spin_lock_irqsave(&port->lock, flags); | 140 | spin_lock_irqsave(&port->lock, flags); |
121 | port->count = 0; | 141 | port->count = 0; |
122 | port->flags &= ~ASYNC_NORMAL_ACTIVE; | 142 | port->flags &= ~ASYNC_NORMAL_ACTIVE; |
123 | if (port->tty) | 143 | if (port->tty) { |
144 | set_bit(TTY_IO_ERROR, &port->tty->flags); | ||
124 | tty_kref_put(port->tty); | 145 | tty_kref_put(port->tty); |
146 | } | ||
125 | port->tty = NULL; | 147 | port->tty = NULL; |
126 | spin_unlock_irqrestore(&port->lock, flags); | 148 | spin_unlock_irqrestore(&port->lock, flags); |
127 | wake_up_interruptible(&port->open_wait); | 149 | wake_up_interruptible(&port->open_wait); |
@@ -198,7 +220,7 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts); | |||
198 | * management of these lines. Note that the dtr/rts raise is done each | 220 | * 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. | 221 | * iteration as a hangup may have previously dropped them while we wait. |
200 | */ | 222 | */ |
201 | 223 | ||
202 | int tty_port_block_til_ready(struct tty_port *port, | 224 | int tty_port_block_til_ready(struct tty_port *port, |
203 | struct tty_struct *tty, struct file *filp) | 225 | struct tty_struct *tty, struct file *filp) |
204 | { | 226 | { |
@@ -253,7 +275,8 @@ int tty_port_block_til_ready(struct tty_port *port, | |||
253 | tty_port_raise_dtr_rts(port); | 275 | tty_port_raise_dtr_rts(port); |
254 | 276 | ||
255 | prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); | 277 | prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); |
256 | /* Check for a hangup or uninitialised port. Return accordingly */ | 278 | /* Check for a hangup or uninitialised port. |
279 | Return accordingly */ | ||
257 | if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { | 280 | if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { |
258 | if (port->flags & ASYNC_HUP_NOTIFY) | 281 | if (port->flags & ASYNC_HUP_NOTIFY) |
259 | retval = -EAGAIN; | 282 | retval = -EAGAIN; |
@@ -285,11 +308,11 @@ int tty_port_block_til_ready(struct tty_port *port, | |||
285 | port->flags |= ASYNC_NORMAL_ACTIVE; | 308 | port->flags |= ASYNC_NORMAL_ACTIVE; |
286 | spin_unlock_irqrestore(&port->lock, flags); | 309 | spin_unlock_irqrestore(&port->lock, flags); |
287 | return retval; | 310 | return retval; |
288 | |||
289 | } | 311 | } |
290 | EXPORT_SYMBOL(tty_port_block_til_ready); | 312 | EXPORT_SYMBOL(tty_port_block_til_ready); |
291 | 313 | ||
292 | int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) | 314 | int tty_port_close_start(struct tty_port *port, |
315 | struct tty_struct *tty, struct file *filp) | ||
293 | { | 316 | { |
294 | unsigned long flags; | 317 | unsigned long flags; |
295 | 318 | ||
@@ -299,7 +322,7 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f | |||
299 | return 0; | 322 | return 0; |
300 | } | 323 | } |
301 | 324 | ||
302 | if( tty->count == 1 && port->count != 1) { | 325 | if (tty->count == 1 && port->count != 1) { |
303 | printk(KERN_WARNING | 326 | printk(KERN_WARNING |
304 | "tty_port_close_start: tty->count = 1 port count = %d.\n", | 327 | "tty_port_close_start: tty->count = 1 port count = %d.\n", |
305 | port->count); | 328 | port->count); |
@@ -331,12 +354,20 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f | |||
331 | long timeout; | 354 | long timeout; |
332 | 355 | ||
333 | if (bps > 1200) | 356 | if (bps > 1200) |
334 | timeout = max_t(long, (HZ * 10 * port->drain_delay) / bps, | 357 | timeout = max_t(long, |
335 | HZ / 10); | 358 | (HZ * 10 * port->drain_delay) / bps, HZ / 10); |
336 | else | 359 | else |
337 | timeout = 2 * HZ; | 360 | timeout = 2 * HZ; |
338 | schedule_timeout_interruptible(timeout); | 361 | schedule_timeout_interruptible(timeout); |
339 | } | 362 | } |
363 | /* Flush the ldisc buffering */ | ||
364 | tty_ldisc_flush(tty); | ||
365 | |||
366 | /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to | ||
367 | hang up the line */ | ||
368 | if (tty->termios->c_cflag & HUPCL) | ||
369 | tty_port_lower_dtr_rts(port); | ||
370 | |||
340 | /* Don't call port->drop for the last reference. Callers will want | 371 | /* Don't call port->drop for the last reference. Callers will want |
341 | to drop the last active reference in ->shutdown() or the tty | 372 | to drop the last active reference in ->shutdown() or the tty |
342 | shutdown path */ | 373 | shutdown path */ |
@@ -348,11 +379,6 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) | |||
348 | { | 379 | { |
349 | unsigned long flags; | 380 | unsigned long flags; |
350 | 381 | ||
351 | tty_ldisc_flush(tty); | ||
352 | |||
353 | if (tty->termios->c_cflag & HUPCL) | ||
354 | tty_port_lower_dtr_rts(port); | ||
355 | |||
356 | spin_lock_irqsave(&port->lock, flags); | 382 | spin_lock_irqsave(&port->lock, flags); |
357 | tty->closing = 0; | 383 | tty->closing = 0; |
358 | 384 | ||
@@ -377,7 +403,42 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, | |||
377 | if (tty_port_close_start(port, tty, filp) == 0) | 403 | if (tty_port_close_start(port, tty, filp) == 0) |
378 | return; | 404 | return; |
379 | tty_port_shutdown(port); | 405 | tty_port_shutdown(port); |
406 | set_bit(TTY_IO_ERROR, &tty->flags); | ||
380 | tty_port_close_end(port, tty); | 407 | tty_port_close_end(port, tty); |
381 | tty_port_tty_set(port, NULL); | 408 | tty_port_tty_set(port, NULL); |
382 | } | 409 | } |
383 | EXPORT_SYMBOL(tty_port_close); | 410 | EXPORT_SYMBOL(tty_port_close); |
411 | |||
412 | int tty_port_open(struct tty_port *port, struct tty_struct *tty, | ||
413 | struct file *filp) | ||
414 | { | ||
415 | spin_lock_irq(&port->lock); | ||
416 | if (!tty_hung_up_p(filp)) | ||
417 | ++port->count; | ||
418 | spin_unlock_irq(&port->lock); | ||
419 | tty_port_tty_set(port, tty); | ||
420 | |||
421 | /* | ||
422 | * Do the device-specific open only if the hardware isn't | ||
423 | * already initialized. Serialize open and shutdown using the | ||
424 | * port mutex. | ||
425 | */ | ||
426 | |||
427 | mutex_lock(&port->mutex); | ||
428 | |||
429 | if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { | ||
430 | clear_bit(TTY_IO_ERROR, &tty->flags); | ||
431 | if (port->ops->activate) { | ||
432 | int retval = port->ops->activate(port, tty); | ||
433 | if (retval) { | ||
434 | mutex_unlock(&port->mutex); | ||
435 | return retval; | ||
436 | } | ||
437 | } | ||
438 | set_bit(ASYNCB_INITIALIZED, &port->flags); | ||
439 | } | ||
440 | mutex_unlock(&port->mutex); | ||
441 | return tty_port_block_til_ready(port, tty, filp); | ||
442 | } | ||
443 | |||
444 | EXPORT_SYMBOL(tty_port_open); | ||