-
Notifications
You must be signed in to change notification settings - Fork 55.2k
/
Copy pathn_gsm.c
4656 lines (4148 loc) · 113 KB
/
n_gsm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: GPL-2.0
/*
* n_gsm.c GSM 0710 tty multiplexor
* Copyright (c) 2009/10 Intel Corporation
* Copyright (c) 2022/23 Siemens Mobility GmbH
*
* * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE *
*
* Outgoing path:
* tty -> DLCI fifo -> scheduler -> GSM MUX data queue ---o-> ldisc
* control message -> GSM MUX control queue --´
*
* Incoming path:
* ldisc -> gsm_queue() -o--> tty
* `-> gsm_control_response()
*
* TO DO:
* Mostly done: ioctls for setting modes/timing
* Partly done: hooks so you can pull off frames to non tty devs
* Restart DLCI 0 when it closes ?
* Improve the tx engine
* Resolve tx side locking by adding a queue_head and routing
* all control traffic via it
* General tidy/document
* Review the locking/move to refcounts more (mux now moved to an
* alloc/free model ready)
* Use newest tty open/close port helpers and install hooks
* What to do about power functions ?
* Termios setting and negotiation
* Do we need a 'which mux are you' ioctl to correlate mux and tty sets
*
*/
#include <linux/types.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/sched/signal.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/bitfield.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/math.h>
#include <linux/nospec.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/bitops.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/tty_flip.h>
#include <linux/tty_driver.h>
#include <linux/serial.h>
#include <linux/kfifo.h>
#include <linux/skbuff.h>
#include <net/arp.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/gsmmux.h>
#include "tty.h"
static int debug;
module_param(debug, int, 0600);
/* Module debug bits */
#define DBG_DUMP BIT(0) /* Data transmission dump. */
#define DBG_CD_ON BIT(1) /* Always assume CD line on. */
#define DBG_DATA BIT(2) /* Data transmission details. */
#define DBG_ERRORS BIT(3) /* Details for fail conditions. */
#define DBG_TTY BIT(4) /* Transmission statistics for DLCI TTYs. */
#define DBG_PAYLOAD BIT(5) /* Limits DBG_DUMP to payload frames. */
/* Defaults: these are from the specification */
#define T1 10 /* 100mS */
#define T2 34 /* 333mS */
#define T3 10 /* 10s */
#define N2 3 /* Retry 3 times */
#define K 2 /* outstanding I frames */
#define MAX_T3 255 /* In seconds. */
#define MAX_WINDOW_SIZE 7 /* Limit of K in error recovery mode. */
/* Use long timers for testing at low speed with debug on */
#ifdef DEBUG_TIMING
#define T1 100
#define T2 200
#endif
/*
* Semi-arbitrary buffer size limits. 0710 is normally run with 32-64 byte
* limits so this is plenty
*/
#define MAX_MRU 1500
#define MAX_MTU 1500
#define MIN_MTU (PROT_OVERHEAD + 1)
/* SOF, ADDR, CTRL, LEN1, LEN2, ..., FCS, EOF */
#define PROT_OVERHEAD 7
#define GSM_NET_TX_TIMEOUT (HZ*10)
/*
* struct gsm_mux_net - network interface
*
* Created when net interface is initialized.
*/
struct gsm_mux_net {
struct kref ref;
struct gsm_dlci *dlci;
};
/*
* Each block of data we have queued to go out is in the form of
* a gsm_msg which holds everything we need in a link layer independent
* format
*/
struct gsm_msg {
struct list_head list;
u8 addr; /* DLCI address + flags */
u8 ctrl; /* Control byte + flags */
unsigned int len; /* Length of data block (can be zero) */
u8 *data; /* Points into buffer but not at the start */
u8 buffer[];
};
enum gsm_dlci_state {
DLCI_CLOSED,
DLCI_WAITING_CONFIG, /* Waiting for DLCI configuration from user */
DLCI_CONFIGURE, /* Sending PN (for adaption > 1) */
DLCI_OPENING, /* Sending SABM not seen UA */
DLCI_OPEN, /* SABM/UA complete */
DLCI_CLOSING, /* Sending DISC not seen UA/DM */
};
enum gsm_dlci_mode {
DLCI_MODE_ABM, /* Normal Asynchronous Balanced Mode */
DLCI_MODE_ADM, /* Asynchronous Disconnected Mode */
};
/*
* Each active data link has a gsm_dlci structure associated which ties
* the link layer to an optional tty (if the tty side is open). To avoid
* complexity right now these are only ever freed up when the mux is
* shut down.
*
* At the moment we don't free DLCI objects until the mux is torn down
* this avoid object life time issues but might be worth review later.
*/
struct gsm_dlci {
struct gsm_mux *gsm;
int addr;
enum gsm_dlci_state state;
struct mutex mutex;
/* Link layer */
enum gsm_dlci_mode mode;
spinlock_t lock; /* Protects the internal state */
struct timer_list t1; /* Retransmit timer for SABM and UA */
int retries;
/* Uplink tty if active */
struct tty_port port; /* The tty bound to this DLCI if there is one */
#define TX_SIZE 4096 /* Must be power of 2. */
struct kfifo fifo; /* Queue fifo for the DLCI */
int adaption; /* Adaption layer in use */
int prev_adaption;
u32 modem_rx; /* Our incoming virtual modem lines */
u32 modem_tx; /* Our outgoing modem lines */
unsigned int mtu;
bool dead; /* Refuse re-open */
/* Configuration */
u8 prio; /* Priority */
u8 ftype; /* Frame type */
u8 k; /* Window size */
/* Flow control */
bool throttled; /* Private copy of throttle state */
bool constipated; /* Throttle status for outgoing */
/* Packetised I/O */
struct sk_buff *skb; /* Frame being sent */
struct sk_buff_head skb_list; /* Queued frames */
/* Data handling callback */
void (*data)(struct gsm_dlci *dlci, const u8 *data, int len);
void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
struct net_device *net; /* network interface, if created */
};
/*
* Parameter bits used for parameter negotiation according to 3GPP 27.010
* chapter 5.4.6.3.1.
*/
struct gsm_dlci_param_bits {
u8 d_bits;
u8 i_cl_bits;
u8 p_bits;
u8 t_bits;
__le16 n_bits;
u8 na_bits;
u8 k_bits;
};
static_assert(sizeof(struct gsm_dlci_param_bits) == 8);
#define PN_D_FIELD_DLCI GENMASK(5, 0)
#define PN_I_CL_FIELD_FTYPE GENMASK(3, 0)
#define PN_I_CL_FIELD_ADAPTION GENMASK(7, 4)
#define PN_P_FIELD_PRIO GENMASK(5, 0)
#define PN_T_FIELD_T1 GENMASK(7, 0)
#define PN_N_FIELD_N1 GENMASK(15, 0)
#define PN_NA_FIELD_N2 GENMASK(7, 0)
#define PN_K_FIELD_K GENMASK(2, 0)
/* Total number of supported devices */
#define GSM_TTY_MINORS 256
/* DLCI 0, 62/63 are special or reserved see gsmtty_open */
#define NUM_DLCI 64
/*
* DLCI 0 is used to pass control blocks out of band of the data
* flow (and with a higher link priority). One command can be outstanding
* at a time and we use this structure to manage them. They are created
* and destroyed by the user context, and updated by the receive paths
* and timers
*/
struct gsm_control {
u8 cmd; /* Command we are issuing */
u8 *data; /* Data for the command in case we retransmit */
int len; /* Length of block for retransmission */
int done; /* Done flag */
int error; /* Error if any */
};
enum gsm_encoding {
GSM_BASIC_OPT,
GSM_ADV_OPT,
};
enum gsm_mux_state {
GSM_SEARCH,
GSM0_ADDRESS,
GSM0_CONTROL,
GSM0_LEN0,
GSM0_LEN1,
GSM0_DATA,
GSM0_FCS,
GSM0_SSOF,
GSM1_START,
GSM1_ADDRESS,
GSM1_CONTROL,
GSM1_DATA,
GSM1_OVERRUN,
};
/*
* Each GSM mux we have is represented by this structure. If we are
* operating as an ldisc then we use this structure as our ldisc
* state. We need to sort out lifetimes and locking with respect
* to the gsm mux array. For now we don't free DLCI objects that
* have been instantiated until the mux itself is terminated.
*
* To consider further: tty open versus mux shutdown.
*/
struct gsm_mux {
struct tty_struct *tty; /* The tty our ldisc is bound to */
spinlock_t lock;
struct mutex mutex;
unsigned int num;
struct kref ref;
/* Events on the GSM channel */
wait_queue_head_t event;
/* ldisc send work */
struct work_struct tx_work;
/* Bits for GSM mode decoding */
/* Framing Layer */
u8 *buf;
enum gsm_mux_state state;
unsigned int len;
unsigned int address;
unsigned int count;
bool escape;
enum gsm_encoding encoding;
u8 control;
u8 fcs;
u8 *txframe; /* TX framing buffer */
/* Method for the receiver side */
void (*receive)(struct gsm_mux *gsm, u8 ch);
/* Link Layer */
unsigned int mru;
unsigned int mtu;
int initiator; /* Did we initiate connection */
bool dead; /* Has the mux been shut down */
struct gsm_dlci *dlci[NUM_DLCI];
int old_c_iflag; /* termios c_iflag value before attach */
bool constipated; /* Asked by remote to shut up */
bool has_devices; /* Devices were registered */
spinlock_t tx_lock;
unsigned int tx_bytes; /* TX data outstanding */
#define TX_THRESH_HI 8192
#define TX_THRESH_LO 2048
struct list_head tx_ctrl_list; /* Pending control packets */
struct list_head tx_data_list; /* Pending data packets */
/* Control messages */
struct timer_list kick_timer; /* Kick TX queuing on timeout */
struct timer_list t2_timer; /* Retransmit timer for commands */
int cretries; /* Command retry counter */
struct gsm_control *pending_cmd;/* Our current pending command */
spinlock_t control_lock; /* Protects the pending command */
/* Keep-alive */
struct timer_list ka_timer; /* Keep-alive response timer */
u8 ka_num; /* Keep-alive match pattern */
signed int ka_retries; /* Keep-alive retry counter, -1 if not yet initialized */
/* Configuration */
int adaption; /* 1 or 2 supported */
u8 ftype; /* UI or UIH */
int t1, t2; /* Timers in 1/100th of a sec */
unsigned int t3; /* Power wake-up timer in seconds. */
int n2; /* Retry count */
u8 k; /* Window size */
bool wait_config; /* Wait for configuration by ioctl before DLCI open */
u32 keep_alive; /* Control channel keep-alive in 10ms */
/* Statistics (not currently exposed) */
unsigned long bad_fcs;
unsigned long malformed;
unsigned long io_error;
unsigned long open_error;
unsigned long bad_size;
unsigned long unsupported;
};
/*
* Mux objects - needed so that we can translate a tty index into the
* relevant mux and DLCI.
*/
#define MAX_MUX 4 /* 256 minors */
static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */
static DEFINE_SPINLOCK(gsm_mux_lock);
static struct tty_driver *gsm_tty_driver;
/*
* This section of the driver logic implements the GSM encodings
* both the basic and the 'advanced'. Reliable transport is not
* supported.
*/
#define CR 0x02
#define EA 0x01
#define PF 0x10
/* I is special: the rest are ..*/
#define RR 0x01
#define UI 0x03
#define RNR 0x05
#define REJ 0x09
#define DM 0x0F
#define SABM 0x2F
#define DISC 0x43
#define UA 0x63
#define UIH 0xEF
/* Channel commands */
#define CMD_NSC 0x09
#define CMD_TEST 0x11
#define CMD_PSC 0x21
#define CMD_RLS 0x29
#define CMD_FCOFF 0x31
#define CMD_PN 0x41
#define CMD_RPN 0x49
#define CMD_FCON 0x51
#define CMD_CLD 0x61
#define CMD_SNC 0x69
#define CMD_MSC 0x71
/* Virtual modem bits */
#define MDM_FC 0x01
#define MDM_RTC 0x02
#define MDM_RTR 0x04
#define MDM_IC 0x20
#define MDM_DV 0x40
#define GSM0_SOF 0xF9
#define GSM1_SOF 0x7E
#define GSM1_ESCAPE 0x7D
#define GSM1_ESCAPE_BITS 0x20
#define XON 0x11
#define XOFF 0x13
#define ISO_IEC_646_MASK 0x7F
static const struct tty_port_operations gsm_port_ops;
/*
* CRC table for GSM 0710
*/
static const u8 gsm_fcs8[256] = {
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75,
0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69,
0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D,
0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51,
0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05,
0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19,
0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D,
0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21,
0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95,
0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89,
0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD,
0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1,
0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5,
0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9,
0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD,
0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1,
0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
};
#define INIT_FCS 0xFF
#define GOOD_FCS 0xCF
static void gsm_dlci_close(struct gsm_dlci *dlci);
static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
u8 ctrl);
static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg);
static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr);
static void gsmld_write_trigger(struct gsm_mux *gsm);
static void gsmld_write_task(struct work_struct *work);
/**
* gsm_fcs_add - update FCS
* @fcs: Current FCS
* @c: Next data
*
* Update the FCS to include c. Uses the algorithm in the specification
* notes.
*/
static inline u8 gsm_fcs_add(u8 fcs, u8 c)
{
return gsm_fcs8[fcs ^ c];
}
/**
* gsm_fcs_add_block - update FCS for a block
* @fcs: Current FCS
* @c: buffer of data
* @len: length of buffer
*
* Update the FCS to include c. Uses the algorithm in the specification
* notes.
*/
static inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len)
{
while (len--)
fcs = gsm_fcs8[fcs ^ *c++];
return fcs;
}
/**
* gsm_read_ea - read a byte into an EA
* @val: variable holding value
* @c: byte going into the EA
*
* Processes one byte of an EA. Updates the passed variable
* and returns 1 if the EA is now completely read
*/
static int gsm_read_ea(unsigned int *val, u8 c)
{
/* Add the next 7 bits into the value */
*val <<= 7;
*val |= c >> 1;
/* Was this the last byte of the EA 1 = yes*/
return c & EA;
}
/**
* gsm_read_ea_val - read a value until EA
* @val: variable holding value
* @data: buffer of data
* @dlen: length of data
*
* Processes an EA value. Updates the passed variable and
* returns the processed data length.
*/
static unsigned int gsm_read_ea_val(unsigned int *val, const u8 *data, int dlen)
{
unsigned int len = 0;
for (; dlen > 0; dlen--) {
len++;
if (gsm_read_ea(val, *data++))
break;
}
return len;
}
/**
* gsm_encode_modem - encode modem data bits
* @dlci: DLCI to encode from
*
* Returns the correct GSM encoded modem status bits (6 bit field) for
* the current status of the DLCI and attached tty object
*/
static u8 gsm_encode_modem(const struct gsm_dlci *dlci)
{
u8 modembits = 0;
/* FC is true flow control not modem bits */
if (dlci->throttled)
modembits |= MDM_FC;
if (dlci->modem_tx & TIOCM_DTR)
modembits |= MDM_RTC;
if (dlci->modem_tx & TIOCM_RTS)
modembits |= MDM_RTR;
if (dlci->modem_tx & TIOCM_RI)
modembits |= MDM_IC;
if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator)
modembits |= MDM_DV;
/* special mappings for passive side to operate as UE */
if (dlci->modem_tx & TIOCM_OUT1)
modembits |= MDM_IC;
if (dlci->modem_tx & TIOCM_OUT2)
modembits |= MDM_DV;
return modembits;
}
static void gsm_hex_dump_bytes(const char *fname, const u8 *data,
unsigned long len)
{
char *prefix;
if (!fname) {
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data, len,
true);
return;
}
prefix = kasprintf(GFP_ATOMIC, "%s: ", fname);
if (!prefix)
return;
print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 16, 1, data, len,
true);
kfree(prefix);
}
/**
* gsm_encode_params - encode DLCI parameters
* @dlci: DLCI to encode from
* @params: buffer to fill with the encoded parameters
*
* Encodes the parameters according to GSM 07.10 section 5.4.6.3.1
* table 3.
*/
static int gsm_encode_params(const struct gsm_dlci *dlci,
struct gsm_dlci_param_bits *params)
{
const struct gsm_mux *gsm = dlci->gsm;
unsigned int i, cl;
switch (dlci->ftype) {
case UIH:
i = 0; /* UIH */
break;
case UI:
i = 1; /* UI */
break;
default:
pr_debug("unsupported frame type %d\n", dlci->ftype);
return -EINVAL;
}
switch (dlci->adaption) {
case 1: /* Unstructured */
cl = 0; /* convergence layer type 1 */
break;
case 2: /* Unstructured with modem bits. */
cl = 1; /* convergence layer type 2 */
break;
default:
pr_debug("unsupported adaption %d\n", dlci->adaption);
return -EINVAL;
}
params->d_bits = FIELD_PREP(PN_D_FIELD_DLCI, dlci->addr);
/* UIH, convergence layer type 1 */
params->i_cl_bits = FIELD_PREP(PN_I_CL_FIELD_FTYPE, i) |
FIELD_PREP(PN_I_CL_FIELD_ADAPTION, cl);
params->p_bits = FIELD_PREP(PN_P_FIELD_PRIO, dlci->prio);
params->t_bits = FIELD_PREP(PN_T_FIELD_T1, gsm->t1);
params->n_bits = cpu_to_le16(FIELD_PREP(PN_N_FIELD_N1, dlci->mtu));
params->na_bits = FIELD_PREP(PN_NA_FIELD_N2, gsm->n2);
params->k_bits = FIELD_PREP(PN_K_FIELD_K, dlci->k);
return 0;
}
/**
* gsm_register_devices - register all tty devices for a given mux index
*
* @driver: the tty driver that describes the tty devices
* @index: the mux number is used to calculate the minor numbers of the
* ttys for this mux and may differ from the position in the
* mux array.
*/
static int gsm_register_devices(struct tty_driver *driver, unsigned int index)
{
struct device *dev;
int i;
unsigned int base;
if (!driver || index >= MAX_MUX)
return -EINVAL;
base = index * NUM_DLCI; /* first minor for this index */
for (i = 1; i < NUM_DLCI; i++) {
/* Don't register device 0 - this is the control channel
* and not a usable tty interface
*/
dev = tty_register_device(gsm_tty_driver, base + i, NULL);
if (IS_ERR(dev)) {
if (debug & DBG_ERRORS)
pr_info("%s failed to register device minor %u",
__func__, base + i);
for (i--; i >= 1; i--)
tty_unregister_device(gsm_tty_driver, base + i);
return PTR_ERR(dev);
}
}
return 0;
}
/**
* gsm_unregister_devices - unregister all tty devices for a given mux index
*
* @driver: the tty driver that describes the tty devices
* @index: the mux number is used to calculate the minor numbers of the
* ttys for this mux and may differ from the position in the
* mux array.
*/
static void gsm_unregister_devices(struct tty_driver *driver,
unsigned int index)
{
int i;
unsigned int base;
if (!driver || index >= MAX_MUX)
return;
base = index * NUM_DLCI; /* first minor for this index */
for (i = 1; i < NUM_DLCI; i++) {
/* Don't unregister device 0 - this is the control
* channel and not a usable tty interface
*/
tty_unregister_device(gsm_tty_driver, base + i);
}
}
/**
* gsm_print_packet - display a frame for debug
* @hdr: header to print before decode
* @addr: address EA from the frame
* @cr: C/R bit seen as initiator
* @control: control including PF bit
* @data: following data bytes
* @dlen: length of data
*
* Displays a packet in human readable format for debugging purposes. The
* style is based on amateur radio LAP-B dump display.
*/
static void gsm_print_packet(const char *hdr, int addr, int cr,
u8 control, const u8 *data, int dlen)
{
if (!(debug & DBG_DUMP))
return;
/* Only show user payload frames if debug & DBG_PAYLOAD */
if (!(debug & DBG_PAYLOAD) && addr != 0)
if ((control & ~PF) == UI || (control & ~PF) == UIH)
return;
pr_info("%s %d) %c: ", hdr, addr, "RC"[cr]);
switch (control & ~PF) {
case SABM:
pr_cont("SABM");
break;
case UA:
pr_cont("UA");
break;
case DISC:
pr_cont("DISC");
break;
case DM:
pr_cont("DM");
break;
case UI:
pr_cont("UI");
break;
case UIH:
pr_cont("UIH");
break;
default:
if (!(control & 0x01)) {
pr_cont("I N(S)%d N(R)%d",
(control & 0x0E) >> 1, (control & 0xE0) >> 5);
} else switch (control & 0x0F) {
case RR:
pr_cont("RR(%d)", (control & 0xE0) >> 5);
break;
case RNR:
pr_cont("RNR(%d)", (control & 0xE0) >> 5);
break;
case REJ:
pr_cont("REJ(%d)", (control & 0xE0) >> 5);
break;
default:
pr_cont("[%02X]", control);
}
}
if (control & PF)
pr_cont("(P)");
else
pr_cont("(F)");
gsm_hex_dump_bytes(NULL, data, dlen);
}
/*
* Link level transmission side
*/
/**
* gsm_stuff_frame - bytestuff a packet
* @input: input buffer
* @output: output buffer
* @len: length of input
*
* Expand a buffer by bytestuffing it. The worst case size change
* is doubling and the caller is responsible for handing out
* suitable sized buffers.
*/
static int gsm_stuff_frame(const u8 *input, u8 *output, int len)
{
int olen = 0;
while (len--) {
if (*input == GSM1_SOF || *input == GSM1_ESCAPE
|| (*input & ISO_IEC_646_MASK) == XON
|| (*input & ISO_IEC_646_MASK) == XOFF) {
*output++ = GSM1_ESCAPE;
*output++ = *input++ ^ GSM1_ESCAPE_BITS;
olen++;
} else
*output++ = *input++;
olen++;
}
return olen;
}
/**
* gsm_send - send a control frame
* @gsm: our GSM mux
* @addr: address for control frame
* @cr: command/response bit seen as initiator
* @control: control byte including PF bit
*
* Format up and transmit a control frame. These should be transmitted
* ahead of data when they are needed.
*/
static int gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
{
struct gsm_msg *msg;
u8 *dp;
int ocr;
unsigned long flags;
msg = gsm_data_alloc(gsm, addr, 0, control);
if (!msg)
return -ENOMEM;
/* toggle C/R coding if not initiator */
ocr = cr ^ (gsm->initiator ? 0 : 1);
msg->data -= 3;
dp = msg->data;
*dp++ = (addr << 2) | (ocr << 1) | EA;
*dp++ = control;
if (gsm->encoding == GSM_BASIC_OPT)
*dp++ = EA; /* Length of data = 0 */
*dp = 0xFF - gsm_fcs_add_block(INIT_FCS, msg->data, dp - msg->data);
msg->len = (dp - msg->data) + 1;
gsm_print_packet("Q->", addr, cr, control, NULL, 0);
spin_lock_irqsave(&gsm->tx_lock, flags);
list_add_tail(&msg->list, &gsm->tx_ctrl_list);
gsm->tx_bytes += msg->len;
spin_unlock_irqrestore(&gsm->tx_lock, flags);
gsmld_write_trigger(gsm);
return 0;
}
/**
* gsm_dlci_clear_queues - remove outstanding data for a DLCI
* @gsm: mux
* @dlci: clear for this DLCI
*
* Clears the data queues for a given DLCI.
*/
static void gsm_dlci_clear_queues(struct gsm_mux *gsm, struct gsm_dlci *dlci)
{
struct gsm_msg *msg, *nmsg;
int addr = dlci->addr;
unsigned long flags;
/* Clear DLCI write fifo first */
spin_lock_irqsave(&dlci->lock, flags);
kfifo_reset(&dlci->fifo);
spin_unlock_irqrestore(&dlci->lock, flags);
/* Clear data packets in MUX write queue */
spin_lock_irqsave(&gsm->tx_lock, flags);
list_for_each_entry_safe(msg, nmsg, &gsm->tx_data_list, list) {
if (msg->addr != addr)
continue;
gsm->tx_bytes -= msg->len;
list_del(&msg->list);
kfree(msg);
}
spin_unlock_irqrestore(&gsm->tx_lock, flags);
}
/**
* gsm_response - send a control response
* @gsm: our GSM mux
* @addr: address for control frame
* @control: control byte including PF bit
*
* Format up and transmit a link level response frame.
*/
static inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
{
gsm_send(gsm, addr, 0, control);
}
/**
* gsm_command - send a control command
* @gsm: our GSM mux
* @addr: address for control frame
* @control: control byte including PF bit
*
* Format up and transmit a link level command frame.
*/
static inline void gsm_command(struct gsm_mux *gsm, int addr, int control)
{
gsm_send(gsm, addr, 1, control);
}
/* Data transmission */
#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */
/**
* gsm_data_alloc - allocate data frame
* @gsm: GSM mux
* @addr: DLCI address
* @len: length excluding header and FCS
* @ctrl: control byte
*
* Allocate a new data buffer for sending frames with data. Space is left
* at the front for header bytes but that is treated as an implementation
* detail and not for the high level code to use
*/
static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
u8 ctrl)
{
struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN,
GFP_ATOMIC);
if (m == NULL)
return NULL;
m->data = m->buffer + HDR_LEN - 1; /* Allow for FCS */
m->len = len;
m->addr = addr;
m->ctrl = ctrl;
INIT_LIST_HEAD(&m->list);
return m;
}
/**
* gsm_send_packet - sends a single packet
* @gsm: GSM Mux
* @msg: packet to send
*
* The given packet is encoded and sent out. No memory is freed.
* The caller must hold the gsm tx lock.
*/
static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg)
{
int len, ret;
if (gsm->encoding == GSM_BASIC_OPT) {
gsm->txframe[0] = GSM0_SOF;
memcpy(gsm->txframe + 1, msg->data, msg->len);
gsm->txframe[msg->len + 1] = GSM0_SOF;
len = msg->len + 2;
} else {
gsm->txframe[0] = GSM1_SOF;
len = gsm_stuff_frame(msg->data, gsm->txframe + 1, msg->len);
gsm->txframe[len + 1] = GSM1_SOF;
len += 2;
}
if (debug & DBG_DATA)
gsm_hex_dump_bytes(__func__, gsm->txframe, len);
gsm_print_packet("-->", msg->addr, gsm->initiator, msg->ctrl, msg->data,
msg->len);
ret = gsmld_output(gsm, gsm->txframe, len);
if (ret <= 0)
return ret;
/* FIXME: Can eliminate one SOF in many more cases */
gsm->tx_bytes -= msg->len;
return 0;
}
/**
* gsm_is_flow_ctrl_msg - checks if flow control message
* @msg: message to check
*
* Returns true if the given message is a flow control command of the
* control channel. False is returned in any other case.
*/
static bool gsm_is_flow_ctrl_msg(struct gsm_msg *msg)
{
unsigned int cmd;
if (msg->addr > 0)
return false;
switch (msg->ctrl & ~PF) {
case UI:
case UIH:
cmd = 0;
if (gsm_read_ea_val(&cmd, msg->data + 2, msg->len - 2) < 1)
break;
switch (cmd & ~PF) {
case CMD_FCOFF:
case CMD_FCON:
return true;
}
break;