18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * n_gsm.c GSM 0710 tty multiplexor
48c2ecf20Sopenharmony_ci * Copyright (c) 2009/10 Intel Corporation
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	* THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE *
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * TO DO:
98c2ecf20Sopenharmony_ci *	Mostly done:	ioctls for setting modes/timing
108c2ecf20Sopenharmony_ci *	Partly done:	hooks so you can pull off frames to non tty devs
118c2ecf20Sopenharmony_ci *	Restart DLCI 0 when it closes ?
128c2ecf20Sopenharmony_ci *	Improve the tx engine
138c2ecf20Sopenharmony_ci *	Resolve tx side locking by adding a queue_head and routing
148c2ecf20Sopenharmony_ci *		all control traffic via it
158c2ecf20Sopenharmony_ci *	General tidy/document
168c2ecf20Sopenharmony_ci *	Review the locking/move to refcounts more (mux now moved to an
178c2ecf20Sopenharmony_ci *		alloc/free model ready)
188c2ecf20Sopenharmony_ci *	Use newest tty open/close port helpers and install hooks
198c2ecf20Sopenharmony_ci *	What to do about power functions ?
208c2ecf20Sopenharmony_ci *	Termios setting and negotiation
218c2ecf20Sopenharmony_ci *	Do we need a 'which mux are you' ioctl to correlate mux and tty sets
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/types.h>
268c2ecf20Sopenharmony_ci#include <linux/major.h>
278c2ecf20Sopenharmony_ci#include <linux/errno.h>
288c2ecf20Sopenharmony_ci#include <linux/signal.h>
298c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
308c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
318c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
328c2ecf20Sopenharmony_ci#include <linux/tty.h>
338c2ecf20Sopenharmony_ci#include <linux/ctype.h>
348c2ecf20Sopenharmony_ci#include <linux/mm.h>
358c2ecf20Sopenharmony_ci#include <linux/string.h>
368c2ecf20Sopenharmony_ci#include <linux/slab.h>
378c2ecf20Sopenharmony_ci#include <linux/poll.h>
388c2ecf20Sopenharmony_ci#include <linux/bitops.h>
398c2ecf20Sopenharmony_ci#include <linux/file.h>
408c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
418c2ecf20Sopenharmony_ci#include <linux/module.h>
428c2ecf20Sopenharmony_ci#include <linux/timer.h>
438c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
448c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
458c2ecf20Sopenharmony_ci#include <linux/serial.h>
468c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
478c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
488c2ecf20Sopenharmony_ci#include <net/arp.h>
498c2ecf20Sopenharmony_ci#include <linux/ip.h>
508c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
518c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
528c2ecf20Sopenharmony_ci#include <linux/gsmmux.h>
538c2ecf20Sopenharmony_ci#include "tty.h"
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int debug;
568c2ecf20Sopenharmony_cimodule_param(debug, int, 0600);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Defaults: these are from the specification */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define T1	10		/* 100mS */
618c2ecf20Sopenharmony_ci#define T2	34		/* 333mS */
628c2ecf20Sopenharmony_ci#define N2	3		/* Retry 3 times */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* Use long timers for testing at low speed with debug on */
658c2ecf20Sopenharmony_ci#ifdef DEBUG_TIMING
668c2ecf20Sopenharmony_ci#define T1	100
678c2ecf20Sopenharmony_ci#define T2	200
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/*
718c2ecf20Sopenharmony_ci * Semi-arbitrary buffer size limits. 0710 is normally run with 32-64 byte
728c2ecf20Sopenharmony_ci * limits so this is plenty
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_ci#define MAX_MRU 1500
758c2ecf20Sopenharmony_ci#define MAX_MTU 1500
768c2ecf20Sopenharmony_ci/* SOF, ADDR, CTRL, LEN1, LEN2, ..., FCS, EOF */
778c2ecf20Sopenharmony_ci#define PROT_OVERHEAD 7
788c2ecf20Sopenharmony_ci#define	GSM_NET_TX_TIMEOUT (HZ*10)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/**
818c2ecf20Sopenharmony_ci *	struct gsm_mux_net	-	network interface
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci *	Created when net interface is initialized.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistruct gsm_mux_net {
868c2ecf20Sopenharmony_ci	struct kref ref;
878c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/*
918c2ecf20Sopenharmony_ci *	Each block of data we have queued to go out is in the form of
928c2ecf20Sopenharmony_ci *	a gsm_msg which holds everything we need in a link layer independent
938c2ecf20Sopenharmony_ci *	format
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct gsm_msg {
978c2ecf20Sopenharmony_ci	struct list_head list;
988c2ecf20Sopenharmony_ci	u8 addr;		/* DLCI address + flags */
998c2ecf20Sopenharmony_ci	u8 ctrl;		/* Control byte + flags */
1008c2ecf20Sopenharmony_ci	unsigned int len;	/* Length of data block (can be zero) */
1018c2ecf20Sopenharmony_ci	unsigned char *data;	/* Points into buffer but not at the start */
1028c2ecf20Sopenharmony_ci	unsigned char buffer[];
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cienum gsm_dlci_state {
1068c2ecf20Sopenharmony_ci	DLCI_CLOSED,
1078c2ecf20Sopenharmony_ci	DLCI_OPENING,		/* Sending SABM not seen UA */
1088c2ecf20Sopenharmony_ci	DLCI_OPEN,		/* SABM/UA complete */
1098c2ecf20Sopenharmony_ci	DLCI_CLOSING,		/* Sending DISC not seen UA/DM */
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cienum gsm_dlci_mode {
1138c2ecf20Sopenharmony_ci	DLCI_MODE_ABM,		/* Normal Asynchronous Balanced Mode */
1148c2ecf20Sopenharmony_ci	DLCI_MODE_ADM,		/* Asynchronous Disconnected Mode */
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci *	Each active data link has a gsm_dlci structure associated which ties
1198c2ecf20Sopenharmony_ci *	the link layer to an optional tty (if the tty side is open). To avoid
1208c2ecf20Sopenharmony_ci *	complexity right now these are only ever freed up when the mux is
1218c2ecf20Sopenharmony_ci *	shut down.
1228c2ecf20Sopenharmony_ci *
1238c2ecf20Sopenharmony_ci *	At the moment we don't free DLCI objects until the mux is torn down
1248c2ecf20Sopenharmony_ci *	this avoid object life time issues but might be worth review later.
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistruct gsm_dlci {
1288c2ecf20Sopenharmony_ci	struct gsm_mux *gsm;
1298c2ecf20Sopenharmony_ci	int addr;
1308c2ecf20Sopenharmony_ci	enum gsm_dlci_state state;
1318c2ecf20Sopenharmony_ci	struct mutex mutex;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Link layer */
1348c2ecf20Sopenharmony_ci	enum gsm_dlci_mode mode;
1358c2ecf20Sopenharmony_ci	spinlock_t lock;	/* Protects the internal state */
1368c2ecf20Sopenharmony_ci	struct timer_list t1;	/* Retransmit timer for SABM and UA */
1378c2ecf20Sopenharmony_ci	int retries;
1388c2ecf20Sopenharmony_ci	/* Uplink tty if active */
1398c2ecf20Sopenharmony_ci	struct tty_port port;	/* The tty bound to this DLCI if there is one */
1408c2ecf20Sopenharmony_ci	struct kfifo fifo;	/* Queue fifo for the DLCI */
1418c2ecf20Sopenharmony_ci	int adaption;		/* Adaption layer in use */
1428c2ecf20Sopenharmony_ci	int prev_adaption;
1438c2ecf20Sopenharmony_ci	u32 modem_rx;		/* Our incoming virtual modem lines */
1448c2ecf20Sopenharmony_ci	u32 modem_tx;		/* Our outgoing modem lines */
1458c2ecf20Sopenharmony_ci	bool dead;		/* Refuse re-open */
1468c2ecf20Sopenharmony_ci	/* Flow control */
1478c2ecf20Sopenharmony_ci	bool throttled;		/* Private copy of throttle state */
1488c2ecf20Sopenharmony_ci	bool constipated;	/* Throttle status for outgoing */
1498c2ecf20Sopenharmony_ci	/* Packetised I/O */
1508c2ecf20Sopenharmony_ci	struct sk_buff *skb;	/* Frame being sent */
1518c2ecf20Sopenharmony_ci	struct sk_buff_head skb_list;	/* Queued frames */
1528c2ecf20Sopenharmony_ci	/* Data handling callback */
1538c2ecf20Sopenharmony_ci	void (*data)(struct gsm_dlci *dlci, const u8 *data, int len);
1548c2ecf20Sopenharmony_ci	void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
1558c2ecf20Sopenharmony_ci	struct net_device *net; /* network interface, if created */
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* DLCI 0, 62/63 are special or reserved see gsmtty_open */
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci#define NUM_DLCI		64
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/*
1638c2ecf20Sopenharmony_ci *	DLCI 0 is used to pass control blocks out of band of the data
1648c2ecf20Sopenharmony_ci *	flow (and with a higher link priority). One command can be outstanding
1658c2ecf20Sopenharmony_ci *	at a time and we use this structure to manage them. They are created
1668c2ecf20Sopenharmony_ci *	and destroyed by the user context, and updated by the receive paths
1678c2ecf20Sopenharmony_ci *	and timers
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistruct gsm_control {
1718c2ecf20Sopenharmony_ci	u8 cmd;		/* Command we are issuing */
1728c2ecf20Sopenharmony_ci	u8 *data;	/* Data for the command in case we retransmit */
1738c2ecf20Sopenharmony_ci	int len;	/* Length of block for retransmission */
1748c2ecf20Sopenharmony_ci	int done;	/* Done flag */
1758c2ecf20Sopenharmony_ci	int error;	/* Error if any */
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cienum gsm_mux_state {
1798c2ecf20Sopenharmony_ci	GSM_SEARCH,
1808c2ecf20Sopenharmony_ci	GSM_START,
1818c2ecf20Sopenharmony_ci	GSM_ADDRESS,
1828c2ecf20Sopenharmony_ci	GSM_CONTROL,
1838c2ecf20Sopenharmony_ci	GSM_LEN,
1848c2ecf20Sopenharmony_ci	GSM_DATA,
1858c2ecf20Sopenharmony_ci	GSM_FCS,
1868c2ecf20Sopenharmony_ci	GSM_OVERRUN,
1878c2ecf20Sopenharmony_ci	GSM_LEN0,
1888c2ecf20Sopenharmony_ci	GSM_LEN1,
1898c2ecf20Sopenharmony_ci	GSM_SSOF,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/*
1938c2ecf20Sopenharmony_ci *	Each GSM mux we have is represented by this structure. If we are
1948c2ecf20Sopenharmony_ci *	operating as an ldisc then we use this structure as our ldisc
1958c2ecf20Sopenharmony_ci *	state. We need to sort out lifetimes and locking with respect
1968c2ecf20Sopenharmony_ci *	to the gsm mux array. For now we don't free DLCI objects that
1978c2ecf20Sopenharmony_ci *	have been instantiated until the mux itself is terminated.
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci *	To consider further: tty open versus mux shutdown.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistruct gsm_mux {
2038c2ecf20Sopenharmony_ci	struct tty_struct *tty;		/* The tty our ldisc is bound to */
2048c2ecf20Sopenharmony_ci	spinlock_t lock;
2058c2ecf20Sopenharmony_ci	struct mutex mutex;
2068c2ecf20Sopenharmony_ci	unsigned int num;
2078c2ecf20Sopenharmony_ci	struct kref ref;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Events on the GSM channel */
2108c2ecf20Sopenharmony_ci	wait_queue_head_t event;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Bits for GSM mode decoding */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Framing Layer */
2158c2ecf20Sopenharmony_ci	unsigned char *buf;
2168c2ecf20Sopenharmony_ci	enum gsm_mux_state state;
2178c2ecf20Sopenharmony_ci	unsigned int len;
2188c2ecf20Sopenharmony_ci	unsigned int address;
2198c2ecf20Sopenharmony_ci	unsigned int count;
2208c2ecf20Sopenharmony_ci	bool escape;
2218c2ecf20Sopenharmony_ci	int encoding;
2228c2ecf20Sopenharmony_ci	u8 control;
2238c2ecf20Sopenharmony_ci	u8 fcs;
2248c2ecf20Sopenharmony_ci	u8 received_fcs;
2258c2ecf20Sopenharmony_ci	u8 *txframe;			/* TX framing buffer */
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* Method for the receiver side */
2288c2ecf20Sopenharmony_ci	void (*receive)(struct gsm_mux *gsm, u8 ch);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Link Layer */
2318c2ecf20Sopenharmony_ci	unsigned int mru;
2328c2ecf20Sopenharmony_ci	unsigned int mtu;
2338c2ecf20Sopenharmony_ci	int initiator;			/* Did we initiate connection */
2348c2ecf20Sopenharmony_ci	bool dead;			/* Has the mux been shut down */
2358c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci[NUM_DLCI];
2368c2ecf20Sopenharmony_ci	int old_c_iflag;		/* termios c_iflag value before attach */
2378c2ecf20Sopenharmony_ci	bool constipated;		/* Asked by remote to shut up */
2388c2ecf20Sopenharmony_ci	bool has_devices;		/* Devices were registered */
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	spinlock_t tx_lock;
2418c2ecf20Sopenharmony_ci	unsigned int tx_bytes;		/* TX data outstanding */
2428c2ecf20Sopenharmony_ci#define TX_THRESH_HI		8192
2438c2ecf20Sopenharmony_ci#define TX_THRESH_LO		2048
2448c2ecf20Sopenharmony_ci	struct list_head tx_list;	/* Pending data packets */
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* Control messages */
2478c2ecf20Sopenharmony_ci	struct timer_list t2_timer;	/* Retransmit timer for commands */
2488c2ecf20Sopenharmony_ci	int cretries;			/* Command retry counter */
2498c2ecf20Sopenharmony_ci	struct gsm_control *pending_cmd;/* Our current pending command */
2508c2ecf20Sopenharmony_ci	spinlock_t control_lock;	/* Protects the pending command */
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Configuration */
2538c2ecf20Sopenharmony_ci	int adaption;		/* 1 or 2 supported */
2548c2ecf20Sopenharmony_ci	u8 ftype;		/* UI or UIH */
2558c2ecf20Sopenharmony_ci	int t1, t2;		/* Timers in 1/100th of a sec */
2568c2ecf20Sopenharmony_ci	int n2;			/* Retry count */
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Statistics (not currently exposed) */
2598c2ecf20Sopenharmony_ci	unsigned long bad_fcs;
2608c2ecf20Sopenharmony_ci	unsigned long malformed;
2618c2ecf20Sopenharmony_ci	unsigned long io_error;
2628c2ecf20Sopenharmony_ci	unsigned long bad_size;
2638c2ecf20Sopenharmony_ci	unsigned long unsupported;
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/*
2688c2ecf20Sopenharmony_ci *	Mux objects - needed so that we can translate a tty index into the
2698c2ecf20Sopenharmony_ci *	relevant mux and DLCI.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci#define MAX_MUX		4			/* 256 minors */
2738c2ecf20Sopenharmony_cistatic struct gsm_mux *gsm_mux[MAX_MUX];	/* GSM muxes */
2748c2ecf20Sopenharmony_cistatic spinlock_t gsm_mux_lock;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic struct tty_driver *gsm_tty_driver;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci/*
2798c2ecf20Sopenharmony_ci *	This section of the driver logic implements the GSM encodings
2808c2ecf20Sopenharmony_ci *	both the basic and the 'advanced'. Reliable transport is not
2818c2ecf20Sopenharmony_ci *	supported.
2828c2ecf20Sopenharmony_ci */
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci#define CR			0x02
2858c2ecf20Sopenharmony_ci#define EA			0x01
2868c2ecf20Sopenharmony_ci#define	PF			0x10
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/* I is special: the rest are ..*/
2898c2ecf20Sopenharmony_ci#define RR			0x01
2908c2ecf20Sopenharmony_ci#define UI			0x03
2918c2ecf20Sopenharmony_ci#define RNR			0x05
2928c2ecf20Sopenharmony_ci#define REJ			0x09
2938c2ecf20Sopenharmony_ci#define DM			0x0F
2948c2ecf20Sopenharmony_ci#define SABM			0x2F
2958c2ecf20Sopenharmony_ci#define DISC			0x43
2968c2ecf20Sopenharmony_ci#define UA			0x63
2978c2ecf20Sopenharmony_ci#define	UIH			0xEF
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci/* Channel commands */
3008c2ecf20Sopenharmony_ci#define CMD_NSC			0x09
3018c2ecf20Sopenharmony_ci#define CMD_TEST		0x11
3028c2ecf20Sopenharmony_ci#define CMD_PSC			0x21
3038c2ecf20Sopenharmony_ci#define CMD_RLS			0x29
3048c2ecf20Sopenharmony_ci#define CMD_FCOFF		0x31
3058c2ecf20Sopenharmony_ci#define CMD_PN			0x41
3068c2ecf20Sopenharmony_ci#define CMD_RPN			0x49
3078c2ecf20Sopenharmony_ci#define CMD_FCON		0x51
3088c2ecf20Sopenharmony_ci#define CMD_CLD			0x61
3098c2ecf20Sopenharmony_ci#define CMD_SNC			0x69
3108c2ecf20Sopenharmony_ci#define CMD_MSC			0x71
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/* Virtual modem bits */
3138c2ecf20Sopenharmony_ci#define MDM_FC			0x01
3148c2ecf20Sopenharmony_ci#define MDM_RTC			0x02
3158c2ecf20Sopenharmony_ci#define MDM_RTR			0x04
3168c2ecf20Sopenharmony_ci#define MDM_IC			0x20
3178c2ecf20Sopenharmony_ci#define MDM_DV			0x40
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci#define GSM0_SOF		0xF9
3208c2ecf20Sopenharmony_ci#define GSM1_SOF		0x7E
3218c2ecf20Sopenharmony_ci#define GSM1_ESCAPE		0x7D
3228c2ecf20Sopenharmony_ci#define GSM1_ESCAPE_BITS	0x20
3238c2ecf20Sopenharmony_ci#define XON			0x11
3248c2ecf20Sopenharmony_ci#define XOFF			0x13
3258c2ecf20Sopenharmony_ci#define ISO_IEC_646_MASK	0x7F
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic const struct tty_port_operations gsm_port_ops;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/*
3308c2ecf20Sopenharmony_ci *	CRC table for GSM 0710
3318c2ecf20Sopenharmony_ci */
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic const u8 gsm_fcs8[256] = {
3348c2ecf20Sopenharmony_ci	0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
3358c2ecf20Sopenharmony_ci	0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
3368c2ecf20Sopenharmony_ci	0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
3378c2ecf20Sopenharmony_ci	0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
3388c2ecf20Sopenharmony_ci	0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D,
3398c2ecf20Sopenharmony_ci	0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
3408c2ecf20Sopenharmony_ci	0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51,
3418c2ecf20Sopenharmony_ci	0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
3428c2ecf20Sopenharmony_ci	0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
3438c2ecf20Sopenharmony_ci	0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
3448c2ecf20Sopenharmony_ci	0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19,
3458c2ecf20Sopenharmony_ci	0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
3468c2ecf20Sopenharmony_ci	0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D,
3478c2ecf20Sopenharmony_ci	0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
3488c2ecf20Sopenharmony_ci	0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
3498c2ecf20Sopenharmony_ci	0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
3508c2ecf20Sopenharmony_ci	0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95,
3518c2ecf20Sopenharmony_ci	0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
3528c2ecf20Sopenharmony_ci	0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89,
3538c2ecf20Sopenharmony_ci	0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
3548c2ecf20Sopenharmony_ci	0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
3558c2ecf20Sopenharmony_ci	0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
3568c2ecf20Sopenharmony_ci	0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1,
3578c2ecf20Sopenharmony_ci	0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
3588c2ecf20Sopenharmony_ci	0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5,
3598c2ecf20Sopenharmony_ci	0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
3608c2ecf20Sopenharmony_ci	0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
3618c2ecf20Sopenharmony_ci	0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
3628c2ecf20Sopenharmony_ci	0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD,
3638c2ecf20Sopenharmony_ci	0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
3648c2ecf20Sopenharmony_ci	0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1,
3658c2ecf20Sopenharmony_ci	0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci#define INIT_FCS	0xFF
3698c2ecf20Sopenharmony_ci#define GOOD_FCS	0xCF
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/**
3748c2ecf20Sopenharmony_ci *	gsm_fcs_add	-	update FCS
3758c2ecf20Sopenharmony_ci *	@fcs: Current FCS
3768c2ecf20Sopenharmony_ci *	@c: Next data
3778c2ecf20Sopenharmony_ci *
3788c2ecf20Sopenharmony_ci *	Update the FCS to include c. Uses the algorithm in the specification
3798c2ecf20Sopenharmony_ci *	notes.
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic inline u8 gsm_fcs_add(u8 fcs, u8 c)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	return gsm_fcs8[fcs ^ c];
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci/**
3888c2ecf20Sopenharmony_ci *	gsm_fcs_add_block	-	update FCS for a block
3898c2ecf20Sopenharmony_ci *	@fcs: Current FCS
3908c2ecf20Sopenharmony_ci *	@c: buffer of data
3918c2ecf20Sopenharmony_ci *	@len: length of buffer
3928c2ecf20Sopenharmony_ci *
3938c2ecf20Sopenharmony_ci *	Update the FCS to include c. Uses the algorithm in the specification
3948c2ecf20Sopenharmony_ci *	notes.
3958c2ecf20Sopenharmony_ci */
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	while (len--)
4008c2ecf20Sopenharmony_ci		fcs = gsm_fcs8[fcs ^ *c++];
4018c2ecf20Sopenharmony_ci	return fcs;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/**
4058c2ecf20Sopenharmony_ci *	gsm_read_ea		-	read a byte into an EA
4068c2ecf20Sopenharmony_ci *	@val: variable holding value
4078c2ecf20Sopenharmony_ci *	@c: byte going into the EA
4088c2ecf20Sopenharmony_ci *
4098c2ecf20Sopenharmony_ci *	Processes one byte of an EA. Updates the passed variable
4108c2ecf20Sopenharmony_ci *	and returns 1 if the EA is now completely read
4118c2ecf20Sopenharmony_ci */
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int gsm_read_ea(unsigned int *val, u8 c)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	/* Add the next 7 bits into the value */
4168c2ecf20Sopenharmony_ci	*val <<= 7;
4178c2ecf20Sopenharmony_ci	*val |= c >> 1;
4188c2ecf20Sopenharmony_ci	/* Was this the last byte of the EA 1 = yes*/
4198c2ecf20Sopenharmony_ci	return c & EA;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/**
4238c2ecf20Sopenharmony_ci *	gsm_read_ea_val	-	read a value until EA
4248c2ecf20Sopenharmony_ci *	@val: variable holding value
4258c2ecf20Sopenharmony_ci *	@data: buffer of data
4268c2ecf20Sopenharmony_ci *	@dlen: length of data
4278c2ecf20Sopenharmony_ci *
4288c2ecf20Sopenharmony_ci *	Processes an EA value. Updates the passed variable and
4298c2ecf20Sopenharmony_ci *	returns the processed data length.
4308c2ecf20Sopenharmony_ci */
4318c2ecf20Sopenharmony_cistatic unsigned int gsm_read_ea_val(unsigned int *val, const u8 *data, int dlen)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	unsigned int len = 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	for (; dlen > 0; dlen--) {
4368c2ecf20Sopenharmony_ci		len++;
4378c2ecf20Sopenharmony_ci		if (gsm_read_ea(val, *data++))
4388c2ecf20Sopenharmony_ci			break;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	return len;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/**
4448c2ecf20Sopenharmony_ci *	gsm_encode_modem	-	encode modem data bits
4458c2ecf20Sopenharmony_ci *	@dlci: DLCI to encode from
4468c2ecf20Sopenharmony_ci *
4478c2ecf20Sopenharmony_ci *	Returns the correct GSM encoded modem status bits (6 bit field) for
4488c2ecf20Sopenharmony_ci *	the current status of the DLCI and attached tty object
4498c2ecf20Sopenharmony_ci */
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic u8 gsm_encode_modem(const struct gsm_dlci *dlci)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	u8 modembits = 0;
4548c2ecf20Sopenharmony_ci	/* FC is true flow control not modem bits */
4558c2ecf20Sopenharmony_ci	if (dlci->throttled)
4568c2ecf20Sopenharmony_ci		modembits |= MDM_FC;
4578c2ecf20Sopenharmony_ci	if (dlci->modem_tx & TIOCM_DTR)
4588c2ecf20Sopenharmony_ci		modembits |= MDM_RTC;
4598c2ecf20Sopenharmony_ci	if (dlci->modem_tx & TIOCM_RTS)
4608c2ecf20Sopenharmony_ci		modembits |= MDM_RTR;
4618c2ecf20Sopenharmony_ci	if (dlci->modem_tx & TIOCM_RI)
4628c2ecf20Sopenharmony_ci		modembits |= MDM_IC;
4638c2ecf20Sopenharmony_ci	if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator)
4648c2ecf20Sopenharmony_ci		modembits |= MDM_DV;
4658c2ecf20Sopenharmony_ci	return modembits;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/**
4698c2ecf20Sopenharmony_ci *	gsm_register_devices	-	register all tty devices for a given mux index
4708c2ecf20Sopenharmony_ci *
4718c2ecf20Sopenharmony_ci *	@driver: the tty driver that describes the tty devices
4728c2ecf20Sopenharmony_ci *	@index:  the mux number is used to calculate the minor numbers of the
4738c2ecf20Sopenharmony_ci *	         ttys for this mux and may differ from the position in the
4748c2ecf20Sopenharmony_ci *	         mux array.
4758c2ecf20Sopenharmony_ci */
4768c2ecf20Sopenharmony_cistatic int gsm_register_devices(struct tty_driver *driver, unsigned int index)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct device *dev;
4798c2ecf20Sopenharmony_ci	int i;
4808c2ecf20Sopenharmony_ci	unsigned int base;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (!driver || index >= MAX_MUX)
4838c2ecf20Sopenharmony_ci		return -EINVAL;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	base = index * NUM_DLCI; /* first minor for this index */
4868c2ecf20Sopenharmony_ci	for (i = 1; i < NUM_DLCI; i++) {
4878c2ecf20Sopenharmony_ci		/* Don't register device 0 - this is the control channel
4888c2ecf20Sopenharmony_ci		 * and not a usable tty interface
4898c2ecf20Sopenharmony_ci		 */
4908c2ecf20Sopenharmony_ci		dev = tty_register_device(gsm_tty_driver, base + i, NULL);
4918c2ecf20Sopenharmony_ci		if (IS_ERR(dev)) {
4928c2ecf20Sopenharmony_ci			if (debug & 8)
4938c2ecf20Sopenharmony_ci				pr_info("%s failed to register device minor %u",
4948c2ecf20Sopenharmony_ci					__func__, base + i);
4958c2ecf20Sopenharmony_ci			for (i--; i >= 1; i--)
4968c2ecf20Sopenharmony_ci				tty_unregister_device(gsm_tty_driver, base + i);
4978c2ecf20Sopenharmony_ci			return PTR_ERR(dev);
4988c2ecf20Sopenharmony_ci		}
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/**
5058c2ecf20Sopenharmony_ci *	gsm_unregister_devices	-	unregister all tty devices for a given mux index
5068c2ecf20Sopenharmony_ci *
5078c2ecf20Sopenharmony_ci *	@driver: the tty driver that describes the tty devices
5088c2ecf20Sopenharmony_ci *	@index:  the mux number is used to calculate the minor numbers of the
5098c2ecf20Sopenharmony_ci *	         ttys for this mux and may differ from the position in the
5108c2ecf20Sopenharmony_ci *	         mux array.
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_cistatic void gsm_unregister_devices(struct tty_driver *driver,
5138c2ecf20Sopenharmony_ci				   unsigned int index)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	int i;
5168c2ecf20Sopenharmony_ci	unsigned int base;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (!driver || index >= MAX_MUX)
5198c2ecf20Sopenharmony_ci		return;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	base = index * NUM_DLCI; /* first minor for this index */
5228c2ecf20Sopenharmony_ci	for (i = 1; i < NUM_DLCI; i++) {
5238c2ecf20Sopenharmony_ci		/* Don't unregister device 0 - this is the control
5248c2ecf20Sopenharmony_ci		 * channel and not a usable tty interface
5258c2ecf20Sopenharmony_ci		 */
5268c2ecf20Sopenharmony_ci		tty_unregister_device(gsm_tty_driver, base + i);
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/**
5318c2ecf20Sopenharmony_ci *	gsm_print_packet	-	display a frame for debug
5328c2ecf20Sopenharmony_ci *	@hdr: header to print before decode
5338c2ecf20Sopenharmony_ci *	@addr: address EA from the frame
5348c2ecf20Sopenharmony_ci *	@cr: C/R bit from the frame
5358c2ecf20Sopenharmony_ci *	@control: control including PF bit
5368c2ecf20Sopenharmony_ci *	@data: following data bytes
5378c2ecf20Sopenharmony_ci *	@dlen: length of data
5388c2ecf20Sopenharmony_ci *
5398c2ecf20Sopenharmony_ci *	Displays a packet in human readable format for debugging purposes. The
5408c2ecf20Sopenharmony_ci *	style is based on amateur radio LAP-B dump display.
5418c2ecf20Sopenharmony_ci */
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic void gsm_print_packet(const char *hdr, int addr, int cr,
5448c2ecf20Sopenharmony_ci					u8 control, const u8 *data, int dlen)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	if (!(debug & 1))
5478c2ecf20Sopenharmony_ci		return;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	pr_info("%s %d) %c: ", hdr, addr, "RC"[cr]);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	switch (control & ~PF) {
5528c2ecf20Sopenharmony_ci	case SABM:
5538c2ecf20Sopenharmony_ci		pr_cont("SABM");
5548c2ecf20Sopenharmony_ci		break;
5558c2ecf20Sopenharmony_ci	case UA:
5568c2ecf20Sopenharmony_ci		pr_cont("UA");
5578c2ecf20Sopenharmony_ci		break;
5588c2ecf20Sopenharmony_ci	case DISC:
5598c2ecf20Sopenharmony_ci		pr_cont("DISC");
5608c2ecf20Sopenharmony_ci		break;
5618c2ecf20Sopenharmony_ci	case DM:
5628c2ecf20Sopenharmony_ci		pr_cont("DM");
5638c2ecf20Sopenharmony_ci		break;
5648c2ecf20Sopenharmony_ci	case UI:
5658c2ecf20Sopenharmony_ci		pr_cont("UI");
5668c2ecf20Sopenharmony_ci		break;
5678c2ecf20Sopenharmony_ci	case UIH:
5688c2ecf20Sopenharmony_ci		pr_cont("UIH");
5698c2ecf20Sopenharmony_ci		break;
5708c2ecf20Sopenharmony_ci	default:
5718c2ecf20Sopenharmony_ci		if (!(control & 0x01)) {
5728c2ecf20Sopenharmony_ci			pr_cont("I N(S)%d N(R)%d",
5738c2ecf20Sopenharmony_ci				(control & 0x0E) >> 1, (control & 0xE0) >> 5);
5748c2ecf20Sopenharmony_ci		} else switch (control & 0x0F) {
5758c2ecf20Sopenharmony_ci			case RR:
5768c2ecf20Sopenharmony_ci				pr_cont("RR(%d)", (control & 0xE0) >> 5);
5778c2ecf20Sopenharmony_ci				break;
5788c2ecf20Sopenharmony_ci			case RNR:
5798c2ecf20Sopenharmony_ci				pr_cont("RNR(%d)", (control & 0xE0) >> 5);
5808c2ecf20Sopenharmony_ci				break;
5818c2ecf20Sopenharmony_ci			case REJ:
5828c2ecf20Sopenharmony_ci				pr_cont("REJ(%d)", (control & 0xE0) >> 5);
5838c2ecf20Sopenharmony_ci				break;
5848c2ecf20Sopenharmony_ci			default:
5858c2ecf20Sopenharmony_ci				pr_cont("[%02X]", control);
5868c2ecf20Sopenharmony_ci		}
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (control & PF)
5908c2ecf20Sopenharmony_ci		pr_cont("(P)");
5918c2ecf20Sopenharmony_ci	else
5928c2ecf20Sopenharmony_ci		pr_cont("(F)");
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	print_hex_dump_bytes("", DUMP_PREFIX_NONE, data, dlen);
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci/*
5998c2ecf20Sopenharmony_ci *	Link level transmission side
6008c2ecf20Sopenharmony_ci */
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci/**
6038c2ecf20Sopenharmony_ci *	gsm_stuff_packet	-	bytestuff a packet
6048c2ecf20Sopenharmony_ci *	@input: input buffer
6058c2ecf20Sopenharmony_ci *	@output: output buffer
6068c2ecf20Sopenharmony_ci *	@len: length of input
6078c2ecf20Sopenharmony_ci *
6088c2ecf20Sopenharmony_ci *	Expand a buffer by bytestuffing it. The worst case size change
6098c2ecf20Sopenharmony_ci *	is doubling and the caller is responsible for handing out
6108c2ecf20Sopenharmony_ci *	suitable sized buffers.
6118c2ecf20Sopenharmony_ci */
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic int gsm_stuff_frame(const u8 *input, u8 *output, int len)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	int olen = 0;
6168c2ecf20Sopenharmony_ci	while (len--) {
6178c2ecf20Sopenharmony_ci		if (*input == GSM1_SOF || *input == GSM1_ESCAPE
6188c2ecf20Sopenharmony_ci		    || (*input & ISO_IEC_646_MASK) == XON
6198c2ecf20Sopenharmony_ci		    || (*input & ISO_IEC_646_MASK) == XOFF) {
6208c2ecf20Sopenharmony_ci			*output++ = GSM1_ESCAPE;
6218c2ecf20Sopenharmony_ci			*output++ = *input++ ^ GSM1_ESCAPE_BITS;
6228c2ecf20Sopenharmony_ci			olen++;
6238c2ecf20Sopenharmony_ci		} else
6248c2ecf20Sopenharmony_ci			*output++ = *input++;
6258c2ecf20Sopenharmony_ci		olen++;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci	return olen;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/**
6318c2ecf20Sopenharmony_ci *	gsm_send	-	send a control frame
6328c2ecf20Sopenharmony_ci *	@gsm: our GSM mux
6338c2ecf20Sopenharmony_ci *	@addr: address for control frame
6348c2ecf20Sopenharmony_ci *	@cr: command/response bit
6358c2ecf20Sopenharmony_ci *	@control:  control byte including PF bit
6368c2ecf20Sopenharmony_ci *
6378c2ecf20Sopenharmony_ci *	Format up and transmit a control frame. These do not go via the
6388c2ecf20Sopenharmony_ci *	queueing logic as they should be transmitted ahead of data when
6398c2ecf20Sopenharmony_ci *	they are needed.
6408c2ecf20Sopenharmony_ci *
6418c2ecf20Sopenharmony_ci *	FIXME: Lock versus data TX path
6428c2ecf20Sopenharmony_ci */
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	int len;
6478c2ecf20Sopenharmony_ci	u8 cbuf[10];
6488c2ecf20Sopenharmony_ci	u8 ibuf[3];
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	switch (gsm->encoding) {
6518c2ecf20Sopenharmony_ci	case 0:
6528c2ecf20Sopenharmony_ci		cbuf[0] = GSM0_SOF;
6538c2ecf20Sopenharmony_ci		cbuf[1] = (addr << 2) | (cr << 1) | EA;
6548c2ecf20Sopenharmony_ci		cbuf[2] = control;
6558c2ecf20Sopenharmony_ci		cbuf[3] = EA;	/* Length of data = 0 */
6568c2ecf20Sopenharmony_ci		cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3);
6578c2ecf20Sopenharmony_ci		cbuf[5] = GSM0_SOF;
6588c2ecf20Sopenharmony_ci		len = 6;
6598c2ecf20Sopenharmony_ci		break;
6608c2ecf20Sopenharmony_ci	case 1:
6618c2ecf20Sopenharmony_ci	case 2:
6628c2ecf20Sopenharmony_ci		/* Control frame + packing (but not frame stuffing) in mode 1 */
6638c2ecf20Sopenharmony_ci		ibuf[0] = (addr << 2) | (cr << 1) | EA;
6648c2ecf20Sopenharmony_ci		ibuf[1] = control;
6658c2ecf20Sopenharmony_ci		ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2);
6668c2ecf20Sopenharmony_ci		/* Stuffing may double the size worst case */
6678c2ecf20Sopenharmony_ci		len = gsm_stuff_frame(ibuf, cbuf + 1, 3);
6688c2ecf20Sopenharmony_ci		/* Now add the SOF markers */
6698c2ecf20Sopenharmony_ci		cbuf[0] = GSM1_SOF;
6708c2ecf20Sopenharmony_ci		cbuf[len + 1] = GSM1_SOF;
6718c2ecf20Sopenharmony_ci		/* FIXME: we can omit the lead one in many cases */
6728c2ecf20Sopenharmony_ci		len += 2;
6738c2ecf20Sopenharmony_ci		break;
6748c2ecf20Sopenharmony_ci	default:
6758c2ecf20Sopenharmony_ci		WARN_ON(1);
6768c2ecf20Sopenharmony_ci		return;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci	gsmld_output(gsm, cbuf, len);
6798c2ecf20Sopenharmony_ci	gsm_print_packet("-->", addr, cr, control, NULL, 0);
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci/**
6838c2ecf20Sopenharmony_ci *	gsm_response	-	send a control response
6848c2ecf20Sopenharmony_ci *	@gsm: our GSM mux
6858c2ecf20Sopenharmony_ci *	@addr: address for control frame
6868c2ecf20Sopenharmony_ci *	@control:  control byte including PF bit
6878c2ecf20Sopenharmony_ci *
6888c2ecf20Sopenharmony_ci *	Format up and transmit a link level response frame.
6898c2ecf20Sopenharmony_ci */
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	gsm_send(gsm, addr, 0, control);
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci/**
6978c2ecf20Sopenharmony_ci *	gsm_command	-	send a control command
6988c2ecf20Sopenharmony_ci *	@gsm: our GSM mux
6998c2ecf20Sopenharmony_ci *	@addr: address for control frame
7008c2ecf20Sopenharmony_ci *	@control:  control byte including PF bit
7018c2ecf20Sopenharmony_ci *
7028c2ecf20Sopenharmony_ci *	Format up and transmit a link level command frame.
7038c2ecf20Sopenharmony_ci */
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic inline void gsm_command(struct gsm_mux *gsm, int addr, int control)
7068c2ecf20Sopenharmony_ci{
7078c2ecf20Sopenharmony_ci	gsm_send(gsm, addr, 1, control);
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci/* Data transmission */
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci#define HDR_LEN		6	/* ADDR CTRL [LEN.2] DATA FCS */
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci/**
7158c2ecf20Sopenharmony_ci *	gsm_data_alloc		-	allocate data frame
7168c2ecf20Sopenharmony_ci *	@gsm: GSM mux
7178c2ecf20Sopenharmony_ci *	@addr: DLCI address
7188c2ecf20Sopenharmony_ci *	@len: length excluding header and FCS
7198c2ecf20Sopenharmony_ci *	@ctrl: control byte
7208c2ecf20Sopenharmony_ci *
7218c2ecf20Sopenharmony_ci *	Allocate a new data buffer for sending frames with data. Space is left
7228c2ecf20Sopenharmony_ci *	at the front for header bytes but that is treated as an implementation
7238c2ecf20Sopenharmony_ci *	detail and not for the high level code to use
7248c2ecf20Sopenharmony_ci */
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
7278c2ecf20Sopenharmony_ci								u8 ctrl)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN,
7308c2ecf20Sopenharmony_ci								GFP_ATOMIC);
7318c2ecf20Sopenharmony_ci	if (m == NULL)
7328c2ecf20Sopenharmony_ci		return NULL;
7338c2ecf20Sopenharmony_ci	m->data = m->buffer + HDR_LEN - 1;	/* Allow for FCS */
7348c2ecf20Sopenharmony_ci	m->len = len;
7358c2ecf20Sopenharmony_ci	m->addr = addr;
7368c2ecf20Sopenharmony_ci	m->ctrl = ctrl;
7378c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&m->list);
7388c2ecf20Sopenharmony_ci	return m;
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci/**
7428c2ecf20Sopenharmony_ci *	gsm_is_flow_ctrl_msg	-	checks if flow control message
7438c2ecf20Sopenharmony_ci *	@msg: message to check
7448c2ecf20Sopenharmony_ci *
7458c2ecf20Sopenharmony_ci *	Returns true if the given message is a flow control command of the
7468c2ecf20Sopenharmony_ci *	control channel. False is returned in any other case.
7478c2ecf20Sopenharmony_ci */
7488c2ecf20Sopenharmony_cistatic bool gsm_is_flow_ctrl_msg(struct gsm_msg *msg)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	unsigned int cmd;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (msg->addr > 0)
7538c2ecf20Sopenharmony_ci		return false;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	switch (msg->ctrl & ~PF) {
7568c2ecf20Sopenharmony_ci	case UI:
7578c2ecf20Sopenharmony_ci	case UIH:
7588c2ecf20Sopenharmony_ci		cmd = 0;
7598c2ecf20Sopenharmony_ci		if (gsm_read_ea_val(&cmd, msg->data + 2, msg->len - 2) < 1)
7608c2ecf20Sopenharmony_ci			break;
7618c2ecf20Sopenharmony_ci		switch (cmd & ~PF) {
7628c2ecf20Sopenharmony_ci		case CMD_FCOFF:
7638c2ecf20Sopenharmony_ci		case CMD_FCON:
7648c2ecf20Sopenharmony_ci			return true;
7658c2ecf20Sopenharmony_ci		}
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	return false;
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci/**
7738c2ecf20Sopenharmony_ci *	gsm_data_kick		-	poke the queue
7748c2ecf20Sopenharmony_ci *	@gsm: GSM Mux
7758c2ecf20Sopenharmony_ci *
7768c2ecf20Sopenharmony_ci *	The tty device has called us to indicate that room has appeared in
7778c2ecf20Sopenharmony_ci *	the transmit queue. Ram more data into the pipe if we have any
7788c2ecf20Sopenharmony_ci *	If we have been flow-stopped by a CMD_FCOFF, then we can only
7798c2ecf20Sopenharmony_ci *	send messages on DLCI0 until CMD_FCON
7808c2ecf20Sopenharmony_ci *
7818c2ecf20Sopenharmony_ci *	FIXME: lock against link layer control transmissions
7828c2ecf20Sopenharmony_ci */
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic void gsm_data_kick(struct gsm_mux *gsm, struct gsm_dlci *dlci)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct gsm_msg *msg, *nmsg;
7878c2ecf20Sopenharmony_ci	int len;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
7908c2ecf20Sopenharmony_ci		if (gsm->constipated && !gsm_is_flow_ctrl_msg(msg))
7918c2ecf20Sopenharmony_ci			continue;
7928c2ecf20Sopenharmony_ci		if (gsm->encoding != 0) {
7938c2ecf20Sopenharmony_ci			gsm->txframe[0] = GSM1_SOF;
7948c2ecf20Sopenharmony_ci			len = gsm_stuff_frame(msg->data,
7958c2ecf20Sopenharmony_ci						gsm->txframe + 1, msg->len);
7968c2ecf20Sopenharmony_ci			gsm->txframe[len + 1] = GSM1_SOF;
7978c2ecf20Sopenharmony_ci			len += 2;
7988c2ecf20Sopenharmony_ci		} else {
7998c2ecf20Sopenharmony_ci			gsm->txframe[0] = GSM0_SOF;
8008c2ecf20Sopenharmony_ci			memcpy(gsm->txframe + 1 , msg->data, msg->len);
8018c2ecf20Sopenharmony_ci			gsm->txframe[msg->len + 1] = GSM0_SOF;
8028c2ecf20Sopenharmony_ci			len = msg->len + 2;
8038c2ecf20Sopenharmony_ci		}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		if (debug & 4)
8068c2ecf20Sopenharmony_ci			print_hex_dump_bytes("gsm_data_kick: ",
8078c2ecf20Sopenharmony_ci					     DUMP_PREFIX_OFFSET,
8088c2ecf20Sopenharmony_ci					     gsm->txframe, len);
8098c2ecf20Sopenharmony_ci		if (gsmld_output(gsm, gsm->txframe, len) < 0)
8108c2ecf20Sopenharmony_ci			break;
8118c2ecf20Sopenharmony_ci		/* FIXME: Can eliminate one SOF in many more cases */
8128c2ecf20Sopenharmony_ci		gsm->tx_bytes -= msg->len;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		list_del(&msg->list);
8158c2ecf20Sopenharmony_ci		kfree(msg);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci		if (dlci) {
8188c2ecf20Sopenharmony_ci			tty_port_tty_wakeup(&dlci->port);
8198c2ecf20Sopenharmony_ci		} else {
8208c2ecf20Sopenharmony_ci			int i = 0;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci			for (i = 0; i < NUM_DLCI; i++)
8238c2ecf20Sopenharmony_ci				if (gsm->dlci[i])
8248c2ecf20Sopenharmony_ci					tty_port_tty_wakeup(&gsm->dlci[i]->port);
8258c2ecf20Sopenharmony_ci		}
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci/**
8308c2ecf20Sopenharmony_ci *	__gsm_data_queue		-	queue a UI or UIH frame
8318c2ecf20Sopenharmony_ci *	@dlci: DLCI sending the data
8328c2ecf20Sopenharmony_ci *	@msg: message queued
8338c2ecf20Sopenharmony_ci *
8348c2ecf20Sopenharmony_ci *	Add data to the transmit queue and try and get stuff moving
8358c2ecf20Sopenharmony_ci *	out of the mux tty if not already doing so. The Caller must hold
8368c2ecf20Sopenharmony_ci *	the gsm tx lock.
8378c2ecf20Sopenharmony_ci */
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cistatic void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
8408c2ecf20Sopenharmony_ci{
8418c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
8428c2ecf20Sopenharmony_ci	u8 *dp = msg->data;
8438c2ecf20Sopenharmony_ci	u8 *fcs = dp + msg->len;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* Fill in the header */
8468c2ecf20Sopenharmony_ci	if (gsm->encoding == 0) {
8478c2ecf20Sopenharmony_ci		if (msg->len < 128)
8488c2ecf20Sopenharmony_ci			*--dp = (msg->len << 1) | EA;
8498c2ecf20Sopenharmony_ci		else {
8508c2ecf20Sopenharmony_ci			*--dp = (msg->len >> 7);	/* bits 7 - 15 */
8518c2ecf20Sopenharmony_ci			*--dp = (msg->len & 127) << 1;	/* bits 0 - 6 */
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	*--dp = msg->ctrl;
8568c2ecf20Sopenharmony_ci	if (gsm->initiator)
8578c2ecf20Sopenharmony_ci		*--dp = (msg->addr << 2) | 2 | EA;
8588c2ecf20Sopenharmony_ci	else
8598c2ecf20Sopenharmony_ci		*--dp = (msg->addr << 2) | EA;
8608c2ecf20Sopenharmony_ci	*fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp);
8618c2ecf20Sopenharmony_ci	/* Ugly protocol layering violation */
8628c2ecf20Sopenharmony_ci	if (msg->ctrl == UI || msg->ctrl == (UI|PF))
8638c2ecf20Sopenharmony_ci		*fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len);
8648c2ecf20Sopenharmony_ci	*fcs = 0xFF - *fcs;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl,
8678c2ecf20Sopenharmony_ci							msg->data, msg->len);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* Move the header back and adjust the length, also allow for the FCS
8708c2ecf20Sopenharmony_ci	   now tacked on the end */
8718c2ecf20Sopenharmony_ci	msg->len += (msg->data - dp) + 1;
8728c2ecf20Sopenharmony_ci	msg->data = dp;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/* Add to the actual output queue */
8758c2ecf20Sopenharmony_ci	list_add_tail(&msg->list, &gsm->tx_list);
8768c2ecf20Sopenharmony_ci	gsm->tx_bytes += msg->len;
8778c2ecf20Sopenharmony_ci	gsm_data_kick(gsm, dlci);
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/**
8818c2ecf20Sopenharmony_ci *	gsm_data_queue		-	queue a UI or UIH frame
8828c2ecf20Sopenharmony_ci *	@dlci: DLCI sending the data
8838c2ecf20Sopenharmony_ci *	@msg: message queued
8848c2ecf20Sopenharmony_ci *
8858c2ecf20Sopenharmony_ci *	Add data to the transmit queue and try and get stuff moving
8868c2ecf20Sopenharmony_ci *	out of the mux tty if not already doing so. Take the
8878c2ecf20Sopenharmony_ci *	the gsm tx lock and dlci lock.
8888c2ecf20Sopenharmony_ci */
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_cistatic void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	unsigned long flags;
8938c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
8948c2ecf20Sopenharmony_ci	__gsm_data_queue(dlci, msg);
8958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/**
8998c2ecf20Sopenharmony_ci *	gsm_dlci_data_output	-	try and push data out of a DLCI
9008c2ecf20Sopenharmony_ci *	@gsm: mux
9018c2ecf20Sopenharmony_ci *	@dlci: the DLCI to pull data from
9028c2ecf20Sopenharmony_ci *
9038c2ecf20Sopenharmony_ci *	Pull data from a DLCI and send it into the transmit queue if there
9048c2ecf20Sopenharmony_ci *	is data. Keep to the MRU of the mux. This path handles the usual tty
9058c2ecf20Sopenharmony_ci *	interface which is a byte stream with optional modem data.
9068c2ecf20Sopenharmony_ci *
9078c2ecf20Sopenharmony_ci *	Caller must hold the tx_lock of the mux.
9088c2ecf20Sopenharmony_ci */
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	struct gsm_msg *msg;
9138c2ecf20Sopenharmony_ci	u8 *dp;
9148c2ecf20Sopenharmony_ci	int h, len, size;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/* for modem bits without break data */
9178c2ecf20Sopenharmony_ci	h = ((dlci->adaption == 1) ? 0 : 1);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	len = kfifo_len(&dlci->fifo);
9208c2ecf20Sopenharmony_ci	if (len == 0)
9218c2ecf20Sopenharmony_ci		return 0;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	/* MTU/MRU count only the data bits but watch adaption mode */
9248c2ecf20Sopenharmony_ci	if ((len + h) > gsm->mtu)
9258c2ecf20Sopenharmony_ci		len = gsm->mtu - h;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	size = len + h;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
9308c2ecf20Sopenharmony_ci	/* FIXME: need a timer or something to kick this so it can't
9318c2ecf20Sopenharmony_ci	 * get stuck with no work outstanding and no buffer free
9328c2ecf20Sopenharmony_ci	 */
9338c2ecf20Sopenharmony_ci	if (!msg)
9348c2ecf20Sopenharmony_ci		return -ENOMEM;
9358c2ecf20Sopenharmony_ci	dp = msg->data;
9368c2ecf20Sopenharmony_ci	switch (dlci->adaption) {
9378c2ecf20Sopenharmony_ci	case 1: /* Unstructured */
9388c2ecf20Sopenharmony_ci		break;
9398c2ecf20Sopenharmony_ci	case 2: /* Unstructured with modem bits.
9408c2ecf20Sopenharmony_ci		 * Always one byte as we never send inline break data
9418c2ecf20Sopenharmony_ci		 */
9428c2ecf20Sopenharmony_ci		*dp++ = (gsm_encode_modem(dlci) << 1) | EA;
9438c2ecf20Sopenharmony_ci		break;
9448c2ecf20Sopenharmony_ci	default:
9458c2ecf20Sopenharmony_ci		pr_err("%s: unsupported adaption %d\n", __func__,
9468c2ecf20Sopenharmony_ci		       dlci->adaption);
9478c2ecf20Sopenharmony_ci		break;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	WARN_ON(len != kfifo_out_locked(&dlci->fifo, dp, len,
9518c2ecf20Sopenharmony_ci		&dlci->lock));
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* Notify upper layer about available send space. */
9548c2ecf20Sopenharmony_ci	tty_port_tty_wakeup(&dlci->port);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	__gsm_data_queue(dlci, msg);
9578c2ecf20Sopenharmony_ci	/* Bytes of data we used up */
9588c2ecf20Sopenharmony_ci	return size;
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci/**
9628c2ecf20Sopenharmony_ci *	gsm_dlci_data_output_framed  -	try and push data out of a DLCI
9638c2ecf20Sopenharmony_ci *	@gsm: mux
9648c2ecf20Sopenharmony_ci *	@dlci: the DLCI to pull data from
9658c2ecf20Sopenharmony_ci *
9668c2ecf20Sopenharmony_ci *	Pull data from a DLCI and send it into the transmit queue if there
9678c2ecf20Sopenharmony_ci *	is data. Keep to the MRU of the mux. This path handles framed data
9688c2ecf20Sopenharmony_ci *	queued as skbuffs to the DLCI.
9698c2ecf20Sopenharmony_ci *
9708c2ecf20Sopenharmony_ci *	Caller must hold the tx_lock of the mux.
9718c2ecf20Sopenharmony_ci */
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
9748c2ecf20Sopenharmony_ci						struct gsm_dlci *dlci)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	struct gsm_msg *msg;
9778c2ecf20Sopenharmony_ci	u8 *dp;
9788c2ecf20Sopenharmony_ci	int len, size;
9798c2ecf20Sopenharmony_ci	int last = 0, first = 0;
9808c2ecf20Sopenharmony_ci	int overhead = 0;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* One byte per frame is used for B/F flags */
9838c2ecf20Sopenharmony_ci	if (dlci->adaption == 4)
9848c2ecf20Sopenharmony_ci		overhead = 1;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* dlci->skb is locked by tx_lock */
9878c2ecf20Sopenharmony_ci	if (dlci->skb == NULL) {
9888c2ecf20Sopenharmony_ci		dlci->skb = skb_dequeue_tail(&dlci->skb_list);
9898c2ecf20Sopenharmony_ci		if (dlci->skb == NULL)
9908c2ecf20Sopenharmony_ci			return 0;
9918c2ecf20Sopenharmony_ci		first = 1;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci	len = dlci->skb->len + overhead;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	/* MTU/MRU count only the data bits */
9968c2ecf20Sopenharmony_ci	if (len > gsm->mtu) {
9978c2ecf20Sopenharmony_ci		if (dlci->adaption == 3) {
9988c2ecf20Sopenharmony_ci			/* Over long frame, bin it */
9998c2ecf20Sopenharmony_ci			dev_kfree_skb_any(dlci->skb);
10008c2ecf20Sopenharmony_ci			dlci->skb = NULL;
10018c2ecf20Sopenharmony_ci			return 0;
10028c2ecf20Sopenharmony_ci		}
10038c2ecf20Sopenharmony_ci		len = gsm->mtu;
10048c2ecf20Sopenharmony_ci	} else
10058c2ecf20Sopenharmony_ci		last = 1;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	size = len + overhead;
10088c2ecf20Sopenharmony_ci	msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* FIXME: need a timer or something to kick this so it can't
10118c2ecf20Sopenharmony_ci	   get stuck with no work outstanding and no buffer free */
10128c2ecf20Sopenharmony_ci	if (msg == NULL) {
10138c2ecf20Sopenharmony_ci		skb_queue_tail(&dlci->skb_list, dlci->skb);
10148c2ecf20Sopenharmony_ci		dlci->skb = NULL;
10158c2ecf20Sopenharmony_ci		return -ENOMEM;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci	dp = msg->data;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */
10208c2ecf20Sopenharmony_ci		/* Flag byte to carry the start/end info */
10218c2ecf20Sopenharmony_ci		*dp++ = last << 7 | first << 6 | 1;	/* EA */
10228c2ecf20Sopenharmony_ci		len--;
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci	memcpy(dp, dlci->skb->data, len);
10258c2ecf20Sopenharmony_ci	skb_pull(dlci->skb, len);
10268c2ecf20Sopenharmony_ci	__gsm_data_queue(dlci, msg);
10278c2ecf20Sopenharmony_ci	if (last) {
10288c2ecf20Sopenharmony_ci		dev_kfree_skb_any(dlci->skb);
10298c2ecf20Sopenharmony_ci		dlci->skb = NULL;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci	return size;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci/**
10358c2ecf20Sopenharmony_ci *	gsm_dlci_data_sweep		-	look for data to send
10368c2ecf20Sopenharmony_ci *	@gsm: the GSM mux
10378c2ecf20Sopenharmony_ci *
10388c2ecf20Sopenharmony_ci *	Sweep the GSM mux channels in priority order looking for ones with
10398c2ecf20Sopenharmony_ci *	data to send. We could do with optimising this scan a bit. We aim
10408c2ecf20Sopenharmony_ci *	to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit
10418c2ecf20Sopenharmony_ci *	TX_THRESH_LO we get called again
10428c2ecf20Sopenharmony_ci *
10438c2ecf20Sopenharmony_ci *	FIXME: We should round robin between groups and in theory you can
10448c2ecf20Sopenharmony_ci *	renegotiate DLCI priorities with optional stuff. Needs optimising.
10458c2ecf20Sopenharmony_ci */
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic void gsm_dlci_data_sweep(struct gsm_mux *gsm)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	int len;
10508c2ecf20Sopenharmony_ci	/* Priority ordering: We should do priority with RR of the groups */
10518c2ecf20Sopenharmony_ci	int i = 1;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	while (i < NUM_DLCI) {
10548c2ecf20Sopenharmony_ci		struct gsm_dlci *dlci;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci		if (gsm->tx_bytes > TX_THRESH_HI)
10578c2ecf20Sopenharmony_ci			break;
10588c2ecf20Sopenharmony_ci		dlci = gsm->dlci[i];
10598c2ecf20Sopenharmony_ci		if (dlci == NULL || dlci->constipated) {
10608c2ecf20Sopenharmony_ci			i++;
10618c2ecf20Sopenharmony_ci			continue;
10628c2ecf20Sopenharmony_ci		}
10638c2ecf20Sopenharmony_ci		if (dlci->adaption < 3 && !dlci->net)
10648c2ecf20Sopenharmony_ci			len = gsm_dlci_data_output(gsm, dlci);
10658c2ecf20Sopenharmony_ci		else
10668c2ecf20Sopenharmony_ci			len = gsm_dlci_data_output_framed(gsm, dlci);
10678c2ecf20Sopenharmony_ci		if (len < 0)
10688c2ecf20Sopenharmony_ci			break;
10698c2ecf20Sopenharmony_ci		/* DLCI empty - try the next */
10708c2ecf20Sopenharmony_ci		if (len == 0)
10718c2ecf20Sopenharmony_ci			i++;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci/**
10768c2ecf20Sopenharmony_ci *	gsm_dlci_data_kick	-	transmit if possible
10778c2ecf20Sopenharmony_ci *	@dlci: DLCI to kick
10788c2ecf20Sopenharmony_ci *
10798c2ecf20Sopenharmony_ci *	Transmit data from this DLCI if the queue is empty. We can't rely on
10808c2ecf20Sopenharmony_ci *	a tty wakeup except when we filled the pipe so we need to fire off
10818c2ecf20Sopenharmony_ci *	new data ourselves in other cases.
10828c2ecf20Sopenharmony_ci */
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic void gsm_dlci_data_kick(struct gsm_dlci *dlci)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	unsigned long flags;
10878c2ecf20Sopenharmony_ci	int sweep;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	if (dlci->constipated)
10908c2ecf20Sopenharmony_ci		return;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
10938c2ecf20Sopenharmony_ci	/* If we have nothing running then we need to fire up */
10948c2ecf20Sopenharmony_ci	sweep = (dlci->gsm->tx_bytes < TX_THRESH_LO);
10958c2ecf20Sopenharmony_ci	if (dlci->gsm->tx_bytes == 0) {
10968c2ecf20Sopenharmony_ci		if (dlci->net)
10978c2ecf20Sopenharmony_ci			gsm_dlci_data_output_framed(dlci->gsm, dlci);
10988c2ecf20Sopenharmony_ci		else
10998c2ecf20Sopenharmony_ci			gsm_dlci_data_output(dlci->gsm, dlci);
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci	if (sweep)
11028c2ecf20Sopenharmony_ci		gsm_dlci_data_sweep(dlci->gsm);
11038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci/*
11078c2ecf20Sopenharmony_ci *	Control message processing
11088c2ecf20Sopenharmony_ci */
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci/**
11128c2ecf20Sopenharmony_ci *	gsm_control_reply	-	send a response frame to a control
11138c2ecf20Sopenharmony_ci *	@gsm: gsm channel
11148c2ecf20Sopenharmony_ci *	@cmd: the command to use
11158c2ecf20Sopenharmony_ci *	@data: data to follow encoded info
11168c2ecf20Sopenharmony_ci *	@dlen: length of data
11178c2ecf20Sopenharmony_ci *
11188c2ecf20Sopenharmony_ci *	Encode up and queue a UI/UIH frame containing our response.
11198c2ecf20Sopenharmony_ci */
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
11228c2ecf20Sopenharmony_ci					int dlen)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	struct gsm_msg *msg;
11258c2ecf20Sopenharmony_ci	msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype);
11268c2ecf20Sopenharmony_ci	if (msg == NULL)
11278c2ecf20Sopenharmony_ci		return;
11288c2ecf20Sopenharmony_ci	msg->data[0] = (cmd & 0xFE) << 1 | EA;	/* Clear C/R */
11298c2ecf20Sopenharmony_ci	msg->data[1] = (dlen << 1) | EA;
11308c2ecf20Sopenharmony_ci	memcpy(msg->data + 2, data, dlen);
11318c2ecf20Sopenharmony_ci	gsm_data_queue(gsm->dlci[0], msg);
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci/**
11358c2ecf20Sopenharmony_ci *	gsm_process_modem	-	process received modem status
11368c2ecf20Sopenharmony_ci *	@tty: virtual tty bound to the DLCI
11378c2ecf20Sopenharmony_ci *	@dlci: DLCI to affect
11388c2ecf20Sopenharmony_ci *	@modem: modem bits (full EA)
11398c2ecf20Sopenharmony_ci *
11408c2ecf20Sopenharmony_ci *	Used when a modem control message or line state inline in adaption
11418c2ecf20Sopenharmony_ci *	layer 2 is processed. Sort out the local modem state and throttles
11428c2ecf20Sopenharmony_ci */
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
11458c2ecf20Sopenharmony_ci							u32 modem, int clen)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	int  mlines = 0;
11488c2ecf20Sopenharmony_ci	u8 brk = 0;
11498c2ecf20Sopenharmony_ci	int fc;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	/* The modem status command can either contain one octet (v.24 signals)
11528c2ecf20Sopenharmony_ci	   or two octets (v.24 signals + break signals). The length field will
11538c2ecf20Sopenharmony_ci	   either be 2 or 3 respectively. This is specified in section
11548c2ecf20Sopenharmony_ci	   5.4.6.3.7 of the  27.010 mux spec. */
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	if (clen == 2)
11578c2ecf20Sopenharmony_ci		modem = modem & 0x7f;
11588c2ecf20Sopenharmony_ci	else {
11598c2ecf20Sopenharmony_ci		brk = modem & 0x7f;
11608c2ecf20Sopenharmony_ci		modem = (modem >> 7) & 0x7f;
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/* Flow control/ready to communicate */
11648c2ecf20Sopenharmony_ci	fc = (modem & MDM_FC) || !(modem & MDM_RTR);
11658c2ecf20Sopenharmony_ci	if (fc && !dlci->constipated) {
11668c2ecf20Sopenharmony_ci		/* Need to throttle our output on this device */
11678c2ecf20Sopenharmony_ci		dlci->constipated = true;
11688c2ecf20Sopenharmony_ci	} else if (!fc && dlci->constipated) {
11698c2ecf20Sopenharmony_ci		dlci->constipated = false;
11708c2ecf20Sopenharmony_ci		gsm_dlci_data_kick(dlci);
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	/* Map modem bits */
11748c2ecf20Sopenharmony_ci	if (modem & MDM_RTC)
11758c2ecf20Sopenharmony_ci		mlines |= TIOCM_DSR | TIOCM_DTR;
11768c2ecf20Sopenharmony_ci	if (modem & MDM_RTR)
11778c2ecf20Sopenharmony_ci		mlines |= TIOCM_RTS | TIOCM_CTS;
11788c2ecf20Sopenharmony_ci	if (modem & MDM_IC)
11798c2ecf20Sopenharmony_ci		mlines |= TIOCM_RI;
11808c2ecf20Sopenharmony_ci	if (modem & MDM_DV)
11818c2ecf20Sopenharmony_ci		mlines |= TIOCM_CD;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* Carrier drop -> hangup */
11848c2ecf20Sopenharmony_ci	if (tty) {
11858c2ecf20Sopenharmony_ci		if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD))
11868c2ecf20Sopenharmony_ci			if (!C_CLOCAL(tty))
11878c2ecf20Sopenharmony_ci				tty_hangup(tty);
11888c2ecf20Sopenharmony_ci	}
11898c2ecf20Sopenharmony_ci	if (brk & 0x01)
11908c2ecf20Sopenharmony_ci		tty_insert_flip_char(&dlci->port, 0, TTY_BREAK);
11918c2ecf20Sopenharmony_ci	dlci->modem_rx = mlines;
11928c2ecf20Sopenharmony_ci}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci/**
11958c2ecf20Sopenharmony_ci *	gsm_control_modem	-	modem status received
11968c2ecf20Sopenharmony_ci *	@gsm: GSM channel
11978c2ecf20Sopenharmony_ci *	@data: data following command
11988c2ecf20Sopenharmony_ci *	@clen: command length
11998c2ecf20Sopenharmony_ci *
12008c2ecf20Sopenharmony_ci *	We have received a modem status control message. This is used by
12018c2ecf20Sopenharmony_ci *	the GSM mux protocol to pass virtual modem line status and optionally
12028c2ecf20Sopenharmony_ci *	to indicate break signals. Unpack it, convert to Linux representation
12038c2ecf20Sopenharmony_ci *	and if need be stuff a break message down the tty.
12048c2ecf20Sopenharmony_ci */
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	unsigned int addr = 0;
12098c2ecf20Sopenharmony_ci	unsigned int modem = 0;
12108c2ecf20Sopenharmony_ci	unsigned int brk = 0;
12118c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
12128c2ecf20Sopenharmony_ci	int len = clen;
12138c2ecf20Sopenharmony_ci	const u8 *dp = data;
12148c2ecf20Sopenharmony_ci	struct tty_struct *tty;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	while (gsm_read_ea(&addr, *dp++) == 0) {
12178c2ecf20Sopenharmony_ci		len--;
12188c2ecf20Sopenharmony_ci		if (len == 0)
12198c2ecf20Sopenharmony_ci			return;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci	/* Must be at least one byte following the EA */
12228c2ecf20Sopenharmony_ci	len--;
12238c2ecf20Sopenharmony_ci	if (len <= 0)
12248c2ecf20Sopenharmony_ci		return;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	addr >>= 1;
12278c2ecf20Sopenharmony_ci	/* Closed port, or invalid ? */
12288c2ecf20Sopenharmony_ci	if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL)
12298c2ecf20Sopenharmony_ci		return;
12308c2ecf20Sopenharmony_ci	dlci = gsm->dlci[addr];
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	while (gsm_read_ea(&modem, *dp++) == 0) {
12338c2ecf20Sopenharmony_ci		len--;
12348c2ecf20Sopenharmony_ci		if (len == 0)
12358c2ecf20Sopenharmony_ci			return;
12368c2ecf20Sopenharmony_ci	}
12378c2ecf20Sopenharmony_ci	len--;
12388c2ecf20Sopenharmony_ci	if (len > 0) {
12398c2ecf20Sopenharmony_ci		while (gsm_read_ea(&brk, *dp++) == 0) {
12408c2ecf20Sopenharmony_ci			len--;
12418c2ecf20Sopenharmony_ci			if (len == 0)
12428c2ecf20Sopenharmony_ci				return;
12438c2ecf20Sopenharmony_ci		}
12448c2ecf20Sopenharmony_ci		modem <<= 7;
12458c2ecf20Sopenharmony_ci		modem |= (brk & 0x7f);
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&dlci->port);
12488c2ecf20Sopenharmony_ci	gsm_process_modem(tty, dlci, modem, clen);
12498c2ecf20Sopenharmony_ci	if (tty) {
12508c2ecf20Sopenharmony_ci		tty_wakeup(tty);
12518c2ecf20Sopenharmony_ci		tty_kref_put(tty);
12528c2ecf20Sopenharmony_ci	}
12538c2ecf20Sopenharmony_ci	gsm_control_reply(gsm, CMD_MSC, data, clen);
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci/**
12578c2ecf20Sopenharmony_ci *	gsm_control_rls		-	remote line status
12588c2ecf20Sopenharmony_ci *	@gsm: GSM channel
12598c2ecf20Sopenharmony_ci *	@data: data bytes
12608c2ecf20Sopenharmony_ci *	@clen: data length
12618c2ecf20Sopenharmony_ci *
12628c2ecf20Sopenharmony_ci *	The modem sends us a two byte message on the control channel whenever
12638c2ecf20Sopenharmony_ci *	it wishes to send us an error state from the virtual link. Stuff
12648c2ecf20Sopenharmony_ci *	this into the uplink tty if present
12658c2ecf20Sopenharmony_ci */
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_cistatic void gsm_control_rls(struct gsm_mux *gsm, const u8 *data, int clen)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	struct tty_port *port;
12708c2ecf20Sopenharmony_ci	unsigned int addr = 0;
12718c2ecf20Sopenharmony_ci	u8 bits;
12728c2ecf20Sopenharmony_ci	int len = clen;
12738c2ecf20Sopenharmony_ci	const u8 *dp = data;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	while (gsm_read_ea(&addr, *dp++) == 0) {
12768c2ecf20Sopenharmony_ci		len--;
12778c2ecf20Sopenharmony_ci		if (len == 0)
12788c2ecf20Sopenharmony_ci			return;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci	/* Must be at least one byte following ea */
12818c2ecf20Sopenharmony_ci	len--;
12828c2ecf20Sopenharmony_ci	if (len <= 0)
12838c2ecf20Sopenharmony_ci		return;
12848c2ecf20Sopenharmony_ci	addr >>= 1;
12858c2ecf20Sopenharmony_ci	/* Closed port, or invalid ? */
12868c2ecf20Sopenharmony_ci	if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL)
12878c2ecf20Sopenharmony_ci		return;
12888c2ecf20Sopenharmony_ci	/* No error ? */
12898c2ecf20Sopenharmony_ci	bits = *dp;
12908c2ecf20Sopenharmony_ci	if ((bits & 1) == 0)
12918c2ecf20Sopenharmony_ci		return;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	port = &gsm->dlci[addr]->port;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	if (bits & 2)
12968c2ecf20Sopenharmony_ci		tty_insert_flip_char(port, 0, TTY_OVERRUN);
12978c2ecf20Sopenharmony_ci	if (bits & 4)
12988c2ecf20Sopenharmony_ci		tty_insert_flip_char(port, 0, TTY_PARITY);
12998c2ecf20Sopenharmony_ci	if (bits & 8)
13008c2ecf20Sopenharmony_ci		tty_insert_flip_char(port, 0, TTY_FRAME);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	tty_flip_buffer_push(port);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	gsm_control_reply(gsm, CMD_RLS, data, clen);
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_cistatic void gsm_dlci_begin_close(struct gsm_dlci *dlci);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci/**
13108c2ecf20Sopenharmony_ci *	gsm_control_message	-	DLCI 0 control processing
13118c2ecf20Sopenharmony_ci *	@gsm: our GSM mux
13128c2ecf20Sopenharmony_ci *	@command:  the command EA
13138c2ecf20Sopenharmony_ci *	@data: data beyond the command/length EAs
13148c2ecf20Sopenharmony_ci *	@clen: length
13158c2ecf20Sopenharmony_ci *
13168c2ecf20Sopenharmony_ci *	Input processor for control messages from the other end of the link.
13178c2ecf20Sopenharmony_ci *	Processes the incoming request and queues a response frame or an
13188c2ecf20Sopenharmony_ci *	NSC response if not supported
13198c2ecf20Sopenharmony_ci */
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_cistatic void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
13228c2ecf20Sopenharmony_ci						const u8 *data, int clen)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	u8 buf[1];
13258c2ecf20Sopenharmony_ci	unsigned long flags;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	switch (command) {
13288c2ecf20Sopenharmony_ci	case CMD_CLD: {
13298c2ecf20Sopenharmony_ci		struct gsm_dlci *dlci = gsm->dlci[0];
13308c2ecf20Sopenharmony_ci		/* Modem wishes to close down */
13318c2ecf20Sopenharmony_ci		if (dlci) {
13328c2ecf20Sopenharmony_ci			dlci->dead = true;
13338c2ecf20Sopenharmony_ci			gsm->dead = true;
13348c2ecf20Sopenharmony_ci			gsm_dlci_begin_close(dlci);
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci		}
13378c2ecf20Sopenharmony_ci		break;
13388c2ecf20Sopenharmony_ci	case CMD_TEST:
13398c2ecf20Sopenharmony_ci		/* Modem wishes to test, reply with the data */
13408c2ecf20Sopenharmony_ci		gsm_control_reply(gsm, CMD_TEST, data, clen);
13418c2ecf20Sopenharmony_ci		break;
13428c2ecf20Sopenharmony_ci	case CMD_FCON:
13438c2ecf20Sopenharmony_ci		/* Modem can accept data again */
13448c2ecf20Sopenharmony_ci		gsm->constipated = false;
13458c2ecf20Sopenharmony_ci		gsm_control_reply(gsm, CMD_FCON, NULL, 0);
13468c2ecf20Sopenharmony_ci		/* Kick the link in case it is idling */
13478c2ecf20Sopenharmony_ci		spin_lock_irqsave(&gsm->tx_lock, flags);
13488c2ecf20Sopenharmony_ci		gsm_data_kick(gsm, NULL);
13498c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&gsm->tx_lock, flags);
13508c2ecf20Sopenharmony_ci		break;
13518c2ecf20Sopenharmony_ci	case CMD_FCOFF:
13528c2ecf20Sopenharmony_ci		/* Modem wants us to STFU */
13538c2ecf20Sopenharmony_ci		gsm->constipated = true;
13548c2ecf20Sopenharmony_ci		gsm_control_reply(gsm, CMD_FCOFF, NULL, 0);
13558c2ecf20Sopenharmony_ci		break;
13568c2ecf20Sopenharmony_ci	case CMD_MSC:
13578c2ecf20Sopenharmony_ci		/* Out of band modem line change indicator for a DLCI */
13588c2ecf20Sopenharmony_ci		gsm_control_modem(gsm, data, clen);
13598c2ecf20Sopenharmony_ci		break;
13608c2ecf20Sopenharmony_ci	case CMD_RLS:
13618c2ecf20Sopenharmony_ci		/* Out of band error reception for a DLCI */
13628c2ecf20Sopenharmony_ci		gsm_control_rls(gsm, data, clen);
13638c2ecf20Sopenharmony_ci		break;
13648c2ecf20Sopenharmony_ci	case CMD_PSC:
13658c2ecf20Sopenharmony_ci		/* Modem wishes to enter power saving state */
13668c2ecf20Sopenharmony_ci		gsm_control_reply(gsm, CMD_PSC, NULL, 0);
13678c2ecf20Sopenharmony_ci		break;
13688c2ecf20Sopenharmony_ci		/* Optional unsupported commands */
13698c2ecf20Sopenharmony_ci	case CMD_PN:	/* Parameter negotiation */
13708c2ecf20Sopenharmony_ci	case CMD_RPN:	/* Remote port negotiation */
13718c2ecf20Sopenharmony_ci	case CMD_SNC:	/* Service negotiation command */
13728c2ecf20Sopenharmony_ci	default:
13738c2ecf20Sopenharmony_ci		/* Reply to bad commands with an NSC */
13748c2ecf20Sopenharmony_ci		buf[0] = command;
13758c2ecf20Sopenharmony_ci		gsm_control_reply(gsm, CMD_NSC, buf, 1);
13768c2ecf20Sopenharmony_ci		break;
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci/**
13818c2ecf20Sopenharmony_ci *	gsm_control_response	-	process a response to our control
13828c2ecf20Sopenharmony_ci *	@gsm: our GSM mux
13838c2ecf20Sopenharmony_ci *	@command: the command (response) EA
13848c2ecf20Sopenharmony_ci *	@data: data beyond the command/length EA
13858c2ecf20Sopenharmony_ci *	@clen: length
13868c2ecf20Sopenharmony_ci *
13878c2ecf20Sopenharmony_ci *	Process a response to an outstanding command. We only allow a single
13888c2ecf20Sopenharmony_ci *	control message in flight so this is fairly easy. All the clean up
13898c2ecf20Sopenharmony_ci *	is done by the caller, we just update the fields, flag it as done
13908c2ecf20Sopenharmony_ci *	and return
13918c2ecf20Sopenharmony_ci */
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_cistatic void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
13948c2ecf20Sopenharmony_ci						const u8 *data, int clen)
13958c2ecf20Sopenharmony_ci{
13968c2ecf20Sopenharmony_ci	struct gsm_control *ctrl;
13978c2ecf20Sopenharmony_ci	unsigned long flags;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm->control_lock, flags);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	ctrl = gsm->pending_cmd;
14028c2ecf20Sopenharmony_ci	/* Does the reply match our command */
14038c2ecf20Sopenharmony_ci	command |= 1;
14048c2ecf20Sopenharmony_ci	if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
14058c2ecf20Sopenharmony_ci		/* Our command was replied to, kill the retry timer */
14068c2ecf20Sopenharmony_ci		del_timer(&gsm->t2_timer);
14078c2ecf20Sopenharmony_ci		gsm->pending_cmd = NULL;
14088c2ecf20Sopenharmony_ci		/* Rejected by the other end */
14098c2ecf20Sopenharmony_ci		if (command == CMD_NSC)
14108c2ecf20Sopenharmony_ci			ctrl->error = -EOPNOTSUPP;
14118c2ecf20Sopenharmony_ci		ctrl->done = 1;
14128c2ecf20Sopenharmony_ci		wake_up(&gsm->event);
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm->control_lock, flags);
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci/**
14188c2ecf20Sopenharmony_ci *	gsm_control_transmit	-	send control packet
14198c2ecf20Sopenharmony_ci *	@gsm: gsm mux
14208c2ecf20Sopenharmony_ci *	@ctrl: frame to send
14218c2ecf20Sopenharmony_ci *
14228c2ecf20Sopenharmony_ci *	Send out a pending control command (called under control lock)
14238c2ecf20Sopenharmony_ci */
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 2, gsm->ftype);
14288c2ecf20Sopenharmony_ci	if (msg == NULL)
14298c2ecf20Sopenharmony_ci		return;
14308c2ecf20Sopenharmony_ci	msg->data[0] = (ctrl->cmd << 1) | CR | EA;	/* command */
14318c2ecf20Sopenharmony_ci	msg->data[1] = (ctrl->len << 1) | EA;
14328c2ecf20Sopenharmony_ci	memcpy(msg->data + 2, ctrl->data, ctrl->len);
14338c2ecf20Sopenharmony_ci	gsm_data_queue(gsm->dlci[0], msg);
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci/**
14378c2ecf20Sopenharmony_ci *	gsm_control_retransmit	-	retransmit a control frame
14388c2ecf20Sopenharmony_ci *	@t: timer contained in our gsm object
14398c2ecf20Sopenharmony_ci *
14408c2ecf20Sopenharmony_ci *	Called off the T2 timer expiry in order to retransmit control frames
14418c2ecf20Sopenharmony_ci *	that have been lost in the system somewhere. The control_lock protects
14428c2ecf20Sopenharmony_ci *	us from colliding with another sender or a receive completion event.
14438c2ecf20Sopenharmony_ci *	In that situation the timer may still occur in a small window but
14448c2ecf20Sopenharmony_ci *	gsm->pending_cmd will be NULL and we just let the timer expire.
14458c2ecf20Sopenharmony_ci */
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cistatic void gsm_control_retransmit(struct timer_list *t)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = from_timer(gsm, t, t2_timer);
14508c2ecf20Sopenharmony_ci	struct gsm_control *ctrl;
14518c2ecf20Sopenharmony_ci	unsigned long flags;
14528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm->control_lock, flags);
14538c2ecf20Sopenharmony_ci	ctrl = gsm->pending_cmd;
14548c2ecf20Sopenharmony_ci	if (ctrl) {
14558c2ecf20Sopenharmony_ci		if (gsm->cretries == 0 || !gsm->dlci[0] || gsm->dlci[0]->dead) {
14568c2ecf20Sopenharmony_ci			gsm->pending_cmd = NULL;
14578c2ecf20Sopenharmony_ci			ctrl->error = -ETIMEDOUT;
14588c2ecf20Sopenharmony_ci			ctrl->done = 1;
14598c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&gsm->control_lock, flags);
14608c2ecf20Sopenharmony_ci			wake_up(&gsm->event);
14618c2ecf20Sopenharmony_ci			return;
14628c2ecf20Sopenharmony_ci		}
14638c2ecf20Sopenharmony_ci		gsm->cretries--;
14648c2ecf20Sopenharmony_ci		gsm_control_transmit(gsm, ctrl);
14658c2ecf20Sopenharmony_ci		mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
14668c2ecf20Sopenharmony_ci	}
14678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm->control_lock, flags);
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/**
14718c2ecf20Sopenharmony_ci *	gsm_control_send	-	send a control frame on DLCI 0
14728c2ecf20Sopenharmony_ci *	@gsm: the GSM channel
14738c2ecf20Sopenharmony_ci *	@command: command  to send including CR bit
14748c2ecf20Sopenharmony_ci *	@data: bytes of data (must be kmalloced)
14758c2ecf20Sopenharmony_ci *	@clen: length of the block to send
14768c2ecf20Sopenharmony_ci *
14778c2ecf20Sopenharmony_ci *	Queue and dispatch a control command. Only one command can be
14788c2ecf20Sopenharmony_ci *	active at a time. In theory more can be outstanding but the matching
14798c2ecf20Sopenharmony_ci *	gets really complicated so for now stick to one outstanding.
14808c2ecf20Sopenharmony_ci */
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_cistatic struct gsm_control *gsm_control_send(struct gsm_mux *gsm,
14838c2ecf20Sopenharmony_ci		unsigned int command, u8 *data, int clen)
14848c2ecf20Sopenharmony_ci{
14858c2ecf20Sopenharmony_ci	struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control),
14868c2ecf20Sopenharmony_ci						GFP_ATOMIC);
14878c2ecf20Sopenharmony_ci	unsigned long flags;
14888c2ecf20Sopenharmony_ci	if (ctrl == NULL)
14898c2ecf20Sopenharmony_ci		return NULL;
14908c2ecf20Sopenharmony_ciretry:
14918c2ecf20Sopenharmony_ci	wait_event(gsm->event, gsm->pending_cmd == NULL);
14928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm->control_lock, flags);
14938c2ecf20Sopenharmony_ci	if (gsm->pending_cmd != NULL) {
14948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&gsm->control_lock, flags);
14958c2ecf20Sopenharmony_ci		goto retry;
14968c2ecf20Sopenharmony_ci	}
14978c2ecf20Sopenharmony_ci	ctrl->cmd = command;
14988c2ecf20Sopenharmony_ci	ctrl->data = data;
14998c2ecf20Sopenharmony_ci	ctrl->len = clen;
15008c2ecf20Sopenharmony_ci	gsm->pending_cmd = ctrl;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	/* If DLCI0 is in ADM mode skip retries, it won't respond */
15038c2ecf20Sopenharmony_ci	if (gsm->dlci[0]->mode == DLCI_MODE_ADM)
15048c2ecf20Sopenharmony_ci		gsm->cretries = 0;
15058c2ecf20Sopenharmony_ci	else
15068c2ecf20Sopenharmony_ci		gsm->cretries = gsm->n2;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
15098c2ecf20Sopenharmony_ci	gsm_control_transmit(gsm, ctrl);
15108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm->control_lock, flags);
15118c2ecf20Sopenharmony_ci	return ctrl;
15128c2ecf20Sopenharmony_ci}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci/**
15158c2ecf20Sopenharmony_ci *	gsm_control_wait	-	wait for a control to finish
15168c2ecf20Sopenharmony_ci *	@gsm: GSM mux
15178c2ecf20Sopenharmony_ci *	@control: control we are waiting on
15188c2ecf20Sopenharmony_ci *
15198c2ecf20Sopenharmony_ci *	Waits for the control to complete or time out. Frees any used
15208c2ecf20Sopenharmony_ci *	resources and returns 0 for success, or an error if the remote
15218c2ecf20Sopenharmony_ci *	rejected or ignored the request.
15228c2ecf20Sopenharmony_ci */
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	int err;
15278c2ecf20Sopenharmony_ci	wait_event(gsm->event, control->done == 1);
15288c2ecf20Sopenharmony_ci	err = control->error;
15298c2ecf20Sopenharmony_ci	kfree(control);
15308c2ecf20Sopenharmony_ci	return err;
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci/*
15358c2ecf20Sopenharmony_ci *	DLCI level handling: Needs krefs
15368c2ecf20Sopenharmony_ci */
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci/*
15398c2ecf20Sopenharmony_ci *	State transitions and timers
15408c2ecf20Sopenharmony_ci */
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci/**
15438c2ecf20Sopenharmony_ci *	gsm_dlci_close		-	a DLCI has closed
15448c2ecf20Sopenharmony_ci *	@dlci: DLCI that closed
15458c2ecf20Sopenharmony_ci *
15468c2ecf20Sopenharmony_ci *	Perform processing when moving a DLCI into closed state. If there
15478c2ecf20Sopenharmony_ci *	is an attached tty this is hung up
15488c2ecf20Sopenharmony_ci */
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic void gsm_dlci_close(struct gsm_dlci *dlci)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	unsigned long flags;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	del_timer(&dlci->t1);
15558c2ecf20Sopenharmony_ci	if (debug & 8)
15568c2ecf20Sopenharmony_ci		pr_debug("DLCI %d goes closed.\n", dlci->addr);
15578c2ecf20Sopenharmony_ci	dlci->state = DLCI_CLOSED;
15588c2ecf20Sopenharmony_ci	/* Prevent us from sending data before the link is up again */
15598c2ecf20Sopenharmony_ci	dlci->constipated = true;
15608c2ecf20Sopenharmony_ci	if (dlci->addr != 0) {
15618c2ecf20Sopenharmony_ci		tty_port_tty_hangup(&dlci->port, false);
15628c2ecf20Sopenharmony_ci		spin_lock_irqsave(&dlci->lock, flags);
15638c2ecf20Sopenharmony_ci		kfifo_reset(&dlci->fifo);
15648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&dlci->lock, flags);
15658c2ecf20Sopenharmony_ci		/* Ensure that gsmtty_open() can return. */
15668c2ecf20Sopenharmony_ci		tty_port_set_initialized(&dlci->port, 0);
15678c2ecf20Sopenharmony_ci		wake_up_interruptible(&dlci->port.open_wait);
15688c2ecf20Sopenharmony_ci	} else
15698c2ecf20Sopenharmony_ci		dlci->gsm->dead = true;
15708c2ecf20Sopenharmony_ci	wake_up(&dlci->gsm->event);
15718c2ecf20Sopenharmony_ci	/* A DLCI 0 close is a MUX termination so we need to kick that
15728c2ecf20Sopenharmony_ci	   back to userspace somehow */
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci/**
15768c2ecf20Sopenharmony_ci *	gsm_dlci_open		-	a DLCI has opened
15778c2ecf20Sopenharmony_ci *	@dlci: DLCI that opened
15788c2ecf20Sopenharmony_ci *
15798c2ecf20Sopenharmony_ci *	Perform processing when moving a DLCI into open state.
15808c2ecf20Sopenharmony_ci */
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cistatic void gsm_dlci_open(struct gsm_dlci *dlci)
15838c2ecf20Sopenharmony_ci{
15848c2ecf20Sopenharmony_ci	/* Note that SABM UA .. SABM UA first UA lost can mean that we go
15858c2ecf20Sopenharmony_ci	   open -> open */
15868c2ecf20Sopenharmony_ci	del_timer(&dlci->t1);
15878c2ecf20Sopenharmony_ci	/* This will let a tty open continue */
15888c2ecf20Sopenharmony_ci	dlci->state = DLCI_OPEN;
15898c2ecf20Sopenharmony_ci	dlci->constipated = false;
15908c2ecf20Sopenharmony_ci	if (debug & 8)
15918c2ecf20Sopenharmony_ci		pr_debug("DLCI %d goes open.\n", dlci->addr);
15928c2ecf20Sopenharmony_ci	wake_up(&dlci->gsm->event);
15938c2ecf20Sopenharmony_ci}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci/**
15968c2ecf20Sopenharmony_ci *	gsm_dlci_t1		-	T1 timer expiry
15978c2ecf20Sopenharmony_ci *	@t: timer contained in the DLCI that opened
15988c2ecf20Sopenharmony_ci *
15998c2ecf20Sopenharmony_ci *	The T1 timer handles retransmits of control frames (essentially of
16008c2ecf20Sopenharmony_ci *	SABM and DISC). We resend the command until the retry count runs out
16018c2ecf20Sopenharmony_ci *	in which case an opening port goes back to closed and a closing port
16028c2ecf20Sopenharmony_ci *	is simply put into closed state (any further frames from the other
16038c2ecf20Sopenharmony_ci *	end will get a DM response)
16048c2ecf20Sopenharmony_ci *
16058c2ecf20Sopenharmony_ci *	Some control dlci can stay in ADM mode with other dlci working just
16068c2ecf20Sopenharmony_ci *	fine. In that case we can just keep the control dlci open after the
16078c2ecf20Sopenharmony_ci *	DLCI_OPENING retries time out.
16088c2ecf20Sopenharmony_ci */
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic void gsm_dlci_t1(struct timer_list *t)
16118c2ecf20Sopenharmony_ci{
16128c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = from_timer(dlci, t, t1);
16138c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	switch (dlci->state) {
16168c2ecf20Sopenharmony_ci	case DLCI_OPENING:
16178c2ecf20Sopenharmony_ci		if (dlci->retries) {
16188c2ecf20Sopenharmony_ci			dlci->retries--;
16198c2ecf20Sopenharmony_ci			gsm_command(dlci->gsm, dlci->addr, SABM|PF);
16208c2ecf20Sopenharmony_ci			mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
16218c2ecf20Sopenharmony_ci		} else if (!dlci->addr && gsm->control == (DM | PF)) {
16228c2ecf20Sopenharmony_ci			if (debug & 8)
16238c2ecf20Sopenharmony_ci				pr_info("DLCI %d opening in ADM mode.\n",
16248c2ecf20Sopenharmony_ci					dlci->addr);
16258c2ecf20Sopenharmony_ci			dlci->mode = DLCI_MODE_ADM;
16268c2ecf20Sopenharmony_ci			gsm_dlci_open(dlci);
16278c2ecf20Sopenharmony_ci		} else {
16288c2ecf20Sopenharmony_ci			gsm_dlci_begin_close(dlci); /* prevent half open link */
16298c2ecf20Sopenharmony_ci		}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		break;
16328c2ecf20Sopenharmony_ci	case DLCI_CLOSING:
16338c2ecf20Sopenharmony_ci		if (dlci->retries) {
16348c2ecf20Sopenharmony_ci			dlci->retries--;
16358c2ecf20Sopenharmony_ci			gsm_command(dlci->gsm, dlci->addr, DISC|PF);
16368c2ecf20Sopenharmony_ci			mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
16378c2ecf20Sopenharmony_ci		} else
16388c2ecf20Sopenharmony_ci			gsm_dlci_close(dlci);
16398c2ecf20Sopenharmony_ci		break;
16408c2ecf20Sopenharmony_ci	default:
16418c2ecf20Sopenharmony_ci		pr_debug("%s: unhandled state: %d\n", __func__, dlci->state);
16428c2ecf20Sopenharmony_ci		break;
16438c2ecf20Sopenharmony_ci	}
16448c2ecf20Sopenharmony_ci}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci/**
16478c2ecf20Sopenharmony_ci *	gsm_dlci_begin_open	-	start channel open procedure
16488c2ecf20Sopenharmony_ci *	@dlci: DLCI to open
16498c2ecf20Sopenharmony_ci *
16508c2ecf20Sopenharmony_ci *	Commence opening a DLCI from the Linux side. We issue SABM messages
16518c2ecf20Sopenharmony_ci *	to the modem which should then reply with a UA or ADM, at which point
16528c2ecf20Sopenharmony_ci *	we will move into open state. Opening is done asynchronously with retry
16538c2ecf20Sopenharmony_ci *	running off timers and the responses.
16548c2ecf20Sopenharmony_ci */
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_cistatic void gsm_dlci_begin_open(struct gsm_dlci *dlci)
16578c2ecf20Sopenharmony_ci{
16588c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
16598c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING)
16608c2ecf20Sopenharmony_ci		return;
16618c2ecf20Sopenharmony_ci	dlci->retries = gsm->n2;
16628c2ecf20Sopenharmony_ci	dlci->state = DLCI_OPENING;
16638c2ecf20Sopenharmony_ci	gsm_command(dlci->gsm, dlci->addr, SABM|PF);
16648c2ecf20Sopenharmony_ci	mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
16658c2ecf20Sopenharmony_ci}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci/**
16688c2ecf20Sopenharmony_ci *	gsm_dlci_set_opening	-	change state to opening
16698c2ecf20Sopenharmony_ci *	@dlci: DLCI to open
16708c2ecf20Sopenharmony_ci *
16718c2ecf20Sopenharmony_ci *	Change internal state to wait for DLCI open from initiator side.
16728c2ecf20Sopenharmony_ci *	We set off timers and responses upon reception of an SABM.
16738c2ecf20Sopenharmony_ci */
16748c2ecf20Sopenharmony_cistatic void gsm_dlci_set_opening(struct gsm_dlci *dlci)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	switch (dlci->state) {
16778c2ecf20Sopenharmony_ci	case DLCI_CLOSED:
16788c2ecf20Sopenharmony_ci	case DLCI_CLOSING:
16798c2ecf20Sopenharmony_ci		dlci->state = DLCI_OPENING;
16808c2ecf20Sopenharmony_ci		break;
16818c2ecf20Sopenharmony_ci	default:
16828c2ecf20Sopenharmony_ci		break;
16838c2ecf20Sopenharmony_ci	}
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci/**
16878c2ecf20Sopenharmony_ci *	gsm_dlci_begin_close	-	start channel open procedure
16888c2ecf20Sopenharmony_ci *	@dlci: DLCI to open
16898c2ecf20Sopenharmony_ci *
16908c2ecf20Sopenharmony_ci *	Commence closing a DLCI from the Linux side. We issue DISC messages
16918c2ecf20Sopenharmony_ci *	to the modem which should then reply with a UA, at which point we
16928c2ecf20Sopenharmony_ci *	will move into closed state. Closing is done asynchronously with retry
16938c2ecf20Sopenharmony_ci *	off timers. We may also receive a DM reply from the other end which
16948c2ecf20Sopenharmony_ci *	indicates the channel was already closed.
16958c2ecf20Sopenharmony_ci */
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic void gsm_dlci_begin_close(struct gsm_dlci *dlci)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
17008c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING)
17018c2ecf20Sopenharmony_ci		return;
17028c2ecf20Sopenharmony_ci	dlci->retries = gsm->n2;
17038c2ecf20Sopenharmony_ci	dlci->state = DLCI_CLOSING;
17048c2ecf20Sopenharmony_ci	gsm_command(dlci->gsm, dlci->addr, DISC|PF);
17058c2ecf20Sopenharmony_ci	mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
17068c2ecf20Sopenharmony_ci}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci/**
17098c2ecf20Sopenharmony_ci *	gsm_dlci_data		-	data arrived
17108c2ecf20Sopenharmony_ci *	@dlci: channel
17118c2ecf20Sopenharmony_ci *	@data: block of bytes received
17128c2ecf20Sopenharmony_ci *	@clen: length of received block
17138c2ecf20Sopenharmony_ci *
17148c2ecf20Sopenharmony_ci *	A UI or UIH frame has arrived which contains data for a channel
17158c2ecf20Sopenharmony_ci *	other than the control channel. If the relevant virtual tty is
17168c2ecf20Sopenharmony_ci *	open we shovel the bits down it, if not we drop them.
17178c2ecf20Sopenharmony_ci */
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_cistatic void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
17208c2ecf20Sopenharmony_ci{
17218c2ecf20Sopenharmony_ci	/* krefs .. */
17228c2ecf20Sopenharmony_ci	struct tty_port *port = &dlci->port;
17238c2ecf20Sopenharmony_ci	struct tty_struct *tty;
17248c2ecf20Sopenharmony_ci	unsigned int modem = 0;
17258c2ecf20Sopenharmony_ci	int len = clen;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	if (debug & 16)
17288c2ecf20Sopenharmony_ci		pr_debug("%d bytes for tty\n", len);
17298c2ecf20Sopenharmony_ci	switch (dlci->adaption)  {
17308c2ecf20Sopenharmony_ci	/* Unsupported types */
17318c2ecf20Sopenharmony_ci	case 4:		/* Packetised interruptible data */
17328c2ecf20Sopenharmony_ci		break;
17338c2ecf20Sopenharmony_ci	case 3:		/* Packetised uininterruptible voice/data */
17348c2ecf20Sopenharmony_ci		break;
17358c2ecf20Sopenharmony_ci	case 2:		/* Asynchronous serial with line state in each frame */
17368c2ecf20Sopenharmony_ci		while (gsm_read_ea(&modem, *data++) == 0) {
17378c2ecf20Sopenharmony_ci			len--;
17388c2ecf20Sopenharmony_ci			if (len == 0)
17398c2ecf20Sopenharmony_ci				return;
17408c2ecf20Sopenharmony_ci		}
17418c2ecf20Sopenharmony_ci		tty = tty_port_tty_get(port);
17428c2ecf20Sopenharmony_ci		if (tty) {
17438c2ecf20Sopenharmony_ci			gsm_process_modem(tty, dlci, modem, clen);
17448c2ecf20Sopenharmony_ci			tty_kref_put(tty);
17458c2ecf20Sopenharmony_ci		}
17468c2ecf20Sopenharmony_ci		fallthrough;
17478c2ecf20Sopenharmony_ci	case 1:		/* Line state will go via DLCI 0 controls only */
17488c2ecf20Sopenharmony_ci	default:
17498c2ecf20Sopenharmony_ci		tty_insert_flip_string(port, data, len);
17508c2ecf20Sopenharmony_ci		tty_flip_buffer_push(port);
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci/**
17558c2ecf20Sopenharmony_ci *	gsm_dlci_control	-	data arrived on control channel
17568c2ecf20Sopenharmony_ci *	@dlci: channel
17578c2ecf20Sopenharmony_ci *	@data: block of bytes received
17588c2ecf20Sopenharmony_ci *	@len: length of received block
17598c2ecf20Sopenharmony_ci *
17608c2ecf20Sopenharmony_ci *	A UI or UIH frame has arrived which contains data for DLCI 0 the
17618c2ecf20Sopenharmony_ci *	control channel. This should contain a command EA followed by
17628c2ecf20Sopenharmony_ci *	control data bytes. The command EA contains a command/response bit
17638c2ecf20Sopenharmony_ci *	and we divide up the work accordingly.
17648c2ecf20Sopenharmony_ci */
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	/* See what command is involved */
17698c2ecf20Sopenharmony_ci	unsigned int command = 0;
17708c2ecf20Sopenharmony_ci	while (len-- > 0) {
17718c2ecf20Sopenharmony_ci		if (gsm_read_ea(&command, *data++) == 1) {
17728c2ecf20Sopenharmony_ci			int clen = *data++;
17738c2ecf20Sopenharmony_ci			len--;
17748c2ecf20Sopenharmony_ci			/* FIXME: this is properly an EA */
17758c2ecf20Sopenharmony_ci			clen >>= 1;
17768c2ecf20Sopenharmony_ci			/* Malformed command ? */
17778c2ecf20Sopenharmony_ci			if (clen > len)
17788c2ecf20Sopenharmony_ci				return;
17798c2ecf20Sopenharmony_ci			if (command & 1)
17808c2ecf20Sopenharmony_ci				gsm_control_message(dlci->gsm, command,
17818c2ecf20Sopenharmony_ci								data, clen);
17828c2ecf20Sopenharmony_ci			else
17838c2ecf20Sopenharmony_ci				gsm_control_response(dlci->gsm, command,
17848c2ecf20Sopenharmony_ci								data, clen);
17858c2ecf20Sopenharmony_ci			return;
17868c2ecf20Sopenharmony_ci		}
17878c2ecf20Sopenharmony_ci	}
17888c2ecf20Sopenharmony_ci}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci/*
17918c2ecf20Sopenharmony_ci *	Allocate/Free DLCI channels
17928c2ecf20Sopenharmony_ci */
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci/**
17958c2ecf20Sopenharmony_ci *	gsm_dlci_alloc		-	allocate a DLCI
17968c2ecf20Sopenharmony_ci *	@gsm: GSM mux
17978c2ecf20Sopenharmony_ci *	@addr: address of the DLCI
17988c2ecf20Sopenharmony_ci *
17998c2ecf20Sopenharmony_ci *	Allocate and install a new DLCI object into the GSM mux.
18008c2ecf20Sopenharmony_ci *
18018c2ecf20Sopenharmony_ci *	FIXME: review locking races
18028c2ecf20Sopenharmony_ci */
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_cistatic struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
18058c2ecf20Sopenharmony_ci{
18068c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC);
18078c2ecf20Sopenharmony_ci	if (dlci == NULL)
18088c2ecf20Sopenharmony_ci		return NULL;
18098c2ecf20Sopenharmony_ci	spin_lock_init(&dlci->lock);
18108c2ecf20Sopenharmony_ci	mutex_init(&dlci->mutex);
18118c2ecf20Sopenharmony_ci	if (kfifo_alloc(&dlci->fifo, 4096, GFP_KERNEL) < 0) {
18128c2ecf20Sopenharmony_ci		kfree(dlci);
18138c2ecf20Sopenharmony_ci		return NULL;
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	skb_queue_head_init(&dlci->skb_list);
18178c2ecf20Sopenharmony_ci	timer_setup(&dlci->t1, gsm_dlci_t1, 0);
18188c2ecf20Sopenharmony_ci	tty_port_init(&dlci->port);
18198c2ecf20Sopenharmony_ci	dlci->port.ops = &gsm_port_ops;
18208c2ecf20Sopenharmony_ci	dlci->gsm = gsm;
18218c2ecf20Sopenharmony_ci	dlci->addr = addr;
18228c2ecf20Sopenharmony_ci	dlci->adaption = gsm->adaption;
18238c2ecf20Sopenharmony_ci	dlci->state = DLCI_CLOSED;
18248c2ecf20Sopenharmony_ci	if (addr) {
18258c2ecf20Sopenharmony_ci		dlci->data = gsm_dlci_data;
18268c2ecf20Sopenharmony_ci		/* Prevent us from sending data before the link is up */
18278c2ecf20Sopenharmony_ci		dlci->constipated = true;
18288c2ecf20Sopenharmony_ci	} else {
18298c2ecf20Sopenharmony_ci		dlci->data = gsm_dlci_command;
18308c2ecf20Sopenharmony_ci	}
18318c2ecf20Sopenharmony_ci	gsm->dlci[addr] = dlci;
18328c2ecf20Sopenharmony_ci	return dlci;
18338c2ecf20Sopenharmony_ci}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci/**
18368c2ecf20Sopenharmony_ci *	gsm_dlci_free		-	free DLCI
18378c2ecf20Sopenharmony_ci *	@port: tty port for DLCI to free
18388c2ecf20Sopenharmony_ci *
18398c2ecf20Sopenharmony_ci *	Free up a DLCI.
18408c2ecf20Sopenharmony_ci *
18418c2ecf20Sopenharmony_ci *	Can sleep.
18428c2ecf20Sopenharmony_ci */
18438c2ecf20Sopenharmony_cistatic void gsm_dlci_free(struct tty_port *port)
18448c2ecf20Sopenharmony_ci{
18458c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	del_timer_sync(&dlci->t1);
18488c2ecf20Sopenharmony_ci	dlci->gsm->dlci[dlci->addr] = NULL;
18498c2ecf20Sopenharmony_ci	kfifo_free(&dlci->fifo);
18508c2ecf20Sopenharmony_ci	while ((dlci->skb = skb_dequeue(&dlci->skb_list)))
18518c2ecf20Sopenharmony_ci		dev_kfree_skb(dlci->skb);
18528c2ecf20Sopenharmony_ci	kfree(dlci);
18538c2ecf20Sopenharmony_ci}
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_cistatic inline void dlci_get(struct gsm_dlci *dlci)
18568c2ecf20Sopenharmony_ci{
18578c2ecf20Sopenharmony_ci	tty_port_get(&dlci->port);
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_cistatic inline void dlci_put(struct gsm_dlci *dlci)
18618c2ecf20Sopenharmony_ci{
18628c2ecf20Sopenharmony_ci	tty_port_put(&dlci->port);
18638c2ecf20Sopenharmony_ci}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_cistatic void gsm_destroy_network(struct gsm_dlci *dlci);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci/**
18688c2ecf20Sopenharmony_ci *	gsm_dlci_release		-	release DLCI
18698c2ecf20Sopenharmony_ci *	@dlci: DLCI to destroy
18708c2ecf20Sopenharmony_ci *
18718c2ecf20Sopenharmony_ci *	Release a DLCI. Actual free is deferred until either
18728c2ecf20Sopenharmony_ci *	mux is closed or tty is closed - whichever is last.
18738c2ecf20Sopenharmony_ci *
18748c2ecf20Sopenharmony_ci *	Can sleep.
18758c2ecf20Sopenharmony_ci */
18768c2ecf20Sopenharmony_cistatic void gsm_dlci_release(struct gsm_dlci *dlci)
18778c2ecf20Sopenharmony_ci{
18788c2ecf20Sopenharmony_ci	struct tty_struct *tty = tty_port_tty_get(&dlci->port);
18798c2ecf20Sopenharmony_ci	if (tty) {
18808c2ecf20Sopenharmony_ci		mutex_lock(&dlci->mutex);
18818c2ecf20Sopenharmony_ci		gsm_destroy_network(dlci);
18828c2ecf20Sopenharmony_ci		mutex_unlock(&dlci->mutex);
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci		/* We cannot use tty_hangup() because in tty_kref_put() the tty
18858c2ecf20Sopenharmony_ci		 * driver assumes that the hangup queue is free and reuses it to
18868c2ecf20Sopenharmony_ci		 * queue release_one_tty() -> NULL pointer panic in
18878c2ecf20Sopenharmony_ci		 * process_one_work().
18888c2ecf20Sopenharmony_ci		 */
18898c2ecf20Sopenharmony_ci		tty_vhangup(tty);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci		tty_port_tty_set(&dlci->port, NULL);
18928c2ecf20Sopenharmony_ci		tty_kref_put(tty);
18938c2ecf20Sopenharmony_ci	}
18948c2ecf20Sopenharmony_ci	dlci->state = DLCI_CLOSED;
18958c2ecf20Sopenharmony_ci	dlci_put(dlci);
18968c2ecf20Sopenharmony_ci}
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci/*
18998c2ecf20Sopenharmony_ci *	LAPBish link layer logic
19008c2ecf20Sopenharmony_ci */
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci/**
19038c2ecf20Sopenharmony_ci *	gsm_queue		-	a GSM frame is ready to process
19048c2ecf20Sopenharmony_ci *	@gsm: pointer to our gsm mux
19058c2ecf20Sopenharmony_ci *
19068c2ecf20Sopenharmony_ci *	At this point in time a frame has arrived and been demangled from
19078c2ecf20Sopenharmony_ci *	the line encoding. All the differences between the encodings have
19088c2ecf20Sopenharmony_ci *	been handled below us and the frame is unpacked into the structures.
19098c2ecf20Sopenharmony_ci *	The fcs holds the header FCS but any data FCS must be added here.
19108c2ecf20Sopenharmony_ci */
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic void gsm_queue(struct gsm_mux *gsm)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
19158c2ecf20Sopenharmony_ci	u8 cr;
19168c2ecf20Sopenharmony_ci	int address;
19178c2ecf20Sopenharmony_ci	/* We have to sneak a look at the packet body to do the FCS.
19188c2ecf20Sopenharmony_ci	   A somewhat layering violation in the spec */
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	if ((gsm->control & ~PF) == UI)
19218c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
19228c2ecf20Sopenharmony_ci	if (gsm->encoding == 0) {
19238c2ecf20Sopenharmony_ci		/* WARNING: gsm->received_fcs is used for
19248c2ecf20Sopenharmony_ci		gsm->encoding = 0 only.
19258c2ecf20Sopenharmony_ci		In this case it contain the last piece of data
19268c2ecf20Sopenharmony_ci		required to generate final CRC */
19278c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
19288c2ecf20Sopenharmony_ci	}
19298c2ecf20Sopenharmony_ci	if (gsm->fcs != GOOD_FCS) {
19308c2ecf20Sopenharmony_ci		gsm->bad_fcs++;
19318c2ecf20Sopenharmony_ci		if (debug & 4)
19328c2ecf20Sopenharmony_ci			pr_debug("BAD FCS %02x\n", gsm->fcs);
19338c2ecf20Sopenharmony_ci		return;
19348c2ecf20Sopenharmony_ci	}
19358c2ecf20Sopenharmony_ci	address = gsm->address >> 1;
19368c2ecf20Sopenharmony_ci	if (address >= NUM_DLCI)
19378c2ecf20Sopenharmony_ci		goto invalid;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	cr = gsm->address & 1;		/* C/R bit */
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci	gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len);
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	cr ^= 1 - gsm->initiator;	/* Flip so 1 always means command */
19448c2ecf20Sopenharmony_ci	dlci = gsm->dlci[address];
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	switch (gsm->control) {
19478c2ecf20Sopenharmony_ci	case SABM|PF:
19488c2ecf20Sopenharmony_ci		if (cr == 0)
19498c2ecf20Sopenharmony_ci			goto invalid;
19508c2ecf20Sopenharmony_ci		if (dlci == NULL)
19518c2ecf20Sopenharmony_ci			dlci = gsm_dlci_alloc(gsm, address);
19528c2ecf20Sopenharmony_ci		if (dlci == NULL)
19538c2ecf20Sopenharmony_ci			return;
19548c2ecf20Sopenharmony_ci		if (dlci->dead)
19558c2ecf20Sopenharmony_ci			gsm_response(gsm, address, DM);
19568c2ecf20Sopenharmony_ci		else {
19578c2ecf20Sopenharmony_ci			gsm_response(gsm, address, UA);
19588c2ecf20Sopenharmony_ci			gsm_dlci_open(dlci);
19598c2ecf20Sopenharmony_ci		}
19608c2ecf20Sopenharmony_ci		break;
19618c2ecf20Sopenharmony_ci	case DISC|PF:
19628c2ecf20Sopenharmony_ci		if (cr == 0)
19638c2ecf20Sopenharmony_ci			goto invalid;
19648c2ecf20Sopenharmony_ci		if (dlci == NULL || dlci->state == DLCI_CLOSED) {
19658c2ecf20Sopenharmony_ci			gsm_response(gsm, address, DM);
19668c2ecf20Sopenharmony_ci			return;
19678c2ecf20Sopenharmony_ci		}
19688c2ecf20Sopenharmony_ci		/* Real close complete */
19698c2ecf20Sopenharmony_ci		gsm_response(gsm, address, UA);
19708c2ecf20Sopenharmony_ci		gsm_dlci_close(dlci);
19718c2ecf20Sopenharmony_ci		break;
19728c2ecf20Sopenharmony_ci	case UA|PF:
19738c2ecf20Sopenharmony_ci		if (cr == 0 || dlci == NULL)
19748c2ecf20Sopenharmony_ci			break;
19758c2ecf20Sopenharmony_ci		switch (dlci->state) {
19768c2ecf20Sopenharmony_ci		case DLCI_CLOSING:
19778c2ecf20Sopenharmony_ci			gsm_dlci_close(dlci);
19788c2ecf20Sopenharmony_ci			break;
19798c2ecf20Sopenharmony_ci		case DLCI_OPENING:
19808c2ecf20Sopenharmony_ci			gsm_dlci_open(dlci);
19818c2ecf20Sopenharmony_ci			break;
19828c2ecf20Sopenharmony_ci		default:
19838c2ecf20Sopenharmony_ci			pr_debug("%s: unhandled state: %d\n", __func__,
19848c2ecf20Sopenharmony_ci					dlci->state);
19858c2ecf20Sopenharmony_ci			break;
19868c2ecf20Sopenharmony_ci		}
19878c2ecf20Sopenharmony_ci		break;
19888c2ecf20Sopenharmony_ci	case DM:	/* DM can be valid unsolicited */
19898c2ecf20Sopenharmony_ci	case DM|PF:
19908c2ecf20Sopenharmony_ci		if (cr)
19918c2ecf20Sopenharmony_ci			goto invalid;
19928c2ecf20Sopenharmony_ci		if (dlci == NULL)
19938c2ecf20Sopenharmony_ci			return;
19948c2ecf20Sopenharmony_ci		gsm_dlci_close(dlci);
19958c2ecf20Sopenharmony_ci		break;
19968c2ecf20Sopenharmony_ci	case UI:
19978c2ecf20Sopenharmony_ci	case UI|PF:
19988c2ecf20Sopenharmony_ci	case UIH:
19998c2ecf20Sopenharmony_ci	case UIH|PF:
20008c2ecf20Sopenharmony_ci#if 0
20018c2ecf20Sopenharmony_ci		if (cr)
20028c2ecf20Sopenharmony_ci			goto invalid;
20038c2ecf20Sopenharmony_ci#endif
20048c2ecf20Sopenharmony_ci		if (dlci == NULL || dlci->state != DLCI_OPEN) {
20058c2ecf20Sopenharmony_ci			gsm_response(gsm, address, DM|PF);
20068c2ecf20Sopenharmony_ci			return;
20078c2ecf20Sopenharmony_ci		}
20088c2ecf20Sopenharmony_ci		dlci->data(dlci, gsm->buf, gsm->len);
20098c2ecf20Sopenharmony_ci		break;
20108c2ecf20Sopenharmony_ci	default:
20118c2ecf20Sopenharmony_ci		goto invalid;
20128c2ecf20Sopenharmony_ci	}
20138c2ecf20Sopenharmony_ci	return;
20148c2ecf20Sopenharmony_ciinvalid:
20158c2ecf20Sopenharmony_ci	gsm->malformed++;
20168c2ecf20Sopenharmony_ci	return;
20178c2ecf20Sopenharmony_ci}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci/**
20218c2ecf20Sopenharmony_ci *	gsm0_receive	-	perform processing for non-transparency
20228c2ecf20Sopenharmony_ci *	@gsm: gsm data for this ldisc instance
20238c2ecf20Sopenharmony_ci *	@c: character
20248c2ecf20Sopenharmony_ci *
20258c2ecf20Sopenharmony_ci *	Receive bytes in gsm mode 0
20268c2ecf20Sopenharmony_ci */
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_cistatic void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
20298c2ecf20Sopenharmony_ci{
20308c2ecf20Sopenharmony_ci	unsigned int len;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	switch (gsm->state) {
20338c2ecf20Sopenharmony_ci	case GSM_SEARCH:	/* SOF marker */
20348c2ecf20Sopenharmony_ci		if (c == GSM0_SOF) {
20358c2ecf20Sopenharmony_ci			gsm->state = GSM_ADDRESS;
20368c2ecf20Sopenharmony_ci			gsm->address = 0;
20378c2ecf20Sopenharmony_ci			gsm->len = 0;
20388c2ecf20Sopenharmony_ci			gsm->fcs = INIT_FCS;
20398c2ecf20Sopenharmony_ci		}
20408c2ecf20Sopenharmony_ci		break;
20418c2ecf20Sopenharmony_ci	case GSM_ADDRESS:	/* Address EA */
20428c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
20438c2ecf20Sopenharmony_ci		if (gsm_read_ea(&gsm->address, c))
20448c2ecf20Sopenharmony_ci			gsm->state = GSM_CONTROL;
20458c2ecf20Sopenharmony_ci		break;
20468c2ecf20Sopenharmony_ci	case GSM_CONTROL:	/* Control Byte */
20478c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
20488c2ecf20Sopenharmony_ci		gsm->control = c;
20498c2ecf20Sopenharmony_ci		gsm->state = GSM_LEN0;
20508c2ecf20Sopenharmony_ci		break;
20518c2ecf20Sopenharmony_ci	case GSM_LEN0:		/* Length EA */
20528c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
20538c2ecf20Sopenharmony_ci		if (gsm_read_ea(&gsm->len, c)) {
20548c2ecf20Sopenharmony_ci			if (gsm->len > gsm->mru) {
20558c2ecf20Sopenharmony_ci				gsm->bad_size++;
20568c2ecf20Sopenharmony_ci				gsm->state = GSM_SEARCH;
20578c2ecf20Sopenharmony_ci				break;
20588c2ecf20Sopenharmony_ci			}
20598c2ecf20Sopenharmony_ci			gsm->count = 0;
20608c2ecf20Sopenharmony_ci			if (!gsm->len)
20618c2ecf20Sopenharmony_ci				gsm->state = GSM_FCS;
20628c2ecf20Sopenharmony_ci			else
20638c2ecf20Sopenharmony_ci				gsm->state = GSM_DATA;
20648c2ecf20Sopenharmony_ci			break;
20658c2ecf20Sopenharmony_ci		}
20668c2ecf20Sopenharmony_ci		gsm->state = GSM_LEN1;
20678c2ecf20Sopenharmony_ci		break;
20688c2ecf20Sopenharmony_ci	case GSM_LEN1:
20698c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
20708c2ecf20Sopenharmony_ci		len = c;
20718c2ecf20Sopenharmony_ci		gsm->len |= len << 7;
20728c2ecf20Sopenharmony_ci		if (gsm->len > gsm->mru) {
20738c2ecf20Sopenharmony_ci			gsm->bad_size++;
20748c2ecf20Sopenharmony_ci			gsm->state = GSM_SEARCH;
20758c2ecf20Sopenharmony_ci			break;
20768c2ecf20Sopenharmony_ci		}
20778c2ecf20Sopenharmony_ci		gsm->count = 0;
20788c2ecf20Sopenharmony_ci		if (!gsm->len)
20798c2ecf20Sopenharmony_ci			gsm->state = GSM_FCS;
20808c2ecf20Sopenharmony_ci		else
20818c2ecf20Sopenharmony_ci			gsm->state = GSM_DATA;
20828c2ecf20Sopenharmony_ci		break;
20838c2ecf20Sopenharmony_ci	case GSM_DATA:		/* Data */
20848c2ecf20Sopenharmony_ci		gsm->buf[gsm->count++] = c;
20858c2ecf20Sopenharmony_ci		if (gsm->count == gsm->len)
20868c2ecf20Sopenharmony_ci			gsm->state = GSM_FCS;
20878c2ecf20Sopenharmony_ci		break;
20888c2ecf20Sopenharmony_ci	case GSM_FCS:		/* FCS follows the packet */
20898c2ecf20Sopenharmony_ci		gsm->received_fcs = c;
20908c2ecf20Sopenharmony_ci		gsm_queue(gsm);
20918c2ecf20Sopenharmony_ci		gsm->state = GSM_SSOF;
20928c2ecf20Sopenharmony_ci		break;
20938c2ecf20Sopenharmony_ci	case GSM_SSOF:
20948c2ecf20Sopenharmony_ci		if (c == GSM0_SOF) {
20958c2ecf20Sopenharmony_ci			gsm->state = GSM_SEARCH;
20968c2ecf20Sopenharmony_ci			break;
20978c2ecf20Sopenharmony_ci		}
20988c2ecf20Sopenharmony_ci		break;
20998c2ecf20Sopenharmony_ci	default:
21008c2ecf20Sopenharmony_ci		pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
21018c2ecf20Sopenharmony_ci		break;
21028c2ecf20Sopenharmony_ci	}
21038c2ecf20Sopenharmony_ci}
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci/**
21068c2ecf20Sopenharmony_ci *	gsm1_receive	-	perform processing for non-transparency
21078c2ecf20Sopenharmony_ci *	@gsm: gsm data for this ldisc instance
21088c2ecf20Sopenharmony_ci *	@c: character
21098c2ecf20Sopenharmony_ci *
21108c2ecf20Sopenharmony_ci *	Receive bytes in mode 1 (Advanced option)
21118c2ecf20Sopenharmony_ci */
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_cistatic void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
21148c2ecf20Sopenharmony_ci{
21158c2ecf20Sopenharmony_ci	/* handle XON/XOFF */
21168c2ecf20Sopenharmony_ci	if ((c & ISO_IEC_646_MASK) == XON) {
21178c2ecf20Sopenharmony_ci		gsm->constipated = true;
21188c2ecf20Sopenharmony_ci		return;
21198c2ecf20Sopenharmony_ci	} else if ((c & ISO_IEC_646_MASK) == XOFF) {
21208c2ecf20Sopenharmony_ci		gsm->constipated = false;
21218c2ecf20Sopenharmony_ci		/* Kick the link in case it is idling */
21228c2ecf20Sopenharmony_ci		gsm_data_kick(gsm, NULL);
21238c2ecf20Sopenharmony_ci		return;
21248c2ecf20Sopenharmony_ci	}
21258c2ecf20Sopenharmony_ci	if (c == GSM1_SOF) {
21268c2ecf20Sopenharmony_ci		/* EOF is only valid in frame if we have got to the data state
21278c2ecf20Sopenharmony_ci		   and received at least one byte (the FCS) */
21288c2ecf20Sopenharmony_ci		if (gsm->state == GSM_DATA && gsm->count) {
21298c2ecf20Sopenharmony_ci			/* Extract the FCS */
21308c2ecf20Sopenharmony_ci			gsm->count--;
21318c2ecf20Sopenharmony_ci			gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
21328c2ecf20Sopenharmony_ci			gsm->len = gsm->count;
21338c2ecf20Sopenharmony_ci			gsm_queue(gsm);
21348c2ecf20Sopenharmony_ci			gsm->state  = GSM_START;
21358c2ecf20Sopenharmony_ci			return;
21368c2ecf20Sopenharmony_ci		}
21378c2ecf20Sopenharmony_ci		/* Any partial frame was a runt so go back to start */
21388c2ecf20Sopenharmony_ci		if (gsm->state != GSM_START) {
21398c2ecf20Sopenharmony_ci			if (gsm->state != GSM_SEARCH)
21408c2ecf20Sopenharmony_ci				gsm->malformed++;
21418c2ecf20Sopenharmony_ci			gsm->state = GSM_START;
21428c2ecf20Sopenharmony_ci		}
21438c2ecf20Sopenharmony_ci		/* A SOF in GSM_START means we are still reading idling or
21448c2ecf20Sopenharmony_ci		   framing bytes */
21458c2ecf20Sopenharmony_ci		return;
21468c2ecf20Sopenharmony_ci	}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	if (c == GSM1_ESCAPE) {
21498c2ecf20Sopenharmony_ci		gsm->escape = true;
21508c2ecf20Sopenharmony_ci		return;
21518c2ecf20Sopenharmony_ci	}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	/* Only an unescaped SOF gets us out of GSM search */
21548c2ecf20Sopenharmony_ci	if (gsm->state == GSM_SEARCH)
21558c2ecf20Sopenharmony_ci		return;
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	if (gsm->escape) {
21588c2ecf20Sopenharmony_ci		c ^= GSM1_ESCAPE_BITS;
21598c2ecf20Sopenharmony_ci		gsm->escape = false;
21608c2ecf20Sopenharmony_ci	}
21618c2ecf20Sopenharmony_ci	switch (gsm->state) {
21628c2ecf20Sopenharmony_ci	case GSM_START:		/* First byte after SOF */
21638c2ecf20Sopenharmony_ci		gsm->address = 0;
21648c2ecf20Sopenharmony_ci		gsm->state = GSM_ADDRESS;
21658c2ecf20Sopenharmony_ci		gsm->fcs = INIT_FCS;
21668c2ecf20Sopenharmony_ci		fallthrough;
21678c2ecf20Sopenharmony_ci	case GSM_ADDRESS:	/* Address continuation */
21688c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
21698c2ecf20Sopenharmony_ci		if (gsm_read_ea(&gsm->address, c))
21708c2ecf20Sopenharmony_ci			gsm->state = GSM_CONTROL;
21718c2ecf20Sopenharmony_ci		break;
21728c2ecf20Sopenharmony_ci	case GSM_CONTROL:	/* Control Byte */
21738c2ecf20Sopenharmony_ci		gsm->fcs = gsm_fcs_add(gsm->fcs, c);
21748c2ecf20Sopenharmony_ci		gsm->control = c;
21758c2ecf20Sopenharmony_ci		gsm->count = 0;
21768c2ecf20Sopenharmony_ci		gsm->state = GSM_DATA;
21778c2ecf20Sopenharmony_ci		break;
21788c2ecf20Sopenharmony_ci	case GSM_DATA:		/* Data */
21798c2ecf20Sopenharmony_ci		if (gsm->count > gsm->mru) {	/* Allow one for the FCS */
21808c2ecf20Sopenharmony_ci			gsm->state = GSM_OVERRUN;
21818c2ecf20Sopenharmony_ci			gsm->bad_size++;
21828c2ecf20Sopenharmony_ci		} else
21838c2ecf20Sopenharmony_ci			gsm->buf[gsm->count++] = c;
21848c2ecf20Sopenharmony_ci		break;
21858c2ecf20Sopenharmony_ci	case GSM_OVERRUN:	/* Over-long - eg a dropped SOF */
21868c2ecf20Sopenharmony_ci		break;
21878c2ecf20Sopenharmony_ci	default:
21888c2ecf20Sopenharmony_ci		pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
21898c2ecf20Sopenharmony_ci		break;
21908c2ecf20Sopenharmony_ci	}
21918c2ecf20Sopenharmony_ci}
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci/**
21948c2ecf20Sopenharmony_ci *	gsm_error		-	handle tty error
21958c2ecf20Sopenharmony_ci *	@gsm: ldisc data
21968c2ecf20Sopenharmony_ci *	@data: byte received (may be invalid)
21978c2ecf20Sopenharmony_ci *	@flag: error received
21988c2ecf20Sopenharmony_ci *
21998c2ecf20Sopenharmony_ci *	Handle an error in the receipt of data for a frame. Currently we just
22008c2ecf20Sopenharmony_ci *	go back to hunting for a SOF.
22018c2ecf20Sopenharmony_ci *
22028c2ecf20Sopenharmony_ci *	FIXME: better diagnostics ?
22038c2ecf20Sopenharmony_ci */
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_cistatic void gsm_error(struct gsm_mux *gsm,
22068c2ecf20Sopenharmony_ci				unsigned char data, unsigned char flag)
22078c2ecf20Sopenharmony_ci{
22088c2ecf20Sopenharmony_ci	gsm->state = GSM_SEARCH;
22098c2ecf20Sopenharmony_ci	gsm->io_error++;
22108c2ecf20Sopenharmony_ci}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci/**
22138c2ecf20Sopenharmony_ci *	gsm_cleanup_mux		-	generic GSM protocol cleanup
22148c2ecf20Sopenharmony_ci *	@gsm: our mux
22158c2ecf20Sopenharmony_ci *	@disc: disconnect link?
22168c2ecf20Sopenharmony_ci *
22178c2ecf20Sopenharmony_ci *	Clean up the bits of the mux which are the same for all framing
22188c2ecf20Sopenharmony_ci *	protocols. Remove the mux from the mux table, stop all the timers
22198c2ecf20Sopenharmony_ci *	and then shut down each device hanging up the channels as we go.
22208c2ecf20Sopenharmony_ci */
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_cistatic void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
22238c2ecf20Sopenharmony_ci{
22248c2ecf20Sopenharmony_ci	int i;
22258c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
22268c2ecf20Sopenharmony_ci	struct gsm_msg *txq, *ntxq;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	gsm->dead = true;
22298c2ecf20Sopenharmony_ci	mutex_lock(&gsm->mutex);
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	dlci = gsm->dlci[0];
22328c2ecf20Sopenharmony_ci	if (dlci) {
22338c2ecf20Sopenharmony_ci		if (disc && dlci->state != DLCI_CLOSED) {
22348c2ecf20Sopenharmony_ci			gsm_dlci_begin_close(dlci);
22358c2ecf20Sopenharmony_ci			wait_event(gsm->event, dlci->state == DLCI_CLOSED);
22368c2ecf20Sopenharmony_ci		}
22378c2ecf20Sopenharmony_ci		dlci->dead = true;
22388c2ecf20Sopenharmony_ci	}
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	/* Finish outstanding timers, making sure they are done */
22418c2ecf20Sopenharmony_ci	del_timer_sync(&gsm->t2_timer);
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	/* Free up any link layer users and finally the control channel */
22448c2ecf20Sopenharmony_ci	if (gsm->has_devices) {
22458c2ecf20Sopenharmony_ci		gsm_unregister_devices(gsm_tty_driver, gsm->num);
22468c2ecf20Sopenharmony_ci		gsm->has_devices = false;
22478c2ecf20Sopenharmony_ci	}
22488c2ecf20Sopenharmony_ci	for (i = NUM_DLCI - 1; i >= 0; i--)
22498c2ecf20Sopenharmony_ci		if (gsm->dlci[i])
22508c2ecf20Sopenharmony_ci			gsm_dlci_release(gsm->dlci[i]);
22518c2ecf20Sopenharmony_ci	mutex_unlock(&gsm->mutex);
22528c2ecf20Sopenharmony_ci	/* Now wipe the queues */
22538c2ecf20Sopenharmony_ci	tty_ldisc_flush(gsm->tty);
22548c2ecf20Sopenharmony_ci	list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
22558c2ecf20Sopenharmony_ci		kfree(txq);
22568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gsm->tx_list);
22578c2ecf20Sopenharmony_ci}
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci/**
22608c2ecf20Sopenharmony_ci *	gsm_activate_mux	-	generic GSM setup
22618c2ecf20Sopenharmony_ci *	@gsm: our mux
22628c2ecf20Sopenharmony_ci *
22638c2ecf20Sopenharmony_ci *	Set up the bits of the mux which are the same for all framing
22648c2ecf20Sopenharmony_ci *	protocols. Add the mux to the mux table so it can be opened and
22658c2ecf20Sopenharmony_ci *	finally kick off connecting to DLCI 0 on the modem.
22668c2ecf20Sopenharmony_ci */
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_cistatic int gsm_activate_mux(struct gsm_mux *gsm)
22698c2ecf20Sopenharmony_ci{
22708c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
22718c2ecf20Sopenharmony_ci	int ret;
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	if (gsm->encoding == 0)
22748c2ecf20Sopenharmony_ci		gsm->receive = gsm0_receive;
22758c2ecf20Sopenharmony_ci	else
22768c2ecf20Sopenharmony_ci		gsm->receive = gsm1_receive;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	ret = gsm_register_devices(gsm_tty_driver, gsm->num);
22798c2ecf20Sopenharmony_ci	if (ret)
22808c2ecf20Sopenharmony_ci		return ret;
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	dlci = gsm_dlci_alloc(gsm, 0);
22838c2ecf20Sopenharmony_ci	if (dlci == NULL)
22848c2ecf20Sopenharmony_ci		return -ENOMEM;
22858c2ecf20Sopenharmony_ci	gsm->has_devices = true;
22868c2ecf20Sopenharmony_ci	gsm->dead = false;		/* Tty opens are now permissible */
22878c2ecf20Sopenharmony_ci	return 0;
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci/**
22918c2ecf20Sopenharmony_ci *	gsm_free_mux		-	free up a mux
22928c2ecf20Sopenharmony_ci *	@gsm: mux to free
22938c2ecf20Sopenharmony_ci *
22948c2ecf20Sopenharmony_ci *	Dispose of allocated resources for a dead mux
22958c2ecf20Sopenharmony_ci */
22968c2ecf20Sopenharmony_cistatic void gsm_free_mux(struct gsm_mux *gsm)
22978c2ecf20Sopenharmony_ci{
22988c2ecf20Sopenharmony_ci	int i;
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_MUX; i++) {
23018c2ecf20Sopenharmony_ci		if (gsm == gsm_mux[i]) {
23028c2ecf20Sopenharmony_ci			gsm_mux[i] = NULL;
23038c2ecf20Sopenharmony_ci			break;
23048c2ecf20Sopenharmony_ci		}
23058c2ecf20Sopenharmony_ci	}
23068c2ecf20Sopenharmony_ci	mutex_destroy(&gsm->mutex);
23078c2ecf20Sopenharmony_ci	kfree(gsm->txframe);
23088c2ecf20Sopenharmony_ci	kfree(gsm->buf);
23098c2ecf20Sopenharmony_ci	kfree(gsm);
23108c2ecf20Sopenharmony_ci}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci/**
23138c2ecf20Sopenharmony_ci *	gsm_free_muxr		-	free up a mux
23148c2ecf20Sopenharmony_ci *	@ref: kreference to the mux to free
23158c2ecf20Sopenharmony_ci *
23168c2ecf20Sopenharmony_ci *	Dispose of allocated resources for a dead mux
23178c2ecf20Sopenharmony_ci */
23188c2ecf20Sopenharmony_cistatic void gsm_free_muxr(struct kref *ref)
23198c2ecf20Sopenharmony_ci{
23208c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = container_of(ref, struct gsm_mux, ref);
23218c2ecf20Sopenharmony_ci	gsm_free_mux(gsm);
23228c2ecf20Sopenharmony_ci}
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_cistatic inline void mux_get(struct gsm_mux *gsm)
23258c2ecf20Sopenharmony_ci{
23268c2ecf20Sopenharmony_ci	unsigned long flags;
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm_mux_lock, flags);
23298c2ecf20Sopenharmony_ci	kref_get(&gsm->ref);
23308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm_mux_lock, flags);
23318c2ecf20Sopenharmony_ci}
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_cistatic inline void mux_put(struct gsm_mux *gsm)
23348c2ecf20Sopenharmony_ci{
23358c2ecf20Sopenharmony_ci	unsigned long flags;
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm_mux_lock, flags);
23388c2ecf20Sopenharmony_ci	kref_put(&gsm->ref, gsm_free_muxr);
23398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm_mux_lock, flags);
23408c2ecf20Sopenharmony_ci}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_cistatic inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
23438c2ecf20Sopenharmony_ci{
23448c2ecf20Sopenharmony_ci	return gsm->num * NUM_DLCI;
23458c2ecf20Sopenharmony_ci}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_cistatic inline unsigned int mux_line_to_num(unsigned int line)
23488c2ecf20Sopenharmony_ci{
23498c2ecf20Sopenharmony_ci	return line / NUM_DLCI;
23508c2ecf20Sopenharmony_ci}
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci/**
23538c2ecf20Sopenharmony_ci *	gsm_alloc_mux		-	allocate a mux
23548c2ecf20Sopenharmony_ci *
23558c2ecf20Sopenharmony_ci *	Creates a new mux ready for activation.
23568c2ecf20Sopenharmony_ci */
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_cistatic struct gsm_mux *gsm_alloc_mux(void)
23598c2ecf20Sopenharmony_ci{
23608c2ecf20Sopenharmony_ci	int i;
23618c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL);
23628c2ecf20Sopenharmony_ci	if (gsm == NULL)
23638c2ecf20Sopenharmony_ci		return NULL;
23648c2ecf20Sopenharmony_ci	gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL);
23658c2ecf20Sopenharmony_ci	if (gsm->buf == NULL) {
23668c2ecf20Sopenharmony_ci		kfree(gsm);
23678c2ecf20Sopenharmony_ci		return NULL;
23688c2ecf20Sopenharmony_ci	}
23698c2ecf20Sopenharmony_ci	gsm->txframe = kmalloc(2 * (MAX_MTU + PROT_OVERHEAD - 1), GFP_KERNEL);
23708c2ecf20Sopenharmony_ci	if (gsm->txframe == NULL) {
23718c2ecf20Sopenharmony_ci		kfree(gsm->buf);
23728c2ecf20Sopenharmony_ci		kfree(gsm);
23738c2ecf20Sopenharmony_ci		return NULL;
23748c2ecf20Sopenharmony_ci	}
23758c2ecf20Sopenharmony_ci	spin_lock_init(&gsm->lock);
23768c2ecf20Sopenharmony_ci	mutex_init(&gsm->mutex);
23778c2ecf20Sopenharmony_ci	kref_init(&gsm->ref);
23788c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gsm->tx_list);
23798c2ecf20Sopenharmony_ci	timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
23808c2ecf20Sopenharmony_ci	init_waitqueue_head(&gsm->event);
23818c2ecf20Sopenharmony_ci	spin_lock_init(&gsm->control_lock);
23828c2ecf20Sopenharmony_ci	spin_lock_init(&gsm->tx_lock);
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	gsm->t1 = T1;
23858c2ecf20Sopenharmony_ci	gsm->t2 = T2;
23868c2ecf20Sopenharmony_ci	gsm->n2 = N2;
23878c2ecf20Sopenharmony_ci	gsm->ftype = UIH;
23888c2ecf20Sopenharmony_ci	gsm->adaption = 1;
23898c2ecf20Sopenharmony_ci	gsm->encoding = 1;
23908c2ecf20Sopenharmony_ci	gsm->mru = 64;	/* Default to encoding 1 so these should be 64 */
23918c2ecf20Sopenharmony_ci	gsm->mtu = 64;
23928c2ecf20Sopenharmony_ci	gsm->dead = true;	/* Avoid early tty opens */
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_ci	/* Store the instance to the mux array or abort if no space is
23958c2ecf20Sopenharmony_ci	 * available.
23968c2ecf20Sopenharmony_ci	 */
23978c2ecf20Sopenharmony_ci	spin_lock(&gsm_mux_lock);
23988c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_MUX; i++) {
23998c2ecf20Sopenharmony_ci		if (!gsm_mux[i]) {
24008c2ecf20Sopenharmony_ci			gsm_mux[i] = gsm;
24018c2ecf20Sopenharmony_ci			gsm->num = i;
24028c2ecf20Sopenharmony_ci			break;
24038c2ecf20Sopenharmony_ci		}
24048c2ecf20Sopenharmony_ci	}
24058c2ecf20Sopenharmony_ci	spin_unlock(&gsm_mux_lock);
24068c2ecf20Sopenharmony_ci	if (i == MAX_MUX) {
24078c2ecf20Sopenharmony_ci		mutex_destroy(&gsm->mutex);
24088c2ecf20Sopenharmony_ci		kfree(gsm->txframe);
24098c2ecf20Sopenharmony_ci		kfree(gsm->buf);
24108c2ecf20Sopenharmony_ci		kfree(gsm);
24118c2ecf20Sopenharmony_ci		return NULL;
24128c2ecf20Sopenharmony_ci	}
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	return gsm;
24158c2ecf20Sopenharmony_ci}
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_cistatic void gsm_copy_config_values(struct gsm_mux *gsm,
24188c2ecf20Sopenharmony_ci				   struct gsm_config *c)
24198c2ecf20Sopenharmony_ci{
24208c2ecf20Sopenharmony_ci	memset(c, 0, sizeof(*c));
24218c2ecf20Sopenharmony_ci	c->adaption = gsm->adaption;
24228c2ecf20Sopenharmony_ci	c->encapsulation = gsm->encoding;
24238c2ecf20Sopenharmony_ci	c->initiator = gsm->initiator;
24248c2ecf20Sopenharmony_ci	c->t1 = gsm->t1;
24258c2ecf20Sopenharmony_ci	c->t2 = gsm->t2;
24268c2ecf20Sopenharmony_ci	c->t3 = 0;	/* Not supported */
24278c2ecf20Sopenharmony_ci	c->n2 = gsm->n2;
24288c2ecf20Sopenharmony_ci	if (gsm->ftype == UIH)
24298c2ecf20Sopenharmony_ci		c->i = 1;
24308c2ecf20Sopenharmony_ci	else
24318c2ecf20Sopenharmony_ci		c->i = 2;
24328c2ecf20Sopenharmony_ci	pr_debug("Ftype %d i %d\n", gsm->ftype, c->i);
24338c2ecf20Sopenharmony_ci	c->mru = gsm->mru;
24348c2ecf20Sopenharmony_ci	c->mtu = gsm->mtu;
24358c2ecf20Sopenharmony_ci	c->k = 0;
24368c2ecf20Sopenharmony_ci}
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_cistatic int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
24398c2ecf20Sopenharmony_ci{
24408c2ecf20Sopenharmony_ci	int ret = 0;
24418c2ecf20Sopenharmony_ci	int need_close = 0;
24428c2ecf20Sopenharmony_ci	int need_restart = 0;
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	/* Stuff we don't support yet - UI or I frame transport, windowing */
24458c2ecf20Sopenharmony_ci	if ((c->adaption != 1 && c->adaption != 2) || c->k)
24468c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
24478c2ecf20Sopenharmony_ci	/* Check the MRU/MTU range looks sane */
24488c2ecf20Sopenharmony_ci	if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
24498c2ecf20Sopenharmony_ci		return -EINVAL;
24508c2ecf20Sopenharmony_ci	if (c->n2 > 255)
24518c2ecf20Sopenharmony_ci		return -EINVAL;
24528c2ecf20Sopenharmony_ci	if (c->encapsulation > 1)	/* Basic, advanced, no I */
24538c2ecf20Sopenharmony_ci		return -EINVAL;
24548c2ecf20Sopenharmony_ci	if (c->initiator > 1)
24558c2ecf20Sopenharmony_ci		return -EINVAL;
24568c2ecf20Sopenharmony_ci	if (c->i == 0 || c->i > 2)	/* UIH and UI only */
24578c2ecf20Sopenharmony_ci		return -EINVAL;
24588c2ecf20Sopenharmony_ci	/*
24598c2ecf20Sopenharmony_ci	 * See what is needed for reconfiguration
24608c2ecf20Sopenharmony_ci	 */
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	/* Timing fields */
24638c2ecf20Sopenharmony_ci	if (c->t1 != 0 && c->t1 != gsm->t1)
24648c2ecf20Sopenharmony_ci		need_restart = 1;
24658c2ecf20Sopenharmony_ci	if (c->t2 != 0 && c->t2 != gsm->t2)
24668c2ecf20Sopenharmony_ci		need_restart = 1;
24678c2ecf20Sopenharmony_ci	if (c->encapsulation != gsm->encoding)
24688c2ecf20Sopenharmony_ci		need_restart = 1;
24698c2ecf20Sopenharmony_ci	if (c->adaption != gsm->adaption)
24708c2ecf20Sopenharmony_ci		need_restart = 1;
24718c2ecf20Sopenharmony_ci	/* Requires care */
24728c2ecf20Sopenharmony_ci	if (c->initiator != gsm->initiator)
24738c2ecf20Sopenharmony_ci		need_close = 1;
24748c2ecf20Sopenharmony_ci	if (c->mru != gsm->mru)
24758c2ecf20Sopenharmony_ci		need_restart = 1;
24768c2ecf20Sopenharmony_ci	if (c->mtu != gsm->mtu)
24778c2ecf20Sopenharmony_ci		need_restart = 1;
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	/*
24808c2ecf20Sopenharmony_ci	 * Close down what is needed, restart and initiate the new
24818c2ecf20Sopenharmony_ci	 * configuration. On the first time there is no DLCI[0]
24828c2ecf20Sopenharmony_ci	 * and closing or cleaning up is not necessary.
24838c2ecf20Sopenharmony_ci	 */
24848c2ecf20Sopenharmony_ci	if (need_close || need_restart)
24858c2ecf20Sopenharmony_ci		gsm_cleanup_mux(gsm, true);
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	gsm->initiator = c->initiator;
24888c2ecf20Sopenharmony_ci	gsm->mru = c->mru;
24898c2ecf20Sopenharmony_ci	gsm->mtu = c->mtu;
24908c2ecf20Sopenharmony_ci	gsm->encoding = c->encapsulation;
24918c2ecf20Sopenharmony_ci	gsm->adaption = c->adaption;
24928c2ecf20Sopenharmony_ci	gsm->n2 = c->n2;
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	if (c->i == 1)
24958c2ecf20Sopenharmony_ci		gsm->ftype = UIH;
24968c2ecf20Sopenharmony_ci	else if (c->i == 2)
24978c2ecf20Sopenharmony_ci		gsm->ftype = UI;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	if (c->t1)
25008c2ecf20Sopenharmony_ci		gsm->t1 = c->t1;
25018c2ecf20Sopenharmony_ci	if (c->t2)
25028c2ecf20Sopenharmony_ci		gsm->t2 = c->t2;
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	/*
25058c2ecf20Sopenharmony_ci	 * FIXME: We need to separate activation/deactivation from adding
25068c2ecf20Sopenharmony_ci	 * and removing from the mux array
25078c2ecf20Sopenharmony_ci	 */
25088c2ecf20Sopenharmony_ci	if (gsm->dead) {
25098c2ecf20Sopenharmony_ci		ret = gsm_activate_mux(gsm);
25108c2ecf20Sopenharmony_ci		if (ret)
25118c2ecf20Sopenharmony_ci			return ret;
25128c2ecf20Sopenharmony_ci		if (gsm->initiator)
25138c2ecf20Sopenharmony_ci			gsm_dlci_begin_open(gsm->dlci[0]);
25148c2ecf20Sopenharmony_ci	}
25158c2ecf20Sopenharmony_ci	return 0;
25168c2ecf20Sopenharmony_ci}
25178c2ecf20Sopenharmony_ci
25188c2ecf20Sopenharmony_ci/**
25198c2ecf20Sopenharmony_ci *	gsmld_output		-	write to link
25208c2ecf20Sopenharmony_ci *	@gsm: our mux
25218c2ecf20Sopenharmony_ci *	@data: bytes to output
25228c2ecf20Sopenharmony_ci *	@len: size
25238c2ecf20Sopenharmony_ci *
25248c2ecf20Sopenharmony_ci *	Write a block of data from the GSM mux to the data channel. This
25258c2ecf20Sopenharmony_ci *	will eventually be serialized from above but at the moment isn't.
25268c2ecf20Sopenharmony_ci */
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_cistatic int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
25298c2ecf20Sopenharmony_ci{
25308c2ecf20Sopenharmony_ci	if (tty_write_room(gsm->tty) < len) {
25318c2ecf20Sopenharmony_ci		set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
25328c2ecf20Sopenharmony_ci		return -ENOSPC;
25338c2ecf20Sopenharmony_ci	}
25348c2ecf20Sopenharmony_ci	if (debug & 4)
25358c2ecf20Sopenharmony_ci		print_hex_dump_bytes("gsmld_output: ", DUMP_PREFIX_OFFSET,
25368c2ecf20Sopenharmony_ci				     data, len);
25378c2ecf20Sopenharmony_ci	gsm->tty->ops->write(gsm->tty, data, len);
25388c2ecf20Sopenharmony_ci	return len;
25398c2ecf20Sopenharmony_ci}
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci/**
25428c2ecf20Sopenharmony_ci *	gsmld_attach_gsm	-	mode set up
25438c2ecf20Sopenharmony_ci *	@tty: our tty structure
25448c2ecf20Sopenharmony_ci *	@gsm: our mux
25458c2ecf20Sopenharmony_ci *
25468c2ecf20Sopenharmony_ci *	Set up the MUX for basic mode and commence connecting to the
25478c2ecf20Sopenharmony_ci *	modem. Currently called from the line discipline set up but
25488c2ecf20Sopenharmony_ci *	will need moving to an ioctl path.
25498c2ecf20Sopenharmony_ci */
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_cistatic void gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
25528c2ecf20Sopenharmony_ci{
25538c2ecf20Sopenharmony_ci	gsm->tty = tty_kref_get(tty);
25548c2ecf20Sopenharmony_ci	/* Turn off tty XON/XOFF handling to handle it explicitly. */
25558c2ecf20Sopenharmony_ci	gsm->old_c_iflag = tty->termios.c_iflag;
25568c2ecf20Sopenharmony_ci	tty->termios.c_iflag &= (IXON | IXOFF);
25578c2ecf20Sopenharmony_ci}
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci/**
25608c2ecf20Sopenharmony_ci *	gsmld_detach_gsm	-	stop doing 0710 mux
25618c2ecf20Sopenharmony_ci *	@tty: tty attached to the mux
25628c2ecf20Sopenharmony_ci *	@gsm: mux
25638c2ecf20Sopenharmony_ci *
25648c2ecf20Sopenharmony_ci *	Shutdown and then clean up the resources used by the line discipline
25658c2ecf20Sopenharmony_ci */
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_cistatic void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
25688c2ecf20Sopenharmony_ci{
25698c2ecf20Sopenharmony_ci	WARN_ON(tty != gsm->tty);
25708c2ecf20Sopenharmony_ci	/* Restore tty XON/XOFF handling. */
25718c2ecf20Sopenharmony_ci	gsm->tty->termios.c_iflag = gsm->old_c_iflag;
25728c2ecf20Sopenharmony_ci	tty_kref_put(gsm->tty);
25738c2ecf20Sopenharmony_ci	gsm->tty = NULL;
25748c2ecf20Sopenharmony_ci}
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_cistatic void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp,
25778c2ecf20Sopenharmony_ci			      char *fp, int count)
25788c2ecf20Sopenharmony_ci{
25798c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = tty->disc_data;
25808c2ecf20Sopenharmony_ci	char flags = TTY_NORMAL;
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci	if (debug & 4)
25838c2ecf20Sopenharmony_ci		print_hex_dump_bytes("gsmld_receive: ", DUMP_PREFIX_OFFSET,
25848c2ecf20Sopenharmony_ci				     cp, count);
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	for (; count; count--, cp++) {
25878c2ecf20Sopenharmony_ci		if (fp)
25888c2ecf20Sopenharmony_ci			flags = *fp++;
25898c2ecf20Sopenharmony_ci		switch (flags) {
25908c2ecf20Sopenharmony_ci		case TTY_NORMAL:
25918c2ecf20Sopenharmony_ci			if (gsm->receive)
25928c2ecf20Sopenharmony_ci				gsm->receive(gsm, *cp);
25938c2ecf20Sopenharmony_ci			break;
25948c2ecf20Sopenharmony_ci		case TTY_OVERRUN:
25958c2ecf20Sopenharmony_ci		case TTY_BREAK:
25968c2ecf20Sopenharmony_ci		case TTY_PARITY:
25978c2ecf20Sopenharmony_ci		case TTY_FRAME:
25988c2ecf20Sopenharmony_ci			gsm_error(gsm, *cp, flags);
25998c2ecf20Sopenharmony_ci			break;
26008c2ecf20Sopenharmony_ci		default:
26018c2ecf20Sopenharmony_ci			WARN_ONCE(1, "%s: unknown flag %d\n",
26028c2ecf20Sopenharmony_ci			       tty_name(tty), flags);
26038c2ecf20Sopenharmony_ci			break;
26048c2ecf20Sopenharmony_ci		}
26058c2ecf20Sopenharmony_ci	}
26068c2ecf20Sopenharmony_ci	/* FASYNC if needed ? */
26078c2ecf20Sopenharmony_ci	/* If clogged call tty_throttle(tty); */
26088c2ecf20Sopenharmony_ci}
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci/**
26118c2ecf20Sopenharmony_ci *	gsmld_flush_buffer	-	clean input queue
26128c2ecf20Sopenharmony_ci *	@tty:	terminal device
26138c2ecf20Sopenharmony_ci *
26148c2ecf20Sopenharmony_ci *	Flush the input buffer. Called when the line discipline is
26158c2ecf20Sopenharmony_ci *	being closed, when the tty layer wants the buffer flushed (eg
26168c2ecf20Sopenharmony_ci *	at hangup).
26178c2ecf20Sopenharmony_ci */
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_cistatic void gsmld_flush_buffer(struct tty_struct *tty)
26208c2ecf20Sopenharmony_ci{
26218c2ecf20Sopenharmony_ci}
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci/**
26248c2ecf20Sopenharmony_ci *	gsmld_close		-	close the ldisc for this tty
26258c2ecf20Sopenharmony_ci *	@tty: device
26268c2ecf20Sopenharmony_ci *
26278c2ecf20Sopenharmony_ci *	Called from the terminal layer when this line discipline is
26288c2ecf20Sopenharmony_ci *	being shut down, either because of a close or becsuse of a
26298c2ecf20Sopenharmony_ci *	discipline change. The function will not be called while other
26308c2ecf20Sopenharmony_ci *	ldisc methods are in progress.
26318c2ecf20Sopenharmony_ci */
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_cistatic void gsmld_close(struct tty_struct *tty)
26348c2ecf20Sopenharmony_ci{
26358c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = tty->disc_data;
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	/* The ldisc locks and closes the port before calling our close. This
26388c2ecf20Sopenharmony_ci	 * means we have no way to do a proper disconnect. We will not bother
26398c2ecf20Sopenharmony_ci	 * to do one.
26408c2ecf20Sopenharmony_ci	 */
26418c2ecf20Sopenharmony_ci	gsm_cleanup_mux(gsm, false);
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci	gsmld_detach_gsm(tty, gsm);
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	gsmld_flush_buffer(tty);
26468c2ecf20Sopenharmony_ci	/* Do other clean up here */
26478c2ecf20Sopenharmony_ci	mux_put(gsm);
26488c2ecf20Sopenharmony_ci}
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_ci/**
26518c2ecf20Sopenharmony_ci *	gsmld_open		-	open an ldisc
26528c2ecf20Sopenharmony_ci *	@tty: terminal to open
26538c2ecf20Sopenharmony_ci *
26548c2ecf20Sopenharmony_ci *	Called when this line discipline is being attached to the
26558c2ecf20Sopenharmony_ci *	terminal device. Can sleep. Called serialized so that no
26568c2ecf20Sopenharmony_ci *	other events will occur in parallel. No further open will occur
26578c2ecf20Sopenharmony_ci *	until a close.
26588c2ecf20Sopenharmony_ci */
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_cistatic int gsmld_open(struct tty_struct *tty)
26618c2ecf20Sopenharmony_ci{
26628c2ecf20Sopenharmony_ci	struct gsm_mux *gsm;
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
26658c2ecf20Sopenharmony_ci		return -EPERM;
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	if (tty->ops->write == NULL)
26688c2ecf20Sopenharmony_ci		return -EINVAL;
26698c2ecf20Sopenharmony_ci
26708c2ecf20Sopenharmony_ci	/* Attach our ldisc data */
26718c2ecf20Sopenharmony_ci	gsm = gsm_alloc_mux();
26728c2ecf20Sopenharmony_ci	if (gsm == NULL)
26738c2ecf20Sopenharmony_ci		return -ENOMEM;
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	tty->disc_data = gsm;
26768c2ecf20Sopenharmony_ci	tty->receive_room = 65536;
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	/* Attach the initial passive connection */
26798c2ecf20Sopenharmony_ci	gsm->encoding = 1;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	gsmld_attach_gsm(tty, gsm);
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci	timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	return 0;
26868c2ecf20Sopenharmony_ci}
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_ci/**
26898c2ecf20Sopenharmony_ci *	gsmld_write_wakeup	-	asynchronous I/O notifier
26908c2ecf20Sopenharmony_ci *	@tty: tty device
26918c2ecf20Sopenharmony_ci *
26928c2ecf20Sopenharmony_ci *	Required for the ptys, serial driver etc. since processes
26938c2ecf20Sopenharmony_ci *	that attach themselves to the master and rely on ASYNC
26948c2ecf20Sopenharmony_ci *	IO must be woken up
26958c2ecf20Sopenharmony_ci */
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_cistatic void gsmld_write_wakeup(struct tty_struct *tty)
26988c2ecf20Sopenharmony_ci{
26998c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = tty->disc_data;
27008c2ecf20Sopenharmony_ci	unsigned long flags;
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	/* Queue poll */
27038c2ecf20Sopenharmony_ci	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
27048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm->tx_lock, flags);
27058c2ecf20Sopenharmony_ci	gsm_data_kick(gsm, NULL);
27068c2ecf20Sopenharmony_ci	if (gsm->tx_bytes < TX_THRESH_LO) {
27078c2ecf20Sopenharmony_ci		gsm_dlci_data_sweep(gsm);
27088c2ecf20Sopenharmony_ci	}
27098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm->tx_lock, flags);
27108c2ecf20Sopenharmony_ci}
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci/**
27138c2ecf20Sopenharmony_ci *	gsmld_read		-	read function for tty
27148c2ecf20Sopenharmony_ci *	@tty: tty device
27158c2ecf20Sopenharmony_ci *	@file: file object
27168c2ecf20Sopenharmony_ci *	@buf: userspace buffer pointer
27178c2ecf20Sopenharmony_ci *	@nr: size of I/O
27188c2ecf20Sopenharmony_ci *
27198c2ecf20Sopenharmony_ci *	Perform reads for the line discipline. We are guaranteed that the
27208c2ecf20Sopenharmony_ci *	line discipline will not be closed under us but we may get multiple
27218c2ecf20Sopenharmony_ci *	parallel readers and must handle this ourselves. We may also get
27228c2ecf20Sopenharmony_ci *	a hangup. Always called in user context, may sleep.
27238c2ecf20Sopenharmony_ci *
27248c2ecf20Sopenharmony_ci *	This code must be sure never to sleep through a hangup.
27258c2ecf20Sopenharmony_ci */
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_cistatic ssize_t gsmld_read(struct tty_struct *tty, struct file *file,
27288c2ecf20Sopenharmony_ci			  unsigned char *buf, size_t nr,
27298c2ecf20Sopenharmony_ci			  void **cookie, unsigned long offset)
27308c2ecf20Sopenharmony_ci{
27318c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
27328c2ecf20Sopenharmony_ci}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci/**
27358c2ecf20Sopenharmony_ci *	gsmld_write		-	write function for tty
27368c2ecf20Sopenharmony_ci *	@tty: tty device
27378c2ecf20Sopenharmony_ci *	@file: file object
27388c2ecf20Sopenharmony_ci *	@buf: userspace buffer pointer
27398c2ecf20Sopenharmony_ci *	@nr: size of I/O
27408c2ecf20Sopenharmony_ci *
27418c2ecf20Sopenharmony_ci *	Called when the owner of the device wants to send a frame
27428c2ecf20Sopenharmony_ci *	itself (or some other control data). The data is transferred
27438c2ecf20Sopenharmony_ci *	as-is and must be properly framed and checksummed as appropriate
27448c2ecf20Sopenharmony_ci *	by userspace. Frames are either sent whole or not at all as this
27458c2ecf20Sopenharmony_ci *	avoids pain user side.
27468c2ecf20Sopenharmony_ci */
27478c2ecf20Sopenharmony_ci
27488c2ecf20Sopenharmony_cistatic ssize_t gsmld_write(struct tty_struct *tty, struct file *file,
27498c2ecf20Sopenharmony_ci			   const unsigned char *buf, size_t nr)
27508c2ecf20Sopenharmony_ci{
27518c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = tty->disc_data;
27528c2ecf20Sopenharmony_ci	unsigned long flags;
27538c2ecf20Sopenharmony_ci	int space;
27548c2ecf20Sopenharmony_ci	int ret;
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci	if (!gsm)
27578c2ecf20Sopenharmony_ci		return -ENODEV;
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci	ret = -ENOBUFS;
27608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&gsm->tx_lock, flags);
27618c2ecf20Sopenharmony_ci	space = tty_write_room(tty);
27628c2ecf20Sopenharmony_ci	if (space >= nr)
27638c2ecf20Sopenharmony_ci		ret = tty->ops->write(tty, buf, nr);
27648c2ecf20Sopenharmony_ci	else
27658c2ecf20Sopenharmony_ci		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
27668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&gsm->tx_lock, flags);
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	return ret;
27698c2ecf20Sopenharmony_ci}
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci/**
27728c2ecf20Sopenharmony_ci *	gsmld_poll		-	poll method for N_GSM0710
27738c2ecf20Sopenharmony_ci *	@tty: terminal device
27748c2ecf20Sopenharmony_ci *	@file: file accessing it
27758c2ecf20Sopenharmony_ci *	@wait: poll table
27768c2ecf20Sopenharmony_ci *
27778c2ecf20Sopenharmony_ci *	Called when the line discipline is asked to poll() for data or
27788c2ecf20Sopenharmony_ci *	for special events. This code is not serialized with respect to
27798c2ecf20Sopenharmony_ci *	other events save open/close.
27808c2ecf20Sopenharmony_ci *
27818c2ecf20Sopenharmony_ci *	This code must be sure never to sleep through a hangup.
27828c2ecf20Sopenharmony_ci *	Called without the kernel lock held - fine
27838c2ecf20Sopenharmony_ci */
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_cistatic __poll_t gsmld_poll(struct tty_struct *tty, struct file *file,
27868c2ecf20Sopenharmony_ci							poll_table *wait)
27878c2ecf20Sopenharmony_ci{
27888c2ecf20Sopenharmony_ci	__poll_t mask = 0;
27898c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = tty->disc_data;
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci	poll_wait(file, &tty->read_wait, wait);
27928c2ecf20Sopenharmony_ci	poll_wait(file, &tty->write_wait, wait);
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	if (gsm->dead)
27958c2ecf20Sopenharmony_ci		mask |= EPOLLHUP;
27968c2ecf20Sopenharmony_ci	if (tty_hung_up_p(file))
27978c2ecf20Sopenharmony_ci		mask |= EPOLLHUP;
27988c2ecf20Sopenharmony_ci	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
27998c2ecf20Sopenharmony_ci		mask |= EPOLLHUP;
28008c2ecf20Sopenharmony_ci	if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0)
28018c2ecf20Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM;
28028c2ecf20Sopenharmony_ci	return mask;
28038c2ecf20Sopenharmony_ci}
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_cistatic int gsmld_ioctl(struct tty_struct *tty, struct file *file,
28068c2ecf20Sopenharmony_ci		       unsigned int cmd, unsigned long arg)
28078c2ecf20Sopenharmony_ci{
28088c2ecf20Sopenharmony_ci	struct gsm_config c;
28098c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = tty->disc_data;
28108c2ecf20Sopenharmony_ci	unsigned int base;
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	switch (cmd) {
28138c2ecf20Sopenharmony_ci	case GSMIOC_GETCONF:
28148c2ecf20Sopenharmony_ci		gsm_copy_config_values(gsm, &c);
28158c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)arg, &c, sizeof(c)))
28168c2ecf20Sopenharmony_ci			return -EFAULT;
28178c2ecf20Sopenharmony_ci		return 0;
28188c2ecf20Sopenharmony_ci	case GSMIOC_SETCONF:
28198c2ecf20Sopenharmony_ci		if (copy_from_user(&c, (void __user *)arg, sizeof(c)))
28208c2ecf20Sopenharmony_ci			return -EFAULT;
28218c2ecf20Sopenharmony_ci		return gsm_config(gsm, &c);
28228c2ecf20Sopenharmony_ci	case GSMIOC_GETFIRST:
28238c2ecf20Sopenharmony_ci		base = mux_num_to_base(gsm);
28248c2ecf20Sopenharmony_ci		return put_user(base + 1, (__u32 __user *)arg);
28258c2ecf20Sopenharmony_ci	default:
28268c2ecf20Sopenharmony_ci		return n_tty_ioctl_helper(tty, file, cmd, arg);
28278c2ecf20Sopenharmony_ci	}
28288c2ecf20Sopenharmony_ci}
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci/*
28318c2ecf20Sopenharmony_ci *	Network interface
28328c2ecf20Sopenharmony_ci *
28338c2ecf20Sopenharmony_ci */
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_cistatic int gsm_mux_net_open(struct net_device *net)
28368c2ecf20Sopenharmony_ci{
28378c2ecf20Sopenharmony_ci	pr_debug("%s called\n", __func__);
28388c2ecf20Sopenharmony_ci	netif_start_queue(net);
28398c2ecf20Sopenharmony_ci	return 0;
28408c2ecf20Sopenharmony_ci}
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_cistatic int gsm_mux_net_close(struct net_device *net)
28438c2ecf20Sopenharmony_ci{
28448c2ecf20Sopenharmony_ci	netif_stop_queue(net);
28458c2ecf20Sopenharmony_ci	return 0;
28468c2ecf20Sopenharmony_ci}
28478c2ecf20Sopenharmony_ci
28488c2ecf20Sopenharmony_cistatic void dlci_net_free(struct gsm_dlci *dlci)
28498c2ecf20Sopenharmony_ci{
28508c2ecf20Sopenharmony_ci	if (!dlci->net) {
28518c2ecf20Sopenharmony_ci		WARN_ON(1);
28528c2ecf20Sopenharmony_ci		return;
28538c2ecf20Sopenharmony_ci	}
28548c2ecf20Sopenharmony_ci	dlci->adaption = dlci->prev_adaption;
28558c2ecf20Sopenharmony_ci	dlci->data = dlci->prev_data;
28568c2ecf20Sopenharmony_ci	free_netdev(dlci->net);
28578c2ecf20Sopenharmony_ci	dlci->net = NULL;
28588c2ecf20Sopenharmony_ci}
28598c2ecf20Sopenharmony_cistatic void net_free(struct kref *ref)
28608c2ecf20Sopenharmony_ci{
28618c2ecf20Sopenharmony_ci	struct gsm_mux_net *mux_net;
28628c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci	mux_net = container_of(ref, struct gsm_mux_net, ref);
28658c2ecf20Sopenharmony_ci	dlci = mux_net->dlci;
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	if (dlci->net) {
28688c2ecf20Sopenharmony_ci		unregister_netdev(dlci->net);
28698c2ecf20Sopenharmony_ci		dlci_net_free(dlci);
28708c2ecf20Sopenharmony_ci	}
28718c2ecf20Sopenharmony_ci}
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_cistatic inline void muxnet_get(struct gsm_mux_net *mux_net)
28748c2ecf20Sopenharmony_ci{
28758c2ecf20Sopenharmony_ci	kref_get(&mux_net->ref);
28768c2ecf20Sopenharmony_ci}
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_cistatic inline void muxnet_put(struct gsm_mux_net *mux_net)
28798c2ecf20Sopenharmony_ci{
28808c2ecf20Sopenharmony_ci	kref_put(&mux_net->ref, net_free);
28818c2ecf20Sopenharmony_ci}
28828c2ecf20Sopenharmony_ci
28838c2ecf20Sopenharmony_cistatic netdev_tx_t gsm_mux_net_start_xmit(struct sk_buff *skb,
28848c2ecf20Sopenharmony_ci				      struct net_device *net)
28858c2ecf20Sopenharmony_ci{
28868c2ecf20Sopenharmony_ci	struct gsm_mux_net *mux_net = netdev_priv(net);
28878c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = mux_net->dlci;
28888c2ecf20Sopenharmony_ci	muxnet_get(mux_net);
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_ci	skb_queue_head(&dlci->skb_list, skb);
28918c2ecf20Sopenharmony_ci	net->stats.tx_packets++;
28928c2ecf20Sopenharmony_ci	net->stats.tx_bytes += skb->len;
28938c2ecf20Sopenharmony_ci	gsm_dlci_data_kick(dlci);
28948c2ecf20Sopenharmony_ci	/* And tell the kernel when the last transmit started. */
28958c2ecf20Sopenharmony_ci	netif_trans_update(net);
28968c2ecf20Sopenharmony_ci	muxnet_put(mux_net);
28978c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
28988c2ecf20Sopenharmony_ci}
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci/* called when a packet did not ack after watchdogtimeout */
29018c2ecf20Sopenharmony_cistatic void gsm_mux_net_tx_timeout(struct net_device *net, unsigned int txqueue)
29028c2ecf20Sopenharmony_ci{
29038c2ecf20Sopenharmony_ci	/* Tell syslog we are hosed. */
29048c2ecf20Sopenharmony_ci	dev_dbg(&net->dev, "Tx timed out.\n");
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_ci	/* Update statistics */
29078c2ecf20Sopenharmony_ci	net->stats.tx_errors++;
29088c2ecf20Sopenharmony_ci}
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_cistatic void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
29118c2ecf20Sopenharmony_ci				const unsigned char *in_buf, int size)
29128c2ecf20Sopenharmony_ci{
29138c2ecf20Sopenharmony_ci	struct net_device *net = dlci->net;
29148c2ecf20Sopenharmony_ci	struct sk_buff *skb;
29158c2ecf20Sopenharmony_ci	struct gsm_mux_net *mux_net = netdev_priv(net);
29168c2ecf20Sopenharmony_ci	muxnet_get(mux_net);
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	/* Allocate an sk_buff */
29198c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(size + NET_IP_ALIGN);
29208c2ecf20Sopenharmony_ci	if (!skb) {
29218c2ecf20Sopenharmony_ci		/* We got no receive buffer. */
29228c2ecf20Sopenharmony_ci		net->stats.rx_dropped++;
29238c2ecf20Sopenharmony_ci		muxnet_put(mux_net);
29248c2ecf20Sopenharmony_ci		return;
29258c2ecf20Sopenharmony_ci	}
29268c2ecf20Sopenharmony_ci	skb_reserve(skb, NET_IP_ALIGN);
29278c2ecf20Sopenharmony_ci	skb_put_data(skb, in_buf, size);
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci	skb->dev = net;
29308c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci	/* Ship it off to the kernel */
29338c2ecf20Sopenharmony_ci	netif_rx(skb);
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci	/* update out statistics */
29368c2ecf20Sopenharmony_ci	net->stats.rx_packets++;
29378c2ecf20Sopenharmony_ci	net->stats.rx_bytes += size;
29388c2ecf20Sopenharmony_ci	muxnet_put(mux_net);
29398c2ecf20Sopenharmony_ci	return;
29408c2ecf20Sopenharmony_ci}
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_cistatic void gsm_mux_net_init(struct net_device *net)
29438c2ecf20Sopenharmony_ci{
29448c2ecf20Sopenharmony_ci	static const struct net_device_ops gsm_netdev_ops = {
29458c2ecf20Sopenharmony_ci		.ndo_open		= gsm_mux_net_open,
29468c2ecf20Sopenharmony_ci		.ndo_stop		= gsm_mux_net_close,
29478c2ecf20Sopenharmony_ci		.ndo_start_xmit		= gsm_mux_net_start_xmit,
29488c2ecf20Sopenharmony_ci		.ndo_tx_timeout		= gsm_mux_net_tx_timeout,
29498c2ecf20Sopenharmony_ci	};
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	net->netdev_ops = &gsm_netdev_ops;
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci	/* fill in the other fields */
29548c2ecf20Sopenharmony_ci	net->watchdog_timeo = GSM_NET_TX_TIMEOUT;
29558c2ecf20Sopenharmony_ci	net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
29568c2ecf20Sopenharmony_ci	net->type = ARPHRD_NONE;
29578c2ecf20Sopenharmony_ci	net->tx_queue_len = 10;
29588c2ecf20Sopenharmony_ci}
29598c2ecf20Sopenharmony_ci
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci/* caller holds the dlci mutex */
29628c2ecf20Sopenharmony_cistatic void gsm_destroy_network(struct gsm_dlci *dlci)
29638c2ecf20Sopenharmony_ci{
29648c2ecf20Sopenharmony_ci	struct gsm_mux_net *mux_net;
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	pr_debug("destroy network interface\n");
29678c2ecf20Sopenharmony_ci	if (!dlci->net)
29688c2ecf20Sopenharmony_ci		return;
29698c2ecf20Sopenharmony_ci	mux_net = netdev_priv(dlci->net);
29708c2ecf20Sopenharmony_ci	muxnet_put(mux_net);
29718c2ecf20Sopenharmony_ci}
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci/* caller holds the dlci mutex */
29758c2ecf20Sopenharmony_cistatic int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
29768c2ecf20Sopenharmony_ci{
29778c2ecf20Sopenharmony_ci	char *netname;
29788c2ecf20Sopenharmony_ci	int retval = 0;
29798c2ecf20Sopenharmony_ci	struct net_device *net;
29808c2ecf20Sopenharmony_ci	struct gsm_mux_net *mux_net;
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
29838c2ecf20Sopenharmony_ci		return -EPERM;
29848c2ecf20Sopenharmony_ci
29858c2ecf20Sopenharmony_ci	/* Already in a non tty mode */
29868c2ecf20Sopenharmony_ci	if (dlci->adaption > 2)
29878c2ecf20Sopenharmony_ci		return -EBUSY;
29888c2ecf20Sopenharmony_ci
29898c2ecf20Sopenharmony_ci	if (nc->protocol != htons(ETH_P_IP))
29908c2ecf20Sopenharmony_ci		return -EPROTONOSUPPORT;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	if (nc->adaption != 3 && nc->adaption != 4)
29938c2ecf20Sopenharmony_ci		return -EPROTONOSUPPORT;
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	pr_debug("create network interface\n");
29968c2ecf20Sopenharmony_ci
29978c2ecf20Sopenharmony_ci	netname = "gsm%d";
29988c2ecf20Sopenharmony_ci	if (nc->if_name[0] != '\0')
29998c2ecf20Sopenharmony_ci		netname = nc->if_name;
30008c2ecf20Sopenharmony_ci	net = alloc_netdev(sizeof(struct gsm_mux_net), netname,
30018c2ecf20Sopenharmony_ci			   NET_NAME_UNKNOWN, gsm_mux_net_init);
30028c2ecf20Sopenharmony_ci	if (!net) {
30038c2ecf20Sopenharmony_ci		pr_err("alloc_netdev failed\n");
30048c2ecf20Sopenharmony_ci		return -ENOMEM;
30058c2ecf20Sopenharmony_ci	}
30068c2ecf20Sopenharmony_ci	net->mtu = dlci->gsm->mtu;
30078c2ecf20Sopenharmony_ci	net->min_mtu = 8;
30088c2ecf20Sopenharmony_ci	net->max_mtu = dlci->gsm->mtu;
30098c2ecf20Sopenharmony_ci	mux_net = netdev_priv(net);
30108c2ecf20Sopenharmony_ci	mux_net->dlci = dlci;
30118c2ecf20Sopenharmony_ci	kref_init(&mux_net->ref);
30128c2ecf20Sopenharmony_ci	strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ci	/* reconfigure dlci for network */
30158c2ecf20Sopenharmony_ci	dlci->prev_adaption = dlci->adaption;
30168c2ecf20Sopenharmony_ci	dlci->prev_data = dlci->data;
30178c2ecf20Sopenharmony_ci	dlci->adaption = nc->adaption;
30188c2ecf20Sopenharmony_ci	dlci->data = gsm_mux_rx_netchar;
30198c2ecf20Sopenharmony_ci	dlci->net = net;
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ci	pr_debug("register netdev\n");
30228c2ecf20Sopenharmony_ci	retval = register_netdev(net);
30238c2ecf20Sopenharmony_ci	if (retval) {
30248c2ecf20Sopenharmony_ci		pr_err("network register fail %d\n", retval);
30258c2ecf20Sopenharmony_ci		dlci_net_free(dlci);
30268c2ecf20Sopenharmony_ci		return retval;
30278c2ecf20Sopenharmony_ci	}
30288c2ecf20Sopenharmony_ci	return net->ifindex;	/* return network index */
30298c2ecf20Sopenharmony_ci}
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci/* Line discipline for real tty */
30328c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops tty_ldisc_packet = {
30338c2ecf20Sopenharmony_ci	.owner		 = THIS_MODULE,
30348c2ecf20Sopenharmony_ci	.magic           = TTY_LDISC_MAGIC,
30358c2ecf20Sopenharmony_ci	.name            = "n_gsm",
30368c2ecf20Sopenharmony_ci	.open            = gsmld_open,
30378c2ecf20Sopenharmony_ci	.close           = gsmld_close,
30388c2ecf20Sopenharmony_ci	.flush_buffer    = gsmld_flush_buffer,
30398c2ecf20Sopenharmony_ci	.read            = gsmld_read,
30408c2ecf20Sopenharmony_ci	.write           = gsmld_write,
30418c2ecf20Sopenharmony_ci	.ioctl           = gsmld_ioctl,
30428c2ecf20Sopenharmony_ci	.poll            = gsmld_poll,
30438c2ecf20Sopenharmony_ci	.receive_buf     = gsmld_receive_buf,
30448c2ecf20Sopenharmony_ci	.write_wakeup    = gsmld_write_wakeup
30458c2ecf20Sopenharmony_ci};
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci/*
30488c2ecf20Sopenharmony_ci *	Virtual tty side
30498c2ecf20Sopenharmony_ci */
30508c2ecf20Sopenharmony_ci
30518c2ecf20Sopenharmony_ci#define TX_SIZE		512
30528c2ecf20Sopenharmony_ci
30538c2ecf20Sopenharmony_cistatic int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk)
30548c2ecf20Sopenharmony_ci{
30558c2ecf20Sopenharmony_ci	u8 modembits[3];
30568c2ecf20Sopenharmony_ci	struct gsm_control *ctrl;
30578c2ecf20Sopenharmony_ci	int len = 2;
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci	modembits[0] = (dlci->addr << 2) | 2 | EA;  /* DLCI, Valid, EA */
30608c2ecf20Sopenharmony_ci	modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
30618c2ecf20Sopenharmony_ci	if (brk) {
30628c2ecf20Sopenharmony_ci		modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */
30638c2ecf20Sopenharmony_ci		len++;
30648c2ecf20Sopenharmony_ci	}
30658c2ecf20Sopenharmony_ci	ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len);
30668c2ecf20Sopenharmony_ci	if (ctrl == NULL)
30678c2ecf20Sopenharmony_ci		return -ENOMEM;
30688c2ecf20Sopenharmony_ci	return gsm_control_wait(dlci->gsm, ctrl);
30698c2ecf20Sopenharmony_ci}
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_cistatic int gsm_carrier_raised(struct tty_port *port)
30728c2ecf20Sopenharmony_ci{
30738c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
30748c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci	/* Not yet open so no carrier info */
30778c2ecf20Sopenharmony_ci	if (dlci->state != DLCI_OPEN)
30788c2ecf20Sopenharmony_ci		return 0;
30798c2ecf20Sopenharmony_ci	if (debug & 2)
30808c2ecf20Sopenharmony_ci		return 1;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	/*
30838c2ecf20Sopenharmony_ci	 * Basic mode with control channel in ADM mode may not respond
30848c2ecf20Sopenharmony_ci	 * to CMD_MSC at all and modem_rx is empty.
30858c2ecf20Sopenharmony_ci	 */
30868c2ecf20Sopenharmony_ci	if (gsm->encoding == 0 && gsm->dlci[0]->mode == DLCI_MODE_ADM &&
30878c2ecf20Sopenharmony_ci	    !dlci->modem_rx)
30888c2ecf20Sopenharmony_ci		return 1;
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	return dlci->modem_rx & TIOCM_CD;
30918c2ecf20Sopenharmony_ci}
30928c2ecf20Sopenharmony_ci
30938c2ecf20Sopenharmony_cistatic void gsm_dtr_rts(struct tty_port *port, int onoff)
30948c2ecf20Sopenharmony_ci{
30958c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
30968c2ecf20Sopenharmony_ci	unsigned int modem_tx = dlci->modem_tx;
30978c2ecf20Sopenharmony_ci	if (onoff)
30988c2ecf20Sopenharmony_ci		modem_tx |= TIOCM_DTR | TIOCM_RTS;
30998c2ecf20Sopenharmony_ci	else
31008c2ecf20Sopenharmony_ci		modem_tx &= ~(TIOCM_DTR | TIOCM_RTS);
31018c2ecf20Sopenharmony_ci	if (modem_tx != dlci->modem_tx) {
31028c2ecf20Sopenharmony_ci		dlci->modem_tx = modem_tx;
31038c2ecf20Sopenharmony_ci		gsmtty_modem_update(dlci, 0);
31048c2ecf20Sopenharmony_ci	}
31058c2ecf20Sopenharmony_ci}
31068c2ecf20Sopenharmony_ci
31078c2ecf20Sopenharmony_cistatic const struct tty_port_operations gsm_port_ops = {
31088c2ecf20Sopenharmony_ci	.carrier_raised = gsm_carrier_raised,
31098c2ecf20Sopenharmony_ci	.dtr_rts = gsm_dtr_rts,
31108c2ecf20Sopenharmony_ci	.destruct = gsm_dlci_free,
31118c2ecf20Sopenharmony_ci};
31128c2ecf20Sopenharmony_ci
31138c2ecf20Sopenharmony_cistatic int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
31148c2ecf20Sopenharmony_ci{
31158c2ecf20Sopenharmony_ci	struct gsm_mux *gsm;
31168c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci;
31178c2ecf20Sopenharmony_ci	unsigned int line = tty->index;
31188c2ecf20Sopenharmony_ci	unsigned int mux = mux_line_to_num(line);
31198c2ecf20Sopenharmony_ci	bool alloc = false;
31208c2ecf20Sopenharmony_ci	int ret;
31218c2ecf20Sopenharmony_ci
31228c2ecf20Sopenharmony_ci	line = line & 0x3F;
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci	if (mux >= MAX_MUX)
31258c2ecf20Sopenharmony_ci		return -ENXIO;
31268c2ecf20Sopenharmony_ci	/* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */
31278c2ecf20Sopenharmony_ci	if (gsm_mux[mux] == NULL)
31288c2ecf20Sopenharmony_ci		return -EUNATCH;
31298c2ecf20Sopenharmony_ci	if (line == 0 || line > 61)	/* 62/63 reserved */
31308c2ecf20Sopenharmony_ci		return -ECHRNG;
31318c2ecf20Sopenharmony_ci	gsm = gsm_mux[mux];
31328c2ecf20Sopenharmony_ci	if (gsm->dead)
31338c2ecf20Sopenharmony_ci		return -EL2HLT;
31348c2ecf20Sopenharmony_ci	/* If DLCI 0 is not yet fully open return an error.
31358c2ecf20Sopenharmony_ci	This is ok from a locking
31368c2ecf20Sopenharmony_ci	perspective as we don't have to worry about this
31378c2ecf20Sopenharmony_ci	if DLCI0 is lost */
31388c2ecf20Sopenharmony_ci	mutex_lock(&gsm->mutex);
31398c2ecf20Sopenharmony_ci	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) {
31408c2ecf20Sopenharmony_ci		mutex_unlock(&gsm->mutex);
31418c2ecf20Sopenharmony_ci		return -EL2NSYNC;
31428c2ecf20Sopenharmony_ci	}
31438c2ecf20Sopenharmony_ci	dlci = gsm->dlci[line];
31448c2ecf20Sopenharmony_ci	if (dlci == NULL) {
31458c2ecf20Sopenharmony_ci		alloc = true;
31468c2ecf20Sopenharmony_ci		dlci = gsm_dlci_alloc(gsm, line);
31478c2ecf20Sopenharmony_ci	}
31488c2ecf20Sopenharmony_ci	if (dlci == NULL) {
31498c2ecf20Sopenharmony_ci		mutex_unlock(&gsm->mutex);
31508c2ecf20Sopenharmony_ci		return -ENOMEM;
31518c2ecf20Sopenharmony_ci	}
31528c2ecf20Sopenharmony_ci	ret = tty_port_install(&dlci->port, driver, tty);
31538c2ecf20Sopenharmony_ci	if (ret) {
31548c2ecf20Sopenharmony_ci		if (alloc)
31558c2ecf20Sopenharmony_ci			dlci_put(dlci);
31568c2ecf20Sopenharmony_ci		mutex_unlock(&gsm->mutex);
31578c2ecf20Sopenharmony_ci		return ret;
31588c2ecf20Sopenharmony_ci	}
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ci	dlci_get(dlci);
31618c2ecf20Sopenharmony_ci	dlci_get(gsm->dlci[0]);
31628c2ecf20Sopenharmony_ci	mux_get(gsm);
31638c2ecf20Sopenharmony_ci	tty->driver_data = dlci;
31648c2ecf20Sopenharmony_ci	mutex_unlock(&gsm->mutex);
31658c2ecf20Sopenharmony_ci
31668c2ecf20Sopenharmony_ci	return 0;
31678c2ecf20Sopenharmony_ci}
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_cistatic int gsmtty_open(struct tty_struct *tty, struct file *filp)
31708c2ecf20Sopenharmony_ci{
31718c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
31728c2ecf20Sopenharmony_ci	struct tty_port *port = &dlci->port;
31738c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_ci	port->count++;
31768c2ecf20Sopenharmony_ci	tty_port_tty_set(port, tty);
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	dlci->modem_rx = 0;
31798c2ecf20Sopenharmony_ci	/* We could in theory open and close before we wait - eg if we get
31808c2ecf20Sopenharmony_ci	   a DM straight back. This is ok as that will have caused a hangup */
31818c2ecf20Sopenharmony_ci	tty_port_set_initialized(port, 1);
31828c2ecf20Sopenharmony_ci	/* Start sending off SABM messages */
31838c2ecf20Sopenharmony_ci	if (gsm->initiator)
31848c2ecf20Sopenharmony_ci		gsm_dlci_begin_open(dlci);
31858c2ecf20Sopenharmony_ci	else
31868c2ecf20Sopenharmony_ci		gsm_dlci_set_opening(dlci);
31878c2ecf20Sopenharmony_ci	/* And wait for virtual carrier */
31888c2ecf20Sopenharmony_ci	return tty_port_block_til_ready(port, tty, filp);
31898c2ecf20Sopenharmony_ci}
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_cistatic void gsmtty_close(struct tty_struct *tty, struct file *filp)
31928c2ecf20Sopenharmony_ci{
31938c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
31948c2ecf20Sopenharmony_ci
31958c2ecf20Sopenharmony_ci	if (dlci == NULL)
31968c2ecf20Sopenharmony_ci		return;
31978c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
31988c2ecf20Sopenharmony_ci		return;
31998c2ecf20Sopenharmony_ci	mutex_lock(&dlci->mutex);
32008c2ecf20Sopenharmony_ci	gsm_destroy_network(dlci);
32018c2ecf20Sopenharmony_ci	mutex_unlock(&dlci->mutex);
32028c2ecf20Sopenharmony_ci	if (tty_port_close_start(&dlci->port, tty, filp) == 0)
32038c2ecf20Sopenharmony_ci		return;
32048c2ecf20Sopenharmony_ci	gsm_dlci_begin_close(dlci);
32058c2ecf20Sopenharmony_ci	if (tty_port_initialized(&dlci->port) && C_HUPCL(tty))
32068c2ecf20Sopenharmony_ci		tty_port_lower_dtr_rts(&dlci->port);
32078c2ecf20Sopenharmony_ci	tty_port_close_end(&dlci->port, tty);
32088c2ecf20Sopenharmony_ci	tty_port_tty_set(&dlci->port, NULL);
32098c2ecf20Sopenharmony_ci	return;
32108c2ecf20Sopenharmony_ci}
32118c2ecf20Sopenharmony_ci
32128c2ecf20Sopenharmony_cistatic void gsmtty_hangup(struct tty_struct *tty)
32138c2ecf20Sopenharmony_ci{
32148c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32158c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32168c2ecf20Sopenharmony_ci		return;
32178c2ecf20Sopenharmony_ci	tty_port_hangup(&dlci->port);
32188c2ecf20Sopenharmony_ci	gsm_dlci_begin_close(dlci);
32198c2ecf20Sopenharmony_ci}
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_cistatic int gsmtty_write(struct tty_struct *tty, const unsigned char *buf,
32228c2ecf20Sopenharmony_ci								    int len)
32238c2ecf20Sopenharmony_ci{
32248c2ecf20Sopenharmony_ci	int sent;
32258c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32268c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32278c2ecf20Sopenharmony_ci		return -EINVAL;
32288c2ecf20Sopenharmony_ci	/* Stuff the bytes into the fifo queue */
32298c2ecf20Sopenharmony_ci	sent = kfifo_in_locked(&dlci->fifo, buf, len, &dlci->lock);
32308c2ecf20Sopenharmony_ci	/* Need to kick the channel */
32318c2ecf20Sopenharmony_ci	gsm_dlci_data_kick(dlci);
32328c2ecf20Sopenharmony_ci	return sent;
32338c2ecf20Sopenharmony_ci}
32348c2ecf20Sopenharmony_ci
32358c2ecf20Sopenharmony_cistatic int gsmtty_write_room(struct tty_struct *tty)
32368c2ecf20Sopenharmony_ci{
32378c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32388c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32398c2ecf20Sopenharmony_ci		return -EINVAL;
32408c2ecf20Sopenharmony_ci	return TX_SIZE - kfifo_len(&dlci->fifo);
32418c2ecf20Sopenharmony_ci}
32428c2ecf20Sopenharmony_ci
32438c2ecf20Sopenharmony_cistatic int gsmtty_chars_in_buffer(struct tty_struct *tty)
32448c2ecf20Sopenharmony_ci{
32458c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32468c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32478c2ecf20Sopenharmony_ci		return -EINVAL;
32488c2ecf20Sopenharmony_ci	return kfifo_len(&dlci->fifo);
32498c2ecf20Sopenharmony_ci}
32508c2ecf20Sopenharmony_ci
32518c2ecf20Sopenharmony_cistatic void gsmtty_flush_buffer(struct tty_struct *tty)
32528c2ecf20Sopenharmony_ci{
32538c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32548c2ecf20Sopenharmony_ci	unsigned long flags;
32558c2ecf20Sopenharmony_ci
32568c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32578c2ecf20Sopenharmony_ci		return;
32588c2ecf20Sopenharmony_ci	/* Caution needed: If we implement reliable transport classes
32598c2ecf20Sopenharmony_ci	   then the data being transmitted can't simply be junked once
32608c2ecf20Sopenharmony_ci	   it has first hit the stack. Until then we can just blow it
32618c2ecf20Sopenharmony_ci	   away */
32628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dlci->lock, flags);
32638c2ecf20Sopenharmony_ci	kfifo_reset(&dlci->fifo);
32648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dlci->lock, flags);
32658c2ecf20Sopenharmony_ci	/* Need to unhook this DLCI from the transmit queue logic */
32668c2ecf20Sopenharmony_ci}
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_cistatic void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout)
32698c2ecf20Sopenharmony_ci{
32708c2ecf20Sopenharmony_ci	/* The FIFO handles the queue so the kernel will do the right
32718c2ecf20Sopenharmony_ci	   thing waiting on chars_in_buffer before calling us. No work
32728c2ecf20Sopenharmony_ci	   to do here */
32738c2ecf20Sopenharmony_ci}
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_cistatic int gsmtty_tiocmget(struct tty_struct *tty)
32768c2ecf20Sopenharmony_ci{
32778c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32788c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32798c2ecf20Sopenharmony_ci		return -EINVAL;
32808c2ecf20Sopenharmony_ci	return dlci->modem_rx;
32818c2ecf20Sopenharmony_ci}
32828c2ecf20Sopenharmony_ci
32838c2ecf20Sopenharmony_cistatic int gsmtty_tiocmset(struct tty_struct *tty,
32848c2ecf20Sopenharmony_ci	unsigned int set, unsigned int clear)
32858c2ecf20Sopenharmony_ci{
32868c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
32878c2ecf20Sopenharmony_ci	unsigned int modem_tx = dlci->modem_tx;
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
32908c2ecf20Sopenharmony_ci		return -EINVAL;
32918c2ecf20Sopenharmony_ci	modem_tx &= ~clear;
32928c2ecf20Sopenharmony_ci	modem_tx |= set;
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci	if (modem_tx != dlci->modem_tx) {
32958c2ecf20Sopenharmony_ci		dlci->modem_tx = modem_tx;
32968c2ecf20Sopenharmony_ci		return gsmtty_modem_update(dlci, 0);
32978c2ecf20Sopenharmony_ci	}
32988c2ecf20Sopenharmony_ci	return 0;
32998c2ecf20Sopenharmony_ci}
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_cistatic int gsmtty_ioctl(struct tty_struct *tty,
33038c2ecf20Sopenharmony_ci			unsigned int cmd, unsigned long arg)
33048c2ecf20Sopenharmony_ci{
33058c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
33068c2ecf20Sopenharmony_ci	struct gsm_netconfig nc;
33078c2ecf20Sopenharmony_ci	int index;
33088c2ecf20Sopenharmony_ci
33098c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
33108c2ecf20Sopenharmony_ci		return -EINVAL;
33118c2ecf20Sopenharmony_ci	switch (cmd) {
33128c2ecf20Sopenharmony_ci	case GSMIOC_ENABLE_NET:
33138c2ecf20Sopenharmony_ci		if (copy_from_user(&nc, (void __user *)arg, sizeof(nc)))
33148c2ecf20Sopenharmony_ci			return -EFAULT;
33158c2ecf20Sopenharmony_ci		nc.if_name[IFNAMSIZ-1] = '\0';
33168c2ecf20Sopenharmony_ci		/* return net interface index or error code */
33178c2ecf20Sopenharmony_ci		mutex_lock(&dlci->mutex);
33188c2ecf20Sopenharmony_ci		index = gsm_create_network(dlci, &nc);
33198c2ecf20Sopenharmony_ci		mutex_unlock(&dlci->mutex);
33208c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)arg, &nc, sizeof(nc)))
33218c2ecf20Sopenharmony_ci			return -EFAULT;
33228c2ecf20Sopenharmony_ci		return index;
33238c2ecf20Sopenharmony_ci	case GSMIOC_DISABLE_NET:
33248c2ecf20Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
33258c2ecf20Sopenharmony_ci			return -EPERM;
33268c2ecf20Sopenharmony_ci		mutex_lock(&dlci->mutex);
33278c2ecf20Sopenharmony_ci		gsm_destroy_network(dlci);
33288c2ecf20Sopenharmony_ci		mutex_unlock(&dlci->mutex);
33298c2ecf20Sopenharmony_ci		return 0;
33308c2ecf20Sopenharmony_ci	default:
33318c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
33328c2ecf20Sopenharmony_ci	}
33338c2ecf20Sopenharmony_ci}
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_cistatic void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
33368c2ecf20Sopenharmony_ci{
33378c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
33388c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
33398c2ecf20Sopenharmony_ci		return;
33408c2ecf20Sopenharmony_ci	/* For the moment its fixed. In actual fact the speed information
33418c2ecf20Sopenharmony_ci	   for the virtual channel can be propogated in both directions by
33428c2ecf20Sopenharmony_ci	   the RPN control message. This however rapidly gets nasty as we
33438c2ecf20Sopenharmony_ci	   then have to remap modem signals each way according to whether
33448c2ecf20Sopenharmony_ci	   our virtual cable is null modem etc .. */
33458c2ecf20Sopenharmony_ci	tty_termios_copy_hw(&tty->termios, old);
33468c2ecf20Sopenharmony_ci}
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_cistatic void gsmtty_throttle(struct tty_struct *tty)
33498c2ecf20Sopenharmony_ci{
33508c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
33518c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
33528c2ecf20Sopenharmony_ci		return;
33538c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty))
33548c2ecf20Sopenharmony_ci		dlci->modem_tx &= ~TIOCM_RTS;
33558c2ecf20Sopenharmony_ci	dlci->throttled = true;
33568c2ecf20Sopenharmony_ci	/* Send an MSC with RTS cleared */
33578c2ecf20Sopenharmony_ci	gsmtty_modem_update(dlci, 0);
33588c2ecf20Sopenharmony_ci}
33598c2ecf20Sopenharmony_ci
33608c2ecf20Sopenharmony_cistatic void gsmtty_unthrottle(struct tty_struct *tty)
33618c2ecf20Sopenharmony_ci{
33628c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
33638c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
33648c2ecf20Sopenharmony_ci		return;
33658c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty))
33668c2ecf20Sopenharmony_ci		dlci->modem_tx |= TIOCM_RTS;
33678c2ecf20Sopenharmony_ci	dlci->throttled = false;
33688c2ecf20Sopenharmony_ci	/* Send an MSC with RTS set */
33698c2ecf20Sopenharmony_ci	gsmtty_modem_update(dlci, 0);
33708c2ecf20Sopenharmony_ci}
33718c2ecf20Sopenharmony_ci
33728c2ecf20Sopenharmony_cistatic int gsmtty_break_ctl(struct tty_struct *tty, int state)
33738c2ecf20Sopenharmony_ci{
33748c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
33758c2ecf20Sopenharmony_ci	int encode = 0;	/* Off */
33768c2ecf20Sopenharmony_ci	if (dlci->state == DLCI_CLOSED)
33778c2ecf20Sopenharmony_ci		return -EINVAL;
33788c2ecf20Sopenharmony_ci
33798c2ecf20Sopenharmony_ci	if (state == -1)	/* "On indefinitely" - we can't encode this
33808c2ecf20Sopenharmony_ci				    properly */
33818c2ecf20Sopenharmony_ci		encode = 0x0F;
33828c2ecf20Sopenharmony_ci	else if (state > 0) {
33838c2ecf20Sopenharmony_ci		encode = state / 200;	/* mS to encoding */
33848c2ecf20Sopenharmony_ci		if (encode > 0x0F)
33858c2ecf20Sopenharmony_ci			encode = 0x0F;	/* Best effort */
33868c2ecf20Sopenharmony_ci	}
33878c2ecf20Sopenharmony_ci	return gsmtty_modem_update(dlci, encode);
33888c2ecf20Sopenharmony_ci}
33898c2ecf20Sopenharmony_ci
33908c2ecf20Sopenharmony_cistatic void gsmtty_cleanup(struct tty_struct *tty)
33918c2ecf20Sopenharmony_ci{
33928c2ecf20Sopenharmony_ci	struct gsm_dlci *dlci = tty->driver_data;
33938c2ecf20Sopenharmony_ci	struct gsm_mux *gsm = dlci->gsm;
33948c2ecf20Sopenharmony_ci
33958c2ecf20Sopenharmony_ci	dlci_put(dlci);
33968c2ecf20Sopenharmony_ci	dlci_put(gsm->dlci[0]);
33978c2ecf20Sopenharmony_ci	mux_put(gsm);
33988c2ecf20Sopenharmony_ci}
33998c2ecf20Sopenharmony_ci
34008c2ecf20Sopenharmony_ci/* Virtual ttys for the demux */
34018c2ecf20Sopenharmony_cistatic const struct tty_operations gsmtty_ops = {
34028c2ecf20Sopenharmony_ci	.install		= gsmtty_install,
34038c2ecf20Sopenharmony_ci	.open			= gsmtty_open,
34048c2ecf20Sopenharmony_ci	.close			= gsmtty_close,
34058c2ecf20Sopenharmony_ci	.write			= gsmtty_write,
34068c2ecf20Sopenharmony_ci	.write_room		= gsmtty_write_room,
34078c2ecf20Sopenharmony_ci	.chars_in_buffer	= gsmtty_chars_in_buffer,
34088c2ecf20Sopenharmony_ci	.flush_buffer		= gsmtty_flush_buffer,
34098c2ecf20Sopenharmony_ci	.ioctl			= gsmtty_ioctl,
34108c2ecf20Sopenharmony_ci	.throttle		= gsmtty_throttle,
34118c2ecf20Sopenharmony_ci	.unthrottle		= gsmtty_unthrottle,
34128c2ecf20Sopenharmony_ci	.set_termios		= gsmtty_set_termios,
34138c2ecf20Sopenharmony_ci	.hangup			= gsmtty_hangup,
34148c2ecf20Sopenharmony_ci	.wait_until_sent	= gsmtty_wait_until_sent,
34158c2ecf20Sopenharmony_ci	.tiocmget		= gsmtty_tiocmget,
34168c2ecf20Sopenharmony_ci	.tiocmset		= gsmtty_tiocmset,
34178c2ecf20Sopenharmony_ci	.break_ctl		= gsmtty_break_ctl,
34188c2ecf20Sopenharmony_ci	.cleanup		= gsmtty_cleanup,
34198c2ecf20Sopenharmony_ci};
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ci
34238c2ecf20Sopenharmony_cistatic int __init gsm_init(void)
34248c2ecf20Sopenharmony_ci{
34258c2ecf20Sopenharmony_ci	/* Fill in our line protocol discipline, and register it */
34268c2ecf20Sopenharmony_ci	int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet);
34278c2ecf20Sopenharmony_ci	if (status != 0) {
34288c2ecf20Sopenharmony_ci		pr_err("n_gsm: can't register line discipline (err = %d)\n",
34298c2ecf20Sopenharmony_ci								status);
34308c2ecf20Sopenharmony_ci		return status;
34318c2ecf20Sopenharmony_ci	}
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	gsm_tty_driver = alloc_tty_driver(256);
34348c2ecf20Sopenharmony_ci	if (!gsm_tty_driver) {
34358c2ecf20Sopenharmony_ci		tty_unregister_ldisc(N_GSM0710);
34368c2ecf20Sopenharmony_ci		pr_err("gsm_init: tty allocation failed.\n");
34378c2ecf20Sopenharmony_ci		return -EINVAL;
34388c2ecf20Sopenharmony_ci	}
34398c2ecf20Sopenharmony_ci	gsm_tty_driver->driver_name	= "gsmtty";
34408c2ecf20Sopenharmony_ci	gsm_tty_driver->name		= "gsmtty";
34418c2ecf20Sopenharmony_ci	gsm_tty_driver->major		= 0;	/* Dynamic */
34428c2ecf20Sopenharmony_ci	gsm_tty_driver->minor_start	= 0;
34438c2ecf20Sopenharmony_ci	gsm_tty_driver->type		= TTY_DRIVER_TYPE_SERIAL;
34448c2ecf20Sopenharmony_ci	gsm_tty_driver->subtype	= SERIAL_TYPE_NORMAL;
34458c2ecf20Sopenharmony_ci	gsm_tty_driver->flags	= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
34468c2ecf20Sopenharmony_ci						| TTY_DRIVER_HARDWARE_BREAK;
34478c2ecf20Sopenharmony_ci	gsm_tty_driver->init_termios	= tty_std_termios;
34488c2ecf20Sopenharmony_ci	/* Fixme */
34498c2ecf20Sopenharmony_ci	gsm_tty_driver->init_termios.c_lflag &= ~ECHO;
34508c2ecf20Sopenharmony_ci	tty_set_operations(gsm_tty_driver, &gsmtty_ops);
34518c2ecf20Sopenharmony_ci
34528c2ecf20Sopenharmony_ci	spin_lock_init(&gsm_mux_lock);
34538c2ecf20Sopenharmony_ci
34548c2ecf20Sopenharmony_ci	if (tty_register_driver(gsm_tty_driver)) {
34558c2ecf20Sopenharmony_ci		put_tty_driver(gsm_tty_driver);
34568c2ecf20Sopenharmony_ci		tty_unregister_ldisc(N_GSM0710);
34578c2ecf20Sopenharmony_ci		pr_err("gsm_init: tty registration failed.\n");
34588c2ecf20Sopenharmony_ci		return -EBUSY;
34598c2ecf20Sopenharmony_ci	}
34608c2ecf20Sopenharmony_ci	pr_debug("gsm_init: loaded as %d,%d.\n",
34618c2ecf20Sopenharmony_ci			gsm_tty_driver->major, gsm_tty_driver->minor_start);
34628c2ecf20Sopenharmony_ci	return 0;
34638c2ecf20Sopenharmony_ci}
34648c2ecf20Sopenharmony_ci
34658c2ecf20Sopenharmony_cistatic void __exit gsm_exit(void)
34668c2ecf20Sopenharmony_ci{
34678c2ecf20Sopenharmony_ci	int status = tty_unregister_ldisc(N_GSM0710);
34688c2ecf20Sopenharmony_ci	if (status != 0)
34698c2ecf20Sopenharmony_ci		pr_err("n_gsm: can't unregister line discipline (err = %d)\n",
34708c2ecf20Sopenharmony_ci								status);
34718c2ecf20Sopenharmony_ci	tty_unregister_driver(gsm_tty_driver);
34728c2ecf20Sopenharmony_ci	put_tty_driver(gsm_tty_driver);
34738c2ecf20Sopenharmony_ci}
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_cimodule_init(gsm_init);
34768c2ecf20Sopenharmony_cimodule_exit(gsm_exit);
34778c2ecf20Sopenharmony_ci
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
34808c2ecf20Sopenharmony_ciMODULE_ALIAS_LDISC(N_GSM0710);
3481