Initial commit
[jamendo] / branches / nota-show-app / src / pdu.c
diff --git a/branches/nota-show-app/src/pdu.c b/branches/nota-show-app/src/pdu.c
new file mode 100644 (file)
index 0000000..b0ca443
--- /dev/null
@@ -0,0 +1,216 @@
+/***************************************************************************
+ *            pdu.c
+ *
+ *  Thu Nov 19 15:13:08 2009
+ *  Copyright  2009  Marcin Miklas
+ *  <marcin.miklas@teleca.com>
+ ****************************************************************************/
+#include <string.h>
+#include <netinet/in.h> 
+#include <stdlib.h>
+#include "notaio.h"
+#include "pdu.h"
+
+ServiceMessage* pack_pdu(int sigid, uns8* payload, int payload_len, int* pdu_len) {
+/* Now we need to construct a Service Message to send to the Hello, world
+ * service at the other end. Our service message will look like this:
+                                                               ARGUMENT  ITEM
+0xA1           SIGID   0x41            LENGTH          DATA
+Length of      8 bit   Argument        Length          Data
+signal ID      sigid   type            of data         field
+
+ * Ok, so what exactly does all this mean? Let's go through it one
+ * field at a time:
+ * - 0xA1: this is the length of the following signal ID. 0xA1 means
+ *   it will be 1 byte long (8 bits).
+ * - SIGID: Signal Identifiers are used by service nodes to recognize 
+ *   incoming signals (messages). A list of valid signals should be
+ *   provided in the Service Interface Specification.
+ * - 0x41: Type of the following argument, or one of NULL, TRUE or
+ *   FALSE for simple items where these values are enough.
+ * The bytes following the argument type field make up the value of
+ * the argument. For example, an argument type of 0x14 (unsigned 32-bit
+ * integer) is followed by 4 bytes of data, representing the integer
+ * value in question. In our case, the argument type is 0x41, which
+ * stands for binary data (in our case, the test string we will be
+ * sending). For binary data items such as strings, the argument item
+ * is further divided into two parts:
+ * - LENGTH: Length of the following binary data, in octets. This is an
+ *   8 bit value, so the maximum length is 255 octets. For longer items,
+ *   use type 0x42, which has a 16-bit length field.
+ * - DATA: The amount of data specified by LENGTH.
+ *
+ * For more information about Service Messages, see Appendix D of the
+ * NoTA platform programming guide.
+ */
+       if (!payload) payload_len = 0;
+       ServiceMessage* smsg = (ServiceMessage*)malloc( SERVICE_MESSAGE_HEADER_LEN + payload_len );
+       smsg->sigid_length = 0xA1;
+       smsg->sigid[0] = sigid;
+       smsg->argtype = payload ? 0x42 : 0x01;
+       smsg->arglen = htons(payload_len);
+
+       if (payload) {
+               memcpy(smsg->argdata, payload, payload_len);
+       }
+
+       if (pdu_len) {
+               *pdu_len = SERVICE_MESSAGE_HEADER_LEN + payload_len;
+               if(!payload)
+                       *pdu_len -= sizeof(smsg->arglen);
+       }
+
+
+       return smsg;
+}
+
+/* If we decide to discard a message before it is fully
+ * read, we'll need to
+ * have some way of abandoning remaining data on the
+ * connected socket.
+ * This function serves that purpose for the time being
+ * (that is, until HIN3 supports message-based sockets).
+ */
+static void clear(HSSockID socket,int quiet, ServiceCallbacks* cb)
+{
+       uns8 buf[64];
+    HErrorCode err;
+       int i=0;
+    do {
+        err = n_read(socket, &buf, 64, HSReceiveNonBlocking);
+               if (!quiet) {
+                       while (i < 64) {
+                               LOG1("%02x ", buf[i++]);
+                       }
+               }
+    } while (err > 0);
+       if(!quiet)
+               LOG("\n");
+}
+
+HErrorCode read_smsg(HSSockID* socket, HSReceiveMode mode, ServiceCallbacks* cb)
+{
+       HErrorCode error;
+       ServiceMessage smsg;
+       int count = 0;
+       
+       /* In the following lines, we will read the service message from
+        * the internal RX buffer into the 'smsg' variable. The first bytes 
+        * of a NoTA service message will tell us what kind of message 
+        * we're dealing with.
+        */
+    error = n_read(*socket, &smsg.sigid_length, sizeof(smsg.sigid_length), mode);
+       if (error <= 0)
+               return error;
+       count += error;
+       if (smsg.sigid_length != 0xA1)
+       {
+               //LOG("Only 8-bit signal identifiers currently recognized.\n");
+               clear(*socket, 1, cb);
+               return 0;
+       }
+       error = n_read(*socket, &smsg.sigid, 2, mode);
+       if (error <= 0)
+               return error;
+       count += error;
+       switch(smsg.sigid[0]) 
+       {
+       case PUT_IMAGE:
+               {
+                       error = n_read(*socket, &smsg.argtype, 1, mode);
+                       if (error <= 0)
+                               return error;
+                       if (smsg.argtype != 0x42)
+                       {
+                               clear(*socket, 0, cb);
+                               return 0;
+                       }
+                       error = n_read(*socket, &smsg.arglen, 2, mode);
+                       smsg.arglen = ntohs(smsg.arglen);
+                       if (error <= 0)
+                               return error;
+                       uns8* argdata = malloc(smsg.arglen);
+                       error = n_read(*socket, argdata, smsg.arglen, HSReceiveBlocking);
+                       if (error == smsg.arglen)
+                       {
+                               if(cb->put_image)
+                                       cb->put_image(argdata,smsg.arglen);
+                       }
+                       else
+                       {
+                               LOG2("Error: got only %d bytes of %d expected.\n", error, smsg.arglen);
+                       }
+
+                       free((void*)argdata);
+               }
+               break;
+       case GET_IMAGE:
+               error = n_read(*socket, &smsg.argtype, 1, mode);
+               if (error <= 0)
+                       return error;
+               if (smsg.argtype != 0x01)
+               {
+                       clear(*socket, 0, cb);
+                       return 0;
+               }
+               {
+                       uns8* last_img=NULL;
+                       int last_img_size=0;
+
+                       ServiceMessage* smsg;
+                       int smsg_len=0;
+
+                       if (cb->get_image) {
+                               last_img = cb->get_image(&last_img_size);
+                       }
+                                               
+                       smsg = pack_pdu(PUT_IMAGE, last_img, last_img_size, &smsg_len);
+                       error = n_send(*socket, smsg, smsg_len, HSSendBlocking);
+                       free(smsg);
+
+                       free(last_img);
+               }
+               break;
+       case FACE_FOUND:
+               {
+                       error = n_read(*socket, &smsg.argtype, 1, mode);
+                       if (error <= 0)
+                               return error;
+                       if (smsg.argtype != 0x42)
+                       {
+                               clear(*socket, 0, cb);
+                               return 0;
+                       }
+                       error = n_read(*socket, &smsg.arglen, 2, mode);
+                       smsg.arglen = ntohs(smsg.arglen);
+                       if (error <= 0)
+                               return error;
+                       
+                       struct {int x,y,r;} face;
+                       error = n_read(*socket, &face, sizeof(face), HSReceiveBlocking);
+                       if (error == smsg.arglen) {
+                               if(cb->face_found) {
+                                       cb->face_found(ntohl(face.x),ntohl(face.y),ntohl(face.r));
+                               }
+                       }
+               }
+               break;
+       case DISCONNECT:
+               if (cb->disconnect) {
+                       cb->disconnect(*socket);
+               }
+               return DISCONNECT;
+               break;
+       case QUIT:
+               if (cb->quit) {
+                       cb->quit(*socket);
+               }
+               return QUIT;
+               break;
+       default:
+               LOG("Unsupported service call.\n");
+               clear(*socket, 0, cb);
+               break;
+       }
+       return 0;
+}