libmsc/gsm_04_80.c: make the API abstract from ss_request struct

There is no need to pass a pointer to a ss_request struct when
calling the gsm0480_send_ussd_* functions, because they only
use both transaction ID and InvokeID from there, which may
be passed directly.

This change allows one to use this API without parsing the
whole GSM 04.80 message, or when parsing is failed. Moreover,
if InvokeID is not available, one can pass any incorrect,
(e.g. negative) value, so the universal NULL tag will be used.
Finally, setting a TI flag is also up to the caller.

Change-Id: I13d5abbfdcf8238ebaf0566c420f09cd9255b648
diff --git a/include/osmocom/msc/gsm_04_80.h b/include/osmocom/msc/gsm_04_80.h
index fb057c8..7d63088 100644
--- a/include/osmocom/msc/gsm_04_80.h
+++ b/include/osmocom/msc/gsm_04_80.h
@@ -7,14 +7,14 @@
 struct gsm_subscriber_connection;
 
 int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
-			       const char* response_text,
-			       const struct ss_request *req);
+			       uint8_t transaction_id, uint8_t invoke_id,
+			       const char *response_text);
 int gsm0480_send_ussd_return_error(struct gsm_subscriber_connection *conn,
-				   const struct ss_request *req,
+				   uint8_t transaction_id, uint8_t invoke_id,
 				   uint8_t error_code);
 int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
-			     const struct ss_request *req,
-			     uint8_t error_tag, uint8_t error_code);
+			     uint8_t transaction_id, int invoke_id,
+			     uint8_t problem_tag, uint8_t problem_code);
 
 int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level,
 			 const char *text);
diff --git a/src/libmsc/gsm_04_80.c b/src/libmsc/gsm_04_80.c
index 8799fcb..32e8e23 100644
--- a/src/libmsc/gsm_04_80.c
+++ b/src/libmsc/gsm_04_80.c
@@ -59,10 +59,28 @@
 	return data;
 }
 
+static inline unsigned char *msgb_push_NULL(struct msgb *msgb)
+{
+	uint8_t *data = msgb_push(msgb, 2);
 
-/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
+	data[0] = ASN1_NULL_TYPE_TAG;
+	data[1] = 0;
+	return data;
+}
+
+
+/*! Send a MT RELEASE COMPLETE message with USSD-response,
+ *  wrapped into the ReturnResult component (see section 3.6.1).
+ *
+ * \param[in]  conn            Active subscriber connection
+ * \param[in]  transaction_id  Transaction ID with TI flag set
+ * \param[in]  invoke_id       InvokeID of the request
+ * \param[in]  response_text   The response text
+ * \return     result of \ref msc_tx_dtap
+ */
 int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
-			       const char *response_text, const struct ss_request *req)
+			       uint8_t transaction_id, uint8_t invoke_id,
+			       const char *response_text)
 {
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP");
 	struct gsm48_hdr *gh;
@@ -91,7 +109,7 @@
 	msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG);
 
 	/* Pre-pend the invoke ID */
-	msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+	msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
 
 	/* Wrap this up as a Return Result component */
 	msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT);
@@ -101,15 +119,24 @@
 
 	/* And finally pre-pend the L3 header */
 	gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
-	gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id
-					| (1<<7);  /* TI direction = 1 */
+	gh->proto_discr  = GSM48_PDISC_NC_SS;
+	gh->proto_discr |= transaction_id << 4;
 	gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
 
 	return msc_tx_dtap(conn, msg);
 }
 
+/*! Send a MT RELEASE COMPLETE message with ReturnError component
+ *  (see section 3.6.1) and given error code (see section 3.6.6).
+ *
+ * \param[in]  conn            Active subscriber connection
+ * \param[in]  transaction_id  Transaction ID with TI flag set
+ * \param[in]  invoke_id       InvokeID of the request
+ * \param[in]  error_code      Error code (section 4.5)
+ * \return     result of \ref msc_tx_dtap
+ */
 int gsm0480_send_ussd_return_error(struct gsm_subscriber_connection *conn,
-	const struct ss_request *req, uint8_t error_code)
+	uint8_t transaction_id, uint8_t invoke_id, uint8_t error_code)
 {
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD ERR");
 	struct gsm48_hdr *gh;
@@ -118,7 +145,7 @@
 	msgb_push_TLV1(msg, GSM_0480_ERROR_CODE_TAG, error_code);
 
 	/* Before it insert the invoke ID */
-	msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+	msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
 
 	/* Wrap this up as a Reject component */
 	msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_ERROR);
@@ -128,25 +155,45 @@
 
 	/* And finally pre-pend the L3 header */
 	gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
