diff options
Diffstat (limited to 'drivers/char/tty_port.c')
-rw-r--r-- | drivers/char/tty_port.c | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index c8f8024cb40e..9b8004c72686 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/tty.h> | 7 | #include <linux/tty.h> |
8 | #include <linux/tty_driver.h> | 8 | #include <linux/tty_driver.h> |
9 | #include <linux/tty_flip.h> | 9 | #include <linux/tty_flip.h> |
10 | #include <linux/serial.h> | ||
10 | #include <linux/timer.h> | 11 | #include <linux/timer.h> |
11 | #include <linux/string.h> | 12 | #include <linux/string.h> |
12 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
@@ -94,3 +95,227 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) | |||
94 | spin_unlock_irqrestore(&port->lock, flags); | 95 | spin_unlock_irqrestore(&port->lock, flags); |
95 | } | 96 | } |
96 | EXPORT_SYMBOL(tty_port_tty_set); | 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); | ||