* 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
This commit is contained in:
Harald Welte 2009-02-16 22:52:23 +00:00
parent e1b1338c88
commit 09e38afbcc
4 changed files with 186 additions and 9 deletions

View File

@ -740,6 +740,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
arfcn, ts_number, subch, gsm_lchan_name(lchan->type),
ia.chan_desc.chan_nr, gsm_chreq_name(chreq_reason));
/* FIXME: Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);

View File

@ -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");

View File

@ -192,10 +192,15 @@ static void paging_T3113_expired(void *data)
sig_data.lchan = NULL,
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
if (req->cbfn)
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, req,
req->cbfn_param);
paging_remove_request(&req->bts->paging, req);
}
void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, int type) {
void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
int type, gsm_cbfn *cbfn, void *data)
{
struct gsm_bts_paging_state *bts_entry = &bts->paging;
struct gsm_paging_request *req;
@ -209,6 +214,8 @@ void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, int type
req->subscr = subscr_get(subscr);
req->bts = bts;
req->chan_type = type;
req->cbfn = cbfn;
req->cbfn_param = data;
req->T3113.cb = paging_T3113_expired;
req->T3113.data = req;
schedule_timer(&req->T3113, T3113_VALUE);
@ -224,6 +231,9 @@ void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr)
llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
entry) {
if (req->subscr == subscr) {
if (req->cbfn)
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
NULL, req, req->cbfn_param);
paging_remove_request(&bts->paging, req);
break;
}

View File

@ -188,7 +188,7 @@ void telnet_page(struct telnet_connection *connection, const char *imsi, int typ
if (!subscr)
return;
paging_request(bts, subscr, type);
paging_request(bts, subscr, type, NULL, NULL);
paging_update_buffer_space(bts, 100);
}
@ -230,7 +230,7 @@ void telnet_call(struct telnet_connection *connection, const char* imsi,
return;
/* TODO: add the origin */
gsm48_cc_tx_setup(lchan);
gsm48_cc_tx_setup(lchan, NULL);
}
void telnet_send_gsm_48(struct telnet_connection *connection) {