blob: a19f0b19adda59b5f070a908dc458a7e5cf10f90 [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,
80 10,0,
81 0
82};
83
84const struct lapd_profile lapd_profile_abis = {
Andreas Eversberg3c460442011-09-28 02:46:16 +020085 LAPD_SET_K(2,1),
Andreas Eversberg3744b872011-09-27 12:12:36 +020086 3,
87 260,
Andreas Eversberg3c460442011-09-28 02:46:16 +020088 0, /* infinite */
Andreas Eversberg3744b872011-09-27 12:12:36 +020089 0,240000,
90 1,0,
91 10,0,
92 0
93};
94
95const struct lapd_profile lapd_profile_sat = {
Andreas Eversberg3c460442011-09-28 02:46:16 +020096 LAPD_SET_K(15,15),
Andreas Eversberg3744b872011-09-27 12:12:36 +020097 5,
98 260,
99 5,
Andreas Eversberg3c460442011-09-28 02:46:16 +0200100 2,400000,
101 2,400000,
Andreas Eversberg3744b872011-09-27 12:12:36 +0200102 20,0,
103 1
104};
105
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200106typedef enum {
107 LAPD_TEI_NONE = 0,
108 LAPD_TEI_ASSIGNED,
109 LAPD_TEI_ACTIVE,
110} lapd_tei_state;
111
112const char *lapd_tei_states[] = {
113 "NONE",
114 "ASSIGNED",
115 "ACTIVE",
116};
117
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200118/* Structure representing an allocated TEI within a LAPD instance. */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200119struct lapd_tei {
120 struct llist_head list;
121 struct lapd_instance *li;
122 uint8_t tei;
123 lapd_tei_state state;
124
125 struct llist_head sap_list;
126};
127
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200128/* Structure representing a SAP within a TEI. It includes exactly one datalink
129 * instance. */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200130struct lapd_sap {
131 struct llist_head list;
132 struct lapd_tei *tei;
133 uint8_t sapi;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200134
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200135 struct lapd_datalink dl;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200136};
137
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200138/* Resolve TEI structure from given numeric TEI */
139static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
140{
141 struct lapd_tei *lt;
142
143 llist_for_each_entry(lt, &li->tei_list, list) {
144 if (lt->tei == tei)
145 return lt;
146 }
147 return NULL;
148};
149
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200150/* Change state of TEI */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200151static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
152{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200153 LOGP(DLLAPD, LOGL_INFO, "LAPD state change on TEI %d: %s -> %s\n",
154 teip->tei, lapd_tei_states[teip->state],
155 lapd_tei_states[newstate]);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200156 teip->state = newstate;
157};
158
159/* Allocate a new TEI */
160struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei)
161{
162 struct lapd_tei *teip;
163
164 teip = talloc_zero(li, struct lapd_tei);
165 if (!teip)
166 return NULL;
167
168 teip->li = li;
169 teip->tei = tei;
170 llist_add(&teip->list, &li->tei_list);
171 INIT_LLIST_HEAD(&teip->sap_list);
172
173 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
174
175 return teip;
176}
177
178/* Find a SAP within a given TEI */
179static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi)
180{
181 struct lapd_sap *sap;
182
183 llist_for_each_entry(sap, &teip->sap_list, list) {
184 if (sap->sapi == sapi)
185 return sap;
186 }
187
188 return NULL;
189}
190
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200191static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
192static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200193
194/* Allocate a new SAP within a given TEI */
195static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi)
196{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200197 struct lapd_sap *sap;
198 struct lapd_datalink *dl;
199 struct lapd_instance *li;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200200 struct lapd_profile *profile;
201 int k;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200202
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200203 sap = talloc_zero(teip, struct lapd_sap);
204 if (!sap)
205 return NULL;
206
Andreas Eversberg3c460442011-09-28 02:46:16 +0200207 LOGP(DLLAPD, LOGL_NOTICE, "LAPD Allocating SAP for SAPI=%u / TEI=%u\n",
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200208 sapi, teip->tei);
209
210 sap->sapi = sapi;
211 sap->tei = teip;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200212 dl = &sap->dl;
213 li = teip->li;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200214 profile = &li->profile;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200215
Andreas Eversberg3c460442011-09-28 02:46:16 +0200216 k = profile->k[sapi & 0x3f];
217 LOGP(DLLAPD, LOGL_NOTICE, "k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d"
218 "\n", k, profile->n200, profile->n201, profile->t200_sec,
219 profile->t200_usec, profile->t203_sec, profile->t203_usec);
Andreas Eversberg3744b872011-09-27 12:12:36 +0200220 lapd_dl_init(dl, k, 128, profile->n201);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200221 dl->use_sabme = 1; /* use SABME instead of SABM (GSM) */
222 dl->send_ph_data_req = send_ph_data_req;
223 dl->send_dlsap = send_dlsap;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200224 dl->n200 = profile->n200;
225 dl->n200_est_rel = profile->n200;
226 dl->t200_sec = profile->t200_sec; dl->t200_usec = profile->t200_usec;
227 dl->t203_sec = profile->t203_sec; dl->t203_usec = profile->t203_usec;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200228 dl->lctx.dl = &sap->dl;
229 dl->lctx.sapi = sapi;
230 dl->lctx.tei = teip->tei;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200231 dl->lctx.n201 = profile->n201;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200232
233 lapd_set_mode(&sap->dl, (teip->li->network_side) ? LAPD_MODE_NETWORK
234 : LAPD_MODE_USER);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200235
236 llist_add(&sap->list, &teip->sap_list);
237
238 return sap;
239}
240
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200241/* Free SAP instance, including the datalink */
242static void lapd_sap_free(struct lapd_sap *sap)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200243{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200244 /* free datalink structures and timers */
245 lapd_dl_exit(&sap->dl);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200246
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200247 llist_del(&sap->list);
248 talloc_free(sap);
249}
250
251/* Free TEI instance */
252static void lapd_tei_free(struct lapd_tei *teip)
253{
254 struct lapd_sap *sap, *sap2;
255
256 llist_for_each_entry_safe(sap, sap2, &teip->sap_list, list) {
257 lapd_sap_free(sap);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200258 }
259
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200260 llist_del(&teip->list);
261 talloc_free(teip);
262}
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200263
264/* Input function into TEI manager */
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200265static int lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200266{
267 uint8_t entity = data[0];
268 uint8_t ref = data[1];
269 uint8_t mt = data[3];
270 uint8_t action = data[4] >> 1;
271 uint8_t e = data[4] & 1;
272 uint8_t resp[8];
273 struct lapd_tei *teip;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200274 struct msgb *msg;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200275
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200276 DEBUGP(DLLAPD, "LAPD TEIMGR: entity %x, ref %x, mt %x, action %x, "
277 "e %x\n", entity, ref, mt, action, e);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200278
279 switch (mt) {
280 case 0x01: /* IDENTITY REQUEST */
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200281 DEBUGP(DLLAPD, "LAPD TEIMGR: identity request for TEI %u\n",
282 action);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200283
284 teip = teip_from_tei(li, action);
285 if (!teip) {
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200286 LOGP(DLLAPD, LOGL_INFO, "TEI MGR: New TEI %u\n",
287 action);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200288 teip = lapd_tei_alloc(li, action);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200289 if (!teip)
290 return -ENOMEM;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200291 }
292
293 /* Send ACCEPT */
294 memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
295 resp[7] = (action << 1) | 1;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200296 msg = msgb_alloc_headroom(56, 56, "DL EST");
297 msg->l2h = msgb_push(msg, 8);
298 memcpy(msg->l2h, resp, 8);
299 LOGP(DLLAPD, LOGL_DEBUG, "TX: %s\n",
300 osmo_hexdump(msg->data, msg->len));
301 li->transmit_cb(msg, li->transmit_cbdata);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200302
303 if (teip->state == LAPD_TEI_NONE)
304 lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
305 break;
306 default:
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200307 LOGP(DLLAPD, LOGL_NOTICE, "LAPD TEIMGR: unknown mt %x "
308 "action %x\n", mt, action);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200309 break;
310 };
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200311
312 return 0;
313}
314
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200315/* General input function for any data received for this LAPD instance */
316int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200317{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200318 int i;
319 struct lapd_msg_ctx lctx;
320 int rc;
321 struct lapd_sap *sap;
322 struct lapd_tei *teip;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200323
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200324 LOGP(DLLAPD, LOGL_DEBUG, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
325 if (msg->len < 2) {
326 LOGP(DLLAPD, LOGL_ERROR, "LAPD receive len %d < 2, ignoring\n",
327 msg->len);
328 *error = LAPD_ERR_BAD_LEN;
329 return -EINVAL;
330 };
331 msg->l2h = msg->data;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200332
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200333 memset(&lctx, 0, sizeof(lctx));
334
335 i = 0;
336 /* adress field */
337 lctx.sapi = LAPD_ADDR_SAPI(msg->l2h[i]);
338 lctx.cr = LAPD_ADDR_CR(msg->l2h[i]);
339 lctx.lpd = 0;
340 if (!LAPD_ADDR_EA(msg->l2h[i])) {
341 i++;
342 lctx.tei = LAPD_ADDR_TEI(msg->l2h[i]);
343 }
344 i++;
345 /* control field */
346 if (LAPD_CTRL_is_I(msg->l2h[i])) {
347 lctx.format = LAPD_FORM_I;
348 lctx.n_send = LAPD_CTRL_I_Ns(msg->l2h[i]);
349 i++;
350 lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
351 lctx.p_f = LAPD_CTRL_I_P(msg->l2h[i]);
352 } else if (LAPD_CTRL_is_S(msg->l2h[i])) {
353 lctx.format = LAPD_FORM_S;
354 lctx.s_u = LAPD_CTRL_S_BITS(msg->l2h[i]);
355 i++;
356 lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
357 lctx.p_f = LAPD_CTRL_S_PF(msg->l2h[i]);
358 } else if (LAPD_CTRL_is_U(msg->l2h[i])) {
359 lctx.format = LAPD_FORM_U;
360 lctx.s_u = LAPD_CTRL_U_BITS(msg->l2h[i]);
361 lctx.p_f = LAPD_CTRL_U_PF(msg->l2h[i]);
362 } else
363 lctx.format = LAPD_FORM_UKN;
364 i++;
365 /* length */
366 msg->l3h = msg->l2h + i;
367 msgb_pull(msg, i);
368 lctx.length = msg->len;
369
370 /* perform TEI assignment, if received */
371 if (lctx.tei == 127) {
372 rc = lapd_tei_receive(li, msg->data, msg->len);
373 msgb_free(msg);
374 return rc;
375 }
376
377 /* resolve TEI and SAPI */
378 teip = teip_from_tei(li, lctx.tei);
379 if (!teip) {
380 LOGP(DLLAPD, LOGL_NOTICE, "LAPD Unknown TEI %u\n", lctx.tei);
381 *error = LAPD_ERR_UNKNOWN_TEI;
382 msgb_free(msg);
383 return -EINVAL;
384 }
385 sap = lapd_sap_find(teip, lctx.sapi);
386 if (!sap) {
387 LOGP(DLLAPD, LOGL_INFO, "LAPD No SAP for TEI=%u / SAPI=%u, "
388 "allocating\n", lctx.tei, lctx.sapi);
389 sap = lapd_sap_alloc(teip, lctx.sapi);
390 if (!sap) {
391 *error = LAPD_ERR_NO_MEM;
392 msgb_free(msg);
393 return -ENOMEM;
394 }
395 }
396 lctx.dl = &sap->dl;
397 lctx.n201 = lctx.dl->maxf;
398
399 if (msg->len > lctx.n201) {
400 LOGP(DLLAPD, LOGL_ERROR, "message len %d > N201(%d) "
401 "(discarding)\n", msg->len, lctx.n201);
402 msgb_free(msg);
403 *error = LAPD_ERR_BAD_LEN;
404 return -EINVAL;
405 }
406
407 /* send to LAPD */
408 return lapd_ph_data_ind(msg, &lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200409}
410
411/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
412int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
413{
414 struct lapd_sap *sap;
415 struct lapd_tei *teip;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200416 struct osmo_dlsap_prim dp;
417 struct msgb *msg;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200418
419 teip = teip_from_tei(li, tei);
420 if (!teip)
421 teip = lapd_tei_alloc(li, tei);
422
423 sap = lapd_sap_find(teip, sapi);
424 if (sap)
425 return -EEXIST;
426
427 sap = lapd_sap_alloc(teip, sapi);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200428 if (!sap)
429 return -ENOMEM;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200430
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200431 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-ESTABLISH request TEI=%d SAPI=%d\n",
432 tei, sapi);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200433
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200434 /* prepare prim */
435 msg = msgb_alloc_headroom(56, 56, "DL EST");
436 msg->l3h = msg->data;
437 osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
438
439 /* send to L2 */
440 return lapd_recv_dlsap(&dp, &sap->dl.lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200441}
442
443/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
444int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
445{
446 struct lapd_tei *teip;
447 struct lapd_sap *sap;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200448 struct osmo_dlsap_prim dp;
449 struct msgb *msg;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200450
451 teip = teip_from_tei(li, tei);
452 if (!teip)
453 return -ENODEV;
454
455 sap = lapd_sap_find(teip, sapi);
456 if (!sap)
457 return -ENODEV;
458
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200459 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-RELEASE request TEI=%d SAPI=%d\n",
460 tei, sapi);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200461
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200462 /* prepare prim */
463 msg = msgb_alloc_headroom(56, 56, "DL REL");
464 msg->l3h = msg->data;
465 osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200466
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200467 /* send to L2 */
468 return lapd_recv_dlsap(&dp, &sap->dl.lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200469}
470
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200471/* Transmit Data (DL-DATA request) on the given LAPD Instance / TEI / SAPI */
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200472void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200473 struct msgb *msg)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200474{
475 struct lapd_tei *teip = teip_from_tei(li, tei);
476 struct lapd_sap *sap;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200477 struct osmo_dlsap_prim dp;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200478
479 if (!teip) {
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200480 LOGP(DLLAPD, LOGL_ERROR, "LAPD Cannot transmit on "
Harald Welte4ca16c72011-08-16 14:01:49 +0200481 "non-existing TEI %u\n", tei);
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200482 msgb_free(msg);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200483 return;
484 }
485
486 sap = lapd_sap_find(teip, sapi);
487 if (!sap) {
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200488 LOGP(DLLAPD, LOGL_INFO, "LAPD Tx on unknown SAPI=%u "
489 "in TEI=%u\n", sapi, tei);
490 msgb_free(msg);
491 return;
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200492 }
493
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200494 /* prepare prim */
495 msg->l3h = msg->data;
496 osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200497
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200498 /* send to L2 */
499 lapd_recv_dlsap(&dp, &sap->dl.lctx);
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200500};
501
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200502static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
503{
504 struct lapd_datalink *dl = lctx->dl;
505 struct lapd_sap *sap =
506 container_of(dl, struct lapd_sap, dl);
507 struct lapd_instance *li = sap->tei->li;
508 int format = lctx->format;
509 int addr_len;
510
511 /* control field */
512 switch (format) {
513 case LAPD_FORM_I:
514 msg->l2h = msgb_push(msg, 2);
515 msg->l2h[0] = LAPD_CTRL_I4(lctx->n_send);
516 msg->l2h[1] = LAPD_CTRL_I5(lctx->n_recv, lctx->p_f);
517 break;
518 case LAPD_FORM_S:
519 msg->l2h = msgb_push(msg, 2);
520 msg->l2h[0] = LAPD_CTRL_S4(lctx->s_u);
521 msg->l2h[1] = LAPD_CTRL_S5(lctx->n_recv, lctx->p_f);
522 break;
523 case LAPD_FORM_U:
524 msg->l2h = msgb_push(msg, 1);
525 msg->l2h[0] = LAPD_CTRL_U4(lctx->s_u, lctx->p_f);
526 break;
527 default:
528 msgb_free(msg);
529 return -EINVAL;
530 }
531 /* address field */
Andreas Eversberg3744b872011-09-27 12:12:36 +0200532 if (li->profile.short_address && lctx->tei == 0)
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200533 addr_len = 1;
534 else
535 addr_len = 2;
536 msg->l2h = msgb_push(msg, addr_len);
537 msg->l2h[0] = LAPD_ADDR2(lctx->sapi, lctx->cr);
538 if (addr_len == 1)
539 msg->l2h[0] |= 0x1;
540 else
541 msg->l2h[1] = LAPD_ADDR3(lctx->tei);
542
543 /* forward frame to L1 */
544 LOGP(DLLAPD, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
545 li->transmit_cb(msg, li->transmit_cbdata);
546
547 return 0;
548}
549
550/* A DL-SAP message is received from datalink instance and forwarded to L3 */
551static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
552{
553 struct lapd_datalink *dl = lctx->dl;
554 struct lapd_sap *sap =
555 container_of(dl, struct lapd_sap, dl);
556 struct lapd_instance *li;
557 uint8_t tei, sapi;
558 char *op = (dp->oph.operation == PRIM_OP_INDICATION) ? "indication"
559 : "confirm";
560
561 li = sap->tei->li;
562 tei = lctx->tei;
563 sapi = lctx->sapi;
564
565 switch (dp->oph.primitive) {
566 case PRIM_DL_EST:
567 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-ESTABLISH %s TEI=%d "
568 "SAPI=%d\n", op, lctx->tei, lctx->sapi);
569 break;
570 case PRIM_DL_REL:
571 LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-RELEASE %s TEI=%d "
572 "SAPI=%d\n", op, lctx->tei, lctx->sapi);
573 lapd_sap_free(sap);
574 /* note: sap and dl is now gone, don't use it anymore */
575 break;
576 default:
577 ;
578 }
579
580 li->receive_cb(dp, tei, sapi, li->receive_cbdata);
581
582 return 0;
583}
584
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200585/* Allocate a new LAPD instance */
586struct lapd_instance *lapd_instance_alloc(int network_side,
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200587 void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
588 void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
589 void *rx_cbdata), void *rx_cbdata,
Andreas Eversberg3744b872011-09-27 12:12:36 +0200590 const struct lapd_profile *profile)
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200591{
592 struct lapd_instance *li;
593
594 li = talloc_zero(NULL, struct lapd_instance);
595 if (!li)
596 return NULL;
597
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200598 li->network_side = network_side;
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200599 li->transmit_cb = tx_cb;
600 li->transmit_cbdata = tx_cbdata;
601 li->receive_cb = rx_cb;
602 li->receive_cbdata = rx_cbdata;
Andreas Eversberg3744b872011-09-27 12:12:36 +0200603 memcpy(&li->profile, profile, sizeof(li->profile));
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200604
Pablo Neira Ayuso0ba77d52011-06-05 18:32:44 +0200605 INIT_LLIST_HEAD(&li->tei_list);
606
607 return li;
608}
Harald Welte14078ea2011-08-24 09:45:11 +0200609
610void lapd_instance_free(struct lapd_instance *li)
611{
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200612 struct lapd_tei *teip, *teip2;
Harald Weltef350e252011-08-26 07:55:26 +0200613
Andreas Eversberga7ff0012011-09-26 11:29:30 +0200614 /* Free all TEI instances */
615 llist_for_each_entry_safe(teip, teip2, &li->tei_list, list) {
616 lapd_tei_free(teip);
Harald Weltef350e252011-08-26 07:55:26 +0200617 }
618
Harald Welte14078ea2011-08-24 09:45:11 +0200619 talloc_free(li);
620}