view mtctest/sig_handler.c @ 14:44c1d4d787e9 default tip

themwi-test-mtc: add option to set bearer cap
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 28 Nov 2024 20:37:23 +0000
parents 0ec938ed530b
children
line wrap: on
line source

/*
 * In this module we handle all incoming messages from MNCC,
 * printing all of them and generating protocol-required responses
 * for some.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <themwi/rtp/rtp_alloc_simple.h>
#include "../include/mncc.h"
#include "../include/gsm48_const.h"

extern int disconnect_mode;
extern struct rtp_alloc_simple rtp_info;
extern struct timeval cur_event_time;

int got_msc_rtp_info;
struct sockaddr_storage msc_rtp_addr;
uint8_t rtp_payload_type;

static void
print_bearer_cap(bcap)
	struct gsm_mncc_bearer_cap *bcap;
{
	int i, sv;

	printf("Bearer cap: itcap=%d tmod=%d coding=%d rrq=%d\n",
		bcap->transfer, bcap->mode, bcap->coding, bcap->radio);
	printf("  speech: CTM=%d sv", bcap->speech_ctm);
	for (i = 0; i < 8; i++) {
		sv = bcap->speech_ver[i];
		if (sv < 0)
			break;
		printf(" %d", sv);
	}
	putchar('\n');
	printf("  data: ra=%d sig=%d async=%d nstop=%d ndata=%d\n",
		bcap->data.rate_adaption, bcap->data.sig_access,
		bcap->data.async, bcap->data.nr_stop_bits,
		bcap->data.nr_data_bits);
	printf("  urate=%d par=%d irate=%d transp=%d mtype=%d\n",
		bcap->data.user_rate, bcap->data.parity,
		bcap->data.interm_rate, bcap->data.transp,
		bcap->data.modem_type);
}

static void
print_cc_cap(cc)
	struct gsm_mncc_cccap *cc;
{
	printf("CC capabilities: DTMF=%d PCP=%d\n", cc->dtmf, cc->pcp);
}

static void
print_cause(cause)
	struct gsm_mncc_cause *cause;
{
	int i;

	printf("Cause: loc=%d coding=%d value=%d", cause->location,
		cause->coding, cause->value);
	if (cause->rec)
		printf(" rec=0x%02X", cause->rec_val);
	for (i = 0; i < cause->diag_len; i++) {
		if (!(i & 15)) {
			putchar('\n');
			putchar(' ');
		}
		printf(" %02X", cause->diag[i] & 0xFF);
	}
	putchar('\n');
}

static void
print_progress(prog)
	struct gsm_mncc_progress *prog;
{
	printf("Progress: loc=%d coding=%d descr=0x%02X", prog->location,
		prog->coding, prog->descr);
}

static void
print_useruser(uu)
	struct gsm_mncc_useruser *uu;
{
	printf("User-User IE: proto=0x%02X\n", uu->proto);
	/* dump to be implemented if and when we actually get a UU somewhere */
}

static void
print_keypad(kp)
	int kp;
{
	if (kp >= '!' && kp <= '~')
		printf("Keypad code: %c\n", kp);
	else
		printf("Keypad code: 0x%02X\n", kp);
}

static void
print_facility(fac)
	struct gsm_mncc_facility *fac;
{
	int i;

	printf("Facility IE: %d byte(s)", fac->len);
	for (i = 0; i < fac->len; i++) {
		if (!(i & 15)) {
			putchar('\n');
			putchar(' ');
		}
		printf(" %02X", fac->info[i] & 0xFF);
	}
	putchar('\n');
}

static void
print_ssver(ssv)
	struct gsm_mncc_ssversion *ssv;
{
	int i;

	printf("SS version IE: %d byte(s)", ssv->len);
	for (i = 0; i < ssv->len; i++) {
		if (!(i & 15)) {
			putchar('\n');
			putchar(' ');
		}
		printf(" %02X", ssv->info[i] & 0xFF);
	}
	putchar('\n');
}

static void
print_fields(msg)
	struct gsm_mncc *msg;
{
	if (msg->fields & MNCC_F_BEARER_CAP)
		print_bearer_cap(&msg->bearer_cap);
	if (msg->fields & MNCC_F_CCCAP)
		print_cc_cap(&msg->cccap);
	if (msg->fields & MNCC_F_CAUSE)
		print_cause(&msg->cause);
	if (msg->fields & MNCC_F_PROGRESS)
		print_progress(&msg->progress);
	if (msg->fields & MNCC_F_USERUSER)
		print_useruser(&msg->useruser);
	if (msg->more)
		printf("More data flag set\n");
	if (msg->fields & MNCC_F_KEYPAD)
		print_keypad(msg->keypad);
	if (msg->fields & MNCC_F_FACILITY)
		print_facility(&msg->facility);
	if (msg->fields & MNCC_F_SSVERSION)
		print_ssver(&msg->ssversion);
}

