blob: d488bfd404df22aee9f32669f29a8bb8209beb7b [file] [log] [blame]
Harald Welted38f1052011-02-05 19:13:00 +01001/* OpenBSC minimal LAPD implementation */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +01002
Harald Welted38f1052011-02-05 19:13:00 +01003/* (C) 2009 by oystein@homelien.no
4 * (C) 2011 by Harald Welte <laforge@gnumonks.org>
5 * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010024 */
25
26#include <stdio.h>
27#include <string.h>
28#include <assert.h>
29
30#include "lapd.h"
Harald Welted38f1052011-02-05 19:13:00 +010031
32#include <osmocore/linuxlist.h>
33#include <osmocore/talloc.h>
34#include <openbsc/debug.h>
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010035
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010036typedef enum {
Harald Welte30fe6412011-02-04 20:34:08 +010037 LAPD_TEI_NONE = 0,
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010038
39 LAPD_TEI_ASSIGNED,
40
41 LAPD_TEI_ACTIVE,
42} lapd_tei_state;
43
44const char *lapd_tei_states[] = {
45 "NONE",
46 "ASSIGNED",
47 "ACTIVE",
48};
49
50typedef enum {
Harald Welte30fe6412011-02-04 20:34:08 +010051 LAPD_TYPE_NONE = 0,
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010052
53 LAPD_TYPE_I,
54 LAPD_TYPE_S,
55 LAPD_TYPE_U,
56} lapd_msg_type;
57
58typedef enum {
Harald Welte7e859bc2011-02-04 20:36:50 +010059 /* commands/responses */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010060 LAPD_CMD_NONE = 0,
61
62 LAPD_CMD_I,
63 LAPD_CMD_RR,
64 LAPD_CMD_RNR,
65 LAPD_CMD_REJ,
66
67 LAPD_CMD_SABME,
68 LAPD_CMD_DM,
69 LAPD_CMD_UI,
70 LAPD_CMD_DISC,
71 LAPD_CMD_UA,
72 LAPD_CMD_FRMR,
73 LAPD_CMD_XID,
74} lapd_cmd_type;
75
76const char *lapd_cmd_types[] = {
77 "NONE",
78
79 "I",
80 "RR",
81 "RNR",
82 "REJ",
83
84 "SABME",
85 "DM",
86 "UI",
87 "DISC",
88 "UA",
89 "FRMR",
90 "XID",
91
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010092};
93
Harald Welte30fe6412011-02-04 20:34:08 +010094const char *lapd_msg_types = "?ISU";
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +010095
Harald Welted38f1052011-02-05 19:13:00 +010096struct lapd_tei {
97 struct llist_head list;
98
99 uint8_t tei;
100 uint8_t sapi;
Harald Welte7e859bc2011-02-04 20:36:50 +0100101 /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */
102 int vs; /* next to be transmitted */
103 int va; /* last acked by peer */
104 int vr; /* next expected to be received */
Harald Welte30fe6412011-02-04 20:34:08 +0100105 lapd_tei_state state;
Harald Welted38f1052011-02-05 19:13:00 +0100106};
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100107
Harald Welte7e859bc2011-02-04 20:36:50 +0100108/* 3.5.2.2 Send state variable V(S)
109 * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame
110 * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can
111 * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each
112 * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of
113 * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127.
114 *
115 * 3.5.2.3 Acknowledge state variable V(A)
116 * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame
117 * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been
118 * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can
119 * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values
120 * 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) ≤
121 * V(S).
122 *
123 * 3.5.2.5 Receive state variable V(R)
124 * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame
125 * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the
126 * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1.
127 * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame
128 * whose N(S) equals V(R).
129 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100130#define LAPD_NS(teip) (teip->vs)
131#define LAPD_NR(teip) (teip->vr)
132
Harald Welte7e859bc2011-02-04 20:36:50 +0100133/* 3.5.2.4 Send sequence number N(S)
134 * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in-
135 * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S).
136 *
137 * 3.5.2.6 Receive sequence number N(R)
138 * All I frames and supervisory frames contain N(R), the expected send sequence number of the next
139 * received I frame. At the time that a frame of the above types is designated for transmission, the value
140 * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has
141 * correctly received all I frames numbered up to and including N(R) − 1.
142 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100143
Harald Welted38f1052011-02-05 19:13:00 +0100144static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
Harald Welte30fe6412011-02-04 20:34:08 +0100145{
Harald Welted38f1052011-02-05 19:13:00 +0100146 struct lapd_tei *lt;
147
148 llist_for_each_entry(lt, &li->tei_list, list) {
149 if (lt->tei == tei)
150 return lt;
151 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100152 return NULL;
153};
154
Harald Welted38f1052011-02-05 19:13:00 +0100155static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
Harald Welte30fe6412011-02-04 20:34:08 +0100156{
Harald Welte0abc11a2011-02-05 17:16:26 +0100157 DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei,
Harald Welte30fe6412011-02-04 20:34:08 +0100158 lapd_tei_states[teip->state], lapd_tei_states[newstate]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100159 teip->state = newstate;
160};
161
Harald Welted38f1052011-02-05 19:13:00 +0100162static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
Harald Welte30fe6412011-02-04 20:34:08 +0100163{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100164 int entity = data[0];
Harald Welte30fe6412011-02-04 20:34:08 +0100165 int ref = data[1];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100166 int mt = data[3];
Harald Welte30fe6412011-02-04 20:34:08 +0100167 int action = data[4] >> 1;
168 int e = data[4] & 1;
Harald Welted38f1052011-02-05 19:13:00 +0100169 int tei;
170 uint8_t resp[8];
171 struct lapd_tei *teip;
172
Harald Welte0abc11a2011-02-05 17:16:26 +0100173 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 +0100174
175 switch (mt) {
Harald Welted38f1052011-02-05 19:13:00 +0100176 case 0x01: /* IDENTITY REQUEST */
177 DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", tei);
Harald Welte716d2a42011-02-05 17:29:05 +0100178
Harald Welted38f1052011-02-05 19:13:00 +0100179 teip = teip_from_tei(li, tei);
180 if (!teip) {
181 LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", tei);
182 teip = talloc_zero(li, struct lapd_tei);
183 teip->tei = tei;
184 llist_add(&teip->list, &li->tei_list);
185 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
Harald Welte30fe6412011-02-04 20:34:08 +0100186 }
Harald Welted38f1052011-02-05 19:13:00 +0100187
188 /* Send ACCEPT */
189 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
190 resp[7] = (tei << 1) | 1;
191 li->transmit_cb(resp, 8, li->cbdata);
192
193 if (teip->state == LAPD_TEI_NONE)
194 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
195 break;
Harald Welte30fe6412011-02-04 20:34:08 +0100196 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100197 LOGP(DMI, LOGL_NOTICE, "tei mgmt: unknown mt %x action %x\n",
198 mt, action);
199 break;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100200 };
201};
202
Harald Welted38f1052011-02-05 19:13:00 +0100203uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len,
204 int *ilen, lapd_mph_type *prim)
Harald Welte30fe6412011-02-04 20:34:08 +0100205{
Harald Welte716d2a42011-02-05 17:29:05 +0100206 uint8_t sapi, cr, tei, command;
207 int pf, ns, nr;
208 uint8_t *contents;
Harald Welted38f1052011-02-05 19:13:00 +0100209 struct lapd_tei *teip;
Harald Welte716d2a42011-02-05 17:29:05 +0100210
211 uint8_t resp[8];
212 int l = 0;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100213
214 *ilen = 0;
215 *prim = 0;
216
217 if (len < 2) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100218 DEBUGP(DMI, "len %d < 2\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100219 return NULL;
220 };
221
222 if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100223 DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
Harald Welte30fe6412011-02-04 20:34:08 +0100224 data[1]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100225 return NULL;
226 };
227
Harald Welte716d2a42011-02-05 17:29:05 +0100228 sapi = data[0] >> 2;
229 cr = (data[0] >> 1) & 1;
230 tei = data[1] >> 1;
Harald Welted38f1052011-02-05 19:13:00 +0100231 command = li->network_side ^ cr;
Harald Welte0abc11a2011-02-05 17:16:26 +0100232 //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100233
234 if (len < 3) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100235 DEBUGP(DMI, "len %d < 3\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100236 return NULL;
237 };
238
239 lapd_msg_type typ = 0;
240 lapd_cmd_type cmd = 0;
Harald Welte716d2a42011-02-05 17:29:05 +0100241 pf = -1;
242 ns = -1;
243 nr = -1;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100244 if ((data[2] & 1) == 0) {
245 typ = LAPD_TYPE_I;
246 assert(len >= 4);
247 ns = data[2] >> 1;
248 nr = data[3] >> 1;
249 pf = data[3] & 1;
250 cmd = LAPD_CMD_I;
251 } else if ((data[2] & 3) == 1) {
252 typ = LAPD_TYPE_S;
253 assert(len >= 4);
254 nr = data[3] >> 1;
255 pf = data[3] & 1;
256 switch (data[2]) {
Harald Welte30fe6412011-02-04 20:34:08 +0100257 case 0x1:
258 cmd = LAPD_CMD_RR;
259 break;
260 case 0x5:
261 cmd = LAPD_CMD_RNR;
262 break;
263 case 0x9:
264 cmd = LAPD_CMD_REJ;
265 break;
266 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100267 LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]);
268 return NULL;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100269 };
270 } else if ((data[2] & 3) == 3) {
271 typ = LAPD_TYPE_U;
272 pf = (data[2] >> 4) & 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100273 int val = data[2] & ~(1 << 4);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100274 switch (val) {
Harald Welte30fe6412011-02-04 20:34:08 +0100275 case 0x6f:
276 cmd = LAPD_CMD_SABME;
277 break;
278 case 0x0f:
279 cmd = LAPD_CMD_DM;
280 break;
281 case 0x03:
282 cmd = LAPD_CMD_UI;
283 break;
284 case 0x43:
285 cmd = LAPD_CMD_DISC;
286 break;
287 case 0x63:
288 cmd = LAPD_CMD_UA;
289 break;
290 case 0x87:
291 cmd = LAPD_CMD_FRMR;
292 break;
293 case 0xaf:
294 cmd = LAPD_CMD_XID;
295 break;
296
297 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100298 LOGP(DMI, LOGL_ERROR, "unknown U cmd %x "
299 "(pf %x data %x)\n", val, pf, data[2]);
300 return NULL;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100301 };
302 };
Harald Welte30fe6412011-02-04 20:34:08 +0100303
Harald Welte716d2a42011-02-05 17:29:05 +0100304 contents = &data[4];
Harald Welte30fe6412011-02-04 20:34:08 +0100305 if (typ == LAPD_TYPE_U)
306 contents--;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100307 *ilen = len - (contents - data);
Harald Welte30fe6412011-02-04 20:34:08 +0100308
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100309 if (tei == 127)
Harald Welted38f1052011-02-05 19:13:00 +0100310 lapd_tei_receive(li, contents, *ilen);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100311
Harald Welted38f1052011-02-05 19:13:00 +0100312 teip = teip_from_tei(li, tei);
Harald Welte716d2a42011-02-05 17:29:05 +0100313
Harald Welte0abc11a2011-02-05 17:16:26 +0100314 DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
315 "ilen %d teip %p vs %d va %d vr %d len %d\n",
Harald Welte30fe6412011-02-04 20:34:08 +0100316 lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
317 ns, nr, *ilen, teip, teip ? teip->vs : -1, teip ? teip->va : -1,
318 teip ? teip->vr : -1, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100319
Harald Welte716d2a42011-02-05 17:29:05 +0100320 if (!teip) {
321 LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
322 return NULL;
323 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100324
Harald Welte716d2a42011-02-05 17:29:05 +0100325 switch (cmd) {
326 case LAPD_CMD_I:
327 if (ns != teip->vr) {
328 DEBUGP(DMI, "ns %d != vr %d\n", ns, teip->vr);
329 if (ns == ((teip->vr - 1) & 0x7f)) {
330 DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
331 cmd = 0; // ignore
332 } else {
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100333 assert(0);
Harald Welte716d2a42011-02-05 17:29:05 +0100334 };
335 } else {
336 //printf("IN SEQUENCE\n");
337 teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100338 };
Harald Welte716d2a42011-02-05 17:29:05 +0100339
340 break;
341 case LAPD_CMD_UI:
342 break;
343 case LAPD_CMD_SABME:
344 teip->vs = 0;
345 teip->vr = 0;
346 teip->va = 0;
347
348 // ua
349 resp[l++] = data[0];
350 resp[l++] = (tei << 1) | 1;
351 resp[l++] = 0x73;
Harald Welted38f1052011-02-05 19:13:00 +0100352 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100353 if (teip->state != LAPD_TEI_ACTIVE) {
354 if (teip->state == LAPD_TEI_ASSIGNED) {
355 lapd_tei_set_state(teip,
356 LAPD_TEI_ACTIVE);
357 //printf("ASSIGNED and ACTIVE\n");
358 } else {
359#if 0
360 DEBUGP(DMI, "rr in strange state, send rej\n");
361
362 // rej
Harald Welted38f1052011-02-05 19:13:00 +0100363 resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2);
Harald Welte716d2a42011-02-05 17:29:05 +0100364 resp[l++] = (tei << 1) | 1;
365 resp[l++] = 0x09; //rej
366 resp[l++] = ((teip->vr + 1) << 1) | 0;
Harald Welted38f1052011-02-05 19:13:00 +0100367 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100368 pf = 0; // dont reply
369#endif
370 };
371 };
372
373 *prim = LAPD_MPH_ACTIVATE_IND;
374 break;
375 case LAPD_CMD_RR:
376 teip->va = (nr & 0x7f);
377#if 0
378 if (teip->state != LAPD_TEI_ACTIVE) {
379 if (teip->state == LAPD_TEI_ASSIGNED) {
380 lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
381 *prim = LAPD_MPH_ACTIVATE_IND;
382 //printf("ASSIGNED and ACTIVE\n");
383 } else {
384#if 0
385 DEBUGP(DMI, "rr in strange " "state, send rej\n");
386
387 // rej
Harald Welted38f1052011-02-05 19:13:00 +0100388 resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2);
Harald Welte716d2a42011-02-05 17:29:05 +0100389 resp[l++] = (tei << 1) | 1;
390 resp[l++] = 0x09; //rej
391 resp[l++] =
392 ((teip->vr + 1) << 1) | 0;
Harald Welted38f1052011-02-05 19:13:00 +0100393 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100394 pf = 0; // dont reply
395#endif
396 };
397 };
398#endif
399 if (pf) {
400 // interrogating us, send rr
401 resp[l++] = data[0];
402 resp[l++] = (tei << 1) | 1;
403 resp[l++] = 0x01; // rr
404 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
405
Harald Welted38f1052011-02-05 19:13:00 +0100406 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100407
408 };
409 break;
410 case LAPD_CMD_FRMR:
411 // frame reject
412#if 0
413 if (teip->state == LAPD_TEI_ACTIVE)
414 *prim = LAPD_MPH_DEACTIVATE_IND;
415 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
416#endif
Harald Welte0ae57552011-02-05 18:33:12 +0100417 LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n");
Harald Welte716d2a42011-02-05 17:29:05 +0100418 break;
419 case LAPD_CMD_DISC:
420 // disconnect
421 resp[l++] = data[0];
422 resp[l++] = (tei << 1) | 1;
423 resp[l++] = 0x73;
Harald Welted38f1052011-02-05 19:13:00 +0100424 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100425 lapd_tei_set_state(teip, LAPD_TEI_NONE);
426 break;
427 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100428 LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n",
429 tei, cmd);
430 break;
Harald Welte716d2a42011-02-05 17:29:05 +0100431 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100432
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100433 if (typ == LAPD_TYPE_I) {
Harald Welte0ae57552011-02-05 18:33:12 +0100434 /* send rr
435 * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
436 * 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
437 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100438
Harald Welte0ae57552011-02-05 18:33:12 +0100439 /* interrogating us, send rr */
Harald Welte0abc11a2011-02-05 17:16:26 +0100440 DEBUGP(DMI, "Sending RR response\n");
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100441 resp[l++] = data[0];
442 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100443 resp[l++] = 0x01; // rr
444 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100445
Harald Welted38f1052011-02-05 19:13:00 +0100446 li->transmit_cb(resp, l, li->cbdata);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100447
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500448 if (cmd != 0) {
449 *prim = LAPD_DL_DATA_IND;
450 return contents;
451 }
452 } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) {
453 *prim = LAPD_DL_UNITDATA_IND;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100454 return contents;
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500455 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100456
457 return NULL;
458};
459
Harald Welted38f1052011-02-05 19:13:00 +0100460void lapd_transmit(struct lapd_instance *li, uint8_t tei,
461 uint8_t *data, unsigned int len)
Harald Welte30fe6412011-02-04 20:34:08 +0100462{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100463 //printf("lapd_transmit %d, %d\n", tei, len);
464 //hexdump(data, len);
Harald Welted38f1052011-02-05 19:13:00 +0100465 struct lapd_tei *teip = teip_from_tei(li, tei);
466
467 if (!teip) {
468 LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing "
469 "TEI %u\n", tei);
470 return;
471 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100472
Harald Welte0ae57552011-02-05 18:33:12 +0100473 /* prepend stuff */
Harald Welte30fe6412011-02-04 20:34:08 +0100474 uint8_t buf[10000];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100475 memset(buf, 0, sizeof(buf));
Harald Welte30fe6412011-02-04 20:34:08 +0100476 memmove(buf + 4, data, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100477 len += 4;
478
Harald Welted38f1052011-02-05 19:13:00 +0100479 buf[0] = (teip->sapi << 2) | (li->network_side ? 2 : 0);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100480 buf[1] = (teip->tei << 1) | 1;
481 buf[2] = (LAPD_NS(teip) << 1);
482 buf[3] = (LAPD_NR(teip) << 1) | 0;
483
484 teip->vs = (teip->vs + 1) & 0x7f;
485
Harald Welted38f1052011-02-05 19:13:00 +0100486 li->transmit_cb(buf, len, li->cbdata);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100487};
Harald Welted38f1052011-02-05 19:13:00 +0100488
489struct lapd_instance *lapd_instance_alloc(void (*tx_cb)(uint8_t *data, int len,
490 void *cbdata), void *cbdata)
491{
492 struct lapd_instance *li;
493
494 li = talloc_zero(NULL, struct lapd_instance);
495 if (!li)
496 return NULL;
497
498 li->transmit_cb = tx_cb;
499 li->cbdata = cbdata;
500 li->network_side = 1;
501 INIT_LLIST_HEAD(&li->tei_list);
502
503 return li;
504}