gsm/sms: Rewrite of SMC process, extracted from OpenBSC

The SMC process is used to transfer RP frames. It is now extracted from
OpenBSC. It includes a real state machine now for easier debugging.

Written-by: Andreas Eversberg <jolly@eversberg.eu>
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Andreas Eversberg 2011-10-28 03:55:37 +02:00 committed by Harald Welte
parent d84f47abb3
commit bbf9034eaa
5 changed files with 607 additions and 5 deletions

View File

@ -1,7 +1,7 @@
osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \
gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \
sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \
gsm0411_utils.h
gsm0411_utils.h gsm0411_smc.h
SUBDIRS = protocol

View File

@ -0,0 +1,62 @@
#ifndef _GSM0411_SMC_H
#define _GSM0411_SMC_H
#include <osmocom/gsm/protocol/gsm_04_11.h>
#define GSM411_MMSMS_EST_REQ 0x310
#define GSM411_MMSMS_EST_IND 0x312
#define GSM411_MMSMS_EST_CNF 0x311
#define GSM411_MMSMS_REL_REQ 0x320
#define GSM411_MMSMS_REL_IND 0x322
#define GSM411_MMSMS_DATA_REQ 0x330
#define GSM411_MMSMS_DATA_IND 0x332
#define GSM411_MMSMS_UNIT_DATA_REQ 0x340
#define GSM411_MMSMS_UNIT_DATA_IND 0x342
#define GSM411_MMSMS_ERR_IND 0x372
#define GSM411_MNSMS_ABORT_REQ 0x101
#define GSM411_MNSMS_DATA_REQ 0x102
#define GSM411_MNSMS_DATA_IND 0x103
#define GSM411_MNSMS_EST_REQ 0x104
#define GSM411_MNSMS_EST_IND 0x105
#define GSM411_MNSMS_ERROR_IND 0x106
#define GSM411_MNSMS_REL_REQ 0x107
struct gsm411_smc_inst {
int network; /* is this a MO (0) or MT (1) transfer */
int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg);
int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type);
enum gsm411_cp_state cp_state;
struct osmo_timer_list cp_timer;
struct msgb *cp_msg; /* store pending message */
int cp_rel; /* store pending release */
int cp_retx; /* retry counter */
int cp_max_retr; /* maximum retry */
int cp_tc1; /* timer value TC1* */
};
extern const struct value_string gsm411_cp_cause_strs[];
/* init a new instance */
void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg),
int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type));
/* clear instance */
void gsm411_smc_clear(struct gsm411_smc_inst *inst);
/* message from upper layer */
int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg);
/* message from lower layer */
int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type);
#endif /* _GSM0411_SMC_H */

View File

