457 lines
10 KiB
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;
|
|
}
|
|
|
|
/** @} */
|