diff options
author | Harald Welte <laforge@gnumonks.org> | 2009-02-16 22:52:23 +0000 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2009-02-16 22:52:23 +0000 |
commit | 09e38afbcc9d62f75eae8867abe0d62678adef0f (patch) | |
tree | 6c8c384db2326867f65a2f2df868a6a0fffe4bcd /src/gsm_04_08.c | |
parent | e1b1338c8875be9dfa5c7a76b20204faacbc79d6 (diff) |
* introduce a callback function when paging completes (I know this is somewhat of an overlap
with the signals, but I think paging always has one reason and thus one caller wants to
get notified about completion, including a caller-specific context, etc)
* introduce TLV parser definitions for GSM 04.08
* parse and generate BCD number IE's for 04.08 call control
Diffstat (limited to 'src/gsm_04_08.c')
-rw-r--r-- | src/gsm_04_08.c | 178 |
1 files changed, 172 insertions, 6 deletions
diff --git a/src/gsm_04_08.c b/src/gsm_04_08.c index c69e246e3..d0c9ad5cd 100644 --- a/src/gsm_04_08.c +++ b/src/gsm_04_08.c @@ -46,6 +46,45 @@ #define GSM48_ALLOC_SIZE 1024 #define GSM48_ALLOC_HEADROOM 128 +static const struct tlv_definition rsl_att_tlvdef = { + .def = { + [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, + [GSM48_IE_UTC] = { TLV_TYPE_TV }, + [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, + + [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV }, + [GSM48_IE_CAUSE] = { TLV_TYPE_TLV }, + [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV }, + [GSM48_IE_ALERT] = { TLV_TYPE_TLV }, + [GSM48_IE_FACILITY] = { TLV_TYPE_TLV }, + [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV }, + [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV }, + [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV }, + [GSM48_IE_SIGNAL] = { TLV_TYPE_TV }, + [GSM48_IE_CONN_NUM] = { TLV_TYPE_TLV }, + [GSM48_IE_CONN_SUBADDR] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV }, + [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV }, + [GSM48_IE_USER_USER] = { TLV_TYPE_TLV }, + [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV }, + [GSM48_IE_MORE_DATA] = { TLV_TYPE_T }, + [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T }, + [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T }, + [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T }, + /* FIXME: more elements */ + }, +}; + + static int gsm48_tx_simple(struct gsm_lchan *lchan, u_int8_t pdisc, u_int8_t msg_type); static void schedule_reject(struct gsm_lchan *lchan); @@ -157,6 +196,90 @@ int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi) return 7; } +static const char bcd_num_digits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '*', '#', 'a', 'b', 'c', '\0' +}; + +/* decode a 'called party BCD number' as in 10.5.4.7 */ +u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv) +{ + u_int8_t in_len = bcd_lv[0]; + int i; + + if (in_len < 1) + return 0; + + for (i = 2; i <= in_len; i++) { + /* lower nibble */ + output_len--; + if (output_len <= 1) + break; + *output++ = bcd_num_digits[bcd_lv[i] & 0xf]; + + /* higher nibble */ + output_len--; + if (output_len <= 1) + break; + *output++ = bcd_num_digits[bcd_lv[i] >> 4]; + } + if (output_len >= 1) + *output++ = '\0'; + + /* return number type / calling plan */ + return bcd_lv[1] & 0x3f; +} + +/* convert a single ASCII character to call-control BCD */ +static int asc_to_bcd(const char asc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) { + if (bcd_num_digits[i] == asc) + return i; + } + return -EINVAL; +} + +/* convert a ASCII phone number to 'called party BCD number' */ +int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len, + u_int8_t type, const char *input) +{ + int in_len = strlen(input); + int i; + u_int8_t *bcd_cur = bcd_lv + 2; + + if (in_len/2 + 1 > max_len) + return -EIO; + + /* two digits per byte, plus type byte */ + bcd_lv[0] = in_len/2 + 1; + if (in_len % 2) + bcd_lv[0]++; + + /* if the caller wants to create a valid 'calling party BCD + * number', then the extension bit MUST NOT be set. For the + * 'called party BCD number' it MUST be set. *sigh */ + bcd_lv[1] = type; + + for (i = 0; i < in_len; i++) { + int rc = asc_to_bcd(input[i]); + if (rc < 0) + return rc; + if (i % 2 == 0) + *bcd_cur = rc; + else + *bcd_cur++ |= (rc << 4); + } + /* append padding nibble in case of odd length */ + if (i % 2) + *bcd_cur++ |= 0xf0; + + /* return how many bytes we used */ + return (bcd_cur - bcd_lv); +} + struct msgb *gsm48_msgb_alloc(void) { return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM); @@ -238,7 +361,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi) ret = gsm48_sendmsg(msg); - ret = gsm48_cc_tx_setup(lchan); + //ret = gsm48_cc_tx_setup(lchan); ret = gsm48_tx_mm_info(lchan); return ret; @@ -695,7 +818,7 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg) if (!subscr) { DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: close channel? */ + /* FIXME: request id? close channel? */ return -EINVAL; } DEBUGP(DRR, "<- Channel was requested by %s\n", @@ -811,6 +934,26 @@ static int gsm48_tx_simple(struct gsm_lchan *lchan, return gsm48_sendmsg(msg); } +/* call-back from paging the B-end of the connection */ +static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct gsm_call *call = param; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + switch (event) { + case GSM_PAGING_SUCCEEDED: + /* send SETUP request to called party */ + //gsm48_cc_tx_setup(lchan, call->subscr); + break; + case GSM_PAGING_EXPIRED: + /* notify caller that we cannot reach called party */ + break; + } +} + static int gsm48_cc_rx_status_enq(struct msgb *msg) { return gsm48_cc_tx_status(msg->lchan); @@ -820,7 +963,11 @@ static int gsm48_cc_rx_setup(struct msgb *msg) { struct gsm_call *call = &msg->lchan->call; struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct gsm_subscriber *called_subscr; + char called_number[(43-2)*2 + 1] = "\0"; + struct tlv_parsed tp; + u_int8_t num_type; int ret; if (call->state == GSM_CSTATE_NULL || @@ -833,9 +980,14 @@ static int gsm48_cc_rx_setup(struct msgb *msg) DEBUGP(DCC, "SETUP(tid=0x%02x)\n", call->transaction_id); - /* Parse the number that was dialed and lookup subscriber */ - called_subscr = NULL; + tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len); + if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) + goto err; + /* Parse the number that was dialed and lookup subscriber */ + num_type = decode_bcd_number(called_number, sizeof(called_number), + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + called_subscr = subscr_get_by_extension(called_number); if (!called_subscr) { DEBUGP(DCC, "could not find subscriber, RELEASE\n"); put_lchan(msg->lchan); @@ -843,8 +995,13 @@ static int gsm48_cc_rx_setup(struct msgb *msg) GSM48_MT_CC_RELEASE_COMPL); } + subscr_get(msg->lchan->subscr); + call->subscr = msg->lchan->subscr; + call->called_subscr = called_subscr; + /* start paging of the receiving end of the call */ - paging_request(msg->trx->bts, called_subscr, RSL_CHANNEED_TCH_F); + paging_request(msg->trx->bts, called_subscr, RSL_CHANNEED_TCH_F, + setup_trig_pag_evt, call); /* send a CALL PROCEEDING message to the MO */ ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC, @@ -852,11 +1009,16 @@ static int gsm48_cc_rx_setup(struct msgb *msg) /* change TCH/F mode to voice */ return gsm48_tx_chan_mode_modify(msg->lchan, 0x01); + +err: + /* FIXME: send some kind of RELEASE */ + return 0; } static const u_int8_t calling_bcd[] = { 0xb9, 0x83, 0x32, 0x24 }; -int gsm48_cc_tx_setup(struct gsm_lchan *lchan) +int gsm48_cc_tx_setup(struct gsm_lchan *lchan, + struct gsm_subscriber *calling_subscriber) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; @@ -871,6 +1033,8 @@ int gsm48_cc_tx_setup(struct gsm_lchan *lchan) gh->proto_discr = GSM48_PDISC_CC; gh->msg_type = GSM48_MT_CC_SETUP; + + /* FIXME: use actual number of the caller */ msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE); msgb_tlv_put(msg, GSM48_IE_CALLING_BCD, sizeof(calling_bcd), calling_bcd); @@ -891,6 +1055,7 @@ static int gsm0408_rcv_cc(struct msgb *msg) case GSM48_MT_CC_CALL_CONF: /* Response to SETUP */ DEBUGP(DCC, "CALL CONFIRM\n"); + /* FIXME: we now need to MODIFY the channel */ break; case GSM48_MT_CC_RELEASE_COMPL: /* Answer from MS to RELEASE */ @@ -899,6 +1064,7 @@ static int gsm0408_rcv_cc(struct msgb *msg) break; case GSM48_MT_CC_ALERTING: DEBUGP(DCC, "ALERTING\n"); + /* FIXME: forward ALERTING to other party */ break; case GSM48_MT_CC_CONNECT: DEBUGP(DCC, "CONNECT\n"); |