Blame view

drivers/tty/tty_port.c 15.4 KB
9e48565d2   Alan Cox   tty: Split tty_po...
1
2
3
4
5
6
7
8
9
  /*
   * Tty port functions
   */
  
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/tty.h>
  #include <linux/tty_driver.h>
  #include <linux/tty_flip.h>
3e61696bd   Alan Cox   isicom: redo lock...
10
  #include <linux/serial.h>
9e48565d2   Alan Cox   tty: Split tty_po...
11
12
13
14
15
16
17
18
19
20
21
22
23
  #include <linux/timer.h>
  #include <linux/string.h>
  #include <linux/slab.h>
  #include <linux/sched.h>
  #include <linux/init.h>
  #include <linux/wait.h>
  #include <linux/bitops.h>
  #include <linux/delay.h>
  #include <linux/module.h>
  
  void tty_port_init(struct tty_port *port)
  {
  	memset(port, 0, sizeof(*port));
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
24
  	tty_buffer_init(port);
9e48565d2   Alan Cox   tty: Split tty_po...
25
26
  	init_waitqueue_head(&port->open_wait);
  	init_waitqueue_head(&port->close_wait);
bdc04e317   Alan Cox   serial: move delt...
27
  	init_waitqueue_head(&port->delta_msr_wait);
9e48565d2   Alan Cox   tty: Split tty_po...
28
  	mutex_init(&port->mutex);
44e4909e4   Alan Cox   tty: tty_port: Ch...
29
  	mutex_init(&port->buf_mutex);
4a90f09b2   Alan Cox   tty: usb-serial k...
30
  	spin_lock_init(&port->lock);
9e48565d2   Alan Cox   tty: Split tty_po...
31
32
  	port->close_delay = (50 * HZ) / 100;
  	port->closing_wait = (3000 * HZ) / 100;
568aafc62   Alan Cox   tty: tty_port: Ad...
33
  	kref_init(&port->kref);
9e48565d2   Alan Cox   tty: Split tty_po...
34
35
  }
  EXPORT_SYMBOL(tty_port_init);
72a33bf58   Jiri Slaby   TTY: tty_port, ad...
36
  /**
2cb4ca020   Jiri Slaby   TTY: add tty_port...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
   * tty_port_link_device - link tty and tty_port
   * @port: tty_port of the device
   * @driver: tty_driver for this device
   * @index: index of the tty
   *
   * Provide the tty layer wit ha link from a tty (specified by @index) to a
   * tty_port (@port). Use this only if neither tty_port_register_device nor
   * tty_port_install is used in the driver. If used, this has to be called before
   * tty_register_driver.
   */
  void tty_port_link_device(struct tty_port *port,
  		struct tty_driver *driver, unsigned index)
  {
  	if (WARN_ON(index >= driver->num))
  		return;
  	driver->ports[index] = port;
  }
  EXPORT_SYMBOL_GPL(tty_port_link_device);
  
  /**
72a33bf58   Jiri Slaby   TTY: tty_port, ad...
57
58
59
60
61
62
63
64
65
66
   * tty_port_register_device - register tty device
   * @port: tty_port of the device
   * @driver: tty_driver for this device
   * @index: index of the tty
   * @device: parent if exists, otherwise NULL
   *
   * It is the same as tty_register_device except the provided @port is linked to
   * a concrete tty specified by @index. Use this or tty_port_install (or both).
   * Call tty_port_link_device as a last resort.
   */
057eb856e   Jiri Slaby   TTY: add tty_port...
67
68
69
70
  struct device *tty_port_register_device(struct tty_port *port,
  		struct tty_driver *driver, unsigned index,
  		struct device *device)
  {
2cb4ca020   Jiri Slaby   TTY: add tty_port...
71
  	tty_port_link_device(port, driver, index);
057eb856e   Jiri Slaby   TTY: add tty_port...
72
73
74
  	return tty_register_device(driver, index, device);
  }
  EXPORT_SYMBOL_GPL(tty_port_register_device);
b1b799164   Tomas Hlavacek   tty_register_devi...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  /**
   * tty_port_register_device_attr - register tty device
   * @port: tty_port of the device
   * @driver: tty_driver for this device
   * @index: index of the tty
   * @device: parent if exists, otherwise NULL
   * @drvdata: Driver data to be set to device.
   * @attr_grp: Attribute group to be set on device.
   *
   * It is the same as tty_register_device_attr except the provided @port is
   * linked to a concrete tty specified by @index. Use this or tty_port_install
   * (or both). Call tty_port_link_device as a last resort.
   */
  struct device *tty_port_register_device_attr(struct tty_port *port,
  		struct tty_driver *driver, unsigned index,
  		struct device *device, void *drvdata,
  		const struct attribute_group **attr_grp)
  {
  	tty_port_link_device(port, driver, index);
  	return tty_register_device_attr(driver, index, device, drvdata,
  			attr_grp);
  }
  EXPORT_SYMBOL_GPL(tty_port_register_device_attr);
9e48565d2   Alan Cox   tty: Split tty_po...
98
99
100
  int tty_port_alloc_xmit_buf(struct tty_port *port)
  {
  	/* We may sleep in get_zeroed_page() */
44e4909e4   Alan Cox   tty: tty_port: Ch...
101
  	mutex_lock(&port->buf_mutex);
9e48565d2   Alan Cox   tty: Split tty_po...
102
103
  	if (port->xmit_buf == NULL)
  		port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
44e4909e4   Alan Cox   tty: tty_port: Ch...
104
  	mutex_unlock(&port->buf_mutex);
9e48565d2   Alan Cox   tty: Split tty_po...
105
106
107
108
109
110
111
112
  	if (port->xmit_buf == NULL)
  		return -ENOMEM;
  	return 0;
  }
  EXPORT_SYMBOL(tty_port_alloc_xmit_buf);
  
  void tty_port_free_xmit_buf(struct tty_port *port)
  {
44e4909e4   Alan Cox   tty: tty_port: Ch...
113
  	mutex_lock(&port->buf_mutex);
9e48565d2   Alan Cox   tty: Split tty_po...
114
115
116
117
  	if (port->xmit_buf != NULL) {
  		free_page((unsigned long)port->xmit_buf);
  		port->xmit_buf = NULL;
  	}
44e4909e4   Alan Cox   tty: tty_port: Ch...
118
  	mutex_unlock(&port->buf_mutex);
9e48565d2   Alan Cox   tty: Split tty_po...
119
120
  }
  EXPORT_SYMBOL(tty_port_free_xmit_buf);
de274bfe0   Jiri Slaby   TTY: introduce tt...
121
122
123
124
125
126
127
128
129
130
  /**
   * tty_port_destroy -- destroy inited port
   * @port: tty port to be doestroyed
   *
   * When a port was initialized using tty_port_init, one has to destroy the
   * port by this function. Either indirectly by using tty_port refcounting
   * (tty_port_put) or directly if refcounting is not used.
   */
  void tty_port_destroy(struct tty_port *port)
  {
4f98d4675   Peter Hurley   tty: Complete own...
131
  	cancel_work_sync(&port->buf.work);
de274bfe0   Jiri Slaby   TTY: introduce tt...
132
133
134
  	tty_buffer_free_all(port);
  }
  EXPORT_SYMBOL(tty_port_destroy);
568aafc62   Alan Cox   tty: tty_port: Ad...
135
136
137
  static void tty_port_destructor(struct kref *kref)
  {
  	struct tty_port *port = container_of(kref, struct tty_port, kref);
e3bfea23a   Peter Hurley   tty: Prevent tty_...
138
139
140
141
  
  	/* check if last port ref was dropped before tty release */
  	if (WARN_ON(port->itty))
  		return;
568aafc62   Alan Cox   tty: tty_port: Ad...
142
143
  	if (port->xmit_buf)
  		free_page((unsigned long)port->xmit_buf);
de274bfe0   Jiri Slaby   TTY: introduce tt...
144
  	tty_port_destroy(port);
81c79838c   Jiri Slaby   TTY: pty, fix tty...
145
  	if (port->ops && port->ops->destruct)
568aafc62   Alan Cox   tty: tty_port: Ad...
146
147
148
149
150
151
152
153
154
155
156
  		port->ops->destruct(port);
  	else
  		kfree(port);
  }
  
  void tty_port_put(struct tty_port *port)
  {
  	if (port)
  		kref_put(&port->kref, tty_port_destructor);
  }
  EXPORT_SYMBOL(tty_port_put);
