]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/char/tty_port.c
tty: Introduce a tty_port generic block_til_ready
[linux-2.6-omap-h63xx.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                 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