http://bugzilla.openmoko.org/cgi-bin/bugzilla/show_bug.cgi?id=100

From 454a60afe50aef60ad85d57275d75aade8e1e115
From: Andrzej Zaborowski <balrog@zabor.org>                                         
Date: Fri, 27 Jul 2007 19:39:15 +0200
Subject: [PATCH] Cell Broadcast message decoding and presentation in libgsmd-tool.

This time Cell Broadcast is tested to work, I should have made tests earlier. 
Now I'm correctly getting a CB message with the human readable name of the city
and district when I'm connecting.  In the previous patches the CB PDU was not
being decoded at all.
---
 include/gsmd/sms.h        |    5 ++
 include/gsmd/usock.h      |   42 +++++++++++++++++++
 include/libgsmd/sms.h     |    1 
 src/gsmd/sms_cb.c         |   42 ++++++++-----------
 src/gsmd/sms_pdu.c        |   29 ++++++++++++-
 src/libgsmd/libgsmd_sms.c |   14 ++++++
 src/util/event.c          |  100 ++++++++++++++++++++++++++++++++++++++++++++-
 7 files changed, 202 insertions(+), 31 deletions(-)

Index: gsm/include/gsmd/sms.h
===================================================================
--- gsm.orig/include/gsmd/sms.h	2007-08-15 00:33:33.000000000 +0800
+++ gsm/include/gsmd/sms.h	2007-08-15 00:33:51.000000000 +0800
@@ -8,10 +8,13 @@
 int sms_cb_init(struct gsmd *gsmd);
 int sms_cb_network_init(struct gsmd *gsmd);
 
-#define MAX_PDU_SIZE	180
+#define SMS_MAX_PDU_SIZE	180
+#define CBM_MAX_PDU_SIZE	88
+#define CBM_MAX_PDU_PAGES	15
 int sms_pdu_make_smssubmit(char *dest, const struct gsmd_sms_submit *src);
 int sms_pdu_to_msg(struct gsmd_sms_list *dst, const u_int8_t *src,
 		int pdulen, int len);
+int cbs_pdu_to_msg(struct gsmd_cbm *dst, u_int8_t *src, int pdulen, int len);
 
 int usock_rcv_sms(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, int len);
 int usock_rcv_cb(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, int len);
Index: gsm/include/gsmd/usock.h
===================================================================
--- gsm.orig/include/gsmd/usock.h	2007-08-15 00:33:00.000000000 +0800
+++ gsm/include/gsmd/usock.h	2007-08-15 00:33:51.000000000 +0800
@@ -365,6 +365,48 @@
 	struct __gsmd_sms_storage mem[3];
 } __attribute__ ((packed));
 
+/* Refer to GSM 03.41 subclause 9.3.1 - note: this indicates display mode too */
+enum gsmd_geographical_scope {
+	GSMD_SCOPE_CELL_WIDE_OPER	= 0,
+	GSMD_SCOPE_PLMN_WIDE,
+	GSMD_SCOPE_LOC_AREA_WIDE,
+	GSMD_SCOPE_CELL_WIDE,
+};
+
+enum gsmd_language {
+	GSMD_LANG_GERMAN	= 0,
+	GSMD_LANG_ENGLISH,
+	GSMD_LANG_ITALIAN,
+	GSMD_LANG_FRENCH,
+	GSMD_LANG_SPANISH,
+	GSMD_LANG_DUTCH,
+	GSMD_LANG_SWEDISH,
+	GSMD_LANG_DANISH,
+	GSMD_LANG_PORTUGUESE,
+	GSMD_LANG_FINNISH,
+	GSMD_LANG_NORWEGIAN,
+	GSMD_LANG_GREEK,
+	GSMD_LANG_TURKISH,
+	GSMD_LANG_HUNGARIAN,
+	GSMD_LANG_POLISH,
+	GSMD_LANG_UNSPECIFIED,
+};
+
+/* Refer to GSM 03.41 subclause 9.3 */
+struct gsmd_cbm {
+	struct {
+		enum gsmd_geographical_scope scope;
+		int msg_code;
+		int update_num;
+	} serial;
+	u_int16_t msg_id;
+	enum gsmd_language language;
+	u_int8_t coding_scheme;
+	int pages;
+	int page;
+	u_int8_t data[82];
+};
+
 /* Refer to GSM 07.07 subclause 8.12 */
 struct gsmd_phonebook_readrg {
 	u_int8_t index1;
Index: gsm/include/libgsmd/sms.h
===================================================================
--- gsm.orig/include/libgsmd/sms.h	2007-08-15 00:33:00.000000000 +0800
+++ gsm/include/libgsmd/sms.h	2007-08-15 00:33:51.000000000 +0800
@@ -90,6 +90,7 @@
 
 /* Packing of 7-bit characters, refer to GSM 03.38 subclause 6.1.2.1.1 */
 extern int unpacking_7bit_character(const struct gsmd_sms *src, char *dest);
+extern int cbm_unpacking_7bit_character(const char *src, char *dest);
 
 /* Refer to 3GPP TS 11.11 Annex B */
 extern int packing_UCS2_80(char *src, char *dest);
Index: gsm/src/gsmd/sms_cb.c
===================================================================
--- gsm.orig/src/gsmd/sms_cb.c	2007-08-15 00:33:33.000000000 +0800
+++ gsm/src/gsmd/sms_cb.c	2007-08-15 00:33:51.000000000 +0800
@@ -67,7 +67,7 @@
 	struct gsmd_ucmd *ucmd;
 	struct gsmd_sms_list msg;
 	int i, idx, stat, len, cr;
-	u_int8_t pdu[MAX_PDU_SIZE];
+	u_int8_t pdu[SMS_MAX_PDU_SIZE];
 
 	if (cmd->ret && cmd->ret != -255)
 		return 0;
@@ -86,7 +86,7 @@
 	msg.stat = stat;
 	msg.is_last = (cmd->ret == 0);
 	for (i = 0; resp[cr] >= '0' && resp[cr + 1] >= '0' &&
-			i < MAX_PDU_SIZE; i ++) {
+			i < SMS_MAX_PDU_SIZE; i ++) {
 		if (sscanf(resp + cr, "%2hhX", &pdu[i]) < 1) {
 			gsmd_log(GSMD_DEBUG, "malformed input (%i)\n", i);
 			return -EINVAL;
@@ -115,7 +115,7 @@
 	struct gsmd_ucmd *ucmd;
 	struct gsmd_sms_list msg;
 	int i, stat, len, cr;
-	u_int8_t pdu[MAX_PDU_SIZE];
+	u_int8_t pdu[SMS_MAX_PDU_SIZE];
 
 	if (cmd->ret)
 		return 0;
@@ -134,7 +134,7 @@
 	msg.stat = stat;
 	msg.is_last = 1;
 	for (i = 0; resp[cr] >= '0' && resp[cr + 1] >= '0' &&
-			i < MAX_PDU_SIZE; i ++) {
+			i < SMS_MAX_PDU_SIZE; i ++) {
 		if (sscanf(resp + cr, "%2hhX", &pdu[i]) < 1) {
 			gsmd_log(GSMD_DEBUG, "malformed input (%i)\n", i);
 			return -EINVAL;
@@ -475,7 +475,7 @@
 static int cmt_parse(char *buf, int len, const char *param, struct gsmd *gsmd)
 {
 	/* TODO: TEXT mode */
-	u_int8_t pdu[MAX_PDU_SIZE];
+	u_int8_t pdu[SMS_MAX_PDU_SIZE];
 	const char *comma = strchr(param, ',');
 	char *cr;
 	int i;
@@ -502,7 +502,8 @@
 	}
 
 	cr ++;
-	for (i = 0; cr[0] >= '0' && cr[1] >= '0' && i < MAX_PDU_SIZE; i ++) {
+	for (i = 0; cr[0] >= '0' && cr[1] >= '0' && i < SMS_MAX_PDU_SIZE;
+			i ++) {
 		if (sscanf(cr, "%2hhX", &pdu[i]) < 1) {
 			gsmd_log(GSMD_DEBUG, "malformed input (%i)\n", i);
 			talloc_free(ucmd);
@@ -531,11 +532,6 @@
 	if (!ucmd)
 		return -ENOMEM;
 
-	ucmd->hdr.version = GSMD_PROTO_VERSION;
-	ucmd->hdr.msg_type = GSMD_MSG_EVENT;
-	ucmd->hdr.msg_subtype = GSMD_EVT_IN_CBM;
-	ucmd->hdr.len = sizeof(*aux);
-
 	aux = (struct gsmd_evt_auxdata *) ucmd->buf;
 	if (sscanf(param, "\"%2[A-Z]\",%i", memstr, &aux->u.cbm.index) < 2) {
 		talloc_free(ucmd);
@@ -551,20 +547,20 @@
 static int cbm_parse(char *buf, int len, const char *param, struct gsmd *gsmd)
 {
 	/* TODO: TEXT mode */
-	u_int8_t pdu[MAX_PDU_SIZE];
+	u_int8_t pdu[CBM_MAX_PDU_SIZE];
 	char *cr;
 	int i;
 	struct gsmd_evt_auxdata *aux;
-	struct gsmd_sms_list *msg;
+	struct gsmd_cbm *msg;
 	struct gsmd_ucmd *ucmd = usock_build_event(GSMD_MSG_EVENT,
 			GSMD_EVT_IN_CBM, sizeof(struct gsmd_evt_auxdata) +
-			sizeof(struct gsmd_sms_list));
+			sizeof(struct gsmd_cbm));
 
 	if (!ucmd)
 		return -ENOMEM;
 
 	aux = (struct gsmd_evt_auxdata *) ucmd->buf;
-	msg = (struct gsmd_sms_list *) aux->data;
+	msg = (struct gsmd_cbm *) aux->data;
 
 	len = strtoul(param, &cr, 10);
 	if (cr[0] != '\n') {
@@ -573,7 +569,8 @@
 	}
 
 	cr ++;
-	for (i = 0; cr[0] >= '0' && cr[1] >= '0' && i < MAX_PDU_SIZE; i ++) {
+	for (i = 0; cr[0] >= '0' && cr[1] >= '0' && i < CBM_MAX_PDU_SIZE;
+			i ++) {
 		if (sscanf(cr, "%2hhX", &pdu[i]) < 1) {
 			gsmd_log(GSMD_DEBUG, "malformed input (%i)\n", i);
 			talloc_free(ucmd);
@@ -583,7 +580,7 @@
 	}
 
 	aux->u.cbm.inlined = 1;
-	if (sms_pdu_to_msg(msg, pdu, len, i)) {
+	if (cbs_pdu_to_msg(msg, pdu, len, i)) {
 		gsmd_log(GSMD_DEBUG, "malformed PDU\n");
 		talloc_free(ucmd);
 		return -EINVAL;
@@ -617,7 +614,7 @@
 static int cds_parse(char *buf, int len, const char *param, struct gsmd *gsmd)
 {
 	/* TODO: TEXT mode */
-	u_int8_t pdu[MAX_PDU_SIZE];
+	u_int8_t pdu[SMS_MAX_PDU_SIZE];
 	char *cr;
 	int i;
 	struct gsmd_evt_auxdata *aux;
@@ -639,7 +636,8 @@
 	}
 
 	cr ++;
-	for (i = 0; cr[0] >= '0' && cr[1] >= '0' && i < MAX_PDU_SIZE; i ++) {
+	for (i = 0; cr[0] >= '0' && cr[1] >= '0' && i < SMS_MAX_PDU_SIZE;
+			i ++) {
 		if (sscanf(cr, "%2hhX", &pdu[i]) < 1) {
 			gsmd_log(GSMD_DEBUG, "malformed input (%i)\n", i);
 			talloc_free(ucmd);
@@ -714,11 +712,5 @@
 	 */
 	ret |= gsmd_simplecmd(gsmd, "AT+CNMI=2,1,2,1,0");
 
-	/* Store into ME/TA and notify */
-	ret |= gsmd_simplecmd(gsmd, "AT+CSBS=1");
-
-	/* Store into ME/TA and notify */
-	ret |= gsmd_simplecmd(gsmd, "AT+CSDS=2");
-
 	return ret;
 }
Index: gsm/src/gsmd/sms_pdu.c
===================================================================
--- gsm.orig/src/gsmd/sms_pdu.c	2007-08-15 00:31:46.000000000 +0800
+++ gsm/src/gsmd/sms_pdu.c	2007-08-15 00:33:51.000000000 +0800
@@ -26,6 +26,7 @@
 
 #include <gsmd/gsmd.h>
 #include <gsmd/usock.h>
+#include <gsmd/sms.h>
 
 static int sms_number_bytelen(u_int8_t type, u_int8_t len)
 {
@@ -50,7 +51,7 @@
 	return 0;
 }
 
-static int sms_address2ascii(struct gsmd_addr *dst, u_int8_t *src)
+static int sms_address2ascii(struct gsmd_addr *dst, const u_int8_t *src)
 {
 	int i;
 
@@ -80,7 +81,7 @@
 }
 
 int sms_pdu_to_msg(struct gsmd_sms_list *dst,
-		u_int8_t *src, int pdulen, int len)
+		const u_int8_t *src, int pdulen, int len)
 {
 	int i, vpf;
 	if (len < 1 || len < 1 + src[0] + pdulen || pdulen < 1)
@@ -200,7 +201,7 @@
 }
 
 /* Refer to GSM 03.40 subclause 9.2.3.3, for SMS-SUBMIT */
-int sms_pdu_make_smssubmit(char *dest, struct gsmd_sms_submit *src)
+int sms_pdu_make_smssubmit(char *dest, const struct gsmd_sms_submit *src)
 {
 	/* FIXME: ALPHANUMERIC encoded addresses can be longer than 13B */
 	u_int8_t header[15 + GSMD_ADDR_MAXLEN];
@@ -259,3 +260,25 @@
 
 	return pos + len;
 }
+
+/* Refer to GSM 03.41 subclause 9.3 */
+int cbs_pdu_to_msg(struct gsmd_cbm *dst, u_int8_t *src, int pdulen, int len)
+{
+	if (len != pdulen || len != CBM_MAX_PDU_SIZE)
+		return 1;
+
+	dst->serial.scope = (src[0] >> 6) & 3;
+	dst->serial.msg_code = ((src[0] << 4) | (src[1] >> 4)) & 0x3ff;
+	dst->serial.update_num = src[1] & 0xf;
+
+	dst->msg_id = (src[2] << 8) | src[3];
+
+	dst->language = src[4] & 0xf;
+	dst->coding_scheme = ((src[4] >> 4) & 3) << 2;
+
+	dst->pages = src[5] & 0xf;
+	dst->page = src[5] >> 4;
+
+	memcpy(dst->data, src + 6, len - 6);
+	return 0;
+}
Index: gsm/src/libgsmd/libgsmd_sms.c
===================================================================
--- gsm.orig/src/libgsmd/libgsmd_sms.c	2007-08-15 00:33:00.000000000 +0800
+++ gsm/src/libgsmd/libgsmd_sms.c	2007-08-15 00:33:51.000000000 +0800
@@ -283,6 +283,20 @@
 	return i;
 }
 
+int cbm_unpacking_7bit_character(const char *src, char *dest)
+{
+	int i;
+	u_int8_t ch = 1;
+
+	for (i = 0; i < 93 && ch; i ++)
+		*(dest ++) = ch =
+			((src[(i * 7 + 7) >> 3] << (7 - ((i * 7 + 7) & 7))) |
+			 (src[(i * 7) >> 3] >> ((i * 7) & 7))) & 0x7f;
+	*dest = '\0';
+
+	return i;
+}
+
 /* Refer to 3GPP TS 11.11 Annex B */
 int packing_UCS2_80(char *src, char *dest)
 {
Index: gsm/src/util/event.c
===================================================================
--- gsm.orig/src/util/event.c	2007-08-15 00:33:33.000000000 +0800
+++ gsm/src/util/event.c	2007-08-15 00:33:51.000000000 +0800
@@ -34,10 +34,104 @@
 	return 0;
 }
 
-static int insms_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux)
+static int insms_handler(struct lgsm_handle *lh, int evt,
+		struct gsmd_evt_auxdata *aux)
 {
-	printf("EVENT: Incoming SMS stored at location %i\n", aux->u.sms.index);
+	struct gsmd_sms_list *sms;
+	char payload[GSMD_SMS_DATA_MAXLEN];
+	if (aux->u.sms.inlined) {
+		sms = (struct gsmd_sms_list *) aux->data;
+		printf("EVENT: Incoming SMS from/to %s%s, at %i%i-%i%i-%i%i "
+				"%i%i:%i%i:%i%i, GMT%c%i\n",
+				((sms->addr.type & __GSMD_TOA_TON_MASK) ==
+				 GSMD_TOA_TON_INTERNATIONAL) ? "+" : "",
+				sms->addr.number,
+				sms->time_stamp[0] & 0xf,
+				sms->time_stamp[0] >> 4,
+				sms->time_stamp[1] & 0xf,
+				sms->time_stamp[1] >> 4,
+				sms->time_stamp[2] & 0xf,
+				sms->time_stamp[2] >> 4,
+				sms->time_stamp[3] & 0xf,
+				sms->time_stamp[3] >> 4,
+				sms->time_stamp[4] & 0xf,
+				sms->time_stamp[4] >> 4,
+				sms->time_stamp[5] & 0xf,
+				sms->time_stamp[5] >> 4,
+				(sms->time_stamp[6] & 8) ? '-' : '+',
+				(((sms->time_stamp[6] << 4) |
+				  (sms->time_stamp[6] >> 4)) & 0x3f) >> 2);
+		if (sms->payload.coding_scheme == ALPHABET_DEFAULT) {
+			unpacking_7bit_character(&sms->payload, payload);
+			printf("\"%s\"\n", payload);
+		} else if (sms->payload.coding_scheme == ALPHABET_8BIT)
+			printf("8-bit encoded data\n");
+		else if (sms->payload.coding_scheme == ALPHABET_UCS2)
+			printf("Unicode-16 encoded text\n");
+	} else
+		printf("EVENT: Incoming SMS stored at location %i\n",
+				aux->u.sms.index);
+	return 0;
+}
+
+static int incbm_handler(struct lgsm_handle *lh, int evt,
+		struct gsmd_evt_auxdata *aux)
+{
+	struct gsmd_cbm *msg;
+	char payload[95];
+	static const char *scope_name[] = {
+		[GSMD_SCOPE_CELL_WIDE_OPER] = "immediate-display cell",
+		[GSMD_SCOPE_PLMN_WIDE] = "PLMN",
+		[GSMD_SCOPE_LOC_AREA_WIDE] = "Location Area",
+		[GSMD_SCOPE_CELL_WIDE] = "cell",
+	};
+	static const char *lang_name[] = {
+		[GSMD_LANG_GERMAN] = "German",
+		[GSMD_LANG_ENGLISH] = "English",
+		[GSMD_LANG_ITALIAN] = "Italian",
+		[GSMD_LANG_FRENCH] = "French",
+		[GSMD_LANG_SPANISH] = "Spanish",
+		[GSMD_LANG_DUTCH] = "Dutch",
+		[GSMD_LANG_SWEDISH] = "Swedish",
+		[GSMD_LANG_DANISH] = "Danish",
+		[GSMD_LANG_PORTUGUESE] = "Portuguese",
+		[GSMD_LANG_FINNISH] = "Finnish",
+		[GSMD_LANG_NORWEGIAN] = "Norwegian",
+		[GSMD_LANG_GREEK] = "Greek",
+		[GSMD_LANG_TURKISH] = "Turkish",
+		[GSMD_LANG_HUNGARIAN] = "Hungarian",
+		[GSMD_LANG_POLISH] = "Polish",
+		[GSMD_LANG_UNSPECIFIED] = "an unspecified language",
+	};
+	if (aux->u.cbm.inlined) {
+		msg = (struct gsmd_cbm *) aux->data;
+		printf("EVENT: Incoming %s-wide Cell Broadcast message in "
+				"%s (page %i of %i)\n",
+				scope_name[msg->serial.scope],
+				lang_name[msg->language],
+				msg->page, msg->pages);
+
+		if (msg->coding_scheme == ALPHABET_DEFAULT) {
+			cbm_unpacking_7bit_character(msg->data, payload);
+			printf("\"%s\"\n", payload);
+		} else if (msg->coding_scheme == ALPHABET_8BIT)
+			printf("8-bit encoded data\n");
+		else if (msg->coding_scheme == ALPHABET_UCS2)
+			printf("Unicode-16 encoded text\n");
+	} else
+		printf("EVENT: Incoming Cell Broadcast message stored at "
+				"location %i\n", aux->u.cbm.index);
+	return 0;
+}
 
+static int inds_handler(struct lgsm_handle *lh, int evt,
+		struct gsmd_evt_auxdata *aux)
+{
+	if (aux->u.ds.inlined)
+		printf("EVENT: Incoming Status Report\n");
+	else
+		printf("EVENT: Incoming Status Report stored at location %i\n",
+				aux->u.ds.index);
 	return 0;
 }
 
@@ -139,6 +233,8 @@
 	rc  = lgsm_evt_handler_register(lh, GSMD_EVT_IN_CALL, &incall_handler);
 	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_IN_CLIP, &clip_handler);
 	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_IN_SMS, &insms_handler);
+	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_IN_CBM, &incbm_handler);
+	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_IN_DS, &inds_handler);
 	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_OUT_COLP, &colp_handler);
 	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_NETREG, &netreg_handler);
 	rc |= lgsm_evt_handler_register(lh, GSMD_EVT_SIGNAL, &sigq_handler);
