gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-mdb] branch master updated (b18e966 -> 9065ce5)


From: gnunet
Subject: [taler-taler-mdb] branch master updated (b18e966 -> 9065ce5)
Date: Wed, 04 Dec 2019 14:15:46 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a change to branch master
in repository taler-mdb.

    from b18e966  doxygen added
     new b1fd9e8  Added mdb communication module
     new 2c75a0a  integration mdb first try
     new 94d6cfb  Some corrections
     new 40e04ea  mdb communication working (test integration)
     new 2c9634c  some little bugfixes
     new ffcbd91  major mdb refactoring
     new fbef2d2  refactor
     new 5384d76  neg cond
     new 3ebcc87  neg cond
     new fafab48  parse filenames from config
     new c914ca5  use more named constants
     new 31a0721  nicer logging
     new c1f9e59  fix shutdown
     new 68e18f2  fix FTBFS
     new 9347f9e  config
     new b17c2d6  clarify input
     new 0101047  invert checks
     new 29d743b  invert checks
     new 8f60d0f  fix end session handling
     new ab91096  add 'a' command
     new 0048acc  fix NPE
     new 6202e22  logging
     new 9065ce5  add TODO

The 23 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/Makefile.am  |   2 +-
 src/main.c       | 909 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/mdb/.gitkeep |   0
 taler.conf       |   6 +
 4 files changed, 875 insertions(+), 42 deletions(-)
 delete mode 100644 src/mdb/.gitkeep

diff --git a/src/Makefile.am b/src/Makefile.am
index 881df65..4b5e750 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,7 +7,7 @@ if USE_COVERAGE
 endif
 
 taler_mdb_SOURCES = \
-  main.c
+  main.c 
 taler_mdb_LDADD = \
   -ltalermerchant \
   -ltalerjson \
diff --git a/src/main.c b/src/main.c
index deec632..af40fe6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -21,6 +21,22 @@ along with
 * @brief main functionality of the application
 * @author Boss Marco
 * @author Christian Grothoff
+*
+* TODO:
+* - comment code (Boss)
+* - replace remaining MDB magic constants with #defines (Hofer)
+* - support command line option to "disable_mdb" to run without
+*   connected MDB (instead of failing to launch); in this
+*   mode, print out what WOULD be done with MDB (Grothoff)
+* - in keyboard interaction, distinguish between
+*   payment pending and payment not pending, allow
+*   'c', only if pending (Grothoff)
+* - have new state for waiting for MDB to signal vend success/failure
+*   in keyboard interaction prompt for success y/n at that time (or c cancel) 
(Grothoff)
+* - add key strokes for "failure to vend" and "vend successful"
+*   (useful when faking MDB interaction), factor out function for
+    the respective operations! (Grothoff)
+* - implement refunds on failure to vend (TBD)
 */
 #include "config.h"
 #include <stdio.h>
@@ -53,6 +69,12 @@ along with
 #include <linux/fb.h>
 #endif
 
+/* Constants */
+#define MAX_SIZE_RX_BUFFER 256
+
+/* Constants */
+#define MAX_SIZE_TX_BUFFER 256
+
 /**
  * Disable i18n support.
  */
@@ -62,6 +84,11 @@ along with
 
 #define NFC_FAILURE_RETRY_FREQ GNUNET_TIME_UNIT_SECONDS
 
+/**
+ * How long do we wait at most for an ACK from MDB?
+ */
+#define MAX_ACK_LATENCY GNUNET_TIME_UNIT_SECONDS
+
 /**
  * Timeout in milliseconds for libnfc operations.
  */
@@ -85,35 +112,78 @@ along with
   * Upper lenght of the uid for a valid MIFARE target
   */
 #define UID_LEN_UPPER 7
+
 /**
   * Lower lenght of the uid for a valid MIFARE target
   */
 #define UID_LEN_LOWER 4
 
+/* Commands for communication via MDB/ICP */
+/* VMC commands */
+/**
+ * Acknowledgement
+ */
+#define VMC_ACKN 0x00
 
 /**
- * @brief FRAMEBUFFER_DEVICE framebuffer device to diplay qr code
+ * Request for configuration.
  */