9e48565d2   Alan Cox   tty: Split tty_po...
157

4a90f09b2   Alan Cox   tty: usb-serial k...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  /**
   *	tty_port_tty_get	-	get a tty reference
   *	@port: tty port
   *
   *	Return a refcount protected tty instance or NULL if the port is not
   *	associated with a tty (eg due to close or hangup)
   */
  
  struct tty_struct *tty_port_tty_get(struct tty_port *port)
  {
  	unsigned long flags;
  	struct tty_struct *tty;
  
  	spin_lock_irqsave(&port->lock, flags);
  	tty = tty_kref_get(port->tty);
  	spin_unlock_irqrestore(&port->lock, flags);
  	return tty;
  }
  EXPORT_SYMBOL(tty_port_tty_get);
  
  /**
   *	tty_port_tty_set	-	set the tty of a port
   *	@port: tty port
   *	@tty: the tty
   *
   *	Associate the port and tty pair. Manages any internal refcounts.
   *	Pass NULL to deassociate a port
   */
  
  void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&port->lock, flags);
  	if (port->tty)
  		tty_kref_put(port->tty);
cb4bca354   Alan Cox   tty: Fix tty_port...
194
  	port->tty = tty_kref_get(tty);
4a90f09b2   Alan Cox   tty: usb-serial k...
195
196
197
  	spin_unlock_irqrestore(&port->lock, flags);
  }
  EXPORT_SYMBOL(tty_port_tty_set);
31f35939d   Alan Cox   tty_port: Add a p...
198

957dacaee   Johan Hovold   TTY: fix DTR not ...
199
  static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
7ca0ff9ab   Alan Cox   tty: Add a full p...
200
  {
64bc39791   Alan Cox   tty_port: add "tt...
201
  	mutex_lock(&port->mutex);
8bde9658a   Johan Hovold   TTY: clean up por...
202
203
204
205
  	if (port->console)
  		goto out;
  
  	if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) {
957dacaee   Johan Hovold   TTY: fix DTR not ...
206
207
208
209
210
211
  		/*
  		 * Drop DTR/RTS if HUPCL is set. This causes any attached
  		 * modem to hang up the line.
  		 */
  		if (tty && C_HUPCL(tty))
  			tty_port_lower_dtr_rts(port);
8bde9658a   Johan Hovold   TTY: clean up por...
212
  		if (port->ops->shutdown)
7ca0ff9ab   Alan Cox   tty: Add a full p...
213
  			port->ops->shutdown(port);
8bde9658a   Johan Hovold   TTY: clean up por...
214
215
  	}
  out:
64bc39791   Alan Cox   tty_port: add "tt...
216
  	mutex_unlock(&port->mutex);
7ca0ff9ab   Alan Cox   tty: Add a full p...
217
  }
