turn LAPD copy+paste into an actual LAPV5-DL implementation
This means * getting rid of SAPI+TEI an replacing it with a V5 data link address, * removing the hierarchical TEI/SAPI data structures * removing the TEI manager
This commit is contained in:
parent
7245238cc3
commit
445290267d
425
lapv5.c
425
lapv5.c
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "lapv5.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -73,9 +74,6 @@
|
|||
#define LOGLI(li, level, fmt, args ...) \
|
||||
LOGP(DLLAPD, level, "(%s): " fmt, (li)->name, ## args)
|
||||
|
||||
#define LOGTEI(teip, level, fmt, args ...) \
|
||||
LOGP(DLLAPD, level, "(%s-T%u): " fmt, (teip)->li->name, (teip)->tei, ## args)
|
||||
|
||||
#define LOGSAP(sap, level, fmt, args ...) \
|
||||
LOGP(DLLAPD, level, "(%s): " fmt, (sap)->dl.name, ## args)
|
||||
|
||||
|
@ -92,85 +90,24 @@ const struct lapd_profile lapd_profile_lapv5dl = {
|
|||
.short_address = 0
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LAPD_TEI_NONE = 0,
|
||||
LAPD_TEI_ASSIGNED,
|
||||
LAPD_TEI_ACTIVE,
|
||||
} lapd_tei_state;
|
||||
|
||||
const char *lapd_tei_states[] = {
|
||||
"NONE",
|
||||
"ASSIGNED",
|
||||
"ACTIVE",
|
||||
};
|
||||
|
||||
/* Structure representing an allocated TEI within a LAPD instance. */
|
||||
struct lapd_tei {
|
||||
struct llist_head list;
|
||||
struct lapd_instance *li;
|
||||
uint8_t tei;
|
||||
lapd_tei_state state;
|
||||
|
||||
struct llist_head sap_list;
|
||||
};
|
||||
|
||||
/* Structure representing a SAP within a TEI. It includes exactly one datalink
|
||||
* instance. */
|
||||
struct lapd_sap {
|
||||
struct llist_head list;
|
||||
struct lapd_tei *tei;
|
||||
uint8_t sapi;
|
||||
struct lapv5_sap {
|
||||
struct llist_head list; /* list of SAP within instance */
|
||||
struct lapv5_instance *li; /* back-pointer */
|
||||
uint16_t dladdr; /* V5-Data Link Address */
|
||||
|
||||
struct lapd_datalink dl;
|
||||
struct lapd_datalink dl; /* the actual data link */
|
||||
};
|
||||
|
||||
/* Resolve TEI structure from given numeric TEI */
|
||||
static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
|
||||
{
|
||||
struct lapd_tei *lt;
|
||||
|
||||
llist_for_each_entry(lt, &li->tei_list, list) {
|
||||
if (lt->tei == tei)
|
||||
return lt;
|
||||
}
|
||||
return NULL;
|
||||
};
|
||||
|
||||
/* Change state of TEI */
|
||||
static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
|
||||
{
|
||||
LOGTEI(teip, LOGL_INFO, "LAPD state change on TEI %d: %s -> %s\n",
|
||||
teip->tei, lapd_tei_states[teip->state],
|
||||
lapd_tei_states[newstate]);
|
||||
teip->state = newstate;
|
||||
};
|
||||
|
||||
/* Allocate a new TEI */
|
||||
struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei)
|
||||
{
|
||||
struct lapd_tei *teip;
|
||||
|
||||
teip = talloc_zero(li, struct lapd_tei);
|
||||
if (!teip)
|
||||
return NULL;
|
||||
|
||||
teip->li = li;
|
||||
teip->tei = tei;
|
||||
llist_add(&teip->list, &li->tei_list);
|
||||
INIT_LLIST_HEAD(&teip->sap_list);
|
||||
|
||||
lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
|
||||
|
||||
return teip;
|
||||
}
|
||||
|
||||
/* Find a SAP within a given TEI */
|
||||
static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi)
|
||||
static struct lapv5_sap *lapv5_sap_find(struct lapv5_instance *li, uint16_t dladdr)
|
||||
{
|
||||
struct lapd_sap *sap;
|
||||
struct lapv5_sap *sap;
|
||||
|
||||
llist_for_each_entry(sap, &teip->sap_list, list) {
|
||||
if (sap->sapi == sapi)
|
||||
llist_for_each_entry(sap, &li->sap_list, list) {
|
||||
if (sap->dladdr == dladdr)
|
||||
return sap;
|
||||
}
|
||||
|
||||
|
@ -180,32 +117,29 @@ static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi)
|
|||
static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
|
||||
static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
|
||||
|
||||
/* Allocate a new SAP within a given TEI */
|
||||
static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi)
|
||||
/* Allocate a new SAP within a given instance */
|
||||
static struct lapv5_sap *lapv5_sap_alloc(struct lapv5_instance *li, uint16_t v5dladdr)
|
||||
{
|
||||
struct lapd_sap *sap;
|
||||
struct lapv5_sap *sap;
|
||||
struct lapd_datalink *dl;
|
||||
struct lapd_instance *li = teip->li;
|
||||
struct lapd_profile *profile;
|
||||
char name[256];
|
||||
int k;
|
||||
|
||||
snprintf(name, sizeof(name), "%s-T%u-S%u", li->name, teip->tei, sapi);
|
||||
snprintf(name, sizeof(name), "%s-%u", li->name, v5dladdr);
|
||||
|
||||
sap = talloc_zero(teip, struct lapd_sap);
|
||||
sap = talloc_zero(li, struct lapv5_sap);
|
||||
if (!sap)
|
||||
return NULL;
|
||||
|
||||
LOGP(DLLAPD, LOGL_NOTICE,
|
||||
"(%s): LAPD Allocating SAP for SAPI=%u / TEI=%u (dl=%p, sap=%p)\n",
|
||||
name, sapi, teip->tei, &sap->dl, sap);
|
||||
LOGP(DLLAPD, LOGL_NOTICE, "(%s): LAPV5 Allocating SAP for V5_DLADDR=%u (dl=%p, sap=%p)\n",
|
||||
name, v5dladdr, &sap->dl, sap);
|
||||
|
||||
sap->sapi = sapi;
|
||||
sap->tei = teip;
|
||||
sap->dladdr = v5dladdr;
|
||||
dl = &sap->dl;
|
||||
profile = &li->profile;
|
||||
|
||||
k = profile->k[sapi & 0x3f];
|
||||
k = profile->k[0];
|
||||
LOGP(DLLAPD, LOGL_NOTICE, "(%s): k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d\n",
|
||||
name, k, profile->n200, profile->n201, profile->t200_sec,
|
||||
profile->t200_usec, profile->t203_sec, profile->t203_usec);
|
||||
|
@ -218,24 +152,21 @@ static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi)
|
|||
dl->t200_sec = profile->t200_sec; dl->t200_usec = profile->t200_usec;
|
||||
dl->t203_sec = profile->t203_sec; dl->t203_usec = profile->t203_usec;
|
||||
dl->lctx.dl = &sap->dl;
|
||||
dl->lctx.sapi = sapi;
|
||||
dl->lctx.tei = teip->tei;
|
||||
dl->lctx.sapi = v5dladdr & 0xff;
|
||||
dl->lctx.tei = v5dladdr >> 8;
|
||||
dl->lctx.n201 = profile->n201;
|
||||
|
||||
lapd_set_mode(&sap->dl, (teip->li->network_side) ? LAPD_MODE_NETWORK
|
||||
: LAPD_MODE_USER);
|
||||
lapd_set_mode(&sap->dl, (li->network_side) ? LAPD_MODE_NETWORK : LAPD_MODE_USER);
|
||||
|
||||
llist_add(&sap->list, &teip->sap_list);
|
||||
llist_add(&sap->list, &li->sap_list);
|
||||
|
||||
return sap;
|
||||
}
|
||||
|
||||
/* Free SAP instance, including the datalink */
|
||||
static void lapd_sap_free(struct lapd_sap *sap)
|
||||
static void lapv5_sap_free(struct lapv5_sap *sap)
|
||||
{
|
||||
LOGSAP(sap, LOGL_NOTICE,
|
||||
"LAPD Freeing SAP for SAPI=%u / TEI=%u (dl=%p, sap=%p)\n",
|
||||
sap->sapi, sap->tei->tei, &sap->dl, sap);
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPV5 Freeing SAP for DLADDR=%u (dl=%p, sap=%p)\n", sap->dladdr, &sap->dl, sap);
|
||||
|
||||
/* free datalink structures and timers */
|
||||
lapd_dl_exit(&sap->dl);
|
||||
|
@ -244,99 +175,21 @@ static void lapd_sap_free(struct lapd_sap *sap)
|
|||
talloc_free(sap);
|
||||
}
|
||||
|
||||
/* Free TEI instance */
|
||||
static void lapd_tei_free(struct lapd_tei *teip)
|
||||
/* General input function for any data received for this LAPV5 instance */
|
||||
int lapv5_receive(struct lapv5_instance *li, struct msgb *msg, int *error)
|
||||
{
|
||||
struct lapd_sap *sap, *sap2;
|
||||
|
||||
llist_for_each_entry_safe(sap, sap2, &teip->sap_list, list) {
|
||||
lapd_sap_free(sap);
|
||||
}
|
||||
|
||||
llist_del(&teip->list);
|
||||
talloc_free(teip);
|
||||
}
|
||||
|
||||
/* Input function into TEI manager */
|
||||
static int lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
|
||||
{
|
||||
uint8_t entity;
|
||||
uint8_t ref;
|
||||
uint8_t mt;
|
||||
uint8_t action;
|
||||
uint8_t e;
|
||||
uint8_t resp[8];
|
||||
struct lapd_tei *teip;
|
||||
struct msgb *msg;
|
||||
|
||||
if (len < 5) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD TEIMGR frame receive len %d < 5"
|
||||
", ignoring\n", len);
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
entity = data[0];
|
||||
ref = data[1];
|
||||
mt = data[3];
|
||||
action = data[4] >> 1;
|
||||
e = data[4] & 1;
|
||||
|
||||
DEBUGP(DLLAPD, "LAPD TEIMGR: entity %x, ref %x, mt %x, action %x, "
|
||||
"e %x\n", entity, ref, mt, action, e);
|
||||
|
||||
switch (mt) {
|
||||
case 0x01: /* IDENTITY REQUEST */
|
||||
DEBUGP(DLLAPD, "LAPD TEIMGR: identity request for TEI %u\n",
|
||||
action);
|
||||
|
||||
teip = teip_from_tei(li, action);
|
||||
if (!teip) {
|
||||
LOGLI(li, LOGL_INFO, "TEI MGR: New TEI %u\n",
|
||||
action);
|
||||
teip = lapd_tei_alloc(li, action);
|
||||
if (!teip)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Send ACCEPT */
|
||||
memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
|
||||
resp[7] = (action << 1) | 1;
|
||||
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL EST");
|
||||
msg->l2h = msgb_push(msg, 8);
|
||||
memcpy(msg->l2h, resp, 8);
|
||||
|
||||
/* write to PCAP file, if enabled. */
|
||||
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg);
|
||||
|
||||
LOGTEI(teip, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
li->transmit_cb(msg, li->transmit_cbdata);
|
||||
|
||||
if (teip->state == LAPD_TEI_NONE)
|
||||
lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
|
||||
break;
|
||||
default:
|
||||
LOGLI(li, LOGL_NOTICE, "LAPD TEIMGR: unknown mt %x action %x\n", mt, action);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* General input function for any data received for this LAPD instance */
|
||||
int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
|
||||
{
|
||||
int i;
|
||||
struct lapd_msg_ctx lctx;
|
||||
int rc;
|
||||
struct lapd_sap *sap;
|
||||
struct lapd_tei *teip;
|
||||
struct lapv5_sap *sap;
|
||||
uint16_t dladdr;
|
||||
bool is_isdn;
|
||||
int i;
|
||||
|
||||
/* write to PCAP file, if enabled. */
|
||||
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_INPUT, msg);
|
||||
|
||||
LOGLI(li, LOGL_DEBUG, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
if (msg->len < 2) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD frame receive len %d < 2, ignoring\n", msg->len);
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 frame receive len %d < 2, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
|
@ -346,55 +199,39 @@ int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
|
|||
|
||||
i = 0;
|
||||
/* adress field */
|
||||
lctx.sapi = LAPD_ADDR_SAPI(msg->l2h[i]);
|
||||
lctx.cr = LAPD_ADDR_CR(msg->l2h[i]);
|
||||
dladdr = v51_l3_addr_dec(msg->l2h[0] << 8 | msg->l2h[1], &is_isdn);
|
||||
if (!is_isdn) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 frame with single-octet addr not permitted\n");
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
lctx.lpd = 0;
|
||||
if (!LAPD_ADDR_EA(msg->l2h[i])) {
|
||||
if (msg->len < 3) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD frame with TEI receive "
|
||||
"len %d < 3, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
i++;
|
||||
lctx.tei = LAPD_ADDR_TEI(msg->l2h[i]);
|
||||
}
|
||||
i++;
|
||||
lctx.tei = dladdr >> 8;
|
||||
lctx.sapi = dladdr & 0xff;
|
||||
lctx.cr = LAPD_ADDR_CR(msg->l2h[0]);
|
||||
i += 2;
|
||||
|
||||
/* control field */
|
||||
if (LAPD_CTRL_is_I(msg->l2h[i])) {
|
||||
lctx.format = LAPD_FORM_I;
|
||||
lctx.n_send = LAPD_CTRL_I_Ns(msg->l2h[i]);
|
||||
i++;
|
||||
if (msg->len < 3 && i == 2) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD I frame without TEI "
|
||||
"receive len %d < 3, ignoring\n", msg->len);
|
||||
if (msg->len < 4) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 I frame receive len %d < 4, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
if (msg->len < 4 && i == 3) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD I frame with TEI "
|
||||
"receive len %d < 4, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
|
||||
lctx.p_f = LAPD_CTRL_I_P(msg->l2h[i]);
|
||||
} else if (LAPD_CTRL_is_S(msg->l2h[i])) {
|
||||
lctx.format = LAPD_FORM_S;
|
||||
lctx.s_u = LAPD_CTRL_S_BITS(msg->l2h[i]);
|
||||
i++;
|
||||
if (msg->len < 3 && i == 2) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD S frame without TEI "
|
||||
"receive len %d < 3, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
if (msg->len < 4 && i == 3) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD S frame with TEI "
|
||||
"receive len %d < 4, ignoring\n", msg->len);
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 S frame receive len %d < 4, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
|
||||
lctx.p_f = LAPD_CTRL_S_PF(msg->l2h[i]);
|
||||
} else if (LAPD_CTRL_is_U(msg->l2h[i])) {
|
||||
|
@ -409,26 +246,10 @@ int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
|
|||
msgb_pull(msg, i);
|
||||
lctx.length = msg->len;
|
||||
|
||||
/* perform TEI assignment, if received */
|
||||
if (lctx.tei == 127) {
|
||||
rc = lapd_tei_receive(li, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* resolve TEI and SAPI */
|
||||
teip = teip_from_tei(li, lctx.tei);
|
||||
if (!teip) {
|
||||
LOGLI(li, LOGL_NOTICE, "LAPD Unknown TEI %u\n", lctx.tei);
|
||||
*error = LAPD_ERR_UNKNOWN_TEI;
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
sap = lapd_sap_find(teip, lctx.sapi);
|
||||
sap = lapv5_sap_find(li, dladdr);
|
||||
if (!sap) {
|
||||
LOGTEI(teip, LOGL_INFO, "LAPD No SAP for TEI=%u / SAPI=%u, "
|
||||
"allocating\n", lctx.tei, lctx.sapi);
|
||||
sap = lapd_sap_alloc(teip, lctx.sapi);
|
||||
LOGLI(li, LOGL_INFO, "LAPV5 No SAP for DLADDR=%u, allocating\n", dladdr);
|
||||
sap = lapv5_sap_alloc(li, dladdr);
|
||||
if (!sap) {
|
||||
*error = LAPD_ERR_NO_MEM;
|
||||
msgb_free(msg);
|
||||
|
@ -439,8 +260,7 @@ int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
|
|||
lctx.n201 = lctx.dl->maxf;
|
||||
|
||||
if (msg->len > lctx.n201) {
|
||||
LOGSAP(sap, LOGL_ERROR, "message len %d > N201(%d) "
|
||||
"(discarding)\n", msg->len, lctx.n201);
|
||||
LOGSAP(sap, LOGL_ERROR, "message len %d > N201(%d) (discarding)\n", msg->len, lctx.n201);
|
||||
msgb_free(msg);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
|
@ -451,26 +271,21 @@ int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
|
|||
}
|
||||
|
||||
/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
|
||||
int lapv5_sap_start(struct lapv5_instance *li, uint16_t dladdr)
|
||||
{
|
||||
struct lapd_sap *sap;
|
||||
struct lapd_tei *teip;
|
||||
struct lapv5_sap *sap;
|
||||
struct osmo_dlsap_prim dp;
|
||||
struct msgb *msg;
|
||||
|
||||
teip = teip_from_tei(li, tei);
|
||||
if (!teip)
|
||||
teip = lapd_tei_alloc(li, tei);
|
||||
|
||||
sap = lapd_sap_find(teip, sapi);
|
||||
sap = lapv5_sap_find(li, dladdr);
|
||||
if (sap)
|
||||
return -EEXIST;
|
||||
|
||||
sap = lapd_sap_alloc(teip, sapi);
|
||||
sap = lapv5_sap_alloc(li, dladdr);
|
||||
if (!sap)
|
||||
return -ENOMEM;
|
||||
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPD DL-ESTABLISH request TEI=%d SAPI=%d\n", tei, sapi);
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPV5 DL-ESTABLISH request DLADDR=%u\n", dladdr);
|
||||
|
||||
/* prepare prim */
|
||||
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL EST");
|
||||
|
@ -482,22 +297,17 @@ int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
|
|||
}
|
||||
|
||||
/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
|
||||
int lapv5_sap_stop(struct lapv5_instance *li, uint16_t dladdr)
|
||||
{
|
||||
struct lapd_tei *teip;
|
||||
struct lapd_sap *sap;
|
||||
struct lapv5_sap *sap;
|
||||
struct osmo_dlsap_prim dp;
|
||||
struct msgb *msg;
|
||||
|
||||
teip = teip_from_tei(li, tei);
|
||||
if (!teip)
|
||||
return -ENODEV;
|
||||
|
||||
sap = lapd_sap_find(teip, sapi);
|
||||
sap = lapv5_sap_find(li, dladdr);
|
||||
if (!sap)
|
||||
return -ENODEV;
|
||||
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPD DL-RELEASE request TEI=%d SAPI=%d\n", tei, sapi);
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPV5 DL-RELEASE request DLADDR=%u\n", dladdr);
|
||||
|
||||
/* prepare prim */
|
||||
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL REL");
|
||||
|
@ -508,23 +318,15 @@ int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
|
|||
return lapd_recv_dlsap(&dp, &sap->dl.lctx);
|
||||
}
|
||||
|
||||
/* Transmit Data (DL-DATA request) on the given LAPD Instance / TEI / SAPI */
|
||||
void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
|
||||
struct msgb *msg)
|
||||
/* Transmit Data (DL-DATA request) on the given LAPD Instance / DLADDR */
|
||||
void lapv5_transmit(struct lapv5_instance *li, uint8_t dladdr, struct msgb *msg)
|
||||
{
|
||||
struct lapd_tei *teip = teip_from_tei(li, tei);
|
||||
struct lapd_sap *sap;
|
||||
struct lapv5_sap *sap;
|
||||
struct osmo_dlsap_prim dp;
|
||||
|
||||
if (!teip) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPD Cannot transmit on non-existing TEI %u\n", tei);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
sap = lapd_sap_find(teip, sapi);
|
||||
sap = lapv5_sap_find(li, dladdr);
|
||||
if (!sap) {
|
||||
LOGTEI(teip, LOGL_INFO, "LAPD Tx on unknown SAPI=%u in TEI=%u\n", sapi, tei);
|
||||
LOGLI(li, LOGL_INFO, "LAPV5 Tx on unknown DLADDR=%u\n", dladdr);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
@ -540,11 +342,9 @@ void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
|
|||
static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
|
||||
{
|
||||
struct lapd_datalink *dl = lctx->dl;
|
||||
struct lapd_sap *sap =
|
||||
container_of(dl, struct lapd_sap, dl);
|
||||
struct lapd_instance *li = sap->tei->li;
|
||||
struct lapv5_sap *sap = container_of(dl, struct lapv5_sap, dl);
|
||||
struct lapv5_instance *li = sap->li;
|
||||
int format = lctx->format;
|
||||
int addr_len;
|
||||
|
||||
/* control field */
|
||||
switch (format) {
|
||||
|
@ -567,16 +367,9 @@ static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
|
|||
return -EINVAL;
|
||||
}
|
||||
/* address field */
|
||||
if (li->profile.short_address && lctx->tei == 0)
|
||||
addr_len = 1;
|
||||
else
|
||||
addr_len = 2;
|
||||
msg->l2h = msgb_push(msg, addr_len);
|
||||
msg->l2h = msgb_push(msg, 2);
|
||||
msg->l2h[0] = LAPD_ADDR2(lctx->sapi, lctx->cr);
|
||||
if (addr_len == 1)
|
||||
msg->l2h[0] |= 0x1;
|
||||
else
|
||||
msg->l2h[1] = LAPD_ADDR3(lctx->tei);
|
||||
msg->l2h[1] = LAPD_ADDR3(lctx->tei);
|
||||
|
||||
/* write to PCAP file, if enabled. */
|
||||
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg);
|
||||
|
@ -592,47 +385,42 @@ static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
|
|||
static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
|
||||
{
|
||||
struct lapd_datalink *dl = lctx->dl;
|
||||
struct lapd_sap *sap =
|
||||
container_of(dl, struct lapd_sap, dl);
|
||||
struct lapd_instance *li;
|
||||
uint8_t tei, sapi;
|
||||
char *op = (dp->oph.operation == PRIM_OP_INDICATION) ? "indication"
|
||||
: "confirm";
|
||||
struct lapv5_sap *sap = container_of(dl, struct lapv5_sap, dl);
|
||||
struct lapv5_instance *li;
|
||||
const char *op = (dp->oph.operation == PRIM_OP_INDICATION) ? "indication" : "confirm";
|
||||
uint16_t dladdr;
|
||||
|
||||
li = sap->tei->li;
|
||||
tei = lctx->tei;
|
||||
sapi = lctx->sapi;
|
||||
li = sap->li;
|
||||
|
||||
dladdr = lctx->tei << 8 | lctx->sapi;
|
||||
|
||||
switch (dp->oph.primitive) {
|
||||
case PRIM_DL_EST:
|
||||
LOGDL(dl, LOGL_NOTICE, "LAPD DL-ESTABLISH %s TEI=%d "
|
||||
"SAPI=%d\n", op, lctx->tei, lctx->sapi);
|
||||
LOGDL(dl, LOGL_NOTICE, "LAPD DL-ESTABLISH %s DLADDR=%u\n", op, dladdr);
|
||||
break;
|
||||
case PRIM_DL_REL:
|
||||
LOGDL(dl, LOGL_NOTICE, "LAPD DL-RELEASE %s TEI=%d "
|
||||
"SAPI=%d\n", op, lctx->tei, lctx->sapi);
|
||||
lapd_sap_free(sap);
|
||||
LOGDL(dl, LOGL_NOTICE, "LAPD DL-RELEASE %s DLADDR=%u\n", op, dladdr);
|
||||
lapv5_sap_free(sap);
|
||||
/* note: sap and dl is now gone, don't use it anymore */
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
li->receive_cb(dp, tei, sapi, li->receive_cbdata);
|
||||
li->receive_cb(dp, dladdr, li->receive_cbdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a new LAPD instance */
|
||||
struct lapd_instance *lapd_instance_alloc2(int network_side,
|
||||
/* Allocate a new LAPV5 instance */
|
||||
struct lapv5_instance *lapv5_instance_alloc(int network_side,
|
||||
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
|
||||
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
|
||||
void *rx_cbdata), void *rx_cbdata,
|
||||
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata), void *rx_cbdata,
|
||||
const struct lapd_profile *profile, const char *name)
|
||||
{
|
||||
struct lapd_instance *li;
|
||||
struct lapv5_instance *li;
|
||||
|
||||
li = talloc_zero(NULL, struct lapd_instance);
|
||||
li = talloc_zero(NULL, struct lapv5_instance);
|
||||
if (!li)
|
||||
return NULL;
|
||||
|
||||
|
@ -645,35 +433,25 @@ struct lapd_instance *lapd_instance_alloc2(int network_side,
|
|||
li->name = talloc_strdup(li, name);
|
||||
memcpy(&li->profile, profile, sizeof(li->profile));
|
||||
|
||||
INIT_LLIST_HEAD(&li->tei_list);
|
||||
INIT_LLIST_HEAD(&li->sap_list);
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
struct lapd_instance *lapd_instance_alloc(int network_side,
|
||||
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
|
||||
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
|
||||
void *rx_cbdata), void *rx_cbdata,
|
||||
const struct lapd_profile *profile)
|
||||
{
|
||||
return lapd_instance_alloc2(network_side, tx_cbdata, tx_cb, rx_cb, rx_cbdata, profile, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Change lapd-profile on the fly (use with caution!) */
|
||||
void lapd_instance_set_profile(struct lapd_instance *li,
|
||||
const struct lapd_profile *profile)
|
||||
void lapv5_instance_set_profile(struct lapv5_instance *li, const struct lapd_profile *profile)
|
||||
{
|
||||
memcpy(&li->profile, profile, sizeof(li->profile));
|
||||
}
|
||||
|
||||
void lapd_instance_free(struct lapd_instance *li)
|
||||
void lapv5_instance_free(struct lapv5_instance *li)
|
||||
{
|
||||
struct lapd_tei *teip, *teip2;
|
||||
struct lapv5_sap *sap, *sap2;
|
||||
|
||||
/* Free all TEI instances */
|
||||
llist_for_each_entry_safe(teip, teip2, &li->tei_list, list) {
|
||||
lapd_tei_free(teip);
|
||||
/* Free all SAP instances */
|
||||
llist_for_each_entry_safe(sap, sap2, &li->sap_list, list) {
|
||||
lapv5_sap_free(sap);
|
||||
}
|
||||
|
||||
talloc_free(li);
|
||||
|
@ -692,6 +470,7 @@ void lapd_instance_free(struct lapd_instance *li)
|
|||
* while the last octet (before msg->tail) points to the last FCS octet. */
|
||||
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg)
|
||||
{
|
||||
struct lapd_datalink *dl;
|
||||
uint16_t efaddr, efaddr_enc;
|
||||
bool is_isdn;
|
||||
|
||||
|
@ -715,13 +494,26 @@ int lapv5ef_rx(struct v5x_link *link, struct msgb *msg)
|
|||
switch (efaddr) {
|
||||
case V51_DLADDR_PSTN:
|
||||
/* hand-over to LAPD-DL instance for PSTN */
|
||||
dl = &link->interface->pstn.dl;
|
||||
/* TODO */
|
||||
break;
|
||||
case V51_DLADDR_CTRL:
|
||||
/* hand-over to LAPD-DL instance for CTRL */
|
||||
/* TODO */
|
||||
dl = &link->interface->control.dl;
|
||||
break;
|
||||
case V52_DLADDR_BCC:
|
||||
dl = &link->interface->bcc.dl;
|
||||
/* TOOD: implement V5.2 */
|
||||
msgb_free(msg);
|
||||
break;
|
||||
case V52_DLADDR_PROTECTION:
|
||||
dl = &link->interface->protection[0].dl;
|
||||
/* TOOD: implement V5.2 */
|
||||
msgb_free(msg);
|
||||
break;
|
||||
case V52_DLADDR_LCP:
|
||||
dl = &link->interface->lcp.dl;
|
||||
/* TOOD: implement V5.2 */
|
||||
msgb_free(msg);
|
||||
break;
|
||||
|
@ -732,5 +524,8 @@ int lapv5ef_rx(struct v5x_link *link, struct msgb *msg)
|
|||
return -EINVAL;
|
||||
}
|
||||
/* relay function for LAPD of user ports */
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
struct lapv5_instance {
|
||||
struct llist_head list; /* list of LAPV5 instances */
|
||||
bool network_side;
|
||||
|
||||
void (*transmit_cb)(struct msgb *msg, void *cbdata);
|
||||
void *transmit_cbdata;
|
||||
void (*receive_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata);
|
||||
void *receive_cbdata;
|
||||
|
||||
struct lapd_profile profile; /* must be a copy */
|
||||
|
||||
struct llist_head sap_list; /* list of SAP/datalinks in this instance */
|
||||
int pcap_fd; /* PCAP file descriptor */
|
||||
char *name; /* human-readable name */
|
||||
};
|
||||
|
||||
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg);
|
|
@ -0,0 +1,92 @@
|
|||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
|
||||
static LLIST_HEAD(v5_instances);
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx)
|
||||
{
|
||||
struct v5x_instance *v5i = talloc_zero(ctx, struct v5x_instance);
|
||||
if (!v5i)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&v5i->interfaces);
|
||||
INIT_LLIST_HEAD(&v5i->user_ports);
|
||||
/* TODO: allocate ctrl_fi */
|
||||
|
||||
llist_add_tail(&v5i->list, &v5_instances);
|
||||
|
||||
return v5i;
|
||||
}
|
||||
|
||||
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, uint16_t l3_addr)
|
||||
{
|
||||
v5ts->nr = nr;
|
||||
v5ts->link = v5l;
|
||||
v5ts->l3_address = l3_addr;
|
||||
}
|
||||
|
||||
static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active)
|
||||
{
|
||||
v5cc->link = v5l;
|
||||
v5cc->ts = v5ts;
|
||||
v5cc->active = active;
|
||||
}
|
||||
|
||||
static void v5x_link_init(struct v5x_link *v5l, struct v5x_interface *v5if, uint8_t id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
v5l->id = id;
|
||||
v5l->interface = v5if;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
|
||||
v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/);
|
||||
}
|
||||
|
||||
/* primary c-channel must always be present */
|
||||
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
|
||||
}
|
||||
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect)
|
||||
{
|
||||
struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface);
|
||||
//struct v5x_link *v5l;
|
||||
struct v5x_c_channel *primary_c_chan;
|
||||
|
||||
v5if->instance = v5i;
|
||||
v5if->dialect = dialect;
|
||||
|
||||
/* primary link must alwasy be present */
|
||||
v5x_link_init(&v5if->links[0], v5if, 0);
|
||||
v5if->primary_link = &v5if->links[0];
|
||||
|
||||
primary_c_chan = &v5if->primary_link->c_channel[0];
|
||||
|
||||
|
||||
/* TODO: allocate fi */
|
||||
|
||||
//lapd_dl_init2(&v5if->control.dl, k, v_rnage, maxf, "control");
|
||||
v5if->control.c_chan = primary_c_chan;
|
||||
|
||||
//lapd_dl_init2(&v5if->pstn.dl, k, v_rnage, maxf, "pstn");
|
||||
v5if->pstn.c_chan = primary_c_chan;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V52) {
|
||||
//lapd_dl_init2(&v5if->lcp.dl, k, v_rnage, maxf, "lcp");
|
||||
v5if->lcp.c_chan = primary_c_chan;
|
||||
|
||||
//lapd_dl_init2(&v5if->bcc.dl, k, v_rnage, maxf, "bcc");
|
||||
v5if->bcc.c_chan = primary_c_chan;
|
||||
|
||||
//lapd_dl_init2(&v5if->protection[0].dl, k, v_rnage, maxf, "protection0");
|
||||
v5if->protection[0].c_chan = primary_c_chan;
|
||||
|
||||
//protection[1] ?
|
||||
}
|
||||
|
||||
llist_add_tail(&v5if->list, &v5i->interfaces);
|
||||
|
||||
return v5if;
|
||||
}
|
|
@ -118,3 +118,6 @@ struct v5x_instance {
|
|||
struct llist_head user_ports; /* list of v5x_user_port */
|
||||
struct osmo_fsm_inst *ctrl_fi; /* common control FSM */
|
||||
};
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx);
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect);
|
||||
|
|
Loading…
Reference in New Issue