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