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>
211e413943fabdc228e86e4fbaa11e77efa861c23fstephane duverger#include <linux/sched.h>
22c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/interrupt.h>
23c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/device.h>
24c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/delay.h>
25c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/tty.h>
26c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include <linux/tty_flip.h>
275a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
28f940fcd8eadfe5b909a1474b57de7755edeee62bPaul Gortmaker#include <linux/export.h>
29c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
30c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#include "u_serial.h"
31c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
32c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
33c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
34c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This component encapsulates the TTY layer glue needed to provide basic
35c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * "serial port" functionality through the USB gadget stack.  Each such
36c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * port is exposed through a /dev/ttyGS* node.
37c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
38c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * After initialization (gserial_setup), these TTY port devices stay
39c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * available until they are removed (gserial_cleanup).  Each one may be
40c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * connected to a USB function (gserial_connect), or disconnected (with
41c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_disconnect) when the USB host issues a config change event.
42c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Data can only flow when the port is connected to the host.
43c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
44c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * A given TTY port can be made available in multiple configurations.
45c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * For example, each one might expose a ttyGS0 node which provides a
46c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * login application.  In one case that might use CDC ACM interface 0,
47c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * while another configuration might use interface 3 for that.  The
48c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * work to handle that (including descriptor management) is not part
49c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * of this component.
50c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
51c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Configurations may expose more than one TTY port.  For example, if
52c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * ttyGS0 provides login service, then ttyGS1 might provide dialer access
53c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * for a telephone or fax link.  And ttyGS2 might be something that just
54c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * needs a simple byte stream interface for some messaging protocol that
55c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
56c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
57c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
58937ef73d5075997a8d1777abf217a48bef2ce029David Brownell#define PREFIX	"ttyGS"
59937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
60c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
61c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial is the lifecycle interface, used by USB functions
62c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_port is the I/O nexus, used by the tty driver
63c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * tty_struct links to the tty/filesystem framework
64c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
65c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial <---> gs_port ... links will be null when the USB link is
661f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell * inactive; managed by gserial_{connect,disconnect}().  each gserial
671f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell * instance can wrap its own USB control protocol.
68c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	gserial->ioport == usb_ep->driver_data ... gs_port
69c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	gs_port->port_usb ... gserial
70c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
71c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_port <---> tty_struct ... links will be null when the TTY file
72c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * isn't opened; managed by gs_open()/gs_close()
73c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	gserial->port_tty ... tty_struct
74c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *	tty_struct->driver_data ... gserial
75c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
76c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
77c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
78c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * next layer of buffering.  For TX that's a circular buffer; for RX
79c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * consider it a NOP.  A third layer is provided by the TTY code.
80c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
81c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define QUEUE_SIZE		16
82c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define WRITE_BUF_SIZE		8192		/* TX only */
83c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
84c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* circular buffer */
85c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstruct gs_buf {
86c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		buf_size;
87c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	char			*buf_buf;
88c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	char			*buf_get;
89c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	char			*buf_put;
90c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell};
91c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
92c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
93c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * The port structure holds info for each port, one for each minor number
94c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * (and thus for each /dev/ node).
95c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
96c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstruct gs_port {
97c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spinlock_t		port_lock;	/* guard port_* access */
98c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
99c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gserial		*port_usb;
100c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct tty_struct	*port_tty;
101c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
102c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		open_count;
103c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	bool			openclose;	/* open/close in progress */
104c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	u8			port_num;
105c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
106c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	wait_queue_head_t	close_wait;	/* wait for last close */
107c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
108c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	read_pool;
10928609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	int read_started;
11028609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	int read_allocated;
111937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	struct list_head	read_queue;
112937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	unsigned		n_read;
113c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct tasklet_struct	push;
114c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
115c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	write_pool;
11628609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	int write_started;
11728609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	int write_allocated;
118c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_buf		port_write_buf;
119c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	wait_queue_head_t	drain_wait;	/* wait while writes drain */
120c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
121c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT this state ... */
122c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_cdc_line_coding port_line_coding;	/* 8-N-1 etc */
123c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell};
124c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
125c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* increase N_PORTS if you need more */
126c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define N_PORTS		4
127c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic struct portmaster {
128c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct mutex	lock;			/* protect open/close */
129c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
130c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell} ports[N_PORTS];
131c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned	n_ports;
132c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
133c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define GS_CLOSE_TIMEOUT		15		/* seconds */
134c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
135c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
136c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
137c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#ifdef VERBOSE_DEBUG
138c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define pr_vdebug(fmt, arg...) \
139c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug(fmt, ##arg)
140c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#else
141c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#define pr_vdebug(fmt, arg...) \
142c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	({ if (0) pr_debug(fmt, ##arg); })
143c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell#endif
144c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
145c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
146c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
147c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* Circular Buffer */
148c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
149c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
150c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_alloc
151c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
152c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Allocate a circular buffer and all associated memory.
153c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
154c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_buf_alloc(struct gs_buf *gb, unsigned size)
155c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
156c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_buf = kmalloc(size, GFP_KERNEL);
157c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (gb->buf_buf == NULL)
158c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENOMEM;
159c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
160c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_size = size;
161c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_put = gb->buf_buf;
162c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_get = gb->buf_buf;
163c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
164c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
165c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
166c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
167c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
168c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_free
169c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
170c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Free the buffer and all associated memory.
171c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
172c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_buf_free(struct gs_buf *gb)
173c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
174c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	kfree(gb->buf_buf);
175c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_buf = NULL;
176c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
177c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
178c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
179c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_clear
180c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
181c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Clear out all data in the circular buffer.
182c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
183c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_buf_clear(struct gs_buf *gb)
184c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
185c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gb->buf_get = gb->buf_put;
186c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* equivalent to a get of all data available */
187c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
188c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
189c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
190c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_data_avail
191c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1921f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell * Return the number of bytes of data written into the circular
193c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * buffer.
194c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
195c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned gs_buf_data_avail(struct gs_buf *gb)
196c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
197c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
198c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
199c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
200c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
201c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_space_avail
202c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
203c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes of space available in the circular
204c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * buffer.
205c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
206c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned gs_buf_space_avail(struct gs_buf *gb)
207c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
208c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
209c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
210c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
211c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
212c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_put
213c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
214c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Copy data data from a user buffer and put it into the circular buffer.
215c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Restrict to the amount of space available.
216c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
217c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes copied.
218c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
219c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned
220c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
221c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
222c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned len;
223c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
224c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len  = gs_buf_space_avail(gb);
225c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len)
226c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		count = len;
227c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
228c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count == 0)
229c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return 0;
230c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
231c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gb->buf_buf + gb->buf_size - gb->buf_put;
232c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len) {
233c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(gb->buf_put, buf, len);
234c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(gb->buf_buf, buf+len, count - len);
235c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gb->buf_put = gb->buf_buf + count - len;
236c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} else {
237c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(gb->buf_put, buf, count);
238c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (count < len)
239c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_put += count;
240c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else /* count == len */
241c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_put = gb->buf_buf;
242c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
243c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
244c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return count;
245c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
246c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
247c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
248c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_buf_get
249c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
250c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Get data from the circular buffer and copy to the given buffer.
251c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Restrict to the amount of data available.
252c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
253c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Return the number of bytes copied.
254c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
255c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned
256c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
257c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
258c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned len;
259c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
260c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gs_buf_data_avail(gb);
261c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len)
262c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		count = len;
263c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
264c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count == 0)
265c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return 0;
266c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
267c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gb->buf_buf + gb->buf_size - gb->buf_get;
268c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count > len) {
269c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(buf, gb->buf_get, len);
270c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(buf+len, gb->buf_buf, count - len);
271c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gb->buf_get = gb->buf_buf + count - len;
272c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} else {
273c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		memcpy(buf, gb->buf_get, count);
274c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (count < len)
275c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_get += count;
276c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else /* count == len */
277c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			gb->buf_get = gb->buf_buf;
278c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
279c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
280c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return count;
281c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
282c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
283c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
284c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
285c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* I/O glue between TTY (upper) and USB function (lower) driver layers */
286c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
287c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
288c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_alloc_req
289c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
290c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Allocate a usb_request and its buffer.  Returns a pointer to the
291c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * usb_request or NULL if there is an error.
292c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
2931f1ba11b64947051fc32aa15fcccef6463b433f7David Brownellstruct usb_request *
294c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
295c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
296c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_request *req;
297c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
298c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	req = usb_ep_alloc_request(ep, kmalloc_flags);
299c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
300c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (req != NULL) {
301c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->length = len;
302c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->buf = kmalloc(len, kmalloc_flags);
303c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (req->buf == NULL) {
304c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			usb_ep_free_request(ep, req);
305c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			return NULL;
306c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
307c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
308c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
309c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return req;
310c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
311c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
312c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
313c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_free_req
314c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
315c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Free a usb_request and its buffer.
316c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
3171f1ba11b64947051fc32aa15fcccef6463b433f7David Brownellvoid gs_free_req(struct usb_ep *ep, struct usb_request *req)
318c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
319c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	kfree(req->buf);
320c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_free_request(ep, req);
321c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
322c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
323c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
324c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_send_packet
325c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
326c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * If there is data to send, a packet is built in the given
327c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * buffer and the size is returned.  If there is no data to
328c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * send, 0 is returned.
329c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
330c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Called with port_lock held.
331c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
332c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned
333c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_send_packet(struct gs_port *port, char *packet, unsigned size)
334c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
335c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned len;
336c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
337c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	len = gs_buf_data_avail(&port->port_write_buf);
338c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (len < size)
339c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		size = len;
340c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (size != 0)
341c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		size = gs_buf_get(&port->port_write_buf, packet, size);
342c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return size;
343c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
344c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
345c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
346c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_start_tx
347c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
348c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This function finds available write requests, calls
349c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_send_packet to fill these packets with data, and
350c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * continues until either there are no more write requests
351c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * available or no more data to send.  This function is
352c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * run whenever data arrives or write requests are available.
353c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
354c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: caller owns port_lock; port_usb is non-null.
355c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
356c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_start_tx(struct gs_port *port)
357c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
358c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__releases(&port->port_lock)
359c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__acquires(&port->port_lock)
360c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell*/
361c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
362c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	*pool = &port->write_pool;
363c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_ep		*in = port->port_usb->in;
364c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int			status = 0;
365c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	bool			do_tty_wake = false;
366c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
367c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (!list_empty(pool)) {
368c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct usb_request	*req;
369c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		int			len;
370c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
37128609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		if (port->write_started >= QUEUE_SIZE)
37228609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung			break;
37328609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung
374c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = list_entry(pool->next, struct usb_request, list);
375c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		len = gs_send_packet(port, req->buf, in->maxpacket);
376c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (len == 0) {
377c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			wake_up_interruptible(&port->drain_wait);
378c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
379c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
380c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		do_tty_wake = true;
381c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
382c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->length = len;
383c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_del(&req->list);
3842e25134122c25ebb0679b4bbd536fb46c669f9d7Daniel Glöckner		req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
385c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
386937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
387937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				port->port_num, len, *((u8 *)req->buf),
388c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				*((u8 *)req->buf+1), *((u8 *)req->buf+2));
389c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
390c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* Drop lock while we call out of driver; completions
391c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * could be issued while we do so.  Disconnection may
392c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * happen too; maybe immediately before we queue this!
393c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 *
394c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * NOTE that we may keep sending data for a while after
395c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * the TTY closed (dev->ioport->port_tty is NULL).
396c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 */
397c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock(&port->port_lock);
398c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = usb_ep_queue(in, req, GFP_ATOMIC);
399c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock(&port->port_lock);
400c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
401c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
402c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("%s: %s %s err %d\n",
403c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					__func__, "queue", in->name, status);
404c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			list_add(&req->list, pool);
405c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
406c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
407c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
40828609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		port->write_started++;
40928609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung
410c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* abort immediately after disconnect */
411c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!port->port_usb)
412c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
413c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
414c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
415c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (do_tty_wake && port->port_tty)
416c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_wakeup(port->port_tty);
417c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
418c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
419c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
420c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
421c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: caller owns port_lock, and port_usb is set
422c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
423c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic unsigned gs_start_rx(struct gs_port *port)
424c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
425c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__releases(&port->port_lock)
426c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell__acquires(&port->port_lock)
427c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell*/
428c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
429c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	*pool = &port->read_pool;
430c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_ep		*out = port->port_usb->out;
431c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
432c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (!list_empty(pool)) {
433c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct usb_request	*req;
434c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		int			status;
435c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct tty_struct	*tty;
436c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
437937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		/* no more rx if closed */
438c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty = port->port_tty;
439937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		if (!tty)
440c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
441c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
44228609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		if (port->read_started >= QUEUE_SIZE)
44328609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung			break;
44428609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung
445c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = list_entry(pool->next, struct usb_request, list);
446c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_del(&req->list);
447c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->length = out->maxpacket;
448c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
449c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* drop lock while we call out; the controller driver
450c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 * may need to call us back (e.g. for disconnect)
451c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		 */
452c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock(&port->port_lock);
453c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = usb_ep_queue(out, req, GFP_ATOMIC);
454c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock(&port->port_lock);
455c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
456c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
457c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("%s: %s %s err %d\n",
458c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					__func__, "queue", out->name, status);
459c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			list_add(&req->list, pool);
460c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
461c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
46228609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		port->read_started++;
463c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
464c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* abort immediately after disconnect */
465c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!port->port_usb)
466c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
467c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
46828609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	return port->read_started;
469c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
470c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
471937ef73d5075997a8d1777abf217a48bef2ce029David Brownell/*
472937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * RX tasklet takes data out of the RX queue and hands it up to the TTY
473937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * layer until it refuses to take any more data (or is throttled back).
474937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * Then it issues reads for any further data.
475937ef73d5075997a8d1777abf217a48bef2ce029David Brownell *
476937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * If the RX queue becomes full enough that no usb_request is queued,
477937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
478937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
479937ef73d5075997a8d1777abf217a48bef2ce029David Brownell * can be buffered before the TTY layer's buffers (currently 64 KB).
480937ef73d5075997a8d1777abf217a48bef2ce029David Brownell */
481937ef73d5075997a8d1777abf217a48bef2ce029David Brownellstatic void gs_rx_push(unsigned long _port)
482c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
483937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	struct gs_port		*port = (void *)_port;
484937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	struct tty_struct	*tty;
485937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	struct list_head	*queue = &port->read_queue;
486937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	bool			disconnect = false;
487937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	bool			do_push = false;
488c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
489937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	/* hand any queued data to the tty */
490937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	spin_lock_irq(&port->port_lock);
491937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	tty = port->port_tty;
492937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	while (!list_empty(queue)) {
493937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		struct usb_request	*req;
494c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
495937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		req = list_first_entry(queue, struct usb_request, list);
496c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
497937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		/* discard data if tty was closed */
498937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		if (!tty)
499937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			goto recycle;
500c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
501937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		/* leave data queued if tty was rx throttled */
502937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		if (test_bit(TTY_THROTTLED, &tty->flags))
503937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			break;
504937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
505937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		switch (req->status) {
506937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		case -ESHUTDOWN:
507937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			disconnect = true;
508937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
509937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			break;
510937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
511937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		default:
512937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			/* presumably a transient fault */
513937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			pr_warning(PREFIX "%d: unexpected RX status %d\n",
514937ef73d5075997a8d1777abf217a48bef2ce029David Brownell					port->port_num, req->status);
515937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			/* FALLTHROUGH */
516937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		case 0:
517937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			/* normal completion */
518937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			break;
519937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		}
520937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
521937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		/* push data to (open) tty */
522937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		if (req->actual) {
523937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			char		*packet = req->buf;
524937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			unsigned	size = req->actual;
525937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			unsigned	n;
526937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			int		count;
527937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
528937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			/* we may have pushed part of this packet already... */
529937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			n = port->n_read;
530937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			if (n) {
531937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				packet += n;
532937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				size -= n;
533937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			}
534937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
535937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			count = tty_insert_flip_string(tty, packet, size);
536937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			if (count)
537937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				do_push = true;
538937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			if (count != size) {
539937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				/* stop pushing; TTY layer can't handle more */
540937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				port->n_read += count;
541937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				pr_vdebug(PREFIX "%d: rx block %d/%d\n",
542937ef73d5075997a8d1777abf217a48bef2ce029David Brownell						port->port_num,
543937ef73d5075997a8d1777abf217a48bef2ce029David Brownell						count, req->actual);
544937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				break;
545937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			}
546937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			port->n_read = 0;
547937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		}
548937ef73d5075997a8d1777abf217a48bef2ce029David Brownellrecycle:
549937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		list_move(&req->list, &port->read_pool);
55028609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		port->read_started--;
551937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	}
552937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
55344a0c0190b500ee6bcfc0976fe540f65dee2cd67Jon Povey	/* Push from tty to ldisc; without low_latency set this is handled by
55444a0c0190b500ee6bcfc0976fe540f65dee2cd67Jon Povey	 * a workqueue, so we won't get callbacks and can hold port_lock
555937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 */
5565023829969f56b78a16da94f34c605bbbb344018Shaun Silk	if (tty && do_push)
557937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		tty_flip_buffer_push(tty);
558937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
559937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
560937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	/* We want our data queue to become empty ASAP, keeping data
561937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 * in the tty and ldisc (not here).  If we couldn't push any
562937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 * this time around, there may be trouble unless there's an
563937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 * implicit tty_unthrottle() call on its way...
564937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 *
565937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 * REVISIT we should probably add a timer to keep the tasklet
566937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 * from starving ... but it's not clear that case ever happens.
567937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	 */
568937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	if (!list_empty(queue) && tty) {
569937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		if (!test_bit(TTY_THROTTLED, &tty->flags)) {
570937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			if (do_push)
571937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				tasklet_schedule(&port->push);
572937ef73d5075997a8d1777abf217a48bef2ce029David Brownell			else
573937ef73d5075997a8d1777abf217a48bef2ce029David Brownell				pr_warning(PREFIX "%d: RX not scheduled?\n",
574937ef73d5075997a8d1777abf217a48bef2ce029David Brownell					port->port_num);
575937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		}
576937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	}
577937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
578937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	/* If we're still connected, refill the USB RX queue. */
579937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	if (!disconnect && port->port_usb)
580937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		gs_start_rx(port);
581937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
582937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	spin_unlock_irq(&port->port_lock);
583937ef73d5075997a8d1777abf217a48bef2ce029David Brownell}
584937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
585937ef73d5075997a8d1777abf217a48bef2ce029David Brownellstatic void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
586937ef73d5075997a8d1777abf217a48bef2ce029David Brownell{
587937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	struct gs_port	*port = ep->driver_data;
588937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
589937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	/* Queue all received data until the tty layer is ready for it. */
590937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	spin_lock(&port->port_lock);
591937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	list_add_tail(&req->list, &port->read_queue);
592937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	tasklet_schedule(&port->push);
593c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock(&port->port_lock);
594c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
595c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
596c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
597c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
598c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = ep->driver_data;
599c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
600c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock(&port->port_lock);
601c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	list_add(&req->list, &port->write_pool);
60228609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	port->write_started--;
603c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
604c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	switch (req->status) {
605c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	default:
606c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* presumably a transient fault */
607c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_warning("%s: unexpected %s status %d\n",
608c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, ep->name, req->status);
609c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* FALL THROUGH */
610c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	case 0:
611c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* normal completion */
612c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_tx(port);
613c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
614c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
615c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	case -ESHUTDOWN:
616c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* disconnect */
617c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
618c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		break;
619c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
620c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
621c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock(&port->port_lock);
622c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
623c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
62428609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sungstatic void gs_free_requests(struct usb_ep *ep, struct list_head *head,
62528609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung							 int *allocated)
626c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
627c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_request	*req;
628c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
629c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (!list_empty(head)) {
630c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = list_entry(head->next, struct usb_request, list);
631c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_del(&req->list);
632c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_free_req(ep, req);
63328609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		if (allocated)
63428609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung			(*allocated)--;
635c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
636c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
637c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
638c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
63928609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		void (*fn)(struct usb_ep *, struct usb_request *),
64028609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		int *allocated)
641c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
642c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int			i;
643c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_request	*req;
64428609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
645c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
646c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
647c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * do quite that many this time, don't fail ... we just won't
648c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * be as speedy as we might otherwise be.
649c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
65028609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	for (i = 0; i < n; i++) {
651c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
652c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!req)
653c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			return list_empty(head) ? -ENOMEM : 0;
654c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		req->complete = fn;
655c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		list_add_tail(&req->list, head);
65628609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		if (allocated)
65728609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung			(*allocated)++;
658c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
659c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
660c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
661c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
662c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
663c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_start_io - start USB I/O streams
664c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @dev: encapsulates endpoints to use
665c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: holding port_lock; port_tty and port_usb are non-null
666c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
667c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * We only start I/O when something is connected to both sides of
668c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * this port.  If nothing is listening on the host side, we may
669c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * be pointlessly filling up our TX buffers and FIFO.
670c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
671c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_start_io(struct gs_port *port)
672c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
673c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct list_head	*head = &port->read_pool;
674c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_ep		*ep = port->port_usb->out;
675c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int			status;
676c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned		started;
677c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
678c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Allocate RX and TX I/O buffers.  We can't easily do this much
679c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * earlier (with GFP_KERNEL) because the requests are coupled to
680c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * endpoints, as are the packet sizes we'll be using.  Different
681c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * configurations may use different endpoints with a given port;
682c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * and high speed vs full speed changes packet sizes too.
683c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
68428609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	status = gs_alloc_requests(ep, head, gs_read_complete,
68528609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		&port->read_allocated);
686c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status)
687c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return status;
688c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
689c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
69028609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung			gs_write_complete, &port->write_allocated);
691c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status) {
69228609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		gs_free_requests(ep, head, &port->read_allocated);
693c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return status;
694c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
695c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
696c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* queue read requests */
697937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	port->n_read = 0;
698c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	started = gs_start_rx(port);
699c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
700c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* unblock any pending writes into our circular buffer */
701c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (started) {
702c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_wakeup(port->port_tty);
703c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} else {
70428609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		gs_free_requests(ep, head, &port->read_allocated);
70528609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		gs_free_requests(port->port_usb->in, &port->write_pool,
70628609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung			&port->write_allocated);
707937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		status = -EIO;
708c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
709c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
710937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	return status;
711c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
712c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
713c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
714c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
715c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* TTY Driver */
716c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
717c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*
718c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gs_open sets up the link between a gs_port and its associated TTY.
719c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * That link is broken *only* by TTY close(), and all driver methods
720c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * know that.
721c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
722c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_open(struct tty_struct *tty, struct file *file)
723c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
724c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		port_num = tty->index;
725c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
726c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
727c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
728c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	do {
729c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_lock(&ports[port_num].lock);
730c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port = ports[port_num].port;
731c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (!port)
732c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			status = -ENODEV;
733c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else {
734c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			spin_lock_irq(&port->port_lock);
735c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
736c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* already open?  Great. */
737c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			if (port->open_count) {
738c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				status = 0;
739c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				port->open_count++;
740c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
741c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* currently opening/closing? wait ... */
742c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			} else if (port->openclose) {
743c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				status = -EBUSY;
744c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
745c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* ... else we do the work */
746c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			} else {
747c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				status = -EAGAIN;
748c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				port->openclose = true;
749c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			}
750c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			spin_unlock_irq(&port->port_lock);
751c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
752c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_unlock(&ports[port_num].lock);
753c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
754c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		switch (status) {
755c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		default:
756c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* fully handled */
757c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			return status;
758c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		case -EAGAIN:
759c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* must do the work */
760c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
761c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		case -EBUSY:
762c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* wait for EAGAIN task to finish */
763c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			msleep(1);
764c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			/* REVISIT could have a waitchannel here, if
765c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			 * concurrent open performance is important
766c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			 */
767c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			break;
768c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
769c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	} while (status != -EAGAIN);
770c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
771c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Do the "real open" */
772c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&port->port_lock);
773c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
774c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* allocate circular buffer on first open */
775c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_write_buf.buf_buf == NULL) {
776c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
777c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock_irq(&port->port_lock);
778c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
779c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock_irq(&port->port_lock);
780c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
781c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
782c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
783c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				port->port_num, tty, file);
784c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->openclose = false;
785c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			goto exit_unlock_port;
786c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
787c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
788c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
789c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT if REMOVED (ports[].port NULL), abort the open
790c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * to let rmmod work faster (but this way isn't wrong).
791c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
792c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
793c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT maybe wait for "carrier detect" */
794c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
795c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty->driver_data = port;
796c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_tty = tty;
797c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
798c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->open_count = 1;
799c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->openclose = false;
800c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
801c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* if connected, start the I/O stream */
802c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb) {
8031f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		struct gserial	*gser = port->port_usb;
8041f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell
805c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_debug("gs_open: start ttyGS%d\n", port->port_num);
806c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_io(port);
807c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
8081f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		if (gser->connect)
8091f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell			gser->connect(gser);
810c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
811c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
812c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
813c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
814c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = 0;
815c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
816c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellexit_unlock_port:
817c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&port->port_lock);
818c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
819c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
820c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
821c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_writes_finished(struct gs_port *p)
822c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
823c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int cond;
824c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
825c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* return true on disconnect or empty buffer */
826c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&p->port_lock);
827c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
828c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&p->port_lock);
829c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
830c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return cond;
831c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
832c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
833c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_close(struct tty_struct *tty, struct file *file)
834c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
835c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port *port = tty->driver_data;
8361f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	struct gserial	*gser;
837c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
838c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&port->port_lock);
839c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
840c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count != 1) {
841c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (port->open_count == 0)
842c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			WARN_ON(1);
843c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		else
844c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			--port->open_count;
845c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		goto exit;
846c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
847c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
848c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
849c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
850c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* mark port as closing but in use; we can drop port lock
851c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * and sleep if necessary
852c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
853c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->openclose = true;
854c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->open_count = 0;
855c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
8561f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	gser = port->port_usb;
8571f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	if (gser && gser->disconnect)
8581f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		gser->disconnect(gser);
859c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
860c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* wait for circular write buffer to drain, disconnect, or at
861c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * most GS_CLOSE_TIMEOUT seconds; then discard the rest
862c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
8631f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
864c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_unlock_irq(&port->port_lock);
865c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wait_event_interruptible_timeout(port->drain_wait,
866c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					gs_writes_finished(port),
867c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell					GS_CLOSE_TIMEOUT * HZ);
868c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		spin_lock_irq(&port->port_lock);
8691f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		gser = port->port_usb;
870c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
871c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
872c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* Iff we're disconnected, there can be no I/O in flight so it's
873c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * ok to free the circular buffer; else just scrub it.  And don't
874c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * let the push tasklet fire again until we're re-opened.
875c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
8761f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	if (gser == NULL)
877c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_buf_free(&port->port_write_buf);
878c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	else
879c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_buf_clear(&port->port_write_buf);
880c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
881c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty->driver_data = NULL;
882c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_tty = NULL;
883c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
884c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->openclose = false;
885c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
886c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
887c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->port_num, tty, file);
888c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
889c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	wake_up_interruptible(&port->close_wait);
890c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellexit:
891c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&port->port_lock);
892c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
893c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
894c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
895c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
896c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
897c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
898c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
899c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
900c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
901c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			port->port_num, tty, count);
902c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
903c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
904c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count)
905c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		count = gs_buf_put(&port->port_write_buf, buf, count);
906c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* treat count == 0 as flush_chars() */
907c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
908c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_start_tx(port);
909c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
910c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
911c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return count;
912c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
913c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
914c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_put_char(struct tty_struct *tty, unsigned char ch)
915c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
916c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
917c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
918c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
919c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
920c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
921c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port->port_num, tty, ch, __builtin_return_address(0));
922c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
923c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
924c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = gs_buf_put(&port->port_write_buf, &ch, 1);
925c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
926c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
927c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
928c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
929c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
930c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_flush_chars(struct tty_struct *tty)
931c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
932c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
933c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
934c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
935c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
936c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
937c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
938c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
939c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_tx(port);
940c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
941c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
942c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
943c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_write_room(struct tty_struct *tty)
944c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
945c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
946c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
947c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		room = 0;
948c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
949c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
950c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->port_usb)
951c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		room = gs_buf_space_avail(&port->port_write_buf);
952c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
953c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
954c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
955c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port->port_num, tty, room);
956c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
957c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return room;
958c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
959c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
960c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_chars_in_buffer(struct tty_struct *tty)
961c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
962c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = tty->driver_data;
963c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
964c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		chars = 0;
965c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
966c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
967c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	chars = gs_buf_data_avail(&port->port_write_buf);
968c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
969c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
970c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
971c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port->port_num, tty, chars);
972c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
973c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return chars;
974c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
975c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
976c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/* undo side effects of setting TTY_THROTTLED */
977c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic void gs_unthrottle(struct tty_struct *tty)
978c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
979c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port		*port = tty->driver_data;
980c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long		flags;
981c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
982c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
983937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	if (port->port_usb) {
984937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		/* Kickstart read queue processing.  We don't do xon/xoff,
985937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		 * rts/cts, or other handshaking with the host, but if the
986937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		 * read queue backs up enough we'll be NAKing OUT packets.
987937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		 */
988937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		tasklet_schedule(&port->push);
989937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
990937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	}
991c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
992c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
993c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
9941f1ba11b64947051fc32aa15fcccef6463b433f7David Brownellstatic int gs_break_ctl(struct tty_struct *tty, int duration)
9951f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell{
9961f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	struct gs_port	*port = tty->driver_data;
9971f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	int		status = 0;
9981f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	struct gserial	*gser;
9991f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell
10001f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
10011f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell			port->port_num, duration);
10021f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell
10031f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	spin_lock_irq(&port->port_lock);
10041f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	gser = port->port_usb;
10051f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	if (gser && gser->send_break)
10061f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		status = gser->send_break(gser, duration);
10071f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	spin_unlock_irq(&port->port_lock);
10081f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell
10091f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	return status;
10101f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell}
10111f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell
1012c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic const struct tty_operations gs_tty_ops = {
1013c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.open =			gs_open,
1014c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.close =		gs_close,
1015c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.write =		gs_write,
1016c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.put_char =		gs_put_char,
1017c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.flush_chars =		gs_flush_chars,
1018c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.write_room =		gs_write_room,
1019c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.chars_in_buffer =	gs_chars_in_buffer,
1020c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	.unthrottle =		gs_unthrottle,
10211f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	.break_ctl =		gs_break_ctl,
1022c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell};
1023c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1024c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/*-------------------------------------------------------------------------*/
1025c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1026c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic struct tty_driver *gs_tty_driver;
1027c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1028a035fb58daa970aadbe04a4e5297a4fbfda1334fBenoit Gobystatic int
1029c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellgs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
1030c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1031c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
1032c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1033c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
1034c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port == NULL)
1035c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENOMEM;
1036c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1037c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_init(&port->port_lock);
1038c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	init_waitqueue_head(&port->close_wait);
1039c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	init_waitqueue_head(&port->drain_wait);
1040c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1041c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
1042c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1043c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	INIT_LIST_HEAD(&port->read_pool);
1044937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	INIT_LIST_HEAD(&port->read_queue);
1045c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	INIT_LIST_HEAD(&port->write_pool);
1046c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1047c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_num = port_num;
1048c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_line_coding = *coding;
1049c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1050c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	ports[port_num].port = port;
1051c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1052c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return 0;
1053c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1054c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1055c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1056c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_setup - initialize TTY driver for one or more ports
1057c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @g: gadget to associate with these ports
1058c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @count: how many ports to support
1059c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: may sleep
1060c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1061c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * The TTY stack needs to know in advance how many devices it should
1062c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * plan to manage.  Use this call to set up the ports you will be
1063c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * exporting through USB.  Later, connect them to functions based
1064c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * on what configuration is activated by the USB host; and disconnect
1065c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * them as appropriate.
1066c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1067c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * An example would be a two-configuration device in which both
1068c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * configurations expose port 0, but through different functions.
1069c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * One configuration could even expose port 1 while the other
1070c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * one doesn't.
1071c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1072c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Returns negative errno or zero.
1073c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1074a035fb58daa970aadbe04a4e5297a4fbfda1334fBenoit Gobyint gserial_setup(struct usb_gadget *g, unsigned count)
1075c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1076c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned			i;
1077c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct usb_cdc_line_coding	coding;
1078c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int				status;
1079c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1080c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (count == 0 || count > N_PORTS)
1081c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -EINVAL;
1082c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1083c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver = alloc_tty_driver(count);
1084c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (!gs_tty_driver)
1085c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENOMEM;
1086c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1087c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->driver_name = "g_serial";
1088937ef73d5075997a8d1777abf217a48bef2ce029David Brownell	gs_tty_driver->name = PREFIX;
1089c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* uses dynamically assigned dev_t values */
1090c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1091c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
1092c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
1093c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
1094c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios = tty_std_termios;
1095c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1096c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
1097c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * MS-Windows.  Otherwise, most of these flags shouldn't affect
1098c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * anything unless we were to actually hook up to a serial line.
1099c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
1100c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios.c_cflag =
1101c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1102c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios.c_ispeed = 9600;
1103c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver->init_termios.c_ospeed = 9600;
1104c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1105551509d267905705f6d723e51ec706916f06b859Harvey Harrison	coding.dwDTERate = cpu_to_le32(9600);
1106c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.bCharFormat = 8;
1107c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.bParityType = USB_CDC_NO_PARITY;
1108c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	coding.bDataBits = USB_CDC_1_STOP_BITS;
1109c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1110c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty_set_operations(gs_tty_driver, &gs_tty_ops);
1111c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1112c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* make devices be openable */
1113c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < count; i++) {
1114c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_init(&ports[i].lock);
1115c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		status = gs_port_alloc(i, &coding);
1116c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (status) {
1117c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			count = i;
1118c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			goto fail;
1119c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		}
1120c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1121c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	n_ports = count;
1122c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1123c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* export the driver ... */
1124c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	status = tty_register_driver(gs_tty_driver);
1125c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status) {
1126c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_err("%s: cannot register, err %d\n",
1127c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, status);
1128c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		goto fail;
1129c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1130c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1131c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
1132c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < count; i++) {
1133c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		struct device	*tty_dev;
1134c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1135c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
1136c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (IS_ERR(tty_dev))
1137c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			pr_warning("%s: no classdev for port %d, err %ld\n",
1138c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell				__func__, i, PTR_ERR(tty_dev));
1139c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1140c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1141c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
1142c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			count, (count == 1) ? "" : "s");
1143c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1144c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1145c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellfail:
1146c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	while (count--)
1147c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		kfree(ports[count].port);
1148c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	put_tty_driver(gs_tty_driver);
1149c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver = NULL;
1150c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1151c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1152c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1153c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellstatic int gs_closed(struct gs_port *port)
1154c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1155c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int cond;
1156c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1157c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irq(&port->port_lock);
1158c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	cond = (port->open_count == 0) && !port->openclose;
1159c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irq(&port->port_lock);
1160c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return cond;
1161c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1162c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1163c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1164c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_cleanup - remove TTY-over-USB driver and devices
1165c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: may sleep
1166c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1167c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This is called to free all resources allocated by @gserial_setup().
1168c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Accordingly, it may need to wait until some open /dev/ files have
1169c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * closed.
1170c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1171c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * The caller must have issued @gserial_disconnect() for any ports
1172c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * that had previously been connected, so that there is never any
1173c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * I/O pending when it's called.
1174c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1175c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellvoid gserial_cleanup(void)
1176c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1177c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned	i;
1178c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
1179c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1180ac90e36592ea5171c4e70f58b39a782d871a7d9fDavid Brownell	if (!gs_tty_driver)
1181ac90e36592ea5171c4e70f58b39a782d871a7d9fDavid Brownell		return;
1182ac90e36592ea5171c4e70f58b39a782d871a7d9fDavid Brownell
1183c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* start sysfs and /dev/ttyGS* node removal */
1184c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < n_ports; i++)
1185c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		tty_unregister_device(gs_tty_driver, i);
1186c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1187c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	for (i = 0; i < n_ports; i++) {
1188c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* prevent new opens */
1189c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_lock(&ports[i].lock);
1190c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		port = ports[i].port;
1191c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		ports[i].port = NULL;
1192c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		mutex_unlock(&ports[i].lock);
1193c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1194937ef73d5075997a8d1777abf217a48bef2ce029David Brownell		tasklet_kill(&port->push);
1195937ef73d5075997a8d1777abf217a48bef2ce029David Brownell
1196c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		/* wait for old opens to finish */
1197c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wait_event(port->close_wait, gs_closed(port));
1198c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1199c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		WARN_ON(port->port_usb != NULL);
1200c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1201c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		kfree(port);
1202c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1203c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	n_ports = 0;
1204c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1205c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	tty_unregister_driver(gs_tty_driver);
1206b23097b793081358a6d943263c91bae4c955c4e3Jon Povey	put_tty_driver(gs_tty_driver);
1207c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gs_tty_driver = NULL;
1208c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1209c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	pr_debug("%s: cleaned up ttyGS* support\n", __func__);
1210c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1211c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1212c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1213c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_connect - notify TTY I/O glue that USB link is active
1214c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @gser: the function, set up with endpoints and descriptors
1215c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @port_num: which port is active
1216c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: any (usually from irq)
1217c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1218c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This is called activate endpoints and let the TTY layer know that
1219c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * the connection is active ... not unlike "carrier detect".  It won't
1220c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * necessarily start I/O queues; unless the TTY is held open by any
1221c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * task, there would be no point.  However, the endpoints will be
1222c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * activated so the USB host can perform I/O, subject to basic USB
1223c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * hardware flow control.
1224c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1225c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Caller needs to have set up the endpoints and USB function in @dev
1226c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * before calling this, as well as the appropriate (speed-specific)
1227c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * endpoint descriptors, and also have set up the TTY driver by calling
1228c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @gserial_setup().
1229c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1230c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Returns negative errno or zero.
1231c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * On success, ep->driver_data will be overwritten.
1232c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1233c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellint gserial_connect(struct gserial *gser, u8 port_num)
1234c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1235c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port;
1236c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
1237c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	int		status;
1238c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1239c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (!gs_tty_driver || port_num >= n_ports)
1240c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return -ENXIO;
1241c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1242c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* we "know" gserial_cleanup() hasn't been called */
1243c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port = ports[port_num].port;
1244c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1245c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* activate the endpoints */
124672c973dd2b01b212a159faa330a2bc641a3ed809Tatyana Brokhman	status = usb_ep_enable(gser->in);
1247c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status < 0)
1248c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return status;
1249c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->in->driver_data = port;
1250c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
125172c973dd2b01b212a159faa330a2bc641a3ed809Tatyana Brokhman	status = usb_ep_enable(gser->out);
1252c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (status < 0)
1253c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		goto fail_out;
1254c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->out->driver_data = port;
1255c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1256c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* then tell the tty glue that I/O can work */
1257c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
1258c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->ioport = port;
1259c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_usb = gser;
1260c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1261c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT unclear how best to handle this state...
1262c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 * we don't really couple it with the Linux TTY.
1263c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
1264c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->port_line_coding = port->port_line_coding;
1265c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1266c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT if waiting on "carrier detect", signal. */
1267c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
12681f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	/* if it's already open, start I/O ... and notify the serial
12691f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	 * protocol about open/close status (connect/disconnect).
1270c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	 */
1271c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count) {
1272c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
1273c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_start_io(port);
12741f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		if (gser->connect)
12751f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell			gser->connect(gser);
12761f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell	} else {
12771f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell		if (gser->disconnect)
12781f1ba11b64947051fc32aa15fcccef6463b433f7David Brownell			gser->disconnect(gser);
1279c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1280c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1281c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
1282c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1283c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1284c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1285c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellfail_out:
1286c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_disable(gser->in);
1287c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->in->driver_data = NULL;
1288c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	return status;
1289c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1290c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1291c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell/**
1292c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * gserial_disconnect - notify TTY I/O glue that USB link is inactive
1293c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * @gser: the function, on which gserial_connect() was called
1294c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * Context: any (usually from irq)
1295c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1296c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * This is called to deactivate endpoints and let the TTY layer know
1297c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * that the connection went inactive ... not unlike "hangup".
1298c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell *
1299c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * On return, the state is as if gserial_connect() had never been called;
1300c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell * there is no active USB I/O on these endpoints.
1301c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell */
1302c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownellvoid gserial_disconnect(struct gserial *gser)
1303c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell{
1304c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	struct gs_port	*port = gser->ioport;
1305c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	unsigned long	flags;
1306c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1307c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (!port)
1308c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		return;
1309c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1310c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* tell the TTY glue not to do I/O here any more */
1311c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
1312c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1313c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* REVISIT as above: how best to track this? */
1314c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_line_coding = gser->port_line_coding;
1315c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1316c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	port->port_usb = NULL;
1317c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->ioport = NULL;
1318c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count > 0 || port->openclose) {
1319c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		wake_up_interruptible(&port->drain_wait);
1320c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		if (port->port_tty)
1321c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell			tty_hangup(port->port_tty);
1322c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	}
1323c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
1324c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1325c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* disable endpoints, aborting down any active I/O */
1326c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_disable(gser->out);
1327c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->out->driver_data = NULL;
1328c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1329c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	usb_ep_disable(gser->in);
1330c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	gser->in->driver_data = NULL;
1331c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell
1332c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	/* finally, free any unused/unusable I/O buffers */
1333c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_lock_irqsave(&port->port_lock, flags);
1334c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	if (port->open_count == 0 && !port->openclose)
1335c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell		gs_buf_free(&port->port_write_buf);
133628609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	gs_free_requests(gser->out, &port->read_pool, NULL);
133728609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	gs_free_requests(gser->out, &port->read_queue, NULL);
133828609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	gs_free_requests(gser->in, &port->write_pool, NULL);
133928609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung
134028609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung	port->read_allocated = port->read_started =
134128609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung		port->write_allocated = port->write_started = 0;
134228609d4083bcd4879e951b0c4ecf4c3a88761261Jim Sung
1343c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell	spin_unlock_irqrestore(&port->port_lock, flags);
1344c1dca562be8ada614ef193aa246c6f8705bcd6b9David Brownell}
1345