31f35939d   Alan Cox   tty_port: Add a p...
218
  /**
3e61696bd   Alan Cox   isicom: redo lock...
219
220
221
222
223
224
225
226
227
   *	tty_port_hangup		-	hangup helper
   *	@port: tty port
   *
   *	Perform port level tty hangup flag and count changes. Drop the tty
   *	reference.
   */
  
  void tty_port_hangup(struct tty_port *port)
  {
957dacaee   Johan Hovold   TTY: fix DTR not ...
228
  	struct tty_struct *tty;
3e61696bd   Alan Cox   isicom: redo lock...
229
230
231
232
233
  	unsigned long flags;
  
  	spin_lock_irqsave(&port->lock, flags);
  	port->count = 0;
  	port->flags &= ~ASYNC_NORMAL_ACTIVE;
957dacaee   Johan Hovold   TTY: fix DTR not ...
234
235
236
  	tty = port->tty;
  	if (tty)
  		set_bit(TTY_IO_ERROR, &tty->flags);
3e61696bd   Alan Cox   isicom: redo lock...
237
238
  	port->tty = NULL;
  	spin_unlock_irqrestore(&port->lock, flags);
957dacaee   Johan Hovold   TTY: fix DTR not ...
239
240
  	tty_port_shutdown(port, tty);
  	tty_kref_put(tty);
3e61696bd   Alan Cox   isicom: redo lock...
241
  	wake_up_interruptible(&port->open_wait);
bdc04e317   Alan Cox   serial: move delt...
242
  	wake_up_interruptible(&port->delta_msr_wait);
3e61696bd   Alan Cox   isicom: redo lock...
243
244
245
246
  }
  EXPORT_SYMBOL(tty_port_hangup);
  
  /**
aa27a094e   Jiri Slaby   TTY: add tty_port...
247
248
249
250
251
252
253
254
   * tty_port_tty_hangup - helper to hang up a tty
   *
   * @port: tty port
   * @check_clocal: hang only ttys with CLOCAL unset?
   */
  void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
  {
  	struct tty_struct *tty = tty_port_tty_get(port);
1d9e689c9   Gianluca Anzolin   tty_port: Fix ref...
255
  	if (tty && (!check_clocal || !C_CLOCAL(tty)))
aa27a094e   Jiri Slaby   TTY: add tty_port...
256
  		tty_hangup(tty);
1d9e689c9   Gianluca Anzolin   tty_port: Fix ref...
257
  	tty_kref_put(tty);
aa27a094e   Jiri Slaby   TTY: add tty_port...
258
259
260
261
  }
  EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
  
  /**
6aad04f21   Jiri Slaby   TTY: add tty_port...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
   * tty_port_tty_wakeup - helper to wake up a tty
   *
   * @port: tty port
   */
  void tty_port_tty_wakeup(struct tty_port *port)
  {
  	struct tty_struct *tty = tty_port_tty_get(port);
  
  	if (tty) {
  		tty_wakeup(tty);
  		tty_kref_put(tty);
  	}
  }
  EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
  
  /**
31f35939d   Alan Cox   tty_port: Add a p...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
   *	tty_port_carrier_raised	-	carrier raised check
   *	@port: tty port
   *
   *	Wrapper for the carrier detect logic. For the moment this is used
   *	to hide some internal details. This will eventually become entirely
   *	internal to the tty port.
   */
  
  int tty_port_carrier_raised(struct tty_port *port)
  {
  	if (port->ops->carrier_raised == NULL)
  		return 1;
  	return port->ops->carrier_raised(port);
  }
  EXPORT_SYMBOL(tty_port_carrier_raised);
5d951fb45   Alan Cox   tty: Pull the dtr...
293
294
  
  /**
fcc8ac182   Alan Cox   tty: Add carrier ...
295
   *	tty_port_raise_dtr_rts	-	Raise DTR/RTS
5d951fb45   Alan Cox   tty: Pull the dtr...
296
297
298
299
300
301
302
303
304
   *	@port: tty port
   *
   *	Wrapper for the DTR/RTS raise logic. For the moment this is used
   *	to hide some internal details. This will eventually become entirely
   *	internal to the tty port.
   */
  
  void tty_port_raise_dtr_rts(struct tty_port *port)
  {
fcc8ac182   Alan Cox   tty: Add carrier ...
305
306
  	if (port->ops->dtr_rts)
  		port->ops->dtr_rts(port, 1);
5d951fb45   Alan Cox   tty: Pull the dtr...
307
308
  }
  EXPORT_SYMBOL(tty_port_raise_dtr_rts);
36c621d82   Alan Cox   tty: Introduce a ...
309
310
  
  /**
fcc8ac182   Alan Cox   tty: Add carrier ...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
   *	tty_port_lower_dtr_rts	-	Lower DTR/RTS
   *	@port: tty port
   *
   *	Wrapper for the DTR/RTS raise logic. For the moment this is used
   *	to hide some internal details. This will eventually become entirely
   *	internal to the tty port.
   */
  
  void tty_port_lower_dtr_rts(struct tty_port *port)
  {
  	if (port->ops->dtr_rts)
  		port->ops->dtr_rts(port, 0);
  }
  EXPORT_SYMBOL(tty_port_lower_dtr_rts);
  
  /**
36c621d82   Alan Cox   tty: Introduce a ...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
   *	tty_port_block_til_ready	-	Waiting logic for tty open
   *	@port: the tty port being opened
   *	@tty: the tty device being bound
   *	@filp: the file pointer of the opener
   *
   *	Implement the core POSIX/SuS tty behaviour when opening a tty device.
   *	Handles:
   *		- hangup (both before and during)
   *		- non blocking open
   *		- rts/dtr/dcd
   *		- signals
   *		- port flags and counts
   *
   *	The passed tty_port must implement the carrier_raised method if it can
fcc8ac182   Alan Cox   tty: Add carrier ...
341
   *	do carrier detect and the dtr_rts method if it supports software
36c621d82   Alan Cox   tty: Introduce a ...
342
343
344
   *	management of these lines. Note that the dtr/rts raise is done each
   *	iteration as a hangup may have previously dropped them while we wait.
   */