@ -5,7 +5,7 @@
/* GSM TS 04.11 definitions */
/* Chapter 5.2.3: SMC-CS states at the network side */
/* Chapter 5.2.3: SMC-CS states at the user/network side */
enum gsm411_cp_state {
GSM411_CPS_IDLE = 0,
GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */
@ -13,11 +13,12 @@ enum gsm411_cp_state {
GSM411_CPS_MM_ESTABLISHED = 3,
};
/* Chapter 6.2.2: SMR states at the network side */
/* Chapter 6.2.2: SMR states at the user/network side */
enum gsm411_rp_state {
GSM411_RPS_IDLE = 0,
GSM411_RPS_WAIT_FOR_RP_ACK = 1,
GSM411_RPS_WAIT_TO_TX_RP_ACK = 3,
GSM411_RPS_WAIT_FOR_RETRANS_T = 4,
};
/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */
@ -95,7 +96,7 @@ enum gsm411_rp_cause {
#define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */
#define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */
#define GSM411_TMR_TC1A 30, 0
#define GSM411_TMR_TC1A 30 /* TR1M - 10 */
/* Chapter 8.2.1 */
struct gsm411_rp_hdr {

View File

@ -10,7 +10,7 @@ lib_LTLIBRARIES = libosmogsm.la
libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \
rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \
gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \
gsm0411_utils.c \
gsm0411_utils.c gsm0411_smc.c \
lapd_core.c lapdm.c
libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION)

539
src/gsm/gsm0411_smc.c Normal file
View File

@ -0,0 +1,539 @@
/* Point-to-Point (PP) Short Message Service (SMS)
* Support on Mobile Radio Interface
* 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* (C) 2011 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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* Notes on msg:
*
* Messages from lower layer are freed by lower layer.
*
* Messages to upper layer are freed after upper layer call returns, so upper
* layer cannot use data after returning. Upper layer must not free the msg.
*
* This implies: Lower layer messages can be forwarded to upper layer.
*
* Upper layer messages are freed by lower layer, so they must not be freed
* after calling lower layer.
*
*
* Notes on release:
*
* Whenever the process returns to IDLE, the MM connection is released using
* MMSMS-REL-REQ. It is allowed to destroy this process while processing
* this message.
*
* There is expeption, if MMSMS-REL-IND is received from lower layer, the
* process returns to IDLE without sending MMSMS-REL-REQ.
*
*/
#include <string.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm0411_utils.h>
#include <osmocom/gsm/gsm0411_smc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
static void cp_timer_expired(void *data);
#define MAX_SMS_RETRY 2;
/* init a new instance */
void gsm411_smc_init(struct gsm411_smc_inst *inst, int network,
int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg),
int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type))
{
memset(inst, 0, sizeof(*inst));
inst->network = network;
inst->cp_max_retr = MAX_SMS_RETRY;
inst->cp_tc1 = GSM411_TMR_TC1A / (inst->cp_max_retr + 1);
inst->cp_state = GSM411_CPS_IDLE;
inst->mn_recv = mn_recv;
inst->mm_send = mm_send;
LOGP(DLSMS, LOGL_INFO, "New SMC instance created\n");
}
/* clear instance */
void gsm411_smc_clear(struct gsm411_smc_inst *inst)
{
LOGP(DLSMS, LOGL_INFO, "Clear SMC instance\n");
osmo_timer_del(&inst->cp_timer);
/* free stored msg */
if (inst->cp_msg) {
LOGP(DLSMS, LOGL_INFO, "Dropping pending message\n");
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
}
const char *smc_state_names[] = {
"IDLE",
"MM_CONN_PENDING",
"WAIT_CP_ACK",
"MM_ESTABLISHED",
};
const struct value_string gsm411_cp_cause_strs[] = {
{ GSM411_CP_CAUSE_NET_FAIL, "Network Failure" },
{ GSM411_CP_CAUSE_CONGESTION, "Congestion" },
{ GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" },
{ GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
{ GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
{ GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" },
{ GSM411_CP_CAUSE_MSG_INCOMP_STATE,
"Message incompatible with protocol state" },
{ GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" },
{ GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
{ 0, 0 }
};
static void new_cp_state(struct gsm411_smc_inst *inst,
enum gsm411_cp_state state)
{
LOGP(DLSMS, LOGL_INFO, "New CP state %s -> %s\n",
smc_state_names[inst->cp_state], smc_state_names[state]);
inst->cp_state = state;
}
static int gsm411_tx_cp_error(struct gsm411_smc_inst *inst, uint8_t cause)
{
struct msgb *nmsg = gsm411_msgb_alloc();
uint8_t *causep;
LOGP(DLSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause,
get_value_string(gsm411_cp_cause_strs, cause));
causep = msgb_put(nmsg, 1);
*causep = cause;
return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg,
GSM411_MT_CP_ERROR);
}
/* etablish SMC connection */
static int gsm411_mnsms_est_req(struct gsm411_smc_inst *inst, struct msgb *msg)
{
struct msgb *nmsg;
if (inst->cp_msg) {
LOGP(DLSMS, LOGL_FATAL, "EST REQ, but we already have an "
"cp_msg. This should never happen, please fix!\n");
msgb_free(inst->cp_msg);
}
inst->cp_msg = msg;
new_cp_state(inst, GSM411_CPS_MM_CONN_PENDING);
/* clear stored release flag */
inst->cp_rel = 0;
/* send MMSMS_EST_REQ */
nmsg = gsm411_msgb_alloc();
return inst->mm_send(inst, GSM411_MMSMS_EST_REQ, nmsg, 0);
}
static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst)
{
struct msgb *nmsg;
LOGP(DLSMS, LOGL_INFO, "Send CP data\n");
/* reset retry counter */
if (inst->cp_state != GSM411_CPS_WAIT_CP_ACK)
inst->cp_retx = 0;
/* 5.2.3.1.2: enter MO-wait for CP-ACK */
/* 5.2.3.2.3: enter MT-wait for CP-ACK */
new_cp_state(inst, GSM411_CPS_WAIT_CP_ACK);
inst->cp_timer.data = inst;
inst->cp_timer.cb = cp_timer_expired;
/* 5.3.2.1: Set Timer TC1A */
osmo_timer_schedule(&inst->cp_timer, inst->cp_tc1, 0);
/* clone cp_msg */
nmsg = gsm411_msgb_alloc();
memcpy(msgb_put(nmsg, inst->cp_msg->len), inst->cp_msg->data,
inst->cp_msg->len);
/* send MMSMS_DATA_REQ with CP-DATA */
return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg,
GSM411_MT_CP_DATA);
}
static int gsm411_mmsms_est_cnf(struct gsm411_smc_inst *inst, struct msgb *msg)
{
if (!inst->cp_msg) {
LOGP(DLSMS, LOGL_FATAL, "EST CNF, but we have no cp_msg. This "
"should never happen, please fix!\n");
return -EINVAL;
}
return gsm411_mmsms_send_msg(inst);
}
/* SMC TC1* is expired */
static void cp_timer_expired(void *data)
{
struct gsm411_smc_inst *inst = data;
struct msgb *nmsg;
if (inst->cp_retx == inst->cp_max_retr) {
LOGP(DLSMS, LOGL_INFO, "TC1* timeout, no more retries.\n");
/* 5.3.2.1: enter idle state */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
nmsg = gsm411_msgb_alloc();
inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg);
msgb_free(nmsg);
/* free pending stored msg */
if (inst->cp_msg) {
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
/* release MM connection */
nmsg = gsm411_msgb_alloc();
inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
return;
}
LOGP(DLSMS, LOGL_INFO, "TC1* timeout, retrying...\n");
inst->cp_retx++;
gsm411_mmsms_est_cnf(inst, NULL);
}
static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg)
{
/* free stored msg */
if (inst->cp_msg) {
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
LOGP(DLSMS, LOGL_INFO, "Received CP-ACK\n");
/* 5.3.2.1 enter MM Connection established */
new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED);
/* 5.3.2.1: Reset Timer TC1* */
osmo_timer_del(&inst->cp_timer);
/* pending release? */
if (inst->cp_rel) {
struct msgb *nmsg;
LOGP(DLSMS, LOGL_INFO, "We have pending release.\n");
new_cp_state(inst, GSM411_CPS_IDLE);
/* release MM connection */
nmsg = gsm411_msgb_alloc();
return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
}
return 0;
}
static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg)
{
struct msgb *nmsg;
int mt = GSM411_MNSMS_DATA_IND;
LOGP(DLSMS, LOGL_INFO, "Received CP-DATA\n");
/* 5.3.1 enter MM Connection established (if idle) */
if (inst->cp_state == GSM411_CPS_IDLE) {
new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED);
mt = GSM411_MNSMS_EST_IND;
/* clear stored release flag */
inst->cp_rel = 0;
}
/* send MMSMS_DATA_REQ (CP ACK) */
nmsg = gsm411_msgb_alloc();
inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_ACK);
/* indicate data */
inst->mn_recv(inst, mt, msg);
return 0;
}
/* send CP DATA */
static int gsm411_mnsms_data_req(struct gsm411_smc_inst *inst, struct msgb *msg)
{
if (inst->cp_msg) {
LOGP(DLSMS, LOGL_FATAL, "DATA REQ, but we already have an "
"cp_msg. This should never happen, please fix!\n");
msgb_free(inst->cp_msg);
}
/* store and send */
inst->cp_msg = msg;
return gsm411_mmsms_send_msg(inst);
}
/* release SMC connection */
static int gsm411_mnsms_rel_req(struct gsm411_smc_inst *inst, struct msgb *msg)
{
struct msgb *nmsg;
msgb_free(msg);
/* discard silently */
if (inst->cp_state == GSM411_CPS_IDLE)
return 0;
/* store release, until established or released */
if (inst->cp_state != GSM411_CPS_MM_ESTABLISHED) {
LOGP(DLSMS, LOGL_NOTICE, "Cannot release yet.\n");
inst->cp_rel = 1;
return 0;
}
/* free stored msg */
if (inst->cp_msg) {
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
new_cp_state(inst, GSM411_CPS_IDLE);
/* release MM connection */
nmsg = gsm411_msgb_alloc();
return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
}
static int gsm411_mmsms_cp_error(struct gsm411_smc_inst *inst, struct msgb *msg)
{
struct msgb *nmsg;
/* free stored msg */
if (inst->cp_msg) {
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
LOGP(DLSMS, LOGL_INFO, "Received CP-ERROR\n");
/* 5.3.4 enter idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, msg);
/* release MM connection */
nmsg = gsm411_msgb_alloc();
return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
}
static int gsm411_mmsms_rel_ind(struct gsm411_smc_inst *inst, struct msgb *msg)
{
struct msgb *nmsg;
/* free stored msg */
if (inst->cp_msg) {
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
LOGP(DLSMS, LOGL_INFO, "MM layer is released\n");
/* 5.3.4 enter idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
nmsg = gsm411_msgb_alloc();
inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg);
msgb_free(nmsg);
return 0;
}
/* abort SMC connection */
static int gsm411_mnsms_abort_req(struct gsm411_smc_inst *inst,
struct msgb *msg)
{
struct msgb *nmsg;
/* free stored msg */
if (inst->cp_msg) {
msgb_free(inst->cp_msg);
inst->cp_msg = NULL;
}
/* 5.3.4 go idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* send MMSMS_DATA_REQ with CP-ERROR */
inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, msg, GSM411_MT_CP_ERROR);
/* release MM connection */
nmsg = gsm411_msgb_alloc();
return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0);
}
/* statefull handling for MNSMS SAP messages */
static struct smcdownstate {
uint32_t states;
int type;
const char *name;
int (*rout) (struct gsm411_smc_inst *inst,
struct msgb *msg);
} smcdownstatelist[] = {
/* establish request */
{SBIT(GSM411_CPS_IDLE),
GSM411_MNSMS_EST_REQ,
"MNSMS-EST-REQ", gsm411_mnsms_est_req},
/* release request */
{ALL_STATES,
GSM411_MNSMS_REL_REQ,
"MNSMS-REL-REQ", gsm411_mnsms_rel_req},
/* data request */
{SBIT(GSM411_CPS_MM_ESTABLISHED),
GSM411_MNSMS_DATA_REQ,
"MNSMS-DATA-REQ", gsm411_mnsms_data_req},
/* abort request */
{ALL_STATES - SBIT(GSM411_CPS_IDLE),
GSM411_MNSMS_ABORT_REQ,
"MNSMS-ABORT-REQ", gsm411_mnsms_abort_req},
};
#define SMCDOWNSLLEN \
(sizeof(smcdownstatelist) / sizeof(struct smcdownstate))
/* message from upper layer */
int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg)
{
int i, rc;
/* find function for current state and message */
for (i = 0; i < SMCDOWNSLLEN; i++) {
if ((msg_type == smcdownstatelist[i].type)
&& (SBIT(inst->cp_state) & smcdownstatelist[i].states))
break;
}
if (i == SMCDOWNSLLEN) {
LOGP(DLSMS, LOGL_NOTICE, "Message %u unhandled at this state "
"%s.\n", msg_type, smc_state_names[inst->cp_state]);
msgb_free(msg);
return 0;
}
LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
smcdownstatelist[i].name, smc_state_names[inst->cp_state]);
rc = smcdownstatelist[i].rout(inst, msg);
return rc;
}
/* statefull handling for MMSMS SAP messages */
static struct smcdatastate {
uint32_t states;
int type, cp_type;
const char *name;
int (*rout) (struct gsm411_smc_inst *inst,
struct msgb *msg);
} smcdatastatelist[] = {
/* establish confirm */
{SBIT(GSM411_CPS_MM_CONN_PENDING),
GSM411_MMSMS_EST_CNF, 0,
"MMSMS-EST-CNF", gsm411_mmsms_est_cnf},
/* establish indication (CP DATA) */
{SBIT(GSM411_CPS_IDLE),
GSM411_MMSMS_EST_IND, GSM411_MT_CP_DATA,
"MMSMS-EST-IND (CP DATA)", gsm411_mmsms_cp_data},
/* data indication (CP DATA) */
{SBIT(GSM411_CPS_MM_ESTABLISHED),
GSM411_MMSMS_DATA_IND, GSM411_MT_CP_DATA,
"MMSMS-DATA-IND (CP DATA)", gsm411_mmsms_cp_data},
/* data indication (CP ACK) */
{SBIT(GSM411_CPS_WAIT_CP_ACK),
GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ACK,
"MMSMS-DATA-IND (CP ACK)", gsm411_mmsms_cp_ack},
/* data indication (CP ERROR) */
{ALL_STATES,
GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ERROR,
"MMSMS-DATA-IND (CP_ERROR)", gsm411_mmsms_cp_error},
/* release indication */
{ALL_STATES - SBIT(GSM411_CPS_IDLE),
GSM411_MMSMS_REL_IND, 0,
"MMSMS-REL-IND", gsm411_mmsms_rel_ind},
};
#define SMCDATASLLEN \
(sizeof(smcdatastatelist) / sizeof(struct smcdatastate))
/* message from lower layer
* WARNING: We must not free msg, since it will be performed by the
* lower layer. */
int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type,
struct msgb *msg, int cp_msg_type)
{
int i, rc;
/* find function for current state and message */
for (i = 0; i < SMCDATASLLEN; i++) {
/* state must machtch, MM message must match
* CP msg must match only in case of MMSMS_DATA_IND
*/
if ((msg_type == smcdatastatelist[i].type)
&& (SBIT(inst->cp_state) & smcdatastatelist[i].states)
&& (msg_type != GSM411_MMSMS_DATA_IND
|| cp_msg_type == smcdatastatelist[i].cp_type))
break;
}
if (i == SMCDATASLLEN) {
LOGP(DLSMS, LOGL_NOTICE, "Message 0x%x/%u unhandled at this "
"state %s.\n", msg_type, cp_msg_type,
smc_state_names[inst->cp_state]);
if (msg_type == GSM411_MMSMS_EST_IND
|| msg_type == GSM411_MMSMS_DATA_IND) {
struct msgb *nmsg;
LOGP(DLSMS, LOGL_NOTICE, "RX Unimplemented CP "
"msg_type: 0x%02x\n", msg_type);
/* 5.3.4 enter idle */
new_cp_state(inst, GSM411_CPS_IDLE);
/* indicate error */
gsm411_tx_cp_error(inst,
GSM411_CP_CAUSE_MSGTYPE_NOTEXIST);
/* send error indication to upper layer */
nmsg = gsm411_msgb_alloc();
inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg);
msgb_free(nmsg);
/* release MM connection */
nmsg = gsm411_msgb_alloc();
return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg,
0);
}
return 0;
}
LOGP(DLSMS, LOGL_INFO, "Message %s received in state %s\n",
smcdatastatelist[i].name, smc_state_names[inst->cp_state]);
rc = smcdatastatelist[i].rout(inst, msg);
return rc;
}