dect
/
libdect
Archived
13
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
libdect/src/identities.c

457 lines
10 KiB
C

/*
* DECT Identities
*
* Copyright (c) 2009-2010 Patrick McHardy <kaber@trash.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/**
* @defgroup identity Identities
*
* This module implements the NWK-Layer identities specified in ETSI EN 300 175-6.
*
* @{
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <asm/byteorder.h>
#include <libdect.h>
#include <identities.h>
#include <utils.h>
#define sfmt_debug(fmt, args...) \
dect_debug(DECT_DEBUG_SFMT, fmt, ## args)
static const char * const ari_classes[] = {
[DECT_ARC_A] = "A",
[DECT_ARC_B] = "B",
[DECT_ARC_C] = "C",
[DECT_ARC_D] = "D",
[DECT_ARC_E] = "E",
};
void dect_dump_ari(const struct dect_ari *ari)
{
sfmt_debug("\tclass: %s\n", ari_classes[ari->arc]);
switch (ari->arc) {
case DECT_ARC_A:
sfmt_debug("\tEMC: %.4x\n", ari->emc);
sfmt_debug("\tFPN: %.5x\n", ari->fpn);
break;
case DECT_ARC_B:
sfmt_debug("\tEIC: %x\n", ari->eic);
sfmt_debug("\tFPN: %x\n", ari->fpn);
sfmt_debug("\tFPS: %x\n", ari->fps);
break;
case DECT_ARC_C:
case DECT_ARC_D:
case DECT_ARC_E:
break;
}
}
uint8_t dect_parse_ari(struct dect_ari *ari, uint64_t a)
{
ari->arc = (a & DECT_ARI_ARC_MASK) >> DECT_ARI_ARC_SHIFT;
switch (ari->arc) {
case DECT_ARC_A:
ari->emc = (a & DECT_ARI_A_EMC_MASK) >> DECT_ARI_A_EMC_SHIFT;
ari->fpn = (a & DECT_ARI_A_FPN_MASK) >> DECT_ARI_A_FPN_SHIFT;
return DECT_ARC_A_LEN;
case DECT_ARC_B:
ari->eic = (a & DECT_ARI_B_EIC_MASK) >> DECT_ARI_B_EIC_SHIFT;
ari->fpn = (a & DECT_ARI_B_FPN_MASK) >> DECT_ARI_B_FPN_SHIFT;
ari->fps = (a & DECT_ARI_B_FPS_MASK) >> DECT_ARI_B_FPS_SHIFT;
return DECT_ARC_B_LEN;
case DECT_ARC_C:
ari->poc = (a & DECT_ARI_C_POC_MASK) >> DECT_ARI_C_POC_SHIFT;
ari->fpn = (a & DECT_ARI_C_FPN_MASK) >> DECT_ARI_C_FPN_SHIFT;
ari->fps = (a & DECT_ARI_C_FPS_MASK) >> DECT_ARI_C_FPS_SHIFT;
return DECT_ARC_C_LEN;
case DECT_ARC_D:
ari->gop = (a & DECT_ARI_D_GOP_MASK) >> DECT_ARI_D_GOP_SHIFT;
ari->fpn = (a & DECT_ARI_D_FPN_MASK) >> DECT_ARI_D_FPN_SHIFT;
return DECT_ARC_D_LEN;
case DECT_ARC_E:
ari->fil = (a & DECT_ARI_E_FIL_MASK) >> DECT_ARI_E_FIL_SHIFT;
ari->fpn = (a & DECT_ARI_E_FPN_MASK) >> DECT_ARI_E_FPN_SHIFT;
return DECT_ARC_E_LEN;
default:
return 0;
}
}
uint64_t dect_build_ari(const struct dect_ari *ari)
{
uint64_t a = 0;
a |= (uint64_t)ari->arc << DECT_ARI_ARC_SHIFT;
switch (ari->arc) {
case DECT_ARC_A:
a |= (uint64_t)ari->emc << DECT_ARI_A_EMC_SHIFT;
a |= (uint64_t)ari->fpn << DECT_ARI_A_FPN_SHIFT;
break;
case DECT_ARC_B:
a |= (uint64_t)ari->eic << DECT_ARI_B_EIC_SHIFT;
a |= (uint64_t)ari->fpn << DECT_ARI_B_FPN_SHIFT;
a |= (uint64_t)ari->fps << DECT_ARI_B_FPS_SHIFT;
break;
case DECT_ARC_C:
a |= (uint64_t)ari->poc << DECT_ARI_C_POC_SHIFT;
a |= (uint64_t)ari->fpn << DECT_ARI_C_FPN_SHIFT;
a |= (uint64_t)ari->fps << DECT_ARI_C_FPS_SHIFT;
break;
case DECT_ARC_D:
a |= (uint64_t)ari->gop << DECT_ARI_D_GOP_SHIFT;
a |= (uint64_t)ari->fpn << DECT_ARI_D_FPN_SHIFT;
break;
case DECT_ARC_E:
a |= (uint64_t)ari->fil << DECT_ARI_E_FIL_SHIFT;
a |= (uint64_t)ari->fpn << DECT_ARI_E_FPN_SHIFT;
break;
}
return a;
}
bool dect_ari_cmp(const struct dect_ari *ari1, const struct dect_ari *ari2)
{
return memcmp(ari1, ari2, sizeof(*ari1));
}
EXPORT_SYMBOL(dect_ari_cmp);
static void dect_dump_ipei(const struct dect_ipei *ipei)
{
sfmt_debug("\tEMC: %.4x\n", ipei->emc);
sfmt_debug("\tPSN: %.5x\n", ipei->psn);
}
/**
* Format an IPEI as printed text
*
* @param ipei IPEI
* @param buf destination buffer
*
* Format an IPEI as printed text according to ETSI EN 300 175-6 Annex C.
*/
char *dect_format_ipei_string(const struct dect_ipei *ipei, char *buf)
{
unsigned int i, c, len;
len = sprintf(buf, "%05u%07u", ipei->emc, ipei->psn);
c = 0;
for (i = 0; i < len; i++)
c += (i + 1) * (buf[i] - '0');
c %= 11;
if (c < 10)
buf[len] = c + '0';
else
buf[len] = '*';
buf[len + 1] = '\0';
return buf;
}
EXPORT_SYMBOL(dect_format_ipei_string);
/**
* Parse the textual representation of an IPEI
*
* @param ipei IPEI
* @param str IPEI string
*
* Parse the textual representation of an IPEI formatted according to
* ETSI EN 300 175-6 Annex C.
*
* @returns True if parsing was successful, false in case of an error.
*/
bool dect_parse_ipei_string(struct dect_ipei *ipei, const char *str)
{
unsigned int i, c, len;
len = strlen(str);
if (len != DECT_IPEI_STRING_LEN)
return false;
c = 0;
for (i = 0; i < len - 1; i++)
c += (i + 1) * (str[i] - '0');
c %= 11;
if (c < 10) {
if (str[len - 1] != (int)c + '0')
return false;
} else {
if (str[len - 1] != '*')
return false;
}
return sscanf(str, "%05hu%07u", &ipei->emc, &ipei->psn) == 2;
}
EXPORT_SYMBOL(dect_parse_ipei_string);
static bool dect_parse_ipei(struct dect_ipei *ipei, uint64_t i)
{
ipei->emc = (i & DECT_IPEI_EMC_MASK) >> DECT_IPEI_EMC_SHIFT;
ipei->psn = (i & DECT_IPEI_PSN_MASK);
return true;
}
static uint64_t dect_build_ipei(const struct dect_ipei *ipei)
{
uint64_t i = 0;
i |= (uint64_t)ipei->emc << DECT_IPEI_EMC_SHIFT;
i |= (uint64_t)ipei->psn;
return i;
}
void dect_dump_ipui(const struct dect_ipui *ipui)
{
switch (ipui->put) {
case DECT_IPUI_N:
sfmt_debug("\tPUT: N (IPEI)\n");
return dect_dump_ipei(&ipui->pun.n.ipei);
case DECT_IPUI_O:
sfmt_debug("\tPUT: O (private)\n");
sfmt_debug("\tNumber: %" PRIx64 "\n", ipui->pun.o.number);
return;
case DECT_IPUI_P:
case DECT_IPUI_Q:
case DECT_IPUI_R:
case DECT_IPUI_S:
case DECT_IPUI_T:
case DECT_IPUI_U:
default:
sfmt_debug("\tIPUI: unhandled type %u\n", ipui->put);
}
}
bool dect_parse_ipui(struct dect_ipui *ipui, const uint8_t *ptr, uint8_t len)
{
uint64_t tmp;
if (len < 4)
return false;
tmp = __be64_to_cpu(*(__be64 *)&ptr[0]);
ipui->put = ptr[0] & DECT_IPUI_PUT_MASK;
switch (ipui->put) {
case DECT_IPUI_N:
if (len != 40)
return false;
return dect_parse_ipei(&ipui->pun.n.ipei, tmp >> 24);
case DECT_IPUI_O:
/* Shift away trailing bits */
tmp >>= 64 - len;
/* Clear PUT */
tmp &= ~(0xfULL << (len - 4));
ipui->pun.o.number = tmp;
return true;
case DECT_IPUI_P:
case DECT_IPUI_Q:
case DECT_IPUI_R:
case DECT_IPUI_S:
case DECT_IPUI_T:
case DECT_IPUI_U:
default:
sfmt_debug("\tIPUI: unhandled type %u\n", ipui->put);
return false;
}
}
uint8_t dect_build_ipui(uint8_t *ptr, const struct dect_ipui *ipui)
{
unsigned int i, len;
uint64_t tmp;
switch (ipui->put) {
case DECT_IPUI_N:
tmp = dect_build_ipei(&ipui->pun.n.ipei);
len = 36;
break;
case DECT_IPUI_O:
tmp = ipui->pun.o.number;
len = fls(tmp);
break;
case DECT_IPUI_P:
case DECT_IPUI_Q:
case DECT_IPUI_R:
case DECT_IPUI_S:
case DECT_IPUI_T:
case DECT_IPUI_U:
return 0;
default:
return 0;
}
if (len < 4)
tmp <<= 4 - len;
memset(ptr, 0, div_round_up(4 + len, 8));
ptr[0] = ipui->put;
for (i = 0; i < div_round_up(len, 8U); i++)
ptr[i] |= tmp >> (max(len, 4U) - 4 - 8 * i);
return 4 + len;
}
bool dect_ipui_cmp(const struct dect_ipui *i1, const struct dect_ipui *i2)
{
return memcmp(i1, i2, sizeof(*i1));
}
EXPORT_SYMBOL(dect_ipui_cmp);
struct dect_tpui *dect_ipui_to_tpui(struct dect_tpui *tpui,
const struct dect_ipui *ipui)
{
tpui->type = DECT_TPUI_INDIVIDUAL_DEFAULT;
switch (ipui->put) {
case DECT_IPUI_N:
tpui->id.ipui = ipui->pun.n.ipei.psn & DECT_TPUI_DEFAULT_INDIVIDUAL_IPUI_MASK;
break;
case DECT_IPUI_O:
case DECT_IPUI_P:
case DECT_IPUI_Q:
case DECT_IPUI_R:
case DECT_IPUI_S:
case DECT_IPUI_T:
case DECT_IPUI_U:
break;
}
return tpui;
}
void dect_dump_tpui(const struct dect_tpui *tpui)
{
unsigned int i;
switch (tpui->type) {
case DECT_TPUI_INDIVIDUAL_ASSIGNED:
sfmt_debug("\ttype: individual assigned\n");
sfmt_debug("\tdigits: ");
for (i = 0; i < 5; i++) {
if (tpui->ia.digits[i] != 0xb)
sfmt_debug("%u", tpui->ia.digits[i]);
}
sfmt_debug("\n");
return;
case DECT_TPUI_CONNECTIONLESS_GROUP:
sfmt_debug("\ttype: connectionless group\n");
return;
case DECT_TPUI_CALL_GROUP:
sfmt_debug("\ttype: call group\n");
return;
case DECT_TPUI_INDIVIDUAL_DEFAULT:
sfmt_debug("\ttype: individual default\n");
sfmt_debug("\tIPUI: %.4x\n", tpui->id.ipui);
return;
case DECT_TPUI_EMERGENCY:
sfmt_debug("\ttype: emergency\n");
return;
}
}
uint32_t dect_build_tpui(const struct dect_tpui *tpui)
{
uint32_t t = 0;
switch (tpui->type) {
case DECT_TPUI_INDIVIDUAL_ASSIGNED:
t = tpui->ia.digits[0] << 16;
t |= tpui->ia.digits[1] << 12;
t |= tpui->ia.digits[2] << 8;
t |= tpui->ia.digits[3] << 4;
t |= tpui->ia.digits[4] << 0;
break;
case DECT_TPUI_CONNECTIONLESS_GROUP:
t = DECT_TPUI_CONNECTIONLESS_GROUP_ID;
break;
case DECT_TPUI_CALL_GROUP:
t = DECT_TPUI_CALL_GROUP_ID;
break;
case DECT_TPUI_INDIVIDUAL_DEFAULT:
t = DECT_TPUI_DEFAULT_INDIVIDUAL_ID;
t |= tpui->id.ipui;
break;
case DECT_TPUI_EMERGENCY:
t = DECT_TPUI_EMERGENCY_ID;
break;
}
return t;
}
struct dect_pmid *dect_tpui_to_pmid(struct dect_pmid *pmid,
const struct dect_tpui *tpui)
{
uint32_t t;
t = dect_build_tpui(tpui);
switch (tpui->type) {
case DECT_TPUI_INDIVIDUAL_ASSIGNED:
pmid->type = DECT_PMID_ASSIGNED;
pmid->tpui = t & DECT_PMID_ASSIGNED_TPUI_MASK;
break;
case DECT_TPUI_EMERGENCY:
pmid->type = DECT_PMID_EMERGENCY;
pmid->tpui = t & DECT_PMID_EMERGENCY_TPUI_MASK;
break;
default:
BUG();
}
return pmid;
}
void dect_parse_pmid(struct dect_pmid *pmid, uint32_t p)
{
if ((p & DECT_PMID_DEFAULT_ID_MASK) == DECT_PMID_DEFAULT_ID) {
pmid->type = DECT_PMID_DEFAULT;
pmid->num = p & DECT_PMID_DEFAULT_NUM_MASK;
} else if ((p & DECT_PMID_EMERGENCY_ID_MASK) == DECT_PMID_EMERGENCY_ID) {
pmid->type = DECT_PMID_EMERGENCY;
pmid->tpui = p & DECT_PMID_EMERGENCY_TPUI_MASK;
} else {
pmid->type = DECT_PMID_ASSIGNED;
pmid->tpui = p & DECT_PMID_ASSIGNED_TPUI_MASK;
}
}
uint32_t dect_build_pmid(const struct dect_pmid *pmid)
{
uint32_t p = 0;
switch (pmid->type) {
case DECT_PMID_DEFAULT:
p |= DECT_PMID_DEFAULT_ID;
p |= pmid->num;
break;
case DECT_PMID_EMERGENCY:
p |= DECT_PMID_EMERGENCY_ID;
p |= pmid->tpui;
break;
case DECT_PMID_ASSIGNED:
p |= pmid->tpui;
break;
}
return p;
}
/** @} */