d774a56d2   Alan Cox   tty_port: coding ...
345

36c621d82   Alan Cox   tty: Introduce a ...
346
347
348
349
350
  int tty_port_block_til_ready(struct tty_port *port,
  				struct tty_struct *tty, struct file *filp)
  {
  	int do_clocal = 0, retval;
  	unsigned long flags;
6af9a43d5   Jiri Slaby   tty: fix tty_port...
351
  	DEFINE_WAIT(wait);
36c621d82   Alan Cox   tty: Introduce a ...
352
353
354
  
  	/* block if port is in the process of being closed */
  	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
89c8d91e3   Alan Cox   tty: localise the...
355
  		wait_event_interruptible_tty(tty, port->close_wait,
5fc5b42a3   Jiri Slaby   tty: remove sleep_on
356
  				!(port->flags & ASYNC_CLOSING));
36c621d82   Alan Cox   tty: Introduce a ...
357
358
359
360
361
362
363
364
  		if (port->flags & ASYNC_HUP_NOTIFY)
  			return -EAGAIN;
  		else
  			return -ERESTARTSYS;
  	}
  
  	/* if non-blocking mode is set we can pass directly to open unless
  	   the port has just hung up or is in another error state */
8627b96dd   Alan Cox   tty_port: handle ...
365
366
367
368
369
  	if (tty->flags & (1 << TTY_IO_ERROR)) {
  		port->flags |= ASYNC_NORMAL_ACTIVE;
  		return 0;
  	}
  	if (filp->f_flags & O_NONBLOCK) {
4175f3e31   Alan Cox   tty_port: If we a...
370
  		/* Indicate we are open */
adc8d746c   Alan Cox   tty: move the ter...
371
  		if (tty->termios.c_cflag & CBAUD)
4175f3e31   Alan Cox   tty_port: If we a...
372
  			tty_port_raise_dtr_rts(port);
36c621d82   Alan Cox   tty: Introduce a ...
373
374
375
376
377
378
379
380
381
382
383
384
  		port->flags |= ASYNC_NORMAL_ACTIVE;
  		return 0;
  	}
  
  	if (C_CLOCAL(tty))
  		do_clocal = 1;
  
  	/* Block waiting until we can proceed. We may need to wait for the
  	   carrier, but we must also wait for any close that is in progress
  	   before the next open may complete */
  
  	retval = 0;
36c621d82   Alan Cox   tty: Introduce a ...
385
386
387
388
389
390
391
392
393
394
  
  	/* The port lock protects the port counts */
  	spin_lock_irqsave(&port->lock, flags);
  	if (!tty_hung_up_p(filp))
  		port->count--;
  	port->blocked_open++;
  	spin_unlock_irqrestore(&port->lock, flags);
  
  	while (1) {
  		/* Indicate we are open */
e584a02cf   Johan Hovold   TTY: fix DTR bein...
395
  		if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags))
7834909f1   Alan Cox   tty: tty port zer...
396
  			tty_port_raise_dtr_rts(port);
36c621d82   Alan Cox   tty: Introduce a ...
397

3e3b5c087   Jiri Slaby   tty: use prepare/...
398
  		prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
d774a56d2   Alan Cox   tty_port: coding ...
399
400
  		/* Check for a hangup or uninitialised port.
  							Return accordingly */
36c621d82   Alan Cox   tty: Introduce a ...
401
402
403
404
405
406
407
  		if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
  			if (port->flags & ASYNC_HUP_NOTIFY)
  				retval = -EAGAIN;
  			else
  				retval = -ERESTARTSYS;
  			break;
  		}
0eee50af5   Jiri Slaby   TTY: fix UV seria...
408
409
410
411
412
413
  		/*
  		 * Probe the carrier. For devices with no carrier detect
  		 * tty_port_carrier_raised will always return true.
  		 * Never ask drivers if CLOCAL is set, this causes troubles
  		 * on some hardware.
  		 */
