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