1e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 2e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * n_gsm.c GSM 0710 tty multiplexor 3e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Copyright (c) 2009/10 Intel Corporation 4e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 5e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * This program is free software; you can redistribute it and/or modify 6e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * it under the terms of the GNU General Public License version 2 as 7e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * published by the Free Software Foundation. 8e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 9e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * This program is distributed in the hope that it will be useful, 10e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * but WITHOUT ANY WARRANTY; without even the implied warranty of 11e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * GNU General Public License for more details. 13e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 14e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * You should have received a copy of the GNU General Public License 15e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * along with this program; if not, write to the Free Software 16e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 18e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE * 19e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 20e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * TO DO: 21e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Mostly done: ioctls for setting modes/timing 225f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox * Partly done: hooks so you can pull off frames to non tty devs 23e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Restart DLCI 0 when it closes ? 24e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Improve the tx engine 25e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Resolve tx side locking by adding a queue_head and routing 26e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * all control traffic via it 27e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * General tidy/document 28e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Review the locking/move to refcounts more (mux now moved to an 29e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * alloc/free model ready) 30e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Use newest tty open/close port helpers and install hooks 31e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * What to do about power functions ? 32e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Termios setting and negotiation 33e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Do we need a 'which mux are you' ioctl to correlate mux and tty sets 34e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 35e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 36e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 37e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/types.h> 38e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/major.h> 39e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/errno.h> 40e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/signal.h> 41e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/fcntl.h> 42e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/sched.h> 43e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/interrupt.h> 44e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/tty.h> 45e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/ctype.h> 46e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/mm.h> 47e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/string.h> 48e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/slab.h> 49e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/poll.h> 50e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/bitops.h> 51e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/file.h> 52e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/uaccess.h> 53e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/module.h> 54e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/timer.h> 55e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/tty_flip.h> 56e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/tty_driver.h> 57e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/serial.h> 58e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/kfifo.h> 59e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/skbuff.h> 60bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#include <net/arp.h> 61bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#include <linux/ip.h> 62bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#include <linux/netdevice.h> 63bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#include <linux/etherdevice.h> 64e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#include <linux/gsmmux.h> 65e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 66e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int debug; 67e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxmodule_param(debug, int, 0600); 68e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 69a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox/* Defaults: these are from the specification */ 70a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox 71a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox#define T1 10 /* 100mS */ 72a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox#define T2 34 /* 333mS */ 73a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox#define N2 3 /* Retry 3 times */ 74e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 75e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* Use long timers for testing at low speed with debug on */ 76e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#ifdef DEBUG_TIMING 77a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox#define T1 100 78a8d12007c795f3d69ee0b2d29f0abfefd55c6120Alan Cox#define T2 200 79e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#endif 80e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 815f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox/* 8225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * Semi-arbitrary buffer size limits. 0710 is normally run with 32-64 byte 835f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox * limits so this is plenty 845f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox */ 85bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#define MAX_MRU 1500 86bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#define MAX_MTU 1500 87bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#define GSM_NET_TX_TIMEOUT (HZ*10) 88bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 89bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby/** 90bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * struct gsm_mux_net - network interface 91bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * @struct gsm_dlci* dlci 92bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * @struct net_device_stats stats; 93bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * 94bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * Created when net interface is initialized. 95bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby **/ 96bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystruct gsm_mux_net { 97bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct kref ref; 98bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_dlci *dlci; 99bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct net_device_stats stats; 100bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby}; 101bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 102bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby#define STATS(net) (((struct gsm_mux_net *)netdev_priv(net))->stats) 103e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 104e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 105e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Each block of data we have queued to go out is in the form of 10625985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * a gsm_msg which holds everything we need in a link layer independent 107e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * format 108e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 109e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 110e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstruct gsm_msg { 111e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *next; 112e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 addr; /* DLCI address + flags */ 113e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 ctrl; /* Control byte + flags */ 114e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int len; /* Length of data block (can be zero) */ 115e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned char *data; /* Points into buffer but not at the start */ 116e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned char buffer[0]; 117e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 118e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 119e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 120e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Each active data link has a gsm_dlci structure associated which ties 121e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the link layer to an optional tty (if the tty side is open). To avoid 122e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * complexity right now these are only ever freed up when the mux is 123e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * shut down. 124e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 125e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * At the moment we don't free DLCI objects until the mux is torn down 126e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * this avoid object life time issues but might be worth review later. 127e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 128e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 129e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstruct gsm_dlci { 130e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm; 131e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int addr; 132e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int state; 133e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define DLCI_CLOSED 0 134e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define DLCI_OPENING 1 /* Sending SABM not seen UA */ 135e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define DLCI_OPEN 2 /* SABM/UA complete */ 136e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ 1376ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby struct kref ref; /* freed from port or mux close */ 138bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct mutex mutex; 139e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 140e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Link layer */ 141e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spinlock_t lock; /* Protects the internal state */ 142e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct timer_list t1; /* Retransmit timer for SABM and UA */ 143e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int retries; 144e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Uplink tty if active */ 145e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_port port; /* The tty bound to this DLCI if there is one */ 146e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct kfifo *fifo; /* Queue fifo for the DLCI */ 147e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct kfifo _fifo; /* For new fifo API porting only */ 148e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int adaption; /* Adaption layer in use */ 149bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby int prev_adaption; 150e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u32 modem_rx; /* Our incoming virtual modem lines */ 151e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u32 modem_tx; /* Our outgoing modem lines */ 152e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int dead; /* Refuse re-open */ 153e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Flow control */ 154e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int throttled; /* Private copy of throttle state */ 155e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int constipated; /* Throttle status for outgoing */ 156e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Packetised I/O */ 157e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct sk_buff *skb; /* Frame being sent */ 158e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct sk_buff_head skb_list; /* Queued frames */ 159e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Data handling callback */ 160e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox void (*data)(struct gsm_dlci *dlci, u8 *data, int len); 161bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby void (*prev_data)(struct gsm_dlci *dlci, u8 *data, int len); 162bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct net_device *net; /* network interface, if created */ 163e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 164e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 165e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* DLCI 0, 62/63 are special or reseved see gsmtty_open */ 166e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 167e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define NUM_DLCI 64 168e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 169e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 170e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * DLCI 0 is used to pass control blocks out of band of the data 171e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * flow (and with a higher link priority). One command can be outstanding 172e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * at a time and we use this structure to manage them. They are created 173e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and destroyed by the user context, and updated by the receive paths 174e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and timers 175e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 176e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 177e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstruct gsm_control { 178e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 cmd; /* Command we are issuing */ 179e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *data; /* Data for the command in case we retransmit */ 180e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len; /* Length of block for retransmission */ 181e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int done; /* Done flag */ 182e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int error; /* Error if any */ 183e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 184e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 185e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 186e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Each GSM mux we have is represented by this structure. If we are 187e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * operating as an ldisc then we use this structure as our ldisc 188e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * state. We need to sort out lifetimes and locking with respect 189e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * to the gsm mux array. For now we don't free DLCI objects that 190e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * have been instantiated until the mux itself is terminated. 191e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 192e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * To consider further: tty open versus mux shutdown. 193e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 194e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 195e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstruct gsm_mux { 196e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_struct *tty; /* The tty our ldisc is bound to */ 197e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spinlock_t lock; 198d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby unsigned int num; 1996ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby struct kref ref; 200e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 201e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Events on the GSM channel */ 202e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wait_queue_head_t event; 203e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 204e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Bits for GSM mode decoding */ 205e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 206e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Framing Layer */ 207e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned char *buf; 208e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int state; 209e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_SEARCH 0 210e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_START 1 211e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_ADDRESS 2 212e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_CONTROL 3 213e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_LEN 4 214e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_DATA 5 215e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_FCS 6 216e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM_OVERRUN 7 217c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox#define GSM_LEN0 8 218c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox#define GSM_LEN1 9 219c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox#define GSM_SSOF 10 220e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int len; 221e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int address; 222e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int count; 223e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int escape; 224e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int encoding; 225e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 control; 226e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 fcs; 227c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox u8 received_fcs; 228e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *txframe; /* TX framing buffer */ 229e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 230e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Methods for the receiver side */ 231e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox void (*receive)(struct gsm_mux *gsm, u8 ch); 232e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox void (*error)(struct gsm_mux *gsm, u8 ch, u8 flag); 233e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* And transmit side */ 234e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int (*output)(struct gsm_mux *mux, u8 *data, int len); 235e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 236e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Link Layer */ 237e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int mru; 238e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int mtu; 239e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int initiator; /* Did we initiate connection */ 240e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int dead; /* Has the mux been shut down */ 241e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci[NUM_DLCI]; 242e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int constipated; /* Asked by remote to shut up */ 243e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 244e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spinlock_t tx_lock; 245e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int tx_bytes; /* TX data outstanding */ 246e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define TX_THRESH_HI 8192 247e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define TX_THRESH_LO 2048 248e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *tx_head; /* Pending data packets */ 249e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *tx_tail; 250e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 251e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Control messages */ 252e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct timer_list t2_timer; /* Retransmit timer for commands */ 253e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int cretries; /* Command retry counter */ 254e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_control *pending_cmd;/* Our current pending command */ 255e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spinlock_t control_lock; /* Protects the pending command */ 256e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 257e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Configuration */ 258e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int adaption; /* 1 or 2 supported */ 259e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 ftype; /* UI or UIH */ 260e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int t1, t2; /* Timers in 1/100th of a sec */ 261e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int n2; /* Retry count */ 262e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 263e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Statistics (not currently exposed) */ 264e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long bad_fcs; 265e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long malformed; 266e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long io_error; 267e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long bad_size; 268e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long unsupported; 269e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 270e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 271e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 272e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 273e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Mux objects - needed so that we can translate a tty index into the 274e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * relevant mux and DLCI. 275e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 276e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 277e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define MAX_MUX 4 /* 256 minors */ 278e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ 279e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic spinlock_t gsm_mux_lock; 280e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 281d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorbystatic struct tty_driver *gsm_tty_driver; 282d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby 283e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 284e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * This section of the driver logic implements the GSM encodings 285e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * both the basic and the 'advanced'. Reliable transport is not 286e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * supported. 287e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 288e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 289e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CR 0x02 290e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define EA 0x01 291e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define PF 0x10 292e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 293e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* I is special: the rest are ..*/ 294e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define RR 0x01 295e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define UI 0x03 296e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define RNR 0x05 297e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define REJ 0x09 298e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define DM 0x0F 299e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define SABM 0x2F 300e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define DISC 0x43 301e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define UA 0x63 302e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define UIH 0xEF 303e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 304e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* Channel commands */ 305e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_NSC 0x09 306e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_TEST 0x11 307e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_PSC 0x21 308e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_RLS 0x29 309e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_FCOFF 0x31 310e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_PN 0x41 311e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_RPN 0x49 312e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_FCON 0x51 313e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_CLD 0x61 314e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_SNC 0x69 315e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define CMD_MSC 0x71 316e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 317e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* Virtual modem bits */ 318e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define MDM_FC 0x01 319e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define MDM_RTC 0x02 320e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define MDM_RTR 0x04 321e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define MDM_IC 0x20 322e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define MDM_DV 0x40 323e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 324e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM0_SOF 0xF9 3255f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox#define GSM1_SOF 0x7E 326e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM1_ESCAPE 0x7D 327e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GSM1_ESCAPE_BITS 0x20 328e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define XON 0x11 329e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define XOFF 0x13 330e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 331e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic const struct tty_port_operations gsm_port_ops; 332e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 333e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 334e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * CRC table for GSM 0710 335e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 336e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 337e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic const u8 gsm_fcs8[256] = { 338e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 339e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 340e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 341e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 342e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 343e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, 344e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 345e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, 346e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 347e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, 348e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 349e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, 350e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 351e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, 352e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 353e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 354e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 355e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, 356e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 357e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, 358e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 359e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, 360e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 361e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, 362e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 363e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, 364e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 365e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, 366e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 367e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 368e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 369e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF 370e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 371e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 372e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define INIT_FCS 0xFF 373e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define GOOD_FCS 0xCF 374e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 375e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 376e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_fcs_add - update FCS 377e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @fcs: Current FCS 378e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @c: Next data 379e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 380e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Update the FCS to include c. Uses the algorithm in the specification 381e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * notes. 382e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 383e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 384e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic inline u8 gsm_fcs_add(u8 fcs, u8 c) 385e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 386e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsm_fcs8[fcs ^ c]; 387e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 388e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 389e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 390e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_fcs_add_block - update FCS for a block 391e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @fcs: Current FCS 392e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @c: buffer of data 393e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: length of buffer 394e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 395e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Update the FCS to include c. Uses the algorithm in the specification 396e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * notes. 397e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 398e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 399e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len) 400e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 401e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (len--) 402e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox fcs = gsm_fcs8[fcs ^ *c++]; 403e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return fcs; 404e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 405e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 406e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 407e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_read_ea - read a byte into an EA 408e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @val: variable holding value 409e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * c: byte going into the EA 410e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 411e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Processes one byte of an EA. Updates the passed variable 412e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and returns 1 if the EA is now completely read 413e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 414e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 415e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsm_read_ea(unsigned int *val, u8 c) 416e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 417e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Add the next 7 bits into the value */ 418e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *val <<= 7; 419e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *val |= c >> 1; 420e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Was this the last byte of the EA 1 = yes*/ 421e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return c & EA; 422e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 423e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 424e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 425e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_encode_modem - encode modem data bits 426e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI to encode from 427e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 428e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Returns the correct GSM encoded modem status bits (6 bit field) for 429e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the current status of the DLCI and attached tty object 430e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 431e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 432e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic u8 gsm_encode_modem(const struct gsm_dlci *dlci) 433e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 434e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 modembits = 0; 435e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FC is true flow control not modem bits */ 436e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->throttled) 437e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits |= MDM_FC; 438e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->modem_tx & TIOCM_DTR) 439e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits |= MDM_RTC; 440e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->modem_tx & TIOCM_RTS) 441e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits |= MDM_RTR; 442e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->modem_tx & TIOCM_RI) 443e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits |= MDM_IC; 444e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->modem_tx & TIOCM_CD) 445e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits |= MDM_DV; 446e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return modembits; 447e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 448e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 449e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 450e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_print_packet - display a frame for debug 451e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @hdr: header to print before decode 452e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @addr: address EA from the frame 453e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @cr: C/R bit from the frame 454e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @control: control including PF bit 455e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: following data bytes 456e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlen: length of data 457e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 458e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Displays a packet in human readable format for debugging purposes. The 459e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * style is based on amateur radio LAP-B dump display. 460e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 461e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 462e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_print_packet(const char *hdr, int addr, int cr, 463e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 control, const u8 *data, int dlen) 464e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 465e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (!(debug & 1)) 466e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 467e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 4685f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_info("%s %d) %c: ", hdr, addr, "RC"[cr]); 469e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 470e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (control & ~PF) { 471e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case SABM: 4725f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("SABM"); 473e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 474e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UA: 4755f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("UA"); 476e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 477e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DISC: 4785f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("DISC"); 479e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 480e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DM: 4815f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("DM"); 482e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 483e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UI: 4845f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("UI"); 485e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 486e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UIH: 4875f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("UIH"); 488e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 489e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox default: 490e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (!(control & 0x01)) { 4915f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("I N(S)%d N(R)%d", 4925f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox (control & 0x0E) >> 1, (control & 0xE) >> 5); 493e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else switch (control & 0x0F) { 4945f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case RR: 4955f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("RR(%d)", (control & 0xE0) >> 5); 4965f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox break; 4975f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case RNR: 4985f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("RNR(%d)", (control & 0xE0) >> 5); 4995f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox break; 5005f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case REJ: 5015f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("REJ(%d)", (control & 0xE0) >> 5); 5025f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox break; 5035f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox default: 5045f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("[%02X]", control); 505e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 506e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 507e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 508e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (control & PF) 5095f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("(P)"); 510e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 5115f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("(F)"); 512e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 513e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlen) { 514e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int ct = 0; 515e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (dlen--) { 5165f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox if (ct % 8 == 0) { 5175f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("\n"); 5185f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug(" "); 5195f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox } 5205f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("%02X ", *data++); 521e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ct++; 522e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 523e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 5245f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_cont("\n"); 525e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 526e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 527e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 528e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 529e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Link level transmission side 530e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 531e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 532e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 533e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_stuff_packet - bytestuff a packet 534e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @ibuf: input 535e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @obuf: output 536e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: length of input 537e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 538e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Expand a buffer by bytestuffing it. The worst case size change 539e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * is doubling and the caller is responsible for handing out 540e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * suitable sized buffers. 541e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 542e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 543e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsm_stuff_frame(const u8 *input, u8 *output, int len) 544e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 545e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int olen = 0; 546e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (len--) { 547e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (*input == GSM1_SOF || *input == GSM1_ESCAPE 548e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox || *input == XON || *input == XOFF) { 549e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *output++ = GSM1_ESCAPE; 550e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *output++ = *input++ ^ GSM1_ESCAPE_BITS; 551e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox olen++; 552e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else 553e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *output++ = *input++; 554e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox olen++; 555e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 556e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return olen; 557e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 558e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 559e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 560e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_send - send a control frame 561e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our GSM mux 562e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @addr: address for control frame 563e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @cr: command/response bit 564e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @control: control byte including PF bit 565e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 566e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Format up and transmit a control frame. These do not go via the 567e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * queueing logic as they should be transmitted ahead of data when 568e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * they are needed. 569e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 570e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * FIXME: Lock versus data TX path 571e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 572e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 573e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control) 574e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 575e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len; 576e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 cbuf[10]; 577e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 ibuf[3]; 578e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 579e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (gsm->encoding) { 580e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case 0: 581e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[0] = GSM0_SOF; 582e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[1] = (addr << 2) | (cr << 1) | EA; 583e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[2] = control; 584e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[3] = EA; /* Length of data = 0 */ 585e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3); 586e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[5] = GSM0_SOF; 587e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = 6; 588e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 589e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case 1: 590e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case 2: 591e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Control frame + packing (but not frame stuffing) in mode 1 */ 592e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ibuf[0] = (addr << 2) | (cr << 1) | EA; 593e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ibuf[1] = control; 594e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2); 595e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Stuffing may double the size worst case */ 596e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = gsm_stuff_frame(ibuf, cbuf + 1, 3); 597e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Now add the SOF markers */ 598e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[0] = GSM1_SOF; 599e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cbuf[len + 1] = GSM1_SOF; 600e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: we can omit the lead one in many cases */ 601e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len += 2; 602e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 603e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox default: 604e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox WARN_ON(1); 605e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 606e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 607e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->output(gsm, cbuf, len); 608e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_print_packet("-->", addr, cr, control, NULL, 0); 609e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 610e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 611e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 612e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_response - send a control response 613e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our GSM mux 614e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @addr: address for control frame 615e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @control: control byte including PF bit 616e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 617e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Format up and transmit a link level response frame. 618e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 619e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 620e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic inline void gsm_response(struct gsm_mux *gsm, int addr, int control) 621e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 622e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_send(gsm, addr, 0, control); 623e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 624e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 625e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 626e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_command - send a control command 627e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our GSM mux 628e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @addr: address for control frame 629e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @control: control byte including PF bit 630e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 631e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Format up and transmit a link level command frame. 632e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 633e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 634e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic inline void gsm_command(struct gsm_mux *gsm, int addr, int control) 635e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 636e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_send(gsm, addr, 1, control); 637e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 638e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 639e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* Data transmission */ 640e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 641e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */ 642e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 643e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 644e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_data_alloc - allocate data frame 645e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: GSM mux 646e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @addr: DLCI address 647e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: length excluding header and FCS 648e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @ctrl: control byte 649e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 650e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Allocate a new data buffer for sending frames with data. Space is left 651e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * at the front for header bytes but that is treated as an implementation 652e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * detail and not for the high level code to use 653e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 654e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 655e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, 656e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 ctrl) 657e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 658e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN, 659e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox GFP_ATOMIC); 660e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (m == NULL) 661e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 662e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox m->data = m->buffer + HDR_LEN - 1; /* Allow for FCS */ 663e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox m->len = len; 664e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox m->addr = addr; 665e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox m->ctrl = ctrl; 666e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox m->next = NULL; 667e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return m; 668e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 669e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 670e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 671e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_data_kick - poke the queue 672e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: GSM Mux 673e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 674e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * The tty device has called us to indicate that room has appeared in 675e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the transmit queue. Ram more data into the pipe if we have any 676e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 677e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * FIXME: lock against link layer control transmissions 678e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 679e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 680e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_data_kick(struct gsm_mux *gsm) 681e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 682e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *msg = gsm->tx_head; 683e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len; 684e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int skip_sof = 0; 685e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 686e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: We need to apply this solely to data messages */ 687e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->constipated) 688e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 689e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 690e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (gsm->tx_head != NULL) { 691e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg = gsm->tx_head; 692e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->encoding != 0) { 693e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->txframe[0] = GSM1_SOF; 694e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = gsm_stuff_frame(msg->data, 695e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->txframe + 1, msg->len); 696e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->txframe[len + 1] = GSM1_SOF; 697e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len += 2; 698e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else { 699e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->txframe[0] = GSM0_SOF; 700e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox memcpy(gsm->txframe + 1 , msg->data, msg->len); 701e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->txframe[msg->len + 1] = GSM0_SOF; 702e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = msg->len + 2; 703e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 704e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 7050a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches if (debug & 4) 7060a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches print_hex_dump_bytes("gsm_data_kick: ", 7070a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches DUMP_PREFIX_OFFSET, 7080a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches gsm->txframe, len); 709e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 710e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->output(gsm, gsm->txframe + skip_sof, 711e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len - skip_sof) < 0) 712e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 713e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: Can eliminate one SOF in many more cases */ 714e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_head = msg->next; 715e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->tx_head == NULL) 716e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_tail = NULL; 717e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_bytes -= msg->len; 718e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(msg); 719e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* For a burst of frames skip the extra SOF within the 720e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox burst */ 721e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox skip_sof = 1; 722e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 723e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 724e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 725e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 726e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * __gsm_data_queue - queue a UI or UIH frame 727e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI sending the data 728e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @msg: message queued 729e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 730e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Add data to the transmit queue and try and get stuff moving 731e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * out of the mux tty if not already doing so. The Caller must hold 732e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the gsm tx lock. 733e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 734e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 735e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) 736e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 737e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = dlci->gsm; 738e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *dp = msg->data; 739e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *fcs = dp + msg->len; 740e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 741e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Fill in the header */ 742e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->encoding == 0) { 743e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (msg->len < 128) 744e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *--dp = (msg->len << 1) | EA; 745e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else { 746be7a7411d63ccad165d66fe8e0b11b2ee336159bKen Mills *--dp = (msg->len >> 7); /* bits 7 - 15 */ 747be7a7411d63ccad165d66fe8e0b11b2ee336159bKen Mills *--dp = (msg->len & 127) << 1; /* bits 0 - 6 */ 748e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 749e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 750e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 751e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *--dp = msg->ctrl; 752e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->initiator) 753e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *--dp = (msg->addr << 2) | 2 | EA; 754e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 755e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *--dp = (msg->addr << 2) | EA; 756e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp); 757e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Ugly protocol layering violation */ 758e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (msg->ctrl == UI || msg->ctrl == (UI|PF)) 759e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len); 760e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *fcs = 0xFF - *fcs; 761e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 762e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl, 763e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg->data, msg->len); 764e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 765e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Move the header back and adjust the length, also allow for the FCS 766e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox now tacked on the end */ 767e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg->len += (msg->data - dp) + 1; 768e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg->data = dp; 769e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 770e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Add to the actual output queue */ 771e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->tx_tail) 772e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_tail->next = msg; 773e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 774e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_head = msg; 775e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_tail = msg; 776e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_bytes += msg->len; 777e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_data_kick(gsm); 778e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 779e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 780e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 781e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_data_queue - queue a UI or UIH frame 782e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI sending the data 783e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @msg: message queued 784e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 785e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Add data to the transmit queue and try and get stuff moving 786e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * out of the mux tty if not already doing so. Take the 787e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the gsm tx lock and dlci lock. 788e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 789e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 790e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) 791e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 792e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long flags; 793e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_irqsave(&dlci->gsm->tx_lock, flags); 794e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox __gsm_data_queue(dlci, msg); 795e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); 796e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 797e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 798e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 799e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_data_output - try and push data out of a DLCI 800e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: mux 801e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: the DLCI to pull data from 802e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 803e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Pull data from a DLCI and send it into the transmit queue if there 804e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * is data. Keep to the MRU of the mux. This path handles the usual tty 805e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * interface which is a byte stream with optional modem data. 806e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 807e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Caller must hold the tx_lock of the mux. 808e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 809e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 810e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) 811e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 812e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *msg; 813e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *dp; 814268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy int len, total_size, size; 815e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int h = dlci->adaption - 1; 816e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 817268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy total_size = 0; 818268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy while(1) { 819268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy len = kfifo_len(dlci->fifo); 820268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy if (len == 0) 821268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy return total_size; 822268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy 823268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy /* MTU/MRU count only the data bits */ 824268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy if (len > gsm->mtu) 825268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy len = gsm->mtu; 826268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy 827268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy size = len + h; 828268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy 829268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); 830268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy /* FIXME: need a timer or something to kick this so it can't 831268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy get stuck with no work outstanding and no buffer free */ 832268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy if (msg == NULL) 833268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy return -ENOMEM; 834268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy dp = msg->data; 835268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy switch (dlci->adaption) { 836268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy case 1: /* Unstructured */ 837268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy break; 838268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy case 2: /* Unstructed with modem bits. Always one byte as we never 839268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy send inline break data */ 840268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy *dp++ = gsm_encode_modem(dlci); 841268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy break; 842268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy } 843268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); 844268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy __gsm_data_queue(dlci, msg); 845268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy total_size += size; 846e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 847e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Bytes of data we used up */ 848268e526b935e794386d75025577b745e6bd57f13Mikhail Kshevetskiy return total_size; 849e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 850e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 851e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 852e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_data_output_framed - try and push data out of a DLCI 853e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: mux 854e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: the DLCI to pull data from 855e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 856e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Pull data from a DLCI and send it into the transmit queue if there 857e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * is data. Keep to the MRU of the mux. This path handles framed data 858e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * queued as skbuffs to the DLCI. 859e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 860e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Caller must hold the tx_lock of the mux. 861e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 862e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 863e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsm_dlci_data_output_framed(struct gsm_mux *gsm, 864e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci) 865e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 866e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *msg; 867e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *dp; 868e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len, size; 869e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int last = 0, first = 0; 870e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int overhead = 0; 871e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 872e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* One byte per frame is used for B/F flags */ 873e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->adaption == 4) 874e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox overhead = 1; 875e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 876e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* dlci->skb is locked by tx_lock */ 877e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->skb == NULL) { 878e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->skb = skb_dequeue(&dlci->skb_list); 879e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->skb == NULL) 880e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 881e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox first = 1; 882e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 883e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = dlci->skb->len + overhead; 884e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 885e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* MTU/MRU count only the data bits */ 886e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len > gsm->mtu) { 887e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->adaption == 3) { 888e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Over long frame, bin it */ 889e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree_skb(dlci->skb); 890e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->skb = NULL; 891e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 892e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 893e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = gsm->mtu; 894e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else 895e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox last = 1; 896e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 897e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox size = len + overhead; 898e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); 899e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 900e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: need a timer or something to kick this so it can't 901e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox get stuck with no work outstanding and no buffer free */ 902e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (msg == NULL) 903e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOMEM; 904e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dp = msg->data; 905e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 906e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */ 907e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Flag byte to carry the start/end info */ 908e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox *dp++ = last << 7 | first << 6 | 1; /* EA */ 909e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 910e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 91157f2104f39995bac332ddc492fbf60aa28e0c35eRuss Gorby memcpy(dp, dlci->skb->data, len); 91257f2104f39995bac332ddc492fbf60aa28e0c35eRuss Gorby skb_pull(dlci->skb, len); 913e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox __gsm_data_queue(dlci, msg); 914bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (last) { 915bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby kfree_skb(dlci->skb); 916e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->skb = NULL; 917bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 918e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return size; 919e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 920e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 921e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 922e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_data_sweep - look for data to send 923e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: the GSM mux 924e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 925e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Sweep the GSM mux channels in priority order looking for ones with 926e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * data to send. We could do with optimising this scan a bit. We aim 927e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit 928e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * TX_THRESH_LO we get called again 929e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 930e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * FIXME: We should round robin between groups and in theory you can 931e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * renegotiate DLCI priorities with optional stuff. Needs optimising. 932e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 933e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 934e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_data_sweep(struct gsm_mux *gsm) 935e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 936e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len; 937e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Priority ordering: We should do priority with RR of the groups */ 938e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int i = 1; 939e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 940e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (i < NUM_DLCI) { 941e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci; 942e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 943e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->tx_bytes > TX_THRESH_HI) 944e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 945e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm->dlci[i]; 946e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL || dlci->constipated) { 947e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox i++; 948e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox continue; 949e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 950bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (dlci->adaption < 3 && !dlci->net) 951e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = gsm_dlci_data_output(gsm, dlci); 952e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 953e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len = gsm_dlci_data_output_framed(gsm, dlci); 954e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len < 0) 955e73790a57abc1320b3c3a94da43ae24359687d7cJulia Lawall break; 956e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* DLCI empty - try the next */ 957e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len == 0) 958e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox i++; 959e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 960e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 961e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 962e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 963e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_data_kick - transmit if possible 964e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI to kick 965e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 966e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Transmit data from this DLCI if the queue is empty. We can't rely on 967e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * a tty wakeup except when we filled the pipe so we need to fire off 968e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * new data ourselves in other cases. 969e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 970e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 971e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_data_kick(struct gsm_dlci *dlci) 972e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 973e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long flags; 974e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 975e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_irqsave(&dlci->gsm->tx_lock, flags); 976e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* If we have nothing running then we need to fire up */ 977bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (dlci->gsm->tx_bytes == 0) { 978bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (dlci->net) 979bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby gsm_dlci_data_output_framed(dlci->gsm, dlci); 980bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby else 981bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby gsm_dlci_data_output(dlci->gsm, dlci); 982bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } else if (dlci->gsm->tx_bytes < TX_THRESH_LO) 983e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_data_sweep(dlci->gsm); 984e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); 985e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 986e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 987e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 988e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Control message processing 989e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 990e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 991e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 992e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 993e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_reply - send a response frame to a control 994e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: gsm channel 995e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @cmd: the command to use 996e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: data to follow encoded info 997e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlen: length of data 998e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 999e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Encode up and queue a UI/UIH frame containing our response. 1000e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1001e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1002e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, 1003e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int dlen) 1004e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1005e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *msg; 1006e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); 1007093d804611b9a38fe59753b37c29f840518406a9Ken Mills if (msg == NULL) 1008093d804611b9a38fe59753b37c29f840518406a9Ken Mills return; 1009e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ 1010e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg->data[1] = (dlen << 1) | EA; 1011e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox memcpy(msg->data + 2, data, dlen); 1012e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_data_queue(gsm->dlci[0], msg); 1013e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1014e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1015e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1016e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_process_modem - process received modem status 1017e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: virtual tty bound to the DLCI 1018e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI to affect 1019e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @modem: modem bits (full EA) 1020e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1021e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Used when a modem control message or line state inline in adaption 1022e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * layer 2 is processed. Sort out the local modem state and throttles 1023e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1024e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1025e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, 10267263287af93db4d5cf324a30546f2143419b7900Russ Gorby u32 modem, int clen) 1027e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1028e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int mlines = 0; 10297263287af93db4d5cf324a30546f2143419b7900Russ Gorby u8 brk = 0; 10307263287af93db4d5cf324a30546f2143419b7900Russ Gorby 10317263287af93db4d5cf324a30546f2143419b7900Russ Gorby /* The modem status command can either contain one octet (v.24 signals) 10327263287af93db4d5cf324a30546f2143419b7900Russ Gorby or two octets (v.24 signals + break signals). The length field will 10337263287af93db4d5cf324a30546f2143419b7900Russ Gorby either be 2 or 3 respectively. This is specified in section 10347263287af93db4d5cf324a30546f2143419b7900Russ Gorby 5.4.6.3.7 of the 27.010 mux spec. */ 10357263287af93db4d5cf324a30546f2143419b7900Russ Gorby 10367263287af93db4d5cf324a30546f2143419b7900Russ Gorby if (clen == 2) 10377263287af93db4d5cf324a30546f2143419b7900Russ Gorby modem = modem & 0x7f; 10387263287af93db4d5cf324a30546f2143419b7900Russ Gorby else { 10397263287af93db4d5cf324a30546f2143419b7900Russ Gorby brk = modem & 0x7f; 10407263287af93db4d5cf324a30546f2143419b7900Russ Gorby modem = (modem >> 7) & 0x7f; 10417263287af93db4d5cf324a30546f2143419b7900Russ Gorby }; 1042e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1043e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Flow control/ready to communicate */ 1044e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem & MDM_FC) { 1045e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Need to throttle our output on this device */ 1046e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->constipated = 1; 1047e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1048e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem & MDM_RTC) { 1049e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mlines |= TIOCM_DSR | TIOCM_DTR; 1050e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->constipated = 0; 1051e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_data_kick(dlci); 1052e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1053e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Map modem bits */ 1054e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem & MDM_RTR) 1055e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mlines |= TIOCM_RTS | TIOCM_CTS; 1056e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem & MDM_IC) 1057e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mlines |= TIOCM_RI; 1058e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem & MDM_DV) 1059e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mlines |= TIOCM_CD; 1060e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1061e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Carrier drop -> hangup */ 1062e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty) { 1063e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) 1064e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (!(tty->termios->c_cflag & CLOCAL)) 1065e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_hangup(tty); 1066e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (brk & 0x01) 1067e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_insert_flip_char(tty, 0, TTY_BREAK); 1068e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1069e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->modem_rx = mlines; 1070e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1071e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1072e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1073e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_modem - modem status received 1074e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: GSM channel 1075e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: data following command 1076e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @clen: command length 1077e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1078e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * We have received a modem status control message. This is used by 1079e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the GSM mux protocol to pass virtual modem line status and optionally 1080e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * to indicate break signals. Unpack it, convert to Linux representation 1081e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and if need be stuff a break message down the tty. 1082e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1083e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1084e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) 1085e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1086e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int addr = 0; 1087e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int modem = 0; 1088e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci; 1089e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len = clen; 1090e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *dp = data; 1091e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_struct *tty; 1092e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1093e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (gsm_read_ea(&addr, *dp++) == 0) { 1094e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 1095e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len == 0) 1096e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1097e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1098e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Must be at least one byte following the EA */ 1099e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 1100e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len <= 0) 1101e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1102e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1103e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox addr >>= 1; 1104e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Closed port, or invalid ? */ 1105e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) 1106e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1107e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm->dlci[addr]; 1108e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1109e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (gsm_read_ea(&modem, *dp++) == 0) { 1110e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 1111e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len == 0) 1112e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1113e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1114e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty = tty_port_tty_get(&dlci->port); 11157263287af93db4d5cf324a30546f2143419b7900Russ Gorby gsm_process_modem(tty, dlci, modem, clen); 1116e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty) { 1117e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_wakeup(tty); 1118e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(tty); 1119e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1120e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_MSC, data, clen); 1121e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1122e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1123e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1124e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_rls - remote line status 1125e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: GSM channel 1126e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: data bytes 1127e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @clen: data length 1128e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1129e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * The modem sends us a two byte message on the control channel whenever 1130e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * it wishes to send us an error state from the virtual link. Stuff 1131e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * this into the uplink tty if present 1132e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1133e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1134e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) 1135e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1136e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_struct *tty; 1137e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int addr = 0 ; 1138e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 bits; 1139e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len = clen; 1140e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *dp = data; 1141e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1142e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (gsm_read_ea(&addr, *dp++) == 0) { 1143e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 1144e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len == 0) 1145e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1146e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1147e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Must be at least one byte following ea */ 1148e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 1149e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (len <= 0) 1150e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1151e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox addr >>= 1; 1152e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Closed port, or invalid ? */ 1153e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) 1154e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1155e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* No error ? */ 1156e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox bits = *dp; 1157e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if ((bits & 1) == 0) 1158e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1159e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* See if we have an uplink tty */ 1160e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty = tty_port_tty_get(&gsm->dlci[addr]->port); 1161e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1162e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty) { 1163e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (bits & 2) 1164e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_insert_flip_char(tty, 0, TTY_OVERRUN); 1165e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (bits & 4) 1166e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_insert_flip_char(tty, 0, TTY_PARITY); 1167e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (bits & 8) 1168e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_insert_flip_char(tty, 0, TTY_FRAME); 1169e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_flip_buffer_push(tty); 1170e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(tty); 1171e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1172e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_RLS, data, clen); 1173e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1174e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1175e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_begin_close(struct gsm_dlci *dlci); 1176e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1177e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1178e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_message - DLCI 0 control processing 1179e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our GSM mux 1180e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @command: the command EA 1181e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: data beyond the command/length EAs 1182e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @clen: length 1183e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1184e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Input processor for control messages from the other end of the link. 1185e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Processes the incoming request and queues a response frame or an 1186e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * NSC response if not supported 1187e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1188e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1189e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_message(struct gsm_mux *gsm, unsigned int command, 1190e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *data, int clen) 1191e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1192e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 buf[1]; 1193e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (command) { 1194e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_CLD: { 1195e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = gsm->dlci[0]; 1196e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Modem wishes to close down */ 1197e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci) { 1198e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->dead = 1; 1199e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->dead = 1; 1200e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_close(dlci); 1201e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1202e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1203e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1204e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_TEST: 1205e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Modem wishes to test, reply with the data */ 1206e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_TEST, data, clen); 1207e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1208e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_FCON: 1209e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Modem wants us to STFU */ 1210e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->constipated = 1; 1211e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_FCON, NULL, 0); 1212e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1213e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_FCOFF: 1214e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Modem can accept data again */ 1215e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->constipated = 0; 1216e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); 1217e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Kick the link in case it is idling */ 1218e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_data_kick(gsm); 1219e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1220e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_MSC: 1221e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Out of band modem line change indicator for a DLCI */ 1222e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_modem(gsm, data, clen); 1223e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1224e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_RLS: 1225e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Out of band error reception for a DLCI */ 1226e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_rls(gsm, data, clen); 1227e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1228e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_PSC: 1229e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Modem wishes to enter power saving state */ 1230e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_PSC, NULL, 0); 1231e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1232e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Optional unsupported commands */ 1233e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case CMD_PN: /* Parameter negotiation */ 123425985edcedea6396277003854657b5f3cb31a628Lucas De Marchi case CMD_RPN: /* Remote port negotiation */ 123525985edcedea6396277003854657b5f3cb31a628Lucas De Marchi case CMD_SNC: /* Service negotiation command */ 1236e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox default: 1237e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Reply to bad commands with an NSC */ 1238e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox buf[0] = command; 1239e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_reply(gsm, CMD_NSC, buf, 1); 1240e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1241e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1242e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1243e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1244e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1245e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_response - process a response to our control 1246e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our GSM mux 1247e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @command: the command (response) EA 1248e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: data beyond the command/length EA 1249e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @clen: length 1250e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1251e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Process a response to an outstanding command. We only allow a single 1252e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * control message in flight so this is fairly easy. All the clean up 1253e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * is done by the caller, we just update the fields, flag it as done 1254e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and return 1255e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1256e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1257e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_response(struct gsm_mux *gsm, unsigned int command, 1258e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 *data, int clen) 1259e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1260e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_control *ctrl; 1261e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long flags; 1262e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1263e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_irqsave(&gsm->control_lock, flags); 1264e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1265e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl = gsm->pending_cmd; 1266e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Does the reply match our command */ 1267e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox command |= 1; 1268e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { 1269e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Our command was replied to, kill the retry timer */ 1270e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox del_timer(&gsm->t2_timer); 1271e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->pending_cmd = NULL; 1272e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Rejected by the other end */ 1273e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (command == CMD_NSC) 1274e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->error = -EOPNOTSUPP; 1275e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->done = 1; 1276e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wake_up(&gsm->event); 1277e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1278e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&gsm->control_lock, flags); 1279e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1280e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1281e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 12825f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox * gsm_control_transmit - send control packet 1283e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: gsm mux 1284e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @ctrl: frame to send 1285e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1286e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Send out a pending control command (called under control lock) 1287e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1288e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1289e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl) 1290e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1291ed43b47b29bce303f86e1bff69b6f9924f5afcc4Eric Bénard struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, gsm->ftype); 1292e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (msg == NULL) 1293e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1294e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox msg->data[0] = (ctrl->cmd << 1) | 2 | EA; /* command */ 1295e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox memcpy(msg->data + 1, ctrl->data, ctrl->len); 1296e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_data_queue(gsm->dlci[0], msg); 1297e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1298e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1299e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1300e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_retransmit - retransmit a control frame 1301e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: pointer to our gsm object 1302e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1303e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Called off the T2 timer expiry in order to retransmit control frames 1304e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * that have been lost in the system somewhere. The control_lock protects 1305e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * us from colliding with another sender or a receive completion event. 1306e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * In that situation the timer may still occur in a small window but 1307e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm->pending_cmd will be NULL and we just let the timer expire. 1308e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1309e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1310e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_control_retransmit(unsigned long data) 1311e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1312e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = (struct gsm_mux *)data; 1313e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_control *ctrl; 1314e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long flags; 1315e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_irqsave(&gsm->control_lock, flags); 1316e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl = gsm->pending_cmd; 1317e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (ctrl) { 1318e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->cretries--; 1319e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->cretries == 0) { 1320e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->pending_cmd = NULL; 1321e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->error = -ETIMEDOUT; 1322e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->done = 1; 1323e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&gsm->control_lock, flags); 1324e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wake_up(&gsm->event); 1325e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1326e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1327e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_transmit(gsm, ctrl); 1328e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); 1329e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1330e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&gsm->control_lock, flags); 1331e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1332e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1333e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1334e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_send - send a control frame on DLCI 0 1335e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: the GSM channel 1336e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @command: command to send including CR bit 1337e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: bytes of data (must be kmalloced) 1338e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: length of the block to send 1339e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1340e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Queue and dispatch a control command. Only one command can be 1341e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * active at a time. In theory more can be outstanding but the matching 1342e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gets really complicated so for now stick to one outstanding. 1343e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1344e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1345e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic struct gsm_control *gsm_control_send(struct gsm_mux *gsm, 1346e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int command, u8 *data, int clen) 1347e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1348e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control), 1349e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox GFP_KERNEL); 1350e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned long flags; 1351e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (ctrl == NULL) 1352e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 1353e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxretry: 1354e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wait_event(gsm->event, gsm->pending_cmd == NULL); 1355e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_irqsave(&gsm->control_lock, flags); 1356e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->pending_cmd != NULL) { 1357e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&gsm->control_lock, flags); 1358e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto retry; 1359e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1360e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->cmd = command; 1361e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->data = data; 1362e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl->len = clen; 1363e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->pending_cmd = ctrl; 1364e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->cretries = gsm->n2; 1365e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); 1366e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_transmit(gsm, ctrl); 1367e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock_irqrestore(&gsm->control_lock, flags); 1368e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return ctrl; 1369e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1370e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1371e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1372e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_control_wait - wait for a control to finish 1373e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: GSM mux 1374e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @control: control we are waiting on 1375e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1376e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Waits for the control to complete or time out. Frees any used 1377e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * resources and returns 0 for success, or an error if the remote 1378e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * rejected or ignored the request. 1379e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1380e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1381e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control) 1382e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1383e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int err; 1384e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wait_event(gsm->event, control->done == 1); 1385e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox err = control->error; 1386e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(control); 1387e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return err; 1388e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1389e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1390e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1391e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 1392e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * DLCI level handling: Needs krefs 1393e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1394e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1395e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 1396e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * State transitions and timers 1397e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1398e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1399e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1400e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_close - a DLCI has closed 1401e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI that closed 1402e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1403e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Perform processing when moving a DLCI into closed state. If there 1404e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * is an attached tty this is hung up 1405e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1406e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1407e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_close(struct gsm_dlci *dlci) 1408e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1409e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox del_timer(&dlci->t1); 1410e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (debug & 8) 14115f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug("DLCI %d goes closed.\n", dlci->addr); 1412e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->state = DLCI_CLOSED; 1413e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->addr != 0) { 1414e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_struct *tty = tty_port_tty_get(&dlci->port); 1415e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty) { 1416e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_hangup(tty); 1417e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(tty); 1418e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1419e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfifo_reset(dlci->fifo); 1420e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else 1421e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->gsm->dead = 1; 1422e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wake_up(&dlci->gsm->event); 1423e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* A DLCI 0 close is a MUX termination so we need to kick that 1424e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox back to userspace somehow */ 1425e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1426e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1427e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1428e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_open - a DLCI has opened 1429e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI that opened 1430e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1431e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Perform processing when moving a DLCI into open state. 1432e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1433e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1434e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_open(struct gsm_dlci *dlci) 1435e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1436e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Note that SABM UA .. SABM UA first UA lost can mean that we go 1437e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox open -> open */ 1438e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox del_timer(&dlci->t1); 1439e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* This will let a tty open continue */ 1440e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->state = DLCI_OPEN; 1441e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (debug & 8) 14425f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug("DLCI %d goes open.\n", dlci->addr); 1443e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wake_up(&dlci->gsm->event); 1444e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1445e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1446e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1447e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_t1 - T1 timer expiry 1448e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI that opened 1449e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1450e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * The T1 timer handles retransmits of control frames (essentially of 1451e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * SABM and DISC). We resend the command until the retry count runs out 1452e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * in which case an opening port goes back to closed and a closing port 1453e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * is simply put into closed state (any further frames from the other 1454e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * end will get a DM response) 1455e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1456e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1457e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_t1(unsigned long data) 1458e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1459e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = (struct gsm_dlci *)data; 1460e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = dlci->gsm; 1461e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1462e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (dlci->state) { 1463e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DLCI_OPENING: 1464e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->retries--; 1465e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->retries) { 1466e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_command(dlci->gsm, dlci->addr, SABM|PF); 1467e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); 1468e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else 1469e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_close(dlci); 1470e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1471e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DLCI_CLOSING: 1472e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->retries--; 1473e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->retries) { 1474e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_command(dlci->gsm, dlci->addr, DISC|PF); 1475e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); 1476e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else 1477e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_close(dlci); 1478e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1479e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1480e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1481e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1482e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1483e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_begin_open - start channel open procedure 1484e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI to open 1485e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1486e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Commence opening a DLCI from the Linux side. We issue SABM messages 1487e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * to the modem which should then reply with a UA, at which point we 1488e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * will move into open state. Opening is done asynchronously with retry 1489e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * running off timers and the responses. 1490e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1491e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1492e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_begin_open(struct gsm_dlci *dlci) 1493e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1494e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = dlci->gsm; 1495e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING) 1496e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1497e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->retries = gsm->n2; 1498e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->state = DLCI_OPENING; 1499e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_command(dlci->gsm, dlci->addr, SABM|PF); 1500e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); 1501e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1502e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1503e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1504e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_begin_close - start channel open procedure 1505e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI to open 1506e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1507e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Commence closing a DLCI from the Linux side. We issue DISC messages 1508e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * to the modem which should then reply with a UA, at which point we 1509e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * will move into closed state. Closing is done asynchronously with retry 1510e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * off timers. We may also receive a DM reply from the other end which 1511e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * indicates the channel was already closed. 1512e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1513e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1514e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_begin_close(struct gsm_dlci *dlci) 1515e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1516e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = dlci->gsm; 1517e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING) 1518e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1519e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->retries = gsm->n2; 1520e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->state = DLCI_CLOSING; 1521e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_command(dlci->gsm, dlci->addr, DISC|PF); 1522e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); 1523e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1524e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1525e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1526e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_data - data arrived 1527e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: channel 1528e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: block of bytes received 1529e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: length of received block 1530e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1531e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * A UI or UIH frame has arrived which contains data for a channel 1532e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * other than the control channel. If the relevant virtual tty is 1533e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * open we shovel the bits down it, if not we drop them. 1534e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1535e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 15367263287af93db4d5cf324a30546f2143419b7900Russ Gorbystatic void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int clen) 1537e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1538e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* krefs .. */ 1539e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_port *port = &dlci->port; 1540e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_struct *tty = tty_port_tty_get(port); 1541e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int modem = 0; 15427263287af93db4d5cf324a30546f2143419b7900Russ Gorby int len = clen; 1543e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1544e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (debug & 16) 15455f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug("%d bytes for tty %p\n", len, tty); 1546e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty) { 1547e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (dlci->adaption) { 15485f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox /* Unsupported types */ 15495f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox /* Packetised interruptible data */ 15505f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case 4: 15515f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox break; 15525f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox /* Packetised uininterruptible voice/data */ 15535f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case 3: 15545f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox break; 15555f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox /* Asynchronous serial with line state in each frame */ 15565f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case 2: 15575f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox while (gsm_read_ea(&modem, *data++) == 0) { 15585f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox len--; 15595f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox if (len == 0) 15605f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox return; 15615f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox } 15627263287af93db4d5cf324a30546f2143419b7900Russ Gorby gsm_process_modem(tty, dlci, modem, clen); 15635f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox /* Line state will go via DLCI 0 controls only */ 15645f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox case 1: 15655f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox default: 15665f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox tty_insert_flip_string(tty, data, len); 15675f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox tty_flip_buffer_push(tty); 1568e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1569e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(tty); 1570e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1571e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1572e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1573e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1574e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_control - data arrived on control channel 1575e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: channel 1576e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: block of bytes received 1577e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: length of received block 1578e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1579e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * A UI or UIH frame has arrived which contains data for DLCI 0 the 1580e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * control channel. This should contain a command EA followed by 1581e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * control data bytes. The command EA contains a command/response bit 1582e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and we divide up the work accordingly. 1583e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1584e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1585e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dlci_command(struct gsm_dlci *dlci, u8 *data, int len) 1586e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1587e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* See what command is involved */ 1588e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int command = 0; 1589e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox while (len-- > 0) { 1590e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_read_ea(&command, *data++) == 1) { 1591e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int clen = *data++; 1592e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len--; 1593e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: this is properly an EA */ 1594e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox clen >>= 1; 1595e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Malformed command ? */ 1596e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (clen > len) 1597e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1598e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (command & 1) 1599e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_message(dlci->gsm, command, 1600e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox data, clen); 1601e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 1602e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_control_response(dlci->gsm, command, 1603e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox data, clen); 1604e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1605e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1606e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1607e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1608e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1609e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 1610e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Allocate/Free DLCI channels 1611e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1612e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1613e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1614e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_dlci_alloc - allocate a DLCI 1615e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: GSM mux 1616e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @addr: address of the DLCI 1617e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1618e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Allocate and install a new DLCI object into the GSM mux. 1619e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1620e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * FIXME: review locking races 1621e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1622e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1623e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) 1624e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1625e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC); 1626e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 1627e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 1628e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_init(&dlci->lock); 16296ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_init(&dlci->ref); 1630bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_init(&dlci->mutex); 1631e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->fifo = &dlci->_fifo; 1632e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { 1633e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(dlci); 1634e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 1635e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1636e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1637e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox skb_queue_head_init(&dlci->skb_list); 1638e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox init_timer(&dlci->t1); 1639e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->t1.function = gsm_dlci_t1; 1640e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->t1.data = (unsigned long)dlci; 1641e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_port_init(&dlci->port); 1642e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->port.ops = &gsm_port_ops; 1643e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->gsm = gsm; 1644e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->addr = addr; 1645e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->adaption = gsm->adaption; 1646e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->state = DLCI_CLOSED; 1647e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (addr) 1648e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->data = gsm_dlci_data; 1649e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 1650e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->data = gsm_dlci_command; 1651e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->dlci[addr] = dlci; 1652e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return dlci; 1653e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1654e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1655e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 16566ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * gsm_dlci_free - free DLCI 16576ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * @dlci: DLCI to free 16586ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * 16596ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * Free up a DLCI. 16606ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * 16616ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * Can sleep. 16626ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby */ 16636ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic void gsm_dlci_free(struct kref *ref) 16646ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 16656ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby struct gsm_dlci *dlci = container_of(ref, struct gsm_dlci, ref); 16666ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 16676ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby del_timer_sync(&dlci->t1); 16686ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby dlci->gsm->dlci[dlci->addr] = NULL; 16696ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kfifo_free(dlci->fifo); 16706ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby while ((dlci->skb = skb_dequeue(&dlci->skb_list))) 16716ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kfree_skb(dlci->skb); 16726ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kfree(dlci); 16736ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 16746ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 16756ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic inline void dlci_get(struct gsm_dlci *dlci) 16766ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 16776ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_get(&dlci->ref); 16786ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 16796ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 16806ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic inline void dlci_put(struct gsm_dlci *dlci) 16816ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 16826ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_put(&dlci->ref, gsm_dlci_free); 16836ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 16846ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 16856ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby/** 16866ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * gsm_dlci_release - release DLCI 1687e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @dlci: DLCI to destroy 1688e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 16896ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * Release a DLCI. Actual free is deferred until either 16906ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * mux is closed or tty is closed - whichever is last. 1691e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1692e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Can sleep. 1693e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 16946ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic void gsm_dlci_release(struct gsm_dlci *dlci) 1695e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1696e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_struct *tty = tty_port_tty_get(&dlci->port); 1697e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty) { 1698e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_vhangup(tty); 1699e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(tty); 1700e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 17016ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby dlci_put(dlci); 1702e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1703e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1704e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 1705e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * LAPBish link layer logic 1706e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1707e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1708e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1709e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_queue - a GSM frame is ready to process 1710e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: pointer to our gsm mux 1711e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1712e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * At this point in time a frame has arrived and been demangled from 1713e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * the line encoding. All the differences between the encodings have 1714e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * been handled below us and the frame is unpacked into the structures. 1715e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * The fcs holds the header FCS but any data FCS must be added here. 1716e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1717e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1718e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_queue(struct gsm_mux *gsm) 1719e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1720e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci; 1721e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 cr; 1722e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int address; 1723e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* We have to sneak a look at the packet body to do the FCS. 1724e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox A somewhat layering violation in the spec */ 1725e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1726e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if ((gsm->control & ~PF) == UI) 1727e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); 17289db4e4381a8e881ff65a5d3400bfa471f84217e7Mikhail Kshevetskiy if (gsm->encoding == 0){ 17299db4e4381a8e881ff65a5d3400bfa471f84217e7Mikhail Kshevetskiy /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. 17309db4e4381a8e881ff65a5d3400bfa471f84217e7Mikhail Kshevetskiy In this case it contain the last piece of data 17319db4e4381a8e881ff65a5d3400bfa471f84217e7Mikhail Kshevetskiy required to generate final CRC */ 17329db4e4381a8e881ff65a5d3400bfa471f84217e7Mikhail Kshevetskiy gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); 17339db4e4381a8e881ff65a5d3400bfa471f84217e7Mikhail Kshevetskiy } 1734e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->fcs != GOOD_FCS) { 1735e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->bad_fcs++; 1736e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (debug & 4) 17375f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug("BAD FCS %02x\n", gsm->fcs); 1738e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1739e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1740e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox address = gsm->address >> 1; 1741e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (address >= NUM_DLCI) 1742e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto invalid; 1743e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1744e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cr = gsm->address & 1; /* C/R bit */ 1745e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1746e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len); 1747e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1748e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox cr ^= 1 - gsm->initiator; /* Flip so 1 always means command */ 1749e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm->dlci[address]; 1750e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1751e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (gsm->control) { 1752e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case SABM|PF: 1753e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (cr == 0) 1754e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto invalid; 1755e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 1756e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm_dlci_alloc(gsm, address); 1757e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 1758e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1759e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->dead) 1760e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_response(gsm, address, DM); 1761e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else { 1762e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_response(gsm, address, UA); 1763e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_open(dlci); 1764e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1765e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1766e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DISC|PF: 1767e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (cr == 0) 1768e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto invalid; 1769e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL || dlci->state == DLCI_CLOSED) { 1770e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_response(gsm, address, DM); 1771e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1772e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1773e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Real close complete */ 1774e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_response(gsm, address, UA); 1775e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_close(dlci); 1776e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1777e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UA: 1778e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UA|PF: 1779e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (cr == 0 || dlci == NULL) 1780e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1781e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (dlci->state) { 1782e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DLCI_CLOSING: 1783e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_close(dlci); 1784e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1785e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DLCI_OPENING: 1786e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_open(dlci); 1787e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1788e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1789e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1790e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DM: /* DM can be valid unsolicited */ 1791e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case DM|PF: 1792e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (cr) 1793e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto invalid; 1794e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 1795e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1796e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_close(dlci); 1797e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1798e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UI: 1799e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UI|PF: 1800e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UIH: 1801e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case UIH|PF: 1802e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#if 0 1803e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (cr) 1804e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto invalid; 1805e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#endif 1806e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL || dlci->state != DLCI_OPEN) { 1807e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_command(gsm, address, DM|PF); 1808e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1809e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1810e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->data(dlci, gsm->buf, gsm->len); 1811e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1812e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox default: 1813e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox goto invalid; 1814e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1815e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1816e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxinvalid: 1817e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->malformed++; 1818e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1819e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1820e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1821e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1822e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1823e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm0_receive - perform processing for non-transparency 1824e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: gsm data for this ldisc instance 1825e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @c: character 1826e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1827e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Receive bytes in gsm mode 0 1828e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1829e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1830e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm0_receive(struct gsm_mux *gsm, unsigned char c) 1831e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1832c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox unsigned int len; 1833c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox 1834e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (gsm->state) { 1835e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_SEARCH: /* SOF marker */ 1836e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c == GSM0_SOF) { 1837e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_ADDRESS; 1838e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->address = 0; 1839e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->len = 0; 1840e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = INIT_FCS; 1841e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1842c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox break; 1843c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox case GSM_ADDRESS: /* Address EA */ 1844e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, c); 1845e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_read_ea(&gsm->address, c)) 1846e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_CONTROL; 1847e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1848e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_CONTROL: /* Control Byte */ 1849e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, c); 1850e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->control = c; 1851c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_LEN0; 1852e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1853c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox case GSM_LEN0: /* Length EA */ 1854e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, c); 1855e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_read_ea(&gsm->len, c)) { 1856e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->len > gsm->mru) { 1857e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->bad_size++; 1858e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_SEARCH; 1859e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1860e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1861e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->count = 0; 1862c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox if (!gsm->len) 1863c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_FCS; 1864c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox else 1865c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_DATA; 1866c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox break; 1867e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1868c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_LEN1; 1869c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox break; 1870c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox case GSM_LEN1: 1871c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, c); 1872c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox len = c; 1873c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->len |= len << 7; 1874c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox if (gsm->len > gsm->mru) { 1875c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->bad_size++; 1876c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_SEARCH; 1877c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox break; 1878e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1879c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->count = 0; 1880c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox if (!gsm->len) 1881c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_FCS; 1882c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox else 1883c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_DATA; 1884e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1885e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_DATA: /* Data */ 1886e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->buf[gsm->count++] = c; 1887e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->count == gsm->len) 1888e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_FCS; 1889e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1890e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_FCS: /* FCS follows the packet */ 1891c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->received_fcs = c; 1892e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_queue(gsm); 1893c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_SSOF; 1894c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox break; 1895c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox case GSM_SSOF: 1896c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox if (c == GSM0_SOF) { 1897c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox gsm->state = GSM_SEARCH; 1898c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox break; 1899c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox } 1900e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1901e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1902e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1903e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1904e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1905c2f2f0000bb69f067fea12624272e6a58a811702Alan Cox * gsm1_receive - perform processing for non-transparency 1906e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: gsm data for this ldisc instance 1907e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @c: character 1908e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1909e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Receive bytes in mode 1 (Advanced option) 1910e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1911e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1912e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm1_receive(struct gsm_mux *gsm, unsigned char c) 1913e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1914e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c == GSM1_SOF) { 1915e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* EOF is only valid in frame if we have got to the data state 1916e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox and received at least one byte (the FCS) */ 1917e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->state == GSM_DATA && gsm->count) { 1918e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Extract the FCS */ 1919e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->count--; 1920e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); 1921e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->len = gsm->count; 1922e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_queue(gsm); 1923e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_START; 1924e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1925e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1926e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Any partial frame was a runt so go back to start */ 1927e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->state != GSM_START) { 1928e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->malformed++; 1929e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_START; 1930e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1931e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* A SOF in GSM_START means we are still reading idling or 1932e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox framing bytes */ 1933e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1934e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1935e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1936e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c == GSM1_ESCAPE) { 1937e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->escape = 1; 1938e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1939e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1940e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1941e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Only an unescaped SOF gets us out of GSM search */ 1942e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->state == GSM_SEARCH) 1943e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 1944e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1945e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->escape) { 1946e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c ^= GSM1_ESCAPE_BITS; 1947e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->escape = 0; 1948e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1949e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (gsm->state) { 1950e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_START: /* First byte after SOF */ 1951e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->address = 0; 1952e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_ADDRESS; 1953e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = INIT_FCS; 1954e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Drop through */ 1955e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_ADDRESS: /* Address continuation */ 1956e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, c); 1957e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_read_ea(&gsm->address, c)) 1958e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_CONTROL; 1959e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1960e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_CONTROL: /* Control Byte */ 1961e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->fcs = gsm_fcs_add(gsm->fcs, c); 1962e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->control = c; 1963e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->count = 0; 1964e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_DATA; 1965e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1966e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_DATA: /* Data */ 19675f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox if (gsm->count > gsm->mru) { /* Allow one for the FCS */ 1968e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_OVERRUN; 1969e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->bad_size++; 1970e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } else 1971e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->buf[gsm->count++] = c; 1972e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1973e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ 1974e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 1975e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 1976e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1977e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1978e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1979e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_error - handle tty error 1980e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: ldisc data 1981e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: byte received (may be invalid) 1982e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @flag: error received 1983e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1984e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Handle an error in the receipt of data for a frame. Currently we just 1985e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * go back to hunting for a SOF. 1986e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 1987e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * FIXME: better diagnostics ? 1988e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 1989e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1990e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_error(struct gsm_mux *gsm, 1991e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned char data, unsigned char flag) 1992e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 1993e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->state = GSM_SEARCH; 1994e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->io_error++; 1995e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 1996e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 1997e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 1998e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_cleanup_mux - generic GSM protocol cleanup 1999e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our mux 2000e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2001e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Clean up the bits of the mux which are the same for all framing 2002e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * protocols. Remove the mux from the mux table, stop all the timers 2003e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * and then shut down each device hanging up the channels as we go. 2004e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2005e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2006e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxvoid gsm_cleanup_mux(struct gsm_mux *gsm) 2007e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2008e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int i; 2009e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = gsm->dlci[0]; 2010e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_msg *txq; 2011f17141fdd407de78379222dd59d6f161437db4c8Alan Cox struct gsm_control *gc; 2012e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2013e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->dead = 1; 2014e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2015e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock(&gsm_mux_lock); 2016e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox for (i = 0; i < MAX_MUX; i++) { 2017e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_mux[i] == gsm) { 2018e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_mux[i] = NULL; 2019e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 2020e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2021e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2022e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock(&gsm_mux_lock); 2023e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox WARN_ON(i == MAX_MUX); 2024e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2025f17141fdd407de78379222dd59d6f161437db4c8Alan Cox /* In theory disconnecting DLCI 0 is sufficient but for some 2026f17141fdd407de78379222dd59d6f161437db4c8Alan Cox modems this is apparently not the case. */ 2027f17141fdd407de78379222dd59d6f161437db4c8Alan Cox if (dlci) { 2028f17141fdd407de78379222dd59d6f161437db4c8Alan Cox gc = gsm_control_send(gsm, CMD_CLD, NULL, 0); 2029f17141fdd407de78379222dd59d6f161437db4c8Alan Cox if (gc) 2030f17141fdd407de78379222dd59d6f161437db4c8Alan Cox gsm_control_wait(gsm, gc); 2031f17141fdd407de78379222dd59d6f161437db4c8Alan Cox } 2032e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox del_timer_sync(&gsm->t2_timer); 2033e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Now we are sure T2 has stopped */ 2034e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci) { 2035e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->dead = 1; 2036e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_close(dlci); 2037e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wait_event_interruptible(gsm->event, 2038e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->state == DLCI_CLOSED); 2039e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2040e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Free up any link layer users */ 2041e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox for (i = 0; i < NUM_DLCI; i++) 2042e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->dlci[i]) 20436ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby gsm_dlci_release(gsm->dlci[i]); 2044e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Now wipe the queues */ 2045e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { 2046e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_head = txq->next; 2047e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(txq); 2048e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2049e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tx_tail = NULL; 2050e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2051e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan CoxEXPORT_SYMBOL_GPL(gsm_cleanup_mux); 2052e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2053e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2054e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_activate_mux - generic GSM setup 2055e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our mux 2056e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2057e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Set up the bits of the mux which are the same for all framing 2058e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * protocols. Add the mux to the mux table so it can be opened and 2059e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * finally kick off connecting to DLCI 0 on the modem. 2060e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2061e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2062e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxint gsm_activate_mux(struct gsm_mux *gsm) 2063e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2064e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci; 2065e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int i = 0; 2066e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2067e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox init_timer(&gsm->t2_timer); 2068e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->t2_timer.function = gsm_control_retransmit; 2069e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->t2_timer.data = (unsigned long)gsm; 2070e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox init_waitqueue_head(&gsm->event); 2071e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_init(&gsm->control_lock); 2072e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_init(&gsm->tx_lock); 2073e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2074e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->encoding == 0) 2075e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->receive = gsm0_receive; 2076e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 2077e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->receive = gsm1_receive; 2078e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->error = gsm_error; 2079e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2080e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock(&gsm_mux_lock); 2081e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox for (i = 0; i < MAX_MUX; i++) { 2082e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_mux[i] == NULL) { 2083d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby gsm->num = i; 2084e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_mux[i] = gsm; 2085e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 2086e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2087e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2088e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_unlock(&gsm_mux_lock); 2089e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (i == MAX_MUX) 2090e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EBUSY; 2091e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2092e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm_dlci_alloc(gsm, 0); 2093e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 2094e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOMEM; 2095e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->dead = 0; /* Tty opens are now permissible */ 2096e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 2097e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2098e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan CoxEXPORT_SYMBOL_GPL(gsm_activate_mux); 2099e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2100e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2101e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_free_mux - free up a mux 2102e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @mux: mux to free 2103e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 21046ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * Dispose of allocated resources for a dead mux 2105e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2106e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxvoid gsm_free_mux(struct gsm_mux *gsm) 2107e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2108e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(gsm->txframe); 2109e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(gsm->buf); 2110e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(gsm); 2111e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2112e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan CoxEXPORT_SYMBOL_GPL(gsm_free_mux); 2113e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2114e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 21156ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * gsm_free_muxr - free up a mux 21166ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * @mux: mux to free 21176ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * 21186ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby * Dispose of allocated resources for a dead mux 21196ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby */ 21206ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic void gsm_free_muxr(struct kref *ref) 21216ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 21226ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby struct gsm_mux *gsm = container_of(ref, struct gsm_mux, ref); 21236ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby gsm_free_mux(gsm); 21246ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 21256ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 21266ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic inline void mux_get(struct gsm_mux *gsm) 21276ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 21286ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_get(&gsm->ref); 21296ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 21306ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 21316ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic inline void mux_put(struct gsm_mux *gsm) 21326ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 21336ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_put(&gsm->ref, gsm_free_muxr); 21346ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 21356ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 21366ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby/** 2137e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsm_alloc_mux - allocate a mux 2138e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2139e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Creates a new mux ready for activation. 2140e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2141e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2142e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstruct gsm_mux *gsm_alloc_mux(void) 2143e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2144e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); 2145e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm == NULL) 2146e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 2147e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL); 2148e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->buf == NULL) { 2149e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(gsm); 2150e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 2151e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2152e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL); 2153e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->txframe == NULL) { 2154e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(gsm->buf); 2155e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfree(gsm); 2156e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return NULL; 2157e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2158e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_init(&gsm->lock); 21596ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_init(&gsm->ref); 2160e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2161e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->t1 = T1; 2162e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->t2 = T2; 2163e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->n2 = N2; 2164e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->ftype = UIH; 2165e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->adaption = 1; 2166e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->encoding = 1; 2167e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ 2168e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->mtu = 64; 2169e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->dead = 1; /* Avoid early tty opens */ 2170e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2171e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsm; 2172e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2173e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan CoxEXPORT_SYMBOL_GPL(gsm_alloc_mux); 2174e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2175e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2176e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_output - write to link 2177e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our mux 2178e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @data: bytes to output 2179e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @len: size 2180e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2181e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Write a block of data from the GSM mux to the data channel. This 2182e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * will eventually be serialized from above but at the moment isn't. 2183e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2184e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2185e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) 2186e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2187e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty_write_room(gsm->tty) < len) { 2188e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags); 2189e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOSPC; 2190e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 21910a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches if (debug & 4) 21920a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches print_hex_dump_bytes("gsmld_output: ", DUMP_PREFIX_OFFSET, 21930a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches data, len); 2194e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tty->ops->write(gsm->tty, data, len); 2195e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return len; 2196e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2197e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2198e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2199e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_attach_gsm - mode set up 2200e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: our tty structure 2201e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: our mux 2202e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2203e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Set up the MUX for basic mode and commence connecting to the 2204e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * modem. Currently called from the line discipline set up but 2205e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * will need moving to an ioctl path. 2206e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2207e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2208e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) 2209e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2210d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby int ret, i; 2211d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby int base = gsm->num << 6; /* Base for this MUX */ 2212e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2213e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tty = tty_kref_get(tty); 2214e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->output = gsmld_output; 2215e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ret = gsm_activate_mux(gsm); 2216e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (ret != 0) 2217e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(gsm->tty); 2218d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby else { 2219d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby /* Don't register device 0 - this is the control channel and not 2220d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby a usable tty interface */ 2221d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby for (i = 1; i < NUM_DLCI; i++) 2222d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby tty_register_device(gsm_tty_driver, base + i, NULL); 2223d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby } 2224e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return ret; 2225e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2226e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2227e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2228e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2229e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_detach_gsm - stop doing 0710 mux 223070f23fd66bc821a0e99647f70a809e277cc93c4cJustin P. Mattock * @tty: tty attached to the mux 2231e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @gsm: mux 2232e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2233e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Shutdown and then clean up the resources used by the line discipline 2234e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2235e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2236e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) 2237e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2238d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby int i; 2239d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby int base = gsm->num << 6; /* Base for this MUX */ 2240d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby 2241e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox WARN_ON(tty != gsm->tty); 2242d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby for (i = 1; i < NUM_DLCI; i++) 2243d50f6dcaf22a3234a65ae4f6087173e66b7fff56Russ Gorby tty_unregister_device(gsm_tty_driver, base + i); 2244e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_cleanup_mux(gsm); 2245e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_kref_put(gsm->tty); 2246e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->tty = NULL; 2247e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2248e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 224955db4c64eddf37e31279ec15fe90314713bc9cfaLinus Torvaldsstatic void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, 225055db4c64eddf37e31279ec15fe90314713bc9cfaLinus Torvalds char *fp, int count) 2251e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2252e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = tty->disc_data; 2253e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox const unsigned char *dp; 2254e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox char *f; 2255e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int i; 2256e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox char buf[64]; 2257e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox char flags; 2258e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 22590a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches if (debug & 4) 22600a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches print_hex_dump_bytes("gsmld_receive: ", DUMP_PREFIX_OFFSET, 22610a77c4f9d451a6652f5536548df1b75f4b5b836cJoe Perches cp, count); 2262e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2263e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox for (i = count, dp = cp, f = fp; i; i--, dp++) { 2264e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox flags = *f++; 2265e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (flags) { 2266e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case TTY_NORMAL: 2267e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->receive(gsm, *dp); 2268e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 2269e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case TTY_OVERRUN: 2270e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case TTY_BREAK: 2271e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case TTY_PARITY: 2272e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case TTY_FRAME: 2273e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->error(gsm, *dp, flags); 2274e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 2275e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox default: 22765f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox WARN_ONCE("%s: unknown flag %d\n", 2277e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_name(tty, buf), flags); 2278e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox break; 2279e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2280e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2281e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FASYNC if needed ? */ 2282e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* If clogged call tty_throttle(tty); */ 2283e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2284e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2285e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2286e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_chars_in_buffer - report available bytes 2287e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: tty device 2288e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2289e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Report the number of characters buffered to be delivered to user 2290e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * at this instant in time. 2291e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2292e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Locking: gsm lock 2293e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2294e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2295e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic ssize_t gsmld_chars_in_buffer(struct tty_struct *tty) 2296e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2297e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 2298e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2299e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2300e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2301e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_flush_buffer - clean input queue 2302e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: terminal device 2303e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2304e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Flush the input buffer. Called when the line discipline is 2305e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * being closed, when the tty layer wants the buffer flushed (eg 2306e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * at hangup). 2307e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2308e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2309e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmld_flush_buffer(struct tty_struct *tty) 2310e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2311e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2312e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2313e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2314e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_close - close the ldisc for this tty 2315e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: device 2316e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2317e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Called from the terminal layer when this line discipline is 2318e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * being shut down, either because of a close or becsuse of a 2319e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * discipline change. The function will not be called while other 2320e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * ldisc methods are in progress. 2321e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2322e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2323e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmld_close(struct tty_struct *tty) 2324e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2325e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = tty->disc_data; 2326e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2327e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsmld_detach_gsm(tty, gsm); 2328e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2329e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsmld_flush_buffer(tty); 2330e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Do other clean up here */ 23316ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby mux_put(gsm); 2332e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2333e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2334e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2335e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_open - open an ldisc 2336e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: terminal to open 2337e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2338e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Called when this line discipline is being attached to the 2339e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * terminal device. Can sleep. Called serialized so that no 2340e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * other events will occur in parallel. No further open will occur 2341e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * until a close. 2342e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2343e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2344e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmld_open(struct tty_struct *tty) 2345e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2346e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm; 2347e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2348e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty->ops->write == NULL) 2349e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 2350e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2351e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Attach our ldisc data */ 2352e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm = gsm_alloc_mux(); 2353e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm == NULL) 2354e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOMEM; 2355e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2356e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty->disc_data = gsm; 2357e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty->receive_room = 65536; 2358e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2359e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Attach the initial passive connection */ 2360e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->encoding = 1; 2361e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsmld_attach_gsm(tty, gsm); 2362e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2363e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2364e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2365e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_write_wakeup - asynchronous I/O notifier 2366e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: tty device 2367e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2368e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Required for the ptys, serial driver etc. since processes 2369e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * that attach themselves to the master and rely on ASYNC 2370e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * IO must be woken up 2371e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2372e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2373e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmld_write_wakeup(struct tty_struct *tty) 2374e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2375e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = tty->disc_data; 2376328be395a396b1333b56e04571365dc614c96e46Dan Carpenter unsigned long flags; 2377e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2378e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Queue poll */ 2379e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 2380e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_data_kick(gsm); 2381328be395a396b1333b56e04571365dc614c96e46Dan Carpenter if (gsm->tx_bytes < TX_THRESH_LO) { 2382328be395a396b1333b56e04571365dc614c96e46Dan Carpenter spin_lock_irqsave(&gsm->tx_lock, flags); 2383e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_data_sweep(gsm); 2384328be395a396b1333b56e04571365dc614c96e46Dan Carpenter spin_unlock_irqrestore(&gsm->tx_lock, flags); 2385328be395a396b1333b56e04571365dc614c96e46Dan Carpenter } 2386e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2387e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2388e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2389e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_read - read function for tty 2390e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: tty device 2391e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @file: file object 2392e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @buf: userspace buffer pointer 2393e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @nr: size of I/O 2394e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2395e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Perform reads for the line discipline. We are guaranteed that the 2396e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * line discipline will not be closed under us but we may get multiple 2397e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * parallel readers and must handle this ourselves. We may also get 2398e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * a hangup. Always called in user context, may sleep. 2399e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2400e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * This code must be sure never to sleep through a hangup. 2401e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2402e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2403e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic ssize_t gsmld_read(struct tty_struct *tty, struct file *file, 2404e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned char __user *buf, size_t nr) 2405e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2406e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EOPNOTSUPP; 2407e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2408e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2409e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2410e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_write - write function for tty 2411e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: tty device 2412e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @file: file object 2413e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @buf: userspace buffer pointer 2414e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @nr: size of I/O 2415e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2416e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Called when the owner of the device wants to send a frame 2417e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * itself (or some other control data). The data is transferred 2418e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * as-is and must be properly framed and checksummed as appropriate 2419e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * by userspace. Frames are either sent whole or not at all as this 2420e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * avoids pain user side. 2421e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2422e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2423e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic ssize_t gsmld_write(struct tty_struct *tty, struct file *file, 2424e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox const unsigned char *buf, size_t nr) 2425e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2426e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int space = tty_write_room(tty); 2427e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (space >= nr) 2428e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return tty->ops->write(tty, buf, nr); 2429e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 2430e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOBUFS; 2431e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2432e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2433e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/** 2434e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * gsmld_poll - poll method for N_GSM0710 2435e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @tty: terminal device 2436e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @file: file accessing it 2437e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * @wait: poll table 2438e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2439e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Called when the line discipline is asked to poll() for data or 2440e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * for special events. This code is not serialized with respect to 2441e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * other events save open/close. 2442e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * 2443e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * This code must be sure never to sleep through a hangup. 2444e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Called without the kernel lock held - fine 2445e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2446e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2447e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic unsigned int gsmld_poll(struct tty_struct *tty, struct file *file, 2448e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox poll_table *wait) 2449e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2450e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int mask = 0; 2451e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = tty->disc_data; 2452e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2453e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox poll_wait(file, &tty->read_wait, wait); 2454e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox poll_wait(file, &tty->write_wait, wait); 2455e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty_hung_up_p(file)) 2456e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mask |= POLLHUP; 2457e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0) 2458e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mask |= POLLOUT | POLLWRNORM; 2459e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->dead) 2460e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox mask |= POLLHUP; 2461e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return mask; 2462e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2463e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2464e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm, 2465e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_config *c) 2466e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2467e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int need_close = 0; 2468e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int need_restart = 0; 2469e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2470e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Stuff we don't support yet - UI or I frame transport, windowing */ 24715f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox if ((c->adaption != 1 && c->adaption != 2) || c->k) 2472e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EOPNOTSUPP; 2473e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Check the MRU/MTU range looks sane */ 2474e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8) 2475e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 2476e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->n2 < 3) 2477e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 2478e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->encapsulation > 1) /* Basic, advanced, no I */ 2479e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 2480e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->initiator > 1) 2481e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 2482e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->i == 0 || c->i > 2) /* UIH and UI only */ 2483e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 2484e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* 2485e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * See what is needed for reconfiguration 2486e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2487e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2488e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Timing fields */ 2489e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->t1 != 0 && c->t1 != gsm->t1) 2490e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_restart = 1; 2491e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->t2 != 0 && c->t2 != gsm->t2) 2492e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_restart = 1; 2493e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->encapsulation != gsm->encoding) 2494e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_restart = 1; 2495e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->adaption != gsm->adaption) 2496e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_restart = 1; 2497e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Requires care */ 2498e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->initiator != gsm->initiator) 2499e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_close = 1; 2500e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->mru != gsm->mru) 2501e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_restart = 1; 2502e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->mtu != gsm->mtu) 2503e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox need_restart = 1; 2504e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2505e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* 2506e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Close down what is needed, restart and initiate the new 2507e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * configuration 2508e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2509e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2510e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (need_close || need_restart) { 2511e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_close(gsm->dlci[0]); 2512e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* This will timeout if the link is down due to N2 expiring */ 2513e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox wait_event_interruptible(gsm->event, 2514e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->dlci[0]->state == DLCI_CLOSED); 2515e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (signal_pending(current)) 2516e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINTR; 2517e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2518e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (need_restart) 2519e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_cleanup_mux(gsm); 2520e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2521e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->initiator = c->initiator; 2522e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->mru = c->mru; 252391f78f36694b8748fda855b1f9e3614b027a744fKen Mills gsm->mtu = c->mtu; 2524e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->encoding = c->encapsulation; 2525e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->adaption = c->adaption; 2526820e62ef3d39ba9414dd9b87dba2eedd7e403e53Ken Mills gsm->n2 = c->n2; 2527e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2528e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->i == 1) 2529e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->ftype = UIH; 2530e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else if (c->i == 2) 2531e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->ftype = UI; 2532e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2533e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->t1) 2534e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->t1 = c->t1; 2535e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (c->t2) 2536e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm->t2 = c->t2; 2537e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2538e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: We need to separate activation/deactivation from adding 2539e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox and removing from the mux array */ 2540e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (need_restart) 2541e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_activate_mux(gsm); 2542e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->initiator && need_close) 2543e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_open(gsm->dlci[0]); 2544e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 2545e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2546e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2547e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmld_ioctl(struct tty_struct *tty, struct file *file, 2548e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int cmd, unsigned long arg) 2549e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2550e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_config c; 2551e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm = tty->disc_data; 2552e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2553e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox switch (cmd) { 2554e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSMIOC_GETCONF: 2555e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox memset(&c, 0, sizeof(c)); 2556e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.adaption = gsm->adaption; 2557e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.encapsulation = gsm->encoding; 2558e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.initiator = gsm->initiator; 2559e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.t1 = gsm->t1; 2560e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.t2 = gsm->t2; 2561e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.t3 = 0; /* Not supported */ 2562e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.n2 = gsm->n2; 2563e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->ftype == UIH) 2564e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.i = 1; 2565e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 2566e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.i = 2; 25675f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug("Ftype %d i %d\n", gsm->ftype, c.i); 2568e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.mru = gsm->mru; 2569e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.mtu = gsm->mtu; 2570e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox c.k = 0; 2571e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (copy_to_user((void *)arg, &c, sizeof(c))) 2572e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EFAULT; 2573e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 2574e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox case GSMIOC_SETCONF: 2575e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (copy_from_user(&c, (void *)arg, sizeof(c))) 2576e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EFAULT; 2577e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsmld_config(tty, gsm, &c); 2578e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox default: 2579e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return n_tty_ioctl_helper(tty, file, cmd, arg); 2580e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2581e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2582e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2583bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby/* 2584bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * Network interface 2585bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby * 2586bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby */ 2587bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2588bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic int gsm_mux_net_open(struct net_device *net) 2589bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2590bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby pr_debug("%s called\n", __func__); 2591bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby netif_start_queue(net); 2592bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return 0; 2593bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2594bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2595bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic int gsm_mux_net_close(struct net_device *net) 2596bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2597bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby netif_stop_queue(net); 2598bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return 0; 2599bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2600bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2601bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic struct net_device_stats *gsm_mux_net_get_stats(struct net_device *net) 2602bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2603bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return &((struct gsm_mux_net *)netdev_priv(net))->stats; 2604bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2605bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic void dlci_net_free(struct gsm_dlci *dlci) 2606bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2607bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (!dlci->net) { 2608bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby WARN_ON(1); 2609bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return; 2610bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 2611bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->adaption = dlci->prev_adaption; 2612bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->data = dlci->prev_data; 2613bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby free_netdev(dlci->net); 2614bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->net = NULL; 2615bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2616bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic void net_free(struct kref *ref) 2617bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2618bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_mux_net *mux_net; 2619bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_dlci *dlci; 2620bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2621bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mux_net = container_of(ref, struct gsm_mux_net, ref); 2622bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci = mux_net->dlci; 2623bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2624bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (dlci->net) { 2625bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby unregister_netdev(dlci->net); 2626bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci_net_free(dlci); 2627bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 2628bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2629bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 26306ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic inline void muxnet_get(struct gsm_mux_net *mux_net) 26316ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 26326ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_get(&mux_net->ref); 26336ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 26346ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 26356ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbystatic inline void muxnet_put(struct gsm_mux_net *mux_net) 26366ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby{ 26376ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby kref_put(&mux_net->ref, net_free); 26386ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby} 26396ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 2640bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic int gsm_mux_net_start_xmit(struct sk_buff *skb, 2641bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct net_device *net) 2642bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2643bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); 2644bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_dlci *dlci = mux_net->dlci; 26456ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby muxnet_get(mux_net); 2646bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2647bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby skb_queue_head(&dlci->skb_list, skb); 2648bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby STATS(net).tx_packets++; 2649bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby STATS(net).tx_bytes += skb->len; 2650bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby gsm_dlci_data_kick(dlci); 2651bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* And tell the kernel when the last transmit started. */ 2652bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->trans_start = jiffies; 26536ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby muxnet_put(mux_net); 2654bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return NETDEV_TX_OK; 2655bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2656bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2657bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby/* called when a packet did not ack after watchdogtimeout */ 2658bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic void gsm_mux_net_tx_timeout(struct net_device *net) 2659bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2660bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* Tell syslog we are hosed. */ 2661bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dev_dbg(&net->dev, "Tx timed out.\n"); 2662bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2663bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* Update statistics */ 2664bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby STATS(net).tx_errors++; 2665bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2666bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2667bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic void gsm_mux_rx_netchar(struct gsm_dlci *dlci, 2668bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby unsigned char *in_buf, int size) 2669bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2670bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct net_device *net = dlci->net; 2671bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct sk_buff *skb; 2672bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); 26736ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby muxnet_get(mux_net); 2674bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2675bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* Allocate an sk_buff */ 2676bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby skb = dev_alloc_skb(size + NET_IP_ALIGN); 2677bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (!skb) { 2678bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* We got no receive buffer. */ 2679bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby STATS(net).rx_dropped++; 26806ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby muxnet_put(mux_net); 2681bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return; 2682bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 2683bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby skb_reserve(skb, NET_IP_ALIGN); 2684bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby memcpy(skb_put(skb, size), in_buf, size); 2685bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2686bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby skb->dev = net; 2687bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby skb->protocol = __constant_htons(ETH_P_IP); 2688bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2689bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* Ship it off to the kernel */ 2690bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby netif_rx(skb); 2691bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2692bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* update out statistics */ 2693bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby STATS(net).rx_packets++; 2694bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby STATS(net).rx_bytes += size; 26956ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby muxnet_put(mux_net); 2696bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return; 2697bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2698bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2699bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbyint gsm_change_mtu(struct net_device *net, int new_mtu) 2700bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2701bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); 2702bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu)) 2703bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EINVAL; 2704bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->mtu = new_mtu; 2705bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return 0; 2706bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2707bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2708bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic void gsm_mux_net_init(struct net_device *net) 2709bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2710bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby static const struct net_device_ops gsm_netdev_ops = { 2711bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby .ndo_open = gsm_mux_net_open, 2712bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby .ndo_stop = gsm_mux_net_close, 2713bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby .ndo_start_xmit = gsm_mux_net_start_xmit, 2714bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby .ndo_tx_timeout = gsm_mux_net_tx_timeout, 2715bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby .ndo_get_stats = gsm_mux_net_get_stats, 2716bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby .ndo_change_mtu = gsm_change_mtu, 2717bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby }; 2718bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2719bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->netdev_ops = &gsm_netdev_ops; 2720bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2721bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* fill in the other fields */ 2722bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->watchdog_timeo = GSM_NET_TX_TIMEOUT; 2723bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 2724bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->type = ARPHRD_NONE; 2725bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->tx_queue_len = 10; 2726bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2727bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2728bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2729bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby/* caller holds the dlci mutex */ 2730bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic void gsm_destroy_network(struct gsm_dlci *dlci) 2731bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2732bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_mux_net *mux_net; 2733bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2734bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby pr_debug("destroy network interface"); 2735bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (!dlci->net) 2736bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return; 2737bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mux_net = (struct gsm_mux_net *)netdev_priv(dlci->net); 27386ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby muxnet_put(mux_net); 2739bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2740bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2741bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2742bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby/* caller holds the dlci mutex */ 2743bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorbystatic int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc) 2744bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby{ 2745bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby char *netname; 2746bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby int retval = 0; 2747bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct net_device *net; 2748bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_mux_net *mux_net; 2749bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2750bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (!capable(CAP_NET_ADMIN)) 2751bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EPERM; 2752bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2753bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* Already in a non tty mode */ 2754bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (dlci->adaption > 2) 2755bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EBUSY; 2756bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2757bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (nc->protocol != htons(ETH_P_IP)) 2758bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EPROTONOSUPPORT; 2759bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2760bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (nc->adaption != 3 && nc->adaption != 4) 2761bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EPROTONOSUPPORT; 2762bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2763bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby pr_debug("create network interface"); 2764bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2765bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby netname = "gsm%d"; 2766bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (nc->if_name[0] != '\0') 2767bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby netname = nc->if_name; 2768bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net = alloc_netdev(sizeof(struct gsm_mux_net), 2769bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby netname, 2770bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby gsm_mux_net_init); 2771bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (!net) { 2772bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby pr_err("alloc_netdev failed"); 2773bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -ENOMEM; 2774bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 2775bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby net->mtu = dlci->gsm->mtu; 2776bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mux_net = (struct gsm_mux_net *)netdev_priv(net); 2777bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mux_net->dlci = dlci; 2778bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby kref_init(&mux_net->ref); 2779bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */ 2780bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2781bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* reconfigure dlci for network */ 2782bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->prev_adaption = dlci->adaption; 2783bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->prev_data = dlci->data; 2784bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->adaption = nc->adaption; 2785bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->data = gsm_mux_rx_netchar; 2786bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci->net = net; 2787bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 2788bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby pr_debug("register netdev"); 2789bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby retval = register_netdev(net); 2790bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (retval) { 2791bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby pr_err("network register fail %d\n", retval); 2792bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby dlci_net_free(dlci); 2793bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return retval; 2794bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 2795bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return net->ifindex; /* return network index */ 2796bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby} 2797e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2798e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* Line discipline for real tty */ 2799e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstruct tty_ldisc_ops tty_ldisc_packet = { 2800e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .owner = THIS_MODULE, 2801e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .magic = TTY_LDISC_MAGIC, 2802e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .name = "n_gsm", 2803e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .open = gsmld_open, 2804e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .close = gsmld_close, 2805e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .flush_buffer = gsmld_flush_buffer, 2806e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .chars_in_buffer = gsmld_chars_in_buffer, 2807e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .read = gsmld_read, 2808e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .write = gsmld_write, 2809e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .ioctl = gsmld_ioctl, 2810e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .poll = gsmld_poll, 2811e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .receive_buf = gsmld_receive_buf, 2812e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .write_wakeup = gsmld_write_wakeup 2813e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 2814e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2815e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* 2816e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox * Virtual tty side 2817e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox */ 2818e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2819e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox#define TX_SIZE 512 2820e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2821e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) 2822e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2823e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox u8 modembits[5]; 2824e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_control *ctrl; 2825e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len = 2; 2826e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2827e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (brk) 2828e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox len++; 2829e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2830e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits[0] = len << 1 | EA; /* Data bytes */ 2831e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits[1] = dlci->addr << 2 | 3; /* DLCI, EA, 1 */ 2832e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits[2] = gsm_encode_modem(dlci) << 1 | EA; 2833e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (brk) 2834e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modembits[3] = brk << 4 | 2 | EA; /* Valid, EA */ 2835e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1); 2836e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (ctrl == NULL) 2837e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOMEM; 2838e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsm_control_wait(dlci->gsm, ctrl); 2839e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2840e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2841e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsm_carrier_raised(struct tty_port *port) 2842e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2843e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); 2844e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Not yet open so no carrier info */ 2845e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci->state != DLCI_OPEN) 2846e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 2847e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (debug & 2) 2848e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 1; 2849e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return dlci->modem_rx & TIOCM_CD; 2850e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2851e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2852e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsm_dtr_rts(struct tty_port *port, int onoff) 2853e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2854e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); 2855e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int modem_tx = dlci->modem_tx; 2856e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (onoff) 2857e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modem_tx |= TIOCM_DTR | TIOCM_RTS; 2858e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else 2859e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); 2860e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem_tx != dlci->modem_tx) { 2861e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->modem_tx = modem_tx; 2862e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsmtty_modem_update(dlci, 0); 2863e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 2864e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2865e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2866e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic const struct tty_port_operations gsm_port_ops = { 2867e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .carrier_raised = gsm_carrier_raised, 2868e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .dtr_rts = gsm_dtr_rts, 2869e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 2870e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2871e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2872e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmtty_open(struct tty_struct *tty, struct file *filp) 2873e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2874e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_mux *gsm; 2875e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci; 2876e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct tty_port *port; 2877e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int line = tty->index; 2878e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int mux = line >> 6; 2879e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2880e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox line = line & 0x3F; 2881e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2882e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (mux >= MAX_MUX) 2883e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENXIO; 2884e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */ 2885e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm_mux[mux] == NULL) 2886e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EUNATCH; 2887e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (line == 0 || line > 61) /* 62/63 reserved */ 2888e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ECHRNG; 2889e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm = gsm_mux[mux]; 2890e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (gsm->dead) 2891e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EL2HLT; 2892e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm->dlci[line]; 2893e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 2894e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci = gsm_dlci_alloc(gsm, line); 2895e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 2896e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -ENOMEM; 2897e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox port = &dlci->port; 2898e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox port->count++; 2899e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty->driver_data = dlci; 29006ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby dlci_get(dlci); 29016ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby dlci_get(dlci->gsm->dlci[0]); 29026ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby mux_get(dlci->gsm); 2903e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_port_tty_set(port, tty); 2904e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2905e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->modem_rx = 0; 2906e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* We could in theory open and close before we wait - eg if we get 2907e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox a DM straight back. This is ok as that will have caused a hangup */ 2908e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox set_bit(ASYNCB_INITIALIZED, &port->flags); 2909e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Start sending off SABM messages */ 2910e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_open(dlci); 2911e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* And wait for virtual carrier */ 2912e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return tty_port_block_til_ready(port, tty, filp); 2913e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2914e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2915e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_close(struct tty_struct *tty, struct file *filp) 2916e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2917e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 29186ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby struct gsm_mux *gsm; 29196ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby 2920e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (dlci == NULL) 2921e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return; 2922bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_lock(&dlci->mutex); 2923bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby gsm_destroy_network(dlci); 2924bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_unlock(&dlci->mutex); 29256ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby gsm = dlci->gsm; 2926e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty_port_close_start(&dlci->port, tty, filp) == 0) 29276ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby goto out; 2928e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_close(dlci); 2929e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_port_close_end(&dlci->port, tty); 2930e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_port_tty_set(&dlci->port, NULL); 29316ab8fba7fcb012a42d686abd33555b2215071415Russ Gorbyout: 29326ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby dlci_put(dlci); 29336ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby dlci_put(gsm->dlci[0]); 29346ab8fba7fcb012a42d686abd33555b2215071415Russ Gorby mux_put(gsm); 2935e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2936e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2937e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_hangup(struct tty_struct *tty) 2938e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2939e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2940e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_port_hangup(&dlci->port); 2941e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_begin_close(dlci); 2942e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2943e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2944e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, 2945e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int len) 2946e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2947e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2948e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Stuff the bytes into the fifo queue */ 2949e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); 2950e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Need to kick the channel */ 2951e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_dlci_data_kick(dlci); 2952e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return sent; 2953e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2954e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2955e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmtty_write_room(struct tty_struct *tty) 2956e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2957e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2958e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return TX_SIZE - kfifo_len(dlci->fifo); 2959e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2960e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2961e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmtty_chars_in_buffer(struct tty_struct *tty) 2962e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2963e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2964e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return kfifo_len(dlci->fifo); 2965e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2966e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2967e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_flush_buffer(struct tty_struct *tty) 2968e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2969e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2970e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Caution needed: If we implement reliable transport classes 2971e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox then the data being transmitted can't simply be junked once 2972e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox it has first hit the stack. Until then we can just blow it 2973e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox away */ 2974e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox kfifo_reset(dlci->fifo); 2975e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Need to unhook this DLCI from the transmit queue logic */ 2976e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2977e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2978e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) 2979e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2980e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* The FIFO handles the queue so the kernel will do the right 2981e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox thing waiting on chars_in_buffer before calling us. No work 2982e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox to do here */ 2983e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2984e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 298560b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int gsmtty_tiocmget(struct tty_struct *tty) 2986e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2987e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2988e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return dlci->modem_rx; 2989e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 2990e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 299120b9d17715017ae4dd4ec87fabc36d33b9de708eAlan Coxstatic int gsmtty_tiocmset(struct tty_struct *tty, 2992e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int set, unsigned int clear) 2993e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 2994e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 2995e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int modem_tx = dlci->modem_tx; 2996e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 2997cf16807b61d15e42b20407c954a01a3774520ea7Nikola Diklic-Perin modem_tx &= ~clear; 2998e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox modem_tx |= set; 2999e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3000e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (modem_tx != dlci->modem_tx) { 3001e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->modem_tx = modem_tx; 3002e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsmtty_modem_update(dlci, 0); 3003e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 3004e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 3005e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3006e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3007e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 30086caa76b7786891b42b66a0e61e2c2fff2c884620Alan Coxstatic int gsmtty_ioctl(struct tty_struct *tty, 3009e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox unsigned int cmd, unsigned long arg) 3010e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3011bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_dlci *dlci = tty->driver_data; 3012bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby struct gsm_netconfig nc; 3013bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby int index; 3014bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby 3015bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby switch (cmd) { 3016bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby case GSMIOC_ENABLE_NET: 3017bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (copy_from_user(&nc, (void __user *)arg, sizeof(nc))) 3018bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EFAULT; 3019bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby nc.if_name[IFNAMSIZ-1] = '\0'; 3020bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby /* return net interface index or error code */ 3021bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_lock(&dlci->mutex); 3022bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby index = gsm_create_network(dlci, &nc); 3023bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_unlock(&dlci->mutex); 3024bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (copy_to_user((void __user *)arg, &nc, sizeof(nc))) 3025bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EFAULT; 3026bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return index; 3027bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby case GSMIOC_DISABLE_NET: 3028bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby if (!capable(CAP_NET_ADMIN)) 3029bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -EPERM; 3030bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_lock(&dlci->mutex); 3031bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby gsm_destroy_network(dlci); 3032bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby mutex_unlock(&dlci->mutex); 3033bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return 0; 3034bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby default: 3035bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby return -ENOIOCTLCMD; 3036bcd5abe28f40cc6a935d3339cde27976f6be3f1aRuss Gorby } 3037e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3038e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3039e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) 3040e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3041e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* For the moment its fixed. In actual fact the speed information 3042e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox for the virtual channel can be propogated in both directions by 3043e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox the RPN control message. This however rapidly gets nasty as we 3044e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox then have to remap modem signals each way according to whether 3045e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox our virtual cable is null modem etc .. */ 3046e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_termios_copy_hw(tty->termios, old); 3047e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3048e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3049e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_throttle(struct tty_struct *tty) 3050e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3051e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 3052e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty->termios->c_cflag & CRTSCTS) 3053e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->modem_tx &= ~TIOCM_DTR; 3054e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->throttled = 1; 3055e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Send an MSC with DTR cleared */ 3056e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsmtty_modem_update(dlci, 0); 3057e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3058e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3059e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void gsmtty_unthrottle(struct tty_struct *tty) 3060e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3061e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 3062e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty->termios->c_cflag & CRTSCTS) 3063e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->modem_tx |= TIOCM_DTR; 3064e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox dlci->throttled = 0; 3065e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Send an MSC with DTR set */ 3066e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsmtty_modem_update(dlci, 0); 3067e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3068e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3069e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int gsmtty_break_ctl(struct tty_struct *tty, int state) 3070e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3071e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox struct gsm_dlci *dlci = tty->driver_data; 3072e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int encode = 0; /* Off */ 3073e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3074e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (state == -1) /* "On indefinitely" - we can't encode this 3075e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox properly */ 3076e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox encode = 0x0F; 3077e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox else if (state > 0) { 3078e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox encode = state / 200; /* mS to encoding */ 3079e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (encode > 0x0F) 3080e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox encode = 0x0F; /* Best effort */ 3081e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 3082e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return gsmtty_modem_update(dlci, encode); 3083e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3084e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3085e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3086e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox/* Virtual ttys for the demux */ 3087e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic const struct tty_operations gsmtty_ops = { 3088e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .open = gsmtty_open, 3089e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .close = gsmtty_close, 3090e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .write = gsmtty_write, 3091e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .write_room = gsmtty_write_room, 3092e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .chars_in_buffer = gsmtty_chars_in_buffer, 3093e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .flush_buffer = gsmtty_flush_buffer, 3094e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .ioctl = gsmtty_ioctl, 3095e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .throttle = gsmtty_throttle, 3096e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .unthrottle = gsmtty_unthrottle, 3097e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .set_termios = gsmtty_set_termios, 3098e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .hangup = gsmtty_hangup, 3099e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .wait_until_sent = gsmtty_wait_until_sent, 3100e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .tiocmget = gsmtty_tiocmget, 3101e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .tiocmset = gsmtty_tiocmset, 3102e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox .break_ctl = gsmtty_break_ctl, 3103e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox}; 3104e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3105e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3106e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3107e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic int __init gsm_init(void) 3108e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3109e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Fill in our line protocol discipline, and register it */ 3110e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet); 3111e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (status != 0) { 31125f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_err("n_gsm: can't register line discipline (err = %d)\n", 31135f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox status); 3114e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return status; 3115e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 3116e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3117e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver = alloc_tty_driver(256); 3118e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (!gsm_tty_driver) { 3119e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_unregister_ldisc(N_GSM0710); 31205f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_err("gsm_init: tty allocation failed.\n"); 3121e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EINVAL; 3122e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 3123e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->driver_name = "gsmtty"; 3124e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->name = "gsmtty"; 3125e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->major = 0; /* Dynamic */ 3126e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->minor_start = 0; 3127e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 3128e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->subtype = SERIAL_TYPE_NORMAL; 3129e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV 31305f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox | TTY_DRIVER_HARDWARE_BREAK; 3131e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->init_termios = tty_std_termios; 3132e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox /* Fixme */ 3133e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox gsm_tty_driver->init_termios.c_lflag &= ~ECHO; 3134e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_set_operations(gsm_tty_driver, &gsmtty_ops); 3135e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3136e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox spin_lock_init(&gsm_mux_lock); 3137e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3138e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (tty_register_driver(gsm_tty_driver)) { 3139e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox put_tty_driver(gsm_tty_driver); 3140e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_unregister_ldisc(N_GSM0710); 31415f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_err("gsm_init: tty registration failed.\n"); 3142e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return -EBUSY; 3143e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox } 31445f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_debug("gsm_init: loaded as %d,%d.\n", 31455f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox gsm_tty_driver->major, gsm_tty_driver->minor_start); 3146e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox return 0; 3147e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3148e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3149e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxstatic void __exit gsm_exit(void) 3150e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox{ 3151e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox int status = tty_unregister_ldisc(N_GSM0710); 3152e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox if (status != 0) 31535f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox pr_err("n_gsm: can't unregister line discipline (err = %d)\n", 31545f9a31d63105c3e88bd6d026e7bc53f02a5ac042Alan Cox status); 3155e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox tty_unregister_driver(gsm_tty_driver); 3156e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox put_tty_driver(gsm_tty_driver); 3157e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox} 3158e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3159e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxmodule_init(gsm_init); 3160e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Coxmodule_exit(gsm_exit); 3161e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3162e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan Cox 3163e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan CoxMODULE_LICENSE("GPL"); 3164e1eaea46bb4020b38a141b84f88565d4603f8dd0Alan CoxMODULE_ALIAS_LDISC(N_GSM0710); 3165