xref: /openbmc/linux/drivers/tty/n_gsm.c (revision 09138ba68c1487a42c400485e999386a74911dbc)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
296fd7ce5SGreg Kroah-Hartman /*
396fd7ce5SGreg Kroah-Hartman  * n_gsm.c GSM 0710 tty multiplexor
496fd7ce5SGreg Kroah-Hartman  * Copyright (c) 2009/10 Intel Corporation
596fd7ce5SGreg Kroah-Hartman  *
696fd7ce5SGreg Kroah-Hartman  *	* THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE *
796fd7ce5SGreg Kroah-Hartman  *
80af02167SDaniel Starke  * Outgoing path:
90af02167SDaniel Starke  * tty -> DLCI fifo -> scheduler -> GSM MUX data queue    ---o-> ldisc
100af02167SDaniel Starke  * control message               -> GSM MUX control queue --´
110af02167SDaniel Starke  *
120af02167SDaniel Starke  * Incoming path:
130af02167SDaniel Starke  * ldisc -> gsm_queue() -o--> tty
140af02167SDaniel Starke  *                        `-> gsm_control_response()
150af02167SDaniel Starke  *
1696fd7ce5SGreg Kroah-Hartman  * TO DO:
1796fd7ce5SGreg Kroah-Hartman  *	Mostly done:	ioctls for setting modes/timing
1896fd7ce5SGreg Kroah-Hartman  *	Partly done:	hooks so you can pull off frames to non tty devs
1996fd7ce5SGreg Kroah-Hartman  *	Restart DLCI 0 when it closes ?
2096fd7ce5SGreg Kroah-Hartman  *	Improve the tx engine
2196fd7ce5SGreg Kroah-Hartman  *	Resolve tx side locking by adding a queue_head and routing
2296fd7ce5SGreg Kroah-Hartman  *		all control traffic via it
2396fd7ce5SGreg Kroah-Hartman  *	General tidy/document
2496fd7ce5SGreg Kroah-Hartman  *	Review the locking/move to refcounts more (mux now moved to an
2596fd7ce5SGreg Kroah-Hartman  *		alloc/free model ready)
2696fd7ce5SGreg Kroah-Hartman  *	Use newest tty open/close port helpers and install hooks
2796fd7ce5SGreg Kroah-Hartman  *	What to do about power functions ?
2896fd7ce5SGreg Kroah-Hartman  *	Termios setting and negotiation
2996fd7ce5SGreg Kroah-Hartman  *	Do we need a 'which mux are you' ioctl to correlate mux and tty sets
3096fd7ce5SGreg Kroah-Hartman  *
3196fd7ce5SGreg Kroah-Hartman  */
3296fd7ce5SGreg Kroah-Hartman 
3396fd7ce5SGreg Kroah-Hartman #include <linux/types.h>
3496fd7ce5SGreg Kroah-Hartman #include <linux/major.h>
3596fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
3696fd7ce5SGreg Kroah-Hartman #include <linux/signal.h>
3796fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
38174cd4b1SIngo Molnar #include <linux/sched/signal.h>
3996fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h>
4096fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
4192f1f0c3SDaniel Starke #include <linux/bitfield.h>
4296fd7ce5SGreg Kroah-Hartman #include <linux/ctype.h>
4396fd7ce5SGreg Kroah-Hartman #include <linux/mm.h>
442ec7a802SDaniel Starke #include <linux/math.h>
452b3174c9SIlpo Järvinen #include <linux/nospec.h>
4696fd7ce5SGreg Kroah-Hartman #include <linux/string.h>
4796fd7ce5SGreg Kroah-Hartman #include <linux/slab.h>
4896fd7ce5SGreg Kroah-Hartman #include <linux/poll.h>
4996fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
5096fd7ce5SGreg Kroah-Hartman #include <linux/file.h>
5196fd7ce5SGreg Kroah-Hartman #include <linux/uaccess.h>
5296fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
5396fd7ce5SGreg Kroah-Hartman #include <linux/timer.h>
5496fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h>
5596fd7ce5SGreg Kroah-Hartman #include <linux/tty_driver.h>
5696fd7ce5SGreg Kroah-Hartman #include <linux/serial.h>
5796fd7ce5SGreg Kroah-Hartman #include <linux/kfifo.h>
5896fd7ce5SGreg Kroah-Hartman #include <linux/skbuff.h>
59bcd5abe2SRuss Gorby #include <net/arp.h>
60bcd5abe2SRuss Gorby #include <linux/ip.h>
61bcd5abe2SRuss Gorby #include <linux/netdevice.h>
62bcd5abe2SRuss Gorby #include <linux/etherdevice.h>
6396fd7ce5SGreg Kroah-Hartman #include <linux/gsmmux.h>
645ffa6e34SGreg Kroah-Hartman #include "tty.h"
6596fd7ce5SGreg Kroah-Hartman 
6696fd7ce5SGreg Kroah-Hartman static int debug;
6796fd7ce5SGreg Kroah-Hartman module_param(debug, int, 0600);
6896fd7ce5SGreg Kroah-Hartman 
69c07da737SDaniel Starke /* Module debug bits */
70c07da737SDaniel Starke #define DBG_DUMP	BIT(0) /* Data transmission dump. */
71c07da737SDaniel Starke #define DBG_CD_ON	BIT(1) /* Always assume CD line on. */
72c07da737SDaniel Starke #define DBG_DATA	BIT(2) /* Data transmission details. */
73c07da737SDaniel Starke #define DBG_ERRORS	BIT(3) /* Details for fail conditions. */
74c07da737SDaniel Starke #define DBG_TTY		BIT(4) /* Transmission statistics for DLCI TTYs. */
75c22d054fSDaniel Starke #define DBG_PAYLOAD	BIT(5) /* Limits DBG_DUMP to payload frames. */
76c07da737SDaniel Starke 
77a8d12007SAlan Cox /* Defaults: these are from the specification */
78a8d12007SAlan Cox 
79a8d12007SAlan Cox #define T1	10		/* 100mS */
80a8d12007SAlan Cox #define T2	34		/* 333mS */
812ec7a802SDaniel Starke #define T3	10		/* 10s */
82a8d12007SAlan Cox #define N2	3		/* Retry 3 times */
832ec7a802SDaniel Starke #define K	2		/* outstanding I frames */
842ec7a802SDaniel Starke 
852ec7a802SDaniel Starke #define MAX_T3 255		/* In seconds. */
862ec7a802SDaniel Starke #define MAX_WINDOW_SIZE 7	/* Limit of K in error recovery mode. */
8796fd7ce5SGreg Kroah-Hartman 
8896fd7ce5SGreg Kroah-Hartman /* Use long timers for testing at low speed with debug on */
8996fd7ce5SGreg Kroah-Hartman #ifdef DEBUG_TIMING
90a8d12007SAlan Cox #define T1	100
91a8d12007SAlan Cox #define T2	200
9296fd7ce5SGreg Kroah-Hartman #endif
9396fd7ce5SGreg Kroah-Hartman 
945f9a31d6SAlan Cox /*
9525985edcSLucas De Marchi  * Semi-arbitrary buffer size limits. 0710 is normally run with 32-64 byte
965f9a31d6SAlan Cox  * limits so this is plenty
975f9a31d6SAlan Cox  */
98bcd5abe2SRuss Gorby #define MAX_MRU 1500
99bcd5abe2SRuss Gorby #define MAX_MTU 1500
1007a121247SDaniel Starke #define MIN_MTU (PROT_OVERHEAD + 1)
101535bf600SDaniel Starke /* SOF, ADDR, CTRL, LEN1, LEN2, ..., FCS, EOF */
102535bf600SDaniel Starke #define PROT_OVERHEAD 7
103bcd5abe2SRuss Gorby #define	GSM_NET_TX_TIMEOUT (HZ*10)
104bcd5abe2SRuss Gorby 
105b410e35dSLee Jones /*
106bcd5abe2SRuss Gorby  *	struct gsm_mux_net	-	network interface
107bcd5abe2SRuss Gorby  *
108bcd5abe2SRuss Gorby  *	Created when net interface is initialized.
109724ac070SJiri Slaby  */
110bcd5abe2SRuss Gorby struct gsm_mux_net {
111bcd5abe2SRuss Gorby 	struct kref ref;
112bcd5abe2SRuss Gorby 	struct gsm_dlci *dlci;
113bcd5abe2SRuss Gorby };
114bcd5abe2SRuss Gorby 
11596fd7ce5SGreg Kroah-Hartman /*
11696fd7ce5SGreg Kroah-Hartman  *	Each block of data we have queued to go out is in the form of
11725985edcSLucas De Marchi  *	a gsm_msg which holds everything we need in a link layer independent
11896fd7ce5SGreg Kroah-Hartman  *	format
11996fd7ce5SGreg Kroah-Hartman  */
12096fd7ce5SGreg Kroah-Hartman 
12196fd7ce5SGreg Kroah-Hartman struct gsm_msg {
122b4338e1eSRuss Gorby 	struct list_head list;
12396fd7ce5SGreg Kroah-Hartman 	u8 addr;		/* DLCI address + flags */
12496fd7ce5SGreg Kroah-Hartman 	u8 ctrl;		/* Control byte + flags */
12596fd7ce5SGreg Kroah-Hartman 	unsigned int len;	/* Length of data block (can be zero) */
12696fd7ce5SGreg Kroah-Hartman 	unsigned char *data;	/* Points into buffer but not at the start */
1272f202d03SGustavo A. R. Silva 	unsigned char buffer[];
12896fd7ce5SGreg Kroah-Hartman };
12996fd7ce5SGreg Kroah-Hartman 
13072ae8cc1SJiri Slaby enum gsm_dlci_state {
13172ae8cc1SJiri Slaby 	DLCI_CLOSED,
1324ca58966SDaniel Starke 	DLCI_WAITING_CONFIG,	/* Waiting for DLCI configuration from user */
13392f1f0c3SDaniel Starke 	DLCI_CONFIGURE,		/* Sending PN (for adaption > 1) */
13472ae8cc1SJiri Slaby 	DLCI_OPENING,		/* Sending SABM not seen UA */
13572ae8cc1SJiri Slaby 	DLCI_OPEN,		/* SABM/UA complete */
13672ae8cc1SJiri Slaby 	DLCI_CLOSING,		/* Sending DISC not seen UA/DM */
13772ae8cc1SJiri Slaby };
13872ae8cc1SJiri Slaby 
139e1785996SJiri Slaby enum gsm_dlci_mode {
140e1785996SJiri Slaby 	DLCI_MODE_ABM,		/* Normal Asynchronous Balanced Mode */
141e1785996SJiri Slaby 	DLCI_MODE_ADM,		/* Asynchronous Disconnected Mode */
142e1785996SJiri Slaby };
143e1785996SJiri Slaby 
14496fd7ce5SGreg Kroah-Hartman /*
14596fd7ce5SGreg Kroah-Hartman  *	Each active data link has a gsm_dlci structure associated which ties
14696fd7ce5SGreg Kroah-Hartman  *	the link layer to an optional tty (if the tty side is open). To avoid
14796fd7ce5SGreg Kroah-Hartman  *	complexity right now these are only ever freed up when the mux is
14896fd7ce5SGreg Kroah-Hartman  *	shut down.
14996fd7ce5SGreg Kroah-Hartman  *
15096fd7ce5SGreg Kroah-Hartman  *	At the moment we don't free DLCI objects until the mux is torn down
15196fd7ce5SGreg Kroah-Hartman  *	this avoid object life time issues but might be worth review later.
15296fd7ce5SGreg Kroah-Hartman  */
15396fd7ce5SGreg Kroah-Hartman 
15496fd7ce5SGreg Kroah-Hartman struct gsm_dlci {
15596fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm;
15696fd7ce5SGreg Kroah-Hartman 	int addr;
15772ae8cc1SJiri Slaby 	enum gsm_dlci_state state;
158bcd5abe2SRuss Gorby 	struct mutex mutex;
15996fd7ce5SGreg Kroah-Hartman 
16096fd7ce5SGreg Kroah-Hartman 	/* Link layer */
161e1785996SJiri Slaby 	enum gsm_dlci_mode mode;
16296fd7ce5SGreg Kroah-Hartman 	spinlock_t lock;	/* Protects the internal state */
16396fd7ce5SGreg Kroah-Hartman 	struct timer_list t1;	/* Retransmit timer for SABM and UA */
16496fd7ce5SGreg Kroah-Hartman 	int retries;
16596fd7ce5SGreg Kroah-Hartman 	/* Uplink tty if active */
16696fd7ce5SGreg Kroah-Hartman 	struct tty_port port;	/* The tty bound to this DLCI if there is one */
1679361ebfbSDaniel Starke #define TX_SIZE		4096    /* Must be power of 2. */
168036bca1fSJiri Slaby 	struct kfifo fifo;	/* Queue fifo for the DLCI */
16996fd7ce5SGreg Kroah-Hartman 	int adaption;		/* Adaption layer in use */
170bcd5abe2SRuss Gorby 	int prev_adaption;
17196fd7ce5SGreg Kroah-Hartman 	u32 modem_rx;		/* Our incoming virtual modem lines */
17296fd7ce5SGreg Kroah-Hartman 	u32 modem_tx;		/* Our outgoing modem lines */
1732ec7a802SDaniel Starke 	unsigned int mtu;
1745677fcf3SJiri Slaby 	bool dead;		/* Refuse re-open */
1752ec7a802SDaniel Starke 	/* Configuration */
1762ec7a802SDaniel Starke 	u8 prio;		/* Priority */
1772ec7a802SDaniel Starke 	u8 ftype;		/* Frame type */
1782ec7a802SDaniel Starke 	u8 k;			/* Window size */
17996fd7ce5SGreg Kroah-Hartman 	/* Flow control */
180e9360b9aSJiri Slaby 	bool throttled;		/* Private copy of throttle state */
1817a9ed9c0SJiri Slaby 	bool constipated;	/* Throttle status for outgoing */
18296fd7ce5SGreg Kroah-Hartman 	/* Packetised I/O */
18396fd7ce5SGreg Kroah-Hartman 	struct sk_buff *skb;	/* Frame being sent */
18496fd7ce5SGreg Kroah-Hartman 	struct sk_buff_head skb_list;	/* Queued frames */
18596fd7ce5SGreg Kroah-Hartman 	/* Data handling callback */
1864feb7a4aSTony Lindgren 	void (*data)(struct gsm_dlci *dlci, const u8 *data, int len);
1874feb7a4aSTony Lindgren 	void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
188bcd5abe2SRuss Gorby 	struct net_device *net; /* network interface, if created */
18996fd7ce5SGreg Kroah-Hartman };
19096fd7ce5SGreg Kroah-Hartman 
19192f1f0c3SDaniel Starke /*
19292f1f0c3SDaniel Starke  * Parameter bits used for parameter negotiation according to 3GPP 27.010
19392f1f0c3SDaniel Starke  * chapter 5.4.6.3.1.
19492f1f0c3SDaniel Starke  */
19592f1f0c3SDaniel Starke 
19692f1f0c3SDaniel Starke struct gsm_dlci_param_bits {
19792f1f0c3SDaniel Starke 	u8 d_bits;
19892f1f0c3SDaniel Starke 	u8 i_cl_bits;
19992f1f0c3SDaniel Starke 	u8 p_bits;
20092f1f0c3SDaniel Starke 	u8 t_bits;
20192f1f0c3SDaniel Starke 	__le16 n_bits;
20292f1f0c3SDaniel Starke 	u8 na_bits;
20392f1f0c3SDaniel Starke 	u8 k_bits;
20492f1f0c3SDaniel Starke };
20592f1f0c3SDaniel Starke 
20692f1f0c3SDaniel Starke static_assert(sizeof(struct gsm_dlci_param_bits) == 8);
20792f1f0c3SDaniel Starke 
20892f1f0c3SDaniel Starke #define PN_D_FIELD_DLCI		GENMASK(5, 0)
20992f1f0c3SDaniel Starke #define PN_I_CL_FIELD_FTYPE	GENMASK(3, 0)
21092f1f0c3SDaniel Starke #define PN_I_CL_FIELD_ADAPTION	GENMASK(7, 4)
21192f1f0c3SDaniel Starke #define PN_P_FIELD_PRIO		GENMASK(5, 0)
21292f1f0c3SDaniel Starke #define PN_T_FIELD_T1		GENMASK(7, 0)
21392f1f0c3SDaniel Starke #define PN_N_FIELD_N1		GENMASK(15, 0)
21492f1f0c3SDaniel Starke #define PN_NA_FIELD_N2		GENMASK(7, 0)
21592f1f0c3SDaniel Starke #define PN_K_FIELD_K		GENMASK(2, 0)
21692f1f0c3SDaniel Starke 
217796492deSDaniel Starke /* Total number of supported devices */
218796492deSDaniel Starke #define GSM_TTY_MINORS		256
219796492deSDaniel Starke 
220c33eecc7SGeert Uytterhoeven /* DLCI 0, 62/63 are special or reserved see gsmtty_open */
22196fd7ce5SGreg Kroah-Hartman 
22296fd7ce5SGreg Kroah-Hartman #define NUM_DLCI		64
22396fd7ce5SGreg Kroah-Hartman 
22496fd7ce5SGreg Kroah-Hartman /*
22596fd7ce5SGreg Kroah-Hartman  *	DLCI 0 is used to pass control blocks out of band of the data
22696fd7ce5SGreg Kroah-Hartman  *	flow (and with a higher link priority). One command can be outstanding
22796fd7ce5SGreg Kroah-Hartman  *	at a time and we use this structure to manage them. They are created
22896fd7ce5SGreg Kroah-Hartman  *	and destroyed by the user context, and updated by the receive paths
22996fd7ce5SGreg Kroah-Hartman  *	and timers
23096fd7ce5SGreg Kroah-Hartman  */
23196fd7ce5SGreg Kroah-Hartman 
23296fd7ce5SGreg Kroah-Hartman struct gsm_control {
23396fd7ce5SGreg Kroah-Hartman 	u8 cmd;		/* Command we are issuing */
23496fd7ce5SGreg Kroah-Hartman 	u8 *data;	/* Data for the command in case we retransmit */
23596fd7ce5SGreg Kroah-Hartman 	int len;	/* Length of block for retransmission */
23696fd7ce5SGreg Kroah-Hartman 	int done;	/* Done flag */
23796fd7ce5SGreg Kroah-Hartman 	int error;	/* Error if any */
23896fd7ce5SGreg Kroah-Hartman };
23996fd7ce5SGreg Kroah-Hartman 
24019fb0a66SDaniel Starke enum gsm_encoding {
24119fb0a66SDaniel Starke 	GSM_BASIC_OPT,
24219fb0a66SDaniel Starke 	GSM_ADV_OPT,
24319fb0a66SDaniel Starke };
24419fb0a66SDaniel Starke 
245329aa6e6SJiri Slaby enum gsm_mux_state {
246329aa6e6SJiri Slaby 	GSM_SEARCH,
24762c3763dSDaniel Starke 	GSM0_ADDRESS,
24862c3763dSDaniel Starke 	GSM0_CONTROL,
24962c3763dSDaniel Starke 	GSM0_LEN0,
25062c3763dSDaniel Starke 	GSM0_LEN1,
25162c3763dSDaniel Starke 	GSM0_DATA,
25262c3763dSDaniel Starke 	GSM0_FCS,
25362c3763dSDaniel Starke 	GSM0_SSOF,
25462c3763dSDaniel Starke 	GSM1_START,
25562c3763dSDaniel Starke 	GSM1_ADDRESS,
25662c3763dSDaniel Starke 	GSM1_CONTROL,
25762c3763dSDaniel Starke 	GSM1_DATA,
25862c3763dSDaniel Starke 	GSM1_OVERRUN,
259329aa6e6SJiri Slaby };
260329aa6e6SJiri Slaby 
26196fd7ce5SGreg Kroah-Hartman /*
26296fd7ce5SGreg Kroah-Hartman  *	Each GSM mux we have is represented by this structure. If we are
26396fd7ce5SGreg Kroah-Hartman  *	operating as an ldisc then we use this structure as our ldisc
26496fd7ce5SGreg Kroah-Hartman  *	state. We need to sort out lifetimes and locking with respect
26596fd7ce5SGreg Kroah-Hartman  *	to the gsm mux array. For now we don't free DLCI objects that
26696fd7ce5SGreg Kroah-Hartman  *	have been instantiated until the mux itself is terminated.
26796fd7ce5SGreg Kroah-Hartman  *
26896fd7ce5SGreg Kroah-Hartman  *	To consider further: tty open versus mux shutdown.
26996fd7ce5SGreg Kroah-Hartman  */
27096fd7ce5SGreg Kroah-Hartman 
27196fd7ce5SGreg Kroah-Hartman struct gsm_mux {
27296fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;		/* The tty our ldisc is bound to */
27396fd7ce5SGreg Kroah-Hartman 	spinlock_t lock;
274dfabf7ffSChao Bi 	struct mutex mutex;
275d50f6dcaSRuss Gorby 	unsigned int num;
2766ab8fba7SRuss Gorby 	struct kref ref;
27796fd7ce5SGreg Kroah-Hartman 
27896fd7ce5SGreg Kroah-Hartman 	/* Events on the GSM channel */
27996fd7ce5SGreg Kroah-Hartman 	wait_queue_head_t event;
28096fd7ce5SGreg Kroah-Hartman 
2810af02167SDaniel Starke 	/* ldisc send work */
2820af02167SDaniel Starke 	struct work_struct tx_work;
2830af02167SDaniel Starke 
28496fd7ce5SGreg Kroah-Hartman 	/* Bits for GSM mode decoding */
28596fd7ce5SGreg Kroah-Hartman 
28696fd7ce5SGreg Kroah-Hartman 	/* Framing Layer */
28796fd7ce5SGreg Kroah-Hartman 	unsigned char *buf;
288329aa6e6SJiri Slaby 	enum gsm_mux_state state;
28996fd7ce5SGreg Kroah-Hartman 	unsigned int len;
29096fd7ce5SGreg Kroah-Hartman 	unsigned int address;
29196fd7ce5SGreg Kroah-Hartman 	unsigned int count;
292c50704bdSJiri Slaby 	bool escape;
29319fb0a66SDaniel Starke 	enum gsm_encoding encoding;
29496fd7ce5SGreg Kroah-Hartman 	u8 control;
29596fd7ce5SGreg Kroah-Hartman 	u8 fcs;
29696fd7ce5SGreg Kroah-Hartman 	u8 *txframe;			/* TX framing buffer */
29796fd7ce5SGreg Kroah-Hartman 
298a579767cSJiri Slaby 	/* Method for the receiver side */
29996fd7ce5SGreg Kroah-Hartman 	void (*receive)(struct gsm_mux *gsm, u8 ch);
30096fd7ce5SGreg Kroah-Hartman 
30196fd7ce5SGreg Kroah-Hartman 	/* Link Layer */
30296fd7ce5SGreg Kroah-Hartman 	unsigned int mru;
30396fd7ce5SGreg Kroah-Hartman 	unsigned int mtu;
30496fd7ce5SGreg Kroah-Hartman 	int initiator;			/* Did we initiate connection */
3055677fcf3SJiri Slaby 	bool dead;			/* Has the mux been shut down */
30696fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci[NUM_DLCI];
307f4f7d632SDaniel Starke 	int old_c_iflag;		/* termios c_iflag value before attach */
3087a9ed9c0SJiri Slaby 	bool constipated;		/* Asked by remote to shut up */
30901aecd91SDaniel Starke 	bool has_devices;		/* Devices were registered */
31096fd7ce5SGreg Kroah-Hartman 
311acdab4cbSFedor Pchelkin 	spinlock_t tx_lock;
31296fd7ce5SGreg Kroah-Hartman 	unsigned int tx_bytes;		/* TX data outstanding */
31396fd7ce5SGreg Kroah-Hartman #define TX_THRESH_HI		8192
31496fd7ce5SGreg Kroah-Hartman #define TX_THRESH_LO		2048
3150af02167SDaniel Starke 	struct list_head tx_ctrl_list;	/* Pending control packets */
3160af02167SDaniel Starke 	struct list_head tx_data_list;	/* Pending data packets */
31796fd7ce5SGreg Kroah-Hartman 
31896fd7ce5SGreg Kroah-Hartman 	/* Control messages */
31915743ae5SFedor Pchelkin 	struct timer_list kick_timer;	/* Kick TX queuing on timeout */
32096fd7ce5SGreg Kroah-Hartman 	struct timer_list t2_timer;	/* Retransmit timer for commands */
32196fd7ce5SGreg Kroah-Hartman 	int cretries;			/* Command retry counter */
32296fd7ce5SGreg Kroah-Hartman 	struct gsm_control *pending_cmd;/* Our current pending command */
32396fd7ce5SGreg Kroah-Hartman 	spinlock_t control_lock;	/* Protects the pending command */
32496fd7ce5SGreg Kroah-Hartman 
32572206cc7SDaniel Starke 	/* Keep-alive */
32672206cc7SDaniel Starke 	struct timer_list ka_timer;	/* Keep-alive response timer */
32772206cc7SDaniel Starke 	u8 ka_num;			/* Keep-alive match pattern */
32872206cc7SDaniel Starke 	signed int ka_retries;		/* Keep-alive retry counter, -1 if not yet initialized */
32972206cc7SDaniel Starke 
33096fd7ce5SGreg Kroah-Hartman 	/* Configuration */
33196fd7ce5SGreg Kroah-Hartman 	int adaption;		/* 1 or 2 supported */
33296fd7ce5SGreg Kroah-Hartman 	u8 ftype;		/* UI or UIH */
33396fd7ce5SGreg Kroah-Hartman 	int t1, t2;		/* Timers in 1/100th of a sec */
3342ec7a802SDaniel Starke 	unsigned int t3;	/* Power wake-up timer in seconds. */
33596fd7ce5SGreg Kroah-Hartman 	int n2;			/* Retry count */
3362ec7a802SDaniel Starke 	u8 k;			/* Window size */
3374ca58966SDaniel Starke 	bool wait_config;	/* Wait for configuration by ioctl before DLCI open */
33872206cc7SDaniel Starke 	u32 keep_alive;		/* Control channel keep-alive in 10ms */
33996fd7ce5SGreg Kroah-Hartman 
34096fd7ce5SGreg Kroah-Hartman 	/* Statistics (not currently exposed) */
34196fd7ce5SGreg Kroah-Hartman 	unsigned long bad_fcs;
34296fd7ce5SGreg Kroah-Hartman 	unsigned long malformed;
34396fd7ce5SGreg Kroah-Hartman 	unsigned long io_error;
344a1ce6da0SDaniel Starke 	unsigned long open_error;
34596fd7ce5SGreg Kroah-Hartman 	unsigned long bad_size;
34696fd7ce5SGreg Kroah-Hartman 	unsigned long unsupported;
34796fd7ce5SGreg Kroah-Hartman };
34896fd7ce5SGreg Kroah-Hartman 
34996fd7ce5SGreg Kroah-Hartman 
35096fd7ce5SGreg Kroah-Hartman /*
35196fd7ce5SGreg Kroah-Hartman  *	Mux objects - needed so that we can translate a tty index into the
35296fd7ce5SGreg Kroah-Hartman  *	relevant mux and DLCI.
35396fd7ce5SGreg Kroah-Hartman  */
35496fd7ce5SGreg Kroah-Hartman 
35596fd7ce5SGreg Kroah-Hartman #define MAX_MUX		4			/* 256 minors */
35696fd7ce5SGreg Kroah-Hartman static struct gsm_mux *gsm_mux[MAX_MUX];	/* GSM muxes */
357399d44a1SGuobin Huang static DEFINE_SPINLOCK(gsm_mux_lock);
35896fd7ce5SGreg Kroah-Hartman 
359d50f6dcaSRuss Gorby static struct tty_driver *gsm_tty_driver;
360d50f6dcaSRuss Gorby 
36196fd7ce5SGreg Kroah-Hartman /*
36296fd7ce5SGreg Kroah-Hartman  *	This section of the driver logic implements the GSM encodings
36396fd7ce5SGreg Kroah-Hartman  *	both the basic and the 'advanced'. Reliable transport is not
36496fd7ce5SGreg Kroah-Hartman  *	supported.
36596fd7ce5SGreg Kroah-Hartman  */
36696fd7ce5SGreg Kroah-Hartman 
36796fd7ce5SGreg Kroah-Hartman #define CR			0x02
36896fd7ce5SGreg Kroah-Hartman #define EA			0x01
36996fd7ce5SGreg Kroah-Hartman #define	PF			0x10
37096fd7ce5SGreg Kroah-Hartman 
37196fd7ce5SGreg Kroah-Hartman /* I is special: the rest are ..*/
37296fd7ce5SGreg Kroah-Hartman #define RR			0x01
37396fd7ce5SGreg Kroah-Hartman #define UI			0x03
37496fd7ce5SGreg Kroah-Hartman #define RNR			0x05
37596fd7ce5SGreg Kroah-Hartman #define REJ			0x09
37696fd7ce5SGreg Kroah-Hartman #define DM			0x0F
37796fd7ce5SGreg Kroah-Hartman #define SABM			0x2F
37896fd7ce5SGreg Kroah-Hartman #define DISC			0x43
37996fd7ce5SGreg Kroah-Hartman #define UA			0x63
38096fd7ce5SGreg Kroah-Hartman #define	UIH			0xEF
38196fd7ce5SGreg Kroah-Hartman 
38296fd7ce5SGreg Kroah-Hartman /* Channel commands */
38396fd7ce5SGreg Kroah-Hartman #define CMD_NSC			0x09
38496fd7ce5SGreg Kroah-Hartman #define CMD_TEST		0x11
38596fd7ce5SGreg Kroah-Hartman #define CMD_PSC			0x21
38696fd7ce5SGreg Kroah-Hartman #define CMD_RLS			0x29
38796fd7ce5SGreg Kroah-Hartman #define CMD_FCOFF		0x31
38896fd7ce5SGreg Kroah-Hartman #define CMD_PN			0x41
38996fd7ce5SGreg Kroah-Hartman #define CMD_RPN			0x49
39096fd7ce5SGreg Kroah-Hartman #define CMD_FCON		0x51
39196fd7ce5SGreg Kroah-Hartman #define CMD_CLD			0x61
39296fd7ce5SGreg Kroah-Hartman #define CMD_SNC			0x69
39396fd7ce5SGreg Kroah-Hartman #define CMD_MSC			0x71
39496fd7ce5SGreg Kroah-Hartman 
39596fd7ce5SGreg Kroah-Hartman /* Virtual modem bits */
39696fd7ce5SGreg Kroah-Hartman #define MDM_FC			0x01
39796fd7ce5SGreg Kroah-Hartman #define MDM_RTC			0x02
39896fd7ce5SGreg Kroah-Hartman #define MDM_RTR			0x04
39996fd7ce5SGreg Kroah-Hartman #define MDM_IC			0x20
40096fd7ce5SGreg Kroah-Hartman #define MDM_DV			0x40
40196fd7ce5SGreg Kroah-Hartman 
40296fd7ce5SGreg Kroah-Hartman #define GSM0_SOF		0xF9
40396fd7ce5SGreg Kroah-Hartman #define GSM1_SOF		0x7E
40496fd7ce5SGreg Kroah-Hartman #define GSM1_ESCAPE		0x7D
40596fd7ce5SGreg Kroah-Hartman #define GSM1_ESCAPE_BITS	0x20
40696fd7ce5SGreg Kroah-Hartman #define XON			0x11
40796fd7ce5SGreg Kroah-Hartman #define XOFF			0x13
4088838b2afSdaniel.starke@siemens.com #define ISO_IEC_646_MASK	0x7F
40996fd7ce5SGreg Kroah-Hartman 
41096fd7ce5SGreg Kroah-Hartman static const struct tty_port_operations gsm_port_ops;
41196fd7ce5SGreg Kroah-Hartman 
41296fd7ce5SGreg Kroah-Hartman /*
41396fd7ce5SGreg Kroah-Hartman  *	CRC table for GSM 0710
41496fd7ce5SGreg Kroah-Hartman  */
41596fd7ce5SGreg Kroah-Hartman 
41696fd7ce5SGreg Kroah-Hartman static const u8 gsm_fcs8[256] = {
41796fd7ce5SGreg Kroah-Hartman 	0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
41896fd7ce5SGreg Kroah-Hartman 	0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
41996fd7ce5SGreg Kroah-Hartman 	0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
42096fd7ce5SGreg Kroah-Hartman 	0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
42196fd7ce5SGreg Kroah-Hartman 	0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D,
42296fd7ce5SGreg Kroah-Hartman 	0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
42396fd7ce5SGreg Kroah-Hartman 	0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51,
42496fd7ce5SGreg Kroah-Hartman 	0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
42596fd7ce5SGreg Kroah-Hartman 	0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
42696fd7ce5SGreg Kroah-Hartman 	0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
42796fd7ce5SGreg Kroah-Hartman 	0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19,
42896fd7ce5SGreg Kroah-Hartman 	0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
42996fd7ce5SGreg Kroah-Hartman 	0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D,
43096fd7ce5SGreg Kroah-Hartman 	0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
43196fd7ce5SGreg Kroah-Hartman 	0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
43296fd7ce5SGreg Kroah-Hartman 	0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
43396fd7ce5SGreg Kroah-Hartman 	0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95,
43496fd7ce5SGreg Kroah-Hartman 	0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
43596fd7ce5SGreg Kroah-Hartman 	0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89,
43696fd7ce5SGreg Kroah-Hartman 	0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
43796fd7ce5SGreg Kroah-Hartman 	0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
43896fd7ce5SGreg Kroah-Hartman 	0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
43996fd7ce5SGreg Kroah-Hartman 	0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1,
44096fd7ce5SGreg Kroah-Hartman 	0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
44196fd7ce5SGreg Kroah-Hartman 	0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5,
44296fd7ce5SGreg Kroah-Hartman 	0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
44396fd7ce5SGreg Kroah-Hartman 	0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
44496fd7ce5SGreg Kroah-Hartman 	0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
44596fd7ce5SGreg Kroah-Hartman 	0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD,
44696fd7ce5SGreg Kroah-Hartman 	0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
44796fd7ce5SGreg Kroah-Hartman 	0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1,
44896fd7ce5SGreg Kroah-Hartman 	0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
44996fd7ce5SGreg Kroah-Hartman };
45096fd7ce5SGreg Kroah-Hartman 
45196fd7ce5SGreg Kroah-Hartman #define INIT_FCS	0xFF
45296fd7ce5SGreg Kroah-Hartman #define GOOD_FCS	0xCF
45396fd7ce5SGreg Kroah-Hartman 
45492f1f0c3SDaniel Starke static void gsm_dlci_close(struct gsm_dlci *dlci);
455a579767cSJiri Slaby static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
456c19ffe00SDaniel Starke static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
4570af02167SDaniel Starke static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
4580af02167SDaniel Starke 								u8 ctrl);
4590af02167SDaniel Starke static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg);
4604ca58966SDaniel Starke static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr);
4610af02167SDaniel Starke static void gsmld_write_trigger(struct gsm_mux *gsm);
4620af02167SDaniel Starke static void gsmld_write_task(struct work_struct *work);
463a579767cSJiri Slaby 
46496fd7ce5SGreg Kroah-Hartman /**
46596fd7ce5SGreg Kroah-Hartman  *	gsm_fcs_add	-	update FCS
46696fd7ce5SGreg Kroah-Hartman  *	@fcs: Current FCS
46796fd7ce5SGreg Kroah-Hartman  *	@c: Next data
46896fd7ce5SGreg Kroah-Hartman  *
46996fd7ce5SGreg Kroah-Hartman  *	Update the FCS to include c. Uses the algorithm in the specification
47096fd7ce5SGreg Kroah-Hartman  *	notes.
47196fd7ce5SGreg Kroah-Hartman  */
47296fd7ce5SGreg Kroah-Hartman 
gsm_fcs_add(u8 fcs,u8 c)47396fd7ce5SGreg Kroah-Hartman static inline u8 gsm_fcs_add(u8 fcs, u8 c)
47496fd7ce5SGreg Kroah-Hartman {
47596fd7ce5SGreg Kroah-Hartman 	return gsm_fcs8[fcs ^ c];
47696fd7ce5SGreg Kroah-Hartman }
47796fd7ce5SGreg Kroah-Hartman 
47896fd7ce5SGreg Kroah-Hartman /**
47996fd7ce5SGreg Kroah-Hartman  *	gsm_fcs_add_block	-	update FCS for a block
48096fd7ce5SGreg Kroah-Hartman  *	@fcs: Current FCS
48196fd7ce5SGreg Kroah-Hartman  *	@c: buffer of data
48296fd7ce5SGreg Kroah-Hartman  *	@len: length of buffer
48396fd7ce5SGreg Kroah-Hartman  *
48496fd7ce5SGreg Kroah-Hartman  *	Update the FCS to include c. Uses the algorithm in the specification
48596fd7ce5SGreg Kroah-Hartman  *	notes.
48696fd7ce5SGreg Kroah-Hartman  */
48796fd7ce5SGreg Kroah-Hartman 
gsm_fcs_add_block(u8 fcs,u8 * c,int len)48896fd7ce5SGreg Kroah-Hartman static inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len)
48996fd7ce5SGreg Kroah-Hartman {
49096fd7ce5SGreg Kroah-Hartman 	while (len--)
49196fd7ce5SGreg Kroah-Hartman 		fcs = gsm_fcs8[fcs ^ *c++];
49296fd7ce5SGreg Kroah-Hartman 	return fcs;
49396fd7ce5SGreg Kroah-Hartman }
49496fd7ce5SGreg Kroah-Hartman 
49596fd7ce5SGreg Kroah-Hartman /**
49696fd7ce5SGreg Kroah-Hartman  *	gsm_read_ea		-	read a byte into an EA
49796fd7ce5SGreg Kroah-Hartman  *	@val: variable holding value
498724ac070SJiri Slaby  *	@c: byte going into the EA
49996fd7ce5SGreg Kroah-Hartman  *
50096fd7ce5SGreg Kroah-Hartman  *	Processes one byte of an EA. Updates the passed variable
50196fd7ce5SGreg Kroah-Hartman  *	and returns 1 if the EA is now completely read
50296fd7ce5SGreg Kroah-Hartman  */
50396fd7ce5SGreg Kroah-Hartman 
gsm_read_ea(unsigned int * val,u8 c)50496fd7ce5SGreg Kroah-Hartman static int gsm_read_ea(unsigned int *val, u8 c)
50596fd7ce5SGreg Kroah-Hartman {
50696fd7ce5SGreg Kroah-Hartman 	/* Add the next 7 bits into the value */
50796fd7ce5SGreg Kroah-Hartman 	*val <<= 7;
50896fd7ce5SGreg Kroah-Hartman 	*val |= c >> 1;
50996fd7ce5SGreg Kroah-Hartman 	/* Was this the last byte of the EA 1 = yes*/
51096fd7ce5SGreg Kroah-Hartman 	return c & EA;
51196fd7ce5SGreg Kroah-Hartman }
51296fd7ce5SGreg Kroah-Hartman 
51396fd7ce5SGreg Kroah-Hartman /**
514bec02248SDaniel Starke  *	gsm_read_ea_val	-	read a value until EA
515bec02248SDaniel Starke  *	@val: variable holding value
516bec02248SDaniel Starke  *	@data: buffer of data
517bec02248SDaniel Starke  *	@dlen: length of data
518bec02248SDaniel Starke  *
519bec02248SDaniel Starke  *	Processes an EA value. Updates the passed variable and
520bec02248SDaniel Starke  *	returns the processed data length.
521bec02248SDaniel Starke  */
gsm_read_ea_val(unsigned int * val,const u8 * data,int dlen)522bec02248SDaniel Starke static unsigned int gsm_read_ea_val(unsigned int *val, const u8 *data, int dlen)
523bec02248SDaniel Starke {
524bec02248SDaniel Starke 	unsigned int len = 0;
525bec02248SDaniel Starke 
526bec02248SDaniel Starke 	for (; dlen > 0; dlen--) {
527bec02248SDaniel Starke 		len++;
528bec02248SDaniel Starke 		if (gsm_read_ea(val, *data++))
529bec02248SDaniel Starke 			break;
530bec02248SDaniel Starke 	}
531bec02248SDaniel Starke 	return len;
532bec02248SDaniel Starke }
533bec02248SDaniel Starke 
534bec02248SDaniel Starke /**
53596fd7ce5SGreg Kroah-Hartman  *	gsm_encode_modem	-	encode modem data bits
53696fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI to encode from
53796fd7ce5SGreg Kroah-Hartman  *
53896fd7ce5SGreg Kroah-Hartman  *	Returns the correct GSM encoded modem status bits (6 bit field) for
53996fd7ce5SGreg Kroah-Hartman  *	the current status of the DLCI and attached tty object
54096fd7ce5SGreg Kroah-Hartman  */
54196fd7ce5SGreg Kroah-Hartman 
gsm_encode_modem(const struct gsm_dlci * dlci)54296fd7ce5SGreg Kroah-Hartman static u8 gsm_encode_modem(const struct gsm_dlci *dlci)
54396fd7ce5SGreg Kroah-Hartman {
54496fd7ce5SGreg Kroah-Hartman 	u8 modembits = 0;
54596fd7ce5SGreg Kroah-Hartman 	/* FC is true flow control not modem bits */
54696fd7ce5SGreg Kroah-Hartman 	if (dlci->throttled)
54796fd7ce5SGreg Kroah-Hartman 		modembits |= MDM_FC;
54896fd7ce5SGreg Kroah-Hartman 	if (dlci->modem_tx & TIOCM_DTR)
54996fd7ce5SGreg Kroah-Hartman 		modembits |= MDM_RTC;
55096fd7ce5SGreg Kroah-Hartman 	if (dlci->modem_tx & TIOCM_RTS)
55196fd7ce5SGreg Kroah-Hartman 		modembits |= MDM_RTR;
55296fd7ce5SGreg Kroah-Hartman 	if (dlci->modem_tx & TIOCM_RI)
55396fd7ce5SGreg Kroah-Hartman 		modembits |= MDM_IC;
554737b0ef3Sdaniel.starke@siemens.com 	if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator)
55596fd7ce5SGreg Kroah-Hartman 		modembits |= MDM_DV;
55642ec0b93SDaniel Starke 	/* special mappings for passive side to operate as UE */
55742ec0b93SDaniel Starke 	if (dlci->modem_tx & TIOCM_OUT1)
55842ec0b93SDaniel Starke 		modembits |= MDM_IC;
55942ec0b93SDaniel Starke 	if (dlci->modem_tx & TIOCM_OUT2)
56042ec0b93SDaniel Starke 		modembits |= MDM_DV;
56196fd7ce5SGreg Kroah-Hartman 	return modembits;
56296fd7ce5SGreg Kroah-Hartman }
56396fd7ce5SGreg Kroah-Hartman 
gsm_hex_dump_bytes(const char * fname,const u8 * data,unsigned long len)564925ea0faSTony Lindgren static void gsm_hex_dump_bytes(const char *fname, const u8 *data,
565925ea0faSTony Lindgren 			       unsigned long len)
566925ea0faSTony Lindgren {
567925ea0faSTony Lindgren 	char *prefix;
568925ea0faSTony Lindgren 
569925ea0faSTony Lindgren 	if (!fname) {
570925ea0faSTony Lindgren 		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data, len,
571925ea0faSTony Lindgren 			       true);
572925ea0faSTony Lindgren 		return;
573925ea0faSTony Lindgren 	}
574925ea0faSTony Lindgren 
575e74024b2STony Lindgren 	prefix = kasprintf(GFP_ATOMIC, "%s: ", fname);
576925ea0faSTony Lindgren 	if (!prefix)
577925ea0faSTony Lindgren 		return;
578925ea0faSTony Lindgren 	print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 16, 1, data, len,
579925ea0faSTony Lindgren 		       true);
580925ea0faSTony Lindgren 	kfree(prefix);
581925ea0faSTony Lindgren }
582925ea0faSTony Lindgren 
58396fd7ce5SGreg Kroah-Hartman /**
58492f1f0c3SDaniel Starke  * gsm_encode_params	-	encode DLCI parameters
58592f1f0c3SDaniel Starke  * @dlci: DLCI to encode from
58692f1f0c3SDaniel Starke  * @params: buffer to fill with the encoded parameters
58792f1f0c3SDaniel Starke  *
58892f1f0c3SDaniel Starke  * Encodes the parameters according to GSM 07.10 section 5.4.6.3.1
58992f1f0c3SDaniel Starke  * table 3.
59092f1f0c3SDaniel Starke  */
gsm_encode_params(const struct gsm_dlci * dlci,struct gsm_dlci_param_bits * params)59192f1f0c3SDaniel Starke static int gsm_encode_params(const struct gsm_dlci *dlci,
59292f1f0c3SDaniel Starke 			     struct gsm_dlci_param_bits *params)
59392f1f0c3SDaniel Starke {
59492f1f0c3SDaniel Starke 	const struct gsm_mux *gsm = dlci->gsm;
59592f1f0c3SDaniel Starke 	unsigned int i, cl;
59692f1f0c3SDaniel Starke 
59792f1f0c3SDaniel Starke 	switch (dlci->ftype) {
59892f1f0c3SDaniel Starke 	case UIH:
59992f1f0c3SDaniel Starke 		i = 0; /* UIH */
60092f1f0c3SDaniel Starke 		break;
60192f1f0c3SDaniel Starke 	case UI:
60292f1f0c3SDaniel Starke 		i = 1; /* UI */
60392f1f0c3SDaniel Starke 		break;
60492f1f0c3SDaniel Starke 	default:
60592f1f0c3SDaniel Starke 		pr_debug("unsupported frame type %d\n", dlci->ftype);
60692f1f0c3SDaniel Starke 		return -EINVAL;
60792f1f0c3SDaniel Starke 	}
60892f1f0c3SDaniel Starke 
60992f1f0c3SDaniel Starke 	switch (dlci->adaption) {
61092f1f0c3SDaniel Starke 	case 1: /* Unstructured */
61192f1f0c3SDaniel Starke 		cl = 0; /* convergence layer type 1 */
61292f1f0c3SDaniel Starke 		break;
61392f1f0c3SDaniel Starke 	case 2: /* Unstructured with modem bits. */
61492f1f0c3SDaniel Starke 		cl = 1; /* convergence layer type 2 */
61592f1f0c3SDaniel Starke 		break;
61692f1f0c3SDaniel Starke 	default:
61792f1f0c3SDaniel Starke 		pr_debug("unsupported adaption %d\n", dlci->adaption);
61892f1f0c3SDaniel Starke 		return -EINVAL;
61992f1f0c3SDaniel Starke 	}
62092f1f0c3SDaniel Starke 
62192f1f0c3SDaniel Starke 	params->d_bits = FIELD_PREP(PN_D_FIELD_DLCI, dlci->addr);
62292f1f0c3SDaniel Starke 	/* UIH, convergence layer type 1 */
62392f1f0c3SDaniel Starke 	params->i_cl_bits = FIELD_PREP(PN_I_CL_FIELD_FTYPE, i) |
62492f1f0c3SDaniel Starke 			    FIELD_PREP(PN_I_CL_FIELD_ADAPTION, cl);
62592f1f0c3SDaniel Starke 	params->p_bits = FIELD_PREP(PN_P_FIELD_PRIO, dlci->prio);
62692f1f0c3SDaniel Starke 	params->t_bits = FIELD_PREP(PN_T_FIELD_T1, gsm->t1);
62792f1f0c3SDaniel Starke 	params->n_bits = cpu_to_le16(FIELD_PREP(PN_N_FIELD_N1, dlci->mtu));
62892f1f0c3SDaniel Starke 	params->na_bits = FIELD_PREP(PN_NA_FIELD_N2, gsm->n2);
62992f1f0c3SDaniel Starke 	params->k_bits = FIELD_PREP(PN_K_FIELD_K, dlci->k);
63092f1f0c3SDaniel Starke 
63192f1f0c3SDaniel Starke 	return 0;
63292f1f0c3SDaniel Starke }
63392f1f0c3SDaniel Starke 
63492f1f0c3SDaniel Starke /**
63501aecd91SDaniel Starke  *	gsm_register_devices	-	register all tty devices for a given mux index
63601aecd91SDaniel Starke  *
63701aecd91SDaniel Starke  *	@driver: the tty driver that describes the tty devices
63801aecd91SDaniel Starke  *	@index:  the mux number is used to calculate the minor numbers of the
63901aecd91SDaniel Starke  *	         ttys for this mux and may differ from the position in the
64001aecd91SDaniel Starke  *	         mux array.
64101aecd91SDaniel Starke  */
gsm_register_devices(struct tty_driver * driver,unsigned int index)64201aecd91SDaniel Starke static int gsm_register_devices(struct tty_driver *driver, unsigned int index)
64301aecd91SDaniel Starke {
64401aecd91SDaniel Starke 	struct device *dev;
64501aecd91SDaniel Starke 	int i;
64601aecd91SDaniel Starke 	unsigned int base;
64701aecd91SDaniel Starke 
64801aecd91SDaniel Starke 	if (!driver || index >= MAX_MUX)
64901aecd91SDaniel Starke 		return -EINVAL;
65001aecd91SDaniel Starke 
65101aecd91SDaniel Starke 	base = index * NUM_DLCI; /* first minor for this index */
65201aecd91SDaniel Starke 	for (i = 1; i < NUM_DLCI; i++) {
65301aecd91SDaniel Starke 		/* Don't register device 0 - this is the control channel
65401aecd91SDaniel Starke 		 * and not a usable tty interface
65501aecd91SDaniel Starke 		 */
65601aecd91SDaniel Starke 		dev = tty_register_device(gsm_tty_driver, base + i, NULL);
65701aecd91SDaniel Starke 		if (IS_ERR(dev)) {
658c07da737SDaniel Starke 			if (debug & DBG_ERRORS)
65901aecd91SDaniel Starke 				pr_info("%s failed to register device minor %u",
66001aecd91SDaniel Starke 					__func__, base + i);
66101aecd91SDaniel Starke 			for (i--; i >= 1; i--)
66201aecd91SDaniel Starke 				tty_unregister_device(gsm_tty_driver, base + i);
66301aecd91SDaniel Starke 			return PTR_ERR(dev);
66401aecd91SDaniel Starke 		}
66501aecd91SDaniel Starke 	}
66601aecd91SDaniel Starke 
66701aecd91SDaniel Starke 	return 0;
66801aecd91SDaniel Starke }
66901aecd91SDaniel Starke 
67001aecd91SDaniel Starke /**
67101aecd91SDaniel Starke  *	gsm_unregister_devices	-	unregister all tty devices for a given mux index
67201aecd91SDaniel Starke  *
67301aecd91SDaniel Starke  *	@driver: the tty driver that describes the tty devices
67401aecd91SDaniel Starke  *	@index:  the mux number is used to calculate the minor numbers of the
67501aecd91SDaniel Starke  *	         ttys for this mux and may differ from the position in the
67601aecd91SDaniel Starke  *	         mux array.
67701aecd91SDaniel Starke  */
gsm_unregister_devices(struct tty_driver * driver,unsigned int index)67801aecd91SDaniel Starke static void gsm_unregister_devices(struct tty_driver *driver,
67901aecd91SDaniel Starke 				   unsigned int index)
68001aecd91SDaniel Starke {
68101aecd91SDaniel Starke 	int i;
68201aecd91SDaniel Starke 	unsigned int base;
68301aecd91SDaniel Starke 
68401aecd91SDaniel Starke 	if (!driver || index >= MAX_MUX)
68501aecd91SDaniel Starke 		return;
68601aecd91SDaniel Starke 
68701aecd91SDaniel Starke 	base = index * NUM_DLCI; /* first minor for this index */
68801aecd91SDaniel Starke 	for (i = 1; i < NUM_DLCI; i++) {
68901aecd91SDaniel Starke 		/* Don't unregister device 0 - this is the control
69001aecd91SDaniel Starke 		 * channel and not a usable tty interface
69101aecd91SDaniel Starke 		 */
69201aecd91SDaniel Starke 		tty_unregister_device(gsm_tty_driver, base + i);
69301aecd91SDaniel Starke 	}
69401aecd91SDaniel Starke }
69501aecd91SDaniel Starke 
69601aecd91SDaniel Starke /**
69796fd7ce5SGreg Kroah-Hartman  *	gsm_print_packet	-	display a frame for debug
69896fd7ce5SGreg Kroah-Hartman  *	@hdr: header to print before decode
69996fd7ce5SGreg Kroah-Hartman  *	@addr: address EA from the frame
70057435c42Sdaniel.starke@siemens.com  *	@cr: C/R bit seen as initiator
70196fd7ce5SGreg Kroah-Hartman  *	@control: control including PF bit
70296fd7ce5SGreg Kroah-Hartman  *	@data: following data bytes
70396fd7ce5SGreg Kroah-Hartman  *	@dlen: length of data
70496fd7ce5SGreg Kroah-Hartman  *
70596fd7ce5SGreg Kroah-Hartman  *	Displays a packet in human readable format for debugging purposes. The
70696fd7ce5SGreg Kroah-Hartman  *	style is based on amateur radio LAP-B dump display.
70796fd7ce5SGreg Kroah-Hartman  */
70896fd7ce5SGreg Kroah-Hartman 
gsm_print_packet(const char * hdr,int addr,int cr,u8 control,const u8 * data,int dlen)70996fd7ce5SGreg Kroah-Hartman static void gsm_print_packet(const char *hdr, int addr, int cr,
71096fd7ce5SGreg Kroah-Hartman 					u8 control, const u8 *data, int dlen)
71196fd7ce5SGreg Kroah-Hartman {
712c07da737SDaniel Starke 	if (!(debug & DBG_DUMP))
71396fd7ce5SGreg Kroah-Hartman 		return;
714c22d054fSDaniel Starke 	/* Only show user payload frames if debug & DBG_PAYLOAD */
715c22d054fSDaniel Starke 	if (!(debug & DBG_PAYLOAD) && addr != 0)
716c22d054fSDaniel Starke 		if ((control & ~PF) == UI || (control & ~PF) == UIH)
717c22d054fSDaniel Starke 			return;
71896fd7ce5SGreg Kroah-Hartman 
7195f9a31d6SAlan Cox 	pr_info("%s %d) %c: ", hdr, addr, "RC"[cr]);
72096fd7ce5SGreg Kroah-Hartman 
72196fd7ce5SGreg Kroah-Hartman 	switch (control & ~PF) {
72296fd7ce5SGreg Kroah-Hartman 	case SABM:
7235f9a31d6SAlan Cox 		pr_cont("SABM");
72496fd7ce5SGreg Kroah-Hartman 		break;
72596fd7ce5SGreg Kroah-Hartman 	case UA:
7265f9a31d6SAlan Cox 		pr_cont("UA");
72796fd7ce5SGreg Kroah-Hartman 		break;
72896fd7ce5SGreg Kroah-Hartman 	case DISC:
7295f9a31d6SAlan Cox 		pr_cont("DISC");
73096fd7ce5SGreg Kroah-Hartman 		break;
73196fd7ce5SGreg Kroah-Hartman 	case DM:
7325f9a31d6SAlan Cox 		pr_cont("DM");
73396fd7ce5SGreg Kroah-Hartman 		break;
73496fd7ce5SGreg Kroah-Hartman 	case UI:
7355f9a31d6SAlan Cox 		pr_cont("UI");
73696fd7ce5SGreg Kroah-Hartman 		break;
73796fd7ce5SGreg Kroah-Hartman 	case UIH:
7385f9a31d6SAlan Cox 		pr_cont("UIH");
73996fd7ce5SGreg Kroah-Hartman 		break;
74096fd7ce5SGreg Kroah-Hartman 	default:
74196fd7ce5SGreg Kroah-Hartman 		if (!(control & 0x01)) {
7425f9a31d6SAlan Cox 			pr_cont("I N(S)%d N(R)%d",
74347fdd641SAlan Cox 				(control & 0x0E) >> 1, (control & 0xE0) >> 5);
74496fd7ce5SGreg Kroah-Hartman 		} else switch (control & 0x0F) {
74596fd7ce5SGreg Kroah-Hartman 			case RR:
7465f9a31d6SAlan Cox 				pr_cont("RR(%d)", (control & 0xE0) >> 5);
74796fd7ce5SGreg Kroah-Hartman 				break;
74896fd7ce5SGreg Kroah-Hartman 			case RNR:
7495f9a31d6SAlan Cox 				pr_cont("RNR(%d)", (control & 0xE0) >> 5);
75096fd7ce5SGreg Kroah-Hartman 				break;
75196fd7ce5SGreg Kroah-Hartman 			case REJ:
7525f9a31d6SAlan Cox 				pr_cont("REJ(%d)", (control & 0xE0) >> 5);
75396fd7ce5SGreg Kroah-Hartman 				break;
75496fd7ce5SGreg Kroah-Hartman 			default:
7555f9a31d6SAlan Cox 				pr_cont("[%02X]", control);
75696fd7ce5SGreg Kroah-Hartman 		}
75796fd7ce5SGreg Kroah-Hartman 	}
75896fd7ce5SGreg Kroah-Hartman 
75996fd7ce5SGreg Kroah-Hartman 	if (control & PF)
7605f9a31d6SAlan Cox 		pr_cont("(P)");
76196fd7ce5SGreg Kroah-Hartman 	else
7625f9a31d6SAlan Cox 		pr_cont("(F)");
76396fd7ce5SGreg Kroah-Hartman 
764925ea0faSTony Lindgren 	gsm_hex_dump_bytes(NULL, data, dlen);
76596fd7ce5SGreg Kroah-Hartman }
76696fd7ce5SGreg Kroah-Hartman 
76796fd7ce5SGreg Kroah-Hartman 
76896fd7ce5SGreg Kroah-Hartman /*
76996fd7ce5SGreg Kroah-Hartman  *	Link level transmission side
77096fd7ce5SGreg Kroah-Hartman  */
77196fd7ce5SGreg Kroah-Hartman 
77296fd7ce5SGreg Kroah-Hartman /**
773542a121aSLee Jones  *	gsm_stuff_frame	-	bytestuff a packet
774724ac070SJiri Slaby  *	@input: input buffer
775724ac070SJiri Slaby  *	@output: output buffer
77696fd7ce5SGreg Kroah-Hartman  *	@len: length of input
77796fd7ce5SGreg Kroah-Hartman  *
77896fd7ce5SGreg Kroah-Hartman  *	Expand a buffer by bytestuffing it. The worst case size change
77996fd7ce5SGreg Kroah-Hartman  *	is doubling and the caller is responsible for handing out
78096fd7ce5SGreg Kroah-Hartman  *	suitable sized buffers.
78196fd7ce5SGreg Kroah-Hartman  */
78296fd7ce5SGreg Kroah-Hartman 
gsm_stuff_frame(const u8 * input,u8 * output,int len)78396fd7ce5SGreg Kroah-Hartman static int gsm_stuff_frame(const u8 *input, u8 *output, int len)
78496fd7ce5SGreg Kroah-Hartman {
78596fd7ce5SGreg Kroah-Hartman 	int olen = 0;
78696fd7ce5SGreg Kroah-Hartman 	while (len--) {
78796fd7ce5SGreg Kroah-Hartman 		if (*input == GSM1_SOF || *input == GSM1_ESCAPE
7888838b2afSdaniel.starke@siemens.com 		    || (*input & ISO_IEC_646_MASK) == XON
7898838b2afSdaniel.starke@siemens.com 		    || (*input & ISO_IEC_646_MASK) == XOFF) {
79096fd7ce5SGreg Kroah-Hartman 			*output++ = GSM1_ESCAPE;
79196fd7ce5SGreg Kroah-Hartman 			*output++ = *input++ ^ GSM1_ESCAPE_BITS;
79296fd7ce5SGreg Kroah-Hartman 			olen++;
79396fd7ce5SGreg Kroah-Hartman 		} else
79496fd7ce5SGreg Kroah-Hartman 			*output++ = *input++;
79596fd7ce5SGreg Kroah-Hartman 		olen++;
79696fd7ce5SGreg Kroah-Hartman 	}
79796fd7ce5SGreg Kroah-Hartman 	return olen;
79896fd7ce5SGreg Kroah-Hartman }
79996fd7ce5SGreg Kroah-Hartman 
80096fd7ce5SGreg Kroah-Hartman /**
80196fd7ce5SGreg Kroah-Hartman  *	gsm_send	-	send a control frame
80296fd7ce5SGreg Kroah-Hartman  *	@gsm: our GSM mux
80396fd7ce5SGreg Kroah-Hartman  *	@addr: address for control frame
80457435c42Sdaniel.starke@siemens.com  *	@cr: command/response bit seen as initiator
80596fd7ce5SGreg Kroah-Hartman  *	@control:  control byte including PF bit
80696fd7ce5SGreg Kroah-Hartman  *
8070af02167SDaniel Starke  *	Format up and transmit a control frame. These should be transmitted
8080af02167SDaniel Starke  *	ahead of data when they are needed.
80996fd7ce5SGreg Kroah-Hartman  */
gsm_send(struct gsm_mux * gsm,int addr,int cr,int control)8100af02167SDaniel Starke static int gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
81196fd7ce5SGreg Kroah-Hartman {
8120af02167SDaniel Starke 	struct gsm_msg *msg;
8130af02167SDaniel Starke 	u8 *dp;
81457435c42Sdaniel.starke@siemens.com 	int ocr;
815acdab4cbSFedor Pchelkin 	unsigned long flags;
8160af02167SDaniel Starke 
8170af02167SDaniel Starke 	msg = gsm_data_alloc(gsm, addr, 0, control);
8180af02167SDaniel Starke 	if (!msg)
8190af02167SDaniel Starke 		return -ENOMEM;
82057435c42Sdaniel.starke@siemens.com 
82157435c42Sdaniel.starke@siemens.com 	/* toggle C/R coding if not initiator */
82257435c42Sdaniel.starke@siemens.com 	ocr = cr ^ (gsm->initiator ? 0 : 1);
82396fd7ce5SGreg Kroah-Hartman 
8240af02167SDaniel Starke 	msg->data -= 3;
8250af02167SDaniel Starke 	dp = msg->data;
8260af02167SDaniel Starke 	*dp++ = (addr << 2) | (ocr << 1) | EA;
8270af02167SDaniel Starke 	*dp++ = control;
8280af02167SDaniel Starke 
82919fb0a66SDaniel Starke 	if (gsm->encoding == GSM_BASIC_OPT)
8300af02167SDaniel Starke 		*dp++ = EA; /* Length of data = 0 */
8310af02167SDaniel Starke 
8320af02167SDaniel Starke 	*dp = 0xFF - gsm_fcs_add_block(INIT_FCS, msg->data, dp - msg->data);
8330af02167SDaniel Starke 	msg->len = (dp - msg->data) + 1;
8340af02167SDaniel Starke 
8350af02167SDaniel Starke 	gsm_print_packet("Q->", addr, cr, control, NULL, 0);
8360af02167SDaniel Starke 
837acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&gsm->tx_lock, flags);
8380af02167SDaniel Starke 	list_add_tail(&msg->list, &gsm->tx_ctrl_list);
8390af02167SDaniel Starke 	gsm->tx_bytes += msg->len;
840acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&gsm->tx_lock, flags);
8410af02167SDaniel Starke 	gsmld_write_trigger(gsm);
8420af02167SDaniel Starke 
8430af02167SDaniel Starke 	return 0;
84496fd7ce5SGreg Kroah-Hartman }
8450af02167SDaniel Starke 
8460af02167SDaniel Starke /**
8470af02167SDaniel Starke  *	gsm_dlci_clear_queues	-	remove outstanding data for a DLCI
8480af02167SDaniel Starke  *	@gsm: mux
8490af02167SDaniel Starke  *	@dlci: clear for this DLCI
8500af02167SDaniel Starke  *
8510af02167SDaniel Starke  *	Clears the data queues for a given DLCI.
8520af02167SDaniel Starke  */
gsm_dlci_clear_queues(struct gsm_mux * gsm,struct gsm_dlci * dlci)8530af02167SDaniel Starke static void gsm_dlci_clear_queues(struct gsm_mux *gsm, struct gsm_dlci *dlci)
8540af02167SDaniel Starke {
8550af02167SDaniel Starke 	struct gsm_msg *msg, *nmsg;
8560af02167SDaniel Starke 	int addr = dlci->addr;
8570af02167SDaniel Starke 	unsigned long flags;
8580af02167SDaniel Starke 
8590af02167SDaniel Starke 	/* Clear DLCI write fifo first */
8600af02167SDaniel Starke 	spin_lock_irqsave(&dlci->lock, flags);
8610af02167SDaniel Starke 	kfifo_reset(&dlci->fifo);
8620af02167SDaniel Starke 	spin_unlock_irqrestore(&dlci->lock, flags);
8630af02167SDaniel Starke 
8640af02167SDaniel Starke 	/* Clear data packets in MUX write queue */
865acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&gsm->tx_lock, flags);
8660af02167SDaniel Starke 	list_for_each_entry_safe(msg, nmsg, &gsm->tx_data_list, list) {
8670af02167SDaniel Starke 		if (msg->addr != addr)
8680af02167SDaniel Starke 			continue;
8690af02167SDaniel Starke 		gsm->tx_bytes -= msg->len;
8700af02167SDaniel Starke 		list_del(&msg->list);
8710af02167SDaniel Starke 		kfree(msg);
872f999c3b3SZhenguo Zhao 	}
873acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&gsm->tx_lock, flags);
87496fd7ce5SGreg Kroah-Hartman }
87596fd7ce5SGreg Kroah-Hartman 
87696fd7ce5SGreg Kroah-Hartman /**
87796fd7ce5SGreg Kroah-Hartman  *	gsm_response	-	send a control response
87896fd7ce5SGreg Kroah-Hartman  *	@gsm: our GSM mux
87996fd7ce5SGreg Kroah-Hartman  *	@addr: address for control frame
88096fd7ce5SGreg Kroah-Hartman  *	@control:  control byte including PF bit
88196fd7ce5SGreg Kroah-Hartman  *
88296fd7ce5SGreg Kroah-Hartman  *	Format up and transmit a link level response frame.
88396fd7ce5SGreg Kroah-Hartman  */
88496fd7ce5SGreg Kroah-Hartman 
gsm_response(struct gsm_mux * gsm,int addr,int control)88596fd7ce5SGreg Kroah-Hartman static inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
88696fd7ce5SGreg Kroah-Hartman {
88757435c42Sdaniel.starke@siemens.com 	gsm_send(gsm, addr, 0, control);
88896fd7ce5SGreg Kroah-Hartman }
88996fd7ce5SGreg Kroah-Hartman 
89096fd7ce5SGreg Kroah-Hartman /**
89196fd7ce5SGreg Kroah-Hartman  *	gsm_command	-	send a control command
89296fd7ce5SGreg Kroah-Hartman  *	@gsm: our GSM mux
89396fd7ce5SGreg Kroah-Hartman  *	@addr: address for control frame
89496fd7ce5SGreg Kroah-Hartman  *	@control:  control byte including PF bit
89596fd7ce5SGreg Kroah-Hartman  *
89696fd7ce5SGreg Kroah-Hartman  *	Format up and transmit a link level command frame.
89796fd7ce5SGreg Kroah-Hartman  */
89896fd7ce5SGreg Kroah-Hartman 
gsm_command(struct gsm_mux * gsm,int addr,int control)89996fd7ce5SGreg Kroah-Hartman static inline void gsm_command(struct gsm_mux *gsm, int addr, int control)
90096fd7ce5SGreg Kroah-Hartman {
90196fd7ce5SGreg Kroah-Hartman 	gsm_send(gsm, addr, 1, control);
90296fd7ce5SGreg Kroah-Hartman }
90396fd7ce5SGreg Kroah-Hartman 
90496fd7ce5SGreg Kroah-Hartman /* Data transmission */
90596fd7ce5SGreg Kroah-Hartman 
90696fd7ce5SGreg Kroah-Hartman #define HDR_LEN		6	/* ADDR CTRL [LEN.2] DATA FCS */
90796fd7ce5SGreg Kroah-Hartman 
90896fd7ce5SGreg Kroah-Hartman /**
90996fd7ce5SGreg Kroah-Hartman  *	gsm_data_alloc		-	allocate data frame
91096fd7ce5SGreg Kroah-Hartman  *	@gsm: GSM mux
91196fd7ce5SGreg Kroah-Hartman  *	@addr: DLCI address
91296fd7ce5SGreg Kroah-Hartman  *	@len: length excluding header and FCS
91396fd7ce5SGreg Kroah-Hartman  *	@ctrl: control byte
91496fd7ce5SGreg Kroah-Hartman  *
91596fd7ce5SGreg Kroah-Hartman  *	Allocate a new data buffer for sending frames with data. Space is left
91696fd7ce5SGreg Kroah-Hartman  *	at the front for header bytes but that is treated as an implementation
91796fd7ce5SGreg Kroah-Hartman  *	detail and not for the high level code to use
91896fd7ce5SGreg Kroah-Hartman  */
91996fd7ce5SGreg Kroah-Hartman 
gsm_data_alloc(struct gsm_mux * gsm,u8 addr,int len,u8 ctrl)92096fd7ce5SGreg Kroah-Hartman static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
92196fd7ce5SGreg Kroah-Hartman 								u8 ctrl)
92296fd7ce5SGreg Kroah-Hartman {
92396fd7ce5SGreg Kroah-Hartman 	struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN,
92496fd7ce5SGreg Kroah-Hartman 								GFP_ATOMIC);
92596fd7ce5SGreg Kroah-Hartman 	if (m == NULL)
92696fd7ce5SGreg Kroah-Hartman 		return NULL;
92796fd7ce5SGreg Kroah-Hartman 	m->data = m->buffer + HDR_LEN - 1;	/* Allow for FCS */
92896fd7ce5SGreg Kroah-Hartman 	m->len = len;
92996fd7ce5SGreg Kroah-Hartman 	m->addr = addr;
93096fd7ce5SGreg Kroah-Hartman 	m->ctrl = ctrl;
931b4338e1eSRuss Gorby 	INIT_LIST_HEAD(&m->list);
93296fd7ce5SGreg Kroah-Hartman 	return m;
93396fd7ce5SGreg Kroah-Hartman }
93496fd7ce5SGreg Kroah-Hartman 
93596fd7ce5SGreg Kroah-Hartman /**
9360af02167SDaniel Starke  *	gsm_send_packet	-	sends a single packet
9370af02167SDaniel Starke  *	@gsm: GSM Mux
9380af02167SDaniel Starke  *	@msg: packet to send
9390af02167SDaniel Starke  *
9400af02167SDaniel Starke  *	The given packet is encoded and sent out. No memory is freed.
9410af02167SDaniel Starke  *	The caller must hold the gsm tx lock.
9420af02167SDaniel Starke  */
gsm_send_packet(struct gsm_mux * gsm,struct gsm_msg * msg)9430af02167SDaniel Starke static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg)
9440af02167SDaniel Starke {
9450af02167SDaniel Starke 	int len, ret;
9460af02167SDaniel Starke 
9470af02167SDaniel Starke 
94819fb0a66SDaniel Starke 	if (gsm->encoding == GSM_BASIC_OPT) {
9490af02167SDaniel Starke 		gsm->txframe[0] = GSM0_SOF;
9500af02167SDaniel Starke 		memcpy(gsm->txframe + 1, msg->data, msg->len);
9510af02167SDaniel Starke 		gsm->txframe[msg->len + 1] = GSM0_SOF;
9520af02167SDaniel Starke 		len = msg->len + 2;
9530af02167SDaniel Starke 	} else {
9540af02167SDaniel Starke 		gsm->txframe[0] = GSM1_SOF;
9550af02167SDaniel Starke 		len = gsm_stuff_frame(msg->data, gsm->txframe + 1, msg->len);
9560af02167SDaniel Starke 		gsm->txframe[len + 1] = GSM1_SOF;
9570af02167SDaniel Starke 		len += 2;
9580af02167SDaniel Starke 	}
9590af02167SDaniel Starke 
960c07da737SDaniel Starke 	if (debug & DBG_DATA)
9610af02167SDaniel Starke 		gsm_hex_dump_bytes(__func__, gsm->txframe, len);
9620af02167SDaniel Starke 	gsm_print_packet("-->", msg->addr, gsm->initiator, msg->ctrl, msg->data,
9630af02167SDaniel Starke 			 msg->len);
9640af02167SDaniel Starke 
9650af02167SDaniel Starke 	ret = gsmld_output(gsm, gsm->txframe, len);
9660af02167SDaniel Starke 	if (ret <= 0)
9670af02167SDaniel Starke 		return ret;
9680af02167SDaniel Starke 	/* FIXME: Can eliminate one SOF in many more cases */
9690af02167SDaniel Starke 	gsm->tx_bytes -= msg->len;
9700af02167SDaniel Starke 
9710af02167SDaniel Starke 	return 0;
9720af02167SDaniel Starke }
9730af02167SDaniel Starke 
9740af02167SDaniel Starke /**
975bec02248SDaniel Starke  *	gsm_is_flow_ctrl_msg	-	checks if flow control message
976bec02248SDaniel Starke  *	@msg: message to check
977bec02248SDaniel Starke  *
978bec02248SDaniel Starke  *	Returns true if the given message is a flow control command of the
979bec02248SDaniel Starke  *	control channel. False is returned in any other case.
980bec02248SDaniel Starke  */
gsm_is_flow_ctrl_msg(struct gsm_msg * msg)981bec02248SDaniel Starke static bool gsm_is_flow_ctrl_msg(struct gsm_msg *msg)
982bec02248SDaniel Starke {
983bec02248SDaniel Starke 	unsigned int cmd;
984bec02248SDaniel Starke 
985bec02248SDaniel Starke 	if (msg->addr > 0)
986bec02248SDaniel Starke 		return false;
987bec02248SDaniel Starke 
988bec02248SDaniel Starke 	switch (msg->ctrl & ~PF) {
989bec02248SDaniel Starke 	case UI:
990bec02248SDaniel Starke 	case UIH:
991bec02248SDaniel Starke 		cmd = 0;
992bec02248SDaniel Starke 		if (gsm_read_ea_val(&cmd, msg->data + 2, msg->len - 2) < 1)
993bec02248SDaniel Starke 			break;
994bec02248SDaniel Starke 		switch (cmd & ~PF) {
995bec02248SDaniel Starke 		case CMD_FCOFF:
996bec02248SDaniel Starke 		case CMD_FCON:
997bec02248SDaniel Starke 			return true;
998bec02248SDaniel Starke 		}
999bec02248SDaniel Starke 		break;
1000bec02248SDaniel Starke 	}
1001bec02248SDaniel Starke 
1002bec02248SDaniel Starke 	return false;
1003bec02248SDaniel Starke }
1004bec02248SDaniel Starke 
1005bec02248SDaniel Starke /**
100696fd7ce5SGreg Kroah-Hartman  *	gsm_data_kick	-	poke the queue
100796fd7ce5SGreg Kroah-Hartman  *	@gsm: GSM Mux
100896fd7ce5SGreg Kroah-Hartman  *
100996fd7ce5SGreg Kroah-Hartman  *	The tty device has called us to indicate that room has appeared in
10100af02167SDaniel Starke  *	the transmit queue. Ram more data into the pipe if we have any.
1011c01af4feSFrederic Berat  *	If we have been flow-stopped by a CMD_FCOFF, then we can only
10120af02167SDaniel Starke  *	send messages on DLCI0 until CMD_FCON. The caller must hold
10130af02167SDaniel Starke  *	the gsm tx lock.
101496fd7ce5SGreg Kroah-Hartman  */
gsm_data_kick(struct gsm_mux * gsm)10150af02167SDaniel Starke static int gsm_data_kick(struct gsm_mux *gsm)
101696fd7ce5SGreg Kroah-Hartman {
1017b4338e1eSRuss Gorby 	struct gsm_msg *msg, *nmsg;
10180af02167SDaniel Starke 	struct gsm_dlci *dlci;
10190af02167SDaniel Starke 	int ret;
102096fd7ce5SGreg Kroah-Hartman 
10210af02167SDaniel Starke 	clear_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
10220af02167SDaniel Starke 
10230af02167SDaniel Starke 	/* Serialize control messages and control channel messages first */
10240af02167SDaniel Starke 	list_for_each_entry_safe(msg, nmsg, &gsm->tx_ctrl_list, list) {
1025bec02248SDaniel Starke 		if (gsm->constipated && !gsm_is_flow_ctrl_msg(msg))
102659ff0680SDaniel Starke 			continue;
10270af02167SDaniel Starke 		ret = gsm_send_packet(gsm, msg);
10280af02167SDaniel Starke 		switch (ret) {
10290af02167SDaniel Starke 		case -ENOSPC:
10300af02167SDaniel Starke 			return -ENOSPC;
10310af02167SDaniel Starke 		case -ENODEV:
10320af02167SDaniel Starke 			/* ldisc not open */
103396fd7ce5SGreg Kroah-Hartman 			gsm->tx_bytes -= msg->len;
1034b4338e1eSRuss Gorby 			list_del(&msg->list);
1035b4338e1eSRuss Gorby 			kfree(msg);
10360af02167SDaniel Starke 			continue;
10370af02167SDaniel Starke 		default:
10380af02167SDaniel Starke 			if (ret >= 0) {
10390af02167SDaniel Starke 				list_del(&msg->list);
10400af02167SDaniel Starke 				kfree(msg);
10410af02167SDaniel Starke 			}
10420af02167SDaniel Starke 			break;
104301dbb362SGregory CLEMENT 		}
104496fd7ce5SGreg Kroah-Hartman 	}
10450af02167SDaniel Starke 
10460af02167SDaniel Starke 	if (gsm->constipated)
10470af02167SDaniel Starke 		return -EAGAIN;
10480af02167SDaniel Starke 
10490af02167SDaniel Starke 	/* Serialize other channels */
10500af02167SDaniel Starke 	if (list_empty(&gsm->tx_data_list))
10510af02167SDaniel Starke 		return 0;
10520af02167SDaniel Starke 	list_for_each_entry_safe(msg, nmsg, &gsm->tx_data_list, list) {
10530af02167SDaniel Starke 		dlci = gsm->dlci[msg->addr];
10540af02167SDaniel Starke 		/* Send only messages for DLCIs with valid state */
10550af02167SDaniel Starke 		if (dlci->state != DLCI_OPEN) {
10560af02167SDaniel Starke 			gsm->tx_bytes -= msg->len;
10570af02167SDaniel Starke 			list_del(&msg->list);
10580af02167SDaniel Starke 			kfree(msg);
10590af02167SDaniel Starke 			continue;
10600af02167SDaniel Starke 		}
10610af02167SDaniel Starke 		ret = gsm_send_packet(gsm, msg);
10620af02167SDaniel Starke 		switch (ret) {
10630af02167SDaniel Starke 		case -ENOSPC:
10640af02167SDaniel Starke 			return -ENOSPC;
10650af02167SDaniel Starke 		case -ENODEV:
10660af02167SDaniel Starke 			/* ldisc not open */
10670af02167SDaniel Starke 			gsm->tx_bytes -= msg->len;
10680af02167SDaniel Starke 			list_del(&msg->list);
10690af02167SDaniel Starke 			kfree(msg);
10700af02167SDaniel Starke 			continue;
10710af02167SDaniel Starke 		default:
10720af02167SDaniel Starke 			if (ret >= 0) {
10730af02167SDaniel Starke 				list_del(&msg->list);
10740af02167SDaniel Starke 				kfree(msg);
10750af02167SDaniel Starke 			}
10760af02167SDaniel Starke 			break;
10770af02167SDaniel Starke 		}
10780af02167SDaniel Starke 	}
10790af02167SDaniel Starke 
10800af02167SDaniel Starke 	return 1;
108196fd7ce5SGreg Kroah-Hartman }
108296fd7ce5SGreg Kroah-Hartman 
108396fd7ce5SGreg Kroah-Hartman /**
108496fd7ce5SGreg Kroah-Hartman  *	__gsm_data_queue		-	queue a UI or UIH frame
108596fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI sending the data
108696fd7ce5SGreg Kroah-Hartman  *	@msg: message queued
108796fd7ce5SGreg Kroah-Hartman  *
108896fd7ce5SGreg Kroah-Hartman  *	Add data to the transmit queue and try and get stuff moving
108996fd7ce5SGreg Kroah-Hartman  *	out of the mux tty if not already doing so. The Caller must hold
109096fd7ce5SGreg Kroah-Hartman  *	the gsm tx lock.
109196fd7ce5SGreg Kroah-Hartman  */
109296fd7ce5SGreg Kroah-Hartman 
__gsm_data_queue(struct gsm_dlci * dlci,struct gsm_msg * msg)109396fd7ce5SGreg Kroah-Hartman static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
109496fd7ce5SGreg Kroah-Hartman {
109596fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = dlci->gsm;
109696fd7ce5SGreg Kroah-Hartman 	u8 *dp = msg->data;
109796fd7ce5SGreg Kroah-Hartman 	u8 *fcs = dp + msg->len;
109896fd7ce5SGreg Kroah-Hartman 
109996fd7ce5SGreg Kroah-Hartman 	/* Fill in the header */
110019fb0a66SDaniel Starke 	if (gsm->encoding == GSM_BASIC_OPT) {
110196fd7ce5SGreg Kroah-Hartman 		if (msg->len < 128)
110296fd7ce5SGreg Kroah-Hartman 			*--dp = (msg->len << 1) | EA;
110396fd7ce5SGreg Kroah-Hartman 		else {
1104be7a7411SKen Mills 			*--dp = (msg->len >> 7);	/* bits 7 - 15 */
1105be7a7411SKen Mills 			*--dp = (msg->len & 127) << 1;	/* bits 0 - 6 */
110696fd7ce5SGreg Kroah-Hartman 		}
110796fd7ce5SGreg Kroah-Hartman 	}
110896fd7ce5SGreg Kroah-Hartman 
110996fd7ce5SGreg Kroah-Hartman 	*--dp = msg->ctrl;
111096fd7ce5SGreg Kroah-Hartman 	if (gsm->initiator)
111187127773SDaniel Starke 		*--dp = (msg->addr << 2) | CR | EA;
111296fd7ce5SGreg Kroah-Hartman 	else
111396fd7ce5SGreg Kroah-Hartman 		*--dp = (msg->addr << 2) | EA;
111496fd7ce5SGreg Kroah-Hartman 	*fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp);
111596fd7ce5SGreg Kroah-Hartman 	/* Ugly protocol layering violation */
111696fd7ce5SGreg Kroah-Hartman 	if (msg->ctrl == UI || msg->ctrl == (UI|PF))
111796fd7ce5SGreg Kroah-Hartman 		*fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len);
111896fd7ce5SGreg Kroah-Hartman 	*fcs = 0xFF - *fcs;
111996fd7ce5SGreg Kroah-Hartman 
112096fd7ce5SGreg Kroah-Hartman 	gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl,
112196fd7ce5SGreg Kroah-Hartman 							msg->data, msg->len);
112296fd7ce5SGreg Kroah-Hartman 
112396fd7ce5SGreg Kroah-Hartman 	/* Move the header back and adjust the length, also allow for the FCS
112496fd7ce5SGreg Kroah-Hartman 	   now tacked on the end */
112596fd7ce5SGreg Kroah-Hartman 	msg->len += (msg->data - dp) + 1;
112696fd7ce5SGreg Kroah-Hartman 	msg->data = dp;
112796fd7ce5SGreg Kroah-Hartman 
112896fd7ce5SGreg Kroah-Hartman 	/* Add to the actual output queue */
11290af02167SDaniel Starke 	switch (msg->ctrl & ~PF) {
11300af02167SDaniel Starke 	case UI:
11310af02167SDaniel Starke 	case UIH:
11320af02167SDaniel Starke 		if (msg->addr > 0) {
11330af02167SDaniel Starke 			list_add_tail(&msg->list, &gsm->tx_data_list);
11340af02167SDaniel Starke 			break;
11350af02167SDaniel Starke 		}
11360af02167SDaniel Starke 		fallthrough;
11370af02167SDaniel Starke 	default:
11380af02167SDaniel Starke 		list_add_tail(&msg->list, &gsm->tx_ctrl_list);
11390af02167SDaniel Starke 		break;
11400af02167SDaniel Starke 	}
114196fd7ce5SGreg Kroah-Hartman 	gsm->tx_bytes += msg->len;
11420af02167SDaniel Starke 
11430af02167SDaniel Starke 	gsmld_write_trigger(gsm);
114415743ae5SFedor Pchelkin 	mod_timer(&gsm->kick_timer, jiffies + 10 * gsm->t1 * HZ / 100);
114596fd7ce5SGreg Kroah-Hartman }
114696fd7ce5SGreg Kroah-Hartman 
114796fd7ce5SGreg Kroah-Hartman /**
114896fd7ce5SGreg Kroah-Hartman  *	gsm_data_queue		-	queue a UI or UIH frame
114996fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI sending the data
115096fd7ce5SGreg Kroah-Hartman  *	@msg: message queued
115196fd7ce5SGreg Kroah-Hartman  *
115296fd7ce5SGreg Kroah-Hartman  *	Add data to the transmit queue and try and get stuff moving
115396fd7ce5SGreg Kroah-Hartman  *	out of the mux tty if not already doing so. Take the
115496fd7ce5SGreg Kroah-Hartman  *	the gsm tx lock and dlci lock.
115596fd7ce5SGreg Kroah-Hartman  */
115696fd7ce5SGreg Kroah-Hartman 
gsm_data_queue(struct gsm_dlci * dlci,struct gsm_msg * msg)115796fd7ce5SGreg Kroah-Hartman static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
115896fd7ce5SGreg Kroah-Hartman {
1159acdab4cbSFedor Pchelkin 	unsigned long flags;
1160acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
116196fd7ce5SGreg Kroah-Hartman 	__gsm_data_queue(dlci, msg);
1162acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
116396fd7ce5SGreg Kroah-Hartman }
116496fd7ce5SGreg Kroah-Hartman 
116596fd7ce5SGreg Kroah-Hartman /**
116696fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_data_output	-	try and push data out of a DLCI
116796fd7ce5SGreg Kroah-Hartman  *	@gsm: mux
116896fd7ce5SGreg Kroah-Hartman  *	@dlci: the DLCI to pull data from
116996fd7ce5SGreg Kroah-Hartman  *
117096fd7ce5SGreg Kroah-Hartman  *	Pull data from a DLCI and send it into the transmit queue if there
117196fd7ce5SGreg Kroah-Hartman  *	is data. Keep to the MRU of the mux. This path handles the usual tty
117296fd7ce5SGreg Kroah-Hartman  *	interface which is a byte stream with optional modem data.
117396fd7ce5SGreg Kroah-Hartman  *
1174acdab4cbSFedor Pchelkin  *	Caller must hold the tx_lock of the mux.
117596fd7ce5SGreg Kroah-Hartman  */
117696fd7ce5SGreg Kroah-Hartman 
gsm_dlci_data_output(struct gsm_mux * gsm,struct gsm_dlci * dlci)117796fd7ce5SGreg Kroah-Hartman static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
117896fd7ce5SGreg Kroah-Hartman {
117996fd7ce5SGreg Kroah-Hartman 	struct gsm_msg *msg;
118096fd7ce5SGreg Kroah-Hartman 	u8 *dp;
1181556fc8acSDaniel Starke 	int h, len, size;
118296fd7ce5SGreg Kroah-Hartman 
1183556fc8acSDaniel Starke 	/* for modem bits without break data */
1184556fc8acSDaniel Starke 	h = ((dlci->adaption == 1) ? 0 : 1);
1185556fc8acSDaniel Starke 
1186036bca1fSJiri Slaby 	len = kfifo_len(&dlci->fifo);
118796fd7ce5SGreg Kroah-Hartman 	if (len == 0)
1188556fc8acSDaniel Starke 		return 0;
118996fd7ce5SGreg Kroah-Hartman 
1190556fc8acSDaniel Starke 	/* MTU/MRU count only the data bits but watch adaption mode */
11912ec7a802SDaniel Starke 	if ((len + h) > dlci->mtu)
11922ec7a802SDaniel Starke 		len = dlci->mtu - h;
119396fd7ce5SGreg Kroah-Hartman 
119496fd7ce5SGreg Kroah-Hartman 	size = len + h;
119596fd7ce5SGreg Kroah-Hartman 
11962ec7a802SDaniel Starke 	msg = gsm_data_alloc(gsm, dlci->addr, size, dlci->ftype);
1197556fc8acSDaniel Starke 	if (!msg)
119896fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
119996fd7ce5SGreg Kroah-Hartman 	dp = msg->data;
120096fd7ce5SGreg Kroah-Hartman 	switch (dlci->adaption) {
120196fd7ce5SGreg Kroah-Hartman 	case 1: /* Unstructured */
120296fd7ce5SGreg Kroah-Hartman 		break;
1203556fc8acSDaniel Starke 	case 2: /* Unstructured with modem bits.
1204556fc8acSDaniel Starke 		 * Always one byte as we never send inline break data
1205556fc8acSDaniel Starke 		 */
120606d5afd4SDaniel Starke 		*dp++ = (gsm_encode_modem(dlci) << 1) | EA;
120796fd7ce5SGreg Kroah-Hartman 		break;
1208556fc8acSDaniel Starke 	default:
1209556fc8acSDaniel Starke 		pr_err("%s: unsupported adaption %d\n", __func__,
1210556fc8acSDaniel Starke 		       dlci->adaption);
1211556fc8acSDaniel Starke 		break;
121296fd7ce5SGreg Kroah-Hartman 	}
1213556fc8acSDaniel Starke 
1214556fc8acSDaniel Starke 	WARN_ON(len != kfifo_out_locked(&dlci->fifo, dp, len,
1215556fc8acSDaniel Starke 		&dlci->lock));
1216556fc8acSDaniel Starke 
1217556fc8acSDaniel Starke 	/* Notify upper layer about available send space. */
1218556fc8acSDaniel Starke 	tty_port_tty_wakeup(&dlci->port);
1219556fc8acSDaniel Starke 
122096fd7ce5SGreg Kroah-Hartman 	__gsm_data_queue(dlci, msg);
122196fd7ce5SGreg Kroah-Hartman 	/* Bytes of data we used up */
1222556fc8acSDaniel Starke 	return size;
122396fd7ce5SGreg Kroah-Hartman }
122496fd7ce5SGreg Kroah-Hartman 
122596fd7ce5SGreg Kroah-Hartman /**
122696fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_data_output_framed  -	try and push data out of a DLCI
122796fd7ce5SGreg Kroah-Hartman  *	@gsm: mux
122896fd7ce5SGreg Kroah-Hartman  *	@dlci: the DLCI to pull data from
122996fd7ce5SGreg Kroah-Hartman  *
123096fd7ce5SGreg Kroah-Hartman  *	Pull data from a DLCI and send it into the transmit queue if there
123196fd7ce5SGreg Kroah-Hartman  *	is data. Keep to the MRU of the mux. This path handles framed data
123296fd7ce5SGreg Kroah-Hartman  *	queued as skbuffs to the DLCI.
123396fd7ce5SGreg Kroah-Hartman  *
1234acdab4cbSFedor Pchelkin  *	Caller must hold the tx_lock of the mux.
123596fd7ce5SGreg Kroah-Hartman  */
123696fd7ce5SGreg Kroah-Hartman 
gsm_dlci_data_output_framed(struct gsm_mux * gsm,struct gsm_dlci * dlci)123796fd7ce5SGreg Kroah-Hartman static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
123896fd7ce5SGreg Kroah-Hartman 						struct gsm_dlci *dlci)
123996fd7ce5SGreg Kroah-Hartman {
124096fd7ce5SGreg Kroah-Hartman 	struct gsm_msg *msg;
124196fd7ce5SGreg Kroah-Hartman 	u8 *dp;
124296fd7ce5SGreg Kroah-Hartman 	int len, size;
124396fd7ce5SGreg Kroah-Hartman 	int last = 0, first = 0;
124496fd7ce5SGreg Kroah-Hartman 	int overhead = 0;
124596fd7ce5SGreg Kroah-Hartman 
124696fd7ce5SGreg Kroah-Hartman 	/* One byte per frame is used for B/F flags */
124796fd7ce5SGreg Kroah-Hartman 	if (dlci->adaption == 4)
124896fd7ce5SGreg Kroah-Hartman 		overhead = 1;
124996fd7ce5SGreg Kroah-Hartman 
1250acdab4cbSFedor Pchelkin 	/* dlci->skb is locked by tx_lock */
125196fd7ce5SGreg Kroah-Hartman 	if (dlci->skb == NULL) {
125288ed2a60SRuss Gorby 		dlci->skb = skb_dequeue_tail(&dlci->skb_list);
125396fd7ce5SGreg Kroah-Hartman 		if (dlci->skb == NULL)
125496fd7ce5SGreg Kroah-Hartman 			return 0;
125596fd7ce5SGreg Kroah-Hartman 		first = 1;
125696fd7ce5SGreg Kroah-Hartman 	}
125796fd7ce5SGreg Kroah-Hartman 	len = dlci->skb->len + overhead;
125896fd7ce5SGreg Kroah-Hartman 
125996fd7ce5SGreg Kroah-Hartman 	/* MTU/MRU count only the data bits */
12602ec7a802SDaniel Starke 	if (len > dlci->mtu) {
126196fd7ce5SGreg Kroah-Hartman 		if (dlci->adaption == 3) {
126296fd7ce5SGreg Kroah-Hartman 			/* Over long frame, bin it */
1263329e5678SRuss Gorby 			dev_kfree_skb_any(dlci->skb);
126496fd7ce5SGreg Kroah-Hartman 			dlci->skb = NULL;
126596fd7ce5SGreg Kroah-Hartman 			return 0;
126696fd7ce5SGreg Kroah-Hartman 		}
12672ec7a802SDaniel Starke 		len = dlci->mtu;
126896fd7ce5SGreg Kroah-Hartman 	} else
126996fd7ce5SGreg Kroah-Hartman 		last = 1;
127096fd7ce5SGreg Kroah-Hartman 
127196fd7ce5SGreg Kroah-Hartman 	size = len + overhead;
12722ec7a802SDaniel Starke 	msg = gsm_data_alloc(gsm, dlci->addr, size, dlci->ftype);
127388ed2a60SRuss Gorby 	if (msg == NULL) {
127488ed2a60SRuss Gorby 		skb_queue_tail(&dlci->skb_list, dlci->skb);
127588ed2a60SRuss Gorby 		dlci->skb = NULL;
127696fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
127788ed2a60SRuss Gorby 	}
127896fd7ce5SGreg Kroah-Hartman 	dp = msg->data;
127996fd7ce5SGreg Kroah-Hartman 
128096fd7ce5SGreg Kroah-Hartman 	if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */
128196fd7ce5SGreg Kroah-Hartman 		/* Flag byte to carry the start/end info */
128296fd7ce5SGreg Kroah-Hartman 		*dp++ = last << 7 | first << 6 | 1;	/* EA */
128396fd7ce5SGreg Kroah-Hartman 		len--;
128496fd7ce5SGreg Kroah-Hartman 	}
128557f2104fSRuss Gorby 	memcpy(dp, dlci->skb->data, len);
128657f2104fSRuss Gorby 	skb_pull(dlci->skb, len);
128796fd7ce5SGreg Kroah-Hartman 	__gsm_data_queue(dlci, msg);
1288bcd5abe2SRuss Gorby 	if (last) {
1289329e5678SRuss Gorby 		dev_kfree_skb_any(dlci->skb);
129096fd7ce5SGreg Kroah-Hartman 		dlci->skb = NULL;
1291bcd5abe2SRuss Gorby 	}
129296fd7ce5SGreg Kroah-Hartman 	return size;
129396fd7ce5SGreg Kroah-Hartman }
129496fd7ce5SGreg Kroah-Hartman 
129596fd7ce5SGreg Kroah-Hartman /**
1296c19ffe00SDaniel Starke  *	gsm_dlci_modem_output	-	try and push modem status out of a DLCI
1297c19ffe00SDaniel Starke  *	@gsm: mux
1298c19ffe00SDaniel Starke  *	@dlci: the DLCI to pull modem status from
1299c19ffe00SDaniel Starke  *	@brk: break signal
1300c19ffe00SDaniel Starke  *
1301c19ffe00SDaniel Starke  *	Push an empty frame in to the transmit queue to update the modem status
1302c19ffe00SDaniel Starke  *	bits and to transmit an optional break.
1303c19ffe00SDaniel Starke  *
1304acdab4cbSFedor Pchelkin  *	Caller must hold the tx_lock of the mux.
1305c19ffe00SDaniel Starke  */
1306c19ffe00SDaniel Starke 
gsm_dlci_modem_output(struct gsm_mux * gsm,struct gsm_dlci * dlci,u8 brk)1307c19ffe00SDaniel Starke static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
1308c19ffe00SDaniel Starke 				 u8 brk)
1309c19ffe00SDaniel Starke {
1310c19ffe00SDaniel Starke 	u8 *dp = NULL;
1311c19ffe00SDaniel Starke 	struct gsm_msg *msg;
131219317433SDaniel Starke 	int size = 0;
1313c19ffe00SDaniel Starke 
1314c19ffe00SDaniel Starke 	/* for modem bits without break data */
131519317433SDaniel Starke 	switch (dlci->adaption) {
131619317433SDaniel Starke 	case 1: /* Unstructured */
131719317433SDaniel Starke 		break;
131819317433SDaniel Starke 	case 2: /* Unstructured with modem bits. */
131919317433SDaniel Starke 		size++;
1320c19ffe00SDaniel Starke 		if (brk > 0)
1321c19ffe00SDaniel Starke 			size++;
132219317433SDaniel Starke 		break;
132319317433SDaniel Starke 	default:
1324c19ffe00SDaniel Starke 		pr_err("%s: unsupported adaption %d\n", __func__,
1325c19ffe00SDaniel Starke 		       dlci->adaption);
132619317433SDaniel Starke 		return -EINVAL;
1327c19ffe00SDaniel Starke 	}
1328c19ffe00SDaniel Starke 
13292ec7a802SDaniel Starke 	msg = gsm_data_alloc(gsm, dlci->addr, size, dlci->ftype);
1330c19ffe00SDaniel Starke 	if (!msg) {
1331c19ffe00SDaniel Starke 		pr_err("%s: gsm_data_alloc error", __func__);
1332c19ffe00SDaniel Starke 		return -ENOMEM;
1333c19ffe00SDaniel Starke 	}
1334c19ffe00SDaniel Starke 	dp = msg->data;
1335c19ffe00SDaniel Starke 	switch (dlci->adaption) {
1336c19ffe00SDaniel Starke 	case 1: /* Unstructured */
1337c19ffe00SDaniel Starke 		break;
1338c19ffe00SDaniel Starke 	case 2: /* Unstructured with modem bits. */
1339c19ffe00SDaniel Starke 		if (brk == 0) {
1340c19ffe00SDaniel Starke 			*dp++ = (gsm_encode_modem(dlci) << 1) | EA;
1341c19ffe00SDaniel Starke 		} else {
1342c19ffe00SDaniel Starke 			*dp++ = gsm_encode_modem(dlci) << 1;
1343c19ffe00SDaniel Starke 			*dp++ = (brk << 4) | 2 | EA; /* Length, Break, EA */
1344c19ffe00SDaniel Starke 		}
1345c19ffe00SDaniel Starke 		break;
1346c19ffe00SDaniel Starke 	default:
1347c19ffe00SDaniel Starke 		/* Handled above */
1348c19ffe00SDaniel Starke 		break;
1349c19ffe00SDaniel Starke 	}
1350c19ffe00SDaniel Starke 
1351c19ffe00SDaniel Starke 	__gsm_data_queue(dlci, msg);
1352c19ffe00SDaniel Starke 	return size;
1353c19ffe00SDaniel Starke }
1354c19ffe00SDaniel Starke 
1355c19ffe00SDaniel Starke /**
135696fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_data_sweep		-	look for data to send
135796fd7ce5SGreg Kroah-Hartman  *	@gsm: the GSM mux
135896fd7ce5SGreg Kroah-Hartman  *
135996fd7ce5SGreg Kroah-Hartman  *	Sweep the GSM mux channels in priority order looking for ones with
136096fd7ce5SGreg Kroah-Hartman  *	data to send. We could do with optimising this scan a bit. We aim
136196fd7ce5SGreg Kroah-Hartman  *	to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit
136296fd7ce5SGreg Kroah-Hartman  *	TX_THRESH_LO we get called again
136396fd7ce5SGreg Kroah-Hartman  *
136496fd7ce5SGreg Kroah-Hartman  *	FIXME: We should round robin between groups and in theory you can
136596fd7ce5SGreg Kroah-Hartman  *	renegotiate DLCI priorities with optional stuff. Needs optimising.
136696fd7ce5SGreg Kroah-Hartman  */
136796fd7ce5SGreg Kroah-Hartman 
gsm_dlci_data_sweep(struct gsm_mux * gsm)1368c568f708SDaniel Starke static int gsm_dlci_data_sweep(struct gsm_mux *gsm)
136996fd7ce5SGreg Kroah-Hartman {
137096fd7ce5SGreg Kroah-Hartman 	/* Priority ordering: We should do priority with RR of the groups */
13710af02167SDaniel Starke 	int i, len, ret = 0;
13720af02167SDaniel Starke 	bool sent;
137396fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci;
137496fd7ce5SGreg Kroah-Hartman 
13750af02167SDaniel Starke 	while (gsm->tx_bytes < TX_THRESH_HI) {
13760af02167SDaniel Starke 		for (sent = false, i = 1; i < NUM_DLCI; i++) {
137796fd7ce5SGreg Kroah-Hartman 			dlci = gsm->dlci[i];
13780af02167SDaniel Starke 			/* skip unused or blocked channel */
13790af02167SDaniel Starke 			if (!dlci || dlci->constipated)
138096fd7ce5SGreg Kroah-Hartman 				continue;
13810af02167SDaniel Starke 			/* skip channels with invalid state */
13820af02167SDaniel Starke 			if (dlci->state != DLCI_OPEN)
13830af02167SDaniel Starke 				continue;
13840af02167SDaniel Starke 			/* count the sent data per adaption */
1385bcd5abe2SRuss Gorby 			if (dlci->adaption < 3 && !dlci->net)
138696fd7ce5SGreg Kroah-Hartman 				len = gsm_dlci_data_output(gsm, dlci);
138796fd7ce5SGreg Kroah-Hartman 			else
138896fd7ce5SGreg Kroah-Hartman 				len = gsm_dlci_data_output_framed(gsm, dlci);
13890af02167SDaniel Starke 			/* on error exit */
139096fd7ce5SGreg Kroah-Hartman 			if (len < 0)
13910af02167SDaniel Starke 				return ret;
13920af02167SDaniel Starke 			if (len > 0) {
1393c568f708SDaniel Starke 				ret++;
13940af02167SDaniel Starke 				sent = true;
13950af02167SDaniel Starke 				/* The lower DLCs can starve the higher DLCs! */
13960af02167SDaniel Starke 				break;
139796fd7ce5SGreg Kroah-Hartman 			}
13980af02167SDaniel Starke 			/* try next */
13990af02167SDaniel Starke 		}
14000af02167SDaniel Starke 		if (!sent)
14010af02167SDaniel Starke 			break;
1402a3be423fSShaomin Deng 	}
1403c568f708SDaniel Starke 
1404c568f708SDaniel Starke 	return ret;
140596fd7ce5SGreg Kroah-Hartman }
140696fd7ce5SGreg Kroah-Hartman 
140796fd7ce5SGreg Kroah-Hartman /**
140896fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_data_kick	-	transmit if possible
140996fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI to kick
141096fd7ce5SGreg Kroah-Hartman  *
141196fd7ce5SGreg Kroah-Hartman  *	Transmit data from this DLCI if the queue is empty. We can't rely on
141296fd7ce5SGreg Kroah-Hartman  *	a tty wakeup except when we filled the pipe so we need to fire off
141396fd7ce5SGreg Kroah-Hartman  *	new data ourselves in other cases.
141496fd7ce5SGreg Kroah-Hartman  */
141596fd7ce5SGreg Kroah-Hartman 
gsm_dlci_data_kick(struct gsm_dlci * dlci)141696fd7ce5SGreg Kroah-Hartman static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
141796fd7ce5SGreg Kroah-Hartman {
1418acdab4cbSFedor Pchelkin 	unsigned long flags;
1419192b6041SRuss Gorby 	int sweep;
142096fd7ce5SGreg Kroah-Hartman 
142110c6c383Ssamix.lebsir 	if (dlci->constipated)
1422c01af4feSFrederic Berat 		return;
1423c01af4feSFrederic Berat 
1424acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
142596fd7ce5SGreg Kroah-Hartman 	/* If we have nothing running then we need to fire up */
1426192b6041SRuss Gorby 	sweep = (dlci->gsm->tx_bytes < TX_THRESH_LO);
1427bcd5abe2SRuss Gorby 	if (dlci->gsm->tx_bytes == 0) {
1428bcd5abe2SRuss Gorby 		if (dlci->net)
1429bcd5abe2SRuss Gorby 			gsm_dlci_data_output_framed(dlci->gsm, dlci);
1430bcd5abe2SRuss Gorby 		else
143196fd7ce5SGreg Kroah-Hartman 			gsm_dlci_data_output(dlci->gsm, dlci);
1432192b6041SRuss Gorby 	}
1433192b6041SRuss Gorby 	if (sweep)
143496fd7ce5SGreg Kroah-Hartman 		gsm_dlci_data_sweep(dlci->gsm);
1435acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
143696fd7ce5SGreg Kroah-Hartman }
143796fd7ce5SGreg Kroah-Hartman 
143896fd7ce5SGreg Kroah-Hartman /*
143996fd7ce5SGreg Kroah-Hartman  *	Control message processing
144096fd7ce5SGreg Kroah-Hartman  */
144196fd7ce5SGreg Kroah-Hartman 
144296fd7ce5SGreg Kroah-Hartman 
144396fd7ce5SGreg Kroah-Hartman /**
1444de640bc6SDaniel Starke  * gsm_control_command	-	send a command frame to a control
1445de640bc6SDaniel Starke  * @gsm: gsm channel
1446de640bc6SDaniel Starke  * @cmd: the command to use
1447de640bc6SDaniel Starke  * @data: data to follow encoded info
1448de640bc6SDaniel Starke  * @dlen: length of data
1449de640bc6SDaniel Starke  *
1450de640bc6SDaniel Starke  * Encode up and queue a UI/UIH frame containing our command.
1451de640bc6SDaniel Starke  */
gsm_control_command(struct gsm_mux * gsm,int cmd,const u8 * data,int dlen)1452de640bc6SDaniel Starke static int gsm_control_command(struct gsm_mux *gsm, int cmd, const u8 *data,
1453de640bc6SDaniel Starke 			       int dlen)
1454de640bc6SDaniel Starke {
14552ec7a802SDaniel Starke 	struct gsm_msg *msg;
145657677126SDaniel Starke 	struct gsm_dlci *dlci = gsm->dlci[0];
1457de640bc6SDaniel Starke 
145857677126SDaniel Starke 	msg = gsm_data_alloc(gsm, 0, dlen + 2, dlci->ftype);
1459de640bc6SDaniel Starke 	if (msg == NULL)
1460de640bc6SDaniel Starke 		return -ENOMEM;
1461de640bc6SDaniel Starke 
1462de640bc6SDaniel Starke 	msg->data[0] = (cmd << 1) | CR | EA;	/* Set C/R */
1463de640bc6SDaniel Starke 	msg->data[1] = (dlen << 1) | EA;
1464de640bc6SDaniel Starke 	memcpy(msg->data + 2, data, dlen);
146557677126SDaniel Starke 	gsm_data_queue(dlci, msg);
1466de640bc6SDaniel Starke 
1467de640bc6SDaniel Starke 	return 0;
1468de640bc6SDaniel Starke }
1469de640bc6SDaniel Starke 
1470de640bc6SDaniel Starke /**
147196fd7ce5SGreg Kroah-Hartman  *	gsm_control_reply	-	send a response frame to a control
147296fd7ce5SGreg Kroah-Hartman  *	@gsm: gsm channel
147396fd7ce5SGreg Kroah-Hartman  *	@cmd: the command to use
147496fd7ce5SGreg Kroah-Hartman  *	@data: data to follow encoded info
147596fd7ce5SGreg Kroah-Hartman  *	@dlen: length of data
147696fd7ce5SGreg Kroah-Hartman  *
147796fd7ce5SGreg Kroah-Hartman  *	Encode up and queue a UI/UIH frame containing our response.
147896fd7ce5SGreg Kroah-Hartman  */
147996fd7ce5SGreg Kroah-Hartman 
gsm_control_reply(struct gsm_mux * gsm,int cmd,const u8 * data,int dlen)14804feb7a4aSTony Lindgren static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
148196fd7ce5SGreg Kroah-Hartman 					int dlen)
148296fd7ce5SGreg Kroah-Hartman {
148396fd7ce5SGreg Kroah-Hartman 	struct gsm_msg *msg;
148457677126SDaniel Starke 	struct gsm_dlci *dlci = gsm->dlci[0];
14852ec7a802SDaniel Starke 
148657677126SDaniel Starke 	msg = gsm_data_alloc(gsm, 0, dlen + 2, dlci->ftype);
1487093d8046SKen Mills 	if (msg == NULL)
1488093d8046SKen Mills 		return;
148996fd7ce5SGreg Kroah-Hartman 	msg->data[0] = (cmd & 0xFE) << 1 | EA;	/* Clear C/R */
149096fd7ce5SGreg Kroah-Hartman 	msg->data[1] = (dlen << 1) | EA;
149196fd7ce5SGreg Kroah-Hartman 	memcpy(msg->data + 2, data, dlen);
149257677126SDaniel Starke 	gsm_data_queue(dlci, msg);
149396fd7ce5SGreg Kroah-Hartman }
149496fd7ce5SGreg Kroah-Hartman 
149596fd7ce5SGreg Kroah-Hartman /**
149696fd7ce5SGreg Kroah-Hartman  *	gsm_process_modem	-	process received modem status
149796fd7ce5SGreg Kroah-Hartman  *	@tty: virtual tty bound to the DLCI
149896fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI to affect
149996fd7ce5SGreg Kroah-Hartman  *	@modem: modem bits (full EA)
1500687f9ad4Sdaniel.starke@siemens.com  *	@slen: number of signal octets
150196fd7ce5SGreg Kroah-Hartman  *
150296fd7ce5SGreg Kroah-Hartman  *	Used when a modem control message or line state inline in adaption
150396fd7ce5SGreg Kroah-Hartman  *	layer 2 is processed. Sort out the local modem state and throttles
150496fd7ce5SGreg Kroah-Hartman  */
150596fd7ce5SGreg Kroah-Hartman 
gsm_process_modem(struct tty_struct * tty,struct gsm_dlci * dlci,u32 modem,int slen)150696fd7ce5SGreg Kroah-Hartman static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
1507687f9ad4Sdaniel.starke@siemens.com 							u32 modem, int slen)
150896fd7ce5SGreg Kroah-Hartman {
150996fd7ce5SGreg Kroah-Hartman 	int  mlines = 0;
15107263287aSRuss Gorby 	u8 brk = 0;
1511c01af4feSFrederic Berat 	int fc;
15127263287aSRuss Gorby 
1513687f9ad4Sdaniel.starke@siemens.com 	/* The modem status command can either contain one octet (V.24 signals)
1514687f9ad4Sdaniel.starke@siemens.com 	 * or two octets (V.24 signals + break signals). This is specified in
1515687f9ad4Sdaniel.starke@siemens.com 	 * section 5.4.6.3.7 of the 07.10 mux spec.
1516687f9ad4Sdaniel.starke@siemens.com 	 */
15177263287aSRuss Gorby 
1518687f9ad4Sdaniel.starke@siemens.com 	if (slen == 1)
15197263287aSRuss Gorby 		modem = modem & 0x7f;
15207263287aSRuss Gorby 	else {
15217263287aSRuss Gorby 		brk = modem & 0x7f;
15227263287aSRuss Gorby 		modem = (modem >> 7) & 0x7f;
1523c01af4feSFrederic Berat 	}
152496fd7ce5SGreg Kroah-Hartman 
152596fd7ce5SGreg Kroah-Hartman 	/* Flow control/ready to communicate */
1526c01af4feSFrederic Berat 	fc = (modem & MDM_FC) || !(modem & MDM_RTR);
1527c01af4feSFrederic Berat 	if (fc && !dlci->constipated) {
152896fd7ce5SGreg Kroah-Hartman 		/* Need to throttle our output on this device */
15297a9ed9c0SJiri Slaby 		dlci->constipated = true;
1530c01af4feSFrederic Berat 	} else if (!fc && dlci->constipated) {
15317a9ed9c0SJiri Slaby 		dlci->constipated = false;
153296fd7ce5SGreg Kroah-Hartman 		gsm_dlci_data_kick(dlci);
153396fd7ce5SGreg Kroah-Hartman 	}
1534c01af4feSFrederic Berat 
153596fd7ce5SGreg Kroah-Hartman 	/* Map modem bits */
1536c01af4feSFrederic Berat 	if (modem & MDM_RTC)
1537c01af4feSFrederic Berat 		mlines |= TIOCM_DSR | TIOCM_DTR;
153896fd7ce5SGreg Kroah-Hartman 	if (modem & MDM_RTR)
153996fd7ce5SGreg Kroah-Hartman 		mlines |= TIOCM_RTS | TIOCM_CTS;
154096fd7ce5SGreg Kroah-Hartman 	if (modem & MDM_IC)
154196fd7ce5SGreg Kroah-Hartman 		mlines |= TIOCM_RI;
154296fd7ce5SGreg Kroah-Hartman 	if (modem & MDM_DV)
154396fd7ce5SGreg Kroah-Hartman 		mlines |= TIOCM_CD;
154496fd7ce5SGreg Kroah-Hartman 
154596fd7ce5SGreg Kroah-Hartman 	/* Carrier drop -> hangup */
154696fd7ce5SGreg Kroah-Hartman 	if (tty) {
154796fd7ce5SGreg Kroah-Hartman 		if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD))
15489db276f8SPeter Hurley 			if (!C_CLOCAL(tty))
154996fd7ce5SGreg Kroah-Hartman 				tty_hangup(tty);
155096fd7ce5SGreg Kroah-Hartman 	}
155192a19f9cSJiri Slaby 	if (brk & 0x01)
155292a19f9cSJiri Slaby 		tty_insert_flip_char(&dlci->port, 0, TTY_BREAK);
155396fd7ce5SGreg Kroah-Hartman 	dlci->modem_rx = mlines;
1554684ae4f9SDaniel Starke 	wake_up_interruptible(&dlci->gsm->event);
155596fd7ce5SGreg Kroah-Hartman }
155696fd7ce5SGreg Kroah-Hartman 
155796fd7ce5SGreg Kroah-Hartman /**
155892f1f0c3SDaniel Starke  * gsm_process_negotiation	-	process received parameters
155992f1f0c3SDaniel Starke  * @gsm: GSM channel
156092f1f0c3SDaniel Starke  * @addr: DLCI address
156192f1f0c3SDaniel Starke  * @cr: command/response
156292f1f0c3SDaniel Starke  * @params: encoded parameters from the parameter negotiation message
156392f1f0c3SDaniel Starke  *
156492f1f0c3SDaniel Starke  * Used when the response for our parameter negotiation command was
156592f1f0c3SDaniel Starke  * received.
156692f1f0c3SDaniel Starke  */
gsm_process_negotiation(struct gsm_mux * gsm,unsigned int addr,unsigned int cr,const struct gsm_dlci_param_bits * params)156792f1f0c3SDaniel Starke static int gsm_process_negotiation(struct gsm_mux *gsm, unsigned int addr,
156892f1f0c3SDaniel Starke 				   unsigned int cr,
156992f1f0c3SDaniel Starke 				   const struct gsm_dlci_param_bits *params)
157092f1f0c3SDaniel Starke {
157192f1f0c3SDaniel Starke 	struct gsm_dlci *dlci = gsm->dlci[addr];
157292f1f0c3SDaniel Starke 	unsigned int ftype, i, adaption, prio, n1, k;
157392f1f0c3SDaniel Starke 
157492f1f0c3SDaniel Starke 	i = FIELD_GET(PN_I_CL_FIELD_FTYPE, params->i_cl_bits);
157592f1f0c3SDaniel Starke 	adaption = FIELD_GET(PN_I_CL_FIELD_ADAPTION, params->i_cl_bits) + 1;
157692f1f0c3SDaniel Starke 	prio = FIELD_GET(PN_P_FIELD_PRIO, params->p_bits);
157792f1f0c3SDaniel Starke 	n1 = FIELD_GET(PN_N_FIELD_N1, get_unaligned_le16(&params->n_bits));
157892f1f0c3SDaniel Starke 	k = FIELD_GET(PN_K_FIELD_K, params->k_bits);
157992f1f0c3SDaniel Starke 
158092f1f0c3SDaniel Starke 	if (n1 < MIN_MTU) {
158192f1f0c3SDaniel Starke 		if (debug & DBG_ERRORS)
158292f1f0c3SDaniel Starke 			pr_info("%s N1 out of range in PN\n", __func__);
158392f1f0c3SDaniel Starke 		return -EINVAL;
158492f1f0c3SDaniel Starke 	}
158592f1f0c3SDaniel Starke 
158692f1f0c3SDaniel Starke 	switch (i) {
158792f1f0c3SDaniel Starke 	case 0x00:
158892f1f0c3SDaniel Starke 		ftype = UIH;
158992f1f0c3SDaniel Starke 		break;
159092f1f0c3SDaniel Starke 	case 0x01:
159192f1f0c3SDaniel Starke 		ftype = UI;
159292f1f0c3SDaniel Starke 		break;
159392f1f0c3SDaniel Starke 	case 0x02: /* I frames are not supported */
159492f1f0c3SDaniel Starke 		if (debug & DBG_ERRORS)
159592f1f0c3SDaniel Starke 			pr_info("%s unsupported I frame request in PN\n",
159692f1f0c3SDaniel Starke 				__func__);
1597e74c048aSDaniel Starke 		gsm->unsupported++;
159892f1f0c3SDaniel Starke 		return -EINVAL;
159992f1f0c3SDaniel Starke 	default:
160092f1f0c3SDaniel Starke 		if (debug & DBG_ERRORS)
160192f1f0c3SDaniel Starke 			pr_info("%s i out of range in PN\n", __func__);
160292f1f0c3SDaniel Starke 		return -EINVAL;
160392f1f0c3SDaniel Starke 	}
160492f1f0c3SDaniel Starke 
160592f1f0c3SDaniel Starke 	if (!cr && gsm->initiator) {
160692f1f0c3SDaniel Starke 		if (adaption != dlci->adaption) {
160792f1f0c3SDaniel Starke 			if (debug & DBG_ERRORS)
160892f1f0c3SDaniel Starke 				pr_info("%s invalid adaption %d in PN\n",
160992f1f0c3SDaniel Starke 					__func__, adaption);
161092f1f0c3SDaniel Starke 			return -EINVAL;
161192f1f0c3SDaniel Starke 		}
161292f1f0c3SDaniel Starke 		if (prio != dlci->prio) {
161392f1f0c3SDaniel Starke 			if (debug & DBG_ERRORS)
161492f1f0c3SDaniel Starke 				pr_info("%s invalid priority %d in PN",
161592f1f0c3SDaniel Starke 					__func__, prio);
161692f1f0c3SDaniel Starke 			return -EINVAL;
161792f1f0c3SDaniel Starke 		}
161892f1f0c3SDaniel Starke 		if (n1 > gsm->mru || n1 > dlci->mtu) {
161992f1f0c3SDaniel Starke 			/* We requested a frame size but the other party wants
162092f1f0c3SDaniel Starke 			 * to send larger frames. The standard allows only a
162192f1f0c3SDaniel Starke 			 * smaller response value than requested (5.4.6.3.1).
162292f1f0c3SDaniel Starke 			 */
162392f1f0c3SDaniel Starke 			if (debug & DBG_ERRORS)
162492f1f0c3SDaniel Starke 				pr_info("%s invalid N1 %d in PN\n", __func__,
162592f1f0c3SDaniel Starke 					n1);
162692f1f0c3SDaniel Starke 			return -EINVAL;
162792f1f0c3SDaniel Starke 		}
162892f1f0c3SDaniel Starke 		dlci->mtu = n1;
162992f1f0c3SDaniel Starke 		if (ftype != dlci->ftype) {
163092f1f0c3SDaniel Starke 			if (debug & DBG_ERRORS)
163192f1f0c3SDaniel Starke 				pr_info("%s invalid i %d in PN\n", __func__, i);
163292f1f0c3SDaniel Starke 			return -EINVAL;
163392f1f0c3SDaniel Starke 		}
163492f1f0c3SDaniel Starke 		if (ftype != UI && ftype != UIH && k > dlci->k) {
163592f1f0c3SDaniel Starke 			if (debug & DBG_ERRORS)
163692f1f0c3SDaniel Starke 				pr_info("%s invalid k %d in PN\n", __func__, k);
163792f1f0c3SDaniel Starke 			return -EINVAL;
163892f1f0c3SDaniel Starke 		}
163992f1f0c3SDaniel Starke 		dlci->k = k;
164092f1f0c3SDaniel Starke 	} else if (cr && !gsm->initiator) {
164192f1f0c3SDaniel Starke 		/* Only convergence layer type 1 and 2 are supported. */
164292f1f0c3SDaniel Starke 		if (adaption != 1 && adaption != 2) {
164392f1f0c3SDaniel Starke 			if (debug & DBG_ERRORS)
164492f1f0c3SDaniel Starke 				pr_info("%s invalid adaption %d in PN\n",
164592f1f0c3SDaniel Starke 					__func__, adaption);
164692f1f0c3SDaniel Starke 			return -EINVAL;
164792f1f0c3SDaniel Starke 		}
164892f1f0c3SDaniel Starke 		dlci->adaption = adaption;
164992f1f0c3SDaniel Starke 		if (n1 > gsm->mru) {
165092f1f0c3SDaniel Starke 			/* Propose a smaller value */
165192f1f0c3SDaniel Starke 			dlci->mtu = gsm->mru;
165292f1f0c3SDaniel Starke 		} else if (n1 > MAX_MTU) {
165392f1f0c3SDaniel Starke 			/* Propose a smaller value */
165492f1f0c3SDaniel Starke 			dlci->mtu = MAX_MTU;
165592f1f0c3SDaniel Starke 		} else {
165692f1f0c3SDaniel Starke 			dlci->mtu = n1;
165792f1f0c3SDaniel Starke 		}
165892f1f0c3SDaniel Starke 		dlci->prio = prio;
165992f1f0c3SDaniel Starke 		dlci->ftype = ftype;
166092f1f0c3SDaniel Starke 		dlci->k = k;
166192f1f0c3SDaniel Starke 	} else {
166292f1f0c3SDaniel Starke 		return -EINVAL;
166392f1f0c3SDaniel Starke 	}
166492f1f0c3SDaniel Starke 
166592f1f0c3SDaniel Starke 	return 0;
166692f1f0c3SDaniel Starke }
166792f1f0c3SDaniel Starke 
166892f1f0c3SDaniel Starke /**
166996fd7ce5SGreg Kroah-Hartman  *	gsm_control_modem	-	modem status received
167096fd7ce5SGreg Kroah-Hartman  *	@gsm: GSM channel
167196fd7ce5SGreg Kroah-Hartman  *	@data: data following command
167296fd7ce5SGreg Kroah-Hartman  *	@clen: command length
167396fd7ce5SGreg Kroah-Hartman  *
167496fd7ce5SGreg Kroah-Hartman  *	We have received a modem status control message. This is used by
167596fd7ce5SGreg Kroah-Hartman  *	the GSM mux protocol to pass virtual modem line status and optionally
167696fd7ce5SGreg Kroah-Hartman  *	to indicate break signals. Unpack it, convert to Linux representation
167796fd7ce5SGreg Kroah-Hartman  *	and if need be stuff a break message down the tty.
167896fd7ce5SGreg Kroah-Hartman  */
167996fd7ce5SGreg Kroah-Hartman 
gsm_control_modem(struct gsm_mux * gsm,const u8 * data,int clen)16804feb7a4aSTony Lindgren static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
168196fd7ce5SGreg Kroah-Hartman {
168296fd7ce5SGreg Kroah-Hartman 	unsigned int addr = 0;
168396fd7ce5SGreg Kroah-Hartman 	unsigned int modem = 0;
168496fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci;
168596fd7ce5SGreg Kroah-Hartman 	int len = clen;
1686669609ceSDaniel Starke 	int cl = clen;
16874feb7a4aSTony Lindgren 	const u8 *dp = data;
168896fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
168996fd7ce5SGreg Kroah-Hartman 
1690669609ceSDaniel Starke 	len = gsm_read_ea_val(&addr, data, cl);
1691669609ceSDaniel Starke 	if (len < 1)
169296fd7ce5SGreg Kroah-Hartman 		return;
169396fd7ce5SGreg Kroah-Hartman 
169496fd7ce5SGreg Kroah-Hartman 	addr >>= 1;
169596fd7ce5SGreg Kroah-Hartman 	/* Closed port, or invalid ? */
169696fd7ce5SGreg Kroah-Hartman 	if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL)
169796fd7ce5SGreg Kroah-Hartman 		return;
169896fd7ce5SGreg Kroah-Hartman 	dlci = gsm->dlci[addr];
169996fd7ce5SGreg Kroah-Hartman 
1700669609ceSDaniel Starke 	/* Must be at least one byte following the EA */
1701669609ceSDaniel Starke 	if ((cl - len) < 1)
170296fd7ce5SGreg Kroah-Hartman 		return;
1703669609ceSDaniel Starke 
1704669609ceSDaniel Starke 	dp += len;
1705669609ceSDaniel Starke 	cl -= len;
1706669609ceSDaniel Starke 
1707669609ceSDaniel Starke 	/* get the modem status */
1708669609ceSDaniel Starke 	len = gsm_read_ea_val(&modem, dp, cl);
1709669609ceSDaniel Starke 	if (len < 1)
1710669609ceSDaniel Starke 		return;
1711669609ceSDaniel Starke 
171296fd7ce5SGreg Kroah-Hartman 	tty = tty_port_tty_get(&dlci->port);
1713669609ceSDaniel Starke 	gsm_process_modem(tty, dlci, modem, cl);
171496fd7ce5SGreg Kroah-Hartman 	if (tty) {
171596fd7ce5SGreg Kroah-Hartman 		tty_wakeup(tty);
171696fd7ce5SGreg Kroah-Hartman 		tty_kref_put(tty);
171796fd7ce5SGreg Kroah-Hartman 	}
171896fd7ce5SGreg Kroah-Hartman 	gsm_control_reply(gsm, CMD_MSC, data, clen);
171996fd7ce5SGreg Kroah-Hartman }
172096fd7ce5SGreg Kroah-Hartman 
172196fd7ce5SGreg Kroah-Hartman /**
172292f1f0c3SDaniel Starke  * gsm_control_negotiation	-	parameter negotiation received
172392f1f0c3SDaniel Starke  * @gsm: GSM channel
172492f1f0c3SDaniel Starke  * @cr: command/response flag
172592f1f0c3SDaniel Starke  * @data: data following command
172692f1f0c3SDaniel Starke  * @dlen: data length
172792f1f0c3SDaniel Starke  *
172892f1f0c3SDaniel Starke  * We have received a parameter negotiation message. This is used by
172992f1f0c3SDaniel Starke  * the GSM mux protocol to configure protocol parameters for a new DLCI.
173092f1f0c3SDaniel Starke  */
gsm_control_negotiation(struct gsm_mux * gsm,unsigned int cr,const u8 * data,unsigned int dlen)173192f1f0c3SDaniel Starke static void gsm_control_negotiation(struct gsm_mux *gsm, unsigned int cr,
173292f1f0c3SDaniel Starke 				    const u8 *data, unsigned int dlen)
173392f1f0c3SDaniel Starke {
173492f1f0c3SDaniel Starke 	unsigned int addr;
173592f1f0c3SDaniel Starke 	struct gsm_dlci_param_bits pn_reply;
173692f1f0c3SDaniel Starke 	struct gsm_dlci *dlci;
173792f1f0c3SDaniel Starke 	struct gsm_dlci_param_bits *params;
173892f1f0c3SDaniel Starke 
1739a1ce6da0SDaniel Starke 	if (dlen < sizeof(struct gsm_dlci_param_bits)) {
1740a1ce6da0SDaniel Starke 		gsm->open_error++;
174192f1f0c3SDaniel Starke 		return;
1742a1ce6da0SDaniel Starke 	}
174392f1f0c3SDaniel Starke 
174492f1f0c3SDaniel Starke 	/* Invalid DLCI? */
174592f1f0c3SDaniel Starke 	params = (struct gsm_dlci_param_bits *)data;
174692f1f0c3SDaniel Starke 	addr = FIELD_GET(PN_D_FIELD_DLCI, params->d_bits);
1747a1ce6da0SDaniel Starke 	if (addr == 0 || addr >= NUM_DLCI || !gsm->dlci[addr]) {
1748a1ce6da0SDaniel Starke 		gsm->open_error++;
174992f1f0c3SDaniel Starke 		return;
1750a1ce6da0SDaniel Starke 	}
175192f1f0c3SDaniel Starke 	dlci = gsm->dlci[addr];
175292f1f0c3SDaniel Starke 
175392f1f0c3SDaniel Starke 	/* Too late for parameter negotiation? */
1754a1ce6da0SDaniel Starke 	if ((!cr && dlci->state == DLCI_OPENING) || dlci->state == DLCI_OPEN) {
1755a1ce6da0SDaniel Starke 		gsm->open_error++;
175692f1f0c3SDaniel Starke 		return;
1757a1ce6da0SDaniel Starke 	}
175892f1f0c3SDaniel Starke 
175992f1f0c3SDaniel Starke 	/* Process the received parameters */
176092f1f0c3SDaniel Starke 	if (gsm_process_negotiation(gsm, addr, cr, params) != 0) {
176192f1f0c3SDaniel Starke 		/* Negotiation failed. Close the link. */
176292f1f0c3SDaniel Starke 		if (debug & DBG_ERRORS)
176392f1f0c3SDaniel Starke 			pr_info("%s PN failed\n", __func__);
1764a1ce6da0SDaniel Starke 		gsm->open_error++;
176592f1f0c3SDaniel Starke 		gsm_dlci_close(dlci);
176692f1f0c3SDaniel Starke 		return;
176792f1f0c3SDaniel Starke 	}
176892f1f0c3SDaniel Starke 
176992f1f0c3SDaniel Starke 	if (cr) {
177092f1f0c3SDaniel Starke 		/* Reply command with accepted parameters. */
177192f1f0c3SDaniel Starke 		if (gsm_encode_params(dlci, &pn_reply) == 0)
177292f1f0c3SDaniel Starke 			gsm_control_reply(gsm, CMD_PN, (const u8 *)&pn_reply,
177392f1f0c3SDaniel Starke 					  sizeof(pn_reply));
177492f1f0c3SDaniel Starke 		else if (debug & DBG_ERRORS)
177592f1f0c3SDaniel Starke 			pr_info("%s PN invalid\n", __func__);
177692f1f0c3SDaniel Starke 	} else if (dlci->state == DLCI_CONFIGURE) {
177792f1f0c3SDaniel Starke 		/* Proceed with link setup by sending SABM before UA */
177892f1f0c3SDaniel Starke 		dlci->state = DLCI_OPENING;
177992f1f0c3SDaniel Starke 		gsm_command(gsm, dlci->addr, SABM|PF);
178092f1f0c3SDaniel Starke 		mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
178192f1f0c3SDaniel Starke 	} else {
178292f1f0c3SDaniel Starke 		if (debug & DBG_ERRORS)
178392f1f0c3SDaniel Starke 			pr_info("%s PN in invalid state\n", __func__);
1784a1ce6da0SDaniel Starke 		gsm->open_error++;
178592f1f0c3SDaniel Starke 	}
178692f1f0c3SDaniel Starke }
178792f1f0c3SDaniel Starke 
178892f1f0c3SDaniel Starke /**
178996fd7ce5SGreg Kroah-Hartman  *	gsm_control_rls		-	remote line status
179096fd7ce5SGreg Kroah-Hartman  *	@gsm: GSM channel
179196fd7ce5SGreg Kroah-Hartman  *	@data: data bytes
179296fd7ce5SGreg Kroah-Hartman  *	@clen: data length
179396fd7ce5SGreg Kroah-Hartman  *
179496fd7ce5SGreg Kroah-Hartman  *	The modem sends us a two byte message on the control channel whenever
179596fd7ce5SGreg Kroah-Hartman  *	it wishes to send us an error state from the virtual link. Stuff
179696fd7ce5SGreg Kroah-Hartman  *	this into the uplink tty if present
179796fd7ce5SGreg Kroah-Hartman  */
179896fd7ce5SGreg Kroah-Hartman 
gsm_control_rls(struct gsm_mux * gsm,const u8 * data,int clen)17994feb7a4aSTony Lindgren static void gsm_control_rls(struct gsm_mux *gsm, const u8 *data, int clen)
180096fd7ce5SGreg Kroah-Hartman {
180192a19f9cSJiri Slaby 	struct tty_port *port;
180296fd7ce5SGreg Kroah-Hartman 	unsigned int addr = 0;
180396fd7ce5SGreg Kroah-Hartman 	u8 bits;
180496fd7ce5SGreg Kroah-Hartman 	int len = clen;
18054feb7a4aSTony Lindgren 	const u8 *dp = data;
180696fd7ce5SGreg Kroah-Hartman 
180796fd7ce5SGreg Kroah-Hartman 	while (gsm_read_ea(&addr, *dp++) == 0) {
180896fd7ce5SGreg Kroah-Hartman 		len--;
180996fd7ce5SGreg Kroah-Hartman 		if (len == 0)
181096fd7ce5SGreg Kroah-Hartman 			return;
181196fd7ce5SGreg Kroah-Hartman 	}
181296fd7ce5SGreg Kroah-Hartman 	/* Must be at least one byte following ea */
181396fd7ce5SGreg Kroah-Hartman 	len--;
181496fd7ce5SGreg Kroah-Hartman 	if (len <= 0)
181596fd7ce5SGreg Kroah-Hartman 		return;
181696fd7ce5SGreg Kroah-Hartman 	addr >>= 1;
181796fd7ce5SGreg Kroah-Hartman 	/* Closed port, or invalid ? */
181896fd7ce5SGreg Kroah-Hartman 	if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL)
181996fd7ce5SGreg Kroah-Hartman 		return;
182096fd7ce5SGreg Kroah-Hartman 	/* No error ? */
182196fd7ce5SGreg Kroah-Hartman 	bits = *dp;
182296fd7ce5SGreg Kroah-Hartman 	if ((bits & 1) == 0)
182396fd7ce5SGreg Kroah-Hartman 		return;
182496fd7ce5SGreg Kroah-Hartman 
182592a19f9cSJiri Slaby 	port = &gsm->dlci[addr]->port;
182692a19f9cSJiri Slaby 
182796fd7ce5SGreg Kroah-Hartman 	if (bits & 2)
182892a19f9cSJiri Slaby 		tty_insert_flip_char(port, 0, TTY_OVERRUN);
182996fd7ce5SGreg Kroah-Hartman 	if (bits & 4)
183092a19f9cSJiri Slaby 		tty_insert_flip_char(port, 0, TTY_PARITY);
183196fd7ce5SGreg Kroah-Hartman 	if (bits & 8)
183292a19f9cSJiri Slaby 		tty_insert_flip_char(port, 0, TTY_FRAME);
183392a19f9cSJiri Slaby 
18342e124b4aSJiri Slaby 	tty_flip_buffer_push(port);
18352e124b4aSJiri Slaby 
183696fd7ce5SGreg Kroah-Hartman 	gsm_control_reply(gsm, CMD_RLS, data, clen);
183796fd7ce5SGreg Kroah-Hartman }
183896fd7ce5SGreg Kroah-Hartman 
183996fd7ce5SGreg Kroah-Hartman static void gsm_dlci_begin_close(struct gsm_dlci *dlci);
184096fd7ce5SGreg Kroah-Hartman 
184196fd7ce5SGreg Kroah-Hartman /**
184296fd7ce5SGreg Kroah-Hartman  *	gsm_control_message	-	DLCI 0 control processing
184396fd7ce5SGreg Kroah-Hartman  *	@gsm: our GSM mux
184496fd7ce5SGreg Kroah-Hartman  *	@command:  the command EA
184596fd7ce5SGreg Kroah-Hartman  *	@data: data beyond the command/length EAs
184696fd7ce5SGreg Kroah-Hartman  *	@clen: length
184796fd7ce5SGreg Kroah-Hartman  *
184896fd7ce5SGreg Kroah-Hartman  *	Input processor for control messages from the other end of the link.
184996fd7ce5SGreg Kroah-Hartman  *	Processes the incoming request and queues a response frame or an
185096fd7ce5SGreg Kroah-Hartman  *	NSC response if not supported
185196fd7ce5SGreg Kroah-Hartman  */
185296fd7ce5SGreg Kroah-Hartman 
gsm_control_message(struct gsm_mux * gsm,unsigned int command,const u8 * data,int clen)185396fd7ce5SGreg Kroah-Hartman static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
18544feb7a4aSTony Lindgren 						const u8 *data, int clen)
185596fd7ce5SGreg Kroah-Hartman {
185696fd7ce5SGreg Kroah-Hartman 	u8 buf[1];
18575e44708fSRuss Gorby 
185896fd7ce5SGreg Kroah-Hartman 	switch (command) {
185996fd7ce5SGreg Kroah-Hartman 	case CMD_CLD: {
1860a8c5b825SDaniel Starke 		struct gsm_dlci *dlci = gsm->dlci[0];
186196fd7ce5SGreg Kroah-Hartman 		/* Modem wishes to close down */
186296fd7ce5SGreg Kroah-Hartman 		if (dlci) {
18635677fcf3SJiri Slaby 			dlci->dead = true;
18645677fcf3SJiri Slaby 			gsm->dead = true;
1865a8c5b825SDaniel Starke 			gsm_dlci_begin_close(dlci);
186696fd7ce5SGreg Kroah-Hartman 		}
186796fd7ce5SGreg Kroah-Hartman 		}
186896fd7ce5SGreg Kroah-Hartman 		break;
186996fd7ce5SGreg Kroah-Hartman 	case CMD_TEST:
187096fd7ce5SGreg Kroah-Hartman 		/* Modem wishes to test, reply with the data */
187196fd7ce5SGreg Kroah-Hartman 		gsm_control_reply(gsm, CMD_TEST, data, clen);
187296fd7ce5SGreg Kroah-Hartman 		break;
187396fd7ce5SGreg Kroah-Hartman 	case CMD_FCON:
187496fd7ce5SGreg Kroah-Hartman 		/* Modem can accept data again */
18757a9ed9c0SJiri Slaby 		gsm->constipated = false;
1876c01af4feSFrederic Berat 		gsm_control_reply(gsm, CMD_FCON, NULL, 0);
187796fd7ce5SGreg Kroah-Hartman 		/* Kick the link in case it is idling */
18780af02167SDaniel Starke 		gsmld_write_trigger(gsm);
187996fd7ce5SGreg Kroah-Hartman 		break;
1880c01af4feSFrederic Berat 	case CMD_FCOFF:
1881c01af4feSFrederic Berat 		/* Modem wants us to STFU */
18827a9ed9c0SJiri Slaby 		gsm->constipated = true;
1883c01af4feSFrederic Berat 		gsm_control_reply(gsm, CMD_FCOFF, NULL, 0);
1884c01af4feSFrederic Berat 		break;
188596fd7ce5SGreg Kroah-Hartman 	case CMD_MSC:
188696fd7ce5SGreg Kroah-Hartman 		/* Out of band modem line change indicator for a DLCI */
188796fd7ce5SGreg Kroah-Hartman 		gsm_control_modem(gsm, data, clen);
188896fd7ce5SGreg Kroah-Hartman 		break;
188996fd7ce5SGreg Kroah-Hartman 	case CMD_RLS:
189096fd7ce5SGreg Kroah-Hartman 		/* Out of band error reception for a DLCI */
189196fd7ce5SGreg Kroah-Hartman 		gsm_control_rls(gsm, data, clen);
189296fd7ce5SGreg Kroah-Hartman 		break;
189396fd7ce5SGreg Kroah-Hartman 	case CMD_PSC:
189496fd7ce5SGreg Kroah-Hartman 		/* Modem wishes to enter power saving state */
189596fd7ce5SGreg Kroah-Hartman 		gsm_control_reply(gsm, CMD_PSC, NULL, 0);
189696fd7ce5SGreg Kroah-Hartman 		break;
189792f1f0c3SDaniel Starke 		/* Optional commands */
189892f1f0c3SDaniel Starke 	case CMD_PN:
189992f1f0c3SDaniel Starke 		/* Modem sends a parameter negotiation command */
190092f1f0c3SDaniel Starke 		gsm_control_negotiation(gsm, 1, data, clen);
190192f1f0c3SDaniel Starke 		break;
190296fd7ce5SGreg Kroah-Hartman 		/* Optional unsupported commands */
190325985edcSLucas De Marchi 	case CMD_RPN:	/* Remote port negotiation */
190425985edcSLucas De Marchi 	case CMD_SNC:	/* Service negotiation command */
1905e74c048aSDaniel Starke 		gsm->unsupported++;
1906e74c048aSDaniel Starke 		fallthrough;
190796fd7ce5SGreg Kroah-Hartman 	default:
190896fd7ce5SGreg Kroah-Hartman 		/* Reply to bad commands with an NSC */
190996fd7ce5SGreg Kroah-Hartman 		buf[0] = command;
191096fd7ce5SGreg Kroah-Hartman 		gsm_control_reply(gsm, CMD_NSC, buf, 1);
191196fd7ce5SGreg Kroah-Hartman 		break;
191296fd7ce5SGreg Kroah-Hartman 	}
191396fd7ce5SGreg Kroah-Hartman }
191496fd7ce5SGreg Kroah-Hartman 
191596fd7ce5SGreg Kroah-Hartman /**
191696fd7ce5SGreg Kroah-Hartman  *	gsm_control_response	-	process a response to our control
191796fd7ce5SGreg Kroah-Hartman  *	@gsm: our GSM mux
191896fd7ce5SGreg Kroah-Hartman  *	@command: the command (response) EA
191996fd7ce5SGreg Kroah-Hartman  *	@data: data beyond the command/length EA
192096fd7ce5SGreg Kroah-Hartman  *	@clen: length
192196fd7ce5SGreg Kroah-Hartman  *
192296fd7ce5SGreg Kroah-Hartman  *	Process a response to an outstanding command. We only allow a single
192396fd7ce5SGreg Kroah-Hartman  *	control message in flight so this is fairly easy. All the clean up
192496fd7ce5SGreg Kroah-Hartman  *	is done by the caller, we just update the fields, flag it as done
192596fd7ce5SGreg Kroah-Hartman  *	and return
192696fd7ce5SGreg Kroah-Hartman  */
192796fd7ce5SGreg Kroah-Hartman 
gsm_control_response(struct gsm_mux * gsm,unsigned int command,const u8 * data,int clen)192896fd7ce5SGreg Kroah-Hartman static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
19294feb7a4aSTony Lindgren 						const u8 *data, int clen)
193096fd7ce5SGreg Kroah-Hartman {
193196fd7ce5SGreg Kroah-Hartman 	struct gsm_control *ctrl;
193272206cc7SDaniel Starke 	struct gsm_dlci *dlci;
193396fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
193496fd7ce5SGreg Kroah-Hartman 
193596fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&gsm->control_lock, flags);
193696fd7ce5SGreg Kroah-Hartman 
193796fd7ce5SGreg Kroah-Hartman 	ctrl = gsm->pending_cmd;
193872206cc7SDaniel Starke 	dlci = gsm->dlci[0];
193996fd7ce5SGreg Kroah-Hartman 	command |= 1;
194092f1f0c3SDaniel Starke 	/* Does the reply match our command */
194196fd7ce5SGreg Kroah-Hartman 	if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
194296fd7ce5SGreg Kroah-Hartman 		/* Our command was replied to, kill the retry timer */
194396fd7ce5SGreg Kroah-Hartman 		del_timer(&gsm->t2_timer);
194496fd7ce5SGreg Kroah-Hartman 		gsm->pending_cmd = NULL;
194596fd7ce5SGreg Kroah-Hartman 		/* Rejected by the other end */
194696fd7ce5SGreg Kroah-Hartman 		if (command == CMD_NSC)
194796fd7ce5SGreg Kroah-Hartman 			ctrl->error = -EOPNOTSUPP;
194896fd7ce5SGreg Kroah-Hartman 		ctrl->done = 1;
194996fd7ce5SGreg Kroah-Hartman 		wake_up(&gsm->event);
195092f1f0c3SDaniel Starke 	/* Or did we receive the PN response to our PN command */
195192f1f0c3SDaniel Starke 	} else if (command == CMD_PN) {
195292f1f0c3SDaniel Starke 		gsm_control_negotiation(gsm, 0, data, clen);
195372206cc7SDaniel Starke 	/* Or did we receive the TEST response to our TEST command */
195472206cc7SDaniel Starke 	} else if (command == CMD_TEST && clen == 1 && *data == gsm->ka_num) {
195572206cc7SDaniel Starke 		gsm->ka_retries = -1; /* trigger new keep-alive message */
195672206cc7SDaniel Starke 		if (dlci && !dlci->dead)
195772206cc7SDaniel Starke 			mod_timer(&gsm->ka_timer, jiffies + gsm->keep_alive * HZ / 100);
195872206cc7SDaniel Starke 	}
195972206cc7SDaniel Starke 	spin_unlock_irqrestore(&gsm->control_lock, flags);
196072206cc7SDaniel Starke }
196172206cc7SDaniel Starke 
196272206cc7SDaniel Starke /**
196372206cc7SDaniel Starke  * gsm_control_keep_alive	-	check timeout or start keep-alive
196472206cc7SDaniel Starke  * @t: timer contained in our gsm object
196572206cc7SDaniel Starke  *
196672206cc7SDaniel Starke  * Called off the keep-alive timer expiry signaling that our link
196772206cc7SDaniel Starke  * partner is not responding anymore. Link will be closed.
196872206cc7SDaniel Starke  * This is also called to startup our timer.
196972206cc7SDaniel Starke  */
197072206cc7SDaniel Starke 
gsm_control_keep_alive(struct timer_list * t)197172206cc7SDaniel Starke static void gsm_control_keep_alive(struct timer_list *t)
197272206cc7SDaniel Starke {
197372206cc7SDaniel Starke 	struct gsm_mux *gsm = from_timer(gsm, t, ka_timer);
197472206cc7SDaniel Starke 	unsigned long flags;
197572206cc7SDaniel Starke 
197672206cc7SDaniel Starke 	spin_lock_irqsave(&gsm->control_lock, flags);
197772206cc7SDaniel Starke 	if (gsm->ka_num && gsm->ka_retries == 0) {
197872206cc7SDaniel Starke 		/* Keep-alive expired -> close the link */
197972206cc7SDaniel Starke 		if (debug & DBG_ERRORS)
198072206cc7SDaniel Starke 			pr_debug("%s keep-alive timed out\n", __func__);
198172206cc7SDaniel Starke 		spin_unlock_irqrestore(&gsm->control_lock, flags);
198272206cc7SDaniel Starke 		if (gsm->dlci[0])
198372206cc7SDaniel Starke 			gsm_dlci_begin_close(gsm->dlci[0]);
198472206cc7SDaniel Starke 		return;
198572206cc7SDaniel Starke 	} else if (gsm->keep_alive && gsm->dlci[0] && !gsm->dlci[0]->dead) {
198672206cc7SDaniel Starke 		if (gsm->ka_retries > 0) {
198772206cc7SDaniel Starke 			/* T2 expired for keep-alive -> resend */
198872206cc7SDaniel Starke 			gsm->ka_retries--;
198972206cc7SDaniel Starke 		} else {
199072206cc7SDaniel Starke 			/* Start keep-alive timer */
199172206cc7SDaniel Starke 			gsm->ka_num++;
199272206cc7SDaniel Starke 			if (!gsm->ka_num)
199372206cc7SDaniel Starke 				gsm->ka_num++;
199472206cc7SDaniel Starke 			gsm->ka_retries = (signed int)gsm->n2;
199572206cc7SDaniel Starke 		}
199672206cc7SDaniel Starke 		gsm_control_command(gsm, CMD_TEST, &gsm->ka_num,
199772206cc7SDaniel Starke 				    sizeof(gsm->ka_num));
199872206cc7SDaniel Starke 		mod_timer(&gsm->ka_timer,
199972206cc7SDaniel Starke 			  jiffies + gsm->t2 * HZ / 100);
200096fd7ce5SGreg Kroah-Hartman 	}
200196fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&gsm->control_lock, flags);
200296fd7ce5SGreg Kroah-Hartman }
200396fd7ce5SGreg Kroah-Hartman 
200496fd7ce5SGreg Kroah-Hartman /**
200596fd7ce5SGreg Kroah-Hartman  *	gsm_control_transmit	-	send control packet
200696fd7ce5SGreg Kroah-Hartman  *	@gsm: gsm mux
200796fd7ce5SGreg Kroah-Hartman  *	@ctrl: frame to send
200896fd7ce5SGreg Kroah-Hartman  *
200996fd7ce5SGreg Kroah-Hartman  *	Send out a pending control command (called under control lock)
201096fd7ce5SGreg Kroah-Hartman  */
201196fd7ce5SGreg Kroah-Hartman 
gsm_control_transmit(struct gsm_mux * gsm,struct gsm_control * ctrl)201296fd7ce5SGreg Kroah-Hartman static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl)
201396fd7ce5SGreg Kroah-Hartman {
2014de640bc6SDaniel Starke 	gsm_control_command(gsm, ctrl->cmd, ctrl->data, ctrl->len);
201596fd7ce5SGreg Kroah-Hartman }
201696fd7ce5SGreg Kroah-Hartman 
201796fd7ce5SGreg Kroah-Hartman /**
201896fd7ce5SGreg Kroah-Hartman  *	gsm_control_retransmit	-	retransmit a control frame
2019724ac070SJiri Slaby  *	@t: timer contained in our gsm object
202096fd7ce5SGreg Kroah-Hartman  *
202196fd7ce5SGreg Kroah-Hartman  *	Called off the T2 timer expiry in order to retransmit control frames
202296fd7ce5SGreg Kroah-Hartman  *	that have been lost in the system somewhere. The control_lock protects
202396fd7ce5SGreg Kroah-Hartman  *	us from colliding with another sender or a receive completion event.
202496fd7ce5SGreg Kroah-Hartman  *	In that situation the timer may still occur in a small window but
202596fd7ce5SGreg Kroah-Hartman  *	gsm->pending_cmd will be NULL and we just let the timer expire.
202696fd7ce5SGreg Kroah-Hartman  */
202796fd7ce5SGreg Kroah-Hartman 
gsm_control_retransmit(struct timer_list * t)2028e99e88a9SKees Cook static void gsm_control_retransmit(struct timer_list *t)
202996fd7ce5SGreg Kroah-Hartman {
2030e99e88a9SKees Cook 	struct gsm_mux *gsm = from_timer(gsm, t, t2_timer);
203196fd7ce5SGreg Kroah-Hartman 	struct gsm_control *ctrl;
203296fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
203396fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&gsm->control_lock, flags);
203496fd7ce5SGreg Kroah-Hartman 	ctrl = gsm->pending_cmd;
203596fd7ce5SGreg Kroah-Hartman 	if (ctrl) {
20364fae831bSDaniel Starke 		if (gsm->cretries == 0 || !gsm->dlci[0] || gsm->dlci[0]->dead) {
203796fd7ce5SGreg Kroah-Hartman 			gsm->pending_cmd = NULL;
203896fd7ce5SGreg Kroah-Hartman 			ctrl->error = -ETIMEDOUT;
203996fd7ce5SGreg Kroah-Hartman 			ctrl->done = 1;
204096fd7ce5SGreg Kroah-Hartman 			spin_unlock_irqrestore(&gsm->control_lock, flags);
204196fd7ce5SGreg Kroah-Hartman 			wake_up(&gsm->event);
204296fd7ce5SGreg Kroah-Hartman 			return;
204396fd7ce5SGreg Kroah-Hartman 		}
2044d0bcdffcSDaniel Starke 		gsm->cretries--;
204596fd7ce5SGreg Kroah-Hartman 		gsm_control_transmit(gsm, ctrl);
204696fd7ce5SGreg Kroah-Hartman 		mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
204796fd7ce5SGreg Kroah-Hartman 	}
204896fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&gsm->control_lock, flags);
204996fd7ce5SGreg Kroah-Hartman }
205096fd7ce5SGreg Kroah-Hartman 
205196fd7ce5SGreg Kroah-Hartman /**
205296fd7ce5SGreg Kroah-Hartman  *	gsm_control_send	-	send a control frame on DLCI 0
205396fd7ce5SGreg Kroah-Hartman  *	@gsm: the GSM channel
205496fd7ce5SGreg Kroah-Hartman  *	@command: command  to send including CR bit
205596fd7ce5SGreg Kroah-Hartman  *	@data: bytes of data (must be kmalloced)
2056724ac070SJiri Slaby  *	@clen: length of the block to send
205796fd7ce5SGreg Kroah-Hartman  *
205896fd7ce5SGreg Kroah-Hartman  *	Queue and dispatch a control command. Only one command can be
205996fd7ce5SGreg Kroah-Hartman  *	active at a time. In theory more can be outstanding but the matching
206096fd7ce5SGreg Kroah-Hartman  *	gets really complicated so for now stick to one outstanding.
206196fd7ce5SGreg Kroah-Hartman  */
206296fd7ce5SGreg Kroah-Hartman 
gsm_control_send(struct gsm_mux * gsm,unsigned int command,u8 * data,int clen)206396fd7ce5SGreg Kroah-Hartman static struct gsm_control *gsm_control_send(struct gsm_mux *gsm,
206496fd7ce5SGreg Kroah-Hartman 		unsigned int command, u8 *data, int clen)
206596fd7ce5SGreg Kroah-Hartman {
206696fd7ce5SGreg Kroah-Hartman 	struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control),
20677b7dfe48SDuoming Zhou 						GFP_ATOMIC);
206896fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
206996fd7ce5SGreg Kroah-Hartman 	if (ctrl == NULL)
207096fd7ce5SGreg Kroah-Hartman 		return NULL;
207196fd7ce5SGreg Kroah-Hartman retry:
207296fd7ce5SGreg Kroah-Hartman 	wait_event(gsm->event, gsm->pending_cmd == NULL);
207396fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&gsm->control_lock, flags);
207496fd7ce5SGreg Kroah-Hartman 	if (gsm->pending_cmd != NULL) {
207596fd7ce5SGreg Kroah-Hartman 		spin_unlock_irqrestore(&gsm->control_lock, flags);
207696fd7ce5SGreg Kroah-Hartman 		goto retry;
207796fd7ce5SGreg Kroah-Hartman 	}
207896fd7ce5SGreg Kroah-Hartman 	ctrl->cmd = command;
207996fd7ce5SGreg Kroah-Hartman 	ctrl->data = data;
208096fd7ce5SGreg Kroah-Hartman 	ctrl->len = clen;
208196fd7ce5SGreg Kroah-Hartman 	gsm->pending_cmd = ctrl;
2082e9ec2254STony Lindgren 
2083e9ec2254STony Lindgren 	/* If DLCI0 is in ADM mode skip retries, it won't respond */
2084e9ec2254STony Lindgren 	if (gsm->dlci[0]->mode == DLCI_MODE_ADM)
2085d0bcdffcSDaniel Starke 		gsm->cretries = 0;
2086e9ec2254STony Lindgren 	else
208796fd7ce5SGreg Kroah-Hartman 		gsm->cretries = gsm->n2;
2088e9ec2254STony Lindgren 
208996fd7ce5SGreg Kroah-Hartman 	mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
209096fd7ce5SGreg Kroah-Hartman 	gsm_control_transmit(gsm, ctrl);
209196fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&gsm->control_lock, flags);
209296fd7ce5SGreg Kroah-Hartman 	return ctrl;
209396fd7ce5SGreg Kroah-Hartman }
209496fd7ce5SGreg Kroah-Hartman 
209596fd7ce5SGreg Kroah-Hartman /**
209696fd7ce5SGreg Kroah-Hartman  *	gsm_control_wait	-	wait for a control to finish
209796fd7ce5SGreg Kroah-Hartman  *	@gsm: GSM mux
209896fd7ce5SGreg Kroah-Hartman  *	@control: control we are waiting on
209996fd7ce5SGreg Kroah-Hartman  *
210096fd7ce5SGreg Kroah-Hartman  *	Waits for the control to complete or time out. Frees any used
210196fd7ce5SGreg Kroah-Hartman  *	resources and returns 0 for success, or an error if the remote
210296fd7ce5SGreg Kroah-Hartman  *	rejected or ignored the request.
210396fd7ce5SGreg Kroah-Hartman  */
210496fd7ce5SGreg Kroah-Hartman 
gsm_control_wait(struct gsm_mux * gsm,struct gsm_control * control)210596fd7ce5SGreg Kroah-Hartman static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control)
210696fd7ce5SGreg Kroah-Hartman {
210796fd7ce5SGreg Kroah-Hartman 	int err;
210896fd7ce5SGreg Kroah-Hartman 	wait_event(gsm->event, control->done == 1);
210996fd7ce5SGreg Kroah-Hartman 	err = control->error;
211096fd7ce5SGreg Kroah-Hartman 	kfree(control);
211196fd7ce5SGreg Kroah-Hartman 	return err;
211296fd7ce5SGreg Kroah-Hartman }
211396fd7ce5SGreg Kroah-Hartman 
211496fd7ce5SGreg Kroah-Hartman 
211596fd7ce5SGreg Kroah-Hartman /*
211696fd7ce5SGreg Kroah-Hartman  *	DLCI level handling: Needs krefs
211796fd7ce5SGreg Kroah-Hartman  */
211896fd7ce5SGreg Kroah-Hartman 
211996fd7ce5SGreg Kroah-Hartman /*
212096fd7ce5SGreg Kroah-Hartman  *	State transitions and timers
212196fd7ce5SGreg Kroah-Hartman  */
212296fd7ce5SGreg Kroah-Hartman 
212396fd7ce5SGreg Kroah-Hartman /**
212496fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_close		-	a DLCI has closed
212596fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI that closed
212696fd7ce5SGreg Kroah-Hartman  *
212796fd7ce5SGreg Kroah-Hartman  *	Perform processing when moving a DLCI into closed state. If there
212896fd7ce5SGreg Kroah-Hartman  *	is an attached tty this is hung up
212996fd7ce5SGreg Kroah-Hartman  */
213096fd7ce5SGreg Kroah-Hartman 
gsm_dlci_close(struct gsm_dlci * dlci)213196fd7ce5SGreg Kroah-Hartman static void gsm_dlci_close(struct gsm_dlci *dlci)
213296fd7ce5SGreg Kroah-Hartman {
213396fd7ce5SGreg Kroah-Hartman 	del_timer(&dlci->t1);
2134c07da737SDaniel Starke 	if (debug & DBG_ERRORS)
21355f9a31d6SAlan Cox 		pr_debug("DLCI %d goes closed.\n", dlci->addr);
213696fd7ce5SGreg Kroah-Hartman 	dlci->state = DLCI_CLOSED;
2137ac77f007SDaniel Starke 	/* Prevent us from sending data before the link is up again */
2138ac77f007SDaniel Starke 	dlci->constipated = true;
213996fd7ce5SGreg Kroah-Hartman 	if (dlci->addr != 0) {
2140aa27a094SJiri Slaby 		tty_port_tty_hangup(&dlci->port, false);
21410af02167SDaniel Starke 		gsm_dlci_clear_queues(dlci->gsm, dlci);
2142a2ab75b8Sdaniel.starke@siemens.com 		/* Ensure that gsmtty_open() can return. */
2143515be7baSIlpo Järvinen 		tty_port_set_initialized(&dlci->port, false);
2144a2ab75b8Sdaniel.starke@siemens.com 		wake_up_interruptible(&dlci->port.open_wait);
214572206cc7SDaniel Starke 	} else {
214672206cc7SDaniel Starke 		del_timer(&dlci->gsm->ka_timer);
21475677fcf3SJiri Slaby 		dlci->gsm->dead = true;
214872206cc7SDaniel Starke 	}
214996fd7ce5SGreg Kroah-Hartman 	/* A DLCI 0 close is a MUX termination so we need to kick that
215096fd7ce5SGreg Kroah-Hartman 	   back to userspace somehow */
21510af02167SDaniel Starke 	gsm_dlci_data_kick(dlci);
2152684ae4f9SDaniel Starke 	wake_up_all(&dlci->gsm->event);
215396fd7ce5SGreg Kroah-Hartman }
215496fd7ce5SGreg Kroah-Hartman 
215596fd7ce5SGreg Kroah-Hartman /**
215696fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_open		-	a DLCI has opened
215796fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI that opened
215896fd7ce5SGreg Kroah-Hartman  *
215996fd7ce5SGreg Kroah-Hartman  *	Perform processing when moving a DLCI into open state.
216096fd7ce5SGreg Kroah-Hartman  */
216196fd7ce5SGreg Kroah-Hartman 
gsm_dlci_open(struct gsm_dlci * dlci)216296fd7ce5SGreg Kroah-Hartman static void gsm_dlci_open(struct gsm_dlci *dlci)
216396fd7ce5SGreg Kroah-Hartman {
216472206cc7SDaniel Starke 	struct gsm_mux *gsm = dlci->gsm;
216572206cc7SDaniel Starke 
216696fd7ce5SGreg Kroah-Hartman 	/* Note that SABM UA .. SABM UA first UA lost can mean that we go
216796fd7ce5SGreg Kroah-Hartman 	   open -> open */
216896fd7ce5SGreg Kroah-Hartman 	del_timer(&dlci->t1);
216996fd7ce5SGreg Kroah-Hartman 	/* This will let a tty open continue */
217096fd7ce5SGreg Kroah-Hartman 	dlci->state = DLCI_OPEN;
2171ac77f007SDaniel Starke 	dlci->constipated = false;
2172c07da737SDaniel Starke 	if (debug & DBG_ERRORS)
21735f9a31d6SAlan Cox 		pr_debug("DLCI %d goes open.\n", dlci->addr);
217448473802SDaniel Starke 	/* Send current modem state */
217572206cc7SDaniel Starke 	if (dlci->addr) {
2176c19ffe00SDaniel Starke 		gsm_modem_update(dlci, 0);
217772206cc7SDaniel Starke 	} else {
217872206cc7SDaniel Starke 		/* Start keep-alive control */
217972206cc7SDaniel Starke 		gsm->ka_num = 0;
218072206cc7SDaniel Starke 		gsm->ka_retries = -1;
218172206cc7SDaniel Starke 		mod_timer(&gsm->ka_timer,
218272206cc7SDaniel Starke 			  jiffies + gsm->keep_alive * HZ / 100);
218372206cc7SDaniel Starke 	}
21840af02167SDaniel Starke 	gsm_dlci_data_kick(dlci);
218596fd7ce5SGreg Kroah-Hartman 	wake_up(&dlci->gsm->event);
218696fd7ce5SGreg Kroah-Hartman }
218796fd7ce5SGreg Kroah-Hartman 
218896fd7ce5SGreg Kroah-Hartman /**
218992f1f0c3SDaniel Starke  * gsm_dlci_negotiate	-	start parameter negotiation
219092f1f0c3SDaniel Starke  * @dlci: DLCI to open
219192f1f0c3SDaniel Starke  *
219292f1f0c3SDaniel Starke  * Starts the parameter negotiation for the new DLCI. This needs to be done
219392f1f0c3SDaniel Starke  * before the DLCI initialized the channel via SABM.
219492f1f0c3SDaniel Starke  */
gsm_dlci_negotiate(struct gsm_dlci * dlci)219592f1f0c3SDaniel Starke static int gsm_dlci_negotiate(struct gsm_dlci *dlci)
219692f1f0c3SDaniel Starke {
219792f1f0c3SDaniel Starke 	struct gsm_mux *gsm = dlci->gsm;
219892f1f0c3SDaniel Starke 	struct gsm_dlci_param_bits params;
219992f1f0c3SDaniel Starke 	int ret;
220092f1f0c3SDaniel Starke 
220192f1f0c3SDaniel Starke 	ret = gsm_encode_params(dlci, &params);
220292f1f0c3SDaniel Starke 	if (ret != 0)
220392f1f0c3SDaniel Starke 		return ret;
220492f1f0c3SDaniel Starke 
220592f1f0c3SDaniel Starke 	/* We cannot asynchronous wait for the command response with
220692f1f0c3SDaniel Starke 	 * gsm_command() and gsm_control_wait() at this point.
220792f1f0c3SDaniel Starke 	 */
220892f1f0c3SDaniel Starke 	ret = gsm_control_command(gsm, CMD_PN, (const u8 *)&params,
220992f1f0c3SDaniel Starke 				  sizeof(params));
221092f1f0c3SDaniel Starke 
221192f1f0c3SDaniel Starke 	return ret;
221292f1f0c3SDaniel Starke }
221392f1f0c3SDaniel Starke 
221492f1f0c3SDaniel Starke /**
221596fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_t1		-	T1 timer expiry
2216724ac070SJiri Slaby  *	@t: timer contained in the DLCI that opened
221796fd7ce5SGreg Kroah-Hartman  *
221896fd7ce5SGreg Kroah-Hartman  *	The T1 timer handles retransmits of control frames (essentially of
221996fd7ce5SGreg Kroah-Hartman  *	SABM and DISC). We resend the command until the retry count runs out
222096fd7ce5SGreg Kroah-Hartman  *	in which case an opening port goes back to closed and a closing port
222196fd7ce5SGreg Kroah-Hartman  *	is simply put into closed state (any further frames from the other
222296fd7ce5SGreg Kroah-Hartman  *	end will get a DM response)
2223ea3d8465STony Lindgren  *
2224ea3d8465STony Lindgren  *	Some control dlci can stay in ADM mode with other dlci working just
2225ea3d8465STony Lindgren  *	fine. In that case we can just keep the control dlci open after the
2226ea3d8465STony Lindgren  *	DLCI_OPENING retries time out.
222796fd7ce5SGreg Kroah-Hartman  */
222896fd7ce5SGreg Kroah-Hartman 
gsm_dlci_t1(struct timer_list * t)2229e99e88a9SKees Cook static void gsm_dlci_t1(struct timer_list *t)
223096fd7ce5SGreg Kroah-Hartman {
2231e99e88a9SKees Cook 	struct gsm_dlci *dlci = from_timer(dlci, t, t1);
223296fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = dlci->gsm;
223396fd7ce5SGreg Kroah-Hartman 
223496fd7ce5SGreg Kroah-Hartman 	switch (dlci->state) {
223592f1f0c3SDaniel Starke 	case DLCI_CONFIGURE:
223692f1f0c3SDaniel Starke 		if (dlci->retries && gsm_dlci_negotiate(dlci) == 0) {
223792f1f0c3SDaniel Starke 			dlci->retries--;
223892f1f0c3SDaniel Starke 			mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
223992f1f0c3SDaniel Starke 		} else {
2240a1ce6da0SDaniel Starke 			gsm->open_error++;
224192f1f0c3SDaniel Starke 			gsm_dlci_begin_close(dlci); /* prevent half open link */
224292f1f0c3SDaniel Starke 		}
224392f1f0c3SDaniel Starke 		break;
224496fd7ce5SGreg Kroah-Hartman 	case DLCI_OPENING:
224596fd7ce5SGreg Kroah-Hartman 		if (dlci->retries) {
2246f30e10caSDaniel Starke 			dlci->retries--;
224796fd7ce5SGreg Kroah-Hartman 			gsm_command(dlci->gsm, dlci->addr, SABM|PF);
224896fd7ce5SGreg Kroah-Hartman 			mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
2249ea3d8465STony Lindgren 		} else if (!dlci->addr && gsm->control == (DM | PF)) {
2250c07da737SDaniel Starke 			if (debug & DBG_ERRORS)
2251ea3d8465STony Lindgren 				pr_info("DLCI %d opening in ADM mode.\n",
2252ea3d8465STony Lindgren 					dlci->addr);
2253e9ec2254STony Lindgren 			dlci->mode = DLCI_MODE_ADM;
2254ea3d8465STony Lindgren 			gsm_dlci_open(dlci);
2255ea3d8465STony Lindgren 		} else {
2256a1ce6da0SDaniel Starke 			gsm->open_error++;
2257e3b7468fSdaniel.starke@siemens.com 			gsm_dlci_begin_close(dlci); /* prevent half open link */
2258ea3d8465STony Lindgren 		}
2259ea3d8465STony Lindgren 
226096fd7ce5SGreg Kroah-Hartman 		break;
226196fd7ce5SGreg Kroah-Hartman 	case DLCI_CLOSING:
226296fd7ce5SGreg Kroah-Hartman 		if (dlci->retries) {
2263f30e10caSDaniel Starke 			dlci->retries--;
226496fd7ce5SGreg Kroah-Hartman 			gsm_command(dlci->gsm, dlci->addr, DISC|PF);
226596fd7ce5SGreg Kroah-Hartman 			mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
226696fd7ce5SGreg Kroah-Hartman 		} else
226796fd7ce5SGreg Kroah-Hartman 			gsm_dlci_close(dlci);
226896fd7ce5SGreg Kroah-Hartman 		break;
226972ae8cc1SJiri Slaby 	default:
227072ae8cc1SJiri Slaby 		pr_debug("%s: unhandled state: %d\n", __func__, dlci->state);
227172ae8cc1SJiri Slaby 		break;
227296fd7ce5SGreg Kroah-Hartman 	}
227396fd7ce5SGreg Kroah-Hartman }
227496fd7ce5SGreg Kroah-Hartman 
227596fd7ce5SGreg Kroah-Hartman /**
227696fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_begin_open	-	start channel open procedure
227796fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI to open
227896fd7ce5SGreg Kroah-Hartman  *
227996fd7ce5SGreg Kroah-Hartman  *	Commence opening a DLCI from the Linux side. We issue SABM messages
2280ea3d8465STony Lindgren  *	to the modem which should then reply with a UA or ADM, at which point
2281ea3d8465STony Lindgren  *	we will move into open state. Opening is done asynchronously with retry
228296fd7ce5SGreg Kroah-Hartman  *	running off timers and the responses.
228392f1f0c3SDaniel Starke  *	Parameter negotiation is performed before SABM if required.
228496fd7ce5SGreg Kroah-Hartman  */
228596fd7ce5SGreg Kroah-Hartman 
gsm_dlci_begin_open(struct gsm_dlci * dlci)228696fd7ce5SGreg Kroah-Hartman static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
228796fd7ce5SGreg Kroah-Hartman {
228892f1f0c3SDaniel Starke 	struct gsm_mux *gsm = dlci ? dlci->gsm : NULL;
228992f1f0c3SDaniel Starke 	bool need_pn = false;
229092f1f0c3SDaniel Starke 
229192f1f0c3SDaniel Starke 	if (!gsm)
229296fd7ce5SGreg Kroah-Hartman 		return;
229392f1f0c3SDaniel Starke 
229492f1f0c3SDaniel Starke 	if (dlci->addr != 0) {
229592f1f0c3SDaniel Starke 		if (gsm->adaption != 1 || gsm->adaption != dlci->adaption)
229692f1f0c3SDaniel Starke 			need_pn = true;
229792f1f0c3SDaniel Starke 		if (dlci->prio != (roundup(dlci->addr + 1, 8) - 1))
229892f1f0c3SDaniel Starke 			need_pn = true;
229992f1f0c3SDaniel Starke 		if (gsm->ftype != dlci->ftype)
230092f1f0c3SDaniel Starke 			need_pn = true;
230192f1f0c3SDaniel Starke 	}
230292f1f0c3SDaniel Starke 
230392f1f0c3SDaniel Starke 	switch (dlci->state) {
230492f1f0c3SDaniel Starke 	case DLCI_CLOSED:
23054ca58966SDaniel Starke 	case DLCI_WAITING_CONFIG:
230692f1f0c3SDaniel Starke 	case DLCI_CLOSING:
230796fd7ce5SGreg Kroah-Hartman 		dlci->retries = gsm->n2;
230892f1f0c3SDaniel Starke 		if (!need_pn) {
230996fd7ce5SGreg Kroah-Hartman 			dlci->state = DLCI_OPENING;
231092f1f0c3SDaniel Starke 			gsm_command(gsm, dlci->addr, SABM|PF);
231192f1f0c3SDaniel Starke 		} else {
231292f1f0c3SDaniel Starke 			/* Configure DLCI before setup */
231392f1f0c3SDaniel Starke 			dlci->state = DLCI_CONFIGURE;
231492f1f0c3SDaniel Starke 			if (gsm_dlci_negotiate(dlci) != 0) {
231592f1f0c3SDaniel Starke 				gsm_dlci_close(dlci);
231692f1f0c3SDaniel Starke 				return;
231792f1f0c3SDaniel Starke 			}
231892f1f0c3SDaniel Starke 		}
231996fd7ce5SGreg Kroah-Hartman 		mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
232092f1f0c3SDaniel Starke 		break;
232192f1f0c3SDaniel Starke 	default:
232292f1f0c3SDaniel Starke 		break;
232392f1f0c3SDaniel Starke 	}
232496fd7ce5SGreg Kroah-Hartman }
232596fd7ce5SGreg Kroah-Hartman 
232696fd7ce5SGreg Kroah-Hartman /**
2327ac77f007SDaniel Starke  *	gsm_dlci_set_opening	-	change state to opening
2328ac77f007SDaniel Starke  *	@dlci: DLCI to open
2329ac77f007SDaniel Starke  *
2330ac77f007SDaniel Starke  *	Change internal state to wait for DLCI open from initiator side.
2331ac77f007SDaniel Starke  *	We set off timers and responses upon reception of an SABM.
2332ac77f007SDaniel Starke  */
gsm_dlci_set_opening(struct gsm_dlci * dlci)2333ac77f007SDaniel Starke static void gsm_dlci_set_opening(struct gsm_dlci *dlci)
2334ac77f007SDaniel Starke {
2335ac77f007SDaniel Starke 	switch (dlci->state) {
2336ac77f007SDaniel Starke 	case DLCI_CLOSED:
23374ca58966SDaniel Starke 	case DLCI_WAITING_CONFIG:
2338ac77f007SDaniel Starke 	case DLCI_CLOSING:
2339ac77f007SDaniel Starke 		dlci->state = DLCI_OPENING;
2340ac77f007SDaniel Starke 		break;
2341ac77f007SDaniel Starke 	default:
2342ac77f007SDaniel Starke 		break;
2343ac77f007SDaniel Starke 	}
2344ac77f007SDaniel Starke }
2345ac77f007SDaniel Starke 
2346ac77f007SDaniel Starke /**
23474ca58966SDaniel Starke  * gsm_dlci_set_wait_config	-	wait for channel configuration
23484ca58966SDaniel Starke  * @dlci: DLCI to configure
23494ca58966SDaniel Starke  *
23504ca58966SDaniel Starke  * Wait for a DLCI configuration from the application.
23514ca58966SDaniel Starke  */
gsm_dlci_set_wait_config(struct gsm_dlci * dlci)23524ca58966SDaniel Starke static void gsm_dlci_set_wait_config(struct gsm_dlci *dlci)
23534ca58966SDaniel Starke {
23544ca58966SDaniel Starke 	switch (dlci->state) {
23554ca58966SDaniel Starke 	case DLCI_CLOSED:
23564ca58966SDaniel Starke 	case DLCI_CLOSING:
23574ca58966SDaniel Starke 		dlci->state = DLCI_WAITING_CONFIG;
23584ca58966SDaniel Starke 		break;
23594ca58966SDaniel Starke 	default:
23604ca58966SDaniel Starke 		break;
23614ca58966SDaniel Starke 	}
23624ca58966SDaniel Starke }
23634ca58966SDaniel Starke 
23644ca58966SDaniel Starke /**
236596fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_begin_close	-	start channel open procedure
236696fd7ce5SGreg Kroah-Hartman  *	@dlci: DLCI to open
236796fd7ce5SGreg Kroah-Hartman  *
236896fd7ce5SGreg Kroah-Hartman  *	Commence closing a DLCI from the Linux side. We issue DISC messages
236996fd7ce5SGreg Kroah-Hartman  *	to the modem which should then reply with a UA, at which point we
237096fd7ce5SGreg Kroah-Hartman  *	will move into closed state. Closing is done asynchronously with retry
237196fd7ce5SGreg Kroah-Hartman  *	off timers. We may also receive a DM reply from the other end which
237296fd7ce5SGreg Kroah-Hartman  *	indicates the channel was already closed.
237396fd7ce5SGreg Kroah-Hartman  */
237496fd7ce5SGreg Kroah-Hartman 
gsm_dlci_begin_close(struct gsm_dlci * dlci)237596fd7ce5SGreg Kroah-Hartman static void gsm_dlci_begin_close(struct gsm_dlci *dlci)
237696fd7ce5SGreg Kroah-Hartman {
237796fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = dlci->gsm;
237896fd7ce5SGreg Kroah-Hartman 	if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING)
237996fd7ce5SGreg Kroah-Hartman 		return;
238096fd7ce5SGreg Kroah-Hartman 	dlci->retries = gsm->n2;
238196fd7ce5SGreg Kroah-Hartman 	dlci->state = DLCI_CLOSING;
238296fd7ce5SGreg Kroah-Hartman 	gsm_command(dlci->gsm, dlci->addr, DISC|PF);
238396fd7ce5SGreg Kroah-Hartman 	mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
2384684ae4f9SDaniel Starke 	wake_up_interruptible(&gsm->event);
238596fd7ce5SGreg Kroah-Hartman }
238696fd7ce5SGreg Kroah-Hartman 
238796fd7ce5SGreg Kroah-Hartman /**
238896fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_data		-	data arrived
238996fd7ce5SGreg Kroah-Hartman  *	@dlci: channel
239096fd7ce5SGreg Kroah-Hartman  *	@data: block of bytes received
2391724ac070SJiri Slaby  *	@clen: length of received block
239296fd7ce5SGreg Kroah-Hartman  *
239396fd7ce5SGreg Kroah-Hartman  *	A UI or UIH frame has arrived which contains data for a channel
239496fd7ce5SGreg Kroah-Hartman  *	other than the control channel. If the relevant virtual tty is
239596fd7ce5SGreg Kroah-Hartman  *	open we shovel the bits down it, if not we drop them.
239696fd7ce5SGreg Kroah-Hartman  */
239796fd7ce5SGreg Kroah-Hartman 
gsm_dlci_data(struct gsm_dlci * dlci,const u8 * data,int clen)23984feb7a4aSTony Lindgren static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
239996fd7ce5SGreg Kroah-Hartman {
240096fd7ce5SGreg Kroah-Hartman 	/* krefs .. */
240196fd7ce5SGreg Kroah-Hartman 	struct tty_port *port = &dlci->port;
24022e124b4aSJiri Slaby 	struct tty_struct *tty;
240396fd7ce5SGreg Kroah-Hartman 	unsigned int modem = 0;
2404669609ceSDaniel Starke 	int len;
240596fd7ce5SGreg Kroah-Hartman 
2406c07da737SDaniel Starke 	if (debug & DBG_TTY)
2407669609ceSDaniel Starke 		pr_debug("%d bytes for tty\n", clen);
240896fd7ce5SGreg Kroah-Hartman 	switch (dlci->adaption)  {
240996fd7ce5SGreg Kroah-Hartman 	/* Unsupported types */
24103e913eebSGustavo A. R. Silva 	case 4:		/* Packetised interruptible data */
241196fd7ce5SGreg Kroah-Hartman 		break;
24123e913eebSGustavo A. R. Silva 	case 3:		/* Packetised uininterruptible voice/data */
241396fd7ce5SGreg Kroah-Hartman 		break;
24143e913eebSGustavo A. R. Silva 	case 2:		/* Asynchronous serial with line state in each frame */
2415669609ceSDaniel Starke 		len = gsm_read_ea_val(&modem, data, clen);
2416669609ceSDaniel Starke 		if (len < 1)
241796fd7ce5SGreg Kroah-Hartman 			return;
24182e124b4aSJiri Slaby 		tty = tty_port_tty_get(port);
24192e124b4aSJiri Slaby 		if (tty) {
2420669609ceSDaniel Starke 			gsm_process_modem(tty, dlci, modem, len);
24211adf6feeSDaniel Starke 			tty_wakeup(tty);
24222e124b4aSJiri Slaby 			tty_kref_put(tty);
24232e124b4aSJiri Slaby 		}
2424669609ceSDaniel Starke 		/* Skip processed modem data */
2425669609ceSDaniel Starke 		data += len;
2426669609ceSDaniel Starke 		clen -= len;
2427df561f66SGustavo A. R. Silva 		fallthrough;
24283e913eebSGustavo A. R. Silva 	case 1:		/* Line state will go via DLCI 0 controls only */
242996fd7ce5SGreg Kroah-Hartman 	default:
2430669609ceSDaniel Starke 		tty_insert_flip_string(port, data, clen);
24312e124b4aSJiri Slaby 		tty_flip_buffer_push(port);
243296fd7ce5SGreg Kroah-Hartman 	}
243396fd7ce5SGreg Kroah-Hartman }
243496fd7ce5SGreg Kroah-Hartman 
243596fd7ce5SGreg Kroah-Hartman /**
2436542a121aSLee Jones  *	gsm_dlci_command	-	data arrived on control channel
243796fd7ce5SGreg Kroah-Hartman  *	@dlci: channel
243896fd7ce5SGreg Kroah-Hartman  *	@data: block of bytes received
243996fd7ce5SGreg Kroah-Hartman  *	@len: length of received block
244096fd7ce5SGreg Kroah-Hartman  *
244196fd7ce5SGreg Kroah-Hartman  *	A UI or UIH frame has arrived which contains data for DLCI 0 the
244296fd7ce5SGreg Kroah-Hartman  *	control channel. This should contain a command EA followed by
244396fd7ce5SGreg Kroah-Hartman  *	control data bytes. The command EA contains a command/response bit
244496fd7ce5SGreg Kroah-Hartman  *	and we divide up the work accordingly.
244596fd7ce5SGreg Kroah-Hartman  */
244696fd7ce5SGreg Kroah-Hartman 
gsm_dlci_command(struct gsm_dlci * dlci,const u8 * data,int len)24474feb7a4aSTony Lindgren static void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len)
244896fd7ce5SGreg Kroah-Hartman {
244996fd7ce5SGreg Kroah-Hartman 	/* See what command is involved */
245096fd7ce5SGreg Kroah-Hartman 	unsigned int command = 0;
2451669609ceSDaniel Starke 	unsigned int clen = 0;
2452669609ceSDaniel Starke 	unsigned int dlen;
2453669609ceSDaniel Starke 
2454669609ceSDaniel Starke 	/* read the command */
2455669609ceSDaniel Starke 	dlen = gsm_read_ea_val(&command, data, len);
2456669609ceSDaniel Starke 	len -= dlen;
2457669609ceSDaniel Starke 	data += dlen;
2458669609ceSDaniel Starke 
2459669609ceSDaniel Starke 	/* read any control data */
2460669609ceSDaniel Starke 	dlen = gsm_read_ea_val(&clen, data, len);
2461669609ceSDaniel Starke 	len -= dlen;
2462669609ceSDaniel Starke 	data += dlen;
2463669609ceSDaniel Starke 
246496fd7ce5SGreg Kroah-Hartman 	/* Malformed command? */
2465b99f51baSDaniel Starke 	if (clen > len) {
2466b99f51baSDaniel Starke 		dlci->gsm->malformed++;
246796fd7ce5SGreg Kroah-Hartman 		return;
2468b99f51baSDaniel Starke 	}
2469669609ceSDaniel Starke 
247096fd7ce5SGreg Kroah-Hartman 	if (command & 1)
2471669609ceSDaniel Starke 		gsm_control_message(dlci->gsm, command, data, clen);
247296fd7ce5SGreg Kroah-Hartman 	else
2473669609ceSDaniel Starke 		gsm_control_response(dlci->gsm, command, data, clen);
247496fd7ce5SGreg Kroah-Hartman }
247596fd7ce5SGreg Kroah-Hartman 
2476c568f708SDaniel Starke /**
247715743ae5SFedor Pchelkin  *	gsm_kick_timer	-	transmit if possible
247815743ae5SFedor Pchelkin  *	@t: timer contained in our gsm object
2479c568f708SDaniel Starke  *
2480c568f708SDaniel Starke  *	Transmit data from DLCIs if the queue is empty. We can't rely on
2481c568f708SDaniel Starke  *	a tty wakeup except when we filled the pipe so we need to fire off
2482c568f708SDaniel Starke  *	new data ourselves in other cases.
2483c568f708SDaniel Starke  */
gsm_kick_timer(struct timer_list * t)248415743ae5SFedor Pchelkin static void gsm_kick_timer(struct timer_list *t)
2485c568f708SDaniel Starke {
248615743ae5SFedor Pchelkin 	struct gsm_mux *gsm = from_timer(gsm, t, kick_timer);
2487acdab4cbSFedor Pchelkin 	unsigned long flags;
2488c568f708SDaniel Starke 	int sent = 0;
2489c568f708SDaniel Starke 
2490acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&gsm->tx_lock, flags);
2491c568f708SDaniel Starke 	/* If we have nothing running then we need to fire up */
2492c568f708SDaniel Starke 	if (gsm->tx_bytes < TX_THRESH_LO)
2493c568f708SDaniel Starke 		sent = gsm_dlci_data_sweep(gsm);
2494acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&gsm->tx_lock, flags);
2495c568f708SDaniel Starke 
2496c07da737SDaniel Starke 	if (sent && debug & DBG_DATA)
2497c568f708SDaniel Starke 		pr_info("%s TX queue stalled\n", __func__);
2498c568f708SDaniel Starke }
2499c568f708SDaniel Starke 
25004ca58966SDaniel Starke /**
25014ca58966SDaniel Starke  * gsm_dlci_copy_config_values	-	copy DLCI configuration
25024ca58966SDaniel Starke  * @dlci: source DLCI
25034ca58966SDaniel Starke  * @dc: configuration structure to fill
25044ca58966SDaniel Starke  */
gsm_dlci_copy_config_values(struct gsm_dlci * dlci,struct gsm_dlci_config * dc)25054ca58966SDaniel Starke static void gsm_dlci_copy_config_values(struct gsm_dlci *dlci, struct gsm_dlci_config *dc)
25064ca58966SDaniel Starke {
25074ca58966SDaniel Starke 	memset(dc, 0, sizeof(*dc));
25084ca58966SDaniel Starke 	dc->channel = (u32)dlci->addr;
25094ca58966SDaniel Starke 	dc->adaption = (u32)dlci->adaption;
25104ca58966SDaniel Starke 	dc->mtu = (u32)dlci->mtu;
25114ca58966SDaniel Starke 	dc->priority = (u32)dlci->prio;
25124ca58966SDaniel Starke 	if (dlci->ftype == UIH)
25134ca58966SDaniel Starke 		dc->i = 1;
25144ca58966SDaniel Starke 	else
25154ca58966SDaniel Starke 		dc->i = 2;
25164ca58966SDaniel Starke 	dc->k = (u32)dlci->k;
25174ca58966SDaniel Starke }
25184ca58966SDaniel Starke 
25194ca58966SDaniel Starke /**
25204ca58966SDaniel Starke  * gsm_dlci_config	-	configure DLCI from configuration
25214ca58966SDaniel Starke  * @dlci: DLCI to configure
25224ca58966SDaniel Starke  * @dc: DLCI configuration
25234ca58966SDaniel Starke  * @open: open DLCI after configuration?
25244ca58966SDaniel Starke  */
gsm_dlci_config(struct gsm_dlci * dlci,struct gsm_dlci_config * dc,int open)25254ca58966SDaniel Starke static int gsm_dlci_config(struct gsm_dlci *dlci, struct gsm_dlci_config *dc, int open)
25264ca58966SDaniel Starke {
25274ca58966SDaniel Starke 	struct gsm_mux *gsm;
25284ca58966SDaniel Starke 	bool need_restart = false;
25294ca58966SDaniel Starke 	bool need_open = false;
25304ca58966SDaniel Starke 	unsigned int i;
25314ca58966SDaniel Starke 
25324ca58966SDaniel Starke 	/*
25334ca58966SDaniel Starke 	 * Check that userspace doesn't put stuff in here to prevent breakages
25344ca58966SDaniel Starke 	 * in the future.
25354ca58966SDaniel Starke 	 */
25364ca58966SDaniel Starke 	for (i = 0; i < ARRAY_SIZE(dc->reserved); i++)
25374ca58966SDaniel Starke 		if (dc->reserved[i])
25384ca58966SDaniel Starke 			return -EINVAL;
25394ca58966SDaniel Starke 
25404ca58966SDaniel Starke 	if (!dlci)
25414ca58966SDaniel Starke 		return -EINVAL;
25424ca58966SDaniel Starke 	gsm = dlci->gsm;
25434ca58966SDaniel Starke 
25444ca58966SDaniel Starke 	/* Stuff we don't support yet - I frame transport */
25454ca58966SDaniel Starke 	if (dc->adaption != 1 && dc->adaption != 2)
25464ca58966SDaniel Starke 		return -EOPNOTSUPP;
25474ca58966SDaniel Starke 	if (dc->mtu > MAX_MTU || dc->mtu < MIN_MTU || dc->mtu > gsm->mru)
25484ca58966SDaniel Starke 		return -EINVAL;
25494ca58966SDaniel Starke 	if (dc->priority >= 64)
25504ca58966SDaniel Starke 		return -EINVAL;
25514ca58966SDaniel Starke 	if (dc->i == 0 || dc->i > 2)  /* UIH and UI only */
25524ca58966SDaniel Starke 		return -EINVAL;
25534ca58966SDaniel Starke 	if (dc->k > 7)
25544ca58966SDaniel Starke 		return -EINVAL;
2555a031c77dSDaniel Starke 	if (dc->flags & ~GSM_FL_RESTART)   /* allow future extensions */
2556a031c77dSDaniel Starke 		return -EINVAL;
25574ca58966SDaniel Starke 
25584ca58966SDaniel Starke 	/*
25594ca58966SDaniel Starke 	 * See what is needed for reconfiguration
25604ca58966SDaniel Starke 	 */
25614ca58966SDaniel Starke 	/* Framing fields */
25624ca58966SDaniel Starke 	if (dc->adaption != dlci->adaption)
25634ca58966SDaniel Starke 		need_restart = true;
25644ca58966SDaniel Starke 	if (dc->mtu != dlci->mtu)
25654ca58966SDaniel Starke 		need_restart = true;
25664ca58966SDaniel Starke 	if (dc->i != dlci->ftype)
25674ca58966SDaniel Starke 		need_restart = true;
25684ca58966SDaniel Starke 	/* Requires care */
25694ca58966SDaniel Starke 	if (dc->priority != dlci->prio)
25704ca58966SDaniel Starke 		need_restart = true;
2571a031c77dSDaniel Starke 	if (dc->flags & GSM_FL_RESTART)
2572a031c77dSDaniel Starke 		need_restart = true;
25734ca58966SDaniel Starke 
25744ca58966SDaniel Starke 	if ((open && gsm->wait_config) || need_restart)
25754ca58966SDaniel Starke 		need_open = true;
25764ca58966SDaniel Starke 	if (dlci->state == DLCI_WAITING_CONFIG) {
25774ca58966SDaniel Starke 		need_restart = false;
25784ca58966SDaniel Starke 		need_open = true;
25794ca58966SDaniel Starke 	}
25804ca58966SDaniel Starke 
25814ca58966SDaniel Starke 	/*
25824ca58966SDaniel Starke 	 * Close down what is needed, restart and initiate the new
25834ca58966SDaniel Starke 	 * configuration.
25844ca58966SDaniel Starke 	 */
25854ca58966SDaniel Starke 	if (need_restart) {
25864ca58966SDaniel Starke 		gsm_dlci_begin_close(dlci);
25874ca58966SDaniel Starke 		wait_event_interruptible(gsm->event, dlci->state == DLCI_CLOSED);
25884ca58966SDaniel Starke 		if (signal_pending(current))
25894ca58966SDaniel Starke 			return -EINTR;
25904ca58966SDaniel Starke 	}
25914ca58966SDaniel Starke 	/*
25924ca58966SDaniel Starke 	 * Setup the new configuration values
25934ca58966SDaniel Starke 	 */
25944ca58966SDaniel Starke 	dlci->adaption = (int)dc->adaption;
25954ca58966SDaniel Starke 
25964ca58966SDaniel Starke 	if (dc->mtu)
25974ca58966SDaniel Starke 		dlci->mtu = (unsigned int)dc->mtu;
25984ca58966SDaniel Starke 	else
25994ca58966SDaniel Starke 		dlci->mtu = gsm->mtu;
26004ca58966SDaniel Starke 
26014ca58966SDaniel Starke 	if (dc->priority)
26024ca58966SDaniel Starke 		dlci->prio = (u8)dc->priority;
26034ca58966SDaniel Starke 	else
26044ca58966SDaniel Starke 		dlci->prio = roundup(dlci->addr + 1, 8) - 1;
26054ca58966SDaniel Starke 
26064ca58966SDaniel Starke 	if (dc->i == 1)
26074ca58966SDaniel Starke 		dlci->ftype = UIH;
26084ca58966SDaniel Starke 	else if (dc->i == 2)
26094ca58966SDaniel Starke 		dlci->ftype = UI;
26104ca58966SDaniel Starke 
26114ca58966SDaniel Starke 	if (dc->k)
26124ca58966SDaniel Starke 		dlci->k = (u8)dc->k;
26134ca58966SDaniel Starke 	else
26144ca58966SDaniel Starke 		dlci->k = gsm->k;
26154ca58966SDaniel Starke 
26164ca58966SDaniel Starke 	if (need_open) {
26174ca58966SDaniel Starke 		if (gsm->initiator)
26184ca58966SDaniel Starke 			gsm_dlci_begin_open(dlci);
26194ca58966SDaniel Starke 		else
26204ca58966SDaniel Starke 			gsm_dlci_set_opening(dlci);
26214ca58966SDaniel Starke 	}
26224ca58966SDaniel Starke 
26234ca58966SDaniel Starke 	return 0;
26244ca58966SDaniel Starke }
26254ca58966SDaniel Starke 
262696fd7ce5SGreg Kroah-Hartman /*
262796fd7ce5SGreg Kroah-Hartman  *	Allocate/Free DLCI channels
262896fd7ce5SGreg Kroah-Hartman  */
262996fd7ce5SGreg Kroah-Hartman 
263096fd7ce5SGreg Kroah-Hartman /**
263196fd7ce5SGreg Kroah-Hartman  *	gsm_dlci_alloc		-	allocate a DLCI
263296fd7ce5SGreg Kroah-Hartman  *	@gsm: GSM mux
263396fd7ce5SGreg Kroah-Hartman  *	@addr: address of the DLCI
263496fd7ce5SGreg Kroah-Hartman  *
263596fd7ce5SGreg Kroah-Hartman  *	Allocate and install a new DLCI object into the GSM mux.
263696fd7ce5SGreg Kroah-Hartman  *
263796fd7ce5SGreg Kroah-Hartman  *	FIXME: review locking races
263896fd7ce5SGreg Kroah-Hartman  */
263996fd7ce5SGreg Kroah-Hartman 
gsm_dlci_alloc(struct gsm_mux * gsm,int addr)264096fd7ce5SGreg Kroah-Hartman static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
264196fd7ce5SGreg Kroah-Hartman {
264296fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC);
264396fd7ce5SGreg Kroah-Hartman 	if (dlci == NULL)
264496fd7ce5SGreg Kroah-Hartman 		return NULL;
264596fd7ce5SGreg Kroah-Hartman 	spin_lock_init(&dlci->lock);
2646bcd5abe2SRuss Gorby 	mutex_init(&dlci->mutex);
26479361ebfbSDaniel Starke 	if (kfifo_alloc(&dlci->fifo, TX_SIZE, GFP_KERNEL) < 0) {
264896fd7ce5SGreg Kroah-Hartman 		kfree(dlci);
264996fd7ce5SGreg Kroah-Hartman 		return NULL;
265096fd7ce5SGreg Kroah-Hartman 	}
265196fd7ce5SGreg Kroah-Hartman 
265296fd7ce5SGreg Kroah-Hartman 	skb_queue_head_init(&dlci->skb_list);
2653e99e88a9SKees Cook 	timer_setup(&dlci->t1, gsm_dlci_t1, 0);
265496fd7ce5SGreg Kroah-Hartman 	tty_port_init(&dlci->port);
265596fd7ce5SGreg Kroah-Hartman 	dlci->port.ops = &gsm_port_ops;
265696fd7ce5SGreg Kroah-Hartman 	dlci->gsm = gsm;
265796fd7ce5SGreg Kroah-Hartman 	dlci->addr = addr;
265896fd7ce5SGreg Kroah-Hartman 	dlci->adaption = gsm->adaption;
26592ec7a802SDaniel Starke 	dlci->mtu = gsm->mtu;
26602ec7a802SDaniel Starke 	if (addr == 0)
26612ec7a802SDaniel Starke 		dlci->prio = 0;
26622ec7a802SDaniel Starke 	else
26632ec7a802SDaniel Starke 		dlci->prio = roundup(addr + 1, 8) - 1;
26642ec7a802SDaniel Starke 	dlci->ftype = gsm->ftype;
26652ec7a802SDaniel Starke 	dlci->k = gsm->k;
266696fd7ce5SGreg Kroah-Hartman 	dlci->state = DLCI_CLOSED;
2667ac77f007SDaniel Starke 	if (addr) {
266896fd7ce5SGreg Kroah-Hartman 		dlci->data = gsm_dlci_data;
2669ac77f007SDaniel Starke 		/* Prevent us from sending data before the link is up */
2670ac77f007SDaniel Starke 		dlci->constipated = true;
2671ac77f007SDaniel Starke 	} else {
267296fd7ce5SGreg Kroah-Hartman 		dlci->data = gsm_dlci_command;
2673ac77f007SDaniel Starke 	}
267496fd7ce5SGreg Kroah-Hartman 	gsm->dlci[addr] = dlci;
267596fd7ce5SGreg Kroah-Hartman 	return dlci;
267696fd7ce5SGreg Kroah-Hartman }
267796fd7ce5SGreg Kroah-Hartman 
267896fd7ce5SGreg Kroah-Hartman /**
26796ab8fba7SRuss Gorby  *	gsm_dlci_free		-	free DLCI
2680724ac070SJiri Slaby  *	@port: tty port for DLCI to free
268196fd7ce5SGreg Kroah-Hartman  *
26826ab8fba7SRuss Gorby  *	Free up a DLCI.
268396fd7ce5SGreg Kroah-Hartman  *
268496fd7ce5SGreg Kroah-Hartman  *	Can sleep.
268596fd7ce5SGreg Kroah-Hartman  */
gsm_dlci_free(struct tty_port * port)26869a8e62bcSJiri Slaby static void gsm_dlci_free(struct tty_port *port)
26876ab8fba7SRuss Gorby {
26889a8e62bcSJiri Slaby 	struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
26896ab8fba7SRuss Gorby 
2690292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&dlci->t1);
26916ab8fba7SRuss Gorby 	dlci->gsm->dlci[dlci->addr] = NULL;
2692036bca1fSJiri Slaby 	kfifo_free(&dlci->fifo);
26936ab8fba7SRuss Gorby 	while ((dlci->skb = skb_dequeue(&dlci->skb_list)))
2694329e5678SRuss Gorby 		dev_kfree_skb(dlci->skb);
26956ab8fba7SRuss Gorby 	kfree(dlci);
26966ab8fba7SRuss Gorby }
26976ab8fba7SRuss Gorby 
dlci_get(struct gsm_dlci * dlci)26986ab8fba7SRuss Gorby static inline void dlci_get(struct gsm_dlci *dlci)
26996ab8fba7SRuss Gorby {
27009a8e62bcSJiri Slaby 	tty_port_get(&dlci->port);
27016ab8fba7SRuss Gorby }
27026ab8fba7SRuss Gorby 
dlci_put(struct gsm_dlci * dlci)27036ab8fba7SRuss Gorby static inline void dlci_put(struct gsm_dlci *dlci)
27046ab8fba7SRuss Gorby {
27059a8e62bcSJiri Slaby 	tty_port_put(&dlci->port);
27066ab8fba7SRuss Gorby }
27076ab8fba7SRuss Gorby 
27084d9b1090SDirkjan Bussink static void gsm_destroy_network(struct gsm_dlci *dlci);
27094d9b1090SDirkjan Bussink 
27106ab8fba7SRuss Gorby /**
27116ab8fba7SRuss Gorby  *	gsm_dlci_release		-	release DLCI
27126ab8fba7SRuss Gorby  *	@dlci: DLCI to destroy
27136ab8fba7SRuss Gorby  *
27146ab8fba7SRuss Gorby  *	Release a DLCI. Actual free is deferred until either
27156ab8fba7SRuss Gorby  *	mux is closed or tty is closed - whichever is last.
27166ab8fba7SRuss Gorby  *
27176ab8fba7SRuss Gorby  *	Can sleep.
27186ab8fba7SRuss Gorby  */
gsm_dlci_release(struct gsm_dlci * dlci)27196ab8fba7SRuss Gorby static void gsm_dlci_release(struct gsm_dlci *dlci)
272096fd7ce5SGreg Kroah-Hartman {
272196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = tty_port_tty_get(&dlci->port);
272296fd7ce5SGreg Kroah-Hartman 	if (tty) {
27234d9b1090SDirkjan Bussink 		mutex_lock(&dlci->mutex);
27244d9b1090SDirkjan Bussink 		gsm_destroy_network(dlci);
27254d9b1090SDirkjan Bussink 		mutex_unlock(&dlci->mutex);
27264d9b1090SDirkjan Bussink 
272796b169f0Sdaniel.starke@siemens.com 		/* We cannot use tty_hangup() because in tty_kref_put() the tty
272896b169f0Sdaniel.starke@siemens.com 		 * driver assumes that the hangup queue is free and reuses it to
272996b169f0Sdaniel.starke@siemens.com 		 * queue release_one_tty() -> NULL pointer panic in
273096b169f0Sdaniel.starke@siemens.com 		 * process_one_work().
273196b169f0Sdaniel.starke@siemens.com 		 */
273296b169f0Sdaniel.starke@siemens.com 		tty_vhangup(tty);
2733be706572SChuansheng Liu 
27344d9b1090SDirkjan Bussink 		tty_port_tty_set(&dlci->port, NULL);
273596fd7ce5SGreg Kroah-Hartman 		tty_kref_put(tty);
273696fd7ce5SGreg Kroah-Hartman 	}
27374d9b1090SDirkjan Bussink 	dlci->state = DLCI_CLOSED;
27386ab8fba7SRuss Gorby 	dlci_put(dlci);
273996fd7ce5SGreg Kroah-Hartman }
274096fd7ce5SGreg Kroah-Hartman 
274196fd7ce5SGreg Kroah-Hartman /*
274296fd7ce5SGreg Kroah-Hartman  *	LAPBish link layer logic
274396fd7ce5SGreg Kroah-Hartman  */
274496fd7ce5SGreg Kroah-Hartman 
274596fd7ce5SGreg Kroah-Hartman /**
274696fd7ce5SGreg Kroah-Hartman  *	gsm_queue		-	a GSM frame is ready to process
274796fd7ce5SGreg Kroah-Hartman  *	@gsm: pointer to our gsm mux
274896fd7ce5SGreg Kroah-Hartman  *
274996fd7ce5SGreg Kroah-Hartman  *	At this point in time a frame has arrived and been demangled from
275096fd7ce5SGreg Kroah-Hartman  *	the line encoding. All the differences between the encodings have
275196fd7ce5SGreg Kroah-Hartman  *	been handled below us and the frame is unpacked into the structures.
275296fd7ce5SGreg Kroah-Hartman  *	The fcs holds the header FCS but any data FCS must be added here.
275396fd7ce5SGreg Kroah-Hartman  */
275496fd7ce5SGreg Kroah-Hartman 
gsm_queue(struct gsm_mux * gsm)275596fd7ce5SGreg Kroah-Hartman static void gsm_queue(struct gsm_mux *gsm)
275696fd7ce5SGreg Kroah-Hartman {
275796fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci;
275896fd7ce5SGreg Kroah-Hartman 	u8 cr;
275996fd7ce5SGreg Kroah-Hartman 	int address;
276096fd7ce5SGreg Kroah-Hartman 
276196fd7ce5SGreg Kroah-Hartman 	if (gsm->fcs != GOOD_FCS) {
276296fd7ce5SGreg Kroah-Hartman 		gsm->bad_fcs++;
2763c07da737SDaniel Starke 		if (debug & DBG_DATA)
27645f9a31d6SAlan Cox 			pr_debug("BAD FCS %02x\n", gsm->fcs);
276596fd7ce5SGreg Kroah-Hartman 		return;
276696fd7ce5SGreg Kroah-Hartman 	}
276796fd7ce5SGreg Kroah-Hartman 	address = gsm->address >> 1;
276896fd7ce5SGreg Kroah-Hartman 	if (address >= NUM_DLCI)
276996fd7ce5SGreg Kroah-Hartman 		goto invalid;
277096fd7ce5SGreg Kroah-Hartman 
277196fd7ce5SGreg Kroah-Hartman 	cr = gsm->address & 1;		/* C/R bit */
277257435c42Sdaniel.starke@siemens.com 	cr ^= gsm->initiator ? 0 : 1;	/* Flip so 1 always means command */
277396fd7ce5SGreg Kroah-Hartman 
277496fd7ce5SGreg Kroah-Hartman 	gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len);
277596fd7ce5SGreg Kroah-Hartman 
277696fd7ce5SGreg Kroah-Hartman 	dlci = gsm->dlci[address];
277796fd7ce5SGreg Kroah-Hartman 
277896fd7ce5SGreg Kroah-Hartman 	switch (gsm->control) {
277996fd7ce5SGreg Kroah-Hartman 	case SABM|PF:
2780a1ce6da0SDaniel Starke 		if (cr == 1) {
2781a1ce6da0SDaniel Starke 			gsm->open_error++;
278296fd7ce5SGreg Kroah-Hartman 			goto invalid;
2783a1ce6da0SDaniel Starke 		}
278496fd7ce5SGreg Kroah-Hartman 		if (dlci == NULL)
278596fd7ce5SGreg Kroah-Hartman 			dlci = gsm_dlci_alloc(gsm, address);
2786a1ce6da0SDaniel Starke 		if (dlci == NULL) {
2787a1ce6da0SDaniel Starke 			gsm->open_error++;
278896fd7ce5SGreg Kroah-Hartman 			return;
2789a1ce6da0SDaniel Starke 		}
279096fd7ce5SGreg Kroah-Hartman 		if (dlci->dead)
2791cc0f4212SZhenguo Zhao 			gsm_response(gsm, address, DM|PF);
279296fd7ce5SGreg Kroah-Hartman 		else {
2793cc0f4212SZhenguo Zhao 			gsm_response(gsm, address, UA|PF);
279496fd7ce5SGreg Kroah-Hartman 			gsm_dlci_open(dlci);
279596fd7ce5SGreg Kroah-Hartman 		}
279696fd7ce5SGreg Kroah-Hartman 		break;
279796fd7ce5SGreg Kroah-Hartman 	case DISC|PF:
2798cd936621SZhenguo Zhao 		if (cr == 1)
279996fd7ce5SGreg Kroah-Hartman 			goto invalid;
280096fd7ce5SGreg Kroah-Hartman 		if (dlci == NULL || dlci->state == DLCI_CLOSED) {
2801cc0f4212SZhenguo Zhao 			gsm_response(gsm, address, DM|PF);
280296fd7ce5SGreg Kroah-Hartman 			return;
280396fd7ce5SGreg Kroah-Hartman 		}
280496fd7ce5SGreg Kroah-Hartman 		/* Real close complete */
2805cc0f4212SZhenguo Zhao 		gsm_response(gsm, address, UA|PF);
280696fd7ce5SGreg Kroah-Hartman 		gsm_dlci_close(dlci);
28070b91b533SZhenguo Zhao 		break;
280896fd7ce5SGreg Kroah-Hartman 	case UA|PF:
280996fd7ce5SGreg Kroah-Hartman 		if (cr == 0 || dlci == NULL)
281096fd7ce5SGreg Kroah-Hartman 			break;
281196fd7ce5SGreg Kroah-Hartman 		switch (dlci->state) {
281296fd7ce5SGreg Kroah-Hartman 		case DLCI_CLOSING:
281396fd7ce5SGreg Kroah-Hartman 			gsm_dlci_close(dlci);
281496fd7ce5SGreg Kroah-Hartman 			break;
281596fd7ce5SGreg Kroah-Hartman 		case DLCI_OPENING:
281696fd7ce5SGreg Kroah-Hartman 			gsm_dlci_open(dlci);
281796fd7ce5SGreg Kroah-Hartman 			break;
281872ae8cc1SJiri Slaby 		default:
281972ae8cc1SJiri Slaby 			pr_debug("%s: unhandled state: %d\n", __func__,
282072ae8cc1SJiri Slaby 					dlci->state);
282172ae8cc1SJiri Slaby 			break;
282296fd7ce5SGreg Kroah-Hartman 		}
282396fd7ce5SGreg Kroah-Hartman 		break;
282496fd7ce5SGreg Kroah-Hartman 	case DM:	/* DM can be valid unsolicited */
282596fd7ce5SGreg Kroah-Hartman 	case DM|PF:
282696fd7ce5SGreg Kroah-Hartman 		if (cr)
282796fd7ce5SGreg Kroah-Hartman 			goto invalid;
282896fd7ce5SGreg Kroah-Hartman 		if (dlci == NULL)
282996fd7ce5SGreg Kroah-Hartman 			return;
283096fd7ce5SGreg Kroah-Hartman 		gsm_dlci_close(dlci);
283196fd7ce5SGreg Kroah-Hartman 		break;
283296fd7ce5SGreg Kroah-Hartman 	case UI:
283396fd7ce5SGreg Kroah-Hartman 	case UI|PF:
283496fd7ce5SGreg Kroah-Hartman 	case UIH:
283596fd7ce5SGreg Kroah-Hartman 	case UIH|PF:
283696fd7ce5SGreg Kroah-Hartman 		if (dlci == NULL || dlci->state != DLCI_OPEN) {
283718a948c7SDaniel Starke 			gsm_response(gsm, address, DM|PF);
283896fd7ce5SGreg Kroah-Hartman 			return;
283996fd7ce5SGreg Kroah-Hartman 		}
284096fd7ce5SGreg Kroah-Hartman 		dlci->data(dlci, gsm->buf, gsm->len);
284196fd7ce5SGreg Kroah-Hartman 		break;
284296fd7ce5SGreg Kroah-Hartman 	default:
284396fd7ce5SGreg Kroah-Hartman 		goto invalid;
284496fd7ce5SGreg Kroah-Hartman 	}
284596fd7ce5SGreg Kroah-Hartman 	return;
284696fd7ce5SGreg Kroah-Hartman invalid:
284796fd7ce5SGreg Kroah-Hartman 	gsm->malformed++;
284896fd7ce5SGreg Kroah-Hartman 	return;
284996fd7ce5SGreg Kroah-Hartman }
285096fd7ce5SGreg Kroah-Hartman 
285162c3763dSDaniel Starke /**
285262c3763dSDaniel Starke  * gsm0_receive_state_check_and_fix	-	check and correct receive state
285362c3763dSDaniel Starke  * @gsm: gsm data for this ldisc instance
285462c3763dSDaniel Starke  *
285562c3763dSDaniel Starke  * Ensures that the current receive state is valid for basic option mode.
285662c3763dSDaniel Starke  */
285762c3763dSDaniel Starke 
gsm0_receive_state_check_and_fix(struct gsm_mux * gsm)285862c3763dSDaniel Starke static void gsm0_receive_state_check_and_fix(struct gsm_mux *gsm)
285962c3763dSDaniel Starke {
286062c3763dSDaniel Starke 	switch (gsm->state) {
286162c3763dSDaniel Starke 	case GSM_SEARCH:
286262c3763dSDaniel Starke 	case GSM0_ADDRESS:
286362c3763dSDaniel Starke 	case GSM0_CONTROL:
286462c3763dSDaniel Starke 	case GSM0_LEN0:
286562c3763dSDaniel Starke 	case GSM0_LEN1:
286662c3763dSDaniel Starke 	case GSM0_DATA:
286762c3763dSDaniel Starke 	case GSM0_FCS:
286862c3763dSDaniel Starke 	case GSM0_SSOF:
286962c3763dSDaniel Starke 		break;
287062c3763dSDaniel Starke 	default:
287162c3763dSDaniel Starke 		gsm->state = GSM_SEARCH;
287262c3763dSDaniel Starke 		break;
287362c3763dSDaniel Starke 	}
287462c3763dSDaniel Starke }
287596fd7ce5SGreg Kroah-Hartman 
287696fd7ce5SGreg Kroah-Hartman /**
287796fd7ce5SGreg Kroah-Hartman  *	gsm0_receive	-	perform processing for non-transparency
287896fd7ce5SGreg Kroah-Hartman  *	@gsm: gsm data for this ldisc instance
287996fd7ce5SGreg Kroah-Hartman  *	@c: character
288096fd7ce5SGreg Kroah-Hartman  *
288196fd7ce5SGreg Kroah-Hartman  *	Receive bytes in gsm mode 0
288296fd7ce5SGreg Kroah-Hartman  */
288396fd7ce5SGreg Kroah-Hartman 
gsm0_receive(struct gsm_mux * gsm,unsigned char c)288496fd7ce5SGreg Kroah-Hartman static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
288596fd7ce5SGreg Kroah-Hartman {
2886c2f2f000SAlan Cox 	unsigned int len;
2887c2f2f000SAlan Cox 
288862c3763dSDaniel Starke 	gsm0_receive_state_check_and_fix(gsm);
288996fd7ce5SGreg Kroah-Hartman 	switch (gsm->state) {
289096fd7ce5SGreg Kroah-Hartman 	case GSM_SEARCH:	/* SOF marker */
289196fd7ce5SGreg Kroah-Hartman 		if (c == GSM0_SOF) {
289262c3763dSDaniel Starke 			gsm->state = GSM0_ADDRESS;
289396fd7ce5SGreg Kroah-Hartman 			gsm->address = 0;
289496fd7ce5SGreg Kroah-Hartman 			gsm->len = 0;
289596fd7ce5SGreg Kroah-Hartman 			gsm->fcs = INIT_FCS;
289696fd7ce5SGreg Kroah-Hartman 		}
2897c2f2f000SAlan Cox 		break;
289862c3763dSDaniel Starke 	case GSM0_ADDRESS:	/* Address EA */
289996fd7ce5SGreg Kroah-Hartman 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
290096fd7ce5SGreg Kroah-Hartman 		if (gsm_read_ea(&gsm->address, c))
290162c3763dSDaniel Starke 			gsm->state = GSM0_CONTROL;
290296fd7ce5SGreg Kroah-Hartman 		break;
290362c3763dSDaniel Starke 	case GSM0_CONTROL:	/* Control Byte */
290496fd7ce5SGreg Kroah-Hartman 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
290596fd7ce5SGreg Kroah-Hartman 		gsm->control = c;
290662c3763dSDaniel Starke 		gsm->state = GSM0_LEN0;
290796fd7ce5SGreg Kroah-Hartman 		break;
290862c3763dSDaniel Starke 	case GSM0_LEN0:		/* Length EA */
290996fd7ce5SGreg Kroah-Hartman 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
291096fd7ce5SGreg Kroah-Hartman 		if (gsm_read_ea(&gsm->len, c)) {
291196fd7ce5SGreg Kroah-Hartman 			if (gsm->len > gsm->mru) {
291296fd7ce5SGreg Kroah-Hartman 				gsm->bad_size++;
291396fd7ce5SGreg Kroah-Hartman 				gsm->state = GSM_SEARCH;
291496fd7ce5SGreg Kroah-Hartman 				break;
291596fd7ce5SGreg Kroah-Hartman 			}
291696fd7ce5SGreg Kroah-Hartman 			gsm->count = 0;
2917c2f2f000SAlan Cox 			if (!gsm->len)
291862c3763dSDaniel Starke 				gsm->state = GSM0_FCS;
2919c2f2f000SAlan Cox 			else
292062c3763dSDaniel Starke 				gsm->state = GSM0_DATA;
2921c2f2f000SAlan Cox 			break;
292296fd7ce5SGreg Kroah-Hartman 		}
292362c3763dSDaniel Starke 		gsm->state = GSM0_LEN1;
2924c2f2f000SAlan Cox 		break;
292562c3763dSDaniel Starke 	case GSM0_LEN1:
2926c2f2f000SAlan Cox 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
2927c2f2f000SAlan Cox 		len = c;
2928c2f2f000SAlan Cox 		gsm->len |= len << 7;
2929c2f2f000SAlan Cox 		if (gsm->len > gsm->mru) {
2930c2f2f000SAlan Cox 			gsm->bad_size++;
2931c2f2f000SAlan Cox 			gsm->state = GSM_SEARCH;
2932c2f2f000SAlan Cox 			break;
2933c2f2f000SAlan Cox 		}
2934c2f2f000SAlan Cox 		gsm->count = 0;
2935c2f2f000SAlan Cox 		if (!gsm->len)
293662c3763dSDaniel Starke 			gsm->state = GSM0_FCS;
2937c2f2f000SAlan Cox 		else
293862c3763dSDaniel Starke 			gsm->state = GSM0_DATA;
293996fd7ce5SGreg Kroah-Hartman 		break;
294062c3763dSDaniel Starke 	case GSM0_DATA:		/* Data */
294196fd7ce5SGreg Kroah-Hartman 		gsm->buf[gsm->count++] = c;
2942774d83b0SDaniel Starke 		if (gsm->count >= MAX_MRU) {
2943774d83b0SDaniel Starke 			gsm->bad_size++;
2944774d83b0SDaniel Starke 			gsm->state = GSM_SEARCH;
2945774d83b0SDaniel Starke 		} else if (gsm->count >= gsm->len) {
29467a0e4b17SDaniel Starke 			/* Calculate final FCS for UI frames over all data */
29477a0e4b17SDaniel Starke 			if ((gsm->control & ~PF) != UIH) {
29487a0e4b17SDaniel Starke 				gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
29497a0e4b17SDaniel Starke 							     gsm->count);
29507a0e4b17SDaniel Starke 			}
295162c3763dSDaniel Starke 			gsm->state = GSM0_FCS;
29527a0e4b17SDaniel Starke 		}
295396fd7ce5SGreg Kroah-Hartman 		break;
295462c3763dSDaniel Starke 	case GSM0_FCS:		/* FCS follows the packet */
29557a0e4b17SDaniel Starke 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
295662c3763dSDaniel Starke 		gsm->state = GSM0_SSOF;
2957c2f2f000SAlan Cox 		break;
295862c3763dSDaniel Starke 	case GSM0_SSOF:
2959c2f2f000SAlan Cox 		gsm->state = GSM_SEARCH;
29607a0e4b17SDaniel Starke 		if (c == GSM0_SOF)
29617a0e4b17SDaniel Starke 			gsm_queue(gsm);
29627a0e4b17SDaniel Starke 		else
29637a0e4b17SDaniel Starke 			gsm->bad_size++;
2964c2f2f000SAlan Cox 		break;
2965329aa6e6SJiri Slaby 	default:
2966329aa6e6SJiri Slaby 		pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
2967329aa6e6SJiri Slaby 		break;
2968c2f2f000SAlan Cox 	}
296996fd7ce5SGreg Kroah-Hartman }
297096fd7ce5SGreg Kroah-Hartman 
297196fd7ce5SGreg Kroah-Hartman /**
297262c3763dSDaniel Starke  * gsm1_receive_state_check_and_fix	-	check and correct receive state
297362c3763dSDaniel Starke  * @gsm: gsm data for this ldisc instance
297462c3763dSDaniel Starke  *
297562c3763dSDaniel Starke  * Ensures that the current receive state is valid for advanced option mode.
297662c3763dSDaniel Starke  */
297762c3763dSDaniel Starke 
gsm1_receive_state_check_and_fix(struct gsm_mux * gsm)297862c3763dSDaniel Starke static void gsm1_receive_state_check_and_fix(struct gsm_mux *gsm)
297962c3763dSDaniel Starke {
298062c3763dSDaniel Starke 	switch (gsm->state) {
298162c3763dSDaniel Starke 	case GSM_SEARCH:
298262c3763dSDaniel Starke 	case GSM1_START:
298362c3763dSDaniel Starke 	case GSM1_ADDRESS:
298462c3763dSDaniel Starke 	case GSM1_CONTROL:
298562c3763dSDaniel Starke 	case GSM1_DATA:
298662c3763dSDaniel Starke 	case GSM1_OVERRUN:
298762c3763dSDaniel Starke 		break;
298862c3763dSDaniel Starke 	default:
298962c3763dSDaniel Starke 		gsm->state = GSM_SEARCH;
299062c3763dSDaniel Starke 		break;
299162c3763dSDaniel Starke 	}
299262c3763dSDaniel Starke }
299362c3763dSDaniel Starke 
299462c3763dSDaniel Starke /**
2995c2f2f000SAlan Cox  *	gsm1_receive	-	perform processing for non-transparency
299696fd7ce5SGreg Kroah-Hartman  *	@gsm: gsm data for this ldisc instance
299796fd7ce5SGreg Kroah-Hartman  *	@c: character
299896fd7ce5SGreg Kroah-Hartman  *
299996fd7ce5SGreg Kroah-Hartman  *	Receive bytes in mode 1 (Advanced option)
300096fd7ce5SGreg Kroah-Hartman  */
300196fd7ce5SGreg Kroah-Hartman 
gsm1_receive(struct gsm_mux * gsm,unsigned char c)300296fd7ce5SGreg Kroah-Hartman static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
300396fd7ce5SGreg Kroah-Hartman {
300462c3763dSDaniel Starke 	gsm1_receive_state_check_and_fix(gsm);
3005f4f7d632SDaniel Starke 	/* handle XON/XOFF */
3006f4f7d632SDaniel Starke 	if ((c & ISO_IEC_646_MASK) == XON) {
3007f4f7d632SDaniel Starke 		gsm->constipated = true;
3008f4f7d632SDaniel Starke 		return;
3009f4f7d632SDaniel Starke 	} else if ((c & ISO_IEC_646_MASK) == XOFF) {
3010f4f7d632SDaniel Starke 		gsm->constipated = false;
3011f4f7d632SDaniel Starke 		/* Kick the link in case it is idling */
30120af02167SDaniel Starke 		gsmld_write_trigger(gsm);
3013f4f7d632SDaniel Starke 		return;
3014f4f7d632SDaniel Starke 	}
301596fd7ce5SGreg Kroah-Hartman 	if (c == GSM1_SOF) {
30167a0e4b17SDaniel Starke 		/* EOF is only valid in frame if we have got to the data state */
301762c3763dSDaniel Starke 		if (gsm->state == GSM1_DATA) {
30187a0e4b17SDaniel Starke 			if (gsm->count < 1) {
30197a0e4b17SDaniel Starke 				/* Missing FSC */
30207a0e4b17SDaniel Starke 				gsm->malformed++;
302162c3763dSDaniel Starke 				gsm->state = GSM1_START;
30227a0e4b17SDaniel Starke 				return;
30237a0e4b17SDaniel Starke 			}
30247a0e4b17SDaniel Starke 			/* Remove the FCS from data */
302596fd7ce5SGreg Kroah-Hartman 			gsm->count--;
30267a0e4b17SDaniel Starke 			if ((gsm->control & ~PF) != UIH) {
30277a0e4b17SDaniel Starke 				/* Calculate final FCS for UI frames over all
30287a0e4b17SDaniel Starke 				 * data but FCS
30297a0e4b17SDaniel Starke 				 */
30307a0e4b17SDaniel Starke 				gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
30317a0e4b17SDaniel Starke 							     gsm->count);
30327a0e4b17SDaniel Starke 			}
30337a0e4b17SDaniel Starke 			/* Add the FCS itself to test against GOOD_FCS */
303496fd7ce5SGreg Kroah-Hartman 			gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
303596fd7ce5SGreg Kroah-Hartman 			gsm->len = gsm->count;
303696fd7ce5SGreg Kroah-Hartman 			gsm_queue(gsm);
303762c3763dSDaniel Starke 			gsm->state  = GSM1_START;
303896fd7ce5SGreg Kroah-Hartman 			return;
303996fd7ce5SGreg Kroah-Hartman 		}
304096fd7ce5SGreg Kroah-Hartman 		/* Any partial frame was a runt so go back to start */
304162c3763dSDaniel Starke 		if (gsm->state != GSM1_START) {
3042a24b4b2fSDaniel Starke 			if (gsm->state != GSM_SEARCH)
304396fd7ce5SGreg Kroah-Hartman 				gsm->malformed++;
304462c3763dSDaniel Starke 			gsm->state = GSM1_START;
304596fd7ce5SGreg Kroah-Hartman 		}
304696fd7ce5SGreg Kroah-Hartman 		/* A SOF in GSM_START means we are still reading idling or
304796fd7ce5SGreg Kroah-Hartman 		   framing bytes */
304896fd7ce5SGreg Kroah-Hartman 		return;
304996fd7ce5SGreg Kroah-Hartman 	}
305096fd7ce5SGreg Kroah-Hartman 
305196fd7ce5SGreg Kroah-Hartman 	if (c == GSM1_ESCAPE) {
3052c50704bdSJiri Slaby 		gsm->escape = true;
305396fd7ce5SGreg Kroah-Hartman 		return;
305496fd7ce5SGreg Kroah-Hartman 	}
305596fd7ce5SGreg Kroah-Hartman 
305696fd7ce5SGreg Kroah-Hartman 	/* Only an unescaped SOF gets us out of GSM search */
305796fd7ce5SGreg Kroah-Hartman 	if (gsm->state == GSM_SEARCH)
305896fd7ce5SGreg Kroah-Hartman 		return;
305996fd7ce5SGreg Kroah-Hartman 
306096fd7ce5SGreg Kroah-Hartman 	if (gsm->escape) {
306196fd7ce5SGreg Kroah-Hartman 		c ^= GSM1_ESCAPE_BITS;
3062c50704bdSJiri Slaby 		gsm->escape = false;
306396fd7ce5SGreg Kroah-Hartman 	}
306496fd7ce5SGreg Kroah-Hartman 	switch (gsm->state) {
306562c3763dSDaniel Starke 	case GSM1_START:		/* First byte after SOF */
306696fd7ce5SGreg Kroah-Hartman 		gsm->address = 0;
306762c3763dSDaniel Starke 		gsm->state = GSM1_ADDRESS;
306896fd7ce5SGreg Kroah-Hartman 		gsm->fcs = INIT_FCS;
3069df561f66SGustavo A. R. Silva 		fallthrough;
307062c3763dSDaniel Starke 	case GSM1_ADDRESS:	/* Address continuation */
307196fd7ce5SGreg Kroah-Hartman 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
307296fd7ce5SGreg Kroah-Hartman 		if (gsm_read_ea(&gsm->address, c))
307362c3763dSDaniel Starke 			gsm->state = GSM1_CONTROL;
307496fd7ce5SGreg Kroah-Hartman 		break;
307562c3763dSDaniel Starke 	case GSM1_CONTROL:	/* Control Byte */
307696fd7ce5SGreg Kroah-Hartman 		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
307796fd7ce5SGreg Kroah-Hartman 		gsm->control = c;
307896fd7ce5SGreg Kroah-Hartman 		gsm->count = 0;
307962c3763dSDaniel Starke 		gsm->state = GSM1_DATA;
308096fd7ce5SGreg Kroah-Hartman 		break;
308162c3763dSDaniel Starke 	case GSM1_DATA:		/* Data */
3082774d83b0SDaniel Starke 		if (gsm->count > gsm->mru || gsm->count > MAX_MRU) {	/* Allow one for the FCS */
308362c3763dSDaniel Starke 			gsm->state = GSM1_OVERRUN;
308496fd7ce5SGreg Kroah-Hartman 			gsm->bad_size++;
308596fd7ce5SGreg Kroah-Hartman 		} else
308696fd7ce5SGreg Kroah-Hartman 			gsm->buf[gsm->count++] = c;
308796fd7ce5SGreg Kroah-Hartman 		break;
308862c3763dSDaniel Starke 	case GSM1_OVERRUN:	/* Over-long - eg a dropped SOF */
308996fd7ce5SGreg Kroah-Hartman 		break;
3090329aa6e6SJiri Slaby 	default:
3091329aa6e6SJiri Slaby 		pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
3092329aa6e6SJiri Slaby 		break;
309396fd7ce5SGreg Kroah-Hartman 	}
309496fd7ce5SGreg Kroah-Hartman }
309596fd7ce5SGreg Kroah-Hartman 
309696fd7ce5SGreg Kroah-Hartman /**
309796fd7ce5SGreg Kroah-Hartman  *	gsm_error		-	handle tty error
309896fd7ce5SGreg Kroah-Hartman  *	@gsm: ldisc data
309996fd7ce5SGreg Kroah-Hartman  *
310096fd7ce5SGreg Kroah-Hartman  *	Handle an error in the receipt of data for a frame. Currently we just
310196fd7ce5SGreg Kroah-Hartman  *	go back to hunting for a SOF.
310296fd7ce5SGreg Kroah-Hartman  *
310396fd7ce5SGreg Kroah-Hartman  *	FIXME: better diagnostics ?
310496fd7ce5SGreg Kroah-Hartman  */
310596fd7ce5SGreg Kroah-Hartman 
gsm_error(struct gsm_mux * gsm)3106ea502201SJiri Slaby static void gsm_error(struct gsm_mux *gsm)
310796fd7ce5SGreg Kroah-Hartman {
310896fd7ce5SGreg Kroah-Hartman 	gsm->state = GSM_SEARCH;
310996fd7ce5SGreg Kroah-Hartman 	gsm->io_error++;
311096fd7ce5SGreg Kroah-Hartman }
311196fd7ce5SGreg Kroah-Hartman 
311296fd7ce5SGreg Kroah-Hartman /**
311396fd7ce5SGreg Kroah-Hartman  *	gsm_cleanup_mux		-	generic GSM protocol cleanup
311496fd7ce5SGreg Kroah-Hartman  *	@gsm: our mux
3115aa371e96SDaniel Starke  *	@disc: disconnect link?
311696fd7ce5SGreg Kroah-Hartman  *
311796fd7ce5SGreg Kroah-Hartman  *	Clean up the bits of the mux which are the same for all framing
311896fd7ce5SGreg Kroah-Hartman  *	protocols. Remove the mux from the mux table, stop all the timers
311996fd7ce5SGreg Kroah-Hartman  *	and then shut down each device hanging up the channels as we go.
312096fd7ce5SGreg Kroah-Hartman  */
312196fd7ce5SGreg Kroah-Hartman 
gsm_cleanup_mux(struct gsm_mux * gsm,bool disc)3122aa371e96SDaniel Starke static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
312396fd7ce5SGreg Kroah-Hartman {
312496fd7ce5SGreg Kroah-Hartman 	int i;
31253c4f8333SYi Yang 	struct gsm_dlci *dlci;
3126329e5678SRuss Gorby 	struct gsm_msg *txq, *ntxq;
312796fd7ce5SGreg Kroah-Hartman 
31285677fcf3SJiri Slaby 	gsm->dead = true;
3129dfabf7ffSChao Bi 	mutex_lock(&gsm->mutex);
3130aa371e96SDaniel Starke 
31313c4f8333SYi Yang 	dlci = gsm->dlci[0];
3132aa371e96SDaniel Starke 	if (dlci) {
3133aa371e96SDaniel Starke 		if (disc && dlci->state != DLCI_CLOSED) {
3134aa371e96SDaniel Starke 			gsm_dlci_begin_close(dlci);
3135aa371e96SDaniel Starke 			wait_event(gsm->event, dlci->state == DLCI_CLOSED);
3136aa371e96SDaniel Starke 		}
3137aa371e96SDaniel Starke 		dlci->dead = true;
3138aa371e96SDaniel Starke 	}
3139aa371e96SDaniel Starke 
3140aa371e96SDaniel Starke 	/* Finish outstanding timers, making sure they are done */
314115743ae5SFedor Pchelkin 	del_timer_sync(&gsm->kick_timer);
3142aa371e96SDaniel Starke 	del_timer_sync(&gsm->t2_timer);
314372206cc7SDaniel Starke 	del_timer_sync(&gsm->ka_timer);
314496fd7ce5SGreg Kroah-Hartman 
31450af02167SDaniel Starke 	/* Finish writing to ldisc */
31460af02167SDaniel Starke 	flush_work(&gsm->tx_work);
31470af02167SDaniel Starke 
3148deefc58bSDaniel Starke 	/* Free up any link layer users and finally the control channel */
314901aecd91SDaniel Starke 	if (gsm->has_devices) {
315001aecd91SDaniel Starke 		gsm_unregister_devices(gsm_tty_driver, gsm->num);
315101aecd91SDaniel Starke 		gsm->has_devices = false;
315201aecd91SDaniel Starke 	}
3153deefc58bSDaniel Starke 	for (i = NUM_DLCI - 1; i >= 0; i--)
315429346e21SDaniel Starke 		if (gsm->dlci[i])
31556ab8fba7SRuss Gorby 			gsm_dlci_release(gsm->dlci[i]);
3156dfabf7ffSChao Bi 	mutex_unlock(&gsm->mutex);
315796fd7ce5SGreg Kroah-Hartman 	/* Now wipe the queues */
315817eac652SDaniel Starke 	tty_ldisc_flush(gsm->tty);
3159*c29f192eSLonglong Xia 
3160*c29f192eSLonglong Xia 	guard(spinlock_irqsave)(&gsm->tx_lock);
31610af02167SDaniel Starke 	list_for_each_entry_safe(txq, ntxq, &gsm->tx_ctrl_list, list)
316296fd7ce5SGreg Kroah-Hartman 		kfree(txq);
31630af02167SDaniel Starke 	INIT_LIST_HEAD(&gsm->tx_ctrl_list);
31640af02167SDaniel Starke 	list_for_each_entry_safe(txq, ntxq, &gsm->tx_data_list, list)
31650af02167SDaniel Starke 		kfree(txq);
31660af02167SDaniel Starke 	INIT_LIST_HEAD(&gsm->tx_data_list);
316796fd7ce5SGreg Kroah-Hartman }
316896fd7ce5SGreg Kroah-Hartman 
316996fd7ce5SGreg Kroah-Hartman /**
317096fd7ce5SGreg Kroah-Hartman  *	gsm_activate_mux	-	generic GSM setup
317196fd7ce5SGreg Kroah-Hartman  *	@gsm: our mux
317296fd7ce5SGreg Kroah-Hartman  *
317396fd7ce5SGreg Kroah-Hartman  *	Set up the bits of the mux which are the same for all framing
317496fd7ce5SGreg Kroah-Hartman  *	protocols. Add the mux to the mux table so it can be opened and
317596fd7ce5SGreg Kroah-Hartman  *	finally kick off connecting to DLCI 0 on the modem.
317696fd7ce5SGreg Kroah-Hartman  */
317796fd7ce5SGreg Kroah-Hartman 
gsm_activate_mux(struct gsm_mux * gsm)317854af5836SRashika Kheria static int gsm_activate_mux(struct gsm_mux *gsm)
317996fd7ce5SGreg Kroah-Hartman {
318096fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci;
318101aecd91SDaniel Starke 	int ret;
318296fd7ce5SGreg Kroah-Hartman 
318373496604SDaniel Starke 	dlci = gsm_dlci_alloc(gsm, 0);
318473496604SDaniel Starke 	if (dlci == NULL)
318573496604SDaniel Starke 		return -ENOMEM;
318673496604SDaniel Starke 
318719fb0a66SDaniel Starke 	if (gsm->encoding == GSM_BASIC_OPT)
318896fd7ce5SGreg Kroah-Hartman 		gsm->receive = gsm0_receive;
318996fd7ce5SGreg Kroah-Hartman 	else
319096fd7ce5SGreg Kroah-Hartman 		gsm->receive = gsm1_receive;
319196fd7ce5SGreg Kroah-Hartman 
319201aecd91SDaniel Starke 	ret = gsm_register_devices(gsm_tty_driver, gsm->num);
319301aecd91SDaniel Starke 	if (ret)
319401aecd91SDaniel Starke 		return ret;
319501aecd91SDaniel Starke 
319601aecd91SDaniel Starke 	gsm->has_devices = true;
31975677fcf3SJiri Slaby 	gsm->dead = false;		/* Tty opens are now permissible */
319896fd7ce5SGreg Kroah-Hartman 	return 0;
319996fd7ce5SGreg Kroah-Hartman }
320096fd7ce5SGreg Kroah-Hartman 
320196fd7ce5SGreg Kroah-Hartman /**
320296fd7ce5SGreg Kroah-Hartman  *	gsm_free_mux		-	free up a mux
3203724ac070SJiri Slaby  *	@gsm: mux to free
320496fd7ce5SGreg Kroah-Hartman  *
32056ab8fba7SRuss Gorby  *	Dispose of allocated resources for a dead mux
320696fd7ce5SGreg Kroah-Hartman  */
gsm_free_mux(struct gsm_mux * gsm)320754af5836SRashika Kheria static void gsm_free_mux(struct gsm_mux *gsm)
320896fd7ce5SGreg Kroah-Hartman {
32091ec92e97SDaniel Starke 	int i;
32101ec92e97SDaniel Starke 
32111ec92e97SDaniel Starke 	for (i = 0; i < MAX_MUX; i++) {
32121ec92e97SDaniel Starke 		if (gsm == gsm_mux[i]) {
32131ec92e97SDaniel Starke 			gsm_mux[i] = NULL;
32141ec92e97SDaniel Starke 			break;
32151ec92e97SDaniel Starke 		}
32161ec92e97SDaniel Starke 	}
32171ec92e97SDaniel Starke 	mutex_destroy(&gsm->mutex);
321896fd7ce5SGreg Kroah-Hartman 	kfree(gsm->txframe);
321996fd7ce5SGreg Kroah-Hartman 	kfree(gsm->buf);
322096fd7ce5SGreg Kroah-Hartman 	kfree(gsm);
322196fd7ce5SGreg Kroah-Hartman }
322296fd7ce5SGreg Kroah-Hartman 
322396fd7ce5SGreg Kroah-Hartman /**
32246ab8fba7SRuss Gorby  *	gsm_free_muxr		-	free up a mux
3225724ac070SJiri Slaby  *	@ref: kreference to the mux to free
32266ab8fba7SRuss Gorby  *
32276ab8fba7SRuss Gorby  *	Dispose of allocated resources for a dead mux
32286ab8fba7SRuss Gorby  */
gsm_free_muxr(struct kref * ref)32296ab8fba7SRuss Gorby static void gsm_free_muxr(struct kref *ref)
32306ab8fba7SRuss Gorby {
32316ab8fba7SRuss Gorby 	struct gsm_mux *gsm = container_of(ref, struct gsm_mux, ref);
32326ab8fba7SRuss Gorby 	gsm_free_mux(gsm);
32336ab8fba7SRuss Gorby }
32346ab8fba7SRuss Gorby 
mux_get(struct gsm_mux * gsm)32356ab8fba7SRuss Gorby static inline void mux_get(struct gsm_mux *gsm)
32366ab8fba7SRuss Gorby {
32371ec92e97SDaniel Starke 	unsigned long flags;
32381ec92e97SDaniel Starke 
32391ec92e97SDaniel Starke 	spin_lock_irqsave(&gsm_mux_lock, flags);
32406ab8fba7SRuss Gorby 	kref_get(&gsm->ref);
32411ec92e97SDaniel Starke 	spin_unlock_irqrestore(&gsm_mux_lock, flags);
32426ab8fba7SRuss Gorby }
32436ab8fba7SRuss Gorby 
mux_put(struct gsm_mux * gsm)32446ab8fba7SRuss Gorby static inline void mux_put(struct gsm_mux *gsm)
32456ab8fba7SRuss Gorby {
32461ec92e97SDaniel Starke 	unsigned long flags;
32471ec92e97SDaniel Starke 
32481ec92e97SDaniel Starke 	spin_lock_irqsave(&gsm_mux_lock, flags);
32496ab8fba7SRuss Gorby 	kref_put(&gsm->ref, gsm_free_muxr);
32501ec92e97SDaniel Starke 	spin_unlock_irqrestore(&gsm_mux_lock, flags);
32516ab8fba7SRuss Gorby }
32526ab8fba7SRuss Gorby 
mux_num_to_base(struct gsm_mux * gsm)325343a9e710SMartin Hundebøll static inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
325443a9e710SMartin Hundebøll {
325543a9e710SMartin Hundebøll 	return gsm->num * NUM_DLCI;
325643a9e710SMartin Hundebøll }
325743a9e710SMartin Hundebøll 
mux_line_to_num(unsigned int line)325843a9e710SMartin Hundebøll static inline unsigned int mux_line_to_num(unsigned int line)
325943a9e710SMartin Hundebøll {
326043a9e710SMartin Hundebøll 	return line / NUM_DLCI;
326143a9e710SMartin Hundebøll }
326243a9e710SMartin Hundebøll 
32636ab8fba7SRuss Gorby /**
326496fd7ce5SGreg Kroah-Hartman  *	gsm_alloc_mux		-	allocate a mux
326596fd7ce5SGreg Kroah-Hartman  *
326696fd7ce5SGreg Kroah-Hartman  *	Creates a new mux ready for activation.
326796fd7ce5SGreg Kroah-Hartman  */
326896fd7ce5SGreg Kroah-Hartman 
gsm_alloc_mux(void)326954af5836SRashika Kheria static struct gsm_mux *gsm_alloc_mux(void)
327096fd7ce5SGreg Kroah-Hartman {
32711ec92e97SDaniel Starke 	int i;
327296fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL);
327396fd7ce5SGreg Kroah-Hartman 	if (gsm == NULL)
327496fd7ce5SGreg Kroah-Hartman 		return NULL;
327596fd7ce5SGreg Kroah-Hartman 	gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL);
327696fd7ce5SGreg Kroah-Hartman 	if (gsm->buf == NULL) {
327796fd7ce5SGreg Kroah-Hartman 		kfree(gsm);
327896fd7ce5SGreg Kroah-Hartman 		return NULL;
327996fd7ce5SGreg Kroah-Hartman 	}
3280535bf600SDaniel Starke 	gsm->txframe = kmalloc(2 * (MAX_MTU + PROT_OVERHEAD - 1), GFP_KERNEL);
328196fd7ce5SGreg Kroah-Hartman 	if (gsm->txframe == NULL) {
328296fd7ce5SGreg Kroah-Hartman 		kfree(gsm->buf);
328396fd7ce5SGreg Kroah-Hartman 		kfree(gsm);
328496fd7ce5SGreg Kroah-Hartman 		return NULL;
328596fd7ce5SGreg Kroah-Hartman 	}
328696fd7ce5SGreg Kroah-Hartman 	spin_lock_init(&gsm->lock);
3287dfabf7ffSChao Bi 	mutex_init(&gsm->mutex);
32886ab8fba7SRuss Gorby 	kref_init(&gsm->ref);
32890af02167SDaniel Starke 	INIT_LIST_HEAD(&gsm->tx_ctrl_list);
32900af02167SDaniel Starke 	INIT_LIST_HEAD(&gsm->tx_data_list);
329115743ae5SFedor Pchelkin 	timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
32924bb1a53bSTetsuo Handa 	timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
329372206cc7SDaniel Starke 	timer_setup(&gsm->ka_timer, gsm_control_keep_alive, 0);
32944bb1a53bSTetsuo Handa 	INIT_WORK(&gsm->tx_work, gsmld_write_task);
32954bb1a53bSTetsuo Handa 	init_waitqueue_head(&gsm->event);
32964bb1a53bSTetsuo Handa 	spin_lock_init(&gsm->control_lock);
3297acdab4cbSFedor Pchelkin 	spin_lock_init(&gsm->tx_lock);
329896fd7ce5SGreg Kroah-Hartman 
329996fd7ce5SGreg Kroah-Hartman 	gsm->t1 = T1;
330096fd7ce5SGreg Kroah-Hartman 	gsm->t2 = T2;
33012ec7a802SDaniel Starke 	gsm->t3 = T3;
330296fd7ce5SGreg Kroah-Hartman 	gsm->n2 = N2;
33032ec7a802SDaniel Starke 	gsm->k = K;
330496fd7ce5SGreg Kroah-Hartman 	gsm->ftype = UIH;
330596fd7ce5SGreg Kroah-Hartman 	gsm->adaption = 1;
330619fb0a66SDaniel Starke 	gsm->encoding = GSM_ADV_OPT;
330796fd7ce5SGreg Kroah-Hartman 	gsm->mru = 64;	/* Default to encoding 1 so these should be 64 */
330896fd7ce5SGreg Kroah-Hartman 	gsm->mtu = 64;
33095677fcf3SJiri Slaby 	gsm->dead = true;	/* Avoid early tty opens */
33104ca58966SDaniel Starke 	gsm->wait_config = false; /* Disabled */
331172206cc7SDaniel Starke 	gsm->keep_alive = 0;	/* Disabled */
331296fd7ce5SGreg Kroah-Hartman 
33131ec92e97SDaniel Starke 	/* Store the instance to the mux array or abort if no space is
33141ec92e97SDaniel Starke 	 * available.
33151ec92e97SDaniel Starke 	 */
33161ec92e97SDaniel Starke 	spin_lock(&gsm_mux_lock);
33171ec92e97SDaniel Starke 	for (i = 0; i < MAX_MUX; i++) {
33181ec92e97SDaniel Starke 		if (!gsm_mux[i]) {
33191ec92e97SDaniel Starke 			gsm_mux[i] = gsm;
33201ec92e97SDaniel Starke 			gsm->num = i;
33211ec92e97SDaniel Starke 			break;
33221ec92e97SDaniel Starke 		}
33231ec92e97SDaniel Starke 	}
33241ec92e97SDaniel Starke 	spin_unlock(&gsm_mux_lock);
33251ec92e97SDaniel Starke 	if (i == MAX_MUX) {
33261ec92e97SDaniel Starke 		mutex_destroy(&gsm->mutex);
33271ec92e97SDaniel Starke 		kfree(gsm->txframe);
33281ec92e97SDaniel Starke 		kfree(gsm->buf);
33291ec92e97SDaniel Starke 		kfree(gsm);
33301ec92e97SDaniel Starke 		return NULL;
33311ec92e97SDaniel Starke 	}
33321ec92e97SDaniel Starke 
333396fd7ce5SGreg Kroah-Hartman 	return gsm;
333496fd7ce5SGreg Kroah-Hartman }
333596fd7ce5SGreg Kroah-Hartman 
gsm_copy_config_values(struct gsm_mux * gsm,struct gsm_config * c)333633841040STony Lindgren static void gsm_copy_config_values(struct gsm_mux *gsm,
333733841040STony Lindgren 				   struct gsm_config *c)
333833841040STony Lindgren {
333933841040STony Lindgren 	memset(c, 0, sizeof(*c));
334033841040STony Lindgren 	c->adaption = gsm->adaption;
334133841040STony Lindgren 	c->encapsulation = gsm->encoding;
334233841040STony Lindgren 	c->initiator = gsm->initiator;
334333841040STony Lindgren 	c->t1 = gsm->t1;
334433841040STony Lindgren 	c->t2 = gsm->t2;
33452ec7a802SDaniel Starke 	c->t3 = gsm->t3;
334633841040STony Lindgren 	c->n2 = gsm->n2;
334733841040STony Lindgren 	if (gsm->ftype == UIH)
334833841040STony Lindgren 		c->i = 1;
334933841040STony Lindgren 	else
335033841040STony Lindgren 		c->i = 2;
335133841040STony Lindgren 	pr_debug("Ftype %d i %d\n", gsm->ftype, c->i);
335233841040STony Lindgren 	c->mru = gsm->mru;
335333841040STony Lindgren 	c->mtu = gsm->mtu;
33542ec7a802SDaniel Starke 	c->k = gsm->k;
335533841040STony Lindgren }
335633841040STony Lindgren 
gsm_config(struct gsm_mux * gsm,struct gsm_config * c)335733841040STony Lindgren static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
335833841040STony Lindgren {
335933841040STony Lindgren 	int need_close = 0;
336033841040STony Lindgren 	int need_restart = 0;
336133841040STony Lindgren 
33628629745cSDaniel Starke 	/* Stuff we don't support yet - UI or I frame transport */
33638629745cSDaniel Starke 	if (c->adaption != 1 && c->adaption != 2)
336433841040STony Lindgren 		return -EOPNOTSUPP;
336533841040STony Lindgren 	/* Check the MRU/MTU range looks sane */
33667a121247SDaniel Starke 	if (c->mru < MIN_MTU || c->mtu < MIN_MTU)
33677a121247SDaniel Starke 		return -EINVAL;
33687a121247SDaniel Starke 	if (c->mru > MAX_MRU || c->mtu > MAX_MTU)
336933841040STony Lindgren 		return -EINVAL;
33702ec7a802SDaniel Starke 	if (c->t3 > MAX_T3)
33712ec7a802SDaniel Starke 		return -EINVAL;
3372d0bcdffcSDaniel Starke 	if (c->n2 > 255)
337333841040STony Lindgren 		return -EINVAL;
337433841040STony Lindgren 	if (c->encapsulation > 1)	/* Basic, advanced, no I */
337533841040STony Lindgren 		return -EINVAL;
337633841040STony Lindgren 	if (c->initiator > 1)
337733841040STony Lindgren 		return -EINVAL;
33782ec7a802SDaniel Starke 	if (c->k > MAX_WINDOW_SIZE)
33792ec7a802SDaniel Starke 		return -EINVAL;
338033841040STony Lindgren 	if (c->i == 0 || c->i > 2)	/* UIH and UI only */
338133841040STony Lindgren 		return -EINVAL;
338233841040STony Lindgren 	/*
338333841040STony Lindgren 	 * See what is needed for reconfiguration
338433841040STony Lindgren 	 */
338533841040STony Lindgren 
338633841040STony Lindgren 	/* Timing fields */
338733841040STony Lindgren 	if (c->t1 != 0 && c->t1 != gsm->t1)
338833841040STony Lindgren 		need_restart = 1;
338933841040STony Lindgren 	if (c->t2 != 0 && c->t2 != gsm->t2)
339033841040STony Lindgren 		need_restart = 1;
339133841040STony Lindgren 	if (c->encapsulation != gsm->encoding)
339233841040STony Lindgren 		need_restart = 1;
339333841040STony Lindgren 	if (c->adaption != gsm->adaption)
339433841040STony Lindgren 		need_restart = 1;
339533841040STony Lindgren 	/* Requires care */
339633841040STony Lindgren 	if (c->initiator != gsm->initiator)
339733841040STony Lindgren 		need_close = 1;
339833841040STony Lindgren 	if (c->mru != gsm->mru)
339933841040STony Lindgren 		need_restart = 1;
340033841040STony Lindgren 	if (c->mtu != gsm->mtu)
340133841040STony Lindgren 		need_restart = 1;
340233841040STony Lindgren 
340333841040STony Lindgren 	/*
340433841040STony Lindgren 	 * Close down what is needed, restart and initiate the new
3405aa371e96SDaniel Starke 	 * configuration. On the first time there is no DLCI[0]
3406aa371e96SDaniel Starke 	 * and closing or cleaning up is not necessary.
340733841040STony Lindgren 	 */
3408aa371e96SDaniel Starke 	if (need_close || need_restart)
3409aa371e96SDaniel Starke 		gsm_cleanup_mux(gsm, true);
341033841040STony Lindgren 
341133841040STony Lindgren 	gsm->initiator = c->initiator;
341233841040STony Lindgren 	gsm->mru = c->mru;
341333841040STony Lindgren 	gsm->mtu = c->mtu;
341419fb0a66SDaniel Starke 	gsm->encoding = c->encapsulation ? GSM_ADV_OPT : GSM_BASIC_OPT;
341533841040STony Lindgren 	gsm->adaption = c->adaption;
341633841040STony Lindgren 	gsm->n2 = c->n2;
341733841040STony Lindgren 
341833841040STony Lindgren 	if (c->i == 1)
341933841040STony Lindgren 		gsm->ftype = UIH;
342033841040STony Lindgren 	else if (c->i == 2)
342133841040STony Lindgren 		gsm->ftype = UI;
342233841040STony Lindgren 
342333841040STony Lindgren 	if (c->t1)
342433841040STony Lindgren 		gsm->t1 = c->t1;
342533841040STony Lindgren 	if (c->t2)
342633841040STony Lindgren 		gsm->t2 = c->t2;
34272ec7a802SDaniel Starke 	if (c->t3)
34282ec7a802SDaniel Starke 		gsm->t3 = c->t3;
34292ec7a802SDaniel Starke 	if (c->k)
34302ec7a802SDaniel Starke 		gsm->k = c->k;
343133841040STony Lindgren 
343233841040STony Lindgren 	/*
343333841040STony Lindgren 	 * FIXME: We need to separate activation/deactivation from adding
343433841040STony Lindgren 	 * and removing from the mux array
343533841040STony Lindgren 	 */
3436edd5f60cSDaniel Starke 	if (gsm->dead) {
3437e1c90bbbSDaniel Starke 		int ret = gsm_activate_mux(gsm);
3438edd5f60cSDaniel Starke 		if (ret)
3439edd5f60cSDaniel Starke 			return ret;
3440edd5f60cSDaniel Starke 		if (gsm->initiator)
344133841040STony Lindgren 			gsm_dlci_begin_open(gsm->dlci[0]);
3442edd5f60cSDaniel Starke 	}
344333841040STony Lindgren 	return 0;
344433841040STony Lindgren }
344533841040STony Lindgren 
gsm_copy_config_ext_values(struct gsm_mux * gsm,struct gsm_config_ext * ce)344672206cc7SDaniel Starke static void gsm_copy_config_ext_values(struct gsm_mux *gsm,
344772206cc7SDaniel Starke 				       struct gsm_config_ext *ce)
344872206cc7SDaniel Starke {
344972206cc7SDaniel Starke 	memset(ce, 0, sizeof(*ce));
34504ca58966SDaniel Starke 	ce->wait_config = gsm->wait_config ? 1 : 0;
345172206cc7SDaniel Starke 	ce->keep_alive = gsm->keep_alive;
345272206cc7SDaniel Starke }
345372206cc7SDaniel Starke 
gsm_config_ext(struct gsm_mux * gsm,struct gsm_config_ext * ce)345472206cc7SDaniel Starke static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce)
345572206cc7SDaniel Starke {
3456e112ec42SDaniel Starke 	bool need_restart = false;
345772206cc7SDaniel Starke 	unsigned int i;
345872206cc7SDaniel Starke 
345972206cc7SDaniel Starke 	/*
346072206cc7SDaniel Starke 	 * Check that userspace doesn't put stuff in here to prevent breakages
346172206cc7SDaniel Starke 	 * in the future.
346272206cc7SDaniel Starke 	 */
346372206cc7SDaniel Starke 	for (i = 0; i < ARRAY_SIZE(ce->reserved); i++)
346472206cc7SDaniel Starke 		if (ce->reserved[i])
346572206cc7SDaniel Starke 			return -EINVAL;
3466e112ec42SDaniel Starke 	if (ce->flags & ~GSM_FL_RESTART)
3467e112ec42SDaniel Starke 		return -EINVAL;
3468e112ec42SDaniel Starke 
3469e112ec42SDaniel Starke 	/* Requires care */
3470e112ec42SDaniel Starke 	if (ce->flags & GSM_FL_RESTART)
3471e112ec42SDaniel Starke 		need_restart = true;
3472e112ec42SDaniel Starke 
3473e112ec42SDaniel Starke 	/*
3474e112ec42SDaniel Starke 	 * Close down what is needed, restart and initiate the new
3475e112ec42SDaniel Starke 	 * configuration. On the first time there is no DLCI[0]
3476e112ec42SDaniel Starke 	 * and closing or cleaning up is not necessary.
3477e112ec42SDaniel Starke 	 */
3478e112ec42SDaniel Starke 	if (need_restart)
3479e112ec42SDaniel Starke 		gsm_cleanup_mux(gsm, true);
348072206cc7SDaniel Starke 
34814ca58966SDaniel Starke 	/*
34824ca58966SDaniel Starke 	 * Setup the new configuration values
34834ca58966SDaniel Starke 	 */
34844ca58966SDaniel Starke 	gsm->wait_config = ce->wait_config ? true : false;
348572206cc7SDaniel Starke 	gsm->keep_alive = ce->keep_alive;
34864ca58966SDaniel Starke 
3487e112ec42SDaniel Starke 	if (gsm->dead) {
3488e112ec42SDaniel Starke 		int ret = gsm_activate_mux(gsm);
3489e112ec42SDaniel Starke 		if (ret)
3490e112ec42SDaniel Starke 			return ret;
3491e112ec42SDaniel Starke 		if (gsm->initiator)
3492e112ec42SDaniel Starke 			gsm_dlci_begin_open(gsm->dlci[0]);
3493e112ec42SDaniel Starke 	}
3494e112ec42SDaniel Starke 
349572206cc7SDaniel Starke 	return 0;
349672206cc7SDaniel Starke }
349772206cc7SDaniel Starke 
349896fd7ce5SGreg Kroah-Hartman /**
349996fd7ce5SGreg Kroah-Hartman  *	gsmld_output		-	write to link
350096fd7ce5SGreg Kroah-Hartman  *	@gsm: our mux
350196fd7ce5SGreg Kroah-Hartman  *	@data: bytes to output
350296fd7ce5SGreg Kroah-Hartman  *	@len: size
350396fd7ce5SGreg Kroah-Hartman  *
350496fd7ce5SGreg Kroah-Hartman  *	Write a block of data from the GSM mux to the data channel. This
350596fd7ce5SGreg Kroah-Hartman  *	will eventually be serialized from above but at the moment isn't.
350696fd7ce5SGreg Kroah-Hartman  */
350796fd7ce5SGreg Kroah-Hartman 
gsmld_output(struct gsm_mux * gsm,u8 * data,int len)350896fd7ce5SGreg Kroah-Hartman static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
350996fd7ce5SGreg Kroah-Hartman {
351096fd7ce5SGreg Kroah-Hartman 	if (tty_write_room(gsm->tty) < len) {
351196fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
351296fd7ce5SGreg Kroah-Hartman 		return -ENOSPC;
351396fd7ce5SGreg Kroah-Hartman 	}
3514c07da737SDaniel Starke 	if (debug & DBG_DATA)
3515925ea0faSTony Lindgren 		gsm_hex_dump_bytes(__func__, data, len);
35169136c683STony Lindgren 	return gsm->tty->ops->write(gsm->tty, data, len);
351796fd7ce5SGreg Kroah-Hartman }
351896fd7ce5SGreg Kroah-Hartman 
35190af02167SDaniel Starke 
35200af02167SDaniel Starke /**
35210af02167SDaniel Starke  *	gsmld_write_trigger	-	schedule ldisc write task
35220af02167SDaniel Starke  *	@gsm: our mux
35230af02167SDaniel Starke  */
gsmld_write_trigger(struct gsm_mux * gsm)35240af02167SDaniel Starke static void gsmld_write_trigger(struct gsm_mux *gsm)
35250af02167SDaniel Starke {
35260af02167SDaniel Starke 	if (!gsm || !gsm->dlci[0] || gsm->dlci[0]->dead)
35270af02167SDaniel Starke 		return;
35280af02167SDaniel Starke 	schedule_work(&gsm->tx_work);
35290af02167SDaniel Starke }
35300af02167SDaniel Starke 
35310af02167SDaniel Starke 
35320af02167SDaniel Starke /**
35330af02167SDaniel Starke  *	gsmld_write_task	-	ldisc write task
35340af02167SDaniel Starke  *	@work: our tx write work
35350af02167SDaniel Starke  *
35360af02167SDaniel Starke  *	Writes out data to the ldisc if possible. We are doing this here to
35370af02167SDaniel Starke  *	avoid dead-locking. This returns if no space or data is left for output.
35380af02167SDaniel Starke  */
gsmld_write_task(struct work_struct * work)35390af02167SDaniel Starke static void gsmld_write_task(struct work_struct *work)
35400af02167SDaniel Starke {
35410af02167SDaniel Starke 	struct gsm_mux *gsm = container_of(work, struct gsm_mux, tx_work);
3542acdab4cbSFedor Pchelkin 	unsigned long flags;
35430af02167SDaniel Starke 	int i, ret;
35440af02167SDaniel Starke 
35450af02167SDaniel Starke 	/* All outstanding control channel and control messages and one data
35460af02167SDaniel Starke 	 * frame is sent.
35470af02167SDaniel Starke 	 */
35480af02167SDaniel Starke 	ret = -ENODEV;
3549acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&gsm->tx_lock, flags);
35500af02167SDaniel Starke 	if (gsm->tty)
35510af02167SDaniel Starke 		ret = gsm_data_kick(gsm);
3552acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&gsm->tx_lock, flags);
35530af02167SDaniel Starke 
35540af02167SDaniel Starke 	if (ret >= 0)
35550af02167SDaniel Starke 		for (i = 0; i < NUM_DLCI; i++)
35560af02167SDaniel Starke 			if (gsm->dlci[i])
35570af02167SDaniel Starke 				tty_port_tty_wakeup(&gsm->dlci[i]->port);
35580af02167SDaniel Starke }
35590af02167SDaniel Starke 
356096fd7ce5SGreg Kroah-Hartman /**
356196fd7ce5SGreg Kroah-Hartman  *	gsmld_attach_gsm	-	mode set up
356296fd7ce5SGreg Kroah-Hartman  *	@tty: our tty structure
356396fd7ce5SGreg Kroah-Hartman  *	@gsm: our mux
356496fd7ce5SGreg Kroah-Hartman  *
356596fd7ce5SGreg Kroah-Hartman  *	Set up the MUX for basic mode and commence connecting to the
356696fd7ce5SGreg Kroah-Hartman  *	modem. Currently called from the line discipline set up but
356796fd7ce5SGreg Kroah-Hartman  *	will need moving to an ioctl path.
356896fd7ce5SGreg Kroah-Hartman  */
356996fd7ce5SGreg Kroah-Hartman 
gsmld_attach_gsm(struct tty_struct * tty,struct gsm_mux * gsm)357001aecd91SDaniel Starke static void gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
357196fd7ce5SGreg Kroah-Hartman {
357296fd7ce5SGreg Kroah-Hartman 	gsm->tty = tty_kref_get(tty);
3573f4f7d632SDaniel Starke 	/* Turn off tty XON/XOFF handling to handle it explicitly. */
3574f4f7d632SDaniel Starke 	gsm->old_c_iflag = tty->termios.c_iflag;
3575f4f7d632SDaniel Starke 	tty->termios.c_iflag &= (IXON | IXOFF);
35760a360e8bSHillf Danton }
357796fd7ce5SGreg Kroah-Hartman 
357896fd7ce5SGreg Kroah-Hartman /**
357996fd7ce5SGreg Kroah-Hartman  *	gsmld_detach_gsm	-	stop doing 0710 mux
358070f23fd6SJustin P. Mattock  *	@tty: tty attached to the mux
358196fd7ce5SGreg Kroah-Hartman  *	@gsm: mux
358296fd7ce5SGreg Kroah-Hartman  *
358396fd7ce5SGreg Kroah-Hartman  *	Shutdown and then clean up the resources used by the line discipline
358496fd7ce5SGreg Kroah-Hartman  */
358596fd7ce5SGreg Kroah-Hartman 
gsmld_detach_gsm(struct tty_struct * tty,struct gsm_mux * gsm)358696fd7ce5SGreg Kroah-Hartman static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
358796fd7ce5SGreg Kroah-Hartman {
358896fd7ce5SGreg Kroah-Hartman 	WARN_ON(tty != gsm->tty);
3589f4f7d632SDaniel Starke 	/* Restore tty XON/XOFF handling. */
3590f4f7d632SDaniel Starke 	gsm->tty->termios.c_iflag = gsm->old_c_iflag;
359196fd7ce5SGreg Kroah-Hartman 	tty_kref_put(gsm->tty);
359296fd7ce5SGreg Kroah-Hartman 	gsm->tty = NULL;
359396fd7ce5SGreg Kroah-Hartman }
359496fd7ce5SGreg Kroah-Hartman 
gsmld_receive_buf(struct tty_struct * tty,const u8 * cp,const u8 * fp,size_t count)3595a8d9cd23SJiri Slaby (SUSE) static void gsmld_receive_buf(struct tty_struct *tty, const u8 *cp,
3596892bc209SJiri Slaby (SUSE) 			      const u8 *fp, size_t count)
359796fd7ce5SGreg Kroah-Hartman {
359896fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = tty->disc_data;
359982f91fe0SPeter Hurley 	char flags = TTY_NORMAL;
360096fd7ce5SGreg Kroah-Hartman 
3601c07da737SDaniel Starke 	if (debug & DBG_DATA)
3602925ea0faSTony Lindgren 		gsm_hex_dump_bytes(__func__, cp, count);
360396fd7ce5SGreg Kroah-Hartman 
3604b93db97eSJiri Slaby 	for (; count; count--, cp++) {
3605b93db97eSJiri Slaby 		if (fp)
3606b93db97eSJiri Slaby 			flags = *fp++;
360796fd7ce5SGreg Kroah-Hartman 		switch (flags) {
360896fd7ce5SGreg Kroah-Hartman 		case TTY_NORMAL:
3609f16c6d2eSMazin Al Haddad 			if (gsm->receive)
3610b93db97eSJiri Slaby 				gsm->receive(gsm, *cp);
361196fd7ce5SGreg Kroah-Hartman 			break;
361296fd7ce5SGreg Kroah-Hartman 		case TTY_OVERRUN:
361396fd7ce5SGreg Kroah-Hartman 		case TTY_BREAK:
361496fd7ce5SGreg Kroah-Hartman 		case TTY_PARITY:
361596fd7ce5SGreg Kroah-Hartman 		case TTY_FRAME:
3616ea502201SJiri Slaby 			gsm_error(gsm);
361796fd7ce5SGreg Kroah-Hartman 			break;
361896fd7ce5SGreg Kroah-Hartman 		default:
3619c01af4feSFrederic Berat 			WARN_ONCE(1, "%s: unknown flag %d\n",
3620429b4749SRasmus Villemoes 			       tty_name(tty), flags);
362196fd7ce5SGreg Kroah-Hartman 			break;
362296fd7ce5SGreg Kroah-Hartman 		}
362396fd7ce5SGreg Kroah-Hartman 	}
362496fd7ce5SGreg Kroah-Hartman 	/* FASYNC if needed ? */
362596fd7ce5SGreg Kroah-Hartman 	/* If clogged call tty_throttle(tty); */
362696fd7ce5SGreg Kroah-Hartman }
362796fd7ce5SGreg Kroah-Hartman 
362896fd7ce5SGreg Kroah-Hartman /**
362996fd7ce5SGreg Kroah-Hartman  *	gsmld_flush_buffer	-	clean input queue
363096fd7ce5SGreg Kroah-Hartman  *	@tty:	terminal device
363196fd7ce5SGreg Kroah-Hartman  *
363296fd7ce5SGreg Kroah-Hartman  *	Flush the input buffer. Called when the line discipline is
363396fd7ce5SGreg Kroah-Hartman  *	being closed, when the tty layer wants the buffer flushed (eg
363496fd7ce5SGreg Kroah-Hartman  *	at hangup).
363596fd7ce5SGreg Kroah-Hartman  */
363696fd7ce5SGreg Kroah-Hartman 
gsmld_flush_buffer(struct tty_struct * tty)363796fd7ce5SGreg Kroah-Hartman static void gsmld_flush_buffer(struct tty_struct *tty)
363896fd7ce5SGreg Kroah-Hartman {
363996fd7ce5SGreg Kroah-Hartman }
364096fd7ce5SGreg Kroah-Hartman 
364196fd7ce5SGreg Kroah-Hartman /**
364296fd7ce5SGreg Kroah-Hartman  *	gsmld_close		-	close the ldisc for this tty
364396fd7ce5SGreg Kroah-Hartman  *	@tty: device
364496fd7ce5SGreg Kroah-Hartman  *
364596fd7ce5SGreg Kroah-Hartman  *	Called from the terminal layer when this line discipline is
364696fd7ce5SGreg Kroah-Hartman  *	being shut down, either because of a close or becsuse of a
364796fd7ce5SGreg Kroah-Hartman  *	discipline change. The function will not be called while other
364896fd7ce5SGreg Kroah-Hartman  *	ldisc methods are in progress.
364996fd7ce5SGreg Kroah-Hartman  */
365096fd7ce5SGreg Kroah-Hartman 
gsmld_close(struct tty_struct * tty)365196fd7ce5SGreg Kroah-Hartman static void gsmld_close(struct tty_struct *tty)
365296fd7ce5SGreg Kroah-Hartman {
365396fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = tty->disc_data;
365496fd7ce5SGreg Kroah-Hartman 
3655284260f2SDaniel Starke 	/* The ldisc locks and closes the port before calling our close. This
3656284260f2SDaniel Starke 	 * means we have no way to do a proper disconnect. We will not bother
3657284260f2SDaniel Starke 	 * to do one.
3658284260f2SDaniel Starke 	 */
3659284260f2SDaniel Starke 	gsm_cleanup_mux(gsm, false);
3660284260f2SDaniel Starke 
366196fd7ce5SGreg Kroah-Hartman 	gsmld_detach_gsm(tty, gsm);
366296fd7ce5SGreg Kroah-Hartman 
366396fd7ce5SGreg Kroah-Hartman 	gsmld_flush_buffer(tty);
366496fd7ce5SGreg Kroah-Hartman 	/* Do other clean up here */
36656ab8fba7SRuss Gorby 	mux_put(gsm);
366696fd7ce5SGreg Kroah-Hartman }
366796fd7ce5SGreg Kroah-Hartman 
366896fd7ce5SGreg Kroah-Hartman /**
366996fd7ce5SGreg Kroah-Hartman  *	gsmld_open		-	open an ldisc
367096fd7ce5SGreg Kroah-Hartman  *	@tty: terminal to open
367196fd7ce5SGreg Kroah-Hartman  *
367296fd7ce5SGreg Kroah-Hartman  *	Called when this line discipline is being attached to the
367396fd7ce5SGreg Kroah-Hartman  *	terminal device. Can sleep. Called serialized so that no
367496fd7ce5SGreg Kroah-Hartman  *	other events will occur in parallel. No further open will occur
367596fd7ce5SGreg Kroah-Hartman  *	until a close.
367696fd7ce5SGreg Kroah-Hartman  */
367796fd7ce5SGreg Kroah-Hartman 
gsmld_open(struct tty_struct * tty)367896fd7ce5SGreg Kroah-Hartman static int gsmld_open(struct tty_struct *tty)
367996fd7ce5SGreg Kroah-Hartman {
368096fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm;
368196fd7ce5SGreg Kroah-Hartman 
368267c37756SThadeu Lima de Souza Cascardo 	if (!capable(CAP_NET_ADMIN))
368367c37756SThadeu Lima de Souza Cascardo 		return -EPERM;
368467c37756SThadeu Lima de Souza Cascardo 
368596fd7ce5SGreg Kroah-Hartman 	if (tty->ops->write == NULL)
368696fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
368796fd7ce5SGreg Kroah-Hartman 
368896fd7ce5SGreg Kroah-Hartman 	/* Attach our ldisc data */
368996fd7ce5SGreg Kroah-Hartman 	gsm = gsm_alloc_mux();
369096fd7ce5SGreg Kroah-Hartman 	if (gsm == NULL)
369196fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
369296fd7ce5SGreg Kroah-Hartman 
369396fd7ce5SGreg Kroah-Hartman 	tty->disc_data = gsm;
369496fd7ce5SGreg Kroah-Hartman 	tty->receive_room = 65536;
369596fd7ce5SGreg Kroah-Hartman 
369696fd7ce5SGreg Kroah-Hartman 	/* Attach the initial passive connection */
369701aecd91SDaniel Starke 	gsmld_attach_gsm(tty, gsm);
369801aecd91SDaniel Starke 
36994ca58966SDaniel Starke 	/* The mux will not be activated yet, we wait for correct
37004ca58966SDaniel Starke 	 * configuration first.
37014ca58966SDaniel Starke 	 */
37024ca58966SDaniel Starke 	if (gsm->encoding == GSM_BASIC_OPT)
37034ca58966SDaniel Starke 		gsm->receive = gsm0_receive;
37044ca58966SDaniel Starke 	else
37054ca58966SDaniel Starke 		gsm->receive = gsm1_receive;
37064ca58966SDaniel Starke 
370701aecd91SDaniel Starke 	return 0;
370896fd7ce5SGreg Kroah-Hartman }
370996fd7ce5SGreg Kroah-Hartman 
371096fd7ce5SGreg Kroah-Hartman /**
371196fd7ce5SGreg Kroah-Hartman  *	gsmld_write_wakeup	-	asynchronous I/O notifier
371296fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
371396fd7ce5SGreg Kroah-Hartman  *
371496fd7ce5SGreg Kroah-Hartman  *	Required for the ptys, serial driver etc. since processes
371596fd7ce5SGreg Kroah-Hartman  *	that attach themselves to the master and rely on ASYNC
371696fd7ce5SGreg Kroah-Hartman  *	IO must be woken up
371796fd7ce5SGreg Kroah-Hartman  */
371896fd7ce5SGreg Kroah-Hartman 
gsmld_write_wakeup(struct tty_struct * tty)371996fd7ce5SGreg Kroah-Hartman static void gsmld_write_wakeup(struct tty_struct *tty)
372096fd7ce5SGreg Kroah-Hartman {
372196fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = tty->disc_data;
372296fd7ce5SGreg Kroah-Hartman 
372396fd7ce5SGreg Kroah-Hartman 	/* Queue poll */
37240af02167SDaniel Starke 	gsmld_write_trigger(gsm);
372596fd7ce5SGreg Kroah-Hartman }
372696fd7ce5SGreg Kroah-Hartman 
372796fd7ce5SGreg Kroah-Hartman /**
372896fd7ce5SGreg Kroah-Hartman  *	gsmld_read		-	read function for tty
372996fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
373096fd7ce5SGreg Kroah-Hartman  *	@file: file object
373196fd7ce5SGreg Kroah-Hartman  *	@buf: userspace buffer pointer
373296fd7ce5SGreg Kroah-Hartman  *	@nr: size of I/O
3733542a121aSLee Jones  *	@cookie: unused
3734542a121aSLee Jones  *	@offset: unused
373596fd7ce5SGreg Kroah-Hartman  *
373696fd7ce5SGreg Kroah-Hartman  *	Perform reads for the line discipline. We are guaranteed that the
373796fd7ce5SGreg Kroah-Hartman  *	line discipline will not be closed under us but we may get multiple
373896fd7ce5SGreg Kroah-Hartman  *	parallel readers and must handle this ourselves. We may also get
373996fd7ce5SGreg Kroah-Hartman  *	a hangup. Always called in user context, may sleep.
374096fd7ce5SGreg Kroah-Hartman  *
374196fd7ce5SGreg Kroah-Hartman  *	This code must be sure never to sleep through a hangup.
374296fd7ce5SGreg Kroah-Hartman  */
374396fd7ce5SGreg Kroah-Hartman 
gsmld_read(struct tty_struct * tty,struct file * file,u8 * buf,size_t nr,void ** cookie,unsigned long offset)374449b8220cSJiri Slaby (SUSE) static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, u8 *buf,
374549b8220cSJiri Slaby (SUSE) 			  size_t nr, void **cookie, unsigned long offset)
374696fd7ce5SGreg Kroah-Hartman {
374796fd7ce5SGreg Kroah-Hartman 	return -EOPNOTSUPP;
374896fd7ce5SGreg Kroah-Hartman }
374996fd7ce5SGreg Kroah-Hartman 
375096fd7ce5SGreg Kroah-Hartman /**
375196fd7ce5SGreg Kroah-Hartman  *	gsmld_write		-	write function for tty
375296fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
375396fd7ce5SGreg Kroah-Hartman  *	@file: file object
375496fd7ce5SGreg Kroah-Hartman  *	@buf: userspace buffer pointer
375596fd7ce5SGreg Kroah-Hartman  *	@nr: size of I/O
375696fd7ce5SGreg Kroah-Hartman  *
375796fd7ce5SGreg Kroah-Hartman  *	Called when the owner of the device wants to send a frame
375896fd7ce5SGreg Kroah-Hartman  *	itself (or some other control data). The data is transferred
375996fd7ce5SGreg Kroah-Hartman  *	as-is and must be properly framed and checksummed as appropriate
376096fd7ce5SGreg Kroah-Hartman  *	by userspace. Frames are either sent whole or not at all as this
376196fd7ce5SGreg Kroah-Hartman  *	avoids pain user side.
376296fd7ce5SGreg Kroah-Hartman  */
376396fd7ce5SGreg Kroah-Hartman 
gsmld_write(struct tty_struct * tty,struct file * file,const u8 * buf,size_t nr)376496fd7ce5SGreg Kroah-Hartman static ssize_t gsmld_write(struct tty_struct *tty, struct file *file,
376549b8220cSJiri Slaby (SUSE) 			   const u8 *buf, size_t nr)
376696fd7ce5SGreg Kroah-Hartman {
376732dd59f9SDaniel Starke 	struct gsm_mux *gsm = tty->disc_data;
3768acdab4cbSFedor Pchelkin 	unsigned long flags;
376932dd59f9SDaniel Starke 	int space;
377032dd59f9SDaniel Starke 	int ret;
377132dd59f9SDaniel Starke 
377232dd59f9SDaniel Starke 	if (!gsm)
377332dd59f9SDaniel Starke 		return -ENODEV;
377432dd59f9SDaniel Starke 
377532dd59f9SDaniel Starke 	ret = -ENOBUFS;
3776acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&gsm->tx_lock, flags);
377732dd59f9SDaniel Starke 	space = tty_write_room(tty);
377896fd7ce5SGreg Kroah-Hartman 	if (space >= nr)
377932dd59f9SDaniel Starke 		ret = tty->ops->write(tty, buf, nr);
378032dd59f9SDaniel Starke 	else
378196fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
3782acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&gsm->tx_lock, flags);
378332dd59f9SDaniel Starke 
378432dd59f9SDaniel Starke 	return ret;
378596fd7ce5SGreg Kroah-Hartman }
378696fd7ce5SGreg Kroah-Hartman 
378796fd7ce5SGreg Kroah-Hartman /**
378896fd7ce5SGreg Kroah-Hartman  *	gsmld_poll		-	poll method for N_GSM0710
378996fd7ce5SGreg Kroah-Hartman  *	@tty: terminal device
379096fd7ce5SGreg Kroah-Hartman  *	@file: file accessing it
379196fd7ce5SGreg Kroah-Hartman  *	@wait: poll table
379296fd7ce5SGreg Kroah-Hartman  *
379396fd7ce5SGreg Kroah-Hartman  *	Called when the line discipline is asked to poll() for data or
379496fd7ce5SGreg Kroah-Hartman  *	for special events. This code is not serialized with respect to
379596fd7ce5SGreg Kroah-Hartman  *	other events save open/close.
379696fd7ce5SGreg Kroah-Hartman  *
379796fd7ce5SGreg Kroah-Hartman  *	This code must be sure never to sleep through a hangup.
379896fd7ce5SGreg Kroah-Hartman  *	Called without the kernel lock held - fine
379996fd7ce5SGreg Kroah-Hartman  */
380096fd7ce5SGreg Kroah-Hartman 
gsmld_poll(struct tty_struct * tty,struct file * file,poll_table * wait)3801afc9a42bSAl Viro static __poll_t gsmld_poll(struct tty_struct *tty, struct file *file,
380296fd7ce5SGreg Kroah-Hartman 							poll_table *wait)
380396fd7ce5SGreg Kroah-Hartman {
3804afc9a42bSAl Viro 	__poll_t mask = 0;
380596fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = tty->disc_data;
380696fd7ce5SGreg Kroah-Hartman 
380796fd7ce5SGreg Kroah-Hartman 	poll_wait(file, &tty->read_wait, wait);
380896fd7ce5SGreg Kroah-Hartman 	poll_wait(file, &tty->write_wait, wait);
38097e5b4322SDaniel Starke 
38107e5b4322SDaniel Starke 	if (gsm->dead)
38117e5b4322SDaniel Starke 		mask |= EPOLLHUP;
381296fd7ce5SGreg Kroah-Hartman 	if (tty_hung_up_p(file))
3813a9a08845SLinus Torvalds 		mask |= EPOLLHUP;
38147e5b4322SDaniel Starke 	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
38157e5b4322SDaniel Starke 		mask |= EPOLLHUP;
381696fd7ce5SGreg Kroah-Hartman 	if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0)
3817a9a08845SLinus Torvalds 		mask |= EPOLLOUT | EPOLLWRNORM;
381896fd7ce5SGreg Kroah-Hartman 	return mask;
381996fd7ce5SGreg Kroah-Hartman }
382096fd7ce5SGreg Kroah-Hartman 
gsmld_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)3821d78328bcSJiri Slaby static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
3822d78328bcSJiri Slaby 		       unsigned long arg)
382396fd7ce5SGreg Kroah-Hartman {
382496fd7ce5SGreg Kroah-Hartman 	struct gsm_config c;
382572206cc7SDaniel Starke 	struct gsm_config_ext ce;
3826afe3154bSDaniel Starke 	struct gsm_dlci_config dc;
382796fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm = tty->disc_data;
38282b3174c9SIlpo Järvinen 	unsigned int base, addr;
3829afe3154bSDaniel Starke 	struct gsm_dlci *dlci;
383096fd7ce5SGreg Kroah-Hartman 
383196fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
383296fd7ce5SGreg Kroah-Hartman 	case GSMIOC_GETCONF:
383333841040STony Lindgren 		gsm_copy_config_values(gsm, &c);
3834edd05a73SJiri Slaby 		if (copy_to_user((void __user *)arg, &c, sizeof(c)))
383596fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
383696fd7ce5SGreg Kroah-Hartman 		return 0;
383796fd7ce5SGreg Kroah-Hartman 	case GSMIOC_SETCONF:
3838edd05a73SJiri Slaby 		if (copy_from_user(&c, (void __user *)arg, sizeof(c)))
383996fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
384033841040STony Lindgren 		return gsm_config(gsm, &c);
3841a7b121b4SMartin Hundebøll 	case GSMIOC_GETFIRST:
3842a7b121b4SMartin Hundebøll 		base = mux_num_to_base(gsm);
3843a7b121b4SMartin Hundebøll 		return put_user(base + 1, (__u32 __user *)arg);
384472206cc7SDaniel Starke 	case GSMIOC_GETCONF_EXT:
384572206cc7SDaniel Starke 		gsm_copy_config_ext_values(gsm, &ce);
384672206cc7SDaniel Starke 		if (copy_to_user((void __user *)arg, &ce, sizeof(ce)))
384772206cc7SDaniel Starke 			return -EFAULT;
384872206cc7SDaniel Starke 		return 0;
384972206cc7SDaniel Starke 	case GSMIOC_SETCONF_EXT:
385072206cc7SDaniel Starke 		if (copy_from_user(&ce, (void __user *)arg, sizeof(ce)))
385172206cc7SDaniel Starke 			return -EFAULT;
385272206cc7SDaniel Starke 		return gsm_config_ext(gsm, &ce);
3853afe3154bSDaniel Starke 	case GSMIOC_GETCONF_DLCI:
3854afe3154bSDaniel Starke 		if (copy_from_user(&dc, (void __user *)arg, sizeof(dc)))
3855afe3154bSDaniel Starke 			return -EFAULT;
3856afe3154bSDaniel Starke 		if (dc.channel == 0 || dc.channel >= NUM_DLCI)
3857afe3154bSDaniel Starke 			return -EINVAL;
38582b3174c9SIlpo Järvinen 		addr = array_index_nospec(dc.channel, NUM_DLCI);
38592b3174c9SIlpo Järvinen 		dlci = gsm->dlci[addr];
3860afe3154bSDaniel Starke 		if (!dlci) {
38612b3174c9SIlpo Järvinen 			dlci = gsm_dlci_alloc(gsm, addr);
3862afe3154bSDaniel Starke 			if (!dlci)
3863afe3154bSDaniel Starke 				return -ENOMEM;
3864afe3154bSDaniel Starke 		}
3865afe3154bSDaniel Starke 		gsm_dlci_copy_config_values(dlci, &dc);
3866afe3154bSDaniel Starke 		if (copy_to_user((void __user *)arg, &dc, sizeof(dc)))
3867afe3154bSDaniel Starke 			return -EFAULT;
3868afe3154bSDaniel Starke 		return 0;
3869afe3154bSDaniel Starke 	case GSMIOC_SETCONF_DLCI:
3870afe3154bSDaniel Starke 		if (copy_from_user(&dc, (void __user *)arg, sizeof(dc)))
3871afe3154bSDaniel Starke 			return -EFAULT;
3872afe3154bSDaniel Starke 		if (dc.channel == 0 || dc.channel >= NUM_DLCI)
3873afe3154bSDaniel Starke 			return -EINVAL;
38742b3174c9SIlpo Järvinen 		addr = array_index_nospec(dc.channel, NUM_DLCI);
38752b3174c9SIlpo Järvinen 		dlci = gsm->dlci[addr];
3876afe3154bSDaniel Starke 		if (!dlci) {
38772b3174c9SIlpo Järvinen 			dlci = gsm_dlci_alloc(gsm, addr);
3878afe3154bSDaniel Starke 			if (!dlci)
3879afe3154bSDaniel Starke 				return -ENOMEM;
3880afe3154bSDaniel Starke 		}
3881afe3154bSDaniel Starke 		return gsm_dlci_config(dlci, &dc, 0);
388296fd7ce5SGreg Kroah-Hartman 	default:
38837c783601SJiri Slaby 		return n_tty_ioctl_helper(tty, cmd, arg);
388496fd7ce5SGreg Kroah-Hartman 	}
388596fd7ce5SGreg Kroah-Hartman }
388696fd7ce5SGreg Kroah-Hartman 
3887bcd5abe2SRuss Gorby /*
3888bcd5abe2SRuss Gorby  *	Network interface
3889bcd5abe2SRuss Gorby  *
3890bcd5abe2SRuss Gorby  */
3891bcd5abe2SRuss Gorby 
gsm_mux_net_open(struct net_device * net)3892bcd5abe2SRuss Gorby static int gsm_mux_net_open(struct net_device *net)
3893bcd5abe2SRuss Gorby {
3894bcd5abe2SRuss Gorby 	pr_debug("%s called\n", __func__);
3895bcd5abe2SRuss Gorby 	netif_start_queue(net);
3896bcd5abe2SRuss Gorby 	return 0;
3897bcd5abe2SRuss Gorby }
3898bcd5abe2SRuss Gorby 
gsm_mux_net_close(struct net_device * net)3899bcd5abe2SRuss Gorby static int gsm_mux_net_close(struct net_device *net)
3900bcd5abe2SRuss Gorby {
3901bcd5abe2SRuss Gorby 	netif_stop_queue(net);
3902bcd5abe2SRuss Gorby 	return 0;
3903bcd5abe2SRuss Gorby }
3904bcd5abe2SRuss Gorby 
dlci_net_free(struct gsm_dlci * dlci)3905bcd5abe2SRuss Gorby static void dlci_net_free(struct gsm_dlci *dlci)
3906bcd5abe2SRuss Gorby {
3907bcd5abe2SRuss Gorby 	if (!dlci->net) {
3908bcd5abe2SRuss Gorby 		WARN_ON(1);
3909bcd5abe2SRuss Gorby 		return;
3910bcd5abe2SRuss Gorby 	}
3911bcd5abe2SRuss Gorby 	dlci->adaption = dlci->prev_adaption;
3912bcd5abe2SRuss Gorby 	dlci->data = dlci->prev_data;
3913bcd5abe2SRuss Gorby 	free_netdev(dlci->net);
3914bcd5abe2SRuss Gorby 	dlci->net = NULL;
3915bcd5abe2SRuss Gorby }
net_free(struct kref * ref)3916bcd5abe2SRuss Gorby static void net_free(struct kref *ref)
3917bcd5abe2SRuss Gorby {
3918bcd5abe2SRuss Gorby 	struct gsm_mux_net *mux_net;
3919bcd5abe2SRuss Gorby 	struct gsm_dlci *dlci;
3920bcd5abe2SRuss Gorby 
3921bcd5abe2SRuss Gorby 	mux_net = container_of(ref, struct gsm_mux_net, ref);
3922bcd5abe2SRuss Gorby 	dlci = mux_net->dlci;
3923bcd5abe2SRuss Gorby 
3924bcd5abe2SRuss Gorby 	if (dlci->net) {
3925bcd5abe2SRuss Gorby 		unregister_netdev(dlci->net);
3926bcd5abe2SRuss Gorby 		dlci_net_free(dlci);
3927bcd5abe2SRuss Gorby 	}
3928bcd5abe2SRuss Gorby }
3929bcd5abe2SRuss Gorby 
muxnet_get(struct gsm_mux_net * mux_net)39306ab8fba7SRuss Gorby static inline void muxnet_get(struct gsm_mux_net *mux_net)
39316ab8fba7SRuss Gorby {
39326ab8fba7SRuss Gorby 	kref_get(&mux_net->ref);
39336ab8fba7SRuss Gorby }
39346ab8fba7SRuss Gorby 
muxnet_put(struct gsm_mux_net * mux_net)39356ab8fba7SRuss Gorby static inline void muxnet_put(struct gsm_mux_net *mux_net)
39366ab8fba7SRuss Gorby {
39376ab8fba7SRuss Gorby 	kref_put(&mux_net->ref, net_free);
39386ab8fba7SRuss Gorby }
39396ab8fba7SRuss Gorby 
gsm_mux_net_start_xmit(struct sk_buff * skb,struct net_device * net)39402468b3e4SLuc Van Oostenryck static netdev_tx_t gsm_mux_net_start_xmit(struct sk_buff *skb,
3941bcd5abe2SRuss Gorby 				      struct net_device *net)
3942bcd5abe2SRuss Gorby {
39435dbc32a8SJulia Lawall 	struct gsm_mux_net *mux_net = netdev_priv(net);
3944bcd5abe2SRuss Gorby 	struct gsm_dlci *dlci = mux_net->dlci;
39456ab8fba7SRuss Gorby 	muxnet_get(mux_net);
3946bcd5abe2SRuss Gorby 
3947bcd5abe2SRuss Gorby 	skb_queue_head(&dlci->skb_list, skb);
394847baf1adSTobias Klauser 	net->stats.tx_packets++;
394947baf1adSTobias Klauser 	net->stats.tx_bytes += skb->len;
3950bcd5abe2SRuss Gorby 	gsm_dlci_data_kick(dlci);
3951bcd5abe2SRuss Gorby 	/* And tell the kernel when the last transmit started. */
3952860e9538SFlorian Westphal 	netif_trans_update(net);
39536ab8fba7SRuss Gorby 	muxnet_put(mux_net);
3954bcd5abe2SRuss Gorby 	return NETDEV_TX_OK;
3955bcd5abe2SRuss Gorby }
3956bcd5abe2SRuss Gorby 
3957bcd5abe2SRuss Gorby /* called when a packet did not ack after watchdogtimeout */
gsm_mux_net_tx_timeout(struct net_device * net,unsigned int txqueue)39580290bd29SMichael S. Tsirkin static void gsm_mux_net_tx_timeout(struct net_device *net, unsigned int txqueue)
3959bcd5abe2SRuss Gorby {
3960bcd5abe2SRuss Gorby 	/* Tell syslog we are hosed. */
3961bcd5abe2SRuss Gorby 	dev_dbg(&net->dev, "Tx timed out.\n");
3962bcd5abe2SRuss Gorby 
3963bcd5abe2SRuss Gorby 	/* Update statistics */
396447baf1adSTobias Klauser 	net->stats.tx_errors++;
3965bcd5abe2SRuss Gorby }
3966bcd5abe2SRuss Gorby 
gsm_mux_rx_netchar(struct gsm_dlci * dlci,const unsigned char * in_buf,int size)3967bcd5abe2SRuss Gorby static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
39684feb7a4aSTony Lindgren 				const unsigned char *in_buf, int size)
3969bcd5abe2SRuss Gorby {
3970bcd5abe2SRuss Gorby 	struct net_device *net = dlci->net;
3971bcd5abe2SRuss Gorby 	struct sk_buff *skb;
39725dbc32a8SJulia Lawall 	struct gsm_mux_net *mux_net = netdev_priv(net);
39736ab8fba7SRuss Gorby 	muxnet_get(mux_net);
3974bcd5abe2SRuss Gorby 
3975bcd5abe2SRuss Gorby 	/* Allocate an sk_buff */
3976bcd5abe2SRuss Gorby 	skb = dev_alloc_skb(size + NET_IP_ALIGN);
3977bcd5abe2SRuss Gorby 	if (!skb) {
3978bcd5abe2SRuss Gorby 		/* We got no receive buffer. */
397947baf1adSTobias Klauser 		net->stats.rx_dropped++;
39806ab8fba7SRuss Gorby 		muxnet_put(mux_net);
3981bcd5abe2SRuss Gorby 		return;
3982bcd5abe2SRuss Gorby 	}
3983bcd5abe2SRuss Gorby 	skb_reserve(skb, NET_IP_ALIGN);
398459ae1d12SJohannes Berg 	skb_put_data(skb, in_buf, size);
3985bcd5abe2SRuss Gorby 
3986bcd5abe2SRuss Gorby 	skb->dev = net;
398775406b3bSVaishali Thakkar 	skb->protocol = htons(ETH_P_IP);
3988bcd5abe2SRuss Gorby 
3989bcd5abe2SRuss Gorby 	/* Ship it off to the kernel */
3990bcd5abe2SRuss Gorby 	netif_rx(skb);
3991bcd5abe2SRuss Gorby 
3992bcd5abe2SRuss Gorby 	/* update out statistics */
399347baf1adSTobias Klauser 	net->stats.rx_packets++;
399447baf1adSTobias Klauser 	net->stats.rx_bytes += size;
39956ab8fba7SRuss Gorby 	muxnet_put(mux_net);
3996bcd5abe2SRuss Gorby 	return;
3997bcd5abe2SRuss Gorby }
3998bcd5abe2SRuss Gorby 
gsm_mux_net_init(struct net_device * net)3999bcd5abe2SRuss Gorby static void gsm_mux_net_init(struct net_device *net)
4000bcd5abe2SRuss Gorby {
4001bcd5abe2SRuss Gorby 	static const struct net_device_ops gsm_netdev_ops = {
4002bcd5abe2SRuss Gorby 		.ndo_open		= gsm_mux_net_open,
4003bcd5abe2SRuss Gorby 		.ndo_stop		= gsm_mux_net_close,
4004bcd5abe2SRuss Gorby 		.ndo_start_xmit		= gsm_mux_net_start_xmit,
4005bcd5abe2SRuss Gorby 		.ndo_tx_timeout		= gsm_mux_net_tx_timeout,
4006bcd5abe2SRuss Gorby 	};
4007bcd5abe2SRuss Gorby 
4008bcd5abe2SRuss Gorby 	net->netdev_ops = &gsm_netdev_ops;
4009bcd5abe2SRuss Gorby 
4010bcd5abe2SRuss Gorby 	/* fill in the other fields */
4011bcd5abe2SRuss Gorby 	net->watchdog_timeo = GSM_NET_TX_TIMEOUT;
4012bcd5abe2SRuss Gorby 	net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
4013bcd5abe2SRuss Gorby 	net->type = ARPHRD_NONE;
4014bcd5abe2SRuss Gorby 	net->tx_queue_len = 10;
4015bcd5abe2SRuss Gorby }
4016bcd5abe2SRuss Gorby 
4017bcd5abe2SRuss Gorby 
4018bcd5abe2SRuss Gorby /* caller holds the dlci mutex */
gsm_destroy_network(struct gsm_dlci * dlci)4019bcd5abe2SRuss Gorby static void gsm_destroy_network(struct gsm_dlci *dlci)
4020bcd5abe2SRuss Gorby {
4021bcd5abe2SRuss Gorby 	struct gsm_mux_net *mux_net;
4022bcd5abe2SRuss Gorby 
4023d8ca4ecfSJiri Slaby 	pr_debug("destroy network interface\n");
4024bcd5abe2SRuss Gorby 	if (!dlci->net)
4025bcd5abe2SRuss Gorby 		return;
40265dbc32a8SJulia Lawall 	mux_net = netdev_priv(dlci->net);
40276ab8fba7SRuss Gorby 	muxnet_put(mux_net);
4028bcd5abe2SRuss Gorby }
4029bcd5abe2SRuss Gorby 
4030bcd5abe2SRuss Gorby 
4031bcd5abe2SRuss Gorby /* caller holds the dlci mutex */
gsm_create_network(struct gsm_dlci * dlci,struct gsm_netconfig * nc)4032bcd5abe2SRuss Gorby static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
4033bcd5abe2SRuss Gorby {
4034bcd5abe2SRuss Gorby 	char *netname;
4035bcd5abe2SRuss Gorby 	int retval = 0;
4036bcd5abe2SRuss Gorby 	struct net_device *net;
4037bcd5abe2SRuss Gorby 	struct gsm_mux_net *mux_net;
4038bcd5abe2SRuss Gorby 
4039bcd5abe2SRuss Gorby 	if (!capable(CAP_NET_ADMIN))
4040bcd5abe2SRuss Gorby 		return -EPERM;
4041bcd5abe2SRuss Gorby 
4042bcd5abe2SRuss Gorby 	/* Already in a non tty mode */
4043bcd5abe2SRuss Gorby 	if (dlci->adaption > 2)
4044bcd5abe2SRuss Gorby 		return -EBUSY;
4045bcd5abe2SRuss Gorby 
4046bcd5abe2SRuss Gorby 	if (nc->protocol != htons(ETH_P_IP))
4047bcd5abe2SRuss Gorby 		return -EPROTONOSUPPORT;
4048bcd5abe2SRuss Gorby 
4049bcd5abe2SRuss Gorby 	if (nc->adaption != 3 && nc->adaption != 4)
4050bcd5abe2SRuss Gorby 		return -EPROTONOSUPPORT;
4051bcd5abe2SRuss Gorby 
4052d8ca4ecfSJiri Slaby 	pr_debug("create network interface\n");
4053bcd5abe2SRuss Gorby 
4054bcd5abe2SRuss Gorby 	netname = "gsm%d";
4055bcd5abe2SRuss Gorby 	if (nc->if_name[0] != '\0')
4056bcd5abe2SRuss Gorby 		netname = nc->if_name;
4057c835a677STom Gundersen 	net = alloc_netdev(sizeof(struct gsm_mux_net), netname,
4058c835a677STom Gundersen 			   NET_NAME_UNKNOWN, gsm_mux_net_init);
4059bcd5abe2SRuss Gorby 	if (!net) {
4060d8ca4ecfSJiri Slaby 		pr_err("alloc_netdev failed\n");
4061bcd5abe2SRuss Gorby 		return -ENOMEM;
4062bcd5abe2SRuss Gorby 	}
40632ec7a802SDaniel Starke 	net->mtu = dlci->mtu;
40647a121247SDaniel Starke 	net->min_mtu = MIN_MTU;
40652ec7a802SDaniel Starke 	net->max_mtu = dlci->mtu;
40665dbc32a8SJulia Lawall 	mux_net = netdev_priv(net);
4067bcd5abe2SRuss Gorby 	mux_net->dlci = dlci;
4068bcd5abe2SRuss Gorby 	kref_init(&mux_net->ref);
4069bcd5abe2SRuss Gorby 	strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */
4070bcd5abe2SRuss Gorby 
4071bcd5abe2SRuss Gorby 	/* reconfigure dlci for network */
4072bcd5abe2SRuss Gorby 	dlci->prev_adaption = dlci->adaption;
4073bcd5abe2SRuss Gorby 	dlci->prev_data = dlci->data;
4074bcd5abe2SRuss Gorby 	dlci->adaption = nc->adaption;
4075bcd5abe2SRuss Gorby 	dlci->data = gsm_mux_rx_netchar;
4076bcd5abe2SRuss Gorby 	dlci->net = net;
4077bcd5abe2SRuss Gorby 
4078d8ca4ecfSJiri Slaby 	pr_debug("register netdev\n");
4079bcd5abe2SRuss Gorby 	retval = register_netdev(net);
4080bcd5abe2SRuss Gorby 	if (retval) {
4081bcd5abe2SRuss Gorby 		pr_err("network register fail %d\n", retval);
4082bcd5abe2SRuss Gorby 		dlci_net_free(dlci);
4083bcd5abe2SRuss Gorby 		return retval;
4084bcd5abe2SRuss Gorby 	}
4085bcd5abe2SRuss Gorby 	return net->ifindex;	/* return network index */
4086bcd5abe2SRuss Gorby }
408796fd7ce5SGreg Kroah-Hartman 
408896fd7ce5SGreg Kroah-Hartman /* Line discipline for real tty */
4089d3157b2cSLad, Prabhakar static struct tty_ldisc_ops tty_ldisc_packet = {
409096fd7ce5SGreg Kroah-Hartman 	.owner		 = THIS_MODULE,
4091fbadf70aSJiri Slaby 	.num		 = N_GSM0710,
409296fd7ce5SGreg Kroah-Hartman 	.name            = "n_gsm",
409396fd7ce5SGreg Kroah-Hartman 	.open            = gsmld_open,
409496fd7ce5SGreg Kroah-Hartman 	.close           = gsmld_close,
409596fd7ce5SGreg Kroah-Hartman 	.flush_buffer    = gsmld_flush_buffer,
409696fd7ce5SGreg Kroah-Hartman 	.read            = gsmld_read,
409796fd7ce5SGreg Kroah-Hartman 	.write           = gsmld_write,
409896fd7ce5SGreg Kroah-Hartman 	.ioctl           = gsmld_ioctl,
409996fd7ce5SGreg Kroah-Hartman 	.poll            = gsmld_poll,
410096fd7ce5SGreg Kroah-Hartman 	.receive_buf     = gsmld_receive_buf,
410196fd7ce5SGreg Kroah-Hartman 	.write_wakeup    = gsmld_write_wakeup
410296fd7ce5SGreg Kroah-Hartman };
410396fd7ce5SGreg Kroah-Hartman 
410496fd7ce5SGreg Kroah-Hartman /*
410596fd7ce5SGreg Kroah-Hartman  *	Virtual tty side
410696fd7ce5SGreg Kroah-Hartman  */
410796fd7ce5SGreg Kroah-Hartman 
4108c19ffe00SDaniel Starke /**
4109c19ffe00SDaniel Starke  *	gsm_modem_upd_via_data	-	send modem bits via convergence layer
4110c19ffe00SDaniel Starke  *	@dlci: channel
4111c19ffe00SDaniel Starke  *	@brk: break signal
4112c19ffe00SDaniel Starke  *
4113c19ffe00SDaniel Starke  *	Send an empty frame to signal mobile state changes and to transmit the
4114c19ffe00SDaniel Starke  *	break signal for adaption 2.
4115c19ffe00SDaniel Starke  */
4116c19ffe00SDaniel Starke 
gsm_modem_upd_via_data(struct gsm_dlci * dlci,u8 brk)4117c19ffe00SDaniel Starke static void gsm_modem_upd_via_data(struct gsm_dlci *dlci, u8 brk)
411896fd7ce5SGreg Kroah-Hartman {
4119c19ffe00SDaniel Starke 	struct gsm_mux *gsm = dlci->gsm;
4120acdab4cbSFedor Pchelkin 	unsigned long flags;
4121c19ffe00SDaniel Starke 
4122c19ffe00SDaniel Starke 	if (dlci->state != DLCI_OPEN || dlci->adaption != 2)
4123c19ffe00SDaniel Starke 		return;
4124c19ffe00SDaniel Starke 
4125acdab4cbSFedor Pchelkin 	spin_lock_irqsave(&gsm->tx_lock, flags);
4126c19ffe00SDaniel Starke 	gsm_dlci_modem_output(gsm, dlci, brk);
4127acdab4cbSFedor Pchelkin 	spin_unlock_irqrestore(&gsm->tx_lock, flags);
4128c19ffe00SDaniel Starke }
4129c19ffe00SDaniel Starke 
4130c19ffe00SDaniel Starke /**
4131c19ffe00SDaniel Starke  *	gsm_modem_upd_via_msc	-	send modem bits via control frame
4132c19ffe00SDaniel Starke  *	@dlci: channel
4133c19ffe00SDaniel Starke  *	@brk: break signal
4134c19ffe00SDaniel Starke  */
4135c19ffe00SDaniel Starke 
gsm_modem_upd_via_msc(struct gsm_dlci * dlci,u8 brk)4136c19ffe00SDaniel Starke static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
413796fd7ce5SGreg Kroah-Hartman {
4138398867f5SDaniel Starke 	u8 modembits[3];
413996fd7ce5SGreg Kroah-Hartman 	struct gsm_control *ctrl;
414096fd7ce5SGreg Kroah-Hartman 	int len = 2;
414196fd7ce5SGreg Kroah-Hartman 
414219fb0a66SDaniel Starke 	if (dlci->gsm->encoding != GSM_BASIC_OPT)
4143c19ffe00SDaniel Starke 		return 0;
414496fd7ce5SGreg Kroah-Hartman 
4145398867f5SDaniel Starke 	modembits[0] = (dlci->addr << 2) | 2 | EA;  /* DLCI, Valid, EA */
4146317f86afSDaniel Starke 	if (!brk) {
4147398867f5SDaniel Starke 		modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
4148317f86afSDaniel Starke 	} else {
4149317f86afSDaniel Starke 		modembits[1] = gsm_encode_modem(dlci) << 1;
4150398867f5SDaniel Starke 		modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */
415196fd7ce5SGreg Kroah-Hartman 		len++;
4152398867f5SDaniel Starke 	}
4153398867f5SDaniel Starke 	ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len);
415496fd7ce5SGreg Kroah-Hartman 	if (ctrl == NULL)
415596fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
415696fd7ce5SGreg Kroah-Hartman 	return gsm_control_wait(dlci->gsm, ctrl);
415796fd7ce5SGreg Kroah-Hartman }
415896fd7ce5SGreg Kroah-Hartman 
4159c19ffe00SDaniel Starke /**
4160c19ffe00SDaniel Starke  *	gsm_modem_update	-	send modem status line state
4161c19ffe00SDaniel Starke  *	@dlci: channel
4162c19ffe00SDaniel Starke  *	@brk: break signal
4163c19ffe00SDaniel Starke  */
4164c19ffe00SDaniel Starke 
gsm_modem_update(struct gsm_dlci * dlci,u8 brk)4165c19ffe00SDaniel Starke static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk)
4166c19ffe00SDaniel Starke {
4167ce4df903SDaniel Starke 	if (dlci->gsm->dead)
4168ce4df903SDaniel Starke 		return -EL2HLT;
4169c19ffe00SDaniel Starke 	if (dlci->adaption == 2) {
4170c19ffe00SDaniel Starke 		/* Send convergence layer type 2 empty data frame. */
4171c19ffe00SDaniel Starke 		gsm_modem_upd_via_data(dlci, brk);
4172c19ffe00SDaniel Starke 		return 0;
417319fb0a66SDaniel Starke 	} else if (dlci->gsm->encoding == GSM_BASIC_OPT) {
4174c19ffe00SDaniel Starke 		/* Send as MSC control message. */
4175c19ffe00SDaniel Starke 		return gsm_modem_upd_via_msc(dlci, brk);
4176c19ffe00SDaniel Starke 	}
4177c19ffe00SDaniel Starke 
4178c19ffe00SDaniel Starke 	/* Modem status lines are not supported. */
4179c19ffe00SDaniel Starke 	return -EPROTONOSUPPORT;
4180c19ffe00SDaniel Starke }
4181c19ffe00SDaniel Starke 
4182684ae4f9SDaniel Starke /**
4183684ae4f9SDaniel Starke  * gsm_wait_modem_change - wait for modem status line change
4184684ae4f9SDaniel Starke  * @dlci: channel
4185684ae4f9SDaniel Starke  * @mask: modem status line bits
4186684ae4f9SDaniel Starke  *
4187684ae4f9SDaniel Starke  * The function returns if:
4188684ae4f9SDaniel Starke  * - any given modem status line bit changed
4189684ae4f9SDaniel Starke  * - the wait event function got interrupted (e.g. by a signal)
4190684ae4f9SDaniel Starke  * - the underlying DLCI was closed
4191684ae4f9SDaniel Starke  * - the underlying ldisc device was removed
4192684ae4f9SDaniel Starke  */
gsm_wait_modem_change(struct gsm_dlci * dlci,u32 mask)4193684ae4f9SDaniel Starke static int gsm_wait_modem_change(struct gsm_dlci *dlci, u32 mask)
4194684ae4f9SDaniel Starke {
4195684ae4f9SDaniel Starke 	struct gsm_mux *gsm = dlci->gsm;
4196684ae4f9SDaniel Starke 	u32 old = dlci->modem_rx;
4197684ae4f9SDaniel Starke 	int ret;
4198684ae4f9SDaniel Starke 
4199684ae4f9SDaniel Starke 	ret = wait_event_interruptible(gsm->event, gsm->dead ||
4200684ae4f9SDaniel Starke 				       dlci->state != DLCI_OPEN ||
4201684ae4f9SDaniel Starke 				       (old ^ dlci->modem_rx) & mask);
4202684ae4f9SDaniel Starke 	if (gsm->dead)
4203684ae4f9SDaniel Starke 		return -ENODEV;
4204684ae4f9SDaniel Starke 	if (dlci->state != DLCI_OPEN)
4205684ae4f9SDaniel Starke 		return -EL2NSYNC;
4206684ae4f9SDaniel Starke 	return ret;
4207684ae4f9SDaniel Starke }
4208684ae4f9SDaniel Starke 
gsm_carrier_raised(struct tty_port * port)4209b300fb26SIlpo Järvinen static bool gsm_carrier_raised(struct tty_port *port)
421096fd7ce5SGreg Kroah-Hartman {
421196fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
4212b2d89ad9STony Lindgren 	struct gsm_mux *gsm = dlci->gsm;
4213b2d89ad9STony Lindgren 
421496fd7ce5SGreg Kroah-Hartman 	/* Not yet open so no carrier info */
421596fd7ce5SGreg Kroah-Hartman 	if (dlci->state != DLCI_OPEN)
4216b300fb26SIlpo Järvinen 		return false;
4217c07da737SDaniel Starke 	if (debug & DBG_CD_ON)
4218b300fb26SIlpo Järvinen 		return true;
4219b2d89ad9STony Lindgren 
4220b2d89ad9STony Lindgren 	/*
4221b2d89ad9STony Lindgren 	 * Basic mode with control channel in ADM mode may not respond
4222b2d89ad9STony Lindgren 	 * to CMD_MSC at all and modem_rx is empty.
4223b2d89ad9STony Lindgren 	 */
422419fb0a66SDaniel Starke 	if (gsm->encoding == GSM_BASIC_OPT &&
422519fb0a66SDaniel Starke 	    gsm->dlci[0]->mode == DLCI_MODE_ADM && !dlci->modem_rx)
4226b300fb26SIlpo Järvinen 		return true;
4227b2d89ad9STony Lindgren 
422896fd7ce5SGreg Kroah-Hartman 	return dlci->modem_rx & TIOCM_CD;
422996fd7ce5SGreg Kroah-Hartman }
423096fd7ce5SGreg Kroah-Hartman 
gsm_dtr_rts(struct tty_port * port,bool active)42315701cb8bSIlpo Järvinen static void gsm_dtr_rts(struct tty_port *port, bool active)
423296fd7ce5SGreg Kroah-Hartman {
423396fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
423496fd7ce5SGreg Kroah-Hartman 	unsigned int modem_tx = dlci->modem_tx;
42355701cb8bSIlpo Järvinen 	if (active)
423696fd7ce5SGreg Kroah-Hartman 		modem_tx |= TIOCM_DTR | TIOCM_RTS;
423796fd7ce5SGreg Kroah-Hartman 	else
423896fd7ce5SGreg Kroah-Hartman 		modem_tx &= ~(TIOCM_DTR | TIOCM_RTS);
423996fd7ce5SGreg Kroah-Hartman 	if (modem_tx != dlci->modem_tx) {
424096fd7ce5SGreg Kroah-Hartman 		dlci->modem_tx = modem_tx;
4241c19ffe00SDaniel Starke 		gsm_modem_update(dlci, 0);
424296fd7ce5SGreg Kroah-Hartman 	}
424396fd7ce5SGreg Kroah-Hartman }
424496fd7ce5SGreg Kroah-Hartman 
424596fd7ce5SGreg Kroah-Hartman static const struct tty_port_operations gsm_port_ops = {
424696fd7ce5SGreg Kroah-Hartman 	.carrier_raised = gsm_carrier_raised,
424796fd7ce5SGreg Kroah-Hartman 	.dtr_rts = gsm_dtr_rts,
42489a8e62bcSJiri Slaby 	.destruct = gsm_dlci_free,
424996fd7ce5SGreg Kroah-Hartman };
425096fd7ce5SGreg Kroah-Hartman 
gsmtty_install(struct tty_driver * driver,struct tty_struct * tty)425186176ed9SJiri Slaby static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
425296fd7ce5SGreg Kroah-Hartman {
425396fd7ce5SGreg Kroah-Hartman 	struct gsm_mux *gsm;
425496fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci;
425596fd7ce5SGreg Kroah-Hartman 	unsigned int line = tty->index;
425643a9e710SMartin Hundebøll 	unsigned int mux = mux_line_to_num(line);
425786176ed9SJiri Slaby 	bool alloc = false;
425886176ed9SJiri Slaby 	int ret;
425996fd7ce5SGreg Kroah-Hartman 
426096fd7ce5SGreg Kroah-Hartman 	line = line & 0x3F;
426196fd7ce5SGreg Kroah-Hartman 
426296fd7ce5SGreg Kroah-Hartman 	if (mux >= MAX_MUX)
426396fd7ce5SGreg Kroah-Hartman 		return -ENXIO;
426496fd7ce5SGreg Kroah-Hartman 	/* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */
426596fd7ce5SGreg Kroah-Hartman 	if (gsm_mux[mux] == NULL)
426696fd7ce5SGreg Kroah-Hartman 		return -EUNATCH;
426796fd7ce5SGreg Kroah-Hartman 	if (line == 0 || line > 61)	/* 62/63 reserved */
426896fd7ce5SGreg Kroah-Hartman 		return -ECHRNG;
426996fd7ce5SGreg Kroah-Hartman 	gsm = gsm_mux[mux];
427096fd7ce5SGreg Kroah-Hartman 	if (gsm->dead)
427196fd7ce5SGreg Kroah-Hartman 		return -EL2HLT;
4272f3c909b4SAldo Iljazi 	/* If DLCI 0 is not yet fully open return an error.
4273f3c909b4SAldo Iljazi 	This is ok from a locking
4274f3c909b4SAldo Iljazi 	perspective as we don't have to worry about this
4275f3c909b4SAldo Iljazi 	if DLCI0 is lost */
4276dfabf7ffSChao Bi 	mutex_lock(&gsm->mutex);
4277dfabf7ffSChao Bi 	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) {
4278dfabf7ffSChao Bi 		mutex_unlock(&gsm->mutex);
42797e8ac7b2Sxiaojin 		return -EL2NSYNC;
4280dfabf7ffSChao Bi 	}
428196fd7ce5SGreg Kroah-Hartman 	dlci = gsm->dlci[line];
428286176ed9SJiri Slaby 	if (dlci == NULL) {
428386176ed9SJiri Slaby 		alloc = true;
428496fd7ce5SGreg Kroah-Hartman 		dlci = gsm_dlci_alloc(gsm, line);
428586176ed9SJiri Slaby 	}
4286dfabf7ffSChao Bi 	if (dlci == NULL) {
4287dfabf7ffSChao Bi 		mutex_unlock(&gsm->mutex);
428896fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
4289dfabf7ffSChao Bi 	}
429086176ed9SJiri Slaby 	ret = tty_port_install(&dlci->port, driver, tty);
429186176ed9SJiri Slaby 	if (ret) {
429286176ed9SJiri Slaby 		if (alloc)
429386176ed9SJiri Slaby 			dlci_put(dlci);
4294dfabf7ffSChao Bi 		mutex_unlock(&gsm->mutex);
429586176ed9SJiri Slaby 		return ret;
429686176ed9SJiri Slaby 	}
429786176ed9SJiri Slaby 
4298dfabf7ffSChao Bi 	dlci_get(dlci);
4299dfabf7ffSChao Bi 	dlci_get(gsm->dlci[0]);
4300dfabf7ffSChao Bi 	mux_get(gsm);
430196fd7ce5SGreg Kroah-Hartman 	tty->driver_data = dlci;
4302dfabf7ffSChao Bi 	mutex_unlock(&gsm->mutex);
430386176ed9SJiri Slaby 
430486176ed9SJiri Slaby 	return 0;
430586176ed9SJiri Slaby }
430686176ed9SJiri Slaby 
gsmtty_open(struct tty_struct * tty,struct file * filp)430786176ed9SJiri Slaby static int gsmtty_open(struct tty_struct *tty, struct file *filp)
430886176ed9SJiri Slaby {
430986176ed9SJiri Slaby 	struct gsm_dlci *dlci = tty->driver_data;
431086176ed9SJiri Slaby 	struct tty_port *port = &dlci->port;
431186176ed9SJiri Slaby 
431286176ed9SJiri Slaby 	port->count++;
431396fd7ce5SGreg Kroah-Hartman 	tty_port_tty_set(port, tty);
431496fd7ce5SGreg Kroah-Hartman 
431596fd7ce5SGreg Kroah-Hartman 	dlci->modem_rx = 0;
431696fd7ce5SGreg Kroah-Hartman 	/* We could in theory open and close before we wait - eg if we get
431796fd7ce5SGreg Kroah-Hartman 	   a DM straight back. This is ok as that will have caused a hangup */
4318515be7baSIlpo Järvinen 	tty_port_set_initialized(port, true);
431996fd7ce5SGreg Kroah-Hartman 	/* Start sending off SABM messages */
43204ca58966SDaniel Starke 	if (!dlci->gsm->wait_config) {
43214ca58966SDaniel Starke 		/* Start sending off SABM messages */
43224ca58966SDaniel Starke 		if (dlci->gsm->initiator)
432396fd7ce5SGreg Kroah-Hartman 			gsm_dlci_begin_open(dlci);
4324ac77f007SDaniel Starke 		else
4325ac77f007SDaniel Starke 			gsm_dlci_set_opening(dlci);
43264ca58966SDaniel Starke 	} else {
43274ca58966SDaniel Starke 		gsm_dlci_set_wait_config(dlci);
43284ca58966SDaniel Starke 	}
432996fd7ce5SGreg Kroah-Hartman 	/* And wait for virtual carrier */
433096fd7ce5SGreg Kroah-Hartman 	return tty_port_block_til_ready(port, tty, filp);
433196fd7ce5SGreg Kroah-Hartman }
433296fd7ce5SGreg Kroah-Hartman 
gsmtty_close(struct tty_struct * tty,struct file * filp)433396fd7ce5SGreg Kroah-Hartman static void gsmtty_close(struct tty_struct *tty, struct file *filp)
433496fd7ce5SGreg Kroah-Hartman {
433596fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
43366ab8fba7SRuss Gorby 
433796fd7ce5SGreg Kroah-Hartman 	if (dlci == NULL)
433896fd7ce5SGreg Kroah-Hartman 		return;
43394d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
43404d9b1090SDirkjan Bussink 		return;
4341bcd5abe2SRuss Gorby 	mutex_lock(&dlci->mutex);
4342bcd5abe2SRuss Gorby 	gsm_destroy_network(dlci);
4343bcd5abe2SRuss Gorby 	mutex_unlock(&dlci->mutex);
434496fd7ce5SGreg Kroah-Hartman 	if (tty_port_close_start(&dlci->port, tty, filp) == 0)
4345dfabf7ffSChao Bi 		return;
434696fd7ce5SGreg Kroah-Hartman 	gsm_dlci_begin_close(dlci);
4347d41861caSPeter Hurley 	if (tty_port_initialized(&dlci->port) && C_HUPCL(tty))
4348957dacaeSJohan Hovold 		tty_port_lower_dtr_rts(&dlci->port);
434996fd7ce5SGreg Kroah-Hartman 	tty_port_close_end(&dlci->port, tty);
435096fd7ce5SGreg Kroah-Hartman 	tty_port_tty_set(&dlci->port, NULL);
4351dfabf7ffSChao Bi 	return;
435296fd7ce5SGreg Kroah-Hartman }
435396fd7ce5SGreg Kroah-Hartman 
gsmtty_hangup(struct tty_struct * tty)435496fd7ce5SGreg Kroah-Hartman static void gsmtty_hangup(struct tty_struct *tty)
435596fd7ce5SGreg Kroah-Hartman {
435696fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
43574d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
43584d9b1090SDirkjan Bussink 		return;
435996fd7ce5SGreg Kroah-Hartman 	tty_port_hangup(&dlci->port);
436096fd7ce5SGreg Kroah-Hartman 	gsm_dlci_begin_close(dlci);
436196fd7ce5SGreg Kroah-Hartman }
436296fd7ce5SGreg Kroah-Hartman 
gsmtty_write(struct tty_struct * tty,const u8 * buf,size_t len)436395713967SJiri Slaby (SUSE) static ssize_t gsmtty_write(struct tty_struct *tty, const u8 *buf, size_t len)
436496fd7ce5SGreg Kroah-Hartman {
43654d9b1090SDirkjan Bussink 	int sent;
436696fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
43674d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
43684d9b1090SDirkjan Bussink 		return -EINVAL;
436996fd7ce5SGreg Kroah-Hartman 	/* Stuff the bytes into the fifo queue */
4370036bca1fSJiri Slaby 	sent = kfifo_in_locked(&dlci->fifo, buf, len, &dlci->lock);
437196fd7ce5SGreg Kroah-Hartman 	/* Need to kick the channel */
437296fd7ce5SGreg Kroah-Hartman 	gsm_dlci_data_kick(dlci);
437396fd7ce5SGreg Kroah-Hartman 	return sent;
437496fd7ce5SGreg Kroah-Hartman }
437596fd7ce5SGreg Kroah-Hartman 
gsmtty_write_room(struct tty_struct * tty)437603b3b1a2SJiri Slaby static unsigned int gsmtty_write_room(struct tty_struct *tty)
437796fd7ce5SGreg Kroah-Hartman {
437896fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
43794d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
43806bfbfcfcSJiri Slaby 		return 0;
43819361ebfbSDaniel Starke 	return kfifo_avail(&dlci->fifo);
438296fd7ce5SGreg Kroah-Hartman }
438396fd7ce5SGreg Kroah-Hartman 
gsmtty_chars_in_buffer(struct tty_struct * tty)4384fff4ef17SJiri Slaby static unsigned int gsmtty_chars_in_buffer(struct tty_struct *tty)
438596fd7ce5SGreg Kroah-Hartman {
438696fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
43874d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
438810eb63e5SJiri Slaby 		return 0;
4389036bca1fSJiri Slaby 	return kfifo_len(&dlci->fifo);
439096fd7ce5SGreg Kroah-Hartman }
439196fd7ce5SGreg Kroah-Hartman 
gsmtty_flush_buffer(struct tty_struct * tty)439296fd7ce5SGreg Kroah-Hartman static void gsmtty_flush_buffer(struct tty_struct *tty)
439396fd7ce5SGreg Kroah-Hartman {
439496fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
439573029a4dSDaniel Starke 	unsigned long flags;
439673029a4dSDaniel Starke 
43974d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
43984d9b1090SDirkjan Bussink 		return;
439996fd7ce5SGreg Kroah-Hartman 	/* Caution needed: If we implement reliable transport classes
440096fd7ce5SGreg Kroah-Hartman 	   then the data being transmitted can't simply be junked once
440196fd7ce5SGreg Kroah-Hartman 	   it has first hit the stack. Until then we can just blow it
440296fd7ce5SGreg Kroah-Hartman 	   away */
440373029a4dSDaniel Starke 	spin_lock_irqsave(&dlci->lock, flags);
4404036bca1fSJiri Slaby 	kfifo_reset(&dlci->fifo);
440573029a4dSDaniel Starke 	spin_unlock_irqrestore(&dlci->lock, flags);
440696fd7ce5SGreg Kroah-Hartman 	/* Need to unhook this DLCI from the transmit queue logic */
440796fd7ce5SGreg Kroah-Hartman }
440896fd7ce5SGreg Kroah-Hartman 
gsmtty_wait_until_sent(struct tty_struct * tty,int timeout)440996fd7ce5SGreg Kroah-Hartman static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout)
441096fd7ce5SGreg Kroah-Hartman {
441196fd7ce5SGreg Kroah-Hartman 	/* The FIFO handles the queue so the kernel will do the right
441296fd7ce5SGreg Kroah-Hartman 	   thing waiting on chars_in_buffer before calling us. No work
441396fd7ce5SGreg Kroah-Hartman 	   to do here */
441496fd7ce5SGreg Kroah-Hartman }
441596fd7ce5SGreg Kroah-Hartman 
gsmtty_tiocmget(struct tty_struct * tty)441660b33c13SAlan Cox static int gsmtty_tiocmget(struct tty_struct *tty)
441796fd7ce5SGreg Kroah-Hartman {
441896fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
44194d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
44204d9b1090SDirkjan Bussink 		return -EINVAL;
442196fd7ce5SGreg Kroah-Hartman 	return dlci->modem_rx;
442296fd7ce5SGreg Kroah-Hartman }
442396fd7ce5SGreg Kroah-Hartman 
gsmtty_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)442420b9d177SAlan Cox static int gsmtty_tiocmset(struct tty_struct *tty,
442596fd7ce5SGreg Kroah-Hartman 	unsigned int set, unsigned int clear)
442696fd7ce5SGreg Kroah-Hartman {
442796fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
442896fd7ce5SGreg Kroah-Hartman 	unsigned int modem_tx = dlci->modem_tx;
442996fd7ce5SGreg Kroah-Hartman 
44304d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
44314d9b1090SDirkjan Bussink 		return -EINVAL;
4432cf16807bSNikola Diklic-Perin 	modem_tx &= ~clear;
443396fd7ce5SGreg Kroah-Hartman 	modem_tx |= set;
443496fd7ce5SGreg Kroah-Hartman 
443596fd7ce5SGreg Kroah-Hartman 	if (modem_tx != dlci->modem_tx) {
443696fd7ce5SGreg Kroah-Hartman 		dlci->modem_tx = modem_tx;
4437c19ffe00SDaniel Starke 		return gsm_modem_update(dlci, 0);
443896fd7ce5SGreg Kroah-Hartman 	}
443996fd7ce5SGreg Kroah-Hartman 	return 0;
444096fd7ce5SGreg Kroah-Hartman }
444196fd7ce5SGreg Kroah-Hartman 
444296fd7ce5SGreg Kroah-Hartman 
gsmtty_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)44436caa76b7SAlan Cox static int gsmtty_ioctl(struct tty_struct *tty,
444496fd7ce5SGreg Kroah-Hartman 			unsigned int cmd, unsigned long arg)
444596fd7ce5SGreg Kroah-Hartman {
4446bcd5abe2SRuss Gorby 	struct gsm_dlci *dlci = tty->driver_data;
4447bcd5abe2SRuss Gorby 	struct gsm_netconfig nc;
44484ca58966SDaniel Starke 	struct gsm_dlci_config dc;
4449bcd5abe2SRuss Gorby 	int index;
4450bcd5abe2SRuss Gorby 
44514d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
44524d9b1090SDirkjan Bussink 		return -EINVAL;
4453bcd5abe2SRuss Gorby 	switch (cmd) {
4454bcd5abe2SRuss Gorby 	case GSMIOC_ENABLE_NET:
4455bcd5abe2SRuss Gorby 		if (copy_from_user(&nc, (void __user *)arg, sizeof(nc)))
4456bcd5abe2SRuss Gorby 			return -EFAULT;
4457bcd5abe2SRuss Gorby 		nc.if_name[IFNAMSIZ-1] = '\0';
4458bcd5abe2SRuss Gorby 		/* return net interface index or error code */
4459bcd5abe2SRuss Gorby 		mutex_lock(&dlci->mutex);
4460bcd5abe2SRuss Gorby 		index = gsm_create_network(dlci, &nc);
4461bcd5abe2SRuss Gorby 		mutex_unlock(&dlci->mutex);
4462bcd5abe2SRuss Gorby 		if (copy_to_user((void __user *)arg, &nc, sizeof(nc)))
4463bcd5abe2SRuss Gorby 			return -EFAULT;
4464bcd5abe2SRuss Gorby 		return index;
4465bcd5abe2SRuss Gorby 	case GSMIOC_DISABLE_NET:
4466bcd5abe2SRuss Gorby 		if (!capable(CAP_NET_ADMIN))
4467bcd5abe2SRuss Gorby 			return -EPERM;
4468bcd5abe2SRuss Gorby 		mutex_lock(&dlci->mutex);
4469bcd5abe2SRuss Gorby 		gsm_destroy_network(dlci);
4470bcd5abe2SRuss Gorby 		mutex_unlock(&dlci->mutex);
4471bcd5abe2SRuss Gorby 		return 0;
44724ca58966SDaniel Starke 	case GSMIOC_GETCONF_DLCI:
44734ca58966SDaniel Starke 		if (copy_from_user(&dc, (void __user *)arg, sizeof(dc)))
44744ca58966SDaniel Starke 			return -EFAULT;
44754ca58966SDaniel Starke 		if (dc.channel != dlci->addr)
44764ca58966SDaniel Starke 			return -EPERM;
44774ca58966SDaniel Starke 		gsm_dlci_copy_config_values(dlci, &dc);
44784ca58966SDaniel Starke 		if (copy_to_user((void __user *)arg, &dc, sizeof(dc)))
44794ca58966SDaniel Starke 			return -EFAULT;
44804ca58966SDaniel Starke 		return 0;
44814ca58966SDaniel Starke 	case GSMIOC_SETCONF_DLCI:
44824ca58966SDaniel Starke 		if (copy_from_user(&dc, (void __user *)arg, sizeof(dc)))
44834ca58966SDaniel Starke 			return -EFAULT;
44844ca58966SDaniel Starke 		if (dc.channel >= NUM_DLCI)
44854ca58966SDaniel Starke 			return -EINVAL;
44864ca58966SDaniel Starke 		if (dc.channel != 0 && dc.channel != dlci->addr)
44874ca58966SDaniel Starke 			return -EPERM;
44884ca58966SDaniel Starke 		return gsm_dlci_config(dlci, &dc, 1);
4489684ae4f9SDaniel Starke 	case TIOCMIWAIT:
4490684ae4f9SDaniel Starke 		return gsm_wait_modem_change(dlci, (u32)arg);
4491bcd5abe2SRuss Gorby 	default:
449296fd7ce5SGreg Kroah-Hartman 		return -ENOIOCTLCMD;
449396fd7ce5SGreg Kroah-Hartman 	}
4494bcd5abe2SRuss Gorby }
449596fd7ce5SGreg Kroah-Hartman 
gsmtty_set_termios(struct tty_struct * tty,const struct ktermios * old)4496a8c11c15SIlpo Järvinen static void gsmtty_set_termios(struct tty_struct *tty,
4497a8c11c15SIlpo Järvinen 			       const struct ktermios *old)
449896fd7ce5SGreg Kroah-Hartman {
44994d9b1090SDirkjan Bussink 	struct gsm_dlci *dlci = tty->driver_data;
45004d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
45014d9b1090SDirkjan Bussink 		return;
450296fd7ce5SGreg Kroah-Hartman 	/* For the moment its fixed. In actual fact the speed information
450396fd7ce5SGreg Kroah-Hartman 	   for the virtual channel can be propogated in both directions by
450496fd7ce5SGreg Kroah-Hartman 	   the RPN control message. This however rapidly gets nasty as we
450596fd7ce5SGreg Kroah-Hartman 	   then have to remap modem signals each way according to whether
450696fd7ce5SGreg Kroah-Hartman 	   our virtual cable is null modem etc .. */
4507adc8d746SAlan Cox 	tty_termios_copy_hw(&tty->termios, old);
450896fd7ce5SGreg Kroah-Hartman }
450996fd7ce5SGreg Kroah-Hartman 
gsmtty_throttle(struct tty_struct * tty)451096fd7ce5SGreg Kroah-Hartman static void gsmtty_throttle(struct tty_struct *tty)
451196fd7ce5SGreg Kroah-Hartman {
451296fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
45134d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
45144d9b1090SDirkjan Bussink 		return;
45159db276f8SPeter Hurley 	if (C_CRTSCTS(tty))
4516c19d9354Sdaniel.starke@siemens.com 		dlci->modem_tx &= ~TIOCM_RTS;
4517e9360b9aSJiri Slaby 	dlci->throttled = true;
4518c19d9354Sdaniel.starke@siemens.com 	/* Send an MSC with RTS cleared */
4519c19ffe00SDaniel Starke 	gsm_modem_update(dlci, 0);
452096fd7ce5SGreg Kroah-Hartman }
452196fd7ce5SGreg Kroah-Hartman 
gsmtty_unthrottle(struct tty_struct * tty)452296fd7ce5SGreg Kroah-Hartman static void gsmtty_unthrottle(struct tty_struct *tty)
452396fd7ce5SGreg Kroah-Hartman {
452496fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
45254d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
45264d9b1090SDirkjan Bussink 		return;
45279db276f8SPeter Hurley 	if (C_CRTSCTS(tty))
4528c19d9354Sdaniel.starke@siemens.com 		dlci->modem_tx |= TIOCM_RTS;
4529e9360b9aSJiri Slaby 	dlci->throttled = false;
4530c19d9354Sdaniel.starke@siemens.com 	/* Send an MSC with RTS set */
4531c19ffe00SDaniel Starke 	gsm_modem_update(dlci, 0);
453296fd7ce5SGreg Kroah-Hartman }
453396fd7ce5SGreg Kroah-Hartman 
gsmtty_break_ctl(struct tty_struct * tty,int state)453496fd7ce5SGreg Kroah-Hartman static int gsmtty_break_ctl(struct tty_struct *tty, int state)
453596fd7ce5SGreg Kroah-Hartman {
453696fd7ce5SGreg Kroah-Hartman 	struct gsm_dlci *dlci = tty->driver_data;
453796fd7ce5SGreg Kroah-Hartman 	int encode = 0;	/* Off */
45384d9b1090SDirkjan Bussink 	if (dlci->state == DLCI_CLOSED)
45394d9b1090SDirkjan Bussink 		return -EINVAL;
454096fd7ce5SGreg Kroah-Hartman 
454196fd7ce5SGreg Kroah-Hartman 	if (state == -1)	/* "On indefinitely" - we can't encode this
454296fd7ce5SGreg Kroah-Hartman 				    properly */
454396fd7ce5SGreg Kroah-Hartman 		encode = 0x0F;
454496fd7ce5SGreg Kroah-Hartman 	else if (state > 0) {
454596fd7ce5SGreg Kroah-Hartman 		encode = state / 200;	/* mS to encoding */
454696fd7ce5SGreg Kroah-Hartman 		if (encode > 0x0F)
454796fd7ce5SGreg Kroah-Hartman 			encode = 0x0F;	/* Best effort */
454896fd7ce5SGreg Kroah-Hartman 	}
4549c19ffe00SDaniel Starke 	return gsm_modem_update(dlci, encode);
455096fd7ce5SGreg Kroah-Hartman }
455196fd7ce5SGreg Kroah-Hartman 
gsmtty_cleanup(struct tty_struct * tty)45528f9cfeedSPan Xinhui static void gsmtty_cleanup(struct tty_struct *tty)
4553dfabf7ffSChao Bi {
4554dfabf7ffSChao Bi 	struct gsm_dlci *dlci = tty->driver_data;
4555dfabf7ffSChao Bi 	struct gsm_mux *gsm = dlci->gsm;
4556dfabf7ffSChao Bi 
4557dfabf7ffSChao Bi 	dlci_put(dlci);
4558dfabf7ffSChao Bi 	dlci_put(gsm->dlci[0]);
4559dfabf7ffSChao Bi 	mux_put(gsm);
4560dfabf7ffSChao Bi }
456196fd7ce5SGreg Kroah-Hartman 
456296fd7ce5SGreg Kroah-Hartman /* Virtual ttys for the demux */
456396fd7ce5SGreg Kroah-Hartman static const struct tty_operations gsmtty_ops = {
456486176ed9SJiri Slaby 	.install		= gsmtty_install,
456596fd7ce5SGreg Kroah-Hartman 	.open			= gsmtty_open,
456696fd7ce5SGreg Kroah-Hartman 	.close			= gsmtty_close,
456796fd7ce5SGreg Kroah-Hartman 	.write			= gsmtty_write,
456896fd7ce5SGreg Kroah-Hartman 	.write_room		= gsmtty_write_room,
456996fd7ce5SGreg Kroah-Hartman 	.chars_in_buffer	= gsmtty_chars_in_buffer,
457096fd7ce5SGreg Kroah-Hartman 	.flush_buffer		= gsmtty_flush_buffer,
457196fd7ce5SGreg Kroah-Hartman 	.ioctl			= gsmtty_ioctl,
457296fd7ce5SGreg Kroah-Hartman 	.throttle		= gsmtty_throttle,
457396fd7ce5SGreg Kroah-Hartman 	.unthrottle		= gsmtty_unthrottle,
457496fd7ce5SGreg Kroah-Hartman 	.set_termios		= gsmtty_set_termios,
457596fd7ce5SGreg Kroah-Hartman 	.hangup			= gsmtty_hangup,
457696fd7ce5SGreg Kroah-Hartman 	.wait_until_sent	= gsmtty_wait_until_sent,
457796fd7ce5SGreg Kroah-Hartman 	.tiocmget		= gsmtty_tiocmget,
457896fd7ce5SGreg Kroah-Hartman 	.tiocmset		= gsmtty_tiocmset,
457996fd7ce5SGreg Kroah-Hartman 	.break_ctl		= gsmtty_break_ctl,
45808f9cfeedSPan Xinhui 	.cleanup		= gsmtty_cleanup,
458196fd7ce5SGreg Kroah-Hartman };
458296fd7ce5SGreg Kroah-Hartman 
458396fd7ce5SGreg Kroah-Hartman 
458496fd7ce5SGreg Kroah-Hartman 
gsm_init(void)458596fd7ce5SGreg Kroah-Hartman static int __init gsm_init(void)
458696fd7ce5SGreg Kroah-Hartman {
458796fd7ce5SGreg Kroah-Hartman 	/* Fill in our line protocol discipline, and register it */
4588fbadf70aSJiri Slaby 	int status = tty_register_ldisc(&tty_ldisc_packet);
458996fd7ce5SGreg Kroah-Hartman 	if (status != 0) {
45905f9a31d6SAlan Cox 		pr_err("n_gsm: can't register line discipline (err = %d)\n",
45915f9a31d6SAlan Cox 								status);
459296fd7ce5SGreg Kroah-Hartman 		return status;
459396fd7ce5SGreg Kroah-Hartman 	}
459496fd7ce5SGreg Kroah-Hartman 
4595796492deSDaniel Starke 	gsm_tty_driver = tty_alloc_driver(GSM_TTY_MINORS, TTY_DRIVER_REAL_RAW |
459639b7b42bSJiri Slaby 			TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK);
459739b7b42bSJiri Slaby 	if (IS_ERR(gsm_tty_driver)) {
45985f9a31d6SAlan Cox 		pr_err("gsm_init: tty allocation failed.\n");
459939b7b42bSJiri Slaby 		status = PTR_ERR(gsm_tty_driver);
4600839e0f22SJiri Slaby 		goto err_unreg_ldisc;
460196fd7ce5SGreg Kroah-Hartman 	}
460296fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->driver_name	= "gsmtty";
460396fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->name		= "gsmtty";
460496fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->major		= 0;	/* Dynamic */
460596fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->minor_start	= 0;
460696fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->type		= TTY_DRIVER_TYPE_SERIAL;
460796fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->subtype	= SERIAL_TYPE_NORMAL;
460896fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->init_termios	= tty_std_termios;
460996fd7ce5SGreg Kroah-Hartman 	/* Fixme */
461096fd7ce5SGreg Kroah-Hartman 	gsm_tty_driver->init_termios.c_lflag &= ~ECHO;
461196fd7ce5SGreg Kroah-Hartman 	tty_set_operations(gsm_tty_driver, &gsmtty_ops);
461296fd7ce5SGreg Kroah-Hartman 
461396fd7ce5SGreg Kroah-Hartman 	if (tty_register_driver(gsm_tty_driver)) {
46145f9a31d6SAlan Cox 		pr_err("gsm_init: tty registration failed.\n");
4615839e0f22SJiri Slaby 		status = -EBUSY;
4616839e0f22SJiri Slaby 		goto err_put_driver;
461796fd7ce5SGreg Kroah-Hartman 	}
46185f9a31d6SAlan Cox 	pr_debug("gsm_init: loaded as %d,%d.\n",
46195f9a31d6SAlan Cox 			gsm_tty_driver->major, gsm_tty_driver->minor_start);
462096fd7ce5SGreg Kroah-Hartman 	return 0;
4621839e0f22SJiri Slaby err_put_driver:
46229f90a4ddSJiri Slaby 	tty_driver_kref_put(gsm_tty_driver);
4623839e0f22SJiri Slaby err_unreg_ldisc:
4624f81ee8b8SJiri Slaby 	tty_unregister_ldisc(&tty_ldisc_packet);
4625839e0f22SJiri Slaby 	return status;
462696fd7ce5SGreg Kroah-Hartman }
462796fd7ce5SGreg Kroah-Hartman 
gsm_exit(void)462896fd7ce5SGreg Kroah-Hartman static void __exit gsm_exit(void)
462996fd7ce5SGreg Kroah-Hartman {
4630357a6a87SJiri Slaby 	tty_unregister_ldisc(&tty_ldisc_packet);
463196fd7ce5SGreg Kroah-Hartman 	tty_unregister_driver(gsm_tty_driver);
46329f90a4ddSJiri Slaby 	tty_driver_kref_put(gsm_tty_driver);
463396fd7ce5SGreg Kroah-Hartman }
463496fd7ce5SGreg Kroah-Hartman 
463596fd7ce5SGreg Kroah-Hartman module_init(gsm_init);
463696fd7ce5SGreg Kroah-Hartman module_exit(gsm_exit);
463796fd7ce5SGreg Kroah-Hartman 
463896fd7ce5SGreg Kroah-Hartman 
463996fd7ce5SGreg Kroah-Hartman MODULE_LICENSE("GPL");
464096fd7ce5SGreg Kroah-Hartman MODULE_ALIAS_LDISC(N_GSM0710);
4641