static void
print_sdp(sdp)
	char *sdp;
{
	char *cp, *ep, *np;

	for (cp = sdp; *cp; cp = np) {
		ep = index(cp, '\n');
		if (ep) {
			*ep = '\0';
			np = ep + 1;
		} else
			np = 0;
		ep = index(cp, '\r');
		if (ep)
			*ep = '\0';
		printf("%s %s\n", cp == sdp ? "SDP:" : "    ", cp);
	}
}

static void
send_connect_ack()
{
	struct gsm_mncc ack;

	printf("Sending connect ack\n");
	bzero(&ack, sizeof(struct gsm_mncc));
	ack.msg_type = MNCC_SETUP_COMPL_REQ;
	ack.callref = 1;
	send_mncc_to_gsm(&ack, sizeof(struct gsm_mncc));
}

static void
send_rtp_connect()
{
	struct gsm_mncc_rtp rtp;

	printf("Sending MNCC_RTP_CONNECT\n");
	bzero(&rtp, sizeof(struct gsm_mncc_rtp));
	rtp.msg_type = MNCC_RTP_CONNECT;
	rtp.callref = 1;
	bcopy(&rtp_info.gsm_addr, &rtp.addr, sizeof(struct sockaddr_storage));
	send_mncc_to_gsm(&rtp, sizeof(struct gsm_mncc_rtp));
}

static void
handle_dtmf_time()
{
	static struct timeval last_dtmf_time;
	struct timeval delta;
	unsigned ms;

	if (timerisset(&last_dtmf_time) &&
	    timercmp(&cur_event_time, &last_dtmf_time, >)) {
		timersub(&cur_event_time, &last_dtmf_time, &delta);
		if (delta.tv_sec >= 2)
			printf("Time since last DTMF event: %u s\n",
				(unsigned) delta.tv_sec);
		else {
			ms = delta.tv_sec * 1000 + delta.tv_usec / 1000;
			printf("Time since last DTMF event: %u ms\n", ms);
		}
	}
	bcopy(&cur_event_time, &last_dtmf_time, sizeof(struct timeval));
}

