tty: use port methods for the rocket driver
[linux-2.6.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 /**
100  *      tty_port_hangup         -       hangup helper
101  *      @port: tty port
102  *
103  *      Perform port level tty hangup flag and count changes. Drop the tty
104  *      reference.
105  */
106
107 void tty_port_hangup(struct tty_port *port)
108 {
109         unsigned long flags;
110
111         spin_lock_irqsave(&port->lock, flags);
112         port->count = 0;
113         port->flags &= ~ASYNC_NORMAL_ACTIVE;
114         if (port->tty)
115                 tty_kref_put(port->tty);
116         port->tty = NULL;
117         spin_unlock_irqrestore(&port->lock, flags);
118         wake_up_interruptible(&port->open_wait);
119 }
120 EXPORT_SYMBOL(tty_port_hangup);
121
122 /**
123  *      tty_port_carrier_raised -       carrier raised check
124  *      @port: tty port
125  *
126  *      Wrapper for the carrier detect logic. For the moment this is used
127  *      to hide some internal details. This will eventually become entirely
128  *      internal to the tty port.
129  */
130
131 int tty_port_carrier_raised(struct tty_port *port)
132 {
133         if (port->ops->carrier_raised == NULL)
134                 return 1;
135         return port->ops->carrier_raised(port);
136 }
137 EXPORT_SYMBOL(tty_port_carrier_raised);
138
139 /**
140  *      tty_port_raise_dtr_rts  -       Riase DTR/RTS
141  *      @port: tty port
142  *
143  *      Wrapper for the DTR/RTS raise logic. For the moment this is used
144  *      to hide some internal details. This will eventually become entirely
145  *      internal to the tty port.
146  */
147
148 void tty_port_raise_dtr_rts(struct tty_port *port)
149 {
150         if (port->ops->raise_dtr_rts)
151                 port->ops->raise_dtr_rts(port);
152 }
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                 if (tty->termios->c_cflag & CBAUD)
220                         tty_port_raise_dtr_rts(port);
221
222                 set_current_state(TASK_INTERRUPTIBLE);
223                 /* Check for a hangup or uninitialised port. Return accordingly */
224                 if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
225                         if (port->flags & ASYNC_HUP_NOTIFY)
226                                 retval = -EAGAIN;
227                         else
228                                 retval = -ERESTARTSYS;
229                         break;
230                 }
231                 /* Probe the carrier. For devices with no carrier detect this
232                    will always return true */
233                 cd = tty_port_carrier_raised(port);
234                 if (!(port->flags & ASYNC_CLOSING) &&
235                                 (do_clocal || cd))
236                         break;
237                 if (signal_pending(current)) {
238                         retval = -ERESTARTSYS;
239                         break;
240                 }
241                 schedule();
242         }
243         set_current_state(TASK_RUNNING);
244         remove_wait_queue(&port->open_wait, &wait);
245
246         /* Update counts. A parallel hangup will have set count to zero and
247            we must not mess that up further */
248         spin_lock_irqsave(&port->lock, flags);
249         if (!tty_hung_up_p(filp))
250                 port->count++;
251         port->blocked_open--;
252         if (retval == 0)
253                 port->flags |= ASYNC_NORMAL_ACTIVE;
254         spin_unlock_irqrestore(&port->lock, flags);
255         return 0;
256         
257 }
258 EXPORT_SYMBOL(tty_port_block_til_ready);
259
260 int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp)
261 {
262         unsigned long flags;
263
264         spin_lock_irqsave(&port->lock, flags);
265         if (tty_hung_up_p(filp)) {
266                 spin_unlock_irqrestore(&port->lock, flags);
267                 return 0;
268         }
269
270         if( tty->count == 1 && port->count != 1) {
271                 printk(KERN_WARNING
272                     "tty_port_close_start: tty->count = 1 port count = %d.\n",
273                                                                 port->count);
274                 port->count = 1;
275         }
276         if (--port->count < 0) {
277                 printk(KERN_WARNING "tty_port_close_start: count = %d\n",
278                                                                 port->count);
279                 port->count = 0;
280         }
281
282         if (port->count) {
283                 spin_unlock_irqrestore(&port->lock, flags);
284                 return 0;
285         }
286         port->flags |= ASYNC_CLOSING;
287         tty->closing = 1;
288         spin_unlock_irqrestore(&port->lock, flags);
289         /* Don't block on a stalled port, just pull the chain */
290         if (tty->flow_stopped)
291                 tty_driver_flush_buffer(tty);
292         if (port->flags & ASYNC_INITIALIZED &&
293                         port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
294                 tty_wait_until_sent(tty, port->closing_wait);
295         return 1;
296 }
297 EXPORT_SYMBOL(tty_port_close_start);
298
299 void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
300 {
301         unsigned long flags;
302
303         tty_ldisc_flush(tty);
304
305         spin_lock_irqsave(&port->lock, flags);
306         tty->closing = 0;
307
308         if (port->blocked_open) {
309                 spin_unlock_irqrestore(&port->lock, flags);
310                 if (port->close_delay) {
311                         msleep_interruptible(
312                                 jiffies_to_msecs(port->close_delay));
313                 }
314                 spin_lock_irqsave(&port->lock, flags);
315                 wake_up_interruptible(&port->open_wait);
316         }
317         port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
318         wake_up_interruptible(&port->close_wait);
319         spin_unlock_irqrestore(&port->lock, flags);
320 }
321 EXPORT_SYMBOL(tty_port_close_end);