-	gh->proto_discr = GSM48_PDISC_NC_SS;
-	gh->proto_discr |= req->transaction_id | (1<<7);  /* TI direction = 1 */
+	gh->proto_discr  = GSM48_PDISC_NC_SS;
+	gh->proto_discr |= transaction_id << 4;
 	gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
 
 	return msc_tx_dtap(conn, msg);
 }
 
+/*! Send a MT RELEASE COMPLETE message with Reject component
+ *  (see section 3.6.1) and given error code (see section 3.6.7).
+ *
+ * \param[in]  conn            Active subscriber connection
+ * \param[in]  transaction_id  Transaction ID with TI flag set
+ * \param[in]  invoke_id       InvokeID of the request
+ * \param[in]  problem_tag     Problem code tag (table 3.13)
+ * \param[in]  problem_code    Problem code (tables 3.14-17)
+ * \return     result of \ref msc_tx_dtap
+ *
+ * Note: if InvokeID is not available, e.g. when message parsing
+ * failed, any incorrect value can be passed (0x00 > x > 0xff), so
+ * the universal NULL-tag (see table 3.6) will be used instead.
+ */
 int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
-			     const struct ss_request *req,
-			     uint8_t error_tag, uint8_t error_code)
+			     uint8_t transaction_id, int invoke_id,
+			     uint8_t problem_tag, uint8_t problem_code)
 {
 	struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ");
 	struct gsm48_hdr *gh;
 
 	/* First insert the problem code */
-	msgb_push_TLV1(msg, error_tag, error_code);
+	msgb_push_TLV1(msg, problem_tag, problem_code);
 
-	/* Before it insert the invoke ID */
-	msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
+	/**
+	 * If the Invoke ID is not available, Universal Null
+	 * (table 3.9) with length = 0 shall be used.
+	 */
+	if (invoke_id < 0 || invoke_id > 255)
+		msgb_push_NULL(msg);
+	else
+		msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
 
 	/* Wrap this up as a Reject component */
 	msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT);
@@ -156,8 +203,8 @@
 
 	/* And finally pre-pend the L3 header */
 	gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
-	gh->proto_discr = GSM48_PDISC_NC_SS;
-	gh->proto_discr |= req->transaction_id | (1<<7);  /* TI direction = 1 */
+	gh->proto_discr  = GSM48_PDISC_NC_SS;
+	gh->proto_discr |= transaction_id << 4;
 	gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
 
 	return msc_tx_dtap(conn, msg);
diff --git a/src/libmsc/gsm_09_11.c b/src/libmsc/gsm_09_11.c
index fc588f5..799dfaa 100644
--- a/src/libmsc/gsm_09_11.c
+++ b/src/libmsc/gsm_09_11.c
@@ -46,7 +46,7 @@
 
 /* A network-specific handler function */
 static int send_own_number(struct gsm_subscriber_connection *conn,
-			   const struct ss_request *req)
+			   uint8_t tid, uint8_t invoke_id)
 {
 	char *own_number = conn->vsub->msisdn;
 	char response_string[GSM_EXTENSION_LENGTH + 20];
@@ -56,7 +56,7 @@
 
 	/* Need trailing CR as EOT character */
 	snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
-	return gsm0480_send_ussd_response(conn, response_string, req);
+	return gsm0480_send_ussd_response(conn, tid, invoke_id, response_string);
 }
 
 /* Entry point for call independent MO SS messages */
@@ -121,7 +121,8 @@
 	if (!rc) {
 		LOGP(DMM, LOGL_ERROR, "SS/USSD message parsing error, "
 			"rejecting request...\n");
-		gsm0480_send_ussd_reject(conn, &req, GSM_0480_PROBLEM_CODE_TAG_GENERAL,
+		gsm0480_send_ussd_reject(conn, tid, -1,
+			GSM_0480_PROBLEM_CODE_TAG_GENERAL,
 			GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
 		/* The GSM 04.80 API uses inverted codes (0 means error) */
 		return -EPROTO;
@@ -131,8 +132,8 @@
 	if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) {
 		if (req.ss_code > 0) {
 			/* Assume interrogateSS or modification of it and reject */
-			return gsm0480_send_ussd_return_error(conn, &req,
-				GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION);
+			return gsm0480_send_ussd_return_error(conn, tid,
+				req.invoke_id, GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION);
 		}
 		/* Still assuming a Release-Complete and returning */
 		return 0;
@@ -141,10 +142,11 @@
 	msc_subscr_conn_communicating(conn);
 	if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) {
 		DEBUGP(DMM, "USSD: Own number requested\n");
-		rc = send_own_number(conn, &req);
+		rc = send_own_number(conn, tid, req.invoke_id);
 	} else {
 		DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text);
-		rc = gsm0480_send_ussd_return_error(conn, &req,
+		rc = gsm0480_send_ussd_return_error(conn,
+			tid, req.invoke_id,
 			GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE);
 	}