static void
handle_signaling_msg(msg, msglen)
	struct gsm_mncc *msg;
	unsigned msglen;
{
	if (msglen != sizeof(struct gsm_mncc)) {
		fprintf(stderr,
			"error: Rx MNCC message type 0x%x has wrong length\n",
			msg->msg_type);
		exit(1);
	}
	if (msg->callref != 1) {
		fprintf(stderr,
	"error: Rx MNCC message type 0x%x has unexpected callref 0x%x\n",
			msg->msg_type, msg->callref);
		exit(1);
	}
	switch (msg->msg_type) {
	case MNCC_SETUP_CNF:
		printf("MNCC_SETUP_CNF: call is answered\n");
		print_fields(msg);
		print_sdp(msg->sdp);
		send_rtp_connect();
		send_connect_ack();
		return;
	case MNCC_CALL_CONF_IND:
		printf("MNCC_CALL_CONF_IND: call is confirmed\n");
		print_fields(msg);
		print_sdp(msg->sdp);
		return;
	case MNCC_ALERT_IND:
		printf("MNCC_ALERT_IND: call is alerting\n");
		print_fields(msg);
		print_sdp(msg->sdp);
		return;
	case MNCC_NOTIFY_IND:
		printf("NNCC_NOTIFY_IND: NOTIFY byte from MS: 0x%02X\n",
			msg->notify);
		return;
	case MNCC_DISC_IND:
		printf("MNCC_DISC_IND: MS initiates disconnect\n");
		print_fields(msg);
		disconnect_mode = 1;
		printf("Responding with release request\n");
		msg->msg_type = MNCC_REL_REQ;
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_FACILITY_IND:
		printf("MNCC_FACILITY_IND: call-related SS from MS\n");
		print_fields(msg);
		return;
	case MNCC_START_DTMF_IND:
		printf("MNCC_START_DTMF_IND: MS sending DTMF start\n");
		print_fields(msg);
		handle_dtmf_time();
		if (msg->fields & MNCC_F_KEYPAD &&
		    is_valid_dtmf_digit(msg->keypad)) {
			printf("Responding with ACK\n");
			msg->msg_type = MNCC_START_DTMF_RSP;
		} else {
			printf("Responding with Reject\n");
			msg->msg_type = MNCC_START_DTMF_REJ;
			mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
					GSM48_CC_CAUSE_INVAL_MAND_INF);
		}
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_STOP_DTMF_IND:
		printf("MNCC_STOP_DTMF_IND: MS sending DTMF stop\n");
		handle_dtmf_time();
		msg->msg_type = MNCC_STOP_DTMF_RSP;
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_MODIFY_IND:
		printf("MNCC_MODIFY_IND: MS requests call modification\n");
		print_fields(msg);
		msg->msg_type = MNCC_MODIFY_REJ;
		mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU,
				GSM48_CC_CAUSE_SERV_OPT_UNIMPL);
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_HOLD_IND:
		printf("MNCC_HOLD_IND: MS requests call hold\n");
		msg->msg_type = MNCC_HOLD_CNF;
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_RETRIEVE_IND:
		printf("MNCC_RETRIEVE_IND: MS requests call retrieve\n");
		send_rtp_connect();
		msg->msg_type = MNCC_RETRIEVE_CNF;
		send_mncc_to_gsm(msg, sizeof(struct gsm_mncc));
		return;
	case MNCC_USERINFO_IND:
		printf("MNCC_USERINFO_IND: user-user info\n");
		print_fields(msg);
		return;
	case MNCC_REL_IND:
		printf("MNCC_REL_IND: final release\n");
		print_fields(msg);
		exit(0);
	case MNCC_REL_CNF:
		printf("MNCC_REL_CNF: final release in response to request\n");
		print_fields(msg);
		exit(0);
	case MNCC_REJ_IND:
		printf("MNCC_REJ_IND: MT call rejected\n");
		print_fields(msg);
		exit(0);
	}
}

static void
handle_rtp_msg(msg, msglen)
	struct gsm_mncc_rtp *msg;
	unsigned msglen;
{
	if (msglen != sizeof(struct gsm_mncc_rtp)) {
		fprintf(stderr,
			"error: Rx MNCC message type 0x%x has wrong length\n",
			msg->msg_type);
		exit(1);
	}
	if (msg->callref != 1) {
		fprintf(stderr,
	"error: Rx MNCC message type 0x%x has unexpected callref 0x%x\n",
			msg->msg_type, msg->callref);
		exit(1);
	}
	switch (msg->msg_type) {
	case MNCC_RTP_CREATE:
		printf("MNCC_RTP_CREATE: RTP info from MSC\n");
		printf("payload_type=0x%x payload_msg_type=0x%x\n",
			msg->payload_type, msg->payload_msg_type);
		print_sdp(msg->sdp);
		got_msc_rtp_info = 1;
		bcopy(&msg->addr, &msc_rtp_addr,
			sizeof(struct sockaddr_storage));
		rtp_payload_type = msg->payload_type;
		return;
	case MNCC_RTP_CONNECT:
		printf("MNCC_RTP_CONNECT: error response\n");
		return;
	case MNCC_RTP_FREE:
		printf("MNCC_RTP_FREE: bogon\n");
		return;
	}
}

void
msg_from_mncc(msg, msglen)
	union mncc_msg *msg;
	unsigned msglen;
{
	switch (msg->msg_type) {
	case MNCC_SETUP_CNF:
	case MNCC_CALL_CONF_IND:
	case MNCC_ALERT_IND:
	case MNCC_NOTIFY_IND:
	case MNCC_DISC_IND:
	case MNCC_FACILITY_IND:
	case MNCC_START_DTMF_IND:
	case MNCC_STOP_DTMF_IND:
	case MNCC_MODIFY_IND:
	case MNCC_HOLD_IND:
	case MNCC_RETRIEVE_IND:
	case MNCC_USERINFO_IND:
	case MNCC_REL_IND:
	case MNCC_REL_CNF:
	case MNCC_REJ_IND:
		handle_signaling_msg(msg, msglen);
		return;
	case MNCC_RTP_CREATE:
	case MNCC_RTP_CONNECT:
	case MNCC_RTP_FREE:
		handle_rtp_msg(msg, msglen);
		return;
	default:
		fprintf(stderr,
			"error: received unexpected MNCC message type 0x%x\n",
			msg->msg_type);
		exit(1);
	}
}