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