blob: de0d5a5467ab106a6b1cb02089f7abe369c80d95 [file] [log] [blame]
Harald Welte306a2062011-02-05 19:13:00 +01001/* OpenBSC minimal LAPD implementation */
Matthew Fredrickson124c80e2010-02-16 22:01:36 +01002
Harald Welte306a2062011-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 Fredrickson124c80e2010-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 Welte306a2062011-02-05 19:13:00 +010031
32#include <osmocore/linuxlist.h>
33#include <osmocore/talloc.h>
34#include <openbsc/debug.h>
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010035
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010036typedef enum {
Harald Welte8c911f82011-02-04 20:34:08 +010037 LAPD_TEI_NONE = 0,
Matthew Fredrickson124c80e2010-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 Welte8c911f82011-02-04 20:34:08 +010051 LAPD_TYPE_NONE = 0,
Matthew Fredrickson124c80e2010-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 Welte98d01032011-02-04 20:36:50 +010059 /* commands/responses */
Matthew Fredrickson124c80e2010-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 Fredrickson124c80e2010-02-16 22:01:36 +010092};
93
Harald Welte8c911f82011-02-04 20:34:08 +010094const char *lapd_msg_types = "?ISU";
Matthew Fredrickson124c80e2010-02-16 22:01:36 +010095
Harald Welte306a2062011-02-05 19:13:00 +010096struct lapd_tei {
97 struct llist_head list;
98
99 uint8_t tei;
100 uint8_t sapi;
Harald Welte98d01032011-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 Welte8c911f82011-02-04 20:34:08 +0100105 lapd_tei_state state;
Harald Welte306a2062011-02-05 19:13:00 +0100106};
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100107
Harald Welte98d01032011-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 Fredrickson124c80e2010-02-16 22:01:36 +0100130#define LAPD_NS(teip) (teip->vs)
131#define LAPD_NR(teip) (teip->vr)
132
Harald Welte98d01032011-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 Fredrickson124c80e2010-02-16 22:01:36 +0100143
Harald Welte306a2062011-02-05 19:13:00 +0100144static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
Harald Welte8c911f82011-02-04 20:34:08 +0100145{
Harald Welte306a2062011-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 Fredrickson124c80e2010-02-16 22:01:36 +0100152 return NULL;
153};
154
Harald Welte306a2062011-02-05 19:13:00 +0100155static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
Harald Welte8c911f82011-02-04 20:34:08 +0100156{
Harald Welte1b2494d2011-02-05 17:16:26 +0100157 DEBUGP(DMI, "state change on tei %d: %s -> %s\n", teip->tei,
Harald Welte8c911f82011-02-04 20:34:08 +0100158 lapd_tei_states[teip->state], lapd_tei_states[newstate]);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100159 teip->state = newstate;
160};
161
Harald Welte306a2062011-02-05 19:13:00 +0100162static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
Harald Welte8c911f82011-02-04 20:34:08 +0100163{
Harald Welte32a74342011-02-05 19:51:05 +0100164 uint8_t entity = data[0];
165 uint8_t ref = data[1];
166 uint8_t mt = data[3];
167 uint8_t action = data[4] >> 1;
168 uint8_t e = data[4] & 1;
Harald Welte306a2062011-02-05 19:13:00 +0100169 uint8_t resp[8];
170 struct lapd_tei *teip;
171
Harald Welte32a74342011-02-05 19:51:05 +0100172 DEBUGP(DMI, "TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100173
174 switch (mt) {
Harald Welte306a2062011-02-05 19:13:00 +0100175 case 0x01: /* IDENTITY REQUEST */
Harald Welte32a74342011-02-05 19:51:05 +0100176 DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", action);
Harald Welte9be54032011-02-05 17:29:05 +0100177
Harald Welte32a74342011-02-05 19:51:05 +0100178 teip = teip_from_tei(li, action);
Harald Welte306a2062011-02-05 19:13:00 +0100179 if (!teip) {
Harald Welte32a74342011-02-05 19:51:05 +0100180 LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", action);
Harald Welte306a2062011-02-05 19:13:00 +0100181 teip = talloc_zero(li, struct lapd_tei);
Harald Welte32a74342011-02-05 19:51:05 +0100182 teip->tei = action;
Harald Welte306a2062011-02-05 19:13:00 +0100183 llist_add(&teip->list, &li->tei_list);
184 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
Harald Welte8c911f82011-02-04 20:34:08 +0100185 }
Harald Welte306a2062011-02-05 19:13:00 +0100186
187 /* Send ACCEPT */
188 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
Harald Welte32a74342011-02-05 19:51:05 +0100189 resp[7] = (action << 1) | 1;
Harald Welte306a2062011-02-05 19:13:00 +0100190 li->transmit_cb(resp, 8, li->cbdata);
191
192 if (teip->state == LAPD_TEI_NONE)
193 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
194 break;
Harald Welte8c911f82011-02-04 20:34:08 +0100195 default:
Harald Welte32a74342011-02-05 19:51:05 +0100196 LOGP(DMI, LOGL_NOTICE, "TEIMGR: unknown mt %x action %x\n",
Harald Weltea91e1c82011-02-05 18:33:12 +0100197 mt, action);
198 break;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100199 };
200};
201
Harald Welte306a2062011-02-05 19:13:00 +0100202uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len,
203 int *ilen, lapd_mph_type *prim)
Harald Welte8c911f82011-02-04 20:34:08 +0100204{
Harald Welte9be54032011-02-05 17:29:05 +0100205 uint8_t sapi, cr, tei, command;
206 int pf, ns, nr;
207 uint8_t *contents;
Harald Welte306a2062011-02-05 19:13:00 +0100208 struct lapd_tei *teip;
Harald Welte9be54032011-02-05 17:29:05 +0100209
210 uint8_t resp[8];
211 int l = 0;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100212
213 *ilen = 0;
214 *prim = 0;
215
216 if (len < 2) {
Harald Welte1b2494d2011-02-05 17:16:26 +0100217 DEBUGP(DMI, "len %d < 2\n", len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100218 return NULL;
219 };
220
221 if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
Harald Welte1b2494d2011-02-05 17:16:26 +0100222 DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
Harald Welte8c911f82011-02-04 20:34:08 +0100223 data[1]);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100224 return NULL;
225 };
226
Harald Welte9be54032011-02-05 17:29:05 +0100227 sapi = data[0] >> 2;
228 cr = (data[0] >> 1) & 1;
229 tei = data[1] >> 1;
Harald Welte306a2062011-02-05 19:13:00 +0100230 command = li->network_side ^ cr;
Harald Welte1b2494d2011-02-05 17:16:26 +0100231 //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100232
233 if (len < 3) {
Harald Welte1b2494d2011-02-05 17:16:26 +0100234 DEBUGP(DMI, "len %d < 3\n", len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100235 return NULL;
236 };
237
238 lapd_msg_type typ = 0;
239 lapd_cmd_type cmd = 0;
Harald Welte9be54032011-02-05 17:29:05 +0100240 pf = -1;
241 ns = -1;
242 nr = -1;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100243 if ((data[2] & 1) == 0) {
244 typ = LAPD_TYPE_I;
245 assert(len >= 4);
246 ns = data[2] >> 1;
247 nr = data[3] >> 1;
248 pf = data[3] & 1;
249 cmd = LAPD_CMD_I;
250 } else if ((data[2] & 3) == 1) {
251 typ = LAPD_TYPE_S;
252 assert(len >= 4);
253 nr = data[3] >> 1;
254 pf = data[3] & 1;
255 switch (data[2]) {
Harald Welte8c911f82011-02-04 20:34:08 +0100256 case 0x1:
257 cmd = LAPD_CMD_RR;
258 break;
259 case 0x5:
260 cmd = LAPD_CMD_RNR;
261 break;
262 case 0x9:
263 cmd = LAPD_CMD_REJ;
264 break;
265 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100266 LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]);
267 return NULL;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100268 };
269 } else if ((data[2] & 3) == 3) {
270 typ = LAPD_TYPE_U;
271 pf = (data[2] >> 4) & 1;
Harald Welte8c911f82011-02-04 20:34:08 +0100272 int val = data[2] & ~(1 << 4);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100273 switch (val) {
Harald Welte8c911f82011-02-04 20:34:08 +0100274 case 0x6f:
275 cmd = LAPD_CMD_SABME;
276 break;
277 case 0x0f:
278 cmd = LAPD_CMD_DM;
279 break;
280 case 0x03:
281 cmd = LAPD_CMD_UI;
282 break;
283 case 0x43:
284 cmd = LAPD_CMD_DISC;
285 break;
286 case 0x63:
287 cmd = LAPD_CMD_UA;
288 break;
289 case 0x87:
290 cmd = LAPD_CMD_FRMR;
291 break;
292 case 0xaf:
293 cmd = LAPD_CMD_XID;
294 break;
295
296 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100297 LOGP(DMI, LOGL_ERROR, "unknown U cmd %x "
298 "(pf %x data %x)\n", val, pf, data[2]);
299 return NULL;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100300 };
301 };
Harald Welte8c911f82011-02-04 20:34:08 +0100302
Harald Welte9be54032011-02-05 17:29:05 +0100303 contents = &data[4];
Harald Welte8c911f82011-02-04 20:34:08 +0100304 if (typ == LAPD_TYPE_U)
305 contents--;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100306 *ilen = len - (contents - data);
Harald Welte8c911f82011-02-04 20:34:08 +0100307
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100308 if (tei == 127)
Harald Welte306a2062011-02-05 19:13:00 +0100309 lapd_tei_receive(li, contents, *ilen);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100310
Harald Welte306a2062011-02-05 19:13:00 +0100311 teip = teip_from_tei(li, tei);
Harald Welte9be54032011-02-05 17:29:05 +0100312
Harald Welte1b2494d2011-02-05 17:16:26 +0100313 DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
314 "ilen %d teip %p vs %d va %d vr %d len %d\n",
Harald Welte8c911f82011-02-04 20:34:08 +0100315 lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
316 ns, nr, *ilen, teip, teip ? teip->vs : -1, teip ? teip->va : -1,
317 teip ? teip->vr : -1, len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100318
Harald Welte9be54032011-02-05 17:29:05 +0100319 if (!teip) {
320 LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
321 return NULL;
322 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100323
Harald Welte9be54032011-02-05 17:29:05 +0100324 switch (cmd) {
325 case LAPD_CMD_I:
326 if (ns != teip->vr) {
327 DEBUGP(DMI, "ns %d != vr %d\n", ns, teip->vr);
328 if (ns == ((teip->vr - 1) & 0x7f)) {
329 DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
330 cmd = 0; // ignore
331 } else {
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100332 assert(0);
Harald Welte9be54032011-02-05 17:29:05 +0100333 };
334 } else {
335 //printf("IN SEQUENCE\n");
336 teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100337 };
Harald Welte9be54032011-02-05 17:29:05 +0100338
339 break;
340 case LAPD_CMD_UI:
341 break;
342 case LAPD_CMD_SABME:
343 teip->vs = 0;
344 teip->vr = 0;
345 teip->va = 0;
346
347 // ua
348 resp[l++] = data[0];
349 resp[l++] = (tei << 1) | 1;
350 resp[l++] = 0x73;
Harald Welte306a2062011-02-05 19:13:00 +0100351 li->transmit_cb(resp, l, li->cbdata);
Harald Welte9be54032011-02-05 17:29:05 +0100352 if (teip->state != LAPD_TEI_ACTIVE) {
353 if (teip->state == LAPD_TEI_ASSIGNED) {
354 lapd_tei_set_state(teip,
355 LAPD_TEI_ACTIVE);
356 //printf("ASSIGNED and ACTIVE\n");
357 } else {
358#if 0
359 DEBUGP(DMI, "rr in strange state, send rej\n");
360
361 // rej
Harald Welte306a2062011-02-05 19:13:00 +0100362 resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2);
Harald Welte9be54032011-02-05 17:29:05 +0100363 resp[l++] = (tei << 1) | 1;
364 resp[l++] = 0x09; //rej
365 resp[l++] = ((teip->vr + 1) << 1) | 0;
Harald Welte306a2062011-02-05 19:13:00 +0100366 li->transmit_cb(resp, l, li->cbdata);
Harald Welte9be54032011-02-05 17:29:05 +0100367 pf = 0; // dont reply
368#endif
369 };
370 };
371
372 *prim = LAPD_MPH_ACTIVATE_IND;
373 break;
374 case LAPD_CMD_RR:
375 teip->va = (nr & 0x7f);
376#if 0
377 if (teip->state != LAPD_TEI_ACTIVE) {
378 if (teip->state == LAPD_TEI_ASSIGNED) {
379 lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
380 *prim = LAPD_MPH_ACTIVATE_IND;
381 //printf("ASSIGNED and ACTIVE\n");
382 } else {
383#if 0
384 DEBUGP(DMI, "rr in strange " "state, send rej\n");
385
386 // rej
Harald Welte306a2062011-02-05 19:13:00 +0100387 resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2);
Harald Welte9be54032011-02-05 17:29:05 +0100388 resp[l++] = (tei << 1) | 1;
389 resp[l++] = 0x09; //rej
390 resp[l++] =
391 ((teip->vr + 1) << 1) | 0;
Harald Welte306a2062011-02-05 19:13:00 +0100392 li->transmit_cb(resp, l, li->cbdata);
Harald Welte9be54032011-02-05 17:29:05 +0100393 pf = 0; // dont reply
394#endif
395 };
396 };
397#endif
398 if (pf) {
399 // interrogating us, send rr
400 resp[l++] = data[0];
401 resp[l++] = (tei << 1) | 1;
402 resp[l++] = 0x01; // rr
403 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
404
Harald Welte306a2062011-02-05 19:13:00 +0100405 li->transmit_cb(resp, l, li->cbdata);
Harald Welte9be54032011-02-05 17:29:05 +0100406
407 };
408 break;
409 case LAPD_CMD_FRMR:
410 // frame reject
411#if 0
412 if (teip->state == LAPD_TEI_ACTIVE)
413 *prim = LAPD_MPH_DEACTIVATE_IND;
414 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
415#endif
Harald Weltea91e1c82011-02-05 18:33:12 +0100416 LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n");
Harald Welte9be54032011-02-05 17:29:05 +0100417 break;
418 case LAPD_CMD_DISC:
419 // disconnect
420 resp[l++] = data[0];
421 resp[l++] = (tei << 1) | 1;
422 resp[l++] = 0x73;
Harald Welte306a2062011-02-05 19:13:00 +0100423 li->transmit_cb(resp, l, li->cbdata);
Harald Welte9be54032011-02-05 17:29:05 +0100424 lapd_tei_set_state(teip, LAPD_TEI_NONE);
425 break;
426 default:
Harald Weltea91e1c82011-02-05 18:33:12 +0100427 LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n",
428 tei, cmd);
429 break;
Harald Welte9be54032011-02-05 17:29:05 +0100430 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100431
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100432 if (typ == LAPD_TYPE_I) {
Harald Weltea91e1c82011-02-05 18:33:12 +0100433 /* send rr
434 * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
435 * 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
436 */
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100437
Harald Weltea91e1c82011-02-05 18:33:12 +0100438 /* interrogating us, send rr */
Harald Welte1b2494d2011-02-05 17:16:26 +0100439 DEBUGP(DMI, "Sending RR response\n");
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100440 resp[l++] = data[0];
441 resp[l++] = (tei << 1) | 1;
Harald Welte8c911f82011-02-04 20:34:08 +0100442 resp[l++] = 0x01; // rr
443 resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100444
Harald Welte306a2062011-02-05 19:13:00 +0100445 li->transmit_cb(resp, l, li->cbdata);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100446
Matthew Fredricksoneb3459e2010-03-15 12:24:39 -0500447 if (cmd != 0) {
448 *prim = LAPD_DL_DATA_IND;
449 return contents;
450 }
451 } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) {
452 *prim = LAPD_DL_UNITDATA_IND;
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100453 return contents;
Matthew Fredricksoneb3459e2010-03-15 12:24:39 -0500454 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100455
456 return NULL;
457};
458
Harald Welte306a2062011-02-05 19:13:00 +0100459void lapd_transmit(struct lapd_instance *li, uint8_t tei,
460 uint8_t *data, unsigned int len)
Harald Welte8c911f82011-02-04 20:34:08 +0100461{
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100462 //printf("lapd_transmit %d, %d\n", tei, len);
463 //hexdump(data, len);
Harald Welte306a2062011-02-05 19:13:00 +0100464 struct lapd_tei *teip = teip_from_tei(li, tei);
465
466 if (!teip) {
467 LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing "
468 "TEI %u\n", tei);
469 return;
470 }
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100471
Harald Weltea91e1c82011-02-05 18:33:12 +0100472 /* prepend stuff */
Harald Welte8c911f82011-02-04 20:34:08 +0100473 uint8_t buf[10000];
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100474 memset(buf, 0, sizeof(buf));
Harald Welte8c911f82011-02-04 20:34:08 +0100475 memmove(buf + 4, data, len);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100476 len += 4;
477
Harald Welte306a2062011-02-05 19:13:00 +0100478 buf[0] = (teip->sapi << 2) | (li->network_side ? 2 : 0);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100479 buf[1] = (teip->tei << 1) | 1;
480 buf[2] = (LAPD_NS(teip) << 1);
481 buf[3] = (LAPD_NR(teip) << 1) | 0;
482
483 teip->vs = (teip->vs + 1) & 0x7f;
484
Harald Welte306a2062011-02-05 19:13:00 +0100485 li->transmit_cb(buf, len, li->cbdata);
Matthew Fredrickson124c80e2010-02-16 22:01:36 +0100486};
Harald Welte306a2062011-02-05 19:13:00 +0100487
488struct lapd_instance *lapd_instance_alloc(void (*tx_cb)(uint8_t *data, int len,
489 void *cbdata), void *cbdata)
490{
491 struct lapd_instance *li;
492
493 li = talloc_zero(NULL, struct lapd_instance);
494 if (!li)
495 return NULL;
496
497 li->transmit_cb = tx_cb;
498 li->cbdata = cbdata;
499 li->network_side = 1;
500 INIT_LLIST_HEAD(&li->tei_list);
501
502 return li;
503}