36c621d82   Alan Cox   tty: Introduce a ...
414
  		if (!(port->flags & ASYNC_CLOSING) &&
0eee50af5   Jiri Slaby   TTY: fix UV seria...
415
  				(do_clocal || tty_port_carrier_raised(port)))
36c621d82   Alan Cox   tty: Introduce a ...
416
417
418
419
420
  			break;
  		if (signal_pending(current)) {
  			retval = -ERESTARTSYS;
  			break;
  		}
89c8d91e3   Alan Cox   tty: localise the...
421
  		tty_unlock(tty);
36c621d82   Alan Cox   tty: Introduce a ...
422
  		schedule();
89c8d91e3   Alan Cox   tty: localise the...
423
  		tty_lock(tty);
36c621d82   Alan Cox   tty: Introduce a ...
424
  	}
3e3b5c087   Jiri Slaby   tty: use prepare/...
425
  	finish_wait(&port->open_wait, &wait);
36c621d82   Alan Cox   tty: Introduce a ...
426
427
428
429
430
431
432
433
434
435
  
  	/* Update counts. A parallel hangup will have set count to zero and
  	   we must not mess that up further */
  	spin_lock_irqsave(&port->lock, flags);
  	if (!tty_hung_up_p(filp))
  		port->count++;
  	port->blocked_open--;
  	if (retval == 0)
  		port->flags |= ASYNC_NORMAL_ACTIVE;
  	spin_unlock_irqrestore(&port->lock, flags);
ecc2e05e7   Alan Cox   tty_port: Fix ret...
436
  	return retval;
36c621d82   Alan Cox   tty: Introduce a ...
437
438
  }
  EXPORT_SYMBOL(tty_port_block_til_ready);
b74414f5f   Johan Hovold   TTY: clean up por...
439
440
441
442
443
444
445
446
447
448
449
450
451
  static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty)
  {
  	unsigned int bps = tty_get_baud_rate(tty);
  	long timeout;
  
  	if (bps > 1200) {
  		timeout = (HZ * 10 * port->drain_delay) / bps;
  		timeout = max_t(long, timeout, HZ / 10);
  	} else {
  		timeout = 2 * HZ;
  	}
  	schedule_timeout_interruptible(timeout);
  }
d774a56d2   Alan Cox   tty_port: coding ...
452
453
  int tty_port_close_start(struct tty_port *port,
  				struct tty_struct *tty, struct file *filp)
a6614999e   Alan Cox   tty: Introduce so...
454
455
456
457
458
459
460
461
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&port->lock, flags);
  	if (tty_hung_up_p(filp)) {
  		spin_unlock_irqrestore(&port->lock, flags);
  		return 0;
  	}
d774a56d2   Alan Cox   tty_port: coding ...
462
  	if (tty->count == 1 && port->count != 1) {
a6614999e   Alan Cox   tty: Introduce so...
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
  		printk(KERN_WARNING
  		    "tty_port_close_start: tty->count = 1 port count = %d.
  ",
  								port->count);
  		port->count = 1;
  	}
  	if (--port->count < 0) {
  		printk(KERN_WARNING "tty_port_close_start: count = %d
  ",
  								port->count);
  		port->count = 0;
  	}
  
  	if (port->count) {
  		spin_unlock_irqrestore(&port->lock, flags);
  		return 0;
  	}
1f5c13fad   Alan Stern   TTY: fix typos
480
  	set_bit(ASYNCB_CLOSING, &port->flags);
a6614999e   Alan Cox   tty: Introduce so...
481
482
  	tty->closing = 1;
  	spin_unlock_irqrestore(&port->lock, flags);
0b2588cad   Johan Hovold   TTY: fix close of...
483
484
485
486
487
488
489
490
491
492
  
  	if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
  		/* Don't block on a stalled port, just pull the chain */
  		if (tty->flow_stopped)
  			tty_driver_flush_buffer(tty);
  		if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
  			tty_wait_until_sent_from_close(tty, port->closing_wait);
  		if (port->drain_delay)
  			tty_port_drain_delay(port, tty);
  	}
e707c35cb   Alan Cox   tty_port: Move hu...
493
494
  	/* Flush the ldisc buffering */
  	tty_ldisc_flush(tty);