-const char *FRAMEBUFFER_DEVICE = "/dev/fb1";
+#define VMC_CONF 0x11
 
 /**
-  * Taler wallet application identifier
-  */
-static const uint8_t taler_aid[] = { 0xF0, 0x00, 0x54, 0x41, 0x4c, 0x45, 0x52 
};
+ * Vending, with sub-command.
+ */
+#define VMC_VEND 0x13
+#define VMC_VEND_REQUEST 0x00
+#define VMC_VEND_CANCEL 0x01
+#define VMC_VEND_SUCCESS 0x02
+#define VMC_VEND_FAILURE 0x03
+#define VMC_VEND_SESSION_COMPLETE 0x04
+/**
+ * Out of sequence.
+ */
+#define VMC_OOSQ 0xB0
 
 /**
-  * NFC select file command to select wallet aid
-  */
-static const uint8_t select_file[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 };
+ * Request to retransmit last command.
+ */
+#define VMC_RETR 0xAA
+
+/* Reader commands */
+#define READER_CONFIG "01"
+#define READER_BEGIN_SESSION "03"
+#define READER_VEND_APPROVE "05"
+#define READER_VEND_DENIED "06"
+#define READER_END_SESSION "07"
+
+/* Unused reader commands */
+#define READER_DISPLAY_REQUEST "02"
+#define READER_SESSION_CANCELLED "08"
+#define READER_SESSION_CANCEL_REQUEST "04"
+#define READER_REVALUE_APPROVED "0D"
+#define READER_REVALUE_DENIED "0E"
+
+
+struct MdbBlock
+{
+  uint8_t *bin;
+
+  size_t bin_size;
+};
+
+
 /**
-  * NFC put command to send data to the wallet
-  */
-static const uint8_t put_data[] = { 0x00, 0xDA, 0x01, 0x00, 0x7c, 0x01 };
+ * Datatype for mdb command
+ */
+struct MdbCommand
+{
+  const char *name;
+
+  struct MdbBlock cmd;
+
+  struct MdbBlock data;
+};
 
-#if FUTURE_FEATURES
-/* tunneling */
-static const uint8_t get_data[] = { 0x00, 0xCA, 0x01, 0x00, 0x00, 0x00 };
-#endif
 
 /**
  * Struct holding the information for a product to sell
@@ -133,7 +203,7 @@ struct Product
   /**
    * Number of the product in the vending machine
    */
-  char *number;
+  unsigned long long number;
 
   /**
    * Key for the product (optional, needed to test the application without 
vending machine)
@@ -141,6 +211,7 @@ struct Product
   char key;
 };
 
+
 /**
  * Handle for a payment
  */
@@ -194,6 +265,49 @@ struct PaymentActivity
   int wallet_has_uri;
 };
 
+
+/**
+ * Data structures associated with the MDB.
+ */
+struct MdbHandle
+{
+
+  uint8_t rxBuffer[MAX_SIZE_RX_BUFFER];
+
+  uint8_t txBuffer[MAX_SIZE_TX_BUFFER];
+
+  struct GNUNET_SCHEDULER_Task *rtask;
+
+  struct GNUNET_SCHEDULER_Task *wtask;
+
+  const struct MdbCommand *cmd;
+
+  const struct MdbCommand *last_cmd;
+
+  size_t rx_off;
+
+  /**
+   * Current write offset in @e txBuffer.
+   */
+  size_t tx_off;
+
+  /**
+   * Number of bytes in @e txBuffer with the serialized data of the
+   * @e last_cmd.
+   */
+  size_t tx_len;
+
+  struct GNUNET_TIME_Absolute ack_timeout;
+
+  struct termios uart_opts_backup;
+
+  int session_running;
+
+  int uartfd;
+
+};
+
+
 /**
  * Handle for the Framebuffer device
  */
@@ -230,6 +344,7 @@ struct Display
   struct fb_fix_screeninfo fix_info;
 };
 
+
 /**
  * NFC context used by the NFC reader
  */
@@ -240,6 +355,11 @@ static nfc_context *context;
  */
 static int global_ret;
 
+/**
+ * Flag set to remember that we are in shutdown.
+ */
+static int in_shutdown;
+
 /**
  * Refenence to the keyboard task
  */
@@ -295,11 +415,77 @@ static struct Product *products;
  */
 static unsigned int products_length;
 
+/**
+ * Data associated with the MDB session.
+ */
+static struct MdbHandle mdb;
+
+/**
+ * MDB response to the request for configuration.
+ */
+static struct MdbCommand readerConfigData;
+
+/**
+ * Ask MDB to begin session (with "infinite" money)
+ */
+static struct MdbCommand beginSession;
+
+/**
+ * Refuse vending request (payment failed)
+ */
+static struct MdbCommand denyVend;
+
+/**
+ * Approve vending request (payment succeeded)
+ */
+static struct MdbCommand approveVend;
+
+/**
+ * Terminate session.
+ */
+static struct MdbCommand endSession;
+
+/**
+ * @brief FRAMEBUFFER_DEVICE framebuffer device to diplay qr code
+ */
+static char *framebuffer_device_filename;
+
+/**
+ * Name of the UART device with the MDB (i.e. /dev/ttyAMA0).
+ */
+static char *uart_device_filename;
+
+/**
+ * Global option '-d' to disable MDB set.
+ */
+static int disable_mdb;
+
+/**
+ * Taler wallet application identifier
+ */
+static const uint8_t taler_aid[] = { 0xF0, 0x00, 0x54, 0x41, 0x4c, 0x45, 0x52 
};
+
+/**
+ * NFC select file command to select wallet aid
+ */
+static const uint8_t select_file[] = { 0x00, 0xA4, 0x04, 0x00, 0x07 };
+
+/**
+ * NFC put command to send data to the wallet
+ */
+static const uint8_t put_data[] = { 0x00, 0xDA, 0x01, 0x00, 0x7c, 0x01 };
+
+#if FUTURE_FEATURES
+/* tunneling */
+static const uint8_t get_data[] = { 0x00, 0xCA, 0x01, 0x00, 0x00, 0x00 };
+#endif
+
 /**
  * Handle for the framebuffer device
  */
 static struct Display qrDisplay;
 
