taler-mdb

GNU Taler Extensions and Integrations
Log | Files | Refs | Submodules | README | LICENSE

taler-mdb-qr-show.c (11892B)


      1 /*
      2  This file is part of TALER
      3  Copyright (C) 2019 GNUnet e.V.
      4 
      5  TALER is free software; you can redistribute it and/or modify it under the
      6  terms of the GNU General Public License as published by the Free Software
      7  Foundation; either version 3, or (at your option) any later version.
      8 
      9  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10  WARRANTY; without even the implied warranty of MERCHANTABILITY or
     11 FITNESS FOR
     12  A PARTICULAR PURPOSE.  See the GNU General Public License for more
     13 details.
     14 
     15  You should have received a copy of the GNU General Public License
     16 along with
     17  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20 * @file taler-mdb-qr-show.c
     21 * @brief shows a QR code on the display for a defined amount of time
     22 * @author Boss Marco
     23 * @author Christian Grothoff
     24 */
     25 #include "config.h"
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <unistd.h>
     30 #include <sys/socket.h>
     31 #if HAVE_SYS_UN_H
     32 #include <sys/un.h>
     33 #endif
     34 #if HAVE_NETINET_IN_H
     35 #include <netinet/in.h>
     36 #endif
     37 #if HAVE_NETINET_IP_H
     38 #include <netinet/ip.h>         /* superset of previous */
     39 #endif
     40 #include <sys/stat.h>
     41 #include <sys/types.h>
     42 #include <errno.h>
     43 #include <termios.h>
     44 #include <nfc/nfc.h>
     45 #include <microhttpd.h>
     46 #include <gnunet/gnunet_util_lib.h>
     47 #include <qrencode.h>
     48 #include <sys/mman.h>
     49 #include <sys/ioctl.h>
     50 #include <fcntl.h>
     51 /* for adafruit pitft display */
     52 #include <linux/fb.h>
     53 #include "taler_mdb_util.h"
     54 
     55 
     56 /**
     57  * Disable i18n support.
     58  */
     59 #define _(s) (s)
     60 
     61 /**
     62  * Handle for the Framebuffer device
     63  */
     64 struct Display
     65 {
     66   /**
     67    * File descriptor for the screen
     68    */
     69   int devicefd;
     70 
     71   /**
     72    * File descriptor to set backlight information
     73    */
     74   int backlightfd;
     75 
     76   /**
     77    * The display memory to set the pixel information
     78    */
     79   uint16_t *memory;
     80 
     81   /**
     82    * Original screen information
     83    */
     84   struct fb_var_screeninfo orig_vinfo;
     85 
     86   /**
     87    * Variable screen information (color depth ...)
     88    */
     89   struct fb_var_screeninfo var_info;
     90 
     91   /**
     92    * Fixed screen informtaion
     93    */
     94   struct fb_fix_screeninfo fix_info;
     95 };
     96 
     97 
     98 /**
     99  * Next program to launch.
    100  */
    101 static char *const *arg_next;
    102 
    103 static struct GNUNET_TIME_Relative delay;
    104 
    105 /**
    106  * Reference to the delay task
    107  */
    108 static struct GNUNET_SCHEDULER_Task *delay_task;
    109 
    110 /**
    111  * Name of the framebuffer device (i.e. /dev/fb1).
    112  */
    113 static char *framebuffer_device_filename;
    114 
    115 /**
    116  * Name of the backlight file of @e framebuffer_device_filename (i.e. /sys/class/backlight/soc:backlight/brightness).
    117  */
    118 static char *framebuffer_backlight_filename;
    119 
    120 /**
    121  * Global option '-i' to invert backlight on/off values
    122  */
    123 static int backlight_invert;
    124 
    125 /**
    126  * Standard backlight on value
    127  */
    128 static char backlight_on = '1';
    129 
    130 /**
    131  * Standard backlight off value
    132  */
    133 static char backlight_off = '0';
    134 
    135 /**
    136  * Handle for the framebuffer device
    137  */
    138 static struct Display qrDisplay;
    139 
    140 
    141 /**
    142  * @brief Create the QR code to pay and display it on screen
    143  *
    144  * @param uri what text to show in the QR code
    145  */
    146 static void
    147 show_qrcode (const char *uri)
    148 {
    149   QRinput *qri;
    150   QRcode *qrc;
    151   unsigned int size;
    152   size_t xOff;
    153   size_t yOff;
    154   unsigned int nwidth;
    155 
    156   if (0 > qrDisplay.devicefd)
    157     return; /* no display, no dice */
    158   qri = QRinput_new2 (0, QR_ECLEVEL_L);
    159   if (NULL == qri)
    160   {
    161     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    162                          "QRinput_new2");
    163     return;
    164   }
    165   /* first try encoding as uppercase-only alpha-numerical
    166      QR code (much smaller encoding); if that fails, also
    167      try using binary encoding (in case nick contains
    168      special characters). */
    169   if ( (0 !=
    170         QRinput_append (qri,
    171                         QR_MODE_AN,
    172                         strlen (uri),
    173                         (unsigned char *) uri)) &&
    174        (0 !=
    175         QRinput_append (qri,
    176                         QR_MODE_8,
    177                         strlen (uri),
    178                         (unsigned char *) uri)))
    179   {
    180     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    181                          "QRinput_append");
    182     return;
    183   }
    184   qrc = QRcode_encodeInput (qri);
    185   if (NULL == qrc)
    186   {
    187     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    188                          "QRcode_encodeInput");
    189     QRinput_free (qri);
    190     return;
    191   }
    192 
    193   /* +8 for 4-pixels border */
    194   size = GNUNET_MIN (qrDisplay.var_info.xres,
    195                      qrDisplay.var_info.yres);
    196   nwidth = qrc->width + 8; /* 4 pixel border */
    197   xOff = 4 * size / nwidth;
    198   yOff = 4 * size / nwidth;
    199   if (qrDisplay.var_info.xres < qrDisplay.var_info.yres)
    200     yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2;
    201   else
    202     xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2;
    203   for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++)
    204     for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++)
    205     {
    206       unsigned int xoff = x * nwidth / size;
    207       unsigned int yoff = y * nwidth / size;
    208       unsigned int off = xoff + yoff * qrc->width;
    209       if ( (xoff >= (unsigned) qrc->width) ||
    210            (yoff >= (unsigned) qrc->width) )
    211         continue;
    212       qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] =
    213         (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000;
    214     }
    215 
    216   QRcode_free (qrc);
    217   QRinput_free (qri);
    218 
    219   if (0 < qrDisplay.backlightfd)
    220     (void) ! write (qrDisplay.backlightfd,
    221                     &backlight_on,
    222                     1);
    223 }
    224 
    225 
    226 static void
    227 stop_task (void *cls)
    228 {
    229   (void) cls;
    230   delay_task = NULL;
    231   GNUNET_SCHEDULER_shutdown ();
    232 }
    233 
    234 
    235 static void
    236 shutdown_task (void *cls)
    237 {
    238   (void) cls;
    239   if (NULL != delay_task)
    240   {
    241     GNUNET_SCHEDULER_cancel (delay_task);
    242     delay_task = NULL;
    243   }
    244   if (NULL != qrDisplay.memory)
    245     memset (qrDisplay.memory,
    246             0xFF,
    247             qrDisplay.var_info.xres
    248             * qrDisplay.var_info.yres
    249             * sizeof (uint16_t));
    250   if (0 < qrDisplay.backlightfd)
    251     (void) ! write (qrDisplay.backlightfd,
    252                     &backlight_off,
    253                     1);
    254   if (NULL != qrDisplay.memory)
    255   {
    256     /* free the display data  */
    257     munmap (qrDisplay.memory,
    258             qrDisplay.fix_info.smem_len);
    259     qrDisplay.memory = NULL;
    260     /* reset original state */
    261     if (0 > ioctl (qrDisplay.devicefd,
    262                    FBIOPUT_VSCREENINFO,
    263                    &qrDisplay.orig_vinfo))
    264     {
    265       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    266                   "failed to reset original display state\n");
    267     }
    268     /* close device */
    269     GNUNET_break (0 == close (qrDisplay.devicefd));
    270     qrDisplay.devicefd = -1;
    271     if (0 < qrDisplay.backlightfd)
    272       GNUNET_break (0 == close (qrDisplay.backlightfd));
    273     qrDisplay.backlightfd = -1;
    274   }
    275 }
    276 
    277 
    278 /**
    279  * @brief Start the application
    280  *
    281  * @param cls closure
    282  * @param args arguments left
    283  * @param cfgfile config file name
    284  * @param cfg handle for the configuration file
    285  */
    286 static void
    287 run (void *cls,
    288      char *const *args,
    289      const char *cfgfile,
    290      const struct GNUNET_CONFIGURATION_Handle *cfg)
    291 {
    292   (void) cls;
    293   (void) cfgfile;
    294 
    295   if (NULL == args[0])
    296     return;
    297   arg_next = args + 1;
    298   if (GNUNET_OK !=
    299       GNUNET_CONFIGURATION_get_value_filename (
    300         cfg,
    301         "taler-mdb",
    302         "FRAMEBUFFER_DEVICE",
    303         &framebuffer_device_filename))
    304   {
    305     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    306                                "taler-mdb",
    307                                "FRAMEBUFFER_DEVICE");
    308     framebuffer_device_filename = GNUNET_strdup ("/dev/fb1");
    309   }
    310   if (GNUNET_OK !=
    311       GNUNET_CONFIGURATION_get_value_filename (
    312         cfg,
    313         "taler-mdb",
    314         "FRAMEBUFFER_BACKLIGHT",
    315         &framebuffer_backlight_filename))
    316   {
    317     GNUNET_log_config_missing (
    318       GNUNET_ERROR_TYPE_ERROR,
    319       "taler-mdb",
    320       "FRAMEBUFFER_BACKLIGHT");
    321     framebuffer_backlight_filename = GNUNET_strdup (
    322       "/sys/class/backlight/soc:backlight/brightness");
    323   }
    324   /* open the framebuffer device */
    325   qrDisplay.devicefd = open (framebuffer_device_filename,
    326                              O_RDWR);
    327   if (0 < qrDisplay.devicefd)
    328   {
    329     /* read information about the screen */
    330     ioctl (qrDisplay.devicefd,
    331            FBIOGET_VSCREENINFO,
    332            &qrDisplay.var_info);
    333 
    334     /* store current screeninfo for reset */
    335     qrDisplay.orig_vinfo = qrDisplay.var_info;
    336 
    337     if (16 != qrDisplay.var_info.bits_per_pixel)
    338     {
    339       /* Change variable info to 16 bit per pixel */
    340       qrDisplay.var_info.bits_per_pixel = 16;
    341       if (0 > ioctl (qrDisplay.devicefd,
    342                      FBIOPUT_VSCREENINFO,
    343                      &qrDisplay.var_info))
    344       {
    345         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    346                              "ioctl(FBIOPUT_VSCREENINFO)");
    347         return;
    348       }
    349     }
    350 
    351     /* Get fixed screen information */
    352     if (0 > ioctl (qrDisplay.devicefd,
    353                    FBIOGET_FSCREENINFO,
    354                    &qrDisplay.fix_info))
    355     {
    356       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    357                            "ioctl(FBIOGET_FSCREENINFO)");
    358       return;
    359     }
    360 
    361     /* get pointer onto frame buffer */
    362     qrDisplay.memory
    363       = mmap (NULL,
    364               qrDisplay.fix_info.smem_len,
    365               PROT_READ | PROT_WRITE, MAP_SHARED,
    366               qrDisplay.devicefd,
    367               0);
    368 
    369     /* open backlight file to turn display backlight on and off */
    370     if (0 > qrDisplay.devicefd)
    371     {
    372       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    373                            "mmap");
    374       return;
    375     }
    376 
    377     memset (qrDisplay.memory,
    378             0xFF,
    379             qrDisplay.var_info.xres * qrDisplay.var_info.yres
    380             * sizeof (uint16_t));
    381 
    382     qrDisplay.backlightfd = open (
    383       framebuffer_backlight_filename, O_WRONLY);
    384     if (0 > qrDisplay.backlightfd)
    385     {
    386       GNUNET_log_strerror_file (
    387         GNUNET_ERROR_TYPE_WARNING,
    388         "open",
    389         framebuffer_backlight_filename);
    390     }
    391     else
    392     {
    393       if (backlight_invert)
    394       {
    395         backlight_on = '0';
    396         backlight_off = '1';
    397       }
    398       (void) ! write (qrDisplay.backlightfd,
    399                       &backlight_off,
    400                       1);
    401     }
    402   }
    403   else
    404   {
    405     GNUNET_log_strerror_file (
    406       GNUNET_ERROR_TYPE_WARNING,
    407       "open",
    408       framebuffer_device_filename);
    409   }
    410   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
    411                                  NULL);
    412   show_qrcode (args[0]);
    413   delay_task = GNUNET_SCHEDULER_add_delayed (delay,
    414                                              &stop_task,
    415                                              NULL);
    416 }
    417 
    418 
    419 int
    420 main (int argc,
    421       char*const*argv)
    422 {
    423   int ret;
    424   /* the available command line options */
    425   struct GNUNET_GETOPT_CommandLineOption options[] = {
    426     GNUNET_GETOPT_option_relative_time ('d',
    427                                         "delay",
    428                                         "DELAY",
    429                                         "how long should we display the QR code before exiting",
    430                                         &delay),
    431     GNUNET_GETOPT_option_flag ('i',
    432                                "backlight-invert",
    433                                "invert the backlight on/off values (standard on = 1)",
    434                                &backlight_invert),
    435     GNUNET_GETOPT_OPTION_END
    436   };
    437 
    438   ret = GNUNET_PROGRAM_run (TALER_MDB_project_data (),
    439                             argc,
    440                             argv,
    441                             "taler-mdb-qr-show",
    442                             "This is an application to show a QR code for a defined period of time before starting another program.\n",
    443                             options,
    444                             &run,
    445                             NULL);
    446   if (GNUNET_OK != ret)
    447     return 1;
    448   if ( (NULL == arg_next) ||
    449        (NULL == arg_next[0]) )
    450     return 0;
    451   execvp (arg_next[0],
    452           arg_next);
    453   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    454                             "execvp",
    455                             arg_next[0]);
    456   return 1;
    457 }