blob: f8a5a7f2dfc1729abf550f21267abf291c0cbbf4 [file] [log] [blame]
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +01001
2/*
3 * minimal standalone network-side lap-d implementation
4 * oystein@homelien.no, 2009
5 */
6
7#include <stdio.h>
8#include <string.h>
9#include <assert.h>
10
11#include "lapd.h"
12#include "openbsc/debug.h"
13
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010014typedef enum {
Harald Welte30fe6412011-02-04 20:34:08 +010015 LAPD_TEI_NONE = 0,
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010016
17 LAPD_TEI_ASSIGNED,
18
19 LAPD_TEI_ACTIVE,
20} lapd_tei_state;
21
22const char *lapd_tei_states[] = {
23 "NONE",
24 "ASSIGNED",
25 "ACTIVE",
26};
27
28typedef enum {
Harald Welte30fe6412011-02-04 20:34:08 +010029 LAPD_TYPE_NONE = 0,
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010030
31 LAPD_TYPE_I,
32 LAPD_TYPE_S,
33 LAPD_TYPE_U,
34} lapd_msg_type;
35
36typedef enum {
Harald Welte7e859bc2011-02-04 20:36:50 +010037 /* commands/responses */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010038 LAPD_CMD_NONE = 0,
39
40 LAPD_CMD_I,
41 LAPD_CMD_RR,
42 LAPD_CMD_RNR,
43 LAPD_CMD_REJ,
44
45 LAPD_CMD_SABME,
46 LAPD_CMD_DM,
47 LAPD_CMD_UI,
48 LAPD_CMD_DISC,
49 LAPD_CMD_UA,
50 LAPD_CMD_FRMR,
51 LAPD_CMD_XID,
52} lapd_cmd_type;
53
54const char *lapd_cmd_types[] = {
55 "NONE",
56
57 "I",
58 "RR",
59 "RNR",
60 "REJ",
61
62 "SABME",
63 "DM",
64 "UI",
65 "DISC",
66 "UA",
67 "FRMR",
68 "XID",
69
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010070};
71
Harald Welte30fe6412011-02-04 20:34:08 +010072const char *lapd_msg_types = "?ISU";
Harald Welte7e859bc2011-02-04 20:36:50 +010073const int network_side = 1; /* 0 for user side */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010074
75typedef struct {
Harald Welte30fe6412011-02-04 20:34:08 +010076 int tei;
77 int sapi;
Harald Welte7e859bc2011-02-04 20:36:50 +010078 /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */
79 int vs; /* next to be transmitted */
80 int va; /* last acked by peer */
81 int vr; /* next expected to be received */
Harald Welte30fe6412011-02-04 20:34:08 +010082 lapd_tei_state state;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010083} lapd_tei_t;
84
Harald Welte7e859bc2011-02-04 20:36:50 +010085/* 3.5.2.2 Send state variable V(S)
86 * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame
87 * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can
88 * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each
89 * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of
90 * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127.
91 *
92 * 3.5.2.3 Acknowledge state variable V(A)
93 * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame
94 * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been
95 * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can
96 * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values
97 * received from its peer (see 3.5.2.6). A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤
98 * V(S).
99 *
100 * 3.5.2.5 Receive state variable V(R)
101 * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame
102 * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the
103 * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1.
104 * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame
105 * whose N(S) equals V(R).
106 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100107#define LAPD_NS(teip) (teip->vs)
108#define LAPD_NR(teip) (teip->vr)
109
Harald Welte7e859bc2011-02-04 20:36:50 +0100110/* 3.5.2.4 Send sequence number N(S)
111 * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in-
112 * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S).
113 *
114 * 3.5.2.6 Receive sequence number N(R)
115 * All I frames and supervisory frames contain N(R), the expected send sequence number of the next
116 * received I frame. At the time that a frame of the above types is designated for transmission, the value
117 * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has
118 * correctly received all I frames numbered up to and including N(R) − 1.
119 */
Harald Welte30fe6412011-02-04 20:34:08 +0100120void (*lapd_transmit_cb) (uint8_t * data, int len, void *cbdata);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100121
122static lapd_tei_t tei_list[] = {
Harald Welte30fe6412011-02-04 20:34:08 +0100123 {25, 62,},
124 {1, 0,},
125 {-1},
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100126};
127
Harald Welted2735292011-02-04 20:38:46 +0100128static lapd_tei_t *teip_from_tei(int tei)
Harald Welte30fe6412011-02-04 20:34:08 +0100129{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100130 lapd_tei_t *p;
131 for (p = tei_list; p->tei != -1; p++) {
Harald Welte30fe6412011-02-04 20:34:08 +0100132 if (p->tei == tei)
133 return p;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100134 };
135 return NULL;
136};
137
Harald Welted2735292011-02-04 20:38:46 +0100138static void lapd_tei_set_state(lapd_tei_t * teip, int newstate)
Harald Welte30fe6412011-02-04 20:34:08 +0100139{
Harald Welte0abc11a2011-02-05 17:16:26 +0100140 DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei,
Harald Welte30fe6412011-02-04 20:34:08 +0100141 lapd_tei_states[teip->state], lapd_tei_states[newstate]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100142 teip->state = newstate;
143};
144
Harald Welted2735292011-02-04 20:38:46 +0100145static void lapd_tei_receive(uint8_t * data, int len, void *cbdata)
Harald Welte30fe6412011-02-04 20:34:08 +0100146{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100147 int entity = data[0];
Harald Welte30fe6412011-02-04 20:34:08 +0100148 int ref = data[1];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100149 int mt = data[3];
Harald Welte30fe6412011-02-04 20:34:08 +0100150 int action = data[4] >> 1;
151 int e = data[4] & 1;
Harald Welte0abc11a2011-02-05 17:16:26 +0100152 DEBUGP(DMI, "tei mgmt: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100153
154 switch (mt) {
Harald Welte30fe6412011-02-04 20:34:08 +0100155 case 0x01:{ // identity request
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100156 int tei = action;
Harald Welte0abc11a2011-02-05 17:16:26 +0100157 DEBUGP(DMI, "tei mgmt: identity request, accepting "
158 "tei %d\n", tei);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100159 uint8_t resp[8];
160 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
161 resp[7] = (tei << 1) | 1;
162 lapd_transmit_cb(resp, 8, cbdata);
Harald Welte30fe6412011-02-04 20:34:08 +0100163 lapd_tei_t *teip = teip_from_tei(tei);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100164 if (teip->state == LAPD_TEI_NONE)
165 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
Harald Welte30fe6412011-02-04 20:34:08 +0100166 break;
167 }
168 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100169 DEBUGP(DMI, "tei mgmt: unknown mt %x action %x\n", mt, action);
Harald Welte30fe6412011-02-04 20:34:08 +0100170 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100171 };
172};
173
Harald Welte30fe6412011-02-04 20:34:08 +0100174uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim,
175 void *cbdata)
176{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100177#if 0
Harald Welte0abc11a2011-02-05 17:16:26 +0100178 DEBUGP(DMI, "receive %p, %d\n", data, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100179 hexdump(data, len);
180#endif
181
182 *ilen = 0;
183 *prim = 0;
184
185 if (len < 2) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100186 DEBUGP(DMI, "len %d < 2\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100187 return NULL;
188 };
189
190 if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100191 DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
Harald Welte30fe6412011-02-04 20:34:08 +0100192 data[1]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100193 return NULL;
194 };
195
Harald Welte30fe6412011-02-04 20:34:08 +0100196 int sapi = data[0] >> 2;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100197 int cr = (data[0] >> 1) & 1;
198 int tei = data[1] >> 1;
199 int command = network_side ^ cr;
Harald Welte0abc11a2011-02-05 17:16:26 +0100200 //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100201
202 if (len < 3) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100203 DEBUGP(DMI, "len %d < 3\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100204 return NULL;
205 };
206
207 lapd_msg_type typ = 0;
208 lapd_cmd_type cmd = 0;
Harald Welte30fe6412011-02-04 20:34:08 +0100209 int pf = -1;
210 int ns = -1;
211 int nr = -1;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100212 if ((data[2] & 1) == 0) {
213 typ = LAPD_TYPE_I;
214 assert(len >= 4);
215 ns = data[2] >> 1;
216 nr = data[3] >> 1;
217 pf = data[3] & 1;
218 cmd = LAPD_CMD_I;
219 } else if ((data[2] & 3) == 1) {
220 typ = LAPD_TYPE_S;
221 assert(len >= 4);
222 nr = data[3] >> 1;
223 pf = data[3] & 1;
224 switch (data[2]) {
Harald Welte30fe6412011-02-04 20:34:08 +0100225 case 0x1:
226 cmd = LAPD_CMD_RR;
227 break;
228 case 0x5:
229 cmd = LAPD_CMD_RNR;
230 break;
231 case 0x9:
232 cmd = LAPD_CMD_REJ;
233 break;
234 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100235 DEBUGP(DMI, "unknown S cmd %x\n", data[2]);
Harald Welte30fe6412011-02-04 20:34:08 +0100236 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100237 };
238 } else if ((data[2] & 3) == 3) {
239 typ = LAPD_TYPE_U;
240 pf = (data[2] >> 4) & 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100241 int val = data[2] & ~(1 << 4);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100242 switch (val) {
Harald Welte30fe6412011-02-04 20:34:08 +0100243 case 0x6f:
244 cmd = LAPD_CMD_SABME;
245 break;
246 case 0x0f:
247 cmd = LAPD_CMD_DM;
248 break;
249 case 0x03:
250 cmd = LAPD_CMD_UI;
251 break;
252 case 0x43:
253 cmd = LAPD_CMD_DISC;
254 break;
255 case 0x63:
256 cmd = LAPD_CMD_UA;
257 break;
258 case 0x87:
259 cmd = LAPD_CMD_FRMR;
260 break;
261 case 0xaf:
262 cmd = LAPD_CMD_XID;
263 break;
264
265 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100266 DEBUGP(DMI, "unknown U cmd %x (pf %x data %x)\n", val,
Harald Welte30fe6412011-02-04 20:34:08 +0100267 pf, data[2]);
268 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100269 };
270 };
Harald Welte30fe6412011-02-04 20:34:08 +0100271
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100272 uint8_t *contents = &data[4];
Harald Welte30fe6412011-02-04 20:34:08 +0100273 if (typ == LAPD_TYPE_U)
274 contents--;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100275 *ilen = len - (contents - data);
Harald Welte30fe6412011-02-04 20:34:08 +0100276
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100277 lapd_tei_t *teip = teip_from_tei(tei);
278 if (tei == 127)
279 lapd_tei_receive(contents, *ilen, cbdata);
280
Harald Welte0abc11a2011-02-05 17:16:26 +0100281 DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
282 "ilen %d teip %p vs %d va %d vr %d len %d\n",
Harald Welte30fe6412011-02-04 20:34:08 +0100283 lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
284 ns, nr, *ilen, teip, teip ? teip->vs : -1, teip ? teip->va : -1,
285 teip ? teip->vr : -1, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100286
287 if (teip) {
288 switch (cmd) {
Harald Welte30fe6412011-02-04 20:34:08 +0100289 case LAPD_CMD_I:{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100290 if (ns != teip->vr) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100291 DEBUGP(DMI, "ns %d != vr %d\n", ns,
Harald Welte30fe6412011-02-04 20:34:08 +0100292 teip->vr);
293 if (ns == ((teip->vr - 1) & 0x7f)) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100294 DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
Harald Welte30fe6412011-02-04 20:34:08 +0100295 cmd = 0; // ignore
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100296 } else {
297 assert(0);
298 };
299 } else {
300 //printf("IN SEQUENCE\n");
Harald Welte30fe6412011-02-04 20:34:08 +0100301 teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100302 };
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100303
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500304 break;
Harald Welte30fe6412011-02-04 20:34:08 +0100305 }
306 case LAPD_CMD_UI:
307 break;
308 case LAPD_CMD_SABME:{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100309 teip->vs = 0;
310 teip->vr = 0;
311 teip->va = 0;
312
313 // ua
314 uint8_t resp[8];
315 int l = 0;
316 resp[l++] = data[0];
317 resp[l++] = (tei << 1) | 1;
318 resp[l++] = 0x73;
319 lapd_transmit_cb(resp, l, cbdata);
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500320 if (teip->state != LAPD_TEI_ACTIVE) {
321 if (teip->state == LAPD_TEI_ASSIGNED) {
Harald Welte30fe6412011-02-04 20:34:08 +0100322 lapd_tei_set_state(teip,
323 LAPD_TEI_ACTIVE);
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500324 //printf("ASSIGNED and ACTIVE\n");
325 } else {
326#if 0
Harald Welte0abc11a2011-02-05 17:16:26 +0100327 DEBUGP(DMI, "rr in strange state, send rej\n");
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500328
329 // rej
330 uint8_t resp[8];
331 int l = 0;
Harald Welte30fe6412011-02-04 20:34:08 +0100332 resp[l++] =
333 (teip->
334 sapi << 2) | (network_side
335 ? 0 : 2);
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500336 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100337 resp[l++] = 0x09; //rej
338 resp[l++] =
339 ((teip->vr + 1) << 1) | 0;
340 lapd_transmit_cb(resp, l,
341 cbdata);
342 pf = 0; // dont reply
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500343#endif
344 };
345 };
Harald Welte30fe6412011-02-04 20:34:08 +0100346
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500347 *prim = LAPD_MPH_ACTIVATE_IND;
Harald Welte30fe6412011-02-04 20:34:08 +0100348 break;
349 }
350 case LAPD_CMD_RR:{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100351 teip->va = (nr & 0x7f);
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500352#if 0
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100353 if (teip->state != LAPD_TEI_ACTIVE) {
354 if (teip->state == LAPD_TEI_ASSIGNED) {
Harald Welte30fe6412011-02-04 20:34:08 +0100355 lapd_tei_set_state(teip,
356 LAPD_TEI_ACTIVE);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100357 *prim = LAPD_MPH_ACTIVATE_IND;
358 //printf("ASSIGNED and ACTIVE\n");
359 } else {
360#if 0
Harald Welte0abc11a2011-02-05 17:16:26 +0100361 DEBUGP(DMI, "rr in strange "
362 "state, send rej\n");
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100363
364 // rej
365 uint8_t resp[8];
366 int l = 0;
Harald Welte30fe6412011-02-04 20:34:08 +0100367 resp[l++] =
368 (teip->
369 sapi << 2) | (network_side
370 ? 0 : 2);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100371 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100372 resp[l++] = 0x09; //rej
373 resp[l++] =
374 ((teip->vr + 1) << 1) | 0;
375 lapd_transmit_cb(resp, l,
376 cbdata);
377 pf = 0; // dont reply
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100378#endif
379 };
380 };
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500381#endif
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100382 if (pf) {
383 // interrogating us, send rr
384 uint8_t resp[8];
385 int l = 0;
386 resp[l++] = data[0];
387 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100388 resp[l++] = 0x01; // rr
389 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
390
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100391 lapd_transmit_cb(resp, l, cbdata);
Harald Welte30fe6412011-02-04 20:34:08 +0100392
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100393 };
394
Harald Welte30fe6412011-02-04 20:34:08 +0100395 break;
396 }
397 case LAPD_CMD_FRMR:{
398 // frame reject
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100399#if 0
400 if (teip->state == LAPD_TEI_ACTIVE)
401 *prim = LAPD_MPH_DEACTIVATE_IND;
402 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
403#endif
Harald Welte0abc11a2011-02-05 17:16:26 +0100404 DEBUGP(DMI, "frame reject, ignoring\n");
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100405 assert(0);
Harald Welte30fe6412011-02-04 20:34:08 +0100406 break;
407 }
408 case LAPD_CMD_DISC:{
409 // disconnect
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100410 uint8_t resp[8];
411 int l = 0;
412 resp[l++] = data[0];
413 resp[l++] = (tei << 1) | 1;
414 resp[l++] = 0x73;
415 lapd_transmit_cb(resp, l, cbdata);
416 lapd_tei_set_state(teip, LAPD_TEI_NONE);
Harald Welte30fe6412011-02-04 20:34:08 +0100417 break;
418 }
419 default:
Harald Welte0abc11a2011-02-05 17:16:26 +0100420 DEBUGP(DMI, "unknown cmd for tei %d (cmd %x)\n", tei,
Harald Welte30fe6412011-02-04 20:34:08 +0100421 cmd);
422 assert(0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100423 };
424 };
425
426 //if ((*prim == 0) && (ilen > 0) && (typ != LAPD_TYPE_S)) {
427 //if (cmd == LAPD_CMD_I) {
428 if (typ == LAPD_TYPE_I) {
429 // send rr
430 // Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
431 // lapd <- S RR sapi 3e tei 25 cmd 0 pf 0 ns -1 nr 5 ilen 0 teip 0x613800 vs 7 va 5 vr 2 len 4
432
433 // interrogating us, send rr
Harald Welte0abc11a2011-02-05 17:16:26 +0100434 DEBUGP(DMI, "Sending RR response\n");
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100435 uint8_t resp[8];
436 int l = 0;
437 resp[l++] = data[0];
438 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100439 resp[l++] = 0x01; // rr
440 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100441
442 lapd_transmit_cb(resp, l, cbdata);
443
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500444 if (cmd != 0) {
445 *prim = LAPD_DL_DATA_IND;
446 return contents;
447 }
448 } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) {
449 *prim = LAPD_DL_UNITDATA_IND;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100450 return contents;
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500451 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100452
453 return NULL;
454};
455
Harald Welte30fe6412011-02-04 20:34:08 +0100456void lapd_transmit(int tei, uint8_t * data, int len, void *cbdata)
457{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100458 //printf("lapd_transmit %d, %d\n", tei, len);
459 //hexdump(data, len);
460 lapd_tei_t *teip = teip_from_tei(tei);
461 //printf("teip %p\n", teip);
462
463 // prepend stuff
Harald Welte30fe6412011-02-04 20:34:08 +0100464 uint8_t buf[10000];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100465 memset(buf, 0, sizeof(buf));
Harald Welte30fe6412011-02-04 20:34:08 +0100466 memmove(buf + 4, data, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100467 len += 4;
468
469 buf[0] = (teip->sapi << 2) | (network_side ? 2 : 0);
470 buf[1] = (teip->tei << 1) | 1;
471 buf[2] = (LAPD_NS(teip) << 1);
472 buf[3] = (LAPD_NR(teip) << 1) | 0;
473
474 teip->vs = (teip->vs + 1) & 0x7f;
475
476 lapd_transmit_cb(buf, len, cbdata);
477};