+
 #if HAVE_QRENCODE_H
 #include <qrencode.h>
 
@@ -393,6 +579,12 @@ show_qrcode (const char *uri)
 
 #endif
 
+
+static void
+run_mdb_event_loop (void);
+
+
+
 /**
  * Cleanup all the data when a order has succeeded or got cancelled
  * @param pa the payment activity to clean up
@@ -430,6 +622,39 @@ cleanup_payment (struct PaymentActivity *pa)
   GNUNET_free (pa);
 }
 
+
+static void
+mdb_shutdown ()
+{
+  if (NULL != mdb.rtask)
+  {
+    GNUNET_SCHEDULER_cancel (mdb.rtask);
+    mdb.rtask = NULL;
+  }
+  if (NULL != mdb.wtask)
+  {
+    GNUNET_SCHEDULER_cancel (mdb.wtask);
+    mdb.wtask = NULL;
+  }
+
+  /* restore UART */
+  if (0 != tcsetattr (mdb.uartfd,
+                      TCSAFLUSH,
+                      &mdb.uart_opts_backup))
+  {
+    printf ("Failed to restore uart discipline\n");
+    global_ret = EXIT_FAILURE;
+  }
+  if (-1 != mdb.uartfd)
+  {
+    (void) close (mdb.uartfd);
+    mdb.uartfd = -1;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Shutdown complete (including MDB)\n");
+}
+
+
 /**
  * Shutdown the application.
  * @param cls closure
@@ -457,6 +682,10 @@ shutdown_task (void *cls)
     GNUNET_SCHEDULER_cancel (keyboard_task);
     keyboard_task = NULL;
   }
+  /* last ditch saying nicely goodbye to MDB */
+  in_shutdown = GNUNET_YES;
+  mdb.cmd = &endSession;
+  run_mdb_event_loop ();
   if (NULL != ctx)
   {
     GNUNET_CURL_fini (ctx);
@@ -467,6 +696,7 @@ shutdown_task (void *cls)
     GNUNET_CURL_gnunet_rc_destroy (rc);
     rc = NULL;
   }
+
   if (NULL != qrDisplay.memory)
   {
     /* free the display data  */
@@ -491,16 +721,13 @@ shutdown_task (void *cls)
   if (NULL != products)
   {
     for (unsigned int i = 0; i < products_length; i++)
-    {
       GNUNET_free (products[i].description);
-      GNUNET_free (products[i].number);
-    }
     GNUNET_array_grow (products,
                        products_length,
                        0);
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Shutdown complete\n");
+              "Shutdown complete (except for MDB)\n");
 }
 
 
@@ -515,6 +742,7 @@ connect_target (void *cls);
 static void
 wallet_select_aid (void *cls);
 
+
 /**
  * Transmit the pay uri from taler to the wallet application via NFC
  * @param cls closure
@@ -559,7 +787,7 @@ wallet_transmit_uri (void *cls)
     return;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "'PUT DATA' command sent successfully\n");
+              "'PUT DATA' command sent successfully via NFC\n");
   pa->wallet_has_uri = GNUNET_YES;
   /* FIXME: or just offer Internet service here? */
   pa->delay_task = GNUNET_SCHEDULER_add_delayed (MAX_HTTP_RETRY_FREQ,
@@ -567,6 +795,7 @@ wallet_transmit_uri (void *cls)
                                                  pa);
 }
 
+
 /**
  * Select the taler wallet app via NFC
  * @param cls closure
@@ -583,7 +812,7 @@ wallet_select_aid (void *cls)
   memcpy (&message[sizeof (select_file)], taler_aid, sizeof (taler_aid));
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Trying to find Taler wallet\n");
+              "Trying to find Taler wallet on NFC\n");
   if (0 > nfc_initiator_transceive_bytes (pa->pnd,
                                           message,
                                           sizeof (message),
@@ -602,7 +831,7 @@ wallet_select_aid (void *cls)
                    sizeof (response)))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Taler wallet found\n");
+                "Taler wallet found over NFC\n");
     pa->delay_task = GNUNET_SCHEDULER_add_now (&wallet_transmit_uri,
                                                pa);
     return;
@@ -626,8 +855,10 @@ wallet_select_aid (void *cls)
                                            pa);
 }
 
+
 /**
  * Connect the NFC reader with a compatible NFC target
+ *
  * @param cls closure
  */
 static void
