blob: a7a86ce602676e7f33dcd61ae2c8e83d7c66f957 [file] [log] [blame]
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +02001/* OpenBSC minimal LAPD implementation */
2
3/* (C) 2009 by oystein@homelien.no
4 * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com>
6 * (C) 2011 by Harald Welte <laforge@gnumonks.org>
Andreas Eversberga7ff0012011-09-26 11:29:30 +02007 * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +02008 *
9 * All Rights Reserved
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 *
25 */
26
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020027#include "internal.h"
28
29#include <stdio.h>
30#include <string.h>
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020031#include <errno.h>
32
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020033#include <osmocom/core/linuxlist.h>
34#include <osmocom/core/logging.h>
Harald Welte71d87b22011-07-18 14:49:56 +020035#include <osmocom/core/talloc.h>
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020036#include <osmocom/core/msgb.h>
37#include <osmocom/core/timer.h>
Andreas Eversberga7ff0012011-09-26 11:29:30 +020038#include <osmocom/abis/lapd.h>
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020039
Andreas Eversberga7ff0012011-09-26 11:29:30 +020040#define LAPD_ADDR2(sapi, cr) ((((sapi) & 0x3f) << 2) | (((cr) & 0x1) << 1))
41#define LAPD_ADDR3(tei) ((((tei) & 0x7f) << 1) | 0x1)
42
43#define LAPD_ADDR_SAPI(addr) ((addr) >> 2)
44#define LAPD_ADDR_CR(addr) (((addr) >> 1) & 0x1)
45#define LAPD_ADDR_EA(addr) ((addr) & 0x1)
46#define LAPD_ADDR_TEI(addr) ((addr) >> 1)
47
48#define LAPD_CTRL_I4(ns) (((ns) & 0x7f) << 1)
49#define LAPD_CTRL_I5(nr, p) ((((nr) & 0x7f) << 1) | ((p) & 0x1))
50#define LAPD_CTRL_S4(s) ((((s) & 0x3) << 2) | 0x1)
51#define LAPD_CTRL_S5(nr, p) ((((nr) & 0x7f) << 1) | ((p) & 0x1))
52#define LAPD_CTRL_U4(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3)
53
54#define LAPD_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0)
55#define LAPD_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1)
56#define LAPD_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3)
57
58#define LAPD_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3)
59#define LAPD_CTRL_U_PF(ctrl) (((ctrl) >> 4) & 0x1)
60
61#define LAPD_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2)
62#define LAPD_CTRL_S_PF(ctrl) (ctrl & 0x1)
63
64#define LAPD_CTRL_I_Ns(ctrl) (((ctrl) & 0xFE) >> 1)
65#define LAPD_CTRL_I_P(ctrl) (ctrl & 0x1)
66#define LAPD_CTRL_Nr(ctrl) (((ctrl) & 0xFE) >> 1)
67
68#define LAPD_LEN(len) ((len << 2) | 0x1)
69#define LAPD_EL 0x1
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +020070
Andreas Eversberg3c460442011-09-28 02:46:16 +020071#define LAPD_SET_K(n, o) {n,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}
72
Andreas Eversberg3744b872011-09-27 12:12:36 +020073const struct lapd_profile lapd_profile_isdn = {
Andreas Eversberg3c460442011-09-28 02:46:16 +020074 LAPD_SET_K(7,7),
Andreas Eversberg3744b872011-09-27 12:12:36 +020075 3,
76 260,
77 3,
78 1,0,
79 1,0,
Andreas Eversbergd88c5bf2011-09-30 03:41:44 +020080 2,0,
Andreas Eversberg3744b872011-09-27 12:12:36 +020081 10,0,
82 0
83};
84
85const struct lapd_profile lapd_profile_abis = {
Andreas Eversberg3c460442011-09-28 02:46:16 +020086 LAPD_SET_K(2,1),
Andreas Eversberg3744b872011-09-27 12:12:36 +020087 3,
88 260,
Andreas Eversberg3c460442011-09-28 02:46:16 +020089 0, /* infinite */
Andreas Eversberg3744b872011-09-27 12:12:36 +020090 0,240000,
91 1,0,
Andreas Eversbergd88c5bf2011-09-30 03:41:44 +020092 2,0,
Andreas Eversberg3744b872011-09-27 12:12:36 +020093 10,0,
94 0
95};
96
97const struct lapd_profile lapd_profile_sat = {
Andreas Eversberg3c460442011-09-28 02:46:16 +020098 LAPD_SET_K(15,15),
Andreas Eversberg3744b872011-09-27 12:12:36 +020099 5,
100 260,
101 5,
Andreas Eversberg3c460442011-09-28 02:46:16 +0200102 2,400000,
103 2,400000,
Andreas Eversbergd88c5bf2011-09-30 03:41:44 +0200104 2,400000,
Andreas Eversberg3744b872011-09-27 12:12:36 +0200105 20,0,
106 1
107};
108
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200109typedef enum {
110 LAPD_TEI_NONE = 0,
111 LAPD_TEI_ASSIGNED,
112 LAPD_TEI_ACTIVE,
113} lapd_tei_state;
114
115const char *lapd_tei_states[] = {
116 "NONE",
117 "ASSIGNED",
118 "ACTIVE",
119};
120
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200121/* Structure representing an allocated TEI within a LAPD instance. */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200122struct lapd_tei {
123 struct llist_head list;
124 struct lapd_instance *li;
125 uint8_t tei;
126 lapd_tei_state state;
127
128 struct llist_head sap_list;
129};
130
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200131/* Structure representing a SAP within a TEI. It includes exactly one datalink
132 * instance. */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200133struct lapd_sap {
134 struct llist_head list;
135 struct lapd_tei *tei;
136 uint8_t sapi;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200137
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200138 struct lapd_datalink dl;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200139};
140
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200141/* Resolve TEI structure from given numeric TEI */
142static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
143{
144 struct lapd_tei *lt;
145
146 llist_for_each_entry(lt, &li->tei_list, list) {
147 if (lt->tei == tei)
148 return lt;
149 }
150 return NULL;
151};
152
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200153/* Change state of TEI */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200154static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
155{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200156 LOGP(DLLAPD, LOGL_INFO, "LAPD state change on TEI %d: %s -> %s\n",
157 teip->tei, lapd_tei_states[teip->state],
158 lapd_tei_states[newstate]);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200159 teip->state = newstate;
160};
161
162/* Allocate a new TEI */
163struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei)
164{
165 struct lapd_tei *teip;
166
167 teip = talloc_zero(li, struct lapd_tei);
168 if (!teip)
169 return NULL;
170
171 teip->li = li;
172 teip->tei = tei;
173 llist_add(&teip->list, &li->tei_list);
174 INIT_LLIST_HEAD(&teip->sap_list);
175
176 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
177
178 return teip;
179}
180
181/* Find a SAP within a given TEI */
182static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi)
183{
184 struct lapd_sap *sap;
185
186 llist_for_each_entry(sap, &teip->sap_list, list) {
187 if (sap->sapi == sapi)
188 return sap;
189 }
190
191 return NULL;
192}
193
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200194static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
195static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200196
197/* Allocate a new SAP within a given TEI */
198static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi)
199{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200200 struct lapd_sap *sap;
201 struct lapd_datalink *dl;
202 struct lapd_instance *li;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200203 struct lapd_profile *profile;
204 int k;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200205
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200206 sap = talloc_zero(teip, struct lapd_sap);
207 if (!sap)
208 return NULL;
209
Andreas Eversberg3c460442011-09-28 02:46:16 +0200210 LOGP(DLLAPD, LOGL_NOTICE, "LAPD Allocating SAP for SAPI=%u / TEI=%u\n",
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200211 sapi, teip->tei);
212
213 sap->sapi = sapi;
214 sap->tei = teip;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200215 dl = &sap->dl;
216 li = teip->li;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200217 profile = &li->profile;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200218
Andreas Eversberg3c460442011-09-28 02:46:16 +0200219 k = profile->k[sapi & 0x3f];
220 LOGP(DLLAPD, LOGL_NOTICE, "k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d"
221 "\n", k, profile->n200, profile->n201, profile->t200_sec,
222 profile->t200_usec, profile->t203_sec, profile->t203_usec);
Andreas Eversberg3744b872011-09-27 12:12:36 +0200223 lapd_dl_init(dl, k, 128, profile->n201);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200224 dl->use_sabme = 1; /* use SABME instead of SABM (GSM) */
225 dl->send_ph_data_req = send_ph_data_req;
226 dl->send_dlsap = send_dlsap;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200227 dl->n200 = profile->n200;
228 dl->n200_est_rel = profile->n200;
229 dl->t200_sec = profile->t200_sec; dl->t200_usec = profile->t200_usec;
230 dl->t203_sec = profile->t203_sec; dl->t203_usec = profile->t203_usec;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200231 dl->lctx.dl = &sap->dl;
232 dl->lctx.sapi = sapi;
233 dl->lctx.tei = teip->tei;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200234 dl->lctx.n201 = profile->n201;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200235
236 lapd_set_mode(&sap->dl, (teip->li->network_side) ? LAPD_MODE_NETWORK
237 : LAPD_MODE_USER);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200238
239 llist_add(&sap->list, &teip->sap_list);
240
241 return sap;
242}
243
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200244/* Free SAP instance, including the datalink */
245static void lapd_sap_free(struct lapd_sap *sap)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200246{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200247 /* free datalink structures and timers */
248 lapd_dl_exit(&sap->dl);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200249
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200250 llist_del(&sap->list);
251 talloc_free(sap);
252}
253
254/* Free TEI instance */
255static void lapd_tei_free(struct lapd_tei *teip)
256{
257 struct lapd_sap *sap, *sap2;
258
259 llist_for_each_entry_safe(sap, sap2, &teip->sap_list, list) {
260 lapd_sap_free(sap);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200261 }
262
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200263 llist_del(&teip->list);
264 talloc_free(teip);
265}
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200266
267/* Input function into TEI manager */
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200268static int lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200269{
Andreas Eversberg8aaed052011-10-01 04:10:57 +0200270 uint8_t entity;
271 uint8_t ref;
272 uint8_t mt;
273 uint8_t action;
274 uint8_t e;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200275 uint8_t resp[8];
276 struct lapd_tei *teip;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200277 struct msgb *msg;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200278
Andreas Eversberg8aaed052011-10-01 04:10:57 +0200279 if (len < 5) {
280 LOGP(DLLAPD, LOGL_ERROR, "LAPD TEIMGR frame receive len %d < 5"
281 ", ignoring\n", len);
282 return -EINVAL;
283 };
284
285 entity = data[0];
286 ref = data[1];
287 mt = data[3];
288 action = data[4] >> 1;
289 e = data[4] & 1;
290
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200291 DEBUGP(DLLAPD, "LAPD TEIMGR: entity %x, ref %x, mt %x, action %x, "
292 "e %x\n", entity, ref, mt, action, e);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200293
294 switch (mt) {
295 case 0x01: /* IDENTITY REQUEST */
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200296 DEBUGP(DLLAPD, "LAPD TEIMGR: identity request for TEI %u\n",
297 action);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200298
299 teip = teip_from_tei(li, action);
300 if (!teip) {
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200301 LOGP(DLLAPD, LOGL_INFO, "TEI MGR: New TEI %u\n",
302 action);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200303 teip = lapd_tei_alloc(li, action);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200304 if (!teip)
305 return -ENOMEM;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200306 }
307
308 /* Send ACCEPT */
309 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
310 resp[7] = (action << 1) | 1;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200311 msg = msgb_alloc_headroom(56, 56, "DL EST");
312 msg->l2h = msgb_push(msg, 8);
313 memcpy(msg->l2h, resp, 8);
314 LOGP(DLLAPD, LOGL_DEBUG, "TX: %s\n",
315 osmo_hexdump(msg->data, msg->len));
316 li->transmit_cb(msg, li->transmit_cbdata);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200317
318 if (teip->state == LAPD_TEI_NONE)
319 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
320 break;
321 default:
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200322 LOGP(DLLAPD, LOGL_NOTICE, "LAPD TEIMGR: unknown mt %x "
323 "action %x\n", mt, action);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200324 break;
325 };
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200326
327 return 0;
328}
329
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200330/* General input function for any data received for this LAPD instance */
331int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200332{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200333 int i;
334 struct lapd_msg_ctx lctx;
335 int rc;
336 struct lapd_sap *sap;
337 struct lapd_tei *teip;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200338
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200339 LOGP(DLLAPD, LOGL_DEBUG, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
340 if (msg->len < 2) {
Andreas Eversberg8aaed052011-10-01 04:10:57 +0200341 LOGP(DLLAPD, LOGL_ERROR, "LAPD frame receive len %d < 2, "
342 "ignoring\n", msg->len);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200343 *error = LAPD_ERR_BAD_LEN;
344 return -EINVAL;
345 };
346 msg->l2h = msg->data;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200347
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200348 memset(&lctx, 0, sizeof(lctx));
349
350 i = 0;
351 /* adress field */
352 lctx.sapi = LAPD_ADDR_SAPI(msg->l2h[i]);
353 lctx.cr = LAPD_ADDR_CR(msg->l2h[i]);
354 lctx.lpd = 0;
355 if (!LAPD_ADDR_EA(msg->l2h[i])) {
Andreas Eversberg8aaed052011-10-01 04:10:57 +0200356 if (msg->len < 3) {
357 LOGP(DLLAPD, LOGL_ERROR, "LAPD frame with TEI receive "
358 "len %d < 3, ignoring\n", msg->len);
359 *error = LAPD_ERR_BAD_LEN;
360 return -EINVAL;
361 };
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200362 i++;
363 lctx.tei = LAPD_ADDR_TEI(msg->l2h[i]);
364 }
365 i++;
366 /* control field */
367 if (LAPD_CTRL_is_I(msg->l2h[i])) {
368 lctx.format = LAPD_FORM_I;
369 lctx.n_send = LAPD_CTRL_I_Ns(msg->l2h[i]);
370 i++;
Andreas Eversberg8aaed052011-10-01 04:10:57 +0200371 if (msg->len < 3 && i == 2) {
372 LOGP(DLLAPD, LOGL_ERROR, "LAPD I frame without TEI "
373 "receive len %d < 3, ignoring\n", msg->len);
374 *error = LAPD_ERR_BAD_LEN;
375 return -EINVAL;
376 };
377 if (msg->len < 4 && i == 3) {
378 LOGP(DLLAPD, LOGL_ERROR, "LAPD I frame with TEI "
379 "receive len %d < 4, ignoring\n", msg->len);
380 *error = LAPD_ERR_BAD_LEN;
381 return -EINVAL;
382 };
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200383 lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
384 lctx.p_f = LAPD_CTRL_I_P(msg->l2h[i]);
385 } else if (LAPD_CTRL_is_S(msg->l2h[i])) {
386 lctx.format = LAPD_FORM_S;
387 lctx.s_u = LAPD_CTRL_S_BITS(msg->l2h[i]);
388 i++;
Andreas Eversberg8aaed052011-10-01 04:10:57 +0200389 if (msg->len < 3 && i == 2) {
390 LOGP(DLLAPD, LOGL_ERROR, "LAPD S frame without TEI "
391 "receive len %d < 3, ignoring\n", msg->len);
392 *error = LAPD_ERR_BAD_LEN;
393 return -EINVAL;
394 };
395 if (msg->len < 4 && i == 3) {
396 LOGP(DLLAPD, LOGL_ERROR, "LAPD S frame with TEI "
397 "receive len %d < 4, ignoring\n", msg->len);
398 *error = LAPD_ERR_BAD_LEN;
399 return -EINVAL;
400 };
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200401 lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
402 lctx.p_f = LAPD_CTRL_S_PF(msg->l2h[i]);
403 } else if (LAPD_CTRL_is_U(msg->l2h[i])) {
404 lctx.format = LAPD_FORM_U;
405 lctx.s_u = LAPD_CTRL_U_BITS(msg->l2h[i]);
406 lctx.p_f = LAPD_CTRL_U_PF(msg->l2h[i]);
407 } else
408 lctx.format = LAPD_FORM_UKN;
409 i++;
410 /* length */
411 msg->l3h = msg->l2h + i;
412 msgb_pull(msg, i);
413 lctx.length = msg->len;
414
415 /* perform TEI assignment, if received */
416 if (lctx.tei == 127) {
417 rc = lapd_tei_receive(li, msg->data, msg->len);
418 msgb_free(msg);
419 return rc;
420 }
421
422 /* resolve TEI and SAPI */
423 teip = teip_from_tei(li, lctx.tei);
424 if (!teip) {
425 LOGP(DLLAPD, LOGL_NOTICE, "LAPD Unknown TEI %u\n", lctx.tei);
426 *error = LAPD_ERR_UNKNOWN_TEI;
427 msgb_free(msg);
428 return -EINVAL;
429 }
430 sap = lapd_sap_find(teip, lctx.sapi);
431 if (!sap) {
432 LOGP(DLLAPD, LOGL_INFO, "LAPD No SAP for TEI=%u / SAPI=%u, "
433 "allocating\n", lctx.tei, lctx.sapi);
434 sap = lapd_sap_alloc(teip, lctx.sapi);
435 if (!sap) {
436 *error = LAPD_ERR_NO_MEM;
437 msgb_free(msg);
438 return -ENOMEM;
439 }
440 }
441 lctx.dl = &sap->dl;
442 lctx.n201 = lctx.dl->maxf;
443
444 if (msg->len > lctx.n201) {
445 LOGP(DLLAPD, LOGL_ERROR, "message len %d > N201(%d) "
446 "(discarding)\n", msg->len, lctx.n201);
447 msgb_free(msg);
448 *error = LAPD_ERR_BAD_LEN;
449 return -EINVAL;
450 }
451
452 /* send to LAPD */
453 return lapd_ph_data_ind(msg, &lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200454}
455
456/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
457int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
458{
459 struct lapd_sap *sap;
460 struct lapd_tei *teip;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200461 struct osmo_dlsap_prim dp;
462 struct msgb *msg;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200463
464 teip = teip_from_tei(li, tei);
465 if (!teip)
466 teip = lapd_tei_alloc(li, tei);
467
468 sap = lapd_sap_find(teip, sapi);
469 if (sap)
470 return -EEXIST;
471
472 sap = lapd_sap_alloc(teip, sapi);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200473 if (!sap)
474 return -ENOMEM;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200475
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200476 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-ESTABLISH request TEI=%d SAPI=%d\n",
477 tei, sapi);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200478
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200479 /* prepare prim */
480 msg = msgb_alloc_headroom(56, 56, "DL EST");
481 msg->l3h = msg->data;
482 osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
483
484 /* send to L2 */
485 return lapd_recv_dlsap(&dp, &sap->dl.lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200486}
487
488/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
489int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
490{
491 struct lapd_tei *teip;
492 struct lapd_sap *sap;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200493 struct osmo_dlsap_prim dp;
494 struct msgb *msg;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200495
496 teip = teip_from_tei(li, tei);
497 if (!teip)
498 return -ENODEV;
499
500 sap = lapd_sap_find(teip, sapi);
501 if (!sap)
502 return -ENODEV;
503
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200504 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-RELEASE request TEI=%d SAPI=%d\n",
505 tei, sapi);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200506
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200507 /* prepare prim */
508 msg = msgb_alloc_headroom(56, 56, "DL REL");
509 msg->l3h = msg->data;
510 osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200511
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200512 /* send to L2 */
513 return lapd_recv_dlsap(&dp, &sap->dl.lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200514}
515
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200516/* Transmit Data (DL-DATA request) on the given LAPD Instance / TEI / SAPI */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200517void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200518 struct msgb *msg)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200519{
520 struct lapd_tei *teip = teip_from_tei(li, tei);
521 struct lapd_sap *sap;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200522 struct osmo_dlsap_prim dp;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200523
524 if (!teip) {
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200525 LOGP(DLLAPD, LOGL_ERROR, "LAPD Cannot transmit on "
Harald Welte4ca16c72011-08-16 14:01:49 +0200526 "non-existing TEI %u\n", tei);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200527 msgb_free(msg);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200528 return;
529 }
530
531 sap = lapd_sap_find(teip, sapi);
532 if (!sap) {
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200533 LOGP(DLLAPD, LOGL_INFO, "LAPD Tx on unknown SAPI=%u "
534 "in TEI=%u\n", sapi, tei);
535 msgb_free(msg);
536 return;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200537 }
538
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200539 /* prepare prim */
540 msg->l3h = msg->data;
541 osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200542
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200543 /* send to L2 */
544 lapd_recv_dlsap(&dp, &sap->dl.lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200545};
546
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200547static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
548{
549 struct lapd_datalink *dl = lctx->dl;
550 struct lapd_sap *sap =
551 container_of(dl, struct lapd_sap, dl);
552 struct lapd_instance *li = sap->tei->li;
553 int format = lctx->format;
554 int addr_len;
555
556 /* control field */
557 switch (format) {
558 case LAPD_FORM_I:
559 msg->l2h = msgb_push(msg, 2);
560 msg->l2h[0] = LAPD_CTRL_I4(lctx->n_send);
561 msg->l2h[1] = LAPD_CTRL_I5(lctx->n_recv, lctx->p_f);
562 break;
563 case LAPD_FORM_S:
564 msg->l2h = msgb_push(msg, 2);
565 msg->l2h[0] = LAPD_CTRL_S4(lctx->s_u);
566 msg->l2h[1] = LAPD_CTRL_S5(lctx->n_recv, lctx->p_f);
567 break;
568 case LAPD_FORM_U:
569 msg->l2h = msgb_push(msg, 1);
570 msg->l2h[0] = LAPD_CTRL_U4(lctx->s_u, lctx->p_f);
571 break;
572 default:
573 msgb_free(msg);
574 return -EINVAL;
575 }
576 /* address field */
Andreas Eversberg3744b872011-09-27 12:12:36 +0200577 if (li->profile.short_address && lctx->tei == 0)
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200578 addr_len = 1;
579 else
580 addr_len = 2;
581 msg->l2h = msgb_push(msg, addr_len);
582 msg->l2h[0] = LAPD_ADDR2(lctx->sapi, lctx->cr);
583 if (addr_len == 1)
584 msg->l2h[0] |= 0x1;
585 else
586 msg->l2h[1] = LAPD_ADDR3(lctx->tei);
587
588 /* forward frame to L1 */
589 LOGP(DLLAPD, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
590 li->transmit_cb(msg, li->transmit_cbdata);
591
592 return 0;
593}
594
595/* A DL-SAP message is received from datalink instance and forwarded to L3 */
596static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
597{
598 struct lapd_datalink *dl = lctx->dl;
599 struct lapd_sap *sap =
600 container_of(dl, struct lapd_sap, dl);
601 struct lapd_instance *li;
602 uint8_t tei, sapi;
603 char *op = (dp->oph.operation == PRIM_OP_INDICATION) ? "indication"
604 : "confirm";
605
606 li = sap->tei->li;
607 tei = lctx->tei;
608 sapi = lctx->sapi;
609
610 switch (dp->oph.primitive) {
611 case PRIM_DL_EST:
612 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-ESTABLISH %s TEI=%d "
613 "SAPI=%d\n", op, lctx->tei, lctx->sapi);
614 break;
615 case PRIM_DL_REL:
616 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-RELEASE %s TEI=%d "
617 "SAPI=%d\n", op, lctx->tei, lctx->sapi);
618 lapd_sap_free(sap);
619 /* note: sap and dl is now gone, don't use it anymore */
620 break;
621 default:
622 ;
623 }
624
625 li->receive_cb(dp, tei, sapi, li->receive_cbdata);
626
627 return 0;
628}
629
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200630/* Allocate a new LAPD instance */
631struct lapd_instance *lapd_instance_alloc(int network_side,
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200632 void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
633 void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
634 void *rx_cbdata), void *rx_cbdata,
Andreas Eversberg3744b872011-09-27 12:12:36 +0200635 const struct lapd_profile *profile)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200636{
637 struct lapd_instance *li;
638
639 li = talloc_zero(NULL, struct lapd_instance);
640 if (!li)
641 return NULL;
642
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200643 li->network_side = network_side;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200644 li->transmit_cb = tx_cb;
645 li->transmit_cbdata = tx_cbdata;
646 li->receive_cb = rx_cb;
647 li->receive_cbdata = rx_cbdata;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200648 memcpy(&li->profile, profile, sizeof(li->profile));
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200649
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200650 INIT_LLIST_HEAD(&li->tei_list);
651
652 return li;
653}
Harald Welte14078ea2011-08-24 09:45:11 +0200654
655void lapd_instance_free(struct lapd_instance *li)
656{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200657 struct lapd_tei *teip, *teip2;
Harald Weltef350e252011-08-26 07:55:26 +0200658
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200659 /* Free all TEI instances */
660 llist_for_each_entry_safe(teip, teip2, &li->tei_list, list) {
661 lapd_tei_free(teip);
Harald Weltef350e252011-08-26 07:55:26 +0200662 }
663
Harald Welte14078ea2011-08-24 09:45:11 +0200664 talloc_free(li);
665}