aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2008-12-27 01:55:51 +0000
committerHarald Welte <laforge@gnumonks.org>2008-12-27 01:55:51 +0000
commit4b634544e6ac1d09efeae804269ed8cb808bd98e (patch)
tree70cdf31bfa1aa66094bb150ed9339b961f3fc8a2
parentfaaa49ca5136bc805b26240e783a09115b8ce075 (diff)
now we get up to the SETUP of MO calls
-rw-r--r--include/openbsc/abis_rsl.h16
-rw-r--r--include/openbsc/gsm_04_08.h21
-rw-r--r--include/openbsc/tlv.h22
-rw-r--r--src/abis_rsl.c111
-rw-r--r--src/chan_alloc.c11
-rw-r--r--src/gsm_04_08.c106
6 files changed, 261 insertions, 26 deletions
diff --git a/include/openbsc/abis_rsl.h b/include/openbsc/abis_rsl.h
index 219fdd3db..769672824 100644
--- a/include/openbsc/abis_rsl.h
+++ b/include/openbsc/abis_rsl.h
@@ -244,6 +244,22 @@ struct rsl_ie_chan_ident {
#endif
} __attribute__ ((packed));
+/* Chapter 9.3.22 */
+#define RLL_CAUSE_T200_EXPIRED 0x01
+#define RLL_CAUSE_REEST_REQ 0x02
+#define RLL_CAUSE_UNSOL_UA_RESP 0x03
+#define RLL_CAUSE_UNSOL_DM_RESP 0x04
+#define RLL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define RLL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define RLL_CAUSE_SEQ_ERR 0x07
+#define RLL_CAUSE_UFRM_INC_PARAM 0x08
+#define RLL_CAUSE_SFRM_INC_PARAM 0x09
+#define RLL_CAUSE_IFRM_INC_MBITS 0x0a
+#define RLL_CAUSE_IFRM_INC_LEN 0x0b
+#define RLL_CAUSE_FRM_UNIMPL 0x0c
+#define RLL_CAUSE_SABM_MF 0x0d
+#define RLL_CAUSE_SABM_INFO_NOTALL 0x0e
+
/* Chapter 9.3.26 */
#define RSL_ERRCLS_NORMAL 0x00
#define RSL_ERRCLS_RESOURCE_UNAVAIL 0x20
diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h
index dc5eba9ae..389daf596 100644
--- a/include/openbsc/gsm_04_08.h
+++ b/include/openbsc/gsm_04_08.h
@@ -46,6 +46,7 @@ struct gsm48_imm_ass {
u_int8_t mob_alloc[0];
};
+/* Chapter 10.5.1.3 */
struct gsm48_loc_area_id {
u_int8_t digits[3]; /* BCD! */
u_int16_t lac;
@@ -57,7 +58,6 @@ struct gsm48_loc_upd_req {
key_seq:4;
struct gsm48_loc_area_id lai;
u_int8_t classmark1;
- u_int8_t ie_mi;
u_int8_t mi_len;
u_int8_t mi[0];
} __attribute__ ((packed));
@@ -70,7 +70,7 @@ struct gsm48_hdr {
} __attribute__ ((packed));
/* Section 10.2 */
-#define GSM48_PDISC_CC 0x02
+#define GSM48_PDISC_CC 0x03
#define GSM48_PDISC_MM 0x05
#define GSM48_PDISC_RR 0x06
#define GSM48_PDISC_MM_GPRS 0x08
@@ -235,6 +235,23 @@ struct gsm48_hdr {
#define GSM_MI_TYPE_TMSI 0x04
#define GSM_MI_ODD 0x08
+#define GSM48_IE_MOBILE_ID 0x17
+
+/* Section 10.5.4.11 / Table 10.5.122 */
+#define GSM48_CAUSE_CS_GSM 0x60
+
+enum gsm48_cause_loc {
+ GSM48_CAUSE_LOC_USER = 0x00,
+ GSM48_CAUSE_LOC_PRN_S_LU = 0x01,
+ GSM48_CAUSE_LOC_PUN_S_LU = 0x02,
+ GSM48_CAUSE_LOC_TRANS_NET = 0x03,
+ GSM48_CAUSE_LOC_PUN_S_RU = 0x04,
+ GSM48_CAUSE_LOC_PRN_S_RU = 0x05,
+ /* not defined */
+ GSM48_CAUSE_LOC_INN_NET = 0x07,
+ GSM48_CAUSE_LOC_NET_BEYOND = 0x0a,
+};
+
struct msgb;
struct gsm_bts;
diff --git a/include/openbsc/tlv.h b/include/openbsc/tlv.h
index 38ca81150..70d0319e8 100644
--- a/include/openbsc/tlv.h
+++ b/include/openbsc/tlv.h
@@ -57,6 +57,15 @@ static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag,
return buf;
}
+static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag,
+ u_int16_t val)
+{
+ *buf++ = tag;
+ *buf++ = val >> 8;
+ *buf++ = val & 0xff;
+ return buf;
+}
+
static inline u_int8_t *msgb_tlv_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
{
u_int8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
@@ -69,6 +78,12 @@ static inline u_int8_t *msgb_tv_put(struct msgb *msg, u_int8_t tag, u_int8_t val
return tv_put(buf, tag, val);
}
+static inline u_int8_t *msgb_tv16_put(struct msgb *msg, u_int8_t tag, u_int16_t val)
+{
+ u_int8_t *buf = msgb_put(msg, 3);
+ return tv16_put(buf, tag, val);
+}
+
static inline u_int8_t *msgb_tlv_push(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
{
u_int8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
@@ -81,4 +96,11 @@ static inline u_int8_t *msgb_tv_push(struct msgb *msg, u_int8_t tag, u_int8_t va
return tv_put(buf, tag, val);
}
+static inline u_int8_t *msgb_tv16_push(struct msgb *msg, u_int8_t tag, u_int16_t val)
+{
+ u_int8_t *buf = msgb_push(msg, 3);
+ return tv16_put(buf, tag, val);
+}
+
+
#endif /* _TLV_H */
diff --git a/src/abis_rsl.c b/src/abis_rsl.c
index df34929ac..f6f5f2fbf 100644
--- a/src/abis_rsl.c
+++ b/src/abis_rsl.c
@@ -336,6 +336,62 @@ int rsl_chan_activate_sdcch4(struct gsm_bts_trx_ts *ts, int subslot)
return rsl_chan_activate(ts->trx->bts, chan_nr, 0x00, &cm, &ci, 0x01, 0x0f, 0x00);
}
+int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, u_int8_t ta)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+ /* FXIME: don't hardcode these!! */
+ u_int8_t encr_info = 0x01;
+ u_int8_t ms_power = 0x0f;
+ u_int8_t bs_power = 0x01;
+
+ u_int8_t chan_nr = lchan2chan_nr(lchan);
+ u_int16_t arfcn = lchan->ts->trx->arfcn;
+ struct rsl_ie_chan_mode cm;
+ struct rsl_ie_chan_ident ci;
+
+ /* FIXME: what to do with data calls ? */
+ cm.dtx_dtu = 0x00;
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ cm.spd_ind = RSL_CMOD_SPD_SIGN;
+ cm.chan_rt = RSL_CMOD_CRT_SDCCH;
+ cm.chan_rate = 0x00;
+ break;
+ case GSM_LCHAN_TCH_F:
+ cm.spd_ind = RSL_CMOD_SPD_SPEECH;
+ cm.chan_rt = RSL_CMOD_CRT_TCH_Bm;
+ cm.chan_rate = 0x11; /* speech coding alg version 2*/
+ break;
+ }
+
+ ci.chan_desc.iei = 0x64;
+ ci.chan_desc.chan_nr = chan_nr;
+ ci.chan_desc.oct3 = (TSC << 5) | ((arfcn & 0x3ff) >> 8);
+ ci.chan_desc.oct4 = arfcn & 0xff;
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
+ dh->chan_nr = chan_nr;
+
+ msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
+ /* For compatibility with Phase 1 */
+ msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
+ (u_int8_t *) &cm);
+ msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
+ (u_int8_t *) &ci);
+ /* FIXME: this shoould be optional */
+#if 0
+ msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
+ (u_int8_t *) &encr_info);
+ msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power);
+#endif
+ msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power);
+ msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
+
+ return abis_rsl_sendmsg(msg);
+}
+
int rsl_chan_release(struct gsm_lchan *lchan)
{
struct abis_rsl_dchan_hdr *dh;
@@ -427,7 +483,7 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id)
}
/* First push the L3 IE tag and length */
- msgb_tv_push(msg, RSL_IE_L3_INFO, l3_len);
+ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
/* Then push the RSL header */
rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
@@ -447,8 +503,27 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
/* BTS has confirmed channel activation, we now need
* to assign the activated channel to the MS */
+ if (rslh->ie_chan != RSL_IE_CHAN_NR)
+ return -EINVAL;
+
+ DEBUGP(DRSL, "Channel Activate ACK Channel 0x%02x\n", rslh->chan_nr);
+ return 0;
+}
+
+/* Chapter 8.4.3: Channel Activate NACK */
+static int rsl_rx_chan_act_nack(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
+ /* BTS has confirmed channel activation, we now need
+ * to assign the activated channel to the MS */
+ if (rslh->ie_chan != RSL_IE_CHAN_NR)
+ return -EINVAL;
+
+ DEBUGP(DRSL, "Channel Activate NACK Channel 0x%02x\n", rslh->chan_nr);
+
+ return 0;
}
static int abis_rsl_rx_dchan(struct msgb *msg)
@@ -460,11 +535,11 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
switch (rslh->c.msg_type) {
case RSL_MT_CHAN_ACTIV_ACK:
- DEBUGP(DRSL, "rsl_rx_dchan: Channel Activate ACK\n");
+ rc = rsl_rx_chan_act_ack(msg);
rc = rsl_rx_chan_act_ack(msg);
break;
case RSL_MT_CHAN_ACTIV_NACK:
- DEBUGP(DRSL, "rsl_rx_dchan: Channel Activate NACK\n");
+ rc = rsl_rx_chan_act_nack(msg);
break;
case RSL_MT_CONN_FAIL:
DEBUGP(DRSL, "rsl_rx_dchan: Connection Fail\n");
@@ -503,7 +578,7 @@ static int rsl_rx_error_rep(struct msgb *msg)
return -EINVAL;
cause_len = rslh->data[1];
- printf(stdout, "RSL ERROR REPORT, Cause ");
+ fprintf(stdout, "RSL ERROR REPORT, Cause ");
hexdump(&rslh->data[2], cause_len);
return 0;
@@ -577,11 +652,15 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
DEBUGP(DRSL, "Activating ARFCN(%u) TS(%u) SS(%u) lctype %u\n",
arfcn, ts_number, subch, lchan->type);
+#if 0
/* send CHANNEL ACTIVATION on RSL to BTS */
if (lchan->ts->pchan == GSM_PCHAN_CCCH_SDCCH4)
rsl_chan_activate_sdcch4(lchan->ts, subch);
else
rsl_chan_activate_tch_f(lchan->ts);
+#else
+ rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
+#endif
/* create IMMEDIATE ASSIGN 04.08 messge */
memset(&ia, 0, sizeof(ia));
@@ -589,7 +668,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
ia.proto_discr = GSM48_PDISC_RR;
ia.msg_type = GSM48_MT_RR_IMM_ASS;
ia.page_mode = GSM48_PM_NORMAL;
- ia.chan_desc.chan_nr = rsl_enc_chan_nr(RSL_CHAN_SDCCH4_ACCH, subch, ts_number);
+ ia.chan_desc.chan_nr = lchan2chan_nr(lchan);
ia.chan_desc.h0.h = 0;
ia.chan_desc.h0.arfcn_high = arfcn >> 8;
ia.chan_desc.h0.arfcn_low = arfcn & 0xff;
@@ -633,6 +712,16 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
return rc;
}
+static int rsl_rx_rll_err_ind(struct msgb *msg)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ u_int8_t *rlm_cause = rllh->data;
+
+ DEBUGP(DRLL, "RLL ERROR INDICATION: chan_nr=0x%02x cause=0x%02x\n",
+ rllh->chan_nr, rlm_cause[1]);
+
+ return 0;
+}
/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
0x02, 0x06,
0x01, 0x20,
@@ -648,19 +737,25 @@ static int abis_rsl_rx_rll(struct msgb *msg)
switch (rllh->c.msg_type) {
case RSL_MT_DATA_IND:
- DEBUGP(DRLL, "DATA INDICATION\n");
+ DEBUGP(DRLL, "DATA INDICATION chan_nr=0x%02x\n", rllh->chan_nr);
/* FIXME: Verify L3 info element */
msg->l3h = &rllh->data[3];
rc = gsm0408_rcvmsg(msg);
break;
case RSL_MT_EST_IND:
- DEBUGP(DRLL, "ESTABLISH INDICATION\n");
+ DEBUGP(DRLL, "ESTABLISH INDICATION chan_nr=0x%02x\n", rllh->chan_nr);
/* FIXME: Verify L3 info element */
msg->l3h = &rllh->data[3];
rc = gsm0408_rcvmsg(msg);
break;
- case RSL_MT_ERROR_IND:
case RSL_MT_REL_IND:
+ DEBUGP(DRLL, "RELEASE INDICATION chan_nr=0x%02x\n", rllh->chan_nr);
+ lchan_free(msg->lchan);
+ rc = 0;
+ break;
+ case RSL_MT_ERROR_IND:
+ rc = rsl_rx_rll_err_ind(msg);
+ break;
case RSL_MT_UNIT_DATA_IND:
fprintf(stderr, "unimplemented Abis RLL message type 0x%02x\n",
rllh->c.msg_type);
diff --git a/src/chan_alloc.c b/src/chan_alloc.c
index ca8690899..4ef5ff902 100644
--- a/src/chan_alloc.c
+++ b/src/chan_alloc.c
@@ -27,6 +27,7 @@
#include <openbsc/gsm_data.h>
#include <openbsc/chan_alloc.h>
+#include <openbsc/abis_nm.h>
struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
enum gsm_phys_chan_config pchan)
@@ -46,6 +47,14 @@ struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
return ts;
}
+static const enum abis_nm_chan_comb chcomb4pchan[] = {
+ [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH,
+ [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCCHComb,
+ [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull,
+ [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH,
+ /* FIXME: bounds check */
+};
/* Allocate a logical channel (TS) */
struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
@@ -58,6 +67,8 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
struct gsm_bts_trx_ts *ts = &trx->ts[j];
if (ts->pchan == GSM_PCHAN_NONE) {
ts->pchan = pchan;
+ /* set channel attribute on OML */
+ abis_nm_set_channel_attr(ts, chcomb4pchan[pchan]);
return ts;
}
}
diff --git a/src/gsm_04_08.c b/src/gsm_04_08.c
index 212b4ad6b..b478bc7f5 100644
--- a/src/gsm_04_08.c
+++ b/src/gsm_04_08.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <netinet/in.h>
#include <openbsc/msgb.h>
#include <openbsc/debug.h>
@@ -64,11 +65,11 @@ static void parse_lai(struct gsm_lai *lai, const struct gsm48_loc_area_id *lai48
static void to_bcd(u_int8_t *bcd, u_int16_t val)
{
- bcd[0] = val % 10;
+ bcd[2] = val % 10;
val = val / 10;
bcd[1] = val % 10;
val = val / 10;
- bcd[2] = val % 10;
+ bcd[0] = val % 10;
val = val / 10;
}
@@ -82,10 +83,16 @@ static void generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
lai48->digits[1] = bcd[2];
to_bcd(bcd, mnc);
+ /* FIXME: do we need three-digit MNC? See Table 10.5.3 */
+#if 0
lai48->digits[1] |= bcd[2] << 4;
lai48->digits[2] = bcd[0] | (bcd[1] << 4);
+#else
+ lai48->digits[1] |= 0xf << 4;
+ lai48->digits[2] = bcd[1] | (bcd[2] << 4);
+#endif
- lai48->lac = lac;
+ lai48->lac = htons(lac);
}
#define TMSI_LEN 4
@@ -93,12 +100,13 @@ static void generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
static void generate_mid_from_tmsi(u_int8_t *buf, u_int8_t *tmsi_bcd)
{
- buf[0] = MID_TMSI_LEN;
- buf[1] = 0xf0 | GSM_MI_TYPE_TMSI;
- buf[2] = tmsi_bcd[0];
- buf[3] = tmsi_bcd[1];
- buf[4] = tmsi_bcd[2];
- buf[5] = tmsi_bcd[3];
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = MID_TMSI_LEN;
+ buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
+ buf[3] = tmsi_bcd[0];
+ buf[4] = tmsi_bcd[1];
+ buf[5] = tmsi_bcd[2];
+ buf[6] = tmsi_bcd[3];
}
static struct msgb *gsm48_msgb_alloc(void)
@@ -111,14 +119,45 @@ static int gsm0408_sendmsg(struct msgb *msg)
if (msg->lchan)
msg->trx = msg->lchan->ts->trx;
+ msg->l3h = msg->data;
+
return rsl_data_request(msg, 0);
}
+static int gsm48_cc_tx_status(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ u_int8_t *cause, *call_state;
+
+ msg->lchan = lchan;
+
+ gh->proto_discr = GSM48_PDISC_CC;
+ gh->msg_type = GSM48_MT_CC_STATUS;
+
+ cause = msgb_put(msg, 3);
+ cause[0] = 2;
+ cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER;
+ cause[2] = 0x80 | 30; /* response to status inquiry */
+
+ call_state = msgb_put(msg, 1);
+ call_state[0] = 0xc0 | 0x00;
+
+ return gsm0408_sendmsg(msg);
+}
+
+static int gsm48_cc_rx_status_enq(struct msgb *msg)
+{
+ return gsm48_cc_tx_status(msg->lchan);
+}
+
static int gsm0408_rcv_cc(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t msg_type = gh->msg_type & 0xbf;
+ int rc = 0;
- switch (gh->msg_type & 0xbf) {
+ switch (msg_type) {
case GSM48_MT_CC_CALL_CONF:
/* Response to SETUP */
DEBUGP(DCC, "CALL CONFIRM\n");
@@ -137,18 +176,27 @@ static int gsm0408_rcv_cc(struct msgb *msg)
DEBUGP(DCC, "RELEASE\n");
/* need to respond with RELEASE_COMPLETE */
break;
- case GSM48_MT_CC_EMERG_SETUP:
- //DEBUGP(DCC, "EMERGENCY SETUP\n");
+ case GSM48_MT_CC_STATUS_ENQ:
+ rc = gsm48_cc_rx_status_enq(msg);
+ break;
+ case GSM48_MT_CC_DISCONNECT:
+ DEBUGP(DCC, "DISCONNECT\n");
+ break;
case GSM48_MT_CC_SETUP:
- //DEBUGP(DCC, "SETUP\n");
+ DEBUGP(DCC, "SETUP\n");
/* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
+ break;
+ case GSM48_MT_CC_EMERG_SETUP:
+ DEBUGP(DCC, "EMERGENCY SETUP\n");
+ /* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
+ break;
default:
fprintf(stderr, "Unimplemented GSM 04.08 msg type 0x%02x\n",
- gh->msg_type);
+ msg_type);
break;
}
- return 0;
+ return rc;
}
/* Chapter 9.2.14 : Send LOCATION UPDATE REJECT */
@@ -209,11 +257,12 @@ static int mm_loc_upd_req(struct msgb *msg)
mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
+ DEBUGP(DMM, "LUPDREQ: mi_type = 0x%02x\n", mi_type);
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
/* look up subscriber based on IMSI */
subscr = subscr_get_by_imsi(&lu->mi[1]);
- break;
+ break;
case GSM_MI_TYPE_TMSI:
/* look up the subscriber based on TMSI, request IMSI if it fails */
subscr = subscr_get_by_tmsi(&lu->mi[1]);
@@ -244,6 +293,29 @@ static int mm_loc_upd_req(struct msgb *msg)
return gsm0408_loc_upd_acc(msg->lchan, subscr->tmsi);
}
+static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ msg->lchan = lchan;
+
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
+
+ DEBUGP(DMM, "-> CM SERVICE ACK\n");
+
+ return gsm0408_sendmsg(msg);
+}
+
+static int gsm48_rx_mm_serv_req(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DMM, "CM SERVICE REQUEST\n");
+ return gsm48_tx_mm_serv_ack(msg->lchan);
+}
+
static int gsm0408_rcv_mm(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -259,6 +331,8 @@ static int gsm0408_rcv_mm(struct msgb *msg)
case GSM48_MT_MM_AUTH_RESP:
case GSM48_MT_MM_IMSI_DETACH_IND:
case GSM48_MT_MM_CM_SERV_REQ:
+ rc = gsm48_rx_mm_serv_req(msg);
+ break;
case GSM48_MT_MM_CM_REEST_REQ:
fprintf(stderr, "Unimplemented GSM 04.08 MM msg type 0x%02x\n",
gh->msg_type);