@@ -672,8 +903,10 @@ connect_target (void *cls)
                                            pa);
 }
 
+
 /**
  * Open the NFC reader.
+ *
  * @param cls closure
  */
 static void
@@ -697,7 +930,7 @@ open_nfc_reader (void *cls)
   if (0 > nfc_initiator_init (pa->pnd))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to initialize NFC device: %s",
+                "Failed to initialize NFC device: %s\n",
                 nfc_strerror (pa->pnd));
     cleanup_payment (pa);
     GNUNET_assert (payment_activity == pa);
@@ -739,16 +972,18 @@ check_payment_cb (void *cls,
                   const char *taler_pay_uri)
 {
   struct PaymentActivity *pa = cls;
+
   (void) refunded;
   (void) refund_amount;
   (void) obj;
-
   pa->cpo = NULL;
   if (MHD_HTTP_OK != http_status)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Backend request to /check-payment failed: %u",
+                "Backend request to /check-payment failed: %u\n",
                 http_status);
+    mdb.cmd = &denyVend;
+    run_mdb_event_loop ();
     cleanup_payment (pa);
     GNUNET_assert (payment_activity == pa);
     payment_activity = NULL;
@@ -757,10 +992,7 @@ check_payment_cb (void *cls,
 
   if (paid)
   {
-    fprintf (stderr,
-             "FIXME: yield product here!\n");
-    /* FIXME: later: continue to offer Internet UNTIL
-       NFC device disconnects (if NFC connected) */
+    mdb.cmd = &approveVend;
     cleanup_payment (pa);
     GNUNET_assert (payment_activity == pa);
     payment_activity = NULL;
@@ -795,8 +1027,10 @@ check_payment_cb (void *cls,
   }
 }
 
+
 /**
  * Check the payment status again
+ *
  * @param cls closure
  */
 static void
@@ -815,8 +1049,10 @@ check_payment_again (void *cls)
                                           pa);
 }
 
+
 /**
  * Callback for a PUT /order request
+ *
  * @param cls closure
  * @param http_status HTTP status code for this request
  * @param ec Taler error code
@@ -840,6 +1076,8 @@ proposal_cb (void *cls,
                 "Failed to setup order with backend: %u/%d\n",
                 http_status,
                 (int) ec);
+    mdb.cmd = &denyVend;
+    run_mdb_event_loop ();
     cleanup_payment (pa);
     GNUNET_assert (payment_activity == pa);
     payment_activity = NULL;
@@ -856,11 +1094,12 @@ proposal_cb (void *cls,
                                           GNUNET_TIME_UNIT_ZERO,
                                           &check_payment_cb,
                                           pa);
-
 }
 
+
 /**
  * Launch a new order
+ *
  * @param product information for product to sell
  * @return payment activity for the order, NULL on failure
  */
@@ -922,8 +1161,10 @@ launch_payment (const struct Product *product)
 static void
 start_read_keyboard (void);
 
+
 /**
  * Read the character from stdin and activate the selected task
+ *
  * @param cls closure
  */
 static void
@@ -944,6 +1185,25 @@ read_keyboard_command (void *cls)
   {
     if (NULL != payment_activity)
     {
+      mdb.cmd = &denyVend;
+      run_mdb_event_loop ();
+      cleanup_payment (payment_activity);
+      payment_activity = NULL;
+    }
+    else
+    {
+      fprintf (stderr,
+               "No purchase activity pending\n");
+    }
+    start_read_keyboard ();
+    return;
+  }
+  if ((char) input == 'a')
+  {
+    if (NULL != payment_activity)
+    {
+      mdb.cmd = &approveVend;
+      run_mdb_event_loop ();
       cleanup_payment (payment_activity);
       payment_activity = NULL;
     }
@@ -957,8 +1217,8 @@ read_keyboard_command (void *cls)
   }
   if (NULL != payment_activity)
   {
-    fprintf (stderr,
-             "Purchase activity already pending\n");
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Purchase activity already pending\n");
     start_read_keyboard ();
     return;
   }
@@ -975,6 +1235,7 @@ read_keyboard_command (void *cls)
   start_read_keyboard ();
 }
 
+
 /**
  * Wait for a keyboard input
  */
@@ -988,7 +1249,9 @@ start_read_keyboard ()
     printf ("'%c' to buy %s\n",
             products[i].key,
             products[i].description);
-  printf ("'c' to cancel last purchase\n'x' to quit\n");
+  printf ("'a' to fake payment for the last purchase\n"
+          "'c' to cancel last purchase\n"
+          "'x' to quit\n");
   printf ("Waiting for keyboard input\n");
   keyboard_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
                                                   &fh,
@@ -996,6 +1259,408 @@ start_read_keyboard ()
                                                   NULL);
 }
 
