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