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