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