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