[RFC] SMS support

andrzej zaborowski balrogg at gmail.com
Fri Jul 6 07:11:12 CEST 2007


Here's a version that modifies the lowlevel parser to correctly deal
with the SMS submit and SMS write commands.  While at it, it should
also fix partial writes to the UART socket (i.e interrupted writes
will be continued). Here's how this works:

usock.c submits a command with more than one line, lines separated
with '\n', looking something like this:
AT+CMGS=<length>\n<PDU>
all as a single atcmd. Now atcmd.c will send only the first line (from
start, up to '\n') instead of the entire command, and wait for a "> "
prompt before sending each next line. The command is not removed from
pending_atcmds until it is completely written to the UART. The parser
should accept unsolicited responses from the modem even in the middle
of transmitting a multiline command, but obviously I couldn't test
this.

This patch applies on top of pH5's multiline responses patch. I
verified the issue with usock stalling after processing AT+CMGL. It
turned out to be a libgsmd-tool bug and not in the multiline responses
parsing, and the attached patch contains an attempt to fix this too,
so AT+CMGL now works correctly. The issue was that libgsmd-tool would
send everything it received from the gsmd socket, back to gsmd as a
new AT command (which was mostly gibberish).

Cheers,
Andrew
-------------- next part --------------
From 3e5832569d3b29a90b29b5d5ac0ffad4765bcff3 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <balrog at zabor.org>
Date: Fri, 6 Jul 2007 06:55:12 +0200
Subject: [PATCH] SMS hacks 2

---
 include/gsmd/gsmd.h         |    8 ++-
 include/gsmd/usock.h        |   35 ++++++++-
 include/gsmd/vendorplugin.h |    2 +-
 include/libgsmd/sms.h       |    2 +-
 src/gsmd/atcmd.c            |  130 +++++++++++++++++++-------------
 src/gsmd/sms_cb.c           |   19 ++++-
 src/gsmd/usock.c            |  175 +++++++++++++++++++++++++++++++++++++-----
 src/gsmd/vendor_ti.c        |    2 +-
 src/libgsmd/libgsmd_sms.c   |   26 +++++--
 src/util/atcmd.c            |    8 +-
 11 files changed, 320 insertions(+), 91 deletions(-)

diff --git a/include/gsmd/gsmd.h b/include/gsmd/gsmd.h
index e95cbbc..a789a5b 100644
--- a/include/gsmd/gsmd.h
+++ b/include/gsmd/gsmd.h
@@ -27,6 +27,7 @@ struct gsmd_atcmd {
 	u_int32_t buflen;
 	u_int16_t id;
 	u_int8_t flags;
+	char *cur;
 	char buf[];
 };
 
@@ -36,6 +37,8 @@ enum llparse_state {
 	LLPARSE_STATE_IDLE_LF,		/* LF before response (V1) */
 	LLPARSE_STATE_RESULT,		/* within result payload */
 	LLPARSE_STATE_RESULT_CR,	/* CR after result */
+	LLPARSE_STATE_PROMPT,		/* within a "> " prompt */
+	LLPARSE_STATE_PROMPT_SPC,	/* a complete "> " prompt */
 	LLPARSE_STATE_ERROR,		/* something went wrong */
 					/* ... idle again */
 };
@@ -49,6 +52,7 @@ struct llparser {
 	unsigned int flags;
 	void *ctx;
 	int (*cb)(const char *buf, int len, void *ctx);
+	int (*prompt_cb)(void *ctx);
 	char *cur;
 	char buf[LLPARSE_BUF_SIZE];
 };
