[PATCH] experimental full DLR support for AT2 module

"Oded Arbel" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Hi list.

Here's the result of my latest read up - "3GPP TS 23.040 V5.4.0" (yes, I'm a very sick person, I know ;-). I think I got full DLR support for AT2  going, with the patch attached. 

Caveats : 
- for some wierd reason the STATUS-REPORT SMs only come directly into the SIM memory of the card. this annoys me to no end, but I didn't feel like solving it today (it's almost 11PM here - I want to go home :-), so you need to have sim-buffering enabled to get this to work.
- sometimes I don't get delivery reports at all - I think it has something to do with sending to a subscriber on a different network - not sure. maybe I need to force RPI on all SMs with DLRs ? 

Tested with Siemens M20 and Wavecom WM02D

Have fun !

--
Oded Arbel
m-Wise mobile solutions
[email protected]

+972-9-9581711 (116)
+972-67-340014

::..
Edmund: "Auntie..."
Lady WhiteAdder: "Don't call me 'Auntie!' It is a term of relativity!
And where there is relativity, there is the concept of sex! And sex is
no subject for the dinner table!"
	-- from "Blackadder II"


 <<smsc_at2.patch>>
smsc_at2.patch (application/octet-stream, 11.5 KB)
--- gw/smsc_at2.c	2002-08-07 10:56:44.000000000 +0300
+++ gw/smsc_at2.c	2002-08-08 22:31:31.000000000 +0300
@@ -241,6 +241,7 @@
 {
     int count;
     int s = 0;
+    int write_count = 0;
     Octstr *linestr = NULL;
 
     linestr = octstr_format("%s\r", line);
@@ -248,7 +249,15 @@
     debug("bb.smsc.at2", 0, "AT2[%s]: --> %s^M", octstr_get_cstr(privdata->name), line);
 
     count = octstr_len(linestr);
-    s = write(privdata->fd, octstr_get_cstr(linestr), count);
+    while (1) {
+	errno = 0;
+	s = write(privdata->fd, octstr_get_cstr(linestr), count);
+	if (s < 0 && errno == EAGAIN && write_count < RETRY_SEND) {
+	    gwthread_sleep(1);
+	    ++write_count;
+	} else
+	    break;
+    };
     O_DESTROY(linestr);
     if (s < 0) {
         debug("bb.smsc.at2", 0, "AT2[%s]: write failed with errno %d", 
@@ -266,9 +275,18 @@
 {
     int s;
     char *ctrlz = "\032" ;
-
+    int write_count = 0;
+    
     debug("bb.smsc.at2", 0, "AT2[%s]: --> ^Z", octstr_get_cstr(privdata->name));
-    s = write(privdata->fd, ctrlz, 1);
+    while (1) {
+	errno = 0;
+	s = write(privdata->fd, ctrlz, 1);
+	if (s < 0 && errno == EAGAIN && write_count < RETRY_SEND) {
+	    gwthread_sleep(1);
+	    ++write_count;
+	} else
+	    break;
+    };
     if (s < 0) {
         debug("bb.smsc.at2", 0, "AT2[%s]: write failed with errno %d", 
               octstr_get_cstr(privdata->name), errno);
@@ -470,7 +488,7 @@
 
 
 int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag, 
-                           int *messages_collected)
+                           int *output)
 {
     Octstr *line = NULL;
     Octstr *line2 = NULL;
@@ -541,8 +559,8 @@
                               octstr_get_cstr(privdata->name));
                     } else {
                         /* count message even if I can't decode it */
-                        if (messages_collected)
-                            ++(*messages_collected);
+                        if (output)
+                            ++(*output);
                         msg = at2_pdu_decode(pdu, privdata);
                         if (msg != NULL) {
                             msg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
@@ -559,6 +577,14 @@
                 }
                 continue;
             }
+            if ((octstr_search(line, octstr_imm("+CMGS:"),0) != -1) && (output)) {
+		/* found response to a +CMGS command, read the message id and return it in output */
+		long temp;
+		if (octstr_parse_long(&temp, line, octstr_search(line, octstr_imm("+CMGS:"),0)+6,10) == -1)
+		    error(0,"AT2[%s]: got +CMGS but failed to read message id", octstr_get_cstr(privdata->name));
+		else
+		    *output = temp;
+            }
 
             if ( -1 != octstr_search(line, octstr_imm("ERROR"), 0)) {
                 ret = -1;
@@ -1183,6 +1222,9 @@
         case AT_DELIVER_SM:
             msg = at2_pdu_decode_deliver_sm(data, privdata);
             break;
+        case AT_STATUS_REPORT_SM:
+	    msg = at2_pdu_decode_report_sm(data, privdata);
+	    break;
 
             /* Add other message types here: */
 
@@ -1354,6 +1396,102 @@
     return message;
 }
 
+Msg *at2_pdu_decode_report_sm(Octstr *data, PrivAT2data *privdata)
+{
+/* example : 06 D2 0B 91 79 62 37 04 10 F4 20 80 80 81 52 05 00 20 80 80 81 52 45 00 00
+                MR 0B 91 97267340014-sender 02080818255000 ts 02080818255400 ts 00
+
+  buffered : 06DC0B917962370410F4208080027075002080800270850030
+  received : 06DC0B917962370410F4208080027075002080800211740000
+	     
+*/
+   Msg* dlrmsg = NULL;
+   Octstr *pdu, *msg_id, *tmpstr = NULL, *receiver = NULL;
+   int type, tp_mr, len, ntype, pos;
+
+    /*
+     * parse the PDU.
+     */
+
+    /* convert the pdu to binary format for ease of processing */
+    pdu = at2_convertpdu(data);
+
+    /* Message reference */
+    tp_mr = octstr_get_char(pdu,1);
+    msg_id = octstr_format("%d",tp_mr);
+    debug("bb.smsc.at2",0,"AT2[%s]: got STATUS-REPORT for message <%d>:", octstr_get_cstr(privdata->name), tp_mr);
+    
+    /* reciver address */
+    len = octstr_get_char(pdu, 2);
+    ntype = octstr_get_char(pdu, 3);
+
+    pos = 4;
+    if ((ntype & 0xD0) == 0xD0) {
+        /* Alphanumeric sender */
+        receiver = octstr_create("");
+        tmpstr = octstr_copy(pdu, pos, (len+1)/2);
+        at2_decode7bituncompressed(tmpstr, (((len - 1) * 4 - 3) / 7) + 1, receiver, 0);
+        octstr_destroy(tmpstr);
+        debug("bb.smsc.at2", 0, "AT2[%s]: Alphanumeric receiver <%s>",
+              octstr_get_cstr(privdata->name), octstr_get_cstr(receiver));
+        pos += (len + 1) / 2;
+    } else {
+	int i;
+        receiver = octstr_create("");
+        if ((ntype & 0x90) == 0x90) {
+            /* International number */
+            octstr_append_char(receiver, '+');
+        }
+        for (i = 0; i < len; i += 2, pos++) {
+            octstr_append_char(receiver, (octstr_get_char(pdu, pos) & 15) + 48);
+            if (i + 1 < len)
+                octstr_append_char(receiver, (octstr_get_char(pdu, pos) >> 4) + 48);
+        }
+        debug("bb.smsc.at2", 0, "AT2[%s]: Numeric receiver %s <%s>",
+              octstr_get_cstr(privdata->name), ((ntype & 0x90) == 0x90 ? "(international)" : ""),
+              octstr_get_cstr(receiver));
+    }
+
+    pos += 14; /* skip time stamps for now */
+
+    if ((type = octstr_get_char(pdu, pos)) == -1 ) {
+	error(1,"AT2[%s]: STATUS-REPORT pdu too short to have TP-Status field !",
+	    octstr_get_cstr(privdata->name));
+	goto error;
+    }
+
+    switch (type) {
+	case 0x30:
+	    type = DLR_BUFFERED;
+	    tmpstr = octstr_create("Buffered/");
+	    break;
+	case 0x00:
+	    type = DLR_SUCCESS;
+	    tmpstr = octstr_create("Success/");
+	    break;
+	default:
+	    /* this is prety stupid : I have no idea how a DLR_FAIL looks like, as I couldn't produce it */
+	    type = DLR_FAIL;
+	    tmpstr = octstr_create("Failed/");
+	    break;
+    }
+
+    if ((dlrmsg = dlr_find(octstr_get_cstr(privdata->conn->id),
+	    octstr_get_cstr(msg_id), octstr_get_cstr(receiver), type)) == NULL) {
+	debug("bb.at2",1,"AT2[%s]: Received delivery notification but can't find that ID in the DLR storage",
+	    octstr_get_cstr(privdata->name));
+	    goto error;
+    }
+
+    octstr_insert(dlrmsg->sms.msgdata, tmpstr, 0);
+	
+error:
+    O_DESTROY(tmpstr);
+    O_DESTROY(pdu);
+    O_DESTROY(receiver);
+    O_DESTROY(msg_id);
+    return dlrmsg;
+}
 
 Octstr *at2_convertpdu(Octstr *pdutext)
 {
@@ -1461,6 +1599,7 @@
         ret = -99;
         retries = RETRY_SEND;
         while ((ret != 0) && (retries-- > 0)) {
+	    int msg_id = -1;
             /* 
              * send the initial command and then wait for > 
              */
@@ -1481,17 +1620,76 @@
             at2_write_ctrlz(privdata);
             
             /* wait 20 secs for modem command */
-            ret = at2_wait_modem_command(privdata, 20, 0, NULL);
+            ret = at2_wait_modem_command(privdata, 20, 0, &msg_id);
             debug("bb.at", 0, "AT2[%s]: send command status: %d",
                   octstr_get_cstr(privdata->name), ret);
             
             if (ret != 0) /* OK only */
                 continue;
+
+	    /* gen DLR_SMSC_SUCCESS */
+	    if (msg->sms.dlr_mask & DLR_SMSC_SUCCESS)
+	    {
+		Msg* dlrmsg;
+
+		dlrmsg = msg_create(sms);
+		dlrmsg->sms.id = msg->sms.id;
+                dlrmsg->sms.service = octstr_duplicate(msg->sms.service);
+                dlrmsg->sms.dlr_mask = DLR_SMSC_SUCCESS;
+                dlrmsg->sms.sms_type = report;
+                dlrmsg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
+                dlrmsg->sms.sender = octstr_duplicate(msg->sms.receiver);
+                dlrmsg->sms.receiver = octstr_duplicate(msg->sms.sender);
+                dlrmsg->sms.msgdata = octstr_create("ACK/");
+                octstr_append(dlrmsg->sms.msgdata,msg->sms.dlr_url);
+                time(&dlrmsg->sms.time);
+
+		debug("bb.at2",0,"AT2[%s]: sending DLR type ACK", octstr_get_cstr(privdata->name));
+		bb_smscconn_receive(privdata->conn, dlrmsg);
+
+		/* store DLR message if needed for SMSC generated delivery reports */
+		if (msg->sms.dlr_mask & (DLR_SUCCESS | DLR_FAIL | DLR_BUFFERED)) {
+		    if (msg_id == -1)
+			error(0,"AT2[%s]: delivery notification requested, but I have no message ID!",
+			    octstr_get_cstr(privdata->name));
+		    else {
+			Octstr* dlrmsgid = octstr_format("%d", msg_id);
+
+			dlr_add(octstr_get_cstr(privdata->conn->id),
+				octstr_get_cstr(dlrmsgid),
+				octstr_get_cstr(msg->sms.receiver),
+				octstr_get_cstr(msg->sms.service),
+				octstr_get_cstr(msg->sms.dlr_url),
+				msg->sms.dlr_mask);
+				
+    			O_DESTROY(dlrmsgid);
+		    }
+		}
+	    }
             counter_increase(privdata->conn->sent);
             bb_smscconn_sent(privdata->conn, msg);
         }
 
         if (ret != 0) {
+		/* gen DLR_SMSC_FAIL */
+		if (msg->sms.dlr_mask & DLR_SMSC_FAIL)
+		{
+		    Msg* dlrmsg;
+
+		    dlrmsg = msg_create(sms);
+                    dlrmsg->sms.service = octstr_duplicate(msg->sms.service);
+                    dlrmsg->sms.dlr_mask = DLR_SMSC_FAIL;
+                    dlrmsg->sms.sms_type = report;
+                    dlrmsg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
+                    dlrmsg->sms.sender = octstr_duplicate(msg->sms.receiver);
+                    dlrmsg->sms.receiver = octstr_duplicate(msg->sms.sender);
+                    dlrmsg->sms.msgdata = octstr_create("NACK/");
+                    octstr_append(dlrmsg->sms.msgdata,msg->sms.dlr_url);
+                    time(&dlrmsg->sms.time);
+
+		    debug("bb.at2",0,"AT2[%s]: sending DLR type NACK", octstr_get_cstr(privdata->name));
+		    bb_smscconn_receive(privdata->conn, dlrmsg);
+		}
             /*
              * no need to do counter_increase(privdata->conn->failed) here, 
              * since bb_smscconn_send_failed() will inc the counter on 
@@ -1517,14 +1715,18 @@
      */
 
     /* 
-     * message type SUBMIT
-     *    01010001 = 0x51 indicating add. UDH, TP-VP(Rel) & MSG_SUBMIT
-     * or 00010001 = 0x11 for just TP-VP(Rel) & MSG_SUBMIT 
+     * message type SUBMIT , bit mapped :
+     * bit7                            ..                                    bit0
+     * TP-RP , TP-UDHI, TP-SRR, TP-VPF(4), TP-VPF(3), TP-RD, TP-MTI(1), TP-MTI(0)
      */
-
-    pdu[pos] = octstr_len(msg->sms.udhdata) ? at2_numtext(5) : at2_numtext(1);
+    i  = (msg->sms.rpi ? 1 : 0) << 7 /* TP-RP */
+	| (octstr_len(msg->sms.udhdata)  ? 1 : 0) << 6 /* TP-UDHI */
+	| ((msg->sms.dlr_mask & (DLR_SUCCESS | DLR_FAIL | DLR_BUFFERED)) ? 1 : 0) << 5 /* TP-SRR */
+	| 16 /* TP-VP(Rel)*/
+	| 1; /* TP-MTI: SUBMIT_SM */
+    pdu[pos] = at2_numtext(i >> 4);
     pos++;
-    pdu[pos] = at2_numtext(AT_SUBMIT_SM);
+    pdu[pos] = at2_numtext(i & 15);
     pos++;
 
     /* message reference (0 for now) */
--- gw/smsc_at2.h	2002-04-15 11:39:00.000000000 +0300
+++ gw/smsc_at2.h	2002-08-08 21:18:38.000000000 +0300
@@ -19,6 +19,7 @@
 /* Message types defines */
 #define AT_DELIVER_SM   0
 #define AT_SUBMIT_SM    1
+#define AT_STATUS_REPORT_SM 2
 
 /* type of phone number defines */
 #define PNT_UNKNOWN     0
@@ -164,7 +165,7 @@
  * Waits for the modem to send us something.
  */
 int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, 
-                           int greaterflag, int* messages_collected);
+                           int greaterflag, int* output);
 
 /*
  * Sets the serial port speed on the device
@@ -209,6 +211,11 @@
 Msg	*at2_pdu_decode_deliver_sm(Octstr *data, PrivAT2data *privdata);
 
 /*
+ * Decode a SUBMIT-REPORT PDU
+ */
+Msg	*at2_pdu_decode_report_sm(Octstr *data, PrivAT2data *privdata);
+
+/*
  * Converts the text representation of hexa to binary
  */
 Octstr *at2_convertpdu(Octstr *pdutext);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.