u_serial.c revision c1dca562be8ada614ef193aa246c6f8705bcd6b9
1c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
2c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * u_serial.c - utilities for USB gadget "serial port"/TTY support
3c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
4c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
5c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copyright (C) 2008 David Brownell
6c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copyright (C) 2008 by Nokia Corporation
7c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
8c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This code also borrows from usbserial.c, which is
9c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
10c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
11c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
12c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
13c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This software is distributed under the terms of the GNU General
14c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Public License ("GPL") as published by the Free Software Foundation,
15c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * either version 2 of that License or (at your option) any later version.
16c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
17c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
18c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* #define VERBOSE_DEBUG */
19c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
20c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/kernel.h>
21c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/interrupt.h>
22c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/device.h>
23c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/delay.h>
24c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/tty.h>
25c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/tty_flip.h>
26c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
27c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include "u_serial.h"
28c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
29c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
30c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
31c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This component encapsulates the TTY layer glue needed to provide basic
32c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * "serial port" functionality through the USB gadget stack.  Each such
33c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * port is exposed through a /dev/ttyGS* node.
34c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
35c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * After initialization (gserial_setup), these TTY port devices stay
36c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * available until they are removed (gserial_cleanup).  Each one may be
37c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * connected to a USB function (gserial_connect), or disconnected (with
38c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_disconnect) when the USB host issues a config change event.
39c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Data can only flow when the port is connected to the host.
40c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
41c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * A given TTY port can be made available in multiple configurations.
42c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * For example, each one might expose a ttyGS0 node which provides a
43c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * login application.  In one case that might use CDC ACM interface 0,
44c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * while another configuration might use interface 3 for that.  The
45c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * work to handle that (including descriptor management) is not part
46c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * of this component.
47c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
48c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Configurations may expose more than one TTY port.  For example, if
49c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * ttyGS0 provides login service, then ttyGS1 might provide dialer access
50c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * for a telephone or fax link.  And ttyGS2 might be something that just
51c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * needs a simple byte stream interface for some messaging protocol that
52c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
53c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
54c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
55c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
56c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial is the lifecycle interface, used by USB functions
57c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_port is the I/O nexus, used by the tty driver
58c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * tty_struct links to the tty/filesystem framework
59c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
60c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial <---> gs_port ... links will be null when the USB link is
61c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * inactive; managed by gserial_{connect,disconnect}().
62c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	gserial->ioport == usb_ep->driver_data ... gs_port
63c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	gs_port->port_usb ... gserial
64c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
65c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_port <---> tty_struct ... links will be null when the TTY file
66c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * isn't opened; managed by gs_open()/gs_close()
67c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	gserial->port_tty ... tty_struct
68c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	tty_struct->driver_data ... gserial
69c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
70c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
71c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
72c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * next layer of buffering.  For TX that's a circular buffer; for RX
73c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * consider it a NOP.  A third layer is provided by the TTY code.
74c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
75c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define QUEUE_SIZE		16
76c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define WRITE_BUF_SIZE		8192		/* TX only */
77c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
78c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* circular buffer */
79c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstruct gs_buf {
80c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		buf_size;
81c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	char			*buf_buf;
82c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	char			*buf_get;
83c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	char			*buf_put;
84c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell};
85c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
86c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
87c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * The port structure holds info for each port, one for each minor number
88c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * (and thus for each /dev/ node).
89c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
90c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstruct gs_port {
91c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spinlock_t		port_lock;	/* guard port_* access */
92c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
93c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gserial		*port_usb;
94c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct tty_struct	*port_tty;
95c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
96c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		open_count;
97c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	bool			openclose;	/* open/close in progress */
98c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	u8			port_num;
99c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
100c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	wait_queue_head_t	close_wait;	/* wait for last close */
101c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
102c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	read_pool;
103c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct tasklet_struct	push;
104c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
105c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	write_pool;
106c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_buf		port_write_buf;
107c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	wait_queue_head_t	drain_wait;	/* wait while writes drain */
108c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
109c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT this state ... */
110c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_cdc_line_coding port_line_coding;	/* 8-N-1 etc */
111c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell};
112c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
113c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* increase N_PORTS if you need more */
114c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define N_PORTS		4
115c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic struct portmaster {
116c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct mutex	lock;			/* protect open/close */
117c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
118c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell} ports[N_PORTS];
119c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned	n_ports;
120c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
121c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define GS_CLOSE_TIMEOUT		15		/* seconds */
122c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
123c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
124c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
125c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#ifdef VERBOSE_DEBUG
126c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define pr_vdebug(fmt, arg...) \
127c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug(fmt, ##arg)
128c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#else
129c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define pr_vdebug(fmt, arg...) \
130c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	({ if (0) pr_debug(fmt, ##arg); })
131c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#endif
132c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
133c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
134c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
135c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* Circular Buffer */
136c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
137c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
138c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_alloc
139c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
140c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Allocate a circular buffer and all associated memory.
141c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
142c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_buf_alloc(struct gs_buf *gb, unsigned size)
143c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
144c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_buf = kmalloc(size, GFP_KERNEL);
145c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (gb->buf_buf == NULL)
146c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENOMEM;
147c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
148c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_size = size;
149c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_put = gb->buf_buf;
150c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_get = gb->buf_buf;
151c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
152c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
153c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
154c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
155c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
156c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_free
157c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
158c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Free the buffer and all associated memory.
159c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
160c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_buf_free(struct gs_buf *gb)
161c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
162c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	kfree(gb->buf_buf);
163c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_buf = NULL;
164c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
165c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
166c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
167c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_clear
168c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
169c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Clear out all data in the circular buffer.
170c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
171c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_buf_clear(struct gs_buf *gb)
172c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
173c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_get = gb->buf_put;
174c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* equivalent to a get of all data available */
175c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
176c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
177c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
178c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_data_avail
179c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
180c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes of data available in the circular
181c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * buffer.
182c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
183c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned gs_buf_data_avail(struct gs_buf *gb)
184c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
185c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
186c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
187c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
188c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
189c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_space_avail
190c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
191c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes of space available in the circular
192c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * buffer.
193c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
194c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned gs_buf_space_avail(struct gs_buf *gb)
195c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
196c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
197c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
198c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
199c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
200c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_put
201c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
202c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copy data data from a user buffer and put it into the circular buffer.
203c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Restrict to the amount of space available.
204c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
205c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes copied.
206c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
207c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned
208c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
209c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
210c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned len;
211c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
212c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len  = gs_buf_space_avail(gb);
213c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len)
214c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		count = len;
215c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
216c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count == 0)
217c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return 0;
218c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
219c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gb->buf_buf + gb->buf_size - gb->buf_put;
220c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len) {
221c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(gb->buf_put, buf, len);
222c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(gb->buf_buf, buf+len, count - len);
223c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gb->buf_put = gb->buf_buf + count - len;
224c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} else {
225c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(gb->buf_put, buf, count);
226c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (count < len)
227c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_put += count;
228c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else /* count == len */
229c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_put = gb->buf_buf;
230c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
231c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
232c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return count;
233c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
234c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
235c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
236c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_get
237c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
238c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Get data from the circular buffer and copy to the given buffer.
239c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Restrict to the amount of data available.
240c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
241c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes copied.
242c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
243c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned
244c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
245c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
246c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned len;
247c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
248c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gs_buf_data_avail(gb);
249c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len)
250c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		count = len;
251c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
252c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count == 0)
253c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return 0;
254c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
255c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gb->buf_buf + gb->buf_size - gb->buf_get;
256c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len) {
257c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(buf, gb->buf_get, len);
258c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(buf+len, gb->buf_buf, count - len);
259c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gb->buf_get = gb->buf_buf + count - len;
260c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} else {
261c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(buf, gb->buf_get, count);
262c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (count < len)
263c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_get += count;
264c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else /* count == len */
265c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_get = gb->buf_buf;
266c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
267c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
268c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return count;
269c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
270c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
271c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
272c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
273c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* I/O glue between TTY (upper) and USB function (lower) driver layers */
274c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
275c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
276c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_alloc_req
277c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
278c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Allocate a usb_request and its buffer.  Returns a pointer to the
279c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * usb_request or NULL if there is an error.
280c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
281c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic struct usb_request *
282c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
283c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
284c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_request *req;
285c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
286c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	req = usb_ep_alloc_request(ep, kmalloc_flags);
287c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
288c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (req != NULL) {
289c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->length = len;
290c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->buf = kmalloc(len, kmalloc_flags);
291c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (req->buf == NULL) {
292c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			usb_ep_free_request(ep, req);
293c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			return NULL;
294c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
295c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
296c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
297c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return req;
298c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
299c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
300c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
301c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_free_req
302c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
303c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Free a usb_request and its buffer.
304c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
305c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_free_req(struct usb_ep *ep, struct usb_request *req)
306c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
307c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	kfree(req->buf);
308c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_free_request(ep, req);
309c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
310c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
311c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
312c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_send_packet
313c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
314c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * If there is data to send, a packet is built in the given
315c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * buffer and the size is returned.  If there is no data to
316c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * send, 0 is returned.
317c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
318c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Called with port_lock held.
319c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
320c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned
321c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_send_packet(struct gs_port *port, char *packet, unsigned size)
322c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
323c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned len;
324c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
325c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gs_buf_data_avail(&port->port_write_buf);
326c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (len < size)
327c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		size = len;
328c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (size != 0)
329c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		size = gs_buf_get(&port->port_write_buf, packet, size);
330c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return size;
331c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
332c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
333c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
334c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_start_tx
335c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
336c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This function finds available write requests, calls
337c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_send_packet to fill these packets with data, and
338c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * continues until either there are no more write requests
339c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * available or no more data to send.  This function is
340c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * run whenever data arrives or write requests are available.
341c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
342c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: caller owns port_lock; port_usb is non-null.
343c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
344c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_start_tx(struct gs_port *port)
345c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
346c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__releases(&port->port_lock)
347c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__acquires(&port->port_lock)
348c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell*/
349c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
350c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	*pool = &port->write_pool;
351c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_ep		*in = port->port_usb->in;
352c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int			status = 0;
353c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	bool			do_tty_wake = false;
354c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
355c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (!list_empty(pool)) {
356c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct usb_request	*req;
357c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		int			len;
358c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
359c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = list_entry(pool->next, struct usb_request, list);
360c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		len = gs_send_packet(port, req->buf, in->maxpacket);
361c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (len == 0) {
362c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			wake_up_interruptible(&port->drain_wait);
363c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
364c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
365c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		do_tty_wake = true;
366c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
367c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->length = len;
368c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_del(&req->list);
369c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
370c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#ifdef VERBOSE_DEBUG
371c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_debug("%s: %s, len=%d, 0x%02x 0x%02x 0x%02x ...\n",
372c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, in->name, len, *((u8 *)req->buf),
373c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				*((u8 *)req->buf+1), *((u8 *)req->buf+2));
374c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#endif
375c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
376c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* Drop lock while we call out of driver; completions
377c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * could be issued while we do so.  Disconnection may
378c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * happen too; maybe immediately before we queue this!
379c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 *
380c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * NOTE that we may keep sending data for a while after
381c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * the TTY closed (dev->ioport->port_tty is NULL).
382c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 */
383c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock(&port->port_lock);
384c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = usb_ep_queue(in, req, GFP_ATOMIC);
385c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock(&port->port_lock);
386c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
387c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
388c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("%s: %s %s err %d\n",
389c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					__func__, "queue", in->name, status);
390c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			list_add(&req->list, pool);
391c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
392c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
393c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
394c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* abort immediately after disconnect */
395c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!port->port_usb)
396c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
397c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
398c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
399c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (do_tty_wake && port->port_tty)
400c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_wakeup(port->port_tty);
401c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
402c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
403c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
404c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_rx_push(unsigned long _port)
405c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
406c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port		*port = (void *)_port;
407c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct tty_struct	*tty = port->port_tty;
408c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
409c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* With low_latency, tty_flip_buffer_push() doesn't put its
410c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * real work through a workqueue, so the ldisc has a better
411c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * chance to keep up with peak USB data rates.
412c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
413c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (tty) {
414c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_flip_buffer_push(tty);
415c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wake_up_interruptible(&tty->read_wait);
416c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
417c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
418c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
419c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
420c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_recv_packet
421c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
422c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Called for each USB packet received.  Reads the packet
423c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * header and stuffs the data in the appropriate tty buffer.
424c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Returns 0 if successful, or a negative error number.
425c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
426c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Called during USB completion routine, on interrupt time.
427c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * With port_lock.
428c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
429c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_recv_packet(struct gs_port *port, char *packet, unsigned size)
430c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
431c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		len;
432c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct tty_struct	*tty;
433c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
434c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* I/O completions can continue for a while after close(), until the
435c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * request queue empties.  Just discard any data we receive, until
436c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * something reopens this TTY ... as if there were no HW flow control.
437c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
438c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty = port->port_tty;
439c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (tty == NULL) {
440c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_vdebug("%s: ttyGS%d, after close\n",
441c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, port->port_num);
442c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -EIO;
443c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
444c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
445c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = tty_insert_flip_string(tty, packet, size);
446c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (len > 0)
447c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tasklet_schedule(&port->push);
448c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (len < size)
449c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_debug("%s: ttyGS%d, drop %d bytes\n",
450c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, port->port_num, size - len);
451c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
452c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
453c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
454c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
455c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: caller owns port_lock, and port_usb is set
456c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
457c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned gs_start_rx(struct gs_port *port)
458c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
459c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__releases(&port->port_lock)
460c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__acquires(&port->port_lock)
461c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell*/
462c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
463c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	*pool = &port->read_pool;
464c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_ep		*out = port->port_usb->out;
465c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		started = 0;
466c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
467c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (!list_empty(pool)) {
468c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct usb_request	*req;
469c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		int			status;
470c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct tty_struct	*tty;
471c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
472c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* no more rx if closed or throttled */
473c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty = port->port_tty;
474c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!tty || test_bit(TTY_THROTTLED, &tty->flags))
475c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
476c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
477c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = list_entry(pool->next, struct usb_request, list);
478c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_del(&req->list);
479c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->length = out->maxpacket;
480c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
481c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* drop lock while we call out; the controller driver
482c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * may need to call us back (e.g. for disconnect)
483c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 */
484c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock(&port->port_lock);
485c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = usb_ep_queue(out, req, GFP_ATOMIC);
486c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock(&port->port_lock);
487c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
488c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
489c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("%s: %s %s err %d\n",
490c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					__func__, "queue", out->name, status);
491c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			list_add(&req->list, pool);
492c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
493c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
494c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		started++;
495c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
496c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* abort immediately after disconnect */
497c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!port->port_usb)
498c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
499c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
500c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return started;
501c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
502c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
503c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
504c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
505c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
506c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = ep->driver_data;
507c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
508c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock(&port->port_lock);
509c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	list_add(&req->list, &port->read_pool);
510c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
511c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	switch (req->status) {
512c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	case 0:
513c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* normal completion */
514c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_recv_packet(port, req->buf, req->actual);
515c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status && status != -EIO)
516c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("%s: %s %s err %d\n",
517c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, "recv", ep->name, status);
518c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_rx(port);
519c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
520c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
521c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	case -ESHUTDOWN:
522c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* disconnect */
523c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
524c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
525c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
526c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	default:
527c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* presumably a transient fault */
528c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_warning("%s: unexpected %s status %d\n",
529c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, ep->name, req->status);
530c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_rx(port);
531c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
532c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
533c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock(&port->port_lock);
534c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
535c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
536c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
537c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
538c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = ep->driver_data;
539c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
540c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock(&port->port_lock);
541c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	list_add(&req->list, &port->write_pool);
542c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
543c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	switch (req->status) {
544c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	default:
545c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* presumably a transient fault */
546c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_warning("%s: unexpected %s status %d\n",
547c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, ep->name, req->status);
548c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* FALL THROUGH */
549c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	case 0:
550c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* normal completion */
551c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_tx(port);
552c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
553c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
554c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	case -ESHUTDOWN:
555c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* disconnect */
556c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
557c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
558c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
559c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
560c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock(&port->port_lock);
561c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
562c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
563c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_free_requests(struct usb_ep *ep, struct list_head *head)
564c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
565c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_request	*req;
566c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
567c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (!list_empty(head)) {
568c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = list_entry(head->next, struct usb_request, list);
569c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_del(&req->list);
570c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_free_req(ep, req);
571c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
572c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
573c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
574c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
575c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		void (*fn)(struct usb_ep *, struct usb_request *))
576c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
577c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int			i;
578c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_request	*req;
579c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
580c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
581c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * do quite that many this time, don't fail ... we just won't
582c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * be as speedy as we might otherwise be.
583c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
584c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < QUEUE_SIZE; i++) {
585c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
586c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!req)
587c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			return list_empty(head) ? -ENOMEM : 0;
588c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->complete = fn;
589c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_add_tail(&req->list, head);
590c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
591c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
592c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
593c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
594c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
595c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_start_io - start USB I/O streams
596c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @dev: encapsulates endpoints to use
597c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: holding port_lock; port_tty and port_usb are non-null
598c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
599c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * We only start I/O when something is connected to both sides of
600c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * this port.  If nothing is listening on the host side, we may
601c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * be pointlessly filling up our TX buffers and FIFO.
602c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
603c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_start_io(struct gs_port *port)
604c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
605c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	*head = &port->read_pool;
606c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_ep		*ep = port->port_usb->out;
607c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int			status;
608c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		started;
609c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
610c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Allocate RX and TX I/O buffers.  We can't easily do this much
611c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * earlier (with GFP_KERNEL) because the requests are coupled to
612c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * endpoints, as are the packet sizes we'll be using.  Different
613c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * configurations may use different endpoints with a given port;
614c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * and high speed vs full speed changes packet sizes too.
615c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
616c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = gs_alloc_requests(ep, head, gs_read_complete);
617c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status)
618c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return status;
619c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
620c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
621c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gs_write_complete);
622c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status) {
623c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_free_requests(ep, head);
624c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return status;
625c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
626c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
627c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* queue read requests */
628c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	started = gs_start_rx(port);
629c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
630c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* unblock any pending writes into our circular buffer */
631c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (started) {
632c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_wakeup(port->port_tty);
633c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} else {
634c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_free_requests(ep, head);
635c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_free_requests(port->port_usb->in, &port->write_pool);
636c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
637c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
638c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return started ? 0 : status;
639c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
640c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
641c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
642c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
643c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* TTY Driver */
644c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
645c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
646c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_open sets up the link between a gs_port and its associated TTY.
647c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * That link is broken *only* by TTY close(), and all driver methods
648c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * know that.
649c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
650c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_open(struct tty_struct *tty, struct file *file)
651c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
652c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		port_num = tty->index;
653c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
654c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
655c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
656c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port_num < 0 || port_num >= n_ports)
657c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENXIO;
658c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
659c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	do {
660c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_lock(&ports[port_num].lock);
661c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port = ports[port_num].port;
662c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!port)
663c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			status = -ENODEV;
664c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else {
665c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			spin_lock_irq(&port->port_lock);
666c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
667c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* already open?  Great. */
668c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			if (port->open_count) {
669c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				status = 0;
670c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				port->open_count++;
671c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
672c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* currently opening/closing? wait ... */
673c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			} else if (port->openclose) {
674c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				status = -EBUSY;
675c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
676c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* ... else we do the work */
677c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			} else {
678c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				status = -EAGAIN;
679c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				port->openclose = true;
680c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			}
681c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			spin_unlock_irq(&port->port_lock);
682c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
683c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_unlock(&ports[port_num].lock);
684c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
685c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		switch (status) {
686c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		default:
687c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* fully handled */
688c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			return status;
689c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		case -EAGAIN:
690c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* must do the work */
691c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
692c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		case -EBUSY:
693c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* wait for EAGAIN task to finish */
694c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			msleep(1);
695c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* REVISIT could have a waitchannel here, if
696c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			 * concurrent open performance is important
697c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			 */
698c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
699c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
700c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} while (status != -EAGAIN);
701c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
702c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Do the "real open" */
703c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&port->port_lock);
704c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
705c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* allocate circular buffer on first open */
706c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_write_buf.buf_buf == NULL) {
707c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
708c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock_irq(&port->port_lock);
709c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
710c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock_irq(&port->port_lock);
711c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
712c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
713c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
714c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				port->port_num, tty, file);
715c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->openclose = false;
716c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			goto exit_unlock_port;
717c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
718c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
719c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
720c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT if REMOVED (ports[].port NULL), abort the open
721c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * to let rmmod work faster (but this way isn't wrong).
722c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
723c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
724c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT maybe wait for "carrier detect" */
725c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
726c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty->driver_data = port;
727c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_tty = tty;
728c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
729c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->open_count = 1;
730c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->openclose = false;
731c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
732c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* low_latency means ldiscs work in tasklet context, without
733c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * needing a workqueue schedule ... easier to keep up.
734c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
735c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty->low_latency = 1;
736c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
737c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* if connected, start the I/O stream */
738c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb) {
739c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_debug("gs_open: start ttyGS%d\n", port->port_num);
740c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_io(port);
741c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
742c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* REVISIT for ACM, issue "network connected" event */
743c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
744c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
745c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
746c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
747c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = 0;
748c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
749c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellexit_unlock_port:
750c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&port->port_lock);
751c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
752c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
753c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
754c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_writes_finished(struct gs_port *p)
755c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
756c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int cond;
757c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
758c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* return true on disconnect or empty buffer */
759c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&p->port_lock);
760c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
761c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&p->port_lock);
762c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
763c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return cond;
764c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
765c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
766c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_close(struct tty_struct *tty, struct file *file)
767c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
768c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port *port = tty->driver_data;
769c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
770c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&port->port_lock);
771c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
772c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count != 1) {
773c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (port->open_count == 0)
774c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			WARN_ON(1);
775c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else
776c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			--port->open_count;
777c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		goto exit;
778c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
779c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
780c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
781c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
782c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* mark port as closing but in use; we can drop port lock
783c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * and sleep if necessary
784c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
785c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->openclose = true;
786c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->open_count = 0;
787c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
788c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
789c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* REVISIT for ACM, issue "network disconnected" event */;
790c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
791c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* wait for circular write buffer to drain, disconnect, or at
792c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * most GS_CLOSE_TIMEOUT seconds; then discard the rest
793c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
794c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (gs_buf_data_avail(&port->port_write_buf) > 0
795c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			&& port->port_usb) {
796c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock_irq(&port->port_lock);
797c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wait_event_interruptible_timeout(port->drain_wait,
798c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					gs_writes_finished(port),
799c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					GS_CLOSE_TIMEOUT * HZ);
800c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock_irq(&port->port_lock);
801c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
802c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
803c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Iff we're disconnected, there can be no I/O in flight so it's
804c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * ok to free the circular buffer; else just scrub it.  And don't
805c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * let the push tasklet fire again until we're re-opened.
806c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
807c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb == NULL)
808c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_buf_free(&port->port_write_buf);
809c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	else
810c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_buf_clear(&port->port_write_buf);
811c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
812c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tasklet_kill(&port->push);
813c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
814c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty->driver_data = NULL;
815c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_tty = NULL;
816c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
817c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->openclose = false;
818c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
819c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
820c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->port_num, tty, file);
821c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
822c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	wake_up_interruptible(&port->close_wait);
823c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellexit:
824c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&port->port_lock);
825c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
826c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
827c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
828c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
829c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
830c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
831c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
832c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
833c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
834c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->port_num, tty, count);
835c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
836c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
837c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count)
838c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		count = gs_buf_put(&port->port_write_buf, buf, count);
839c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* treat count == 0 as flush_chars() */
840c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
841c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_start_tx(port);
842c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
843c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
844c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return count;
845c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
846c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
847c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_put_char(struct tty_struct *tty, unsigned char ch)
848c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
849c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
850c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
851c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
852c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
853c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
854c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port->port_num, tty, ch, __builtin_return_address(0));
855c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
856c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
857c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = gs_buf_put(&port->port_write_buf, &ch, 1);
858c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
859c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
860c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
861c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
862c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
863c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_flush_chars(struct tty_struct *tty)
864c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
865c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
866c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
867c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
868c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
869c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
870c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
871c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
872c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_tx(port);
873c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
874c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
875c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
876c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_write_room(struct tty_struct *tty)
877c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
878c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
879c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
880c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		room = 0;
881c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
882c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
883c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
884c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		room = gs_buf_space_avail(&port->port_write_buf);
885c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
886c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
887c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
888c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port->port_num, tty, room);
889c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
890c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return room;
891c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
892c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
893c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_chars_in_buffer(struct tty_struct *tty)
894c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
895c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
896c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
897c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		chars = 0;
898c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
899c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
900c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	chars = gs_buf_data_avail(&port->port_write_buf);
901c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
902c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
903c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
904c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port->port_num, tty, chars);
905c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
906c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return chars;
907c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
908c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
909c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* undo side effects of setting TTY_THROTTLED */
910c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_unthrottle(struct tty_struct *tty)
911c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
912c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port		*port = tty->driver_data;
913c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long		flags;
914c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		started = 0;
915c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
916c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
917c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
918c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		started = gs_start_rx(port);
919c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
920c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
921c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_unthrottle: ttyGS%d, %d packets\n",
922c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->port_num, started);
923c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
924c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
925c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic const struct tty_operations gs_tty_ops = {
926c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.open =			gs_open,
927c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.close =		gs_close,
928c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.write =		gs_write,
929c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.put_char =		gs_put_char,
930c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.flush_chars =		gs_flush_chars,
931c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.write_room =		gs_write_room,
932c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.chars_in_buffer =	gs_chars_in_buffer,
933c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.unthrottle =		gs_unthrottle,
934c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell};
935c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
936c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
937c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
938c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic struct tty_driver *gs_tty_driver;
939c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
940c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int __init
941c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
942c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
943c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
944c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
945c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
946c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port == NULL)
947c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENOMEM;
948c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
949c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_init(&port->port_lock);
950c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	init_waitqueue_head(&port->close_wait);
951c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	init_waitqueue_head(&port->drain_wait);
952c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
953c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
954c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
955c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	INIT_LIST_HEAD(&port->read_pool);
956c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	INIT_LIST_HEAD(&port->write_pool);
957c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
958c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_num = port_num;
959c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_line_coding = *coding;
960c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
961c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	ports[port_num].port = port;
962c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
963c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
964c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
965c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
966c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
967c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_setup - initialize TTY driver for one or more ports
968c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @g: gadget to associate with these ports
969c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @count: how many ports to support
970c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: may sleep
971c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
972c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * The TTY stack needs to know in advance how many devices it should
973c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * plan to manage.  Use this call to set up the ports you will be
974c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * exporting through USB.  Later, connect them to functions based
975c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * on what configuration is activated by the USB host; and disconnect
976c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * them as appropriate.
977c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
978c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * An example would be a two-configuration device in which both
979c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * configurations expose port 0, but through different functions.
980c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * One configuration could even expose port 1 while the other
981c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * one doesn't.
982c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
983c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Returns negative errno or zero.
984c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
985c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellint __init gserial_setup(struct usb_gadget *g, unsigned count)
986c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
987c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned			i;
988c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_cdc_line_coding	coding;
989c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int				status;
990c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
991c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count == 0 || count > N_PORTS)
992c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -EINVAL;
993c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
994c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver = alloc_tty_driver(count);
995c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (!gs_tty_driver)
996c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENOMEM;
997c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
998c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->owner = THIS_MODULE;
999c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->driver_name = "g_serial";
1000c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->name = "ttyGS";
1001c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* uses dynamically assigned dev_t values */
1002c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1003c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
1004c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
1005c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
1006c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios = tty_std_termios;
1007c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1008c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
1009c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * MS-Windows.  Otherwise, most of these flags shouldn't affect
1010c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * anything unless we were to actually hook up to a serial line.
1011c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
1012c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios.c_cflag =
1013c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1014c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios.c_ispeed = 9600;
1015c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios.c_ospeed = 9600;
1016c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1017c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.dwDTERate = __constant_cpu_to_le32(9600);
1018c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.bCharFormat = 8;
1019c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.bParityType = USB_CDC_NO_PARITY;
1020c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.bDataBits = USB_CDC_1_STOP_BITS;
1021c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1022c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty_set_operations(gs_tty_driver, &gs_tty_ops);
1023c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1024c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* make devices be openable */
1025c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < count; i++) {
1026c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_init(&ports[i].lock);
1027c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_port_alloc(i, &coding);
1028c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
1029c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			count = i;
1030c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			goto fail;
1031c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
1032c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1033c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	n_ports = count;
1034c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1035c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* export the driver ... */
1036c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = tty_register_driver(gs_tty_driver);
1037c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status) {
1038c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		put_tty_driver(gs_tty_driver);
1039c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_err("%s: cannot register, err %d\n",
1040c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, status);
1041c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		goto fail;
1042c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1043c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1044c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
1045c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < count; i++) {
1046c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct device	*tty_dev;
1047c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1048c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
1049c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (IS_ERR(tty_dev))
1050c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_warning("%s: no classdev for port %d, err %ld\n",
1051c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, i, PTR_ERR(tty_dev));
1052c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1053c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1054c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
1055c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			count, (count == 1) ? "" : "s");
1056c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1057c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1058c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellfail:
1059c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (count--)
1060c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		kfree(ports[count].port);
1061c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	put_tty_driver(gs_tty_driver);
1062c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver = NULL;
1063c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1064c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1065c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1066c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_closed(struct gs_port *port)
1067c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1068c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int cond;
1069c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1070c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&port->port_lock);
1071c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	cond = (port->open_count == 0) && !port->openclose;
1072c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&port->port_lock);
1073c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return cond;
1074c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1075c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1076c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1077c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_cleanup - remove TTY-over-USB driver and devices
1078c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: may sleep
1079c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1080c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This is called to free all resources allocated by @gserial_setup().
1081c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Accordingly, it may need to wait until some open /dev/ files have
1082c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * closed.
1083c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1084c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * The caller must have issued @gserial_disconnect() for any ports
1085c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * that had previously been connected, so that there is never any
1086c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * I/O pending when it's called.
1087c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1088c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellvoid gserial_cleanup(void)
1089c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1090c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned	i;
1091c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
1092c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1093c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* start sysfs and /dev/ttyGS* node removal */
1094c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < n_ports; i++)
1095c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_unregister_device(gs_tty_driver, i);
1096c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1097c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < n_ports; i++) {
1098c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* prevent new opens */
1099c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_lock(&ports[i].lock);
1100c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port = ports[i].port;
1101c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		ports[i].port = NULL;
1102c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_unlock(&ports[i].lock);
1103c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1104c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* wait for old opens to finish */
1105c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wait_event(port->close_wait, gs_closed(port));
1106c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1107c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		WARN_ON(port->port_usb != NULL);
1108c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1109c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		kfree(port);
1110c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1111c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	n_ports = 0;
1112c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1113c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty_unregister_driver(gs_tty_driver);
1114c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver = NULL;
1115c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1116c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("%s: cleaned up ttyGS* support\n", __func__);
1117c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1118c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1119c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1120c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_connect - notify TTY I/O glue that USB link is active
1121c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @gser: the function, set up with endpoints and descriptors
1122c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @port_num: which port is active
1123c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: any (usually from irq)
1124c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1125c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This is called activate endpoints and let the TTY layer know that
1126c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * the connection is active ... not unlike "carrier detect".  It won't
1127c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * necessarily start I/O queues; unless the TTY is held open by any
1128c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * task, there would be no point.  However, the endpoints will be
1129c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * activated so the USB host can perform I/O, subject to basic USB
1130c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * hardware flow control.
1131c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1132c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Caller needs to have set up the endpoints and USB function in @dev
1133c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * before calling this, as well as the appropriate (speed-specific)
1134c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * endpoint descriptors, and also have set up the TTY driver by calling
1135c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @gserial_setup().
1136c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1137c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Returns negative errno or zero.
1138c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * On success, ep->driver_data will be overwritten.
1139c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1140c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellint gserial_connect(struct gserial *gser, u8 port_num)
1141c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1142c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
1143c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
1144c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
1145c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1146c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (!gs_tty_driver || port_num >= n_ports)
1147c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENXIO;
1148c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1149c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* we "know" gserial_cleanup() hasn't been called */
1150c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port = ports[port_num].port;
1151c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1152c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* activate the endpoints */
1153c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = usb_ep_enable(gser->in, gser->in_desc);
1154c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status < 0)
1155c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return status;
1156c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->in->driver_data = port;
1157c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1158c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = usb_ep_enable(gser->out, gser->out_desc);
1159c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status < 0)
1160c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		goto fail_out;
1161c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->out->driver_data = port;
1162c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1163c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* then tell the tty glue that I/O can work */
1164c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
1165c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->ioport = port;
1166c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_usb = gser;
1167c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1168c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT unclear how best to handle this state...
1169c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * we don't really couple it with the Linux TTY.
1170c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
1171c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->port_line_coding = port->port_line_coding;
1172c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1173c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT if waiting on "carrier detect", signal. */
1174c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1175c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT for ACM, issue "network connection" status notification:
1176c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * connected if open_count, else disconnected.
1177c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
1178c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1179c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* if it's already open, start I/O */
1180c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count) {
1181c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
1182c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_io(port);
1183c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1184c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1185c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
1186c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1187c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1188c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1189c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellfail_out:
1190c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_disable(gser->in);
1191c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->in->driver_data = NULL;
1192c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1193c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1194c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1195c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1196c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_disconnect - notify TTY I/O glue that USB link is inactive
1197c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @gser: the function, on which gserial_connect() was called
1198c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: any (usually from irq)
1199c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1200c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This is called to deactivate endpoints and let the TTY layer know
1201c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * that the connection went inactive ... not unlike "hangup".
1202c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1203c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * On return, the state is as if gserial_connect() had never been called;
1204c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * there is no active USB I/O on these endpoints.
1205c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1206c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellvoid gserial_disconnect(struct gserial *gser)
1207c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1208c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = gser->ioport;
1209c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
1210c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1211c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (!port)
1212c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return;
1213c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1214c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* tell the TTY glue not to do I/O here any more */
1215c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
1216c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1217c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT as above: how best to track this? */
1218c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_line_coding = gser->port_line_coding;
1219c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1220c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_usb = NULL;
1221c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->ioport = NULL;
1222c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count > 0 || port->openclose) {
1223c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wake_up_interruptible(&port->drain_wait);
1224c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (port->port_tty)
1225c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			tty_hangup(port->port_tty);
1226c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1227c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
1228c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1229c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* disable endpoints, aborting down any active I/O */
1230c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_disable(gser->out);
1231c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->out->driver_data = NULL;
1232c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1233c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_disable(gser->in);
1234c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->in->driver_data = NULL;
1235c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1236c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* finally, free any unused/unusable I/O buffers */
1237c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
1238c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count == 0 && !port->openclose)
1239c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_buf_free(&port->port_write_buf);
1240c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_free_requests(gser->out, &port->read_pool);
1241c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_free_requests(gser->in, &port->write_pool);
1242c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
1243c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1244