/* C-Netz Mobile User Part message coding * * (C) 2020 by Andreas Eversberg * All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "../libdebug/debug.h" #include "mup.h" #include "systemmeldungen.h" const char *einrichtrungstyp_string(uint8_t T) { static char unknown[4]; switch (T) { case 1: return "OGK"; case 2: return "SPK"; case 3: return "PHE"; case 4: return "PFG"; case 5: return "PBR"; case 6: return "FME"; case 8: return "DKV"; case 9: return "SAE"; case 10: return "FDS"; case 11: return "BS "; case 12: return "MSC"; case 13: return "OSK"; default: sprintf(unknown, "%03d", T); return unknown; } } static const char *version_string(uint8_t V) { switch (V) { case 0: return "undefined"; case 1: return "LM3.6 BS"; case 2: return "LM4 BS"; case 3: return "LM5 BS"; case 4: return "LM6 BS"; case 5: return "LM7 BS"; case 6: return "LM8 BS"; } return "unknown"; } static const char *chip_string(uint8_t C) { switch (C) { case 0: return "chipfunction off"; case 1: return "chipfunction on"; } return "unknown"; } static const char *beacon_string(uint8_t B) { switch (B) { case 0: return "normal BS"; case 1: return "stand alone beacon"; case 2: return "low power beacon CCH pair 2 exclusively"; case 3: return "low power beacon CCH pair 3 exclusively"; case 4: return "low power beacon"; } return "unknown"; } static const char *qualitaet_string(uint8_t Q) { switch (Q) { case 0: return "unsichere Angabe"; case 1: return "sichere Angabe"; } return "unknown"; } static const char *wiederholung_string(uint8_t N) { switch (N) { case 0: return "keine Wiederholung"; case 1: return "Wiederholung"; } return "unknown"; } static const char *woche_string(uint8_t Q) { switch (Q) { case 1: return "Sonntag"; case 2: return "Montag"; case 3: return "Dienstag"; case 4: return "Mittwoch"; case 5: return "Donnerstag"; case 6: return "Freitag"; case 7: return "Samstag"; } return "unknown"; } static const char *aktivdatei_string(uint8_t A) { switch (A) { case 0: return "wurde gesendet"; case 1: return "wurde nicht gesendet"; } return "unknown"; } static const char *prio_string(uint8_t P) { switch (P) { case 0: return "Verbindung ohne Prioritaet"; case 1: return "Verbindung mit Prioritaet"; } return "unknown"; } static const char *rufzeit_string(uint8_t F) { switch (F) { case 0: return "Begrenzung durch SPK"; case 1: return "Begrenzung durch MSC"; } return "unknown"; } static const char *typ_string(uint8_t V) { switch (V) { case 0: return "gehender Verbindungs-Aufbau"; case 1: return "kommender Verbindungs-Aufbau"; } return "unknown"; } static const char *fufst_cause(uint8_t X) { switch (X) { case 0: return "undefiniert"; case 1: return "Einh.A (A-BS)"; case 2: return "Einh.B (B-BS)"; case 3: return "Tln-besetzt (A-BS)"; case 4: return "Gassenbesetzt"; case 5: return "Time-Out: kein Sprechkanal"; case 6: return "Time-Out: kein Melden B (A-BS)"; case 7: return "Funk-Tln nicht verbindungsberechtigt"; case 8: return "WS ist blockiert (Time out)"; case 9: return "Funk-Tln nicht aktiv"; case 10: return "Funk-Tln nicht erreichbar"; case 11: return "Time-Out: keine Reaktion von der MSC"; case 12: return "Umbuchung in WS oder gegenlaeufige Einbuchung"; case 13: return "ungueltige Wahl"; case 17: return "Time out: Gespraechszeitbegrenzung (BS)"; case 18: return "Qualitaet Sprechkanal (Funkverbindung MS->BS)"; case 19: return "Qualitaet Fu-Tln-Geraet (Funkverbindung BS->MS)"; case 20: return "Signalisierungsverlust Sprechkanal (MS->BS)"; case 21: return "Signalisierungsverlust Fu-Tln-Geraet (BS-MS)"; case 22: return "Sprechkanal-Funktionsstoerung (HW-/SW Fehler)"; case 23: return "Umschalten"; case 24: return "Authentifikation negativ"; case 25: return "Stoerung Randomzahluebertragung"; case 27: return "Kein Melden B (B-BS), Infobox aktiviert"; case 26: return "Stoerung Autorisierungsparameteruebertragung"; } return "unknown"; } static const char *futln_cause(uint8_t Y) { switch (Y) { case 0: return "Gassenbesetzt"; case 1: return "Teilnehmer-besetzt"; case 2: return "Funktechnisch"; case 3: return "Timeout"; case 4: return "ungueltige Wahl"; } return "unknown"; } /* * message coding and decoding */ /* base station boots */ void decode_swaf(uint8_t *data, int len, uint8_t *V, uint8_t *N, uint8_t *U, uint8_t *F, uint8_t *C, uint8_t *B) { if (len < 6) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *V = data[0]; *N = data[1]; *U = data[2]; *F = data[3]; *C = data[4]; *B = data[5]; PDEBUG(DMUP, DEBUG_INFO, "(BS SWAF) Wiederanlauf der BS: version=%d (%s) FuFSt=%d,%d,%d chip=%d (%s) beacon=%d (%s)\n", *V, version_string(*V), *N, *U, *F, *C, chip_string(*C), *B, beacon_string(*B)); } /* ack to base station boot */ int encode_swqu(uint8_t *opcode, uint8_t **data, uint8_t A) { static uint8_t buffer[1]; PDEBUG(DMUP, DEBUG_INFO, "(MSC SWQU) Wiederanlaufquittung des MSC: aktivdatei=%d (%s)\n", A, aktivdatei_string(A)); *opcode = OPCODE_SWQU; buffer[0] = A; *data = buffer; return sizeof(buffer); } /* base station requests time */ void decode_suaf(uint8_t *data, int len, uint8_t *V, uint8_t *N, uint8_t *U, uint8_t *F, uint8_t *C, uint8_t *B) { if (len < 6) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *V = data[0]; *N = data[1]; *U = data[2]; *F = data[3]; *C = data[4]; *B = data[5]; PDEBUG(DMUP, DEBUG_INFO, "(BS SUAF) Datum-Uhrzeit-Anforderung der BS: version=%d (%s) FuFSt=%d,%d,%d chip=%d (%s) beacon=%d (%s)\n", *V, version_string(*V), *N, *U, *F, *C, chip_string(*C), *B, beacon_string(*B)); } /* ack to time request */ int encode_suqu(uint8_t *opcode, uint8_t **data, uint8_t Q, uint8_t N, time_t now) { static uint8_t buffer[8]; struct tm *tm; uint8_t R, W, D, M, J, h, m, s; tm = localtime(&now); R = 0x00; W = tm->tm_wday + 1; /* 1 = Sonntag */ D = tm->tm_mday; M = tm->tm_mon + 1; J = tm->tm_year % 100; /* Erlaubt: 0--99 */ h = tm->tm_hour; m = tm->tm_min; s = tm->tm_sec; PDEBUG(DMUP, DEBUG_INFO, "(MSC SUQU) Datum-Uhrzeit-Quittung des MSC: Q=%d (%s) Widerholung=%d (%s) Wochentag=%d (%s) Datum: %d.%d.%d %d:%02d:%02d\n", Q, qualitaet_string(Q), N, wiederholung_string(N), W, woche_string(W), D, M, J, h, m, s); *opcode = OPCODE_SUQU; buffer[0] = Q | (N << 1) | (R << 2); buffer[1] = W; buffer[2] = D; buffer[3] = M; buffer[4] = J; buffer[5] = h; buffer[6] = m; buffer[7] = s; *data = buffer; return sizeof(buffer); } /* base station lists voice channels */ void decode_sssaf(uint8_t *data, int len) { uint8_t E_; uint16_t A, E; uint8_t S = 0xff, last_S = 0xff; int i, start_i = 0, stop_i = 0; if (len < 11) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } E_ = data[0] >> 7; A = data[1] | (data[2] << 8); E = data[3] | (data[4] << 8); PDEBUG(DMUP, DEBUG_INFO, "(BS SSSAF) Sprechkanal-Sammel-Sperrauftrag der BS: Liste-Ende=%d Anfang=%d Ende=%d\n", E_, A, E); if (E - A + 1 > 6 * 8) { PDEBUG(DMUP, DEBUG_INFO, " -> Bereich zu gross für Nachricht!\n"); return; } if ((int)E - (int)A < 0) return; /* count number of channels + 1, so that we can output the last range at the end */ for (i = 0; i <= E - A + 1; i++) { /* after all loop turns (A..E), output last range */ if (i == E - A + 1) goto end; S = (data[5 + (i >> 3)] >> (i & 7)) & 1; /* set start of range if first channel */ if (i == 0) start_i = i; /* set stop of range, if first channel OR same state as last channel OR for last channel */ if (i == 0 || S == last_S) stop_i = i; /* output range if we have a change in channel or is we reached the last channels */ if (i > 0 && S != last_S) { end: if (start_i == stop_i) PDEBUG(DMUP, DEBUG_INFO, " -> SpK #%d=%d (%s)\n", start_i + A, last_S, (last_S) ? "gesperrt" : "frei"); else PDEBUG(DMUP, DEBUG_INFO, " -> SpK #%d..%d=%d (%s)\n", start_i + A, stop_i + A, last_S, (last_S) ? "gesperrt" : "frei"); /* new start */ start_i = stop_i = i; } last_S = S; } } /* ack voice channel list */ void encode_sssqu(uint8_t *opcode) { *opcode = OPCODE_SSSQU; PDEBUG(DMUP, DEBUG_INFO, "(MSC SSSQU) Sprechkanal-Sammel-Sperrquittung des MSC\n"); } /* base station locks a voice channel */ void decode_ssaf(uint8_t *data, int len, uint8_t *S) { if (len < 1) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *S = data[0]; PDEBUG(DMUP, DEBUG_INFO, "(BS SSAF) Sprechkanal-Sperr-Auftrag der BS: SPK=%d\n", *S); } /* ack to lockeed voice channel */ int encode_ssqu(uint8_t *opcode, uint8_t **data, uint8_t S) { static uint8_t buffer[1]; PDEBUG(DMUP, DEBUG_INFO, "(MSC SSQU) Sprechkanal-Sperr-Quittung von der MSC: SPK=%d\n", S); *opcode = OPCODE_SSQU; buffer[0] = S; *data = buffer; return sizeof(buffer); } /* base station unlocks a voice channel */ void decode_sfaf(uint8_t *data, int len, uint8_t *S) { if (len < 1) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *S = data[0]; PDEBUG(DMUP, DEBUG_INFO, "(BS SFAF) Sprechkanal-Freigabe-Auftrag der BS: SPK=%d\n", *S); } /* ack to unlockeed voice channel */ int encode_sfqu(uint8_t *opcode, uint8_t **data, uint8_t S) { static uint8_t buffer[1]; PDEBUG(DMUP, DEBUG_INFO, "(MSC SFQU) Sprechkanal-Freigabe-Quittung von der MSC: SPK=%d\n", S); *opcode = OPCODE_SFQU; buffer[0] = S; *data = buffer; return sizeof(buffer); } /* base station ready */ void decode_svaf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS SVAF) Vermittlungsfaehig-Auftrag der BS\n"); } /* ack to base station ready */ int encode_svqu(uint8_t *opcode, uint8_t **data) { PDEBUG(DMUP, DEBUG_INFO, "(MSC SVQU) Vermittlungsfaehig-Quittung des MSC\n"); *opcode = OPCODE_SVQU; *data = NULL; return 0; } /* base station requests alarm messages */ void decode_ylsaf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS YLSAF) Systemmeldungsanforderung an MSC\n"); } /* ack to base stations alarm request */ int encode_ylsmu(uint8_t *opcode, uint8_t **data) { PDEBUG(DMUP, DEBUG_INFO, "(MSC YLSMU) Systemmeldungsbestaetigung vom MSC\n"); *opcode = OPCODE_YLSMU; *data = NULL; return 0; } /* base station sends alarm message */ void decode_ylsmf(uint8_t *data, int len, uint8_t *N, uint8_t *C, struct SysMeld *SM) { if (len < 9) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *N = data[0]; *C = data[8]; switch (*N) { case 1: memset(SM, 0, sizeof(*SM)); SM->FUKO = data[1] << 8; SM->FUKO |= data[2]; SM->Monat = (data[3] >> 4) * 10 + (data[3] & 0xf); SM->Tag = (data[4] >> 4) * 10 + (data[4] & 0xf); SM->Stunde = (data[5] >> 4) * 10 + (data[5] & 0xf); SM->Minute = (data[6] >> 4) * 10 + (data[6] & 0xf); SM->Kennzeichen_posthum = data[7]; break; case 2: SM->Systemmeldungsnr = data[1] << 8; SM->Systemmeldungsnr |= data[2]; SM->Indizienlaenge = data[3]; SM->Indizien[0] = data[4]; SM->Indizien[1] = data[5]; SM->Indizien[2] = data[6]; SM->Indizien[3] = data[7]; break; case 3: SM->Indizien[4] = data[1]; SM->Indizien[5] = data[2]; SM->Indizien[6] = data[3]; SM->Indizien[7] = data[4]; SM->Indizien[8] = data[5]; SM->Indizien[9] = data[6]; SM->ASCII_Typ = data[7]; break; case 4: SM->Einrichtungstyp = data[1]; SM->Einrichtungsnr = data[2]; SM->Zusatzinfo[0] = data[3]; SM->Zusatzinfo[1] = data[4]; SM->Zusatzinfo[2] = data[5]; SM->Zusatzinfo[3] = data[6]; break; } if (*N != 4) return; char indizien[24] = " "; int i, ii; ii = SM->Indizienlaenge; if (ii > 10) ii = 10; for (i = 0; i < ii; i++) { sprintf(indizien + (i * 2) + (i > 3) + (i > 7), "%02X", SM->Indizien[i]); indizien[strlen(indizien)] = ' '; } PDEBUG(DMUP, DEBUG_INFO, "SM: %03d %02d.%02d %02d:%02d %s%02d %c H\"%04X %02d H\"%sH\"%02X%02X%02X%02X\n", *C, SM->Monat, SM->Tag, SM->Stunde, SM->Minute, einrichtrungstyp_string(SM->Einrichtungstyp), SM->Einrichtungsnr, SM->ASCII_Typ ? : '0', SM->Systemmeldungsnr, SM->Indizienlaenge, indizien, SM->Zusatzinfo[0], SM->Zusatzinfo[1], SM->Zusatzinfo[2], SM->Zusatzinfo[3]); print_systemmeldung(SM->Systemmeldungsnr, SM->Indizienlaenge, SM->Indizien); } /* base station ends list of alarm messages */ void decode_ylsef(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS YLSEF) Systemmeldungsuebertragungsende an MSC\n"); } /* base station requests billing info */ void decode_stdaf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS STDAF) Tarifdatenauftrag der BS\n"); } /* reply to billing info */ int encode_xgtau(uint8_t *opcode, uint8_t **data, uint8_t Z, uint32_t T, uint8_t S, uint8_t K, uint16_t CS) { static uint8_t buffer[9]; // Example from UeLE-ROM = { 0xff, 0x00, 0x01, 0xec, 0x3f, 0x01, 0x31, 0x1c, 0x03 }; // { 0xff, 0x00, 0x01, 0xec, 0x3f, 0x01, 0x41, 0x1c, 0x03 }; PDEBUG(DMUP, DEBUG_INFO, "(MSC XGTAU) Tarifdatensignalisierung vom MSC\n"); *opcode = OPCODE_XGTAU; buffer[0] = 0xff; buffer[1] = 0x00; buffer[2] = Z; buffer[3] = T; buffer[4] = T >> 8; buffer[5] = T >> 16; buffer[6] = S | (K << 4); buffer[7] = CS; buffer[8] = CS >> 8; *data = buffer; return sizeof(buffer); } /* inscription */ void decode_ebaf(uint8_t *data, int len, uint16_t *T, uint8_t *U, uint8_t *N, uint16_t *s, uint8_t *u, uint8_t *b, uint8_t *l) { if (len < 6) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *T = (data[1] << 8) | data[0]; *U = data[2] & 0x1f; *N = data[2] >> 5; *s = (data[4] << 8) | data[3]; *u = (data[5] >> 1) & 0x7; *b = (data[5] >> 6) & 0x1; *l = data[5] >> 7; PDEBUG(DMUP, DEBUG_INFO, "(BS EBAF) Einbuchauftrag: FuTln=%d,%d,%d (0161-%d%d%05d)\n", *N, *U, *T, *N, *U, *T); } /* ack to inscription */ int encode_ebpqu(uint8_t *opcode, uint8_t **data) { PDEBUG(DMUP, DEBUG_INFO, "(MSC EBPQU) Einbuchungs-Positiv-Quittiung vom MSC\n"); *opcode = OPCODE_EBPQU; *data = NULL; return 0; } /* leave BS */ void decode_abaf(uint8_t *data, int len, uint16_t *T, uint8_t *U, uint8_t *N) { if (len < 3) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *T = (data[1] << 8) | data[0]; *U = data[2] & 0x1f; *N = data[2] >> 5; PDEBUG(DMUP, DEBUG_INFO, "(BS ABAF) Ausbuchung-Auftrag der BS: FuTln=%d,%d,%d (0161-%d%d%05d)\n", *N, *U, *T, *N, *U, *T); } static char digit2char[16] = "0123456789a*#bcd"; /* MO call */ void _decode_outgoing(uint8_t *data, int len, uint16_t *T, uint8_t *U, uint8_t *N, char *number) { int i; if (len < 11) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *T = (data[1] << 8) | data[0]; *U = data[2] & 0x1f; *N = data[2] >> 5; len = data[3] & 0xf; if (len == 0) { /* 16 digits, starting with 0 */ for (i = 0; i < 16; i++) { if ((i & 1) == 0) number[i] = digit2char[(data[3 + (i >> 1)] & 0xf)]; else number[i] = digit2char[(data[3 + (i >> 1)] >> 4)]; } number[16] = '\0'; } else { /* up to 16 digits */ for (i = 0; i < len; i++) { if ((i & 1) == 0) number[i] = digit2char[(data[3 + ((i + 1) >> 1)] >> 4)]; else number[i] = digit2char[(data[3 + ((i + 1) >> 1)] & 0xf)]; } number[len] = '\0'; } } void decode_gvaf(uint8_t *data, int len, uint16_t *T, uint8_t *U, uint8_t *N, char *number) { _decode_outgoing(data, len, T, U , N, number); PDEBUG(DMUP, DEBUG_INFO, "(BS GVAF) Gehender Verbindungs-Auftrag der BS: FuTln=%d,%d,%d (0161-%d%d%05d) number=%s\n", *N, *U, *T, *N, *U, *T, number); } void decode_gvwaf(uint8_t *data, int len, uint16_t *T, uint8_t *U, uint8_t *N, char *number) { _decode_outgoing(data, len, T, U , N, number); PDEBUG(DMUP, DEBUG_INFO, "(BS GVWAF) Gehender Verbindungs-Warteschlange-Auftrag der BS: FuTln=%d,%d,%d (0161-%d%d%05d) number=%s\n", *N, *U, *T, *N, *U, *T, number); } /* ack to MO call */ int encode_gvpqu(uint8_t *opcode, uint8_t **data, uint8_t P, uint8_t e) { static uint8_t buffer[2]; PDEBUG(DMUP, DEBUG_INFO, "(MSC GVPQU) Verbindungs-Positiv-Quittiung vom MSC: Prio=%d (%s) AP-Pruefung=%d\n", P, prio_string(P), e); *opcode = OPCODE_GVNQU; buffer[0] = P; buffer[1] = e; *data = buffer; return sizeof(buffer); } /* nack to MO call */ int encode_gvnqu(uint8_t *opcode, uint8_t **data, uint8_t X, uint8_t Y) { static uint8_t buffer[2]; PDEBUG(DMUP, DEBUG_INFO, "(MSC GVNQU) Verbindungs-Negativ-Quittiung vom MSC: Grund=%d (%s) Grund(FuTlg)=%d (%s)\n", X, fufst_cause(X), Y, futln_cause(Y)); *opcode = OPCODE_GVNQU; buffer[0] = X; buffer[1] = Y; *data = buffer; return sizeof(buffer); } /* MT call */ int encode_kvau(uint8_t *opcode, uint8_t **data, uint16_t T, uint8_t U, uint8_t N, uint8_t F, uint8_t e) { static uint8_t buffer[5]; PDEBUG(DMUP, DEBUG_INFO, "(MSC KVAU) Kommender Verbindungs-Auftrag vom MSC: FuTln=%d,%d,%d (0161-%d%d%05d) Rufzeitbegrenzung=%d (%s) AP-Pruefung=%d\n", N, U, T, N, U, T, F, rufzeit_string(F), e); *opcode = OPCODE_KVAU; buffer[0] = T; buffer[1] = T >> 8; buffer[2] = U | (N << 5); buffer[3] = F; buffer[4] = e; *data = buffer; return sizeof(buffer); } /* ack to MT call on queue */ void decode_kvwqf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS KVWQF) Kommende Verbindungs-Warteschalngen-Quittung der BS\n"); } /* answer of MT call */ void decode_kvbaf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS KVBAF) Kommende Verbindungs-Beginn-Auftrag der BS\n"); } /* loop test request */ void decode_staf(uint8_t *data, int len, uint8_t *Q, uint8_t *V, uint8_t *e, uint64_t *n) { if (len < 10) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *Q = data[0]; *V = data[1] & 1; *e = (data[1] >> 1) & 1; *n = (uint64_t)data[2]; *n |= (uint64_t)data[3] << 8; *n |= (uint64_t)data[4] << 16; *n |= (uint64_t)data[5] << 24; *n |= (uint64_t)data[6] << 32; *n |= (uint64_t)data[7] << 40; *n |= (uint64_t)data[8] << 48; *n |= (uint64_t)data[9] << 56; PDEBUG(DMUP, DEBUG_INFO, "(BS STAF) Schleifentest-Auftrag der BS: SPK=%d Typ=%d (%s) AP-Pruefung=%d Random=0x%016" PRIx64 "\n", *Q, *V, typ_string(*V), *e, *n); } /* loop test positive */ int encode_stpqu(uint8_t *opcode, uint8_t **data, uint8_t Q, uint8_t A, uint8_t K, uint16_t G, uint8_t U, uint8_t X, uint8_t Y, uint8_t mystery) { static uint8_t buffer[7]; PDEBUG(DMUP, DEBUG_INFO, "(MSC STPQU) Schleifentest-Positiv-Quittiung vom MSC: SPK=%d\n", Q); *opcode = OPCODE_STPQU; buffer[0] = Q; buffer[1] = A; buffer[2] = K; buffer[3] = G; buffer[4] = G >> 8; buffer[5] = (X << 7) | U; buffer[6] = (Y << 7) | (mystery << 1); *data = buffer; return sizeof(buffer); } /* loop test negative */ int encode_stnqu(uint8_t *opcode, uint8_t **data, uint8_t Q) { static uint8_t buffer[1]; PDEBUG(DMUP, DEBUG_INFO, "(MSC STNQU) Schleifentest-Negativ-Quittiung vom MSC: SPK=%d\n", Q); *opcode = OPCODE_STNQU; buffer[0] = Q; *data = buffer; return sizeof(buffer); } /* authentication response */ void decode_apf(uint8_t *data, int len, uint8_t *Q, uint64_t *a) { if (len < 9) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *Q = data[0]; *a = (uint64_t)data[1]; *a |= (uint64_t)data[2] << 8; *a |= (uint64_t)data[3] << 16; *a |= (uint64_t)data[4] << 24; *a |= (uint64_t)data[5] << 32; *a |= (uint64_t)data[6] << 40; *a |= (uint64_t)data[7] << 48; *a |= (uint64_t)data[8] << 56; PDEBUG(DMUP, DEBUG_INFO, "(BS APF) Autorisierunsparameter FUKO: SPK=%d AP=0x%016" PRIx64 "\n", *Q, *a); } /* start metering pulses (answer to call) */ int encode_gstau(uint8_t *opcode, uint8_t **data, uint8_t Q, uint16_t G, uint8_t U, uint8_t Y, uint8_t A, uint8_t K) { static uint8_t buffer[6]; PDEBUG(DMUP, DEBUG_INFO, "(MSC GSTAU) Gebuehren-Start-Auftrag vom MSC: SPK=%d\n", Q); *opcode = OPCODE_GSTAU; buffer[0] = Q; buffer[1] = G; buffer[2] = G >> 8; buffer[3] = (Y << 7) | U; buffer[4] = A; buffer[5] = K; *data = buffer; return sizeof(buffer); } /* MCID */ void decode_faf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS FAF) Fang-Auftrag der BS\n"); } /* release by base station (before SPK assignment) */ void decode_naf(uint8_t *data, int len, uint8_t *X) { if (len < 1) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *X = data[0]; PDEBUG(DMUP, DEBUG_INFO, "(BS AAF) Negativ-Auftrag der BS: Grund=%d (%s)\n", *X, fufst_cause(*X)); } /* release by base station ack (before SPK assignment) */ int encode_equ(uint8_t *opcode, uint8_t **data) { PDEBUG(DMUP, DEBUG_INFO, "(MSC AQU) Ende-Quittung vom MSC\n"); *opcode = OPCODE_EQU; *data = NULL; return 0; } /* release by base station (after SPK assignment) */ void decode_aaf(uint8_t *data, int len, uint8_t *Q, uint8_t *X) { if (len < 2) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *Q = data[0]; *X = data[1]; PDEBUG(DMUP, DEBUG_INFO, "(BS AAF) Ausloese-Auftrag der BS: SPK=%d, Grund=%d (%s)\n", *Q, *X, fufst_cause(*X)); } /* release by base station ack (after SPK assignment) */ int encode_aqu(uint8_t *opcode, uint8_t **data, uint8_t Q) { static uint8_t buffer[1]; PDEBUG(DMUP, DEBUG_INFO, "(MSC AQU) Ausloese-Quittung vom MSC: SPK=%d\n", Q); *opcode = OPCODE_AQU; buffer[0] = Q; *data = buffer; return sizeof(buffer); } /* release by network (before SPK assignment) */ int encode_nau(uint8_t *opcode, uint8_t **data, uint8_t X, uint8_t Y) { static uint8_t buffer[2]; PDEBUG(DMUP, DEBUG_INFO, "(MSC NAU) Negativ-Auftrag vom MSC: Grund=%d (%s) Grund(FuTlg)=%d (%s)\n", X, fufst_cause(X), Y, futln_cause(Y)); *opcode = OPCODE_NAU; buffer[0] = X; buffer[1] = Y; *data = buffer; return sizeof(buffer); } /* release by network ack (before SPK assignment) */ void decode_eqf(uint8_t __attribute__((unused)) *data, int __attribute__((unused)) len) { PDEBUG(DMUP, DEBUG_INFO, "(BS EQF) Ende-Quittung der BS\n"); } /* release by network (after SPK assignment) */ int encode_aau(uint8_t *opcode, uint8_t **data, uint8_t Q, uint8_t X, uint8_t Y) { static uint8_t buffer[3]; PDEBUG(DMUP, DEBUG_INFO, "(MSC AAU) Ausloese-Auftrag vom MSC: SPK=%d, Grund=%d (%s) Grund(FuTlg)=%d (%s)\n", Q, X, fufst_cause(X), Y, futln_cause(Y)); *opcode = OPCODE_AAU; buffer[0] = Q; buffer[1] = X; buffer[2] = Y; *data = buffer; return sizeof(buffer); } /* release by network ack (after SPK assignment) */ void decode_aqf(uint8_t *data, int len, uint8_t *Q) { if (len < 1) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *Q = data[0]; PDEBUG(DMUP, DEBUG_INFO, "(BS AQF) Ausloese-Quittung der BS: SPK=%d\n", *Q); } /* request data base block */ void decode_xadbf(uint8_t *data, int len, uint8_t *PJ, uint16_t *D, uint16_t *L) { if (len < 6) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *PJ = data[1]; *D = data[2] | (data[3] << 8); *L = data[4] | (data[5] << 8); PDEBUG(DMUP, DEBUG_INFO, "(BS XADBF) Auftragssign. Anfordern BS-DB-Datenblock am MSC: job=%d, offset=0x%02x length=0x%02x\n", *PJ, *D, *L); } /* transfer data base block */ int encode_xedbu_1(uint8_t *opcode, uint8_t **data, uint8_t R, uint8_t PJ, uint16_t A) { static uint8_t buffer[4]; PDEBUG(DMUP, DEBUG_INFO, "(MSC XEDBU) Ergebnissignal. Transfer BS-DB-Datenblock (header): return=%d job=%d frames=%d\n", R, PJ, A); *opcode = OPCODE_XEDBU; buffer[0] = R; buffer[1] = PJ; buffer[2] = A; buffer[3] = A >> 8; *data = buffer; return sizeof(buffer); } int encode_xedbu_2(uint8_t *opcode, uint8_t **data, uint8_t S, uint8_t PJ, uint8_t *P) { static uint8_t buffer[11]; if (debuglevel == DEBUG_DEBUG) PDEBUG(DMUP, DEBUG_INFO, "(MSC XEDBU) Ergebnissignal. Transfer BS-DB-Datenblock (data): count=%d job=%d data=%s\n", S, PJ, debug_hex(P, 9)); else if (S == 1) PDEBUG(DMUP, DEBUG_INFO, "(MSC XEDBU) Ergebnissignal. Transfer BS-DB-Datenblock (data): Messages are not shown, due to heavy debug output!\n"); *opcode = OPCODE_XEDBU; buffer[0] = S; buffer[1] = PJ; memcpy(buffer + 2, P, 9); *data = buffer; return sizeof(buffer); } int encode_xedbu_3(uint8_t *opcode, uint8_t **data, uint8_t S, uint8_t PJ, uint16_t D, uint16_t L, uint32_t CS) { static uint8_t buffer[9]; PDEBUG(DMUP, DEBUG_INFO, "(MSC XEDBU) Ergebnissignal. Transfer BS-DB-Datenblock (footer): count=%d job=%d offset=0x%02x length=0x%02x checksum=0x%06x\n", S, PJ, D, L, CS); *opcode = OPCODE_XEDBU; buffer[0] = S; buffer[1] = PJ; buffer[2] = D; buffer[3] = D >> 8; buffer[4] = L; buffer[5] = L >> 8; buffer[6] = CS; buffer[7] = CS >> 8; buffer[8] = CS >> 16; *data = buffer; return sizeof(buffer); } /* BS reboot order */ int encode_yaaau(uint8_t *opcode, uint8_t **data, uint8_t J) { static uint8_t buffer[2]; PDEBUG(DMUP, DEBUG_INFO, "(MSC YAAAU) Auftrag Initialisieren BS des MSC: job=%d\n", J); *opcode = OPCODE_YAAAU; buffer[0] = 0xff; buffer[1] = J; *data = buffer; return sizeof(buffer); } /* MSC boots */ int encode_swau(uint8_t *opcode, uint8_t **data, uint8_t V) { static uint8_t buffer[1]; PDEBUG(DMUP, DEBUG_INFO, "(MSC SWAU) Wiederanlaufauftrag des MSC: version=%d (%s)\n", V, version_string(V)); *opcode = OPCODE_SWAU; buffer[0] = V; *data = buffer; return sizeof(buffer); } /* ack to MSC boot */ void decode_swqf(uint8_t *data, int len, uint8_t *V, uint8_t *N, uint8_t *U, uint8_t *F, uint8_t *C, uint8_t *B) { if (len < 6) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return; } *V = data[0]; *N = data[1]; *U = data[2]; *F = data[3]; *C = data[4]; *B = data[5]; PDEBUG(DMUP, DEBUG_INFO, "(BS SWQF) Wiederanlauf-Quittung der BS: version=%d (%s) FuFSt=%d,%d,%d chip=%d (%s) beacon=%d (%s)\n", *V, version_string(*V), *N, *U, *F, *C, chip_string(*C), *B, beacon_string(*B)); } /* request "Aktivdatei" (inscripted substribers) */ void encode_sadau(uint8_t *opcode) { *opcode = OPCODE_SADAU; PDEBUG(DMUP, DEBUG_INFO, "(MSC SADAU) Aktivdatei-Auftrag vom MSC\n"); } /* ack "Aktivdatei" */ int decode_sadqf(uint8_t *data, int len, uint16_t *S, uint8_t *E, uint8_t *l, uint16_t *T, uint8_t *U, uint8_t *N) { int i, n = 0; if (len < 11) { PDEBUG(DMUP, DEBUG_NOTICE, "Message too short!\n"); return 0; } *S = ((data[1] & 0xf) << 4) | data[0]; *E = data[1] >> 7; for (i = 0; i < 3; i++) { l[n] = (data[1] >> (6 - i)) & 0x1; T[n] = (data[3 + (i * 3)] << 8) | data[2 + (i * 3)]; U[n] = data[4 + (i * 3)] & 0x1f; N[n] = data[4 + (i * 3)] >> 5; if (T[n] != 0 || U[n] != 0 || N[n] != 0) n++; } PDEBUG(DMUP, DEBUG_INFO, "(BS SADQF) Aktivdateiquittung der BS:\n"); for (i = 0; i < n; i++) PDEBUG(DMUP, DEBUG_INFO, " %d: FuTln=%d,%d,%d (0161-%d%d%05d)\n", i + 1, N[i], U[i], T[i], N[i], U[i], T[i]); return n; }