469d6d063   Peter Hurley   tty: Remove unuse...
495
  	/* Report to caller this is the last port reference */
a6614999e   Alan Cox   tty: Introduce so...
496
497
498
499
500
501
502
  	return 1;
  }
  EXPORT_SYMBOL(tty_port_close_start);
  
  void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
  {
  	unsigned long flags;
a6614999e   Alan Cox   tty: Introduce so...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
  	spin_lock_irqsave(&port->lock, flags);
  	tty->closing = 0;
  
  	if (port->blocked_open) {
  		spin_unlock_irqrestore(&port->lock, flags);
  		if (port->close_delay) {
  			msleep_interruptible(
  				jiffies_to_msecs(port->close_delay));
  		}
  		spin_lock_irqsave(&port->lock, flags);
  		wake_up_interruptible(&port->open_wait);
  	}
  	port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
  	wake_up_interruptible(&port->close_wait);
  	spin_unlock_irqrestore(&port->lock, flags);
  }
  EXPORT_SYMBOL(tty_port_close_end);
7ca0ff9ab   Alan Cox   tty: Add a full p...
520
521
522
523
524
525
  
  void tty_port_close(struct tty_port *port, struct tty_struct *tty,
  							struct file *filp)
  {
  	if (tty_port_close_start(port, tty, filp) == 0)
  		return;
957dacaee   Johan Hovold   TTY: fix DTR not ...
526
  	tty_port_shutdown(port, tty);
d74e82868   Alan Cox   tty: tty_port: Ad...
527
  	set_bit(TTY_IO_ERROR, &tty->flags);
7ca0ff9ab   Alan Cox   tty: Add a full p...
528
529
530
531
  	tty_port_close_end(port, tty);
  	tty_port_tty_set(port, NULL);
  }
  EXPORT_SYMBOL(tty_port_close);
64bc39791   Alan Cox   tty_port: add "tt...
532

72a33bf58   Jiri Slaby   TTY: tty_port, ad...
533
534
535
536
537
538
539
540
541
542
  /**
   * tty_port_install - generic tty->ops->install handler
   * @port: tty_port of the device
   * @driver: tty_driver for this device
   * @tty: tty to be installed
   *
   * It is the same as tty_standard_install except the provided @port is linked
   * to a concrete tty specified by @tty. Use this or tty_port_register_device
   * (or both). Call tty_port_link_device as a last resort.
   */
695586ca2   Jiri Slaby   TTY: provide driv...
543
544
545
546
547
548
549
  int tty_port_install(struct tty_port *port, struct tty_driver *driver,
  		struct tty_struct *tty)
  {
  	tty->port = port;
  	return tty_standard_install(driver, tty);
  }
  EXPORT_SYMBOL_GPL(tty_port_install);
64bc39791   Alan Cox   tty_port: add "tt...
550
  int tty_port_open(struct tty_port *port, struct tty_struct *tty,
d774a56d2   Alan Cox   tty_port: coding ...
551
  							struct file *filp)
64bc39791   Alan Cox   tty_port: add "tt...
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  {
  	spin_lock_irq(&port->lock);
  	if (!tty_hung_up_p(filp))
  		++port->count;
  	spin_unlock_irq(&port->lock);
  	tty_port_tty_set(port, tty);
  
  	/*
  	 * Do the device-specific open only if the hardware isn't
  	 * already initialized. Serialize open and shutdown using the
  	 * port mutex.
  	 */
  
  	mutex_lock(&port->mutex);
  
  	if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
a9a37ec33   Alan Cox   tty: tty_port: Mo...
568
  		clear_bit(TTY_IO_ERROR, &tty->flags);
64bc39791   Alan Cox   tty_port: add "tt...
569
570
571
  		if (port->ops->activate) {
  			int retval = port->ops->activate(port, tty);
  			if (retval) {
d774a56d2   Alan Cox   tty_port: coding ...
572
573
574
575
  				mutex_unlock(&port->mutex);
  				return retval;
  			}
  		}
64bc39791   Alan Cox   tty_port: add "tt...
576
577
578
579
580
581
582
  		set_bit(ASYNCB_INITIALIZED, &port->flags);
  	}
  	mutex_unlock(&port->mutex);
  	return tty_port_block_til_ready(port, tty, filp);
  }
  
  EXPORT_SYMBOL(tty_port_open);