SMPP: Add new 'dcs_transparent' ESME setting

If an ESME has the dcs_transparent config flag, then the TP-DCS
of MO-SMS is transparently passed to SMPP and not converted to SMPP
specific data_coding values.

This is needed in cases where ESMEs actually care about the exact
TP-DCS, as the conversion from TP-DCS to SMPP data_coding is not
bijective.
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
index fdef90e..887f0eb 100644
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ b/openbsc/src/libmsc/smpp_openbsc.c
@@ -172,7 +172,7 @@
 		return ESME_RUNKNOWNERR;
 	}
 
-	if (mode ==  MODE_7BIT) {
+	if (mode == MODE_7BIT) {
 		uint8_t ud_len = 0, padbits = 0;
 		sms->data_coding_scheme = GSM338_DCS_1111_7BIT;
 		if (sms->ud_hdr_ind) {
@@ -429,6 +429,7 @@
 {
 	struct deliver_sm_t deliver;
 	uint8_t dcs;
+	int mode;
 
 	memset(&deliver, 0, sizeof(deliver));
 	deliver.command_length	= 0;
@@ -465,14 +466,38 @@
 	deliver.priority_flag	= 0;
 	deliver.registered_delivery = 0;
 
+	/* Figure out SMPP DCS from TP-DCS */
 	dcs = sms->data_coding_scheme;
-	if (dcs == GSM338_DCS_1111_7BIT ||
-	   ((dcs & 0xE0000000) == 0 && (dcs & 0xC) == 0)) {
+	if ((dcs & 0xFC) == GSM338_DCS_1111_7BIT) {
+		deliver.data_coding = 0x01;
+		mode = MODE_7BIT;
+	} else if ((dcs & 0xE0) == 0 && (dcs & 0xC) == 0) {
+		deliver.data_coding = 0x01;
+		mode = MODE_7BIT;
+	} else if ((dcs & 0xFC) == GSM338_DCS_1111_8BIT_DATA) {
+		deliver.data_coding = 0x02;
+		mode = MODE_8BIT;
+	} else if ((dcs & 0xE0) == 0 && (dcs & 0xC) == 4) {
+		deliver.data_coding = 0x02;
+		mode = MODE_8BIT;
+	} else if ((dcs & 0xE0) == 0 && (dcs & 0xC) == 8) {
+		deliver.data_coding = 0x08;	/* UCS-2 */
+		mode = MODE_8BIT;
+	} else {
+		LOGP(DLSMS, LOGL_ERROR, "SMPP MO Unknown Data Coding 0x%02x\n",
+			dcs);
+		return -1;
+	}
+
+	/* Transparently pass on DCS via SMPP if requested */
+	if (esme->acl->dcs_transparent)
+		deliver.data_coding = dcs;
+
+	if (mode == MODE_7BIT) {
 		uint8_t *dst = deliver.short_message;
 
 		/* SMPP has this strange notion of putting 7bit SMS in
 		 * an octet-aligned mode */
-		deliver.data_coding = 0x01;
 		if (sms->ud_hdr_ind) {
 			/* length (bytes) of UDH inside UD */
 			uint8_t udh_len = sms->user_data[0] + 1;
@@ -484,13 +509,9 @@
 		}
 		/* add decoded text */
 		deliver.sm_length += gsm_7bit_expand((char *)dst, sms->user_data, sms->user_data_len, sms->ud_hdr_ind);
-	} else if (dcs == GSM338_DCS_1111_8BIT_DATA ||
-		   ((dcs & 0xE0000000) == 0 && (dcs & 0xC) == 4)) {
-		deliver.data_coding = 0x02;
+	} else {
 		deliver.sm_length = sms->user_data_len;
 		memcpy(deliver.short_message, sms->user_data, deliver.sm_length);
-	} else if ((dcs & 0xE0000000) == 0 && (dcs & 0xC) == 8) {
-		deliver.data_coding = 0x08;	/* UCS-2 */
 		deliver.sm_length = sms->user_data_len;
 		memcpy(deliver.short_message, sms->user_data, deliver.sm_length);
 	}
diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h
index e72f81f..86fbeb5 100644
--- a/openbsc/src/libmsc/smpp_smsc.h
+++ b/openbsc/src/libmsc/smpp_smsc.h
@@ -61,6 +61,7 @@
 	int default_route;
 	int deliver_src_imsi;
 	int osmocom_ext;
+	int dcs_transparent;
 	struct llist_head route_list;
 };
 
diff --git a/openbsc/src/libmsc/smpp_vty.c b/openbsc/src/libmsc/smpp_vty.c
index 4c64a3c..adea959 100644
--- a/openbsc/src/libmsc/smpp_vty.c
+++ b/openbsc/src/libmsc/smpp_vty.c
@@ -361,6 +361,29 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_esme_dcs_transp, cfg_esme_dcs_transp_cmd,
+	"dcs-transparent",
+	"Enable the transparent pass-through of TP-DCS to SMPP DataCoding")
+{
+	struct osmo_smpp_acl *acl = vty->index;
+
+	acl->dcs_transparent = 1;
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_esme_no_dcs_transp, cfg_esme_no_dcs_transp_cmd,
+	"no dcs-transparent", NO_STR
+	"Disable the transparent pass-through of TP-DCS to SMPP DataCoding")
+{
+	struct osmo_smpp_acl *acl = vty->index;
+
+	acl->dcs_transparent = 0;
+
+	return CMD_SUCCESS;
+}
+
+
 static void dump_one_esme(struct vty *vty, struct osmo_esme *esme)
 {
 	char host[128], serv[128];
@@ -419,6 +442,8 @@
 		vty_out(vty, "  deliver-src-imsi%s", VTY_NEWLINE);
 	if (acl->osmocom_ext)
 		vty_out(vty, "  osmocom-extensions%s", VTY_NEWLINE);
+	if (acl->dcs_transparent)
+		vty_out(vty, "  dcs-transparent%s", VTY_NEWLINE);
 
 	llist_for_each_entry(r, &acl->route_list, list)
 		write_esme_route_single(vty, r);
@@ -457,6 +482,8 @@
 	install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd);
 	install_element(SMPP_ESME_NODE, &cfg_esme_osmo_ext_cmd);
 	install_element(SMPP_ESME_NODE, &cfg_esme_no_osmo_ext_cmd);
+	install_element(SMPP_ESME_NODE, &cfg_esme_dcs_transp_cmd);
+	install_element(SMPP_ESME_NODE, &cfg_esme_no_dcs_transp_cmd);
 	install_element(SMPP_ESME_NODE, &ournode_exit_cmd);
 
 	install_element_ve(&show_esme_cmd);