+
+static void
+write_mdb_command (void *cls)
+{
+  int did_write = 0;
+
+  (void) cls;
+  mdb.wtask = NULL;
+  if (mdb.tx_off < mdb.tx_len)
+  {
+    ssize_t ret = write (mdb.uartfd,
+                         &mdb.txBuffer[mdb.tx_off],
+                         mdb.tx_len - mdb.tx_off);
+
+    did_write = 1;
+    if (-1 == ret)
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "write",
+                                uart_device_filename);
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+    mdb.tx_off += ret;
+    if ( (ret > 0) &&
+         (mdb.tx_off == mdb.tx_len) )
+      mdb.ack_timeout = GNUNET_TIME_relative_to_absolute (MAX_ACK_LATENCY);
+  }
+  /* ongoing write incomplete, continue later */
+  if (mdb.tx_off < mdb.tx_len)
+  {
+    run_mdb_event_loop ();
+    return;
+  }
+  if (NULL != mdb.last_cmd)
+  {
+    struct GNUNET_TIME_Relative del;
+
+    /* Still waiting for ACK! */
+    del = GNUNET_TIME_absolute_get_remaining (mdb.ack_timeout);
+    if (0 != del.rel_value_us)
+    {
+      if (did_write)
+        run_mdb_event_loop ();
+      else
+        mdb.wtask = GNUNET_SCHEDULER_add_delayed (del,
+                                                  &write_mdb_command,
+                                                  NULL);
+      return;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "MDB failed to acknowledge command `%s' within timeout\n",
+                mdb.last_cmd->name);
+    mdb.last_cmd = NULL;
+    if (in_shutdown)
+    {
+      mdb_shutdown ();
+      return;
+    }
+  }
+  if (NULL == mdb.cmd)
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Sending command `%s'\n",
+              mdb.cmd->name);
+  mdb.tx_off = 0;
+  mdb.tx_len = mdb.cmd->cmd.bin_size + mdb.cmd->data.bin_size + 1;
+  GNUNET_assert (mdb.tx_len <= sizeof (mdb.txBuffer));
+  {
+    uint32_t chkSum = 0;
+
+    for (size_t idx = 0; idx < mdb.cmd->cmd.bin_size; idx++)
+      chkSum += mdb.txBuffer[idx] = mdb.cmd->cmd.bin[idx];
+    for (size_t idx = 0; idx < mdb.cmd->data.bin_size; idx++)
+      chkSum += mdb.txBuffer[idx + mdb.cmd->cmd.bin_size] =
+        mdb.cmd->data.bin[idx];
+    mdb.txBuffer[mdb.cmd->cmd.bin_size + mdb.cmd->data.bin_size] =
+      (uint8_t) (chkSum & 0xFF);
+  }
+  mdb.last_cmd = mdb.cmd;
+  mdb.cmd = NULL;
+  run_mdb_event_loop ();
+}
+
+
+/**
+ *
+ * @param hex_len number of characters in @a hex
+ */
+static void
+handle_command (const char *hex,
+                size_t hex_len)
+{
+  unsigned int cmd;
+
+  if (0 == hex_len)
+    return;
+  if (0 != (hex_len % 2))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Received unexpected input `%.*s'\n",
+                (int) hex_len,
+                hex);
+    GNUNET_break_op (0);
+    return;
+  }
+  if (1 != sscanf (hex,
+                   "%2X",
+                   &cmd))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Received non-HEX input `%.*s'\n",
+                (int) hex_len,
+                hex);
+    GNUNET_break_op (0);
+    return;
+  }
+  switch (cmd)
+  {
+  case VMC_VEND:
+    {
+      unsigned int subcmd;
+
+      GNUNET_break (GNUNET_YES == mdb.session_running);
+      if (4 > hex_len)
+      {
+        GNUNET_break_op (0);
+        return;
+      }
+      if (1 != sscanf (&hex[2],
+                       "%2X",
+                       &subcmd))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Received non-HEX input `%.*s'\n",
+                    (int) hex_len - 2,
+                    &hex[2]);
+        GNUNET_break_op (0);
+        return;
+      }
+      switch (subcmd)
+      {
+      case VMC_VEND_REQUEST:
+        {
+          unsigned int product;
+
+          GNUNET_break (GNUNET_YES == mdb.session_running);
+          /* NOTE: hex[4..7] contain the price */
+          if (12 > hex_len)
+          {
+            GNUNET_break_op (0);
+            return;
+          }
+          if (1 != sscanf (&hex[8],
+                           "%4X",
+                           &product))
+          {
+            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                        "Received non-HEX input `%.*s'\n",
+                        (int) hex_len - 8,
+                        &hex[8]);
+            GNUNET_break_op (0);
+            return;
+          }
+          for (unsigned int i = 0; i < products_length; i++)
+            if (product == products[i].number)
+            {
+              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                          "Product %u selected on NFC\n",
+                          product);
+              payment_activity = launch_payment (&products[i]);
+              return;
+            }
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      "Unknown product %u selected on MDB, denying vend\n",
+                      product);
+          mdb.cmd = &denyVend;
+          break;
+        }
+      case VMC_VEND_SUCCESS:
+        GNUNET_break (GNUNET_YES == mdb.session_running);
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Vend Success\n");
+        break;
+      case VMC_VEND_FAILURE:
+        {
+          GNUNET_break (GNUNET_YES == mdb.session_running);
+          mdb.cmd = &endSession;
+          mdb.session_running = GNUNET_NO;
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "Received MDB vend failure, refunding customer (FIXME: 
not implemented)\n");
+          /* FIXME: refund logic here! */
+          if (NULL != payment_activity)
+          {
+            cleanup_payment (payment_activity);
+            payment_activity = NULL;
+          }
+          break;
+        }
+      case VMC_VEND_SESSION_COMPLETE:
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                      "Received MDB session complete\n");
+          mdb.session_running = GNUNET_NO;
+          mdb.cmd = &endSession;
+          if (NULL != payment_activity)
+          {
+            cleanup_payment (payment_activity);
+            payment_activity = NULL;
+          }
+        }
+        break;
+      default:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Unknown MDB sub-command %X of command %X\n",
+                    subcmd,
+                    cmd);
+        break;
+      }
+      break;
+    }
+  case VMC_CONF:
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received request for configuration via MDB\n");
+    mdb.cmd = &readerConfigData;
+    break;
+  case 0x14:
+    {
+      unsigned int subcmd;
+
+      if (4 > hex_len)
+      {
+        GNUNET_break_op (0);
+        return;
+      }
+      if (1 != sscanf (&hex[2],
+                       "%2X",
+                       &subcmd))
+      {
+        GNUNET_break_op (0);
+        return;
+      }
+
+      switch (subcmd)
+      {
+      case 0x01:
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Received Reader Enable via MDB\n");
+        mdb.session_running = GNUNET_NO;
+        break;
+      default:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Unknown MDB sub-command %X of command %X\n",
+                    subcmd,
+                    cmd);
+        break;
+      }
+      break;
+    }
+  case VMC_ACKN:
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received acknowledgement (for command `%s') from MDB\n",
+                (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
+    if (&beginSession == mdb.last_cmd)
+      mdb.session_running = GNUNET_YES;
+    if (&denyVend == mdb.last_cmd)
+    {
+      mdb.session_running = GNUNET_NO;
+      mdb.cmd = &endSession;
+    }
+    mdb.last_cmd = NULL;
+    if (NULL != mdb.wtask)
+    {
+      GNUNET_SCHEDULER_cancel (mdb.wtask);
+      mdb.wtask = NULL;
+    }
+    break;
+  case VMC_OOSQ:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received command out of sequence from MDB (for command 
`%s')\n",
+                (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
+    mdb.session_running = GNUNET_NO;
+    if (mdb.last_cmd != &endSession)
+      mdb.cmd = &endSession;
+    else
+      mdb.last_cmd = NULL;
+    break;
+  case VMC_RETR:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received request to resend previous data from MDB (previous 
command was `%s')\n",
+                (NULL != mdb.last_cmd) ? mdb.last_cmd->name : "?");
+    GNUNET_break (NULL == mdb.cmd);
+    GNUNET_break (NULL != mdb.last_cmd);
+    mdb.cmd = mdb.last_cmd;
+    mdb.last_cmd = NULL;
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received unknown MDB command %X\n",
+                cmd);
+    break;
+  }
+}
+
+
+static void
+read_mdb_command (void *cls)
+{
+  ssize_t ret;
+  size_t cmdStartIdx;
+  size_t cmdEndIdx;
+
+  (void) cls;
+  mdb.rtask = NULL;
+  ret = read (mdb.uartfd,
+              &mdb.rxBuffer[mdb.rx_off],
+              sizeof (mdb.rxBuffer) - mdb.rx_off);
+  if (-1 == ret)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "read",
+                              uart_device_filename);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  mdb.rx_off += ret;
+
+  while (mdb.rx_off > 0)
+  {
+    /* find begining of command */
+    for (cmdStartIdx = 0; cmdStartIdx < mdb.rx_off; cmdStartIdx++)
+      if (0x02 == mdb.rxBuffer[cmdStartIdx])
+        break;
+    if (cmdStartIdx == mdb.rx_off)
+    {
+      mdb.rx_off = 0;
+      break;
+    }
+    /* find end of command */
+    for (cmdEndIdx = cmdStartIdx; cmdEndIdx < mdb.rx_off; cmdEndIdx++)
+      if (0x03 == mdb.rxBuffer[cmdEndIdx])
+        break;
+    if (cmdEndIdx == mdb.rx_off)
+    {
+      /* check to make sure rxBuffer was big enough in principle */
+      if ( (cmdStartIdx == 0) &&
+           (mdb.rx_off == sizeof (mdb.rxBuffer)) )
+      {
+        /* Developer: if this happens, try increasing rxBuffer! */
+        GNUNET_break (0);
+        GNUNET_SCHEDULER_shutdown ();
+        return;
+      }
+      memmove (mdb.rxBuffer,
+               &mdb.rxBuffer[cmdStartIdx],
+               mdb.rx_off - cmdStartIdx);
+      mdb.rx_off -= cmdStartIdx;
+      break;
+    }
+    handle_command ((const char *) &mdb.rxBuffer[cmdStartIdx + 1],
+                    cmdEndIdx - cmdStartIdx - 1);
+    memmove (mdb.rxBuffer,
+             &mdb.rxBuffer[cmdEndIdx + 1],
+             mdb.rx_off - cmdEndIdx + 1);
+    mdb.rx_off -= (cmdEndIdx + 1);
+  }
+  if (in_shutdown)
+  {
+    mdb_shutdown ();
+    return;
+  }
+  run_mdb_event_loop ();
+}
+
+
+static void
+run_mdb_event_loop ()
+{
+  struct GNUNET_DISK_FileHandle fh = { mdb.uartfd };
+
+  if (disable_mdb)
+    return;
+  if ( (GNUNET_NO == mdb.session_running) &&
+       (NULL == mdb.cmd) &&
+       (NULL == mdb.last_cmd) )
+    mdb.cmd = &beginSession;
+
+  if ( (NULL == mdb.wtask) &&
+       ( (NULL != mdb.cmd) ||
+         (mdb.tx_len > mdb.tx_off) ) )
+    mdb.wtask = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                                 &fh,
+                                                 &write_mdb_command,
+                                                 NULL);
+  if (NULL == mdb.rtask)
+    mdb.rtask = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                                &fh,
+                                                &read_mdb_command,
+                                                NULL);
+}
+
+
 /**
  * Read the products from the configuration file
  * @param cls closure
@@ -1060,7 +1725,7 @@ read_products (void *cls,
     tmpProduct.key = '\0';
   }
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_string (cls,
+      GNUNET_CONFIGURATION_get_value_number (cls,
                                              section,
                                              "number",
                                              &tmpProduct.number))
@@ -1076,8 +1741,71 @@ read_products (void *cls,
                        tmpProduct);
 }
 
+
+static int
+mdb_init ()
+{
+  struct termios uart_opts_raw;
+
+  if (disable_mdb)
+    return GNUNET_OK;
+  /* open uart connection */
+  if (0 > (mdb.uartfd = open (uart_device_filename,
+                              O_RDWR | O_NOCTTY | O_NDELAY)))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              uart_device_filename);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != tcgetattr (mdb.uartfd, &mdb.uart_opts_backup))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                         "tcgetattr");
+    return GNUNET_SYSERR;
+  }
+  uart_opts_raw = mdb.uart_opts_backup;
+
+  /* reset current uart discipline */
+  memset (&uart_opts_raw,
+          0,
+          sizeof(uart_opts_raw));
+
+  /* set baudrate */
+  cfsetispeed (&uart_opts_raw, B9600);
+  cfsetospeed (&uart_opts_raw, B9600);
+
+  /* set options */
+  uart_opts_raw.c_cflag &= ~PARENB;
+  uart_opts_raw.c_cflag &= ~CSTOPB;
+  uart_opts_raw.c_cflag &= ~CSIZE;
+  uart_opts_raw.c_cflag |= CS8;
+
+  /* 19200 bps, 8 databits, ignore cd-signal, allow reading */
+  uart_opts_raw.c_cflag |= (CLOCAL | CREAD);
+  uart_opts_raw.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+  uart_opts_raw.c_iflag = IGNPAR;
+  uart_opts_raw.c_oflag &= ~OPOST;
+  uart_opts_raw.c_cc[VMIN]  = 0;
+  uart_opts_raw.c_cc[VTIME] = 50;
+  tcflush (mdb.uartfd, TCIOFLUSH);
+  if (0 != tcsetattr (mdb.uartfd,
+                      TCSAFLUSH,
+                      &uart_opts_raw))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                         "tcsetattr");
+    return GNUNET_SYSERR;
+  }
+  run_mdb_event_loop ();
+  return GNUNET_OK;
+}
+
+
 /**
  * Start the application
+ *
  * @param cls closure
  * @param args arguments left
  * @param cfgfile config file name
@@ -1105,6 +1833,28 @@ run (void *cls,
     global_ret = EXIT_FAILURE;
     return;
   }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               "taler-mdb",
+                                               "UART_DEVICE",
+                                               &uart_device_filename))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "taler-mdb",
+                               "UART_DEVICE");
+    uart_device_filename = GNUNET_strdup ("/dev/ttyAMA0");
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               "taler-mdb",
+                                               "FRAMEBUFFER_DEVICE",
+                                               &framebuffer_device_filename))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "taler-mdb",
+                               "FRAMEBUFFER_DEVICE");
+    uart_device_filename = GNUNET_strdup ("/dev/fb1");
+  }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              "taler-mdb",
@@ -1168,6 +1918,17 @@ run (void *cls,
 
   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
                                  NULL);
+
+  /* initialize mdb */
+  if (GNUNET_OK != mdb_init ())
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to initialize MDB (mdb_init() failed)\n");
+    global_ret = EXIT_FAILURE;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
   /* initialize nfc */
   nfc_init (&context);
   if (NULL == context)
@@ -1187,9 +1948,10 @@ run (void *cls,
   GNUNET_assert (GNUNET_OK ==
                  GNUNET_CURL_append_header (ctx,
                                             authorization));
+
 #if HAVE_QRENCODE_H
   /* open the framebuffer device */
-  qrDisplay.devicefd = open (FRAMEBUFFER_DEVICE,
+  qrDisplay.devicefd = open (framebuffer_device_filename,
                              O_RDWR);
   if (0 < qrDisplay.devicefd)
   {
@@ -1256,20 +2018,61 @@ run (void *cls,
     }
     else
     {
-      write (qrDisplay.backlightfd, "0", 1);
+      (void) write (qrDisplay.backlightfd, "0", 1);
     }
   }
   else
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "open(), could not open framebuffer device %s\n",
-                FRAMEBUFFER_DEVICE);
+                framebuffer_device_filename);
   }
 #endif
-
   start_read_keyboard ();
 }
 
+
+static void
+parse_block (struct MdbBlock *blk,
+             const char *hex)
+{
+  if (NULL == hex)
+  {
+    blk->bin_size = 0;
+    blk->bin = NULL;
+    return;
+  }
+  blk->bin_size = strlen (hex) / 2;
+  blk->bin = GNUNET_malloc (blk->bin_size);
+  for (size_t idx = 0; idx < blk->bin_size; idx++)
+  {
+    unsigned int val;
+
+    GNUNET_assert (1 ==
+                   sscanf (&hex[idx * 2],
+                           "%2X",
+                           &val));
+    blk->bin[idx] = (uint8_t) val;
+  }
+}
+
+
+static struct MdbCommand
+setup_mdb_cmd (const char *name,
+               const char *cmd,
+               const char *data)
+{
+  struct MdbCommand cmdNew;
+
+  cmdNew.name = (NULL == name)
+                ? "No Cmd Name Set"
+                : name;
+  parse_block (&cmdNew.cmd, cmd);
+  parse_block (&cmdNew.data, data);
+  return cmdNew;
+}
+
+
 int
 main (int argc,
       char*const*argv)
@@ -1278,6 +2081,10 @@ main (int argc,
   int ret;
   /* the available command line options */
   struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_option_flag ('d',
+                               "disable-mdb",
+                               "disable all interactions with the MDB (for 
testing without machine)",
+                               &disable_mdb),
     GNUNET_GETOPT_OPTION_END
   };
   int have_tty;
@@ -1294,6 +2101,23 @@ main (int argc,
       fprintf (stderr,
                "Failed to set terminal discipline\n");
   }
+
+  readerConfigData = setup_mdb_cmd ("Reader Config",
+                                    READER_CONFIG,
+                                    "0119780A02FF0C");
+  beginSession = setup_mdb_cmd ("Begin Session",
+                                READER_BEGIN_SESSION,
+                                "FFFF");
+  approveVend = setup_mdb_cmd ("Approve Vend",
+                               READER_VEND_APPROVE,
+                               "0001");
+  denyVend = setup_mdb_cmd ("Deny Vend",
+                            READER_VEND_DENIED,
+                            NULL);
+  endSession = setup_mdb_cmd ("End Session",
+                              READER_END_SESSION,
+                              NULL);
+
   ret = GNUNET_PROGRAM_run (argc,
                             argv,
                             "taler-mdb",
@@ -1303,11 +2127,14 @@ main (int argc,
                             NULL);
   if (have_tty)
   {
-    // Restore previous TTY settings
-    if (0 != tcsetattr (STDIN_FILENO, TCSANOW, &tty_opts_backup))
+    /* Restore previous TTY settings */
+    if (0 != tcsetattr (STDIN_FILENO,
+                        TCSANOW,
+                        &tty_opts_backup))
       fprintf (stderr,
                "Failed to restore terminal discipline\n");
   }
+
   if (GNUNET_OK != ret)
     return 1;
   return global_ret;
diff --git a/src/mdb/.gitkeep b/src/mdb/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/taler.conf b/taler.conf
index 4c34022..ac50abb 100644
--- a/taler.conf
+++ b/taler.conf
@@ -13,6 +13,12 @@ fulfillment-url = taler://fulfillment-success
 # must be url - utf8 encoded
 fulfillment-msg = /Enjoy+your+
 
+# Name of the UART where the MDB connector is reachable.
+UART_DEVICE = /dev/ttyAMA0
+
+# Name of the framebuffer to use for the QR code.
+FRAMEBUFFER_DEVICE = /dev/fb1
+
 #Products
 #end declaration
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]