@@ -56,6 +60,7 @@ struct llparser {
 struct gsmd;
 
 #define GSMD_FLAG_V0		0x0001	/* V0 responses to be expected from TA */
+#define GSMD_FLAG_SMS_FMT	0x0002	/* Use TEXT rather than PDU mode */
 
 struct gsmd {
 	unsigned int flags;
@@ -89,7 +94,8 @@ struct gsmd_user {
 
 extern int gsmdlog_init(const char *path);
 /* write a message to the daemons' logfile */
-void __gsmd_log(int level, const char *file, int line, const char *function, const char *message, ...);
+void __gsmd_log(int level, const char *file, int line, const char *function, const char *message, ...)
+	__attribute__ ((__format__ (__printf__, 5, 6)));
 /* macro for logging including filename and line number */
 #define gsmd_log(level, format, args ...) \
 	__gsmd_log(level, __FILE__, __LINE__, __FUNCTION__, format, ## args)
diff --git a/include/gsmd/usock.h b/include/gsmd/usock.h
index 2032230..ad7ef27 100644
--- a/include/gsmd/usock.h
+++ b/include/gsmd/usock.h
@@ -139,7 +139,7 @@ enum gsmd_sms_tp_rp {
 /* for SMS-SUBMIT, SMS-DELIVER */
 enum gsmd_sms_tp_udhi {
 	GSMD_SMS_TP_UDHI_NO_HEADER	= (0<<6),
-	GSMD_SMS_TP_UDHI_WTIH_HEADER	= (1<<6),
+	GSMD_SMS_TP_UDHI_WITH_HEADER	= (1<<6),
 };
 
 /* SMS delflg from 3GPP TS 07.05, Clause 3.5.4 */
@@ -160,6 +160,34 @@ enum gsmd_msg_phonebook {
 	GSMD_PHONEBOOK_GET_SUPPORT	= 6,
 };
 
+/* Type-of-Address, Numbering Plan Identification field */
+enum gsmd_toa_npi {
+	GSMD_TOA_NPI_UNKNOWN		= 0x0,
+	GSMD_TOA_NPI_ISDN		= 0x1,
+	GSMD_TOA_NPI_DATA		= 0x3,
+	GSMD_TOA_NPI_TELEX		= 0x4,
+	GSMD_TOA_NPI_NATIONAL		= 0x8,
+	GSMD_TOA_NPI_PRIVATE		= 0x9,
+	GSMD_TOA_NPI_ERMES		= 0xa,
+	GSMD_TOA_NPI_RESERVED		= 0xf,
+};
+
+/* Type-of-Address, Type-of-Number field */
+enum gsmd_toa_ton {
+	GSMD_TOA_TON_UNKNOWN		= (0<<4),
+	GSMD_TOA_TON_INTERNATIONAL	= (1<<4),
+	GSMD_TOA_TON_NATIONAL		= (2<<4),
+	GSMD_TOA_TON_NETWORK		= (3<<4),
+	GSMD_TOA_TON_SUBSCRIBER		= (4<<4),
+	GSMD_TOA_TON_ALPHANUMERIC	= (5<<4),
+	GSMD_TOA_TON_ABBREVIATED	= (6<<4),
+};
+
+/* Type-of-Address, bit 7 always 1 */
+enum gsmd_toa_reserved {
+	GSMD_TOA_RESERVED		= (1<<7),
+};
+
 /* Length from 3GPP TS 04.08, Clause 10.5.4.7 */
 
 #define GSMD_ADDR_MAXLEN	32
@@ -269,6 +297,11 @@ struct gsmd_sms_deliver {
 	char user_data[140];
 } __attribute__ ((packed));
 
+struct gsmd_sms_send {
+	struct gsmd_addr addr;
+	struct gsmd_sms payload;
+};
+
 /* Refer to GSM 07.07 subclause 8.12 */
 struct gsmd_phonebook_readrg {
 	u_int8_t index1;
diff --git a/include/gsmd/vendorplugin.h b/include/gsmd/vendorplugin.h
index 1911fef..d7052f3 100644
--- a/include/gsmd/vendorplugin.h
+++ b/include/gsmd/vendorplugin.h
@@ -12,7 +12,7 @@ struct gsmd_unsolicit;
 struct gsmd_vendor_plugin {
 	struct llist_head list;
 	unsigned char *name;
-	unsigned char *ext_chars;
+	char *ext_chars;
 	unsigned int num_unsolicit;
 	const struct gsmd_unsolicit *unsolicit;
 	int (*detect)(struct gsmd *g);
diff --git a/include/libgsmd/sms.h b/include/libgsmd/sms.h
index a07fc74..d449933 100644
--- a/include/libgsmd/sms.h
+++ b/include/libgsmd/sms.h
@@ -83,7 +83,7 @@ extern int lgsmd_sms_delete(struct lgsm_handle *lh,
 extern int lgsmd_sms_send(struct lgsm_handle *lh, const struct lgsm_sms *sms);
 
 /* Write Message to Memory */
-extern int lgsmd_sms_write(struct lgsm_handle *lh, 
+extern int lgsmd_sms_write(struct lgsm_handle *lh,
 		const struct lgsm_sms_write *sms_write);
 
 /* Packing of 7-bit characters, refer to GSM 03.38 subclause 6.1.2.1.1 */
diff --git a/src/gsmd/atcmd.c b/src/gsmd/atcmd.c
index d38e8c5..a723e9d 100644
--- a/src/gsmd/atcmd.c
+++ b/src/gsmd/atcmd.c
@@ -82,9 +82,12 @@ static int llparse_byte(struct llparser *llp, char byte)
 
 	switch (llp->state) {
 	case LLPARSE_STATE_IDLE:
+	case LLPARSE_STATE_PROMPT_SPC:
 		if (llp->flags & LGSM_ATCMD_F_EXTENDED) {
 			if (byte == '\r')
 				llp->state = LLPARSE_STATE_IDLE_CR;
+			else if (byte == '>')
+				llp->state = LLPARSE_STATE_PROMPT;
 			else {
 #ifdef STRICT
 				llp->state = LLPARSE_STATE_ERROR;
@@ -108,6 +111,8 @@ static int llparse_byte(struct llparser *llp, char byte)
 		/* can we really go directly into result_cr ? */
 		if (byte == '\r')
 			llp->state = LLPARSE_STATE_RESULT_CR;
+		else if (byte == '>')
+			llp->state = LLPARSE_STATE_PROMPT;
 		else {
 			llp->state = LLPARSE_STATE_RESULT;
 			ret = llparse_append(llp, byte);
@@ -127,6 +132,16 @@ static int llparse_byte(struct llparser *llp, char byte)
 			memset(llp->buf, 0, LLPARSE_BUF_SIZE);
 		}
 		break;
+	case LLPARSE_STATE_PROMPT:
+		if (byte == ' ')
+			llp->state = LLPARSE_STATE_PROMPT_SPC;
+		else {
+			/* this was not a real "> " prompt */
+			llparse_append(llp, '>');
+			ret = llparse_append(llp, byte);
+			llp->state = LLPARSE_STATE_RESULT;
+		}
+		break;
 	case LLPARSE_STATE_ERROR:
 		break;
 	}
@@ -147,6 +162,10 @@ static int llparse_string(struct llparser *llp, char *buf, unsigned int len)
 			/* FIXME: what to do with return value ? */
 			llp->cb(llp->buf, llp->cur - llp->buf, llp->ctx);
 		}
+
+		/* if a full SMS-style prompt was received, poke the select */
+		if (llp->state == LLPARSE_STATE_PROMPT_SPC)
+			llp->prompt_cb(llp->ctx);
 	}
 
 	return 0;
@@ -176,8 +195,8 @@ static int ml_parse(const char *buf, int len, void *ctx)
 	struct gsmd *g = ctx;
 	struct gsmd_atcmd *cmd = NULL;
 	static char mlbuf[1024];
-	int rc = 0, final = 0;
-	int mlbuf_len;
+	static int mlbuf_len = 0;
+	int rc = 0;
 
 	DEBUGP("buf=`%s'(%d)\n", buf, len);
 
@@ -231,7 +250,6 @@ static int ml_parse(const char *buf, int len, void *ctx)
 			DEBUGP("error number %lu\n", err_nr);
 			if (cmd)
 				cmd->ret = err_nr;
-			final = 1;
 			goto final_cb;
 		}
 		if (!strncmp(buf+1, "CMS ERROR", 9)) {
@@ -241,7 +259,6 @@ static int ml_parse(const char *buf, int len, void *ctx)
 			DEBUGP("error number %lu\n", err_nr);
 			if (cmd)
 				cmd->ret = err_nr;
-			final = 1;
 			goto final_cb;
 		}
 
@@ -273,7 +290,7 @@ static int ml_parse(const char *buf, int len, void *ctx)
 
 			/* it might be a multiline response, so if there's a previous
 			   response, send out mlbuf and start afresh with an empty buffer */
-			if (mlbuf[0] != 0) {
+			if (mlbuf_len) {
 				if (!cmd->cb) {
 					gsmd_log(GSMD_NOTICE, "command without cb!!!\n");
 				} else {
@@ -281,8 +298,8 @@ static int ml_parse(const char *buf, int len, void *ctx)
 					cmd->resp = mlbuf;
 					rc = cmd->cb(cmd, cmd->ctx, cmd->resp);
 					DEBUGP("Clearing mlbuf\n");
-					mlbuf[0] = 0;
 				}
+				mlbuf_len = 0;
 			}
 
 			/* the current buf will be appended to mlbuf below */
@@ -301,7 +318,6 @@ static int ml_parse(const char *buf, int len, void *ctx)
 			DEBUGP("unspecified error\n");
 			if (cmd)
 				cmd->ret = 4;
-			final = 1;
 			goto final_cb;
 		}
 
@@ -310,7 +326,6 @@ static int ml_parse(const char *buf, int len, void *ctx)
 			/* Part of Case 'C' */
 			if (cmd)
 				cmd->ret = 0;
-			final = 1;
 			goto final_cb;
 		}
 
@@ -319,14 +334,12 @@ static int ml_parse(const char *buf, int len, void *ctx)
 		if (!strncmp(buf, "NO CARRIER", 11) ||
 		    ((g->flags & GSMD_FLAG_V0) && buf[0] == '3')) {
 			/* Part of Case 'D' */
-			final = 1;
 			goto final_cb;
 		}
 
 		if (!strncmp(buf, "BUSY", 4) ||
 		    ((g->flags & GSMD_FLAG_V0) && buf[0] == '7')) {
 			/* Part of Case 'D' */
-			final = 1;
 			goto final_cb;
 		}
 	}
@@ -334,21 +347,13 @@ static int ml_parse(const char *buf, int len, void *ctx)
 	/* we reach here, if we are at an information response that needs to be
 	 * passed on */
 
-	if (mlbuf[0] == 0) {
-		DEBUGP("Filling mlbuf\n");
-		strncat(mlbuf, buf, sizeof(mlbuf)-1);
-	} else {
-		DEBUGP("Appending buf to mlbuf\n");
-		mlbuf_len = strlen(mlbuf);
-		if (mlbuf_len+1 < sizeof(mlbuf)) {
-			mlbuf[mlbuf_len] = '\n';
-			mlbuf[mlbuf_len+1] = '\0';
-			strncat(mlbuf, buf, sizeof(mlbuf)-mlbuf_len-2);
-		} else {
-			DEBUGP("response too big for mlbuf!!!\n");
-			return -EFBIG;
-		}
-	}
+	if (mlbuf_len)
+		mlbuf[mlbuf_len ++] = '\n';
+	DEBUGP("Appending buf to mlbuf\n");
+	if (len > sizeof(mlbuf) - mlbuf_len)
+		len = sizeof(mlbuf) - mlbuf_len;
+	memcpy(mlbuf + mlbuf_len, buf, len);
+	mlbuf_len += len;
 	return 0;
 
 final_cb:
@@ -365,13 +370,16 @@ final_cb:
 	} else {
 		DEBUGP("Calling final cmd->cb()\n");
 		/* send final result code if there is no information response in mlbuf */
-		if (mlbuf[0] == 0)
-			cmd->resp = buf;
-		else
+		if (mlbuf_len) {
 			cmd->resp = mlbuf;
+			mlbuf[mlbuf_len] = 0;
+			gsmd_log(GSMD_NOTICE,
+					"the text discarded is %s\n", buf);
+		} else
+			cmd->resp = buf;
 		rc = cmd->cb(cmd, cmd->ctx, cmd->resp);
 		DEBUGP("Clearing mlbuf\n");
-		mlbuf[0] = 0;
+		mlbuf_len = 0;
 	}
 
 	/* remove from list of currently executing cmds */
@@ -384,7 +392,15 @@ final_cb:
 		g->gfd_uart.when |= GSMD_FD_WRITE;
 
 	return rc;
-}	
+}
+
+/* called when the modem asked for a new line of a multiline atcmd */
+static int atcmd_prompt(void *data)
+{
+	struct gsmd *g = data;
+
+	g->gfd_uart.when |= GSMD_FD_WRITE;
+}
 
 /* callback to be called if [virtual] UART has some data for us */
 static int atcmd_select_cb(int fd, unsigned int what, void *data)
@@ -392,6 +408,7 @@ static int atcmd_select_cb(int fd, unsigned int what, void *data)
 	int len, rc;
 	static char rxbuf[1024];
 	struct gsmd *g = data;
+	char *cr;
 
 	if (what & GSMD_FD_READ) {
 		memset(rxbuf, 0, sizeof(rxbuf));
@@ -415,8 +432,12 @@ static int atcmd_select_cb(int fd, unsigned int what, void *data)
 	if ((what & GSMD_FD_WRITE) && g->interpreter_ready) {
 		struct gsmd_atcmd *pos, *pos2;
 		llist_for_each_entry_safe(pos, pos2, &g->pending_atcmds, list) {
-			len = strlen(pos->buf);
-			rc = write(fd, pos->buf, strlen(pos->buf));
+			cr = strchr(pos->cur, '\n');
+			if (cr)
+				len = cr - pos->cur;
+			else
+				len = pos->buflen;
+			rc = write(fd, pos->cur, len);
 			if (rc == 0) {
 				gsmd_log(GSMD_ERROR, "write returns 0, aborting\n");
 				break;
@@ -425,27 +446,32 @@ static int atcmd_select_cb(int fd, unsigned int what, void *data)
 					fd, rc);
 				return rc;
 			}
-			if (rc < len) {
-				gsmd_log(GSMD_FATAL, "short write!!! FIXME!\n");
-				exit(3);
-			}
+			if (cr && rc == len)
+				rc ++;	/* Skip the \n */
+			pos->buflen -= rc;
+			pos->cur += rc;
 			write(fd, "\r", 1);
-			/* success: remove from global list of to-be-sent atcmds */
-			llist_del(&pos->list);
-			/* append to global list of executing atcmds */
-			llist_add_tail(&pos->list, &g->busy_atcmds);
+
+			if (!pos->buflen) {
+				/* success: remove from global list of
+				 * to-be-sent atcmds */
+				llist_del(&pos->list);
+				/* append to global list of executing atcmds */
+				llist_add_tail(&pos->list, &g->busy_atcmds);
 
 				/* we only send one cmd at the moment */
-				g->gfd_uart.when &= ~GSMD_FD_WRITE;
 				break;
+			} else {
+				/* The write was short or the atcmd has more
+				 * lines to send after a "> ".  */
+				if (!(rc < len))
+					break;
+			}
 		}
-	}
 
-#if 0
-	if (llist_empty(&g->pending_atcmds))
+		/* Either pending_atcmds is empty or a command has to wait */
 		g->gfd_uart.when &= ~GSMD_FD_WRITE;
-#endif
-		
+	}
 
 	return 0;
 }
@@ -456,10 +482,10 @@ struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen,
 {
 	int buflen = strlen(cmd);
 	struct gsmd_atcmd *atcmd;
-	
+
 	if (rlen > buflen)
 		buflen = rlen;
-	
+
 	atcmd = talloc_size(__atcmd_ctx, sizeof(*atcmd)+ buflen);
 	if (!atcmd)
 		return NULL;
@@ -470,6 +496,7 @@ struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen,
 	atcmd->ret = -255;
 	atcmd->buflen = buflen;
 	atcmd->buf[buflen-1] = '\0';
+	atcmd->cur = atcmd->buf;
 	atcmd->cb = cb;
 	atcmd->resp = NULL;
 	strncpy(atcmd->buf, cmd, buflen-1);
@@ -482,8 +509,9 @@ int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd)
 {
 	DEBUGP("submitting command `%s'\n", cmd->buf);
 
+	if (llist_empty(&g->pending_atcmds))
+		g->gfd_uart.when |= GSMD_FD_WRITE;
 	llist_add_tail(&cmd->list, &g->pending_atcmds);
-	g->gfd_uart.when |= GSMD_FD_WRITE;
 
 	return 0;
 }
@@ -519,9 +547,9 @@ int atcmd_init(struct gsmd *g, int sockfd)
 	g->llp.cur = g->llp.buf;
 	g->llp.len = sizeof(g->llp.buf);
 	g->llp.cb = &ml_parse;
+	g->llp.prompt_cb = &atcmd_prompt;
 	g->llp.ctx = g;
 	g->llp.flags = LGSM_ATCMD_F_EXTENDED;
 
 	return gsmd_register_fd(&g->gfd_uart);
-}	
-
+}
diff --git a/src/gsmd/sms_cb.c b/src/gsmd/sms_cb.c
index 330317c..b9eb827 100644
--- a/src/gsmd/sms_cb.c
+++ b/src/gsmd/sms_cb.c
@@ -91,9 +91,6 @@ static int usock_cpms_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
 	if (!ucmd)
 		return -ENOMEM;
 
