NMT / SMS: Short Message Service support
This commit is contained in:
parent
64c829909b
commit
e7fa08b6df
|
@ -28,3 +28,4 @@ src/amps/amps
|
|||
src/test/test_compandor
|
||||
src/test/test_emphasis
|
||||
src/test/test_dms
|
||||
src/test/test_sms
|
||||
|
|
|
@ -51,6 +51,7 @@ struct debug_cat {
|
|||
{ "database", "\033[0;33m" },
|
||||
{ "transaction", "\033[0;32m" },
|
||||
{ "dms", "\033[0;33m" },
|
||||
{ "sms", "\033[1;37m" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#define DDB 11
|
||||
#define DTRANS 12
|
||||
#define DDMS 13
|
||||
#define DSMS 14
|
||||
|
||||
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, fmt, ## arg)
|
||||
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *fmt, ...);
|
||||
|
|
|
@ -8,6 +8,7 @@ nmt_SOURCES = \
|
|||
dsp.c \
|
||||
frame.c \
|
||||
dms.c \
|
||||
sms.c \
|
||||
image.c \
|
||||
tones.c \
|
||||
announcement.c \
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "../common/main.h"
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
|
@ -36,6 +40,9 @@
|
|||
#include "tones.h"
|
||||
#include "announcement.h"
|
||||
|
||||
#define SMS_FIFO "/tmp/nmt_sms_deliver"
|
||||
static int sms_fd = -1;
|
||||
|
||||
/* settings */
|
||||
int num_chan_type = 0;
|
||||
enum nmt_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_TC };
|
||||
|
@ -44,6 +51,7 @@ char traffic_area[3] = "";
|
|||
char area_no = 0;
|
||||
int compandor = 1;
|
||||
int supervisory = 0;
|
||||
const char *smsc_number = "767";
|
||||
|
||||
void print_help(const char *arg0)
|
||||
{
|
||||
|
@ -69,6 +77,9 @@ void print_help(const char *arg0)
|
|||
printf(" -0 --supervisory 1..4 | 0\n");
|
||||
printf(" Use supervisory signal 1..4 to detect loss of signal from mobile\n");
|
||||
printf(" station, use 0 to disable. (default = '%d')\n", supervisory);
|
||||
printf(" -S --smsc-number <digits>\n");
|
||||
printf(" If this number is dialed, the mobile is connected to the SMSC (Short\n");
|
||||
printf(" Message Service Center). (default = '%s')\n", smsc_number);
|
||||
printf("\nstation-id: Give 7 digits of station-id, you don't need to enter it\n");
|
||||
printf(" for every start of this program.\n");
|
||||
}
|
||||
|
@ -85,10 +96,11 @@ static int handle_options(int argc, char **argv)
|
|||
{"traffic-area", 1, 0, 'y'},
|
||||
{"compandor", 1, 0, 'C'},
|
||||
{"supervisory", 1, 0, '0'},
|
||||
{"smsc-number", 1, 0, 'S'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
set_options_common("t:P:a:y:C:0:", long_options_special);
|
||||
set_options_common("t:P:a:y:C:0:S:", long_options_special);
|
||||
|
||||
while (1) {
|
||||
int option_index = 0, c, rc;
|
||||
|
@ -176,6 +188,10 @@ error_ta:
|
|||
}
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'S':
|
||||
smsc_number = strdup(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
default:
|
||||
opt_switch_common(c, argv[0], &skip_args);
|
||||
}
|
||||
|
@ -186,6 +202,33 @@ error_ta:
|
|||
return skip_args;
|
||||
}
|
||||
|
||||
static void myhandler(void)
|
||||
{
|
||||
static char buffer[256];
|
||||
static int pos = 0, rc, i;
|
||||
int space = sizeof(buffer) - pos;
|
||||
|
||||
rc = read(sms_fd, buffer + pos, space);
|
||||
if (rc > 0) {
|
||||
pos += rc;
|
||||
if (pos == space) {
|
||||
fprintf(stderr, "SMS buffer overflow!\n");
|
||||
pos = 0;
|
||||
}
|
||||
/* check for end of line */
|
||||
for (i = 0; i < pos; i++) {
|
||||
if (buffer[i] == '\r' || buffer[i] == '\n')
|
||||
break;
|
||||
}
|
||||
/* send sms */
|
||||
if (i < pos) {
|
||||
buffer[i] = '\0';
|
||||
pos = 0;
|
||||
deliver_sms(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
|
@ -237,6 +280,20 @@ int main(int argc, char *argv[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* create pipe for SMS delivery */
|
||||
unlink(SMS_FIFO);
|
||||
rc = mkfifo(SMS_FIFO, 0666);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to create SMS deliver FIFO!\n");
|
||||
goto fail;
|
||||
} else {
|
||||
sms_fd = open(SMS_FIFO, O_RDONLY | O_NONBLOCK);
|
||||
if (sms_fd < 0) {
|
||||
fprintf(stderr, "Failed to open SMS deliver FIFO!\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loopback)
|
||||
print_image();
|
||||
|
||||
|
@ -258,7 +315,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
/* create transceiver instance */
|
||||
for (i = 0; i < num_kanal; i++) {
|
||||
rc = nmt_create(kanal[i], (loopback) ? CHAN_TYPE_TEST : chan_type[i], sounddev[i], samplerate, cross_channels, rx_gain, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, ms_power, nmt_digits2value(traffic_area, 2), area_no, compandor, supervisory, loopback);
|
||||
rc = nmt_create(kanal[i], (loopback) ? CHAN_TYPE_TEST : chan_type[i], sounddev[i], samplerate, cross_channels, rx_gain, do_pre_emphasis, do_de_emphasis, write_wave, read_wave, ms_power, nmt_digits2value(traffic_area, 2), area_no, compandor, supervisory, smsc_number, loopback);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to create transceiver instance. Quitting!\n");
|
||||
goto fail;
|
||||
|
@ -286,7 +343,7 @@ int main(int argc, char *argv[])
|
|||
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
|
||||
}
|
||||
|
||||
main_loop(&quit, latency, interval, NULL);
|
||||
main_loop(&quit, latency, interval, myhandler);
|
||||
|
||||
if (rt_prio > 0) {
|
||||
struct sched_param schedp;
|
||||
|
@ -297,6 +354,11 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
fail:
|
||||
/* fifo */
|
||||
if (sms_fd > 0)
|
||||
close(sms_fd);
|
||||
unlink(SMS_FIFO);
|
||||
|
||||
/* cleanup functions */
|
||||
call_cleanup();
|
||||
if (use_mncc_sock)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "../common/call.h"
|
||||
|
@ -30,6 +31,8 @@
|
|||
#include "dsp.h"
|
||||
#include "frame.h"
|
||||
|
||||
static int sms_ref = 0;
|
||||
|
||||
/* Call reference for calls from mobile station to network
|
||||
This offset of 0x400000000 is required for MNCC interface. */
|
||||
static int new_callref = 0x40000000;
|
||||
|
@ -283,7 +286,7 @@ static void nmt_timeout(struct timer *timer);
|
|||
static void nmt_go_idle(nmt_t *nmt);
|
||||
|
||||
/* Create transceiver instance and link to a list. */
|
||||
int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, int loopback)
|
||||
int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, const char *smsc_number, int loopback)
|
||||
{
|
||||
nmt_t *nmt;
|
||||
int rc;
|
||||
|
@ -337,13 +340,20 @@ int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* init audio processing */
|
||||
/* init DMS processing */
|
||||
rc = dms_init_sender(nmt);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DNMT, DEBUG_ERROR, "Failed to init DMS processing!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* init SMS processing */
|
||||
rc = sms_init_sender(nmt);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DNMT, DEBUG_ERROR, "Failed to init SMS processing!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
timer_init(&nmt->timer, nmt_timeout, nmt);
|
||||
nmt->sysinfo.chan_type = chan_type;
|
||||
nmt->sysinfo.ms_power = ms_power;
|
||||
|
@ -351,6 +361,7 @@ int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev,
|
|||
nmt->sysinfo.area_no = area_no;
|
||||
nmt->compandor = compandor;
|
||||
nmt->supervisory = supervisory;
|
||||
strncpy(nmt->smsc_number, smsc_number, sizeof(nmt->smsc_number) - 1);
|
||||
|
||||
/* go into idle state */
|
||||
nmt_go_idle(nmt);
|
||||
|
@ -371,6 +382,7 @@ void nmt_destroy(sender_t *sender)
|
|||
PDEBUG(DNMT, DEBUG_DEBUG, "Destroying 'NMT' instance for channel = %d.\n", sender->kanal);
|
||||
dsp_cleanup_sender(nmt);
|
||||
dms_cleanup_sender(nmt);
|
||||
sms_cleanup_sender(nmt);
|
||||
timer_exit(&nmt->timer);
|
||||
sender_destroy(&nmt->sender);
|
||||
free(nmt);
|
||||
|
@ -382,6 +394,8 @@ static void nmt_go_idle(nmt_t *nmt)
|
|||
timer_stop(&nmt->timer);
|
||||
nmt->page_for_nmt = NULL;
|
||||
nmt->dms_call = 0;
|
||||
dms_reset(nmt);
|
||||
sms_reset(nmt);
|
||||
|
||||
PDEBUG(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.chan_type));
|
||||
nmt_new_state(nmt, STATE_IDLE);
|
||||
|
@ -760,9 +774,9 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
|
|||
break;
|
||||
PDEBUG(DNMT, DEBUG_INFO, "Dialing complete %s->%s, call established.\n", &nmt->subscriber.country, nmt->dialing);
|
||||
/* setup call */
|
||||
if (!strcmp(nmt->dialing, "767")) {
|
||||
if (!strcmp(nmt->dialing, nmt->smsc_number)) {
|
||||
/* SMS */
|
||||
dms_reset(nmt);
|
||||
PDEBUG(DNMT, DEBUG_INFO, "Setup call to SMSC.\n");
|
||||
nmt->dms_call = 1;
|
||||
} else {
|
||||
int callref = ++new_callref;
|
||||
|
@ -980,6 +994,10 @@ static void tx_mt_complete(nmt_t *nmt, frame_t *frame)
|
|||
super_reset(nmt);
|
||||
timer_start(&nmt->timer, SUPERVISORY_TO1);
|
||||
}
|
||||
if (nmt->dms_call) {
|
||||
time_t ti = time(NULL);
|
||||
sms_deliver(nmt, sms_ref, "", SMS_TYPE_UKNOWN, SMS_PLAN_ISDN_TEL, ti, nmt->sms_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1365,7 +1383,7 @@ const char *nmt_get_frame(nmt_t *nmt)
|
|||
*/
|
||||
|
||||
/* Call control starts call towards mobile station. */
|
||||
int call_out_setup(int callref, char *dialing)
|
||||
int _out_setup(int callref, char *dialing, const char *sms)
|
||||
{
|
||||
sender_t *sender;
|
||||
nmt_t *nmt;
|
||||
|
@ -1413,10 +1431,22 @@ inval:
|
|||
|
||||
/* 4. trying to page mobile station */
|
||||
sender->callref = callref;
|
||||
if (sms) {
|
||||
strncpy(nmt->sms_string, sms, sizeof(nmt->sms_string) - 1);
|
||||
nmt->dms_call = 1;
|
||||
}
|
||||
nmt_page(nmt, ms_country, ms_number, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int call_out_setup(int callref, char *dialing)
|
||||
{
|
||||
return _out_setup(callref, dialing, NULL);
|
||||
}
|
||||
int sms_out_setup(char *dialing, const char *sms)
|
||||
{
|
||||
return _out_setup(0, dialing, sms);
|
||||
}
|
||||
|
||||
/* Call control sends disconnect (with tones).
|
||||
* An active call stays active, so tones and annoucements can be received
|
||||
|
@ -1519,3 +1549,61 @@ void call_rx_audio(int callref, int16_t *samples, int count)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SMS layer messages
|
||||
*/
|
||||
|
||||
/* SMS layer releases */
|
||||
void sms_release(nmt_t *nmt)
|
||||
{
|
||||
PDEBUG(DNMT, DEBUG_NOTICE, "Outgoing release, by SMS layer!\n");
|
||||
nmt_release(nmt);
|
||||
}
|
||||
|
||||
void sms_submit(nmt_t *nmt, uint8_t ref, const char *orig_address, uint8_t orig_type, uint8_t orig_plan, int msg_ref, const char *dest_address, uint8_t dest_type, uint8_t dest_plan, const char *message)
|
||||
{
|
||||
PDEBUG(DNMT, DEBUG_NOTICE, "Received SMS from '%s' to '%s'\n", orig_address, dest_address);
|
||||
printf("SMS received '%s' -> '%s': %s\n", orig_address, dest_address, message);
|
||||
|
||||
}
|
||||
|
||||
void sms_deliver_report(nmt_t *nmt, uint8_t ref, int error, uint8_t cause)
|
||||
{
|
||||
PDEBUG(DNMT, DEBUG_NOTICE, "Got SMS deliver report\n");
|
||||
if (error)
|
||||
printf("SMS failed! (cause=%d)\n", cause);
|
||||
else {
|
||||
sms_ref++;
|
||||
printf("SMS sent!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* application sends ud a message, we need to deliver */
|
||||
void deliver_sms(const char *sms)
|
||||
{
|
||||
int i, rc;
|
||||
char number[8];
|
||||
|
||||
/* check for number digits */
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (sms[i] < '0' || sms[i] > '9')
|
||||
break;
|
||||
}
|
||||
if (i < 7 || sms[7] != ',') {
|
||||
PDEBUG(DNMT, DEBUG_NOTICE, "Given SMS MUST start with the 7 digits phone number, followed by a comma (no spaces)\n");
|
||||
return;
|
||||
}
|
||||
strncpy(number, sms, 7);
|
||||
number[7] = '\0';
|
||||
sms += 8;
|
||||
PDEBUG(DNMT, DEBUG_INFO, "SMS for subscriber '%s'\n", number);
|
||||
printf("SMS sending SMSC -> '%s': %s\n", number, sms);
|
||||
|
||||
rc = sms_out_setup(number, sms);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DNMT, DEBUG_INFO, "SMS delivery failed with cause '%d'\n", -rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "../common/compandor.h"
|
||||
#include "../common/dtmf.h"
|
||||
#include "dms.h"
|
||||
#include "sms.h"
|
||||
|
||||
enum dsp_mode {
|
||||
DSP_MODE_DIALTONE, /* stream dial tone to mobile phone */
|
||||
|
@ -134,6 +135,12 @@ typedef struct nmt {
|
|||
/* DMS states */
|
||||
int dms_call; /* indicates that this call is a DMS call */
|
||||
dms_t dms; /* DMS states */
|
||||
|
||||
/* SMS states */
|
||||
char smsc_number[33]; /* digits to match SMSC */
|
||||
sms_t sms; /* SMS states */
|
||||
struct timer sms_timer;
|
||||
char sms_string[256]; /* current string to deliver */
|
||||
} nmt_t;
|
||||
|
||||
void nmt_channel_list(void);
|
||||
|
@ -143,9 +150,10 @@ const char *chan_type_long_name(enum nmt_chan_type chan_type);
|
|||
double nmt_channel2freq(int channel, int uplink);
|
||||
void nmt_country_list(void);
|
||||
uint8_t nmt_country_by_short_name(const char *short_name);
|
||||
int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, int loopback);
|
||||
int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev, int samplerate, int cross_channels, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, const char *smsc_number, int loopback);
|
||||
void nmt_destroy(sender_t *sender);
|
||||
void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double level, double frames_elapsed);
|
||||
const char *nmt_get_frame(nmt_t *nmt);
|
||||
void nmt_rx_super(nmt_t *nmt, int tone, double quality);
|
||||
void deliver_sms(const char *sms);
|
||||
|
||||
|
|
|
@ -0,0 +1,681 @@
|
|||
/* NMT SMS (short message service) processing
|
||||
*
|
||||
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "nmt.h"
|
||||
|
||||
#define SMS_RELEASE_TO 2.0
|
||||
|
||||
/* TP-Message-Type-Indicator (TP-MTI) */
|
||||
#define MTI_SMS_DELIVER 0x00 /* SC -> MS */
|
||||
#define MTI_SMS_DELIVER_REPORT 0x00 /* MS -> SC */
|
||||
#define MTI_SMS_STATUS_REPORT 0x02 /* SC -> MS */
|
||||
#define MTI_SMS_COMMAND 0x02 /* MS -> SC */
|
||||
#define MTI_SMS_SUBMIT 0x01 /* MS -> SC */
|
||||
#define MTI_SMS_SUBMIT_REPORT 0x01 /* SC -> MS */
|
||||
#define MTI_MASK 0x03 /* Bits 0 and 1 */
|
||||
|
||||
/* TP-More-Messages-to-Send (TP-MMS) */
|
||||
#define MMS_NORE 0x00
|
||||
#define MMS_NO_MORE 0x04
|
||||
#define MMS_MASK 0x04 /* Bit 2 */
|
||||
|
||||
/* TP-Validity-Period-Format (TP-VPF) */
|
||||
#define VPF_NOT_PRESENT 0x00
|
||||
#define VPF_PRESENT_INTEGER 0x10
|
||||
#define VPF_PRESENT_SEMI_OCTET 0x18
|
||||
#define VPF_MASK 0x18 /* Bits 3 and 4 */
|
||||
|
||||
/* TP-Status-Report-Indication (TP-SRI) */
|
||||
#define SRI_NO_REPORT 0x00
|
||||
#define SRI_REPORT 0x20
|
||||
#define SRI_MASK 0x20
|
||||
|
||||
/* TP-Status-Report-Request (TP-SRR) */
|
||||
#define SSR_NO_REPORT 0x00
|
||||
#define SSR_REPORT 0x20
|
||||
#define SSR_MASK 0x20
|
||||
|
||||
/* TP-Failure-Cause (TP-FCS) */
|
||||
#define FCS_BUSY 0xc0
|
||||
#define FCS_NO_SC_SUBSCRIPTION 0xc1
|
||||
#define FSC_SC_SYSTEM_FAILURE 0xC2
|
||||
#define FSC_DEST_SME_BARRED 0xC4
|
||||
#define FSC_ERROR_IN_MS 0xD2
|
||||
#define FSC_MEMORY_EXCEEDED 0xD3
|
||||
#define FSC_UNSPECIFIED_ERROR 0xFF
|
||||
|
||||
/* RP-Message-Type-Indicator (RP-MTI) */
|
||||
#define RP_MO_DATA 0x00 /* MS -> SC */
|
||||
#define RP_MT_DATA 0x01 /* SC -> MS */
|
||||
#define RP_MT_ACK 0x02 /* MS -> SC */
|
||||
#define RP_MO_ACK 0x03 /* SC -> MS */
|
||||
#define RP_MT_ERROR 0x04 /* MS -> SC */
|
||||
#define RP_MO_ERROR 0x05 /* SC -> MS */
|
||||
#define RP_SM_MEMORY_AVAILABLE 0x06 /* MS -> SC */
|
||||
#define RP_SM_READY_TO_RECEIVE 0x07 /* MS -> SC */
|
||||
#define RP_SM_NO_MESSAGE 0x07 /* SC -> MS */
|
||||
#define RP_MTI_MASK 0x07
|
||||
|
||||
/* RP IEs */
|
||||
#define RP_IE_USER_DATA 0x41 /* wrong in NMT Doc.450-3 1998-04-03 */
|
||||
#define RP_IE_CAUSE 0x42
|
||||
|
||||
/* SC -> MS header */
|
||||
static const char sms_header[] = {
|
||||
0x01, 0x18, 0x53, 0x4d, 0x53, 0x48, 0x18, 'A', 'B', 'C', 0x02
|
||||
};
|
||||
|
||||
/*
|
||||
* init and exit
|
||||
*/
|
||||
|
||||
static void sms_timeout(struct timer *timer);
|
||||
|
||||
/* init instance */
|
||||
int sms_init_sender(nmt_t *nmt)
|
||||
{
|
||||
timer_init(&nmt->sms_timer, sms_timeout, nmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup transceiver instance. */
|
||||
void sms_cleanup_sender(nmt_t *nmt)
|
||||
{
|
||||
timer_exit(&nmt->sms_timer);
|
||||
sms_reset(nmt);
|
||||
}
|
||||
|
||||
/*
|
||||
* send to lower layer
|
||||
*/
|
||||
|
||||
/* encode header */
|
||||
static int encode_header(uint8_t *data)
|
||||
{
|
||||
memcpy(data, sms_header, sizeof(sms_header));
|
||||
|
||||
return sizeof(sms_header);
|
||||
}
|
||||
|
||||
/* encode address fields */
|
||||
static int encode_address(uint8_t *data, const char *address, uint8_t type, uint8_t plan)
|
||||
{
|
||||
int length = 1;
|
||||
uint8_t digit;
|
||||
int i, j;
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Encode SC->MS header\n");
|
||||
|
||||
data[length++] = 0x80 | (type << 4) | plan;
|
||||
j = 0;
|
||||
for (i = 0; address[i]; i++) {
|
||||
if (address[i] >= '1' && address[i] <= '9')
|
||||
digit = address[i] - '0';
|
||||
else if (address[i] == '0')
|
||||
digit = 10;
|
||||
else if (address[i] == '*')
|
||||
digit = 11;
|
||||
else if (address[i] == '#')
|
||||
digit = 12;
|
||||
else if (address[i] == '+')
|
||||
digit = 13;
|
||||
else
|
||||
continue;
|
||||
if ((j & 1) == 0)
|
||||
data[length] = digit;
|
||||
else
|
||||
data[length++] |= digit << 4;
|
||||
j++;
|
||||
}
|
||||
if ((j & 1))
|
||||
data[length++] |= 0xf0;
|
||||
|
||||
/* length field: number of semi-octets */
|
||||
data[0] = j;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* encode time stamp */
|
||||
static int encode_time(uint8_t *data, time_t timestamp)
|
||||
{
|
||||
struct tm *tm = localtime(×tamp);
|
||||
int length = 0;
|
||||
uint8_t digit1, digit2;
|
||||
int quarters, sign;
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Encode time stamp '%02d.%02d.%02d %02d:%02d:%02d'\n", tm->tm_mday, tm->tm_mon + 1, tm->tm_year % 100, tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
/* year */
|
||||
digit1 = (tm->tm_year % 100) / 10;
|
||||
if (digit1 == 0)
|
||||
digit1 = 10;
|
||||
digit2 = tm->tm_year % 10;
|
||||
if (digit2 == 0)
|
||||
digit2 = 10;
|
||||
data[length++] = (digit2 << 4) | digit1;
|
||||
|
||||
/* month */
|
||||
digit1 = (tm->tm_mon + 1) / 10;
|
||||
if (digit1 == 0)
|
||||
digit1 = 10;
|
||||
digit2 = (tm->tm_mon + 1) % 10;
|
||||
if (digit2 == 0)
|
||||
digit2 = 10;
|
||||
data[length++] = (digit2 << 4) | digit1;
|
||||
|
||||
/* day */
|
||||
digit1 = tm->tm_mday / 10;
|
||||
if (digit1 == 0)
|
||||
digit1 = 10;
|
||||
digit2 = tm->tm_mday % 10;
|
||||
if (digit2 == 0)
|
||||
digit2 = 10;
|
||||
data[length++] = (digit2 << 4) | digit1;
|
||||
|
||||
/* hour */
|
||||
digit1 = tm->tm_hour / 10;
|
||||
if (digit1 == 0)
|
||||
digit1 = 10;
|
||||
digit2 = tm->tm_hour % 10;
|
||||
if (digit2 == 0)
|
||||
digit2 = 10;
|
||||
data[length++] = (digit2 << 4) | digit1;
|
||||
|
||||
/* min */
|
||||
digit1 = tm->tm_min / 10;
|
||||
if (digit1 == 0)
|
||||
digit1 = 10;
|
||||
digit2 = tm->tm_min % 10;
|
||||
if (digit2 == 0)
|
||||
digit2 = 10;
|
||||
data[length++] = (digit2 << 4) | digit1;
|
||||
|
||||
/* sec */
|
||||
digit1 = tm->tm_sec / 10;
|
||||
if (digit1 == 0)
|
||||
digit1 = 10;
|
||||
digit2 = tm->tm_sec % 10;
|
||||
if (digit2 == 0)
|
||||
digit2 = 10;
|
||||
data[length++] = (digit2 << 4) | digit1;
|
||||
|
||||
/* zone */
|
||||
quarters = timezone / 900;
|
||||
if (quarters < 0) {
|
||||
quarters = -quarters;
|
||||
sign = 1;
|
||||
} else {
|
||||
quarters = -quarters;
|
||||
sign = 0;
|
||||
}
|
||||
data[length++] = (quarters << 4) | (sign << 3) | (quarters >> 4);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* encode user data */
|
||||
static int encode_userdata(uint8_t *data, const char *message)
|
||||
{
|
||||
int length = 1;
|
||||
char character;
|
||||
int i, j, pos;
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Encode user data '%s'\n", message);
|
||||
|
||||
j = 0;
|
||||
pos = 0;
|
||||
for (i = 0; message[i]; i++) {
|
||||
if (message[i] < 128)
|
||||
character = message[i];
|
||||
else
|
||||
character = '?';
|
||||
j++;
|
||||
if (pos == 0) {
|
||||
/* character fits and is aligned to the right, new octet */
|
||||
data[length] = character;
|
||||
pos = 7;
|
||||
} else {
|
||||
/* character is shifted by pos */
|
||||
data[length] |= character << pos;
|
||||
if (pos > 1) {
|
||||
/* not all bits fit in octet, so fill the rest to next octet */
|
||||
length++;
|
||||
data[length] = character >> (8 - pos);
|
||||
pos--;
|
||||
} else {
|
||||
/* all bits fit in octet, so go to next octet */
|
||||
pos = 0;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pos)
|
||||
length++;
|
||||
|
||||
/* length field: number of characters */
|
||||
data[0] = j;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* deliver SMS (SC->MS) */
|
||||
int sms_deliver(nmt_t *nmt, uint8_t ref, const char *orig_address, uint8_t orig_type, uint8_t orig_plan, time_t timestamp, const char *message)
|
||||
{
|
||||
uint8_t data[256], *tpdu_length;
|
||||
int length = 0;
|
||||
int orig_len;
|
||||
int msg_len;
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Delivering SMS from upper layer\n");
|
||||
|
||||
orig_len = strlen(orig_address);
|
||||
msg_len = strlen(message);
|
||||
|
||||
if (orig_len > 24) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "Originator Address too long (%d characters)\n", orig_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (msg_len > 140) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "Message too long (%d characters)\n", msg_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* HEADER */
|
||||
length = encode_header(data);
|
||||
|
||||
/* RP */
|
||||
data[length++] = RP_MT_DATA;
|
||||
data[length++] = ref;
|
||||
data[length++] = RP_IE_USER_DATA;
|
||||
tpdu_length = data + length++;
|
||||
|
||||
/* TP */
|
||||
data[length++] = MTI_SMS_DELIVER | MMS_NO_MORE | VPF_NOT_PRESENT | SRI_NO_REPORT;
|
||||
length += encode_address(data + length, orig_address, orig_type, orig_plan); /* TP-OA */
|
||||
data[length++] = 0; /* TP-PID */
|
||||
data[length++] = 0; /* TP-DCS */
|
||||
length += encode_time(data + length, timestamp);
|
||||
length += encode_userdata(data + length, message);
|
||||
|
||||
/* RP length */
|
||||
*tpdu_length = length - (uint8_t)(tpdu_length - data) - 1;
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, " -> TPDU lenght = %d\n", *tpdu_length);
|
||||
|
||||
nmt->sms.mt = 1;
|
||||
dms_send(nmt, data, length, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* report SMS (SC->MS) */
|
||||
static void sms_submit_report(nmt_t *nmt, uint8_t ref, int error)
|
||||
{
|
||||
uint8_t data[64];
|
||||
int length = 0;
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Sending Submit Report (%s)\n", (error) ? "error" : "ok");
|
||||
|
||||
/* HEADER */
|
||||
length = encode_header(data);
|
||||
|
||||
/* RP */
|
||||
data[length++] = (error) ? RP_MO_ERROR : RP_MO_ACK;
|
||||
data[length++] = ref;
|
||||
|
||||
dms_send(nmt, data, length, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* receive from lower layer
|
||||
*/
|
||||
|
||||
/* decdoe message from 8 bit data */
|
||||
static void decode_message(const uint8_t *data, int length, char *message)
|
||||
{
|
||||
int fill;
|
||||
int i;
|
||||
uint16_t result;
|
||||
|
||||
fill = 0;
|
||||
result = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
result |= data[i] << fill;
|
||||
fill += 8;
|
||||
while (fill >= 7) {
|
||||
*message++ = result & 0x7f;
|
||||
result >>= 7;
|
||||
fill -= 7;
|
||||
}
|
||||
}
|
||||
*message++ = '\0';
|
||||
}
|
||||
|
||||
static const char digits2ascii[16] = {
|
||||
'?', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '0', '*', '#', '+', '?', '?' };
|
||||
|
||||
static void decode_address(const uint8_t *data, int digits, char *address)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < digits; i++) {
|
||||
if (!(i & 1))
|
||||
*address++ = digits2ascii[(*data) & 0xf];
|
||||
else
|
||||
*address++ = digits2ascii[(*data++) >> 4];
|
||||
}
|
||||
*address++ = '\0';
|
||||
}
|
||||
|
||||
/* decode sms submit message
|
||||
* return 1 if done, -1 if failed, 0, if more data is required */
|
||||
static int decode_sms_submit(nmt_t *nmt, const uint8_t *data, int length)
|
||||
{
|
||||
uint8_t ref, msg_ref;
|
||||
const uint8_t *orig_data, *tpdu_data, *dest_data, *msg_data;
|
||||
int orig_len, tpdu_len, dest_len, msg_len;
|
||||
int orig_digits, dest_digits, msg_chars;
|
||||
uint8_t orig_type, orig_plan, dest_type, dest_plan;
|
||||
int tp_vpf_present = 0;
|
||||
|
||||
/* decode ref */
|
||||
ref = data[1];
|
||||
data += 2;
|
||||
length -= 2;
|
||||
|
||||
/* do we have originator address length ? */
|
||||
if (length < 2) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "SMS still incomplete, waiting for originator address\n");
|
||||
return 0;
|
||||
}
|
||||
orig_data = 2 + data;
|
||||
orig_digits = data[0];
|
||||
orig_type = (data[1] >> 4) & 0x7;
|
||||
orig_plan = data[1] & 0x0f;
|
||||
orig_len = (orig_digits + 1) >> 1;
|
||||
if (length < 2 + orig_len) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "SMS still incomplete, waiting for originator address digits (got %d of %d)\n", length - 1, orig_len);
|
||||
return 0;
|
||||
}
|
||||
data += 2 + orig_len;
|
||||
length -= 2 + orig_len;
|
||||
|
||||
/* do we have user data IE ? */
|
||||
if (length < 2) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "SMS still incomplete, waiting for user data IE\n");
|
||||
return 0;
|
||||
}
|
||||
if (data[0] != RP_IE_USER_DATA) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "missing user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
tpdu_len = data[1];
|
||||
tpdu_data = 2 + data;
|
||||
if (length < 2 + tpdu_len) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "SMS still incomplete, waiting for TPDU to be complete\n");
|
||||
return 0;
|
||||
}
|
||||
data += 2 + tpdu_len;
|
||||
length -= 2 + tpdu_len;
|
||||
|
||||
/* decode orig address */
|
||||
char orig_address[orig_digits + 1];
|
||||
decode_address(orig_data, orig_digits, orig_address);
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Decoded originating addess: '%s'\n", orig_address);
|
||||
|
||||
/* go into TP */
|
||||
data = tpdu_data;
|
||||
length = tpdu_len;
|
||||
|
||||
/* check msg_type */
|
||||
if (length < 1) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
if ((data[0] & MTI_MASK) != MTI_SMS_SUBMIT) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "especting SUBMIT MTI, but got 0x%02x\n", data[0]);
|
||||
return -1;
|
||||
}
|
||||
if ((data[0] & VPF_MASK))
|
||||
tp_vpf_present = 1;
|
||||
data++;
|
||||
length--;
|
||||
|
||||
/* decode msg ref */
|
||||
if (length < 1) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
msg_ref = data[0];
|
||||
data++;
|
||||
length--;
|
||||
|
||||
/* decode dest address */
|
||||
if (length < 2) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
dest_data = 2 + data;
|
||||
dest_digits = data[0];
|
||||
dest_type = (data[1] >> 4) & 0x7;
|
||||
dest_plan = data[1] & 0x0f;
|
||||
dest_len = (dest_digits + 1) >> 1;
|
||||
if (length < 2 + dest_len) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
data += 2 + dest_len;
|
||||
length -= 2 + dest_len;
|
||||
char dest_address[dest_digits + 1];
|
||||
decode_address(dest_data, dest_digits, dest_address);
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Decoded destination addess: '%s'\n", dest_address);
|
||||
|
||||
/* skip above protocol identifier */
|
||||
if (length < 1) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read above protocol identifier IE\n");
|
||||
return -1;
|
||||
}
|
||||
data++;
|
||||
length--;
|
||||
|
||||
/* decode data coding scheme */
|
||||
if (length < 1) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short data coding scheme IE\n");
|
||||
return -1;
|
||||
}
|
||||
if (data[0] != 0) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "SMS coding unsupported (got 0x%02x)\n", data[0]);
|
||||
return -1;
|
||||
}
|
||||
data++;
|
||||
length--;
|
||||
|
||||
/* skip validity period */
|
||||
if (tp_vpf_present) {
|
||||
if (length < 1) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read validity period IE\n");
|
||||
return -1;
|
||||
}
|
||||
data++;
|
||||
length--;
|
||||
}
|
||||
|
||||
/* decode data message text */
|
||||
if (length < 1) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
msg_data = data + 1;
|
||||
msg_chars = data[0];
|
||||
msg_len = (msg_chars * 7 + 7) / 8;
|
||||
if (length < 1 + msg_len) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "short read user data IE\n");
|
||||
return -1;
|
||||
}
|
||||
char message[msg_chars + 1];
|
||||
decode_message(msg_data, msg_len, message);
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Decoded message: '%s'\n", message);
|
||||
|
||||
sms_submit(nmt, ref, orig_address, orig_type, orig_plan, msg_ref, dest_address, dest_type, dest_plan, message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* decode deliver report
|
||||
* return 1 if done, -1 if failed, 0, if more data is required */
|
||||
static int decode_deliver_report(nmt_t *nmt, const uint8_t *data, int length)
|
||||
{
|
||||
uint8_t ref, cause = 0;
|
||||
int error = 0;
|
||||
|
||||
ref = data[1];
|
||||
|
||||
if ((data[0] & RP_MTI_MASK) == RP_MT_ERROR) {
|
||||
error = 1;
|
||||
if (length < 4) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "deliver report still incomplete, waiting for cause IE\n");
|
||||
return 0;
|
||||
}
|
||||
if (length < 4 + data[3]) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "deliver report still incomplete, waiting for cause IE content\n");
|
||||
return 0;
|
||||
}
|
||||
if (data[2] == RP_IE_CAUSE && data[3] > 0)
|
||||
cause = data[4];
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Decoded delivery report: ERROR, cause=%d\n", cause);
|
||||
} else
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Decoded delivery report: OK\n");
|
||||
|
||||
sms_deliver_report(nmt, ref, error, cause);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* receive from DMS layer */
|
||||
void dms_receive(nmt_t *nmt, const uint8_t *data, int length, int eight_bits)
|
||||
{
|
||||
sms_t *sms = &nmt->sms;
|
||||
int space;
|
||||
int rc = 0;
|
||||
char debug_text[length * 5 + 1];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
sprintf(debug_text + i * 5, " 0x%02x", data[i]);
|
||||
debug_text[length * 5] = '\0';
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Received %d bytes from DMS layer:%s\n", length, debug_text);
|
||||
|
||||
if (sms->mt && !sms->data_sent) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "Ignoring data while we transmit data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* append received data */
|
||||
space = sizeof(sms->rx_buffer) - sms->rx_count;
|
||||
if (space < length) {
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "Received message exceeds RX buffer, terminating call!\n");
|
||||
release:
|
||||
timer_start(&nmt->sms_timer, SMS_RELEASE_TO);
|
||||
return;
|
||||
}
|
||||
memcpy(sms->rx_buffer + sms->rx_count, data, length);
|
||||
sms->rx_count += length;
|
||||
|
||||
/* go into buffer */
|
||||
data = sms->rx_buffer;
|
||||
length = sms->rx_count;
|
||||
|
||||
/* check if complete */
|
||||
if (length < 2)
|
||||
return;
|
||||
switch (data[0] & RP_MTI_MASK) {
|
||||
case RP_MT_ACK:
|
||||
rc = decode_deliver_report(nmt, data, length);
|
||||
break;
|
||||
case RP_MT_ERROR:
|
||||
rc = decode_deliver_report(nmt, data, length);
|
||||
break;
|
||||
case RP_MO_DATA:
|
||||
rc = decode_sms_submit(nmt, data, length);
|
||||
if (rc < 0)
|
||||
sms_submit_report(nmt, data[1], FSC_UNSPECIFIED_ERROR);
|
||||
else if (rc > 0) {
|
||||
sms_submit_report(nmt, data[1], 0);
|
||||
}
|
||||
/* no release, we release afeter the report */
|
||||
rc = 0;
|
||||
break;
|
||||
case RP_SM_READY_TO_RECEIVE:
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "Received READY-TO-RECEVIE message.\n");
|
||||
data += length;
|
||||
length -= length;
|
||||
break;
|
||||
default:
|
||||
PDEBUG(DSMS, DEBUG_NOTICE, "Received unknown RP message type %d.\n", data[0]);
|
||||
rc = -1;
|
||||
}
|
||||
if (rc)
|
||||
goto release;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void sms_timeout(struct timer *timer)
|
||||
{
|
||||
nmt_t *nmt = (nmt_t *)timer->priv;
|
||||
|
||||
sms_release(nmt);
|
||||
}
|
||||
|
||||
/* all data has been sent to mobile */
|
||||
void dms_all_sent(nmt_t *nmt)
|
||||
{
|
||||
sms_t *sms = &nmt->sms;
|
||||
|
||||
if (!sms->data_sent) {
|
||||
if (!sms->mt) {
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Done sending submit report, releasing.\n");
|
||||
timer_start(&nmt->sms_timer, SMS_RELEASE_TO);
|
||||
}
|
||||
sms->data_sent = 1;
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "DMS layer indicates acknowledge of sent data\n");
|
||||
}
|
||||
}
|
||||
|
||||
void sms_reset(nmt_t *nmt)
|
||||
{
|
||||
sms_t *sms = &nmt->sms;
|
||||
|
||||
PDEBUG(DSMS, DEBUG_DEBUG, "Resetting SMS states\n");
|
||||
timer_stop(&nmt->sms_timer);
|
||||
|
||||
memset(sms, 0, sizeof(*sms));
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
#define SMS_TYPE_UKNOWN 0x0
|
||||
#define SMS_TYPE_INTERNATIONAL 0x1
|
||||
#define SMS_TYPE_NATIONAL 0x2
|
||||
#define SMS_TYPE_NETWORK 0x3
|
||||
#define SMS_TYPE_SUBSCRIBER 0x4
|
||||
#define SMS_TYPE_ALPHANUMERIC 0x5
|
||||
#define SMS_TYPE_ABBREVIATED 0x6
|
||||
#define SMS_TYPE_RESERVED 0x7
|
||||
|
||||
#define SMS_PLAN_UNKOWN 0x0
|
||||
#define SMS_PLAN_ISDN_TEL 0x1
|
||||
#define SMS_PLAN_DATA 0x3
|
||||
#define SMS_PLAN_TELEX 0x4
|
||||
#define SMS_PLAN_NATIONAL 0x8
|
||||
#define SMS_PLAN_PRIVATE 0x9
|
||||
#define SMS_PLAN_ERMES 0xa
|
||||
#define SMS_PLAN_RESERVED 0xf
|
||||
|
||||
typedef struct sms {
|
||||
uint8_t rx_buffer[1024]; /* data received from MS */
|
||||
int rx_count; /* number of bytes in buffer */
|
||||
int data_sent; /* all pending data have been sent and was acked */
|
||||
int mt; /* mobile terminating SMS */
|
||||
} sms_t;
|
||||
|
||||
int sms_init_sender(nmt_t *nmt);
|
||||
void sms_cleanup_sender(nmt_t *nmt);
|
||||
void sms_submit(nmt_t *nmt, uint8_t ref, const char *orig_address, uint8_t orig_type, uint8_t orig_plan, int msg_ref, const char *dest_address, uint8_t dest_type, uint8_t dest_plan, const char *message);
|
||||
void sms_deliver_report(nmt_t *nmt, uint8_t ref, int error, uint8_t cause);
|
||||
int sms_deliver(nmt_t *nmt, uint8_t ref, const char *orig_address, uint8_t type, uint8_t plan, time_t timestamp, const char *message);
|
||||
void sms_release(nmt_t *nmt);
|
||||
void sms_reset(nmt_t *nmt);
|
||||
|
|
@ -3,7 +3,8 @@ AM_CPPFLAGS = -Wall -g $(all_includes)
|
|||
noinst_PROGRAMS = \
|
||||
test_compandor \
|
||||
test_emphasis \
|
||||
test_dms
|
||||
test_dms \
|
||||
test_sms
|
||||
|
||||
test_compandor_SOURCES = test_compandor.c
|
||||
|
||||
|
@ -28,3 +29,12 @@ test_dms_LDADD = \
|
|||
$(top_builddir)/src/common/libcommon.a \
|
||||
-lm
|
||||
|
||||
test_sms_SOURCES = \
|
||||
$(top_builddir)/src/nmt/sms.c \
|
||||
test_sms.c
|
||||
|
||||
test_sms_LDADD = \
|
||||
$(COMMON_LA) \
|
||||
$(top_builddir)/src/common/libcommon.a \
|
||||
-lm
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "../nmt/nmt.h"
|
||||
|
||||
static const uint8_t test_mo_sms_data[] = {
|
||||
0x00, 0x00, 0x00, 0xa1, 0x41, 0x0f, 0x11,
|
||||
0x00, 0x04, 0xa1, 0x8a, 0x51,
|
||||
0x00, 0x00, 0xff, 0x05, 0xc8, 0x20, 0x93,
|
||||
0xf9, 0x7c,
|
||||
};
|
||||
|
||||
static const char *test_mo_sms_text = "HALLO";
|
||||
|
||||
static const char *test_mt_sms_text = "Moin Moin";
|
||||
static const char *test_mt_sms_tel = "4948416068";
|
||||
static time_t test_mt_sms_time = 851430904;
|
||||
|
||||
static const uint8_t test_mt_sms_data[] = {
|
||||
0x01, 0x18, 0x53, 0x4d, 0x53, 0x48, 0x18, 0x41, 0x42, 0x43, 0x02,
|
||||
0x01,
|
||||
0x01,
|
||||
0x41,
|
||||
0x1a,
|
||||
0x04,
|
||||
0x0a, 0x91, 0x94, 0x84, 0x14, 0xa6, 0x86,
|
||||
0x00,
|
||||
0x00,
|
||||
0x69, 0x21, 0x42, 0x31, 0x53, 0x4a, 0x48,
|
||||
0x09, 0xcd, 0x77, 0xda, 0x0d, 0x6a, 0xbe, 0xd3, 0x6e,
|
||||
};
|
||||
|
||||
static void assert(int condition, char *why)
|
||||
{
|
||||
printf("%s = %s\n", why, (condition) ? "TRUE" : "FALSE");
|
||||
|
||||
if (!condition) {
|
||||
printf("\n******************** FAILED ********************\n\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void ok(void)
|
||||
{
|
||||
printf("\n OK ;->\n\n");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
static uint8_t dms_buffer[256];
|
||||
static int dms_buffer_count;
|
||||
void dms_send(nmt_t *nmt, const uint8_t *data, int length, int eight_bits)
|
||||
{
|
||||
memcpy(dms_buffer, data, length);
|
||||
dms_buffer_count = length;
|
||||
// int i;
|
||||
|
||||
assert(length == sizeof(test_mt_sms_data), "Expecting SMS binary data length to match");
|
||||
assert(!memcmp(data, test_mt_sms_data, length), "Expecting SMS binary data to match");
|
||||
// for (i = 0; i < length; i++) {
|
||||
// printf("(0x%02x)\n", data[i]);
|
||||
// }
|
||||
}
|
||||
|
||||
void sms_release(nmt_t *nmt)
|
||||
{
|
||||
printf("(got release from SMS layer)\n");
|
||||
}
|
||||
|
||||
void sms_submit(nmt_t *nmt, uint8_t ref, const char *orig_address, uint8_t orig_type, uint8_t orig_plan, int msg_ref, const char *dest_address, uint8_t dest_type, uint8_t dest_plan, const char *message)
|
||||
{
|
||||
strcpy((char *)dms_buffer, message);
|
||||
dms_buffer_count = strlen(message);
|
||||
}
|
||||
|
||||
void sms_deliver_report(nmt_t *nmt, uint8_t ref, int error, uint8_t cause)
|
||||
{
|
||||
printf("(got deliver report from SMS layer)\n");
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
nmt_t *nmt;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
debuglevel = DEBUG_DEBUG;
|
||||
|
||||
nmt = calloc(sizeof(*nmt), 1);
|
||||
sms_reset(nmt);
|
||||
|
||||
/* deliver */
|
||||
printf("(delivering SMS)\n");
|
||||
rc = sms_deliver(nmt, 1, test_mt_sms_tel, SMS_TYPE_INTERNATIONAL, SMS_PLAN_ISDN_TEL, test_mt_sms_time, test_mt_sms_text);
|
||||
assert(rc == 0, "Expecting sms_deliver() to return 0");
|
||||
|
||||
ok();
|
||||
|
||||
free(nmt);
|
||||
nmt = calloc(sizeof(*nmt), 1);
|
||||
sms_reset(nmt);
|
||||
|
||||
printf("(submitting SMS)\n");
|
||||
dms_buffer_count = 0;
|
||||
for (i = 0; i < sizeof(test_mo_sms_data); i++)
|
||||
dms_receive(nmt, test_mo_sms_data + i, 1, 1);
|
||||
|
||||
assert(dms_buffer_count == strlen(test_mo_sms_text), "Expecting SMS text length to match");
|
||||
assert(!memcmp(dms_buffer, test_mo_sms_text, dms_buffer_count), "Expecting SMS text to match");
|
||||
|
||||
ok();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue