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