-
-	
-	
 	ucmd->hdr.version = GSMD_PROTO_VERSION;
 	ucmd->hdr.msg_type = GSMD_MSG_SMS;
 	ucmd->hdr.msg_subtype = GSMD_SMS_GETMSG_STORAGE;
@@ -188,14 +185,26 @@ static const struct gsmd_unsolocit gsm0705_unsolicit[] = {
 int sms_cb_init(struct gsmd *gsmd)
 {
 	struct gsmd_atcmd *atcmd;
+	char buffer[10];
 
 	atcmd = atcmd_fill("AT+CSMS=0", NULL, gu, 0);
 	if (!atcmd)
 		return -ENOMEM;
 	atcmd_submit(gsmd, atcmd);
 
-	/* Switch into "text mode" (Section 3.2.3) */
-	atcdm = atcmd_fill("AT+CMGF=1", 9, &sms_cb_init_cb, gu, 0);
+	/* If text mode, set the encoding */
+	if (gu->gsmd->flags & GSMD_FLAG_SMS_FMT) {
+		atcmd = atcmd_fill("AT+CSCS=\"IRA\"", 13, NULL, gu, 0);
+		if (!atcmd)
+			return -ENOMEM;
+		atcmd_submit(gsmd, atcmd);
+	}
+
+	/* Switch into desired mode (Section 3.2.3) */
+	snprintf(buffer, sizeof(buffer), "AT+CMGF=%i",
+			(gu->gsmd->flags & GSMD_FLAG_SMS_FMT) ?
+			GSMD_SMS_FMT_TEXT : GSMD_SMS_FMT_PDU);
+	atcmd = atcmd_fill(buffer, strlen(buffer), &sms_cb_init_cb, gu, 0);
 	if (!atcmd)
 		return -ENOMEM;
 
diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c
index 0a29e86..631a7d1 100644
--- a/src/gsmd/usock.c
+++ b/src/gsmd/usock.c
@@ -75,7 +75,7 @@ static int usock_cmd_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
 	ucmd->hdr.version = GSMD_PROTO_VERSION;
 	ucmd->hdr.msg_type = GSMD_MSG_PASSTHROUGH;
 	ucmd->hdr.msg_subtype = GSMD_PASSTHROUGH_RESP;
-	ucmd->hdr.len = strlen(resp)+1;
+	ucmd->hdr.len = rlen;
 	ucmd->hdr.id = cmd->id;
 	memcpy(ucmd->buf, resp, ucmd->hdr.len);
 
@@ -100,7 +100,7 @@ static int usock_rcv_passthrough(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
 
 static int usock_rcv_event(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, int len)
 {
-	u_int32_t *evtmask = (u_int32_t *) ((char *)gph + sizeof(*gph), gph->id);
+	u_int32_t *evtmask = (u_int32_t *) ((char *)gph + sizeof(*gph));
 
 	if (len < sizeof(*gph) + sizeof(u_int32_t))
 		return -EINVAL;
@@ -470,18 +470,15 @@ static int sms_read_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
 
 static int sms_send_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
 {
-	struct gsmd_user *gu = ctx;	
+	struct gsmd_user *gu = (struct gsmd_user *) ctx;
 	struct gsmd_ucmd *ucmd;	
-	
-	ucmd = gsmd_ucmd_fill(strlen(resp)+1, GSMD_MSG_SMS,
-			      GSMD_SMS_SEND, 0);
+
+	ucmd = gsmd_ucmd_fill(strlen(resp) + 1,
+			GSMD_MSG_SMS, GSMD_SMS_SEND, cmd->id);
 	if (!ucmd)
 		return -ENOMEM;
-	
 	strcpy(ucmd->buf, resp);
-
 	usock_cmd_enqueue(ucmd, gu);
-
 	return 0;
 }
 
@@ -519,34 +516,142 @@ static int sms_delete_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
 	return 0;
 }
 
+int packing_7bit_character(char *src, char *dest)
+{
+	int i,j = 0;
+	unsigned char ch1, ch2;
+	char tmp[2];
+	int shift = 0;
+	
+	*dest = '\0';
+
+	for ( i=0; i<strlen(src); i++ ) {
+		
+		ch1 = src[i] & 0x7F;
+		ch1 = ch1 >> shift;
+		ch2 = src[(i+1)] & 0x7F;
+		ch2 = ch2 << (7-shift); 
+
+		ch1 = ch1 | ch2;
+		
+		j = strlen(dest);
+ 		sprintf(tmp, "%X", (ch1 >> 4));	
+		dest[j++] = tmp[0];
+		sprintf(tmp, "%X", (ch1 & 0x0F));
+		dest[j++] = tmp[0];		
+		dest[j++] = '\0';		
+			
+		shift++;
+		
+		if ( 7 == shift ) {
+			shift = 0;
+			i++;
+		}
+	}			
+	
+	return 0;
+}
+
+/* Refer to GSM 03.40 subclause 9.2.3.3, for SMS-SUBMIT */
+static int usock_pdu_make_smssubmit(char *dest, struct gsmd_sms_send *src)
+{
+	u_int8_t header[10 + GSMD_ADDR_MAXLEN];
+	int pos = 0, i, coding7bit = 1;
+
+	/* (Should be optional but some modems require it) SMSC Length octet
+	 * is prepended.  If omitted or zero, use SMSC stored in the phone.  */
+	header[pos ++] = 0x00;
+
+	header[pos ++] =
+		GSMD_SMS_TP_MTI_SUBMIT |
+		(0 << 2) |		/* Reject Duplicates: 0 */
+		GSMD_SMS_TP_VPF_NOT_PRESENT |
+		GSMD_SMS_TP_SRR_NOT_REQUEST |
+		GSMD_SMS_TP_UDHI_NO_HEADER |
+		GSMD_SMS_TP_RP_NOT_SET;
+
+	/* TP-Message-Reference - 00 lets the phone set the number itself */
+	header[pos ++] = 0x00;
+
+	header[pos ++] = strlen(src->addr.number);
+	header[pos ++] = src->addr.type;
+	for (i = 0; src->addr.number[i]; i ++) {
+		header[pos] = src->addr.number[i ++] - '0';
+		if (src->addr.number[i])
+			header[pos ++] |= (src->addr.number[i] - '0') << 4;
+		else {
+			header[pos ++] |= 0xf0;
+			break;
+		}
+	}
+
+	/* TP-Protocol-Identifier - 00 means implicit */
+	header[pos ++] = 0x00;
+
+	/* TP-Data-Coding-Scheme - 00 for 7-bit default alphabet */
+	header[pos ++] = coding7bit ? 0x00 : 0x04;
+
+	/* TP-Validity-Period, if present, would go here */
+
+	header[pos ++] = src->payload.length;
+
+	if (dest) {
+		for (i = 0; i < pos; i ++) {
+			sprintf(dest, "%02X", header[i]);
+			dest += 2;
+		}
+		if (coding7bit)
+			packing_7bit_character(src->payload.data, dest);
+		else
+			for (i = 0; i < src->payload.length; i ++) {
+				sprintf(dest, "%02X", src->payload.data[i]);
+				dest += 2;
+			}
+	}
+
+	if (coding7bit)
+		return ((src->payload.length * 7 + 7) >> 3) + pos;
+	else
+		return src->payload.length + pos;
+}
+
+static const char *gsmd_cmgl_stat[] = {
+	"REC UNREAD", "REC READ", "STO UNSENT", "STO SENT", "ALL",
+};
+
 static int usock_rcv_sms(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, 
 			 int len)
 {
 	/* FIXME: TEXT mode support!!  */
 	struct gsmd_atcmd *cmd = NULL;
 	struct gsmd_sms_delete *gsd;
-	struct gsmd_sms *gs;
+	struct gsmd_sms_send *gss;
 	struct gsmd_sms_write *gsw;
 	int *stat, *index;
 	int atcmd_len;
 	char buf[1024];
-	
+
 	switch (gph->msg_subtype) {
 	case GSMD_SMS_LIST:
 		/* FIXME: only support PDU mode!! */
 		if(len < sizeof(*gph) + sizeof(int))
 			return -EINVAL;
-		stat = (int *) ((void *)gph + sizeof(*gph));	
+		stat = (int *) ((void *)gph + sizeof(*gph));
+		if (*stat < 0 || *stat > 4)
+			return -EINVAL;
 
-		sprintf(buf, "%d", *stat);	
-				
-		atcmd_len = 1 + strlen("AT+CMGL=") + strlen(buf);
-		cmd = atcmd_fill("AT+CMGL=", atcmd_len,
-				 &sms_list_cb, gu, gph->id);
+		if (gu->gsmd->flags & GSMD_FLAG_SMS_FMT)
+			atcmd_len = sprintf(buf, "AT+CMGL=\"%s\"",
+					gsmd_cmgl_stat[*stat]);
+		else
+			atcmd_len = sprintf(buf, "AT+CMGL=%i", *stat);
+
+		cmd = atcmd_fill(buf, atcmd_len + 1,
+				&sms_list_cb, gu, gph->id);
 		if (!cmd)
 			return -ENOMEM;
-		sprintf(cmd->buf, "AT+CMGL=%s", buf);
 		break;
+
 	case GSMD_SMS_READ:
 		/* FIXME: only support PDU mode!! */
 		if(len < sizeof(*gph) + sizeof(int))
@@ -562,6 +667,34 @@ static int usock_rcv_sms(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
 			return -ENOMEM;
 		sprintf(cmd->buf, "AT+CMGR=%s", buf);
 		break;
+
+	case GSMD_SMS_SEND:
+		if (len < sizeof(*gph) + sizeof(*gss))
+			return -EINVAL;
+		gss = (struct gsmd_sms_send *) ((void *) gph + sizeof(*gph));	
+
+		if (gu->gsmd->flags & GSMD_FLAG_SMS_FMT) {
+			atcmd_len = sprintf(buf, "AT+CMGS=\"%s\"\n%.*s",
+					gss->addr.number,
+					gss->payload.length,
+					gss->payload.data);
+		} else {
+			atcmd_len = sprintf(buf, "AT+CMGS=%i\n",
+					usock_pdu_make_smssubmit(NULL,
+						gss) - 1);
+			atcmd_len += usock_pdu_make_smssubmit(buf + atcmd_len,
+					gss) * 2;
+		}
+		buf[atcmd_len ++] = 26;	/* ^Z ends the message */
+		buf[atcmd_len ++] = 0;
+
+		cmd = atcmd_fill(buf, atcmd_len, &sms_send_cb, gu, gph->id);
+		if (!cmd)
+			return -ENOMEM;
+		break;
+	case GSMD_SMS_WRITE:
+		gsmd_log(GSMD_DEBUG, "sms write\n");
+		break;
 #if 0
 	case GSMD_SMS_SEND:
 		/* FIXME: only support PDU mode!! */
@@ -609,8 +742,8 @@ static int usock_rcv_sms(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
 	default:
 		return -EINVAL;
 	}
-		
-	gsmd_log(GSMD_DEBUG, "%s\n", cmd->buf);
+
+	gsmd_log(GSMD_DEBUG, "%s\n", cmd ? cmd->buf : 0);
 	if (cmd)
 		return atcmd_submit(gu->gsmd, cmd);
 	else
@@ -866,7 +999,7 @@ static usock_msg_handler *pcmd_type_handlers[__NUM_GSMD_MSGS] = {
 	[GSMD_MSG_PIN]		= &usock_rcv_pin,
 	[GSMD_MSG_PHONE]	= &usock_rcv_phone,
 	[GSMD_MSG_NETWORK]	= &usock_rcv_network,
-	[GSMD_MSG_SMS]		= &usock_rcv_sms,	
+	[GSMD_MSG_SMS]		= &usock_rcv_sms,
 	//[GSMD_MSG_PHONEBOOK]	= &usock_rcv_phonebook,
 };
 
diff --git a/src/gsmd/vendor_ti.c b/src/gsmd/vendor_ti.c
index e158111..4c7d182 100644
--- a/src/gsmd/vendor_ti.c
+++ b/src/gsmd/vendor_ti.c
@@ -277,7 +277,7 @@ static int ticalypso_detect(struct gsmd *g)
 
 static int ticalypso_initsettings(struct gsmd *g)
 {
-	int rc;
+	int rc = 0;
 	struct gsmd_atcmd *cmd;
 
 	/* use +CTZR: to report time zone changes */
diff --git a/src/libgsmd/libgsmd_sms.c b/src/libgsmd/libgsmd_sms.c
index 261dca3..31c416b 100644
--- a/src/libgsmd/libgsmd_sms.c
+++ b/src/libgsmd/libgsmd_sms.c
@@ -83,19 +83,33 @@ int lgsmd_sms_delete(struct lgsm_handle *lh,
 	return 0;
 }
 
-int lgsmd_sms_send(struct lgsm_handle *lh, 
-		const struct lgsm_sms *sms)   
+#ifndef MIN
+# define MIN(a,b)	(((a) < (b)) ? (a) : (b))
+#endif
+
+int lgsmd_sms_send(struct lgsm_handle *lh,
+		const struct lgsm_sms *sms)
 {
 	/* FIXME: only support PDU mode */
 	struct gsmd_msg_hdr *gmh;
-	struct gsmd_sms *gs;
+	struct gsmd_sms_send *gss;
 	int rc;
 
 	gmh = lgsm_gmh_fill(GSMD_MSG_SMS,
-			GSMD_SMS_SEND, sizeof(*gs));
+			GSMD_SMS_SEND, sizeof(*gss));
 	if (!gmh)
 		return -ENOMEM;
-	gs = (struct gsmd_sms *) gmh->data;
+	gss = (struct gsmd_sms_send *) gmh->data;
+
+	gss->addr.type =
+		GSMD_TOA_NPI_ISDN |
+		GSMD_TOA_TON_UNKNOWN |
+		GSMD_TOA_RESERVED;
+	strncpy(gss->addr.number, sms->addr, sizeof(gss->addr.number));
+
+	gss->payload.length =
+		MIN(strlen(sms->data), sizeof(gss->payload.data));
+	memcpy(gss->payload.data, sms->data, gss->payload.length);
 
 	rc = lgsm_send(lh, gmh);
 	if (rc < gmh->len + sizeof(*gmh)) {
@@ -108,7 +122,7 @@ int lgsmd_sms_send(struct lgsm_handle *lh,
 	return 0;
 }
 
-int lgsmd_sms_write(struct lgsm_handle *lh, 
+int lgsmd_sms_write(struct lgsm_handle *lh,
 		const struct lgsm_sms_write *sms_write)
 {
 	/* FIXME: only support PDU mode */
diff --git a/src/util/atcmd.c b/src/util/atcmd.c
index 395f506..f3716d7 100644
--- a/src/util/atcmd.c
+++ b/src/util/atcmd.c
@@ -91,9 +91,11 @@ int atcmd_main(struct lgsm_handle *lgsmh)
 				continue;
 			}
 			printf("STR=`%s'\n", buf);
+
+			/* this is a synchronous call for a passthrough
+			 * command */
+			lgsm_passthrough(lgsmh, buf, rbuf, &rlen);
+			printf("RSTR=`%s'\n", rbuf);
 		}
-		/* this is a synchronous call for a passthrough command */
-		lgsm_passthrough(lgsmh, buf, rbuf, &rlen);
-		printf("RSTR=`%s'\n", rbuf);
 	}
 }
-- 
1.4.4.3


More information about the gsmd-devel mailing list