blob: 8aa71e2f58cbc9e7596825c9a93d874f4ad3cc99 [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;
Harald Welte7e859bc2011-02-04 20:36:50 +0100100 /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */
101 int vs; /* next to be transmitted */
102 int va; /* last acked by peer */
103 int vr; /* next expected to be received */
Harald Welte30fe6412011-02-04 20:34:08 +0100104 lapd_tei_state state;
Harald Welted38f1052011-02-05 19:13:00 +0100105};
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100106
Harald Welte7e859bc2011-02-04 20:36:50 +0100107/* 3.5.2.2 Send state variable V(S)
108 * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame
109 * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can
110 * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each
111 * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of
112 * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127.
113 *
114 * 3.5.2.3 Acknowledge state variable V(A)
115 * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame
116 * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been
117 * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can
118 * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values
119 * 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) ≤
120 * V(S).
121 *
122 * 3.5.2.5 Receive state variable V(R)
123 * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame
124 * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the
125 * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1.
126 * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame
127 * whose N(S) equals V(R).
128 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100129#define LAPD_NS(teip) (teip->vs)
130#define LAPD_NR(teip) (teip->vr)
131
Harald Welte7e859bc2011-02-04 20:36:50 +0100132/* 3.5.2.4 Send sequence number N(S)
133 * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in-
134 * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S).
135 *
136 * 3.5.2.6 Receive sequence number N(R)
137 * All I frames and supervisory frames contain N(R), the expected send sequence number of the next
138 * received I frame. At the time that a frame of the above types is designated for transmission, the value
139 * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has
140 * correctly received all I frames numbered up to and including N(R) − 1.
141 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100142
Harald Welted38f1052011-02-05 19:13:00 +0100143static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
Harald Welte30fe6412011-02-04 20:34:08 +0100144{
Harald Welted38f1052011-02-05 19:13:00 +0100145 struct lapd_tei *lt;
146
147 llist_for_each_entry(lt, &li->tei_list, list) {
148 if (lt->tei == tei)
149 return lt;
150 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100151 return NULL;
152};
153
Harald Welted38f1052011-02-05 19:13:00 +0100154static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
Harald Welte30fe6412011-02-04 20:34:08 +0100155{
Harald Welte0abc11a2011-02-05 17:16:26 +0100156 DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei,
Harald Welte30fe6412011-02-04 20:34:08 +0100157 lapd_tei_states[teip->state], lapd_tei_states[newstate]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100158 teip->state = newstate;
159};
160
Harald Welted38f1052011-02-05 19:13:00 +0100161static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
Harald Welte30fe6412011-02-04 20:34:08 +0100162{
Harald Welte8fc66a02011-02-05 19:51:05 +0100163 uint8_t entity = data[0];
164 uint8_t ref = data[1];
165 uint8_t mt = data[3];
166 uint8_t action = data[4] >> 1;
167 uint8_t e = data[4] & 1;
Harald Welted38f1052011-02-05 19:13:00 +0100168 uint8_t resp[8];
169 struct lapd_tei *teip;
170
Harald Welte8fc66a02011-02-05 19:51:05 +0100171 DEBUGP(DMI, "TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100172
173 switch (mt) {
Harald Welted38f1052011-02-05 19:13:00 +0100174 case 0x01: /* IDENTITY REQUEST */
Harald Welte8fc66a02011-02-05 19:51:05 +0100175 DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", action);
Harald Welte716d2a42011-02-05 17:29:05 +0100176
Harald Welte8fc66a02011-02-05 19:51:05 +0100177 teip = teip_from_tei(li, action);
Harald Welted38f1052011-02-05 19:13:00 +0100178 if (!teip) {
Harald Welte8fc66a02011-02-05 19:51:05 +0100179 LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", action);
Harald Welted38f1052011-02-05 19:13:00 +0100180 teip = talloc_zero(li, struct lapd_tei);
Harald Welte8fc66a02011-02-05 19:51:05 +0100181 teip->tei = action;
Harald Welted38f1052011-02-05 19:13:00 +0100182 llist_add(&teip->list, &li->tei_list);
183 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
Harald Welte30fe6412011-02-04 20:34:08 +0100184 }
Harald Welted38f1052011-02-05 19:13:00 +0100185
186 /* Send ACCEPT */
187 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
Harald Welte8fc66a02011-02-05 19:51:05 +0100188 resp[7] = (action << 1) | 1;
Harald Welted38f1052011-02-05 19:13:00 +0100189 li->transmit_cb(resp, 8, li->cbdata);
190
191 if (teip->state == LAPD_TEI_NONE)
192 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
193 break;
Harald Welte30fe6412011-02-04 20:34:08 +0100194 default:
Harald Welte8fc66a02011-02-05 19:51:05 +0100195 LOGP(DMI, LOGL_NOTICE, "TEIMGR: unknown mt %x action %x\n",
Harald Welte0ae57552011-02-05 18:33:12 +0100196 mt, action);
197 break;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100198 };
199};
200
Harald Welted38f1052011-02-05 19:13:00 +0100201uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len,
202 int *ilen, lapd_mph_type *prim)
Harald Welte30fe6412011-02-04 20:34:08 +0100203{
Harald Welte716d2a42011-02-05 17:29:05 +0100204 uint8_t sapi, cr, tei, command;
205 int pf, ns, nr;
206 uint8_t *contents;
Harald Welted38f1052011-02-05 19:13:00 +0100207 struct lapd_tei *teip;
Harald Welte716d2a42011-02-05 17:29:05 +0100208
209 uint8_t resp[8];
210 int l = 0;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100211
212 *ilen = 0;
213 *prim = 0;
214
215 if (len < 2) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100216 DEBUGP(DMI, "len %d < 2\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100217 return NULL;
218 };
219
220 if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100221 DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
Harald Welte30fe6412011-02-04 20:34:08 +0100222 data[1]);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100223 return NULL;
224 };
225
Harald Welte716d2a42011-02-05 17:29:05 +0100226 sapi = data[0] >> 2;
227 cr = (data[0] >> 1) & 1;
228 tei = data[1] >> 1;
Harald Welted38f1052011-02-05 19:13:00 +0100229 command = li->network_side ^ cr;
Harald Welte0abc11a2011-02-05 17:16:26 +0100230 //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100231
232 if (len < 3) {
Harald Welte0abc11a2011-02-05 17:16:26 +0100233 DEBUGP(DMI, "len %d < 3\n", len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100234 return NULL;
235 };
236
237 lapd_msg_type typ = 0;
238 lapd_cmd_type cmd = 0;
Harald Welte716d2a42011-02-05 17:29:05 +0100239 pf = -1;
240 ns = -1;
241 nr = -1;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100242 if ((data[2] & 1) == 0) {
243 typ = LAPD_TYPE_I;
244 assert(len >= 4);
245 ns = data[2] >> 1;
246 nr = data[3] >> 1;
247 pf = data[3] & 1;
248 cmd = LAPD_CMD_I;
249 } else if ((data[2] & 3) == 1) {
250 typ = LAPD_TYPE_S;
251 assert(len >= 4);
252 nr = data[3] >> 1;
253 pf = data[3] & 1;
254 switch (data[2]) {
Harald Welte30fe6412011-02-04 20:34:08 +0100255 case 0x1:
256 cmd = LAPD_CMD_RR;
257 break;
258 case 0x5:
259 cmd = LAPD_CMD_RNR;
260 break;
261 case 0x9:
262 cmd = LAPD_CMD_REJ;
263 break;
264 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100265 LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]);
266 return NULL;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100267 };
268 } else if ((data[2] & 3) == 3) {
269 typ = LAPD_TYPE_U;
270 pf = (data[2] >> 4) & 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100271 int val = data[2] & ~(1 << 4);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100272 switch (val) {
Harald Welte30fe6412011-02-04 20:34:08 +0100273 case 0x6f:
274 cmd = LAPD_CMD_SABME;
275 break;
276 case 0x0f:
277 cmd = LAPD_CMD_DM;
278 break;
279 case 0x03:
280 cmd = LAPD_CMD_UI;
281 break;
282 case 0x43:
283 cmd = LAPD_CMD_DISC;
284 break;
285 case 0x63:
286 cmd = LAPD_CMD_UA;
287 break;
288 case 0x87:
289 cmd = LAPD_CMD_FRMR;
290 break;
291 case 0xaf:
292 cmd = LAPD_CMD_XID;
293 break;
294
295 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100296 LOGP(DMI, LOGL_ERROR, "unknown U cmd %x "
297 "(pf %x data %x)\n", val, pf, data[2]);
298 return NULL;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100299 };
300 };
Harald Welte30fe6412011-02-04 20:34:08 +0100301
Harald Welte716d2a42011-02-05 17:29:05 +0100302 contents = &data[4];
Harald Welte30fe6412011-02-04 20:34:08 +0100303 if (typ == LAPD_TYPE_U)
304 contents--;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100305 *ilen = len - (contents - data);
Harald Welte30fe6412011-02-04 20:34:08 +0100306
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100307 if (tei == 127)
Harald Welted38f1052011-02-05 19:13:00 +0100308 lapd_tei_receive(li, contents, *ilen);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100309
Harald Welted38f1052011-02-05 19:13:00 +0100310 teip = teip_from_tei(li, tei);
Harald Welte716d2a42011-02-05 17:29:05 +0100311
Harald Welte0abc11a2011-02-05 17:16:26 +0100312 DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
313 "ilen %d teip %p vs %d va %d vr %d len %d\n",
Harald Welte30fe6412011-02-04 20:34:08 +0100314 lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
315 ns, nr, *ilen, teip, teip ? teip->vs : -1, teip ? teip->va : -1,
316 teip ? teip->vr : -1, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100317
Harald Welte716d2a42011-02-05 17:29:05 +0100318 if (!teip) {
319 LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
320 return NULL;
321 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100322
Harald Welte716d2a42011-02-05 17:29:05 +0100323 switch (cmd) {
324 case LAPD_CMD_I:
325 if (ns != teip->vr) {
326 DEBUGP(DMI, "ns %d != vr %d\n", ns, teip->vr);
327 if (ns == ((teip->vr - 1) & 0x7f)) {
328 DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
329 cmd = 0; // ignore
330 } else {
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100331 assert(0);
Harald Welte716d2a42011-02-05 17:29:05 +0100332 };
333 } else {
334 //printf("IN SEQUENCE\n");
335 teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100336 };
Harald Welte716d2a42011-02-05 17:29:05 +0100337
338 break;
339 case LAPD_CMD_UI:
340 break;
341 case LAPD_CMD_SABME:
342 teip->vs = 0;
343 teip->vr = 0;
344 teip->va = 0;
345
346 // ua
347 resp[l++] = data[0];
348 resp[l++] = (tei << 1) | 1;
349 resp[l++] = 0x73;
Harald Welted38f1052011-02-05 19:13:00 +0100350 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100351 if (teip->state != LAPD_TEI_ACTIVE) {
352 if (teip->state == LAPD_TEI_ASSIGNED) {
353 lapd_tei_set_state(teip,
354 LAPD_TEI_ACTIVE);
355 //printf("ASSIGNED and ACTIVE\n");
356 } else {
357#if 0
358 DEBUGP(DMI, "rr in strange state, send rej\n");
359
360 // rej
Harald Welted38f1052011-02-05 19:13:00 +0100361 resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2);
Harald Welte716d2a42011-02-05 17:29:05 +0100362 resp[l++] = (tei << 1) | 1;
363 resp[l++] = 0x09; //rej
364 resp[l++] = ((teip->vr + 1) << 1) | 0;
Harald Welted38f1052011-02-05 19:13:00 +0100365 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100366 pf = 0; // dont reply
367#endif
368 };
369 };
370
371 *prim = LAPD_MPH_ACTIVATE_IND;
372 break;
373 case LAPD_CMD_RR:
374 teip->va = (nr & 0x7f);
375#if 0
376 if (teip->state != LAPD_TEI_ACTIVE) {
377 if (teip->state == LAPD_TEI_ASSIGNED) {
378 lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
379 *prim = LAPD_MPH_ACTIVATE_IND;
380 //printf("ASSIGNED and ACTIVE\n");
381 } else {
382#if 0
383 DEBUGP(DMI, "rr in strange " "state, send rej\n");
384
385 // rej
Harald Welted38f1052011-02-05 19:13:00 +0100386 resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2);
Harald Welte716d2a42011-02-05 17:29:05 +0100387 resp[l++] = (tei << 1) | 1;
388 resp[l++] = 0x09; //rej
389 resp[l++] =
390 ((teip->vr + 1) << 1) | 0;
Harald Welted38f1052011-02-05 19:13:00 +0100391 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100392 pf = 0; // dont reply
393#endif
394 };
395 };
396#endif
397 if (pf) {
398 // interrogating us, send rr
399 resp[l++] = data[0];
400 resp[l++] = (tei << 1) | 1;
401 resp[l++] = 0x01; // rr
402 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
403
Harald Welted38f1052011-02-05 19:13:00 +0100404 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100405
406 };
407 break;
408 case LAPD_CMD_FRMR:
409 // frame reject
410#if 0
411 if (teip->state == LAPD_TEI_ACTIVE)
412 *prim = LAPD_MPH_DEACTIVATE_IND;
413 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
414#endif
Harald Welte0ae57552011-02-05 18:33:12 +0100415 LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n");
Harald Welte716d2a42011-02-05 17:29:05 +0100416 break;
417 case LAPD_CMD_DISC:
418 // disconnect
419 resp[l++] = data[0];
420 resp[l++] = (tei << 1) | 1;
421 resp[l++] = 0x73;
Harald Welted38f1052011-02-05 19:13:00 +0100422 li->transmit_cb(resp, l, li->cbdata);
Harald Welte716d2a42011-02-05 17:29:05 +0100423 lapd_tei_set_state(teip, LAPD_TEI_NONE);
424 break;
425 default:
Harald Welte0ae57552011-02-05 18:33:12 +0100426 LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n",
427 tei, cmd);
428 break;
Harald Welte716d2a42011-02-05 17:29:05 +0100429 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100430
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100431 if (typ == LAPD_TYPE_I) {
Harald Welte0ae57552011-02-05 18:33:12 +0100432 /* send rr
433 * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
434 * 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
435 */
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100436
Harald Welte0ae57552011-02-05 18:33:12 +0100437 /* interrogating us, send rr */
Harald Welte0abc11a2011-02-05 17:16:26 +0100438 DEBUGP(DMI, "Sending RR response\n");
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100439 resp[l++] = data[0];
440 resp[l++] = (tei << 1) | 1;
Harald Welte30fe6412011-02-04 20:34:08 +0100441 resp[l++] = 0x01; // rr
442 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100443
Harald Welted38f1052011-02-05 19:13:00 +0100444 li->transmit_cb(resp, l, li->cbdata);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100445
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500446 if (cmd != 0) {
447 *prim = LAPD_DL_DATA_IND;
448 return contents;
449 }
450 } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) {
451 *prim = LAPD_DL_UNITDATA_IND;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100452 return contents;
Matthew Fredrickson69245a02010-03-15 12:24:39 -0500453 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100454
455 return NULL;
456};
457
Harald Welte4ee2eaf2011-02-05 20:20:50 +0100458void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
Harald Welted38f1052011-02-05 19:13:00 +0100459 uint8_t *data, unsigned int len)
Harald Welte30fe6412011-02-04 20:34:08 +0100460{
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100461 //printf("lapd_transmit %d, %d\n", tei, len);
462 //hexdump(data, len);
Harald Welted38f1052011-02-05 19:13:00 +0100463 struct lapd_tei *teip = teip_from_tei(li, tei);
464
465 if (!teip) {
466 LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing "
467 "TEI %u\n", tei);
468 return;
469 }
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100470
Harald Welte0ae57552011-02-05 18:33:12 +0100471 /* prepend stuff */
Harald Welte30fe6412011-02-04 20:34:08 +0100472 uint8_t buf[10000];
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100473 memset(buf, 0, sizeof(buf));
Harald Welte30fe6412011-02-04 20:34:08 +0100474 memmove(buf + 4, data, len);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100475 len += 4;
476
Harald Welte4ee2eaf2011-02-05 20:20:50 +0100477 buf[0] = (sapi << 2) | (li->network_side ? 2 : 0);
478 buf[1] = (tei << 1) | 1;
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100479 buf[2] = (LAPD_NS(teip) << 1);
480 buf[3] = (LAPD_NR(teip) << 1) | 0;
481
482 teip->vs = (teip->vs + 1) & 0x7f;
483
Harald Welted38f1052011-02-05 19:13:00 +0100484 li->transmit_cb(buf, len, li->cbdata);
Matthew Fredricksonbc6649e2010-02-16 22:01:36 +0100485};
Harald Welted38f1052011-02-05 19:13:00 +0100486
487struct lapd_instance *lapd_instance_alloc(void (*tx_cb)(uint8_t *data, int len,
488 void *cbdata), void *cbdata)
489{
490 struct lapd_instance *li;
491
492 li = talloc_zero(NULL, struct lapd_instance);
493 if (!li)
494 return NULL;
495
496 li->transmit_cb = tx_cb;
497 li->cbdata = cbdata;
498 li->network_side = 1;
499 INIT_LLIST_HEAD(&li->tei_list);
500
501 return li;
502}