taler-digitizer.c (38974B)
1 /* 2 This file is part of TALER cash2ecash 3 Copyright (C) 2026 GNUnet e.V. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation, either version 3 of the 8 License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * @file taler-digitizer.c 22 * @brief runs the logic for the embeded Cash Digitizer machine 23 * @author Reto Tellenbach 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <signal.h> 29 #include <gnunet/gnunet_util_lib.h> 30 #include "taler_digitizer_util.h" 31 #include "taler/taler_digitizer_service.h" 32 #include "lib/bank_api_get_config.h" 33 #include "lib/bank_api_get_accounts.h" 34 #include "lib/bank_api_post_accounts_withdrawals.h" 35 #include "lib/bank_api_get_withdrawals.h" 36 #include "lib/bank_api_post_accounts_withdrawals_confirm.h" 37 #include "digitizer_display.h" 38 #include "gpio/gpiod_wrapper.h" 39 40 41 /** 42 * Time unit for PERSON_WITHDRAL_PERIOD config. 43 * Normaly in Days but, can be changed for testing 44 */ 45 #define DIGITIZER_PERSON_WITHDRAWAL_PERIOD_TIME_UNIT GNUNET_TIME_UNIT_DAYS 46 47 #define FRAMEBUFFER_SIZE 256 48 #define PATH_QR_SHOW "/ext/QRshow" 49 50 #define SCAN_QR_TIMEOUT_SECONDS 60 51 #define DISPLAY_BLOCK_SIZE 1024 52 #define DISPLAY_COUNT 750 53 54 /** 55 * Global return value 56 */ 57 static int global_ret; 58 59 /** 60 * Global option '-d' to enable diagnostics set. 61 */ 62 static int enable_diagnostics; 63 64 /** 65 * Taler bank backend url read from configuration file 66 */ 67 static char *cfg_bank_base_url; 68 69 /** 70 * Taler bank account username 71 */ 72 static char *cfg_bank_account_username; 73 74 /** 75 * Taler bank authentication 76 * method and according data 77 */ 78 static struct DIGITIZER_BankAuthenticationData *cfg_bank_authentication; 79 80 /** 81 * Taler exchange backend url read from configuration file 82 */ 83 static char *cfg_exchange_base_url; 84 85 /** 86 * Currency read from configuration file 87 */ 88 static char *cfg_currency; 89 90 /** 91 * Per-person withdrawal limit read from configuration file 92 */ 93 static struct TALER_Amount cfg_user_withdrawallimit; 94 95 /** 96 * Per-person withdrawal period read from configuration file 97 */ 98 static struct GNUNET_TIME_Relative cfg_user_withdrawal_period; 99 100 /** 101 * KYC functionality flag read from configuration file 102 */ 103 static enum GNUNET_GenericReturnValue cfg_kyc_functionality; 104 105 /** 106 * device path to display framebuffer. 107 * usualy in /dev/fbX. 108 */ 109 static char *cfg_framebuffer_device; 110 111 /** 112 * device path to display backlight. 113 * usualy in /sys/class/backlight/<XX>/brightness 114 */ 115 static char *cfg_framebuffer_backlight; 116 117 /** 118 * Coin Acceptor installed 119 */ 120 static enum GNUNET_GenericReturnValue cfg_ca_enable; 121 122 /** 123 * device path to coin acceptor interface 124 */ 125 static char *cfg_ca_device; 126 127 /** 128 * Pin to enable Accepting coins 129 */ 130 static unsigned long long cfg_ca_en_pin; 131 132 /** 133 * Pin to recive inserted amount signal 134 */ 135 static unsigned long long cfg_ca_rx_pin; 136 137 /** 138 * Biggest value of one coin insertion 139 */ 140 static struct TALER_Amount cfg_ca_max_denomination; 141 142 /** 143 * Smallest value of one coin insertion 144 */ 145 static struct TALER_Amount cfg_ca_min_denomination; 146 147 /** 148 * Bill Acceptor installed 149 */ 150 static enum GNUNET_GenericReturnValue cfg_ba_enable; 151 152 /** 153 * device path to bill acceptor interface 154 */ 155 static char *cfg_ba_device; 156 157 /** 158 * Biggest value of one bill insertion 159 */ 160 static struct TALER_Amount cfg_ba_max_denomination; 161 162 /** 163 * Smallest value of one bill insertion 164 */ 165 static struct TALER_Amount cfg_ba_min_denomination; 166 167 168 169 /** 170 * Handle to the context for http requests 171 */ 172 static struct GNUNET_CURL_Context *curl_ctx; 173 174 /** 175 * Scheduler context for running the @e ctx. 176 */ 177 static struct GNUNET_CURL_RescheduleContext *reschedule_ctx; 178 179 /** 180 * Handle for get_config request 181 */ 182 static struct TALER_BANK_GetConfigHandle *get_config_handle; 183 184 /** 185 * Handle for get_accounts request 186 */ 187 static struct TALER_BANK_GetAccountsHandle *get_accounts_handle; 188 189 /** 190 * Handle for get_accounts request 191 */ 192 static struct TALER_BANK_GetWithdrawalHandle *get_withdrawal_handle; 193 194 /** 195 * Handle for post_accounts_withdrawal request 196 */ 197 static struct TALER_BANK_PostCreateWithdrawalHandle *post_accounts_withdrawal_handle; 198 199 /** 200 * used to init gpio 201 */ 202 static struct gpiod_line_settings *gpio_settings; 203 204 /** 205 * gpio request to enable coin acceping 206 */ 207 static struct gpiod_line_request *rl_ca_enable; 208 209 210 211 enum 212 DIGITIZER_states 213 { 214 /** 215 * Init by loading configs 216 */ 217 DIGITIZER_STATE_INIT, 218 219 /** 220 * 221 */ 222 DIGITIZER_STATE_IDLE, 223 224 /** 225 * 226 */ 227 DIGITIZER_STATE_CREATE_QR, 228 229 /** 230 * 231 */ 232 DIGITIZER_STATE_SCAN_QR, 233 234 /** 235 * 236 */ 237 DIGITIZER_STATE_IDENTIFICATION, 238 /** 239 * 240 */ 241 DIGITIZER_STATE_COUNT_MONEY, 242 /** 243 * 244 */ 245 DIGITIZER_STATE_ACCEPT_CASH, 246 /** 247 * 248 */ 249 DIGITIZER_STATE_CASHING_UP, 250 /** 251 * 252 */ 253 DIGITIZER_STATE_WITHDRAWAL_STATUS, 254 /** 255 * 256 */ 257 DIGITIZER_STATE_CANCEL_WITHDRAWAL, 258 /** 259 * 260 */ 261 DIGITIZER_STATE_WITHDRAWAL_ERROR, 262 /** 263 * 264 */ 265 DIGITIZER_STATE_TERMINAL_ERROR, 266 267 /** 268 * count used to initialise state table with size 269 */ 270 DIGITIZER_STATE_NUMBER_OF_STATES 271 }; 272 273 /** 274 * Data moved between states 275 */ 276 struct DIGITIZER_StateContext 277 { 278 enum TALER_BANK_VersionCompatibility version_compa; 279 280 enum TALLER_BANK_CurrencyCompatibility currency_compa; 281 282 /** 283 * digitizer defined max amount for one insertion action, 284 * sum from bill- and coin-acceptor 285 */ 286 struct TALER_Amount max_insertion_amount; 287 288 /** 289 * digitizer defined min amount for one insertion action, 290 * min from bill-/coin-acceptor 291 */ 292 struct TALER_Amount min_insertion_amount; 293 294 /** 295 * max_wire_transfer_amount of bank config 296 */ 297 struct TALER_Amount bank_max_wire_transfer_amount; 298 299 /** 300 * amount avaliable for current withdrawal process 301 * is updated according to bank balance and bank withdrawal-limits 302 * in init state of each withdrawal process 303 */ 304 struct TALER_Amount withdrawal_limit; 305 306 /** 307 * withdrawal operation information 308 * url and id 309 */ 310 struct TALER_BANK_CreateWithdrawalInformatio wi; 311 312 /** 313 * amount of current prozessing withdrawal 314 */ 315 struct TALER_Amount digitizer_user_balance; 316 }; 317 318 static enum DIGITIZER_states state; 319 320 struct DIGITIZER_StateContext *state_ctx; 321 322 struct DIGITIZER_DisplayContext *display_ctx; 323 324 static void state_controller_task(void *cls); 325 326 327 void 328 start_qr_show(int showtime) 329 { 330 //char buffer[FRAMEBUFFER_SIZE]; 331 char *command; 332 char *url; 333 char *path; 334 335 const char *prefix = getenv("TALER_DIGITIZER_PREFIX"); 336 if (NULL != prefix) 337 GNUNET_asprintf (&path, "%s%s", prefix,PATH_QR_SHOW); 338 else 339 path = GNUNET_strdup ("/usr/local/bin/taler-digitizer-qr-show"); 340 url = TALER_url_join(state_ctx->wi.taler_withdraw_uri, 341 "", 342 "external-confirmation", 343 "1", 344 NULL); 345 GNUNET_asprintf(&command, 346 "%s -c taler-digitizer.conf -d \"%d s\" %s\n", 347 path, 348 showtime, 349 url); 350 GNUNET_free(url); 351 TALER_LOG_DEBUG("Command for QRshow: %s\n",command); 352 system(command); 353 354 GNUNET_free(command); 355 GNUNET_free(path); 356 } 357 358 void 359 clear_screen(void) 360 { 361 char *command; 362 system(command); 363 GNUNET_asprintf(&command, 364 "dd if=/dev/zero of=%s bs=%d count=%d\n", 365 cfg_ca_device, 366 DISPLAY_BLOCK_SIZE, 367 DISPLAY_COUNT); 368 GNUNET_free(command); 369 } 370 371 /** 372 * Joinn currency and V.F format value string to TALER amount". 373 * 374 * @param currency currency string 375 * @param value_fraction value string format "Value.Fraction" 376 * @param[out] amount amount to write the result to 377 * @return #GNUNET_OK if the string is a valid monetary amount specification, 378 * #GNUNET_SYSERR if it is invalid. 379 */ 380 enum GNUNET_GenericReturnValue 381 join_strings_to_amount(const char *currency, 382 const char *value_fraction, 383 struct TALER_Amount *amount) 384 { 385 enum GNUNET_GenericReturnValue ret; 386 char *str; 387 GNUNET_asprintf (&str, 388 "%s:%s", 389 currency, 390 value_fraction); 391 ret = TALER_string_to_amount(str, 392 amount); 393 GNUNET_free(str); 394 return ret; 395 } 396 397 398 static void 399 on_get_config_done (void *cls, 400 const struct TALER_BANK_ConfigResponse *vr) 401 { 402 TALER_LOG_DEBUG ("Callback of GET /config\n"); 403 (void) cls; 404 get_config_handle = NULL; 405 406 state_ctx->version_compa = vr->details.ok.version_compa; 407 state_ctx->currency_compa = strcmp(vr->details.ok.configi.currency, 408 cfg_currency); 409 if(TALER_BANK_CC_MATCH != state_ctx->currency_compa) 410 { 411 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 412 "taler-digitizer", 413 "CURRENCY", 414 "not compatible with bank currency"); 415 state = DIGITIZER_STATE_TERMINAL_ERROR; 416 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 417 return; 418 } 419 if(-1 == TALER_amount_cmp(&(vr->details.ok.configi.max_wire_transfer_amount), 420 &(state_ctx->max_insertion_amount))) 421 { 422 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 423 "taler-digitizer", 424 "BANK_BASE_URL or MAX_DENOMINATION", 425 "max denominations not compatible with bank max_wire_transfer_amount"); 426 state = DIGITIZER_STATE_TERMINAL_ERROR; 427 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 428 return; 429 } 430 state_ctx->bank_max_wire_transfer_amount = vr->details.ok.configi.max_wire_transfer_amount; 431 if(1 == TALER_amount_cmp(&(vr->details.ok.configi.min_wire_transfer_amount), 432 &(state_ctx->min_insertion_amount))) 433 { 434 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 435 "taler-digitizer", 436 "BANK_BASE_URL or MAX_DENOMINATION", 437 "min denominations not compatible with bank min_wire_transfer_amount"); 438 state = DIGITIZER_STATE_TERMINAL_ERROR; 439 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 440 return; 441 } 442 443 444 state = DIGITIZER_STATE_IDLE; 445 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 446 return; 447 } 448 449 /** 450 * callback of accounts request 451 * used in the Idle state 452 */ 453 static void 454 on_get_accounts_done(void *cls, 455 const struct TALER_BANK_AccountsResponse *vr) 456 { 457 TALER_LOG_DEBUG ("Callback of accounts/$USERNAME\n"); 458 459 (void) cls; 460 get_accounts_handle = NULL; 461 struct TALER_Amount account_max_withdrawal; 462 463 if(0 != strcmp("active", 464 vr->details.ok.acc.status)) 465 { 466 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 467 "taler-digitizer", 468 "BANK_ACCOUNT", 469 "bank account is not in active state"); 470 state = DIGITIZER_STATE_TERMINAL_ERROR; 471 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 472 return; 473 } 474 TALER_amount_min (&account_max_withdrawal, 475 &vr->details.ok.acc.balance.amount, 476 &vr->details.ok.acc.debit_threshold); 477 if(-1 == TALER_amount_cmp (&account_max_withdrawal, 478 &state_ctx->max_insertion_amount)) 479 { 480 TALER_LOG_ERROR("bank account does not have enough balance or whithdrawal limit to withdrawal the biggest denomination"); 481 state = DIGITIZER_STATE_TERMINAL_ERROR; 482 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 483 return; 484 } 485 TALER_amount_min(&state_ctx->withdrawal_limit,&account_max_withdrawal,&state_ctx->bank_max_wire_transfer_amount); 486 TALER_amount_min(&state_ctx->withdrawal_limit,&state_ctx->withdrawal_limit,&cfg_user_withdrawallimit); 487 488 489 490 state = DIGITIZER_STATE_CREATE_QR; 491 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 492 return; 493 } 494 495 /** 496 * callback of accounts withdrawal request 497 * used in the QR Create state 498 */ 499 static void 500 on_post_accounts_withdrawal_done(void *cls, 501 const struct TALER_BANK_CreateWithdrawalResponse *vr) 502 { 503 TALER_LOG_DEBUG ("Callback of accounts/$USERNAME/withdrawal\n"); 504 505 (void) cls; 506 post_accounts_withdrawal_handle = NULL; 507 508 state_ctx->wi.taler_withdraw_uri = GNUNET_strdup(vr->details.ok.wopd.taler_withdraw_uri); 509 state_ctx->wi.withdrawal_id = GNUNET_strdup(vr->details.ok.wopd.withdrawal_id); 510 511 state = DIGITIZER_STATE_SCAN_QR; 512 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 513 return; 514 } 515 516 /** 517 * Get withdrawal request for repeated requesting 518 */ 519 static void 520 get_withdrawal_task(void *cls); 521 522 /** 523 * callback of accounts withdrawal request 524 * used in the QR Create state 525 */ 526 static void 527 on_get_withdrawal_status_done(void *cls, 528 const struct TALER_BANK_WithdrawalResponse *vr) 529 { 530 TALER_LOG_DEBUG ("Callback of withdrawal/$WITHDRAWAL_ID\n"); 531 struct GNUNET_SCHEDULER_Task *timeout_handle; 532 timeout_handle = cls; 533 if(0 == strcasecmp("selected", vr->details.ok.acc.status)) 534 { 535 GNUNET_SCHEDULER_cancel(timeout_handle); 536 clear_screen(); 537 state = DIGITIZER_STATE_COUNT_MONEY; 538 // give context for non terminal errors! 539 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 540 } 541 else 542 { 543 struct GNUNET_TIME_Relative poll; 544 poll = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS,200); 545 GNUNET_SCHEDULER_add_delayed(poll,get_withdrawal_task,timeout_handle); 546 } 547 548 return; 549 } 550 551 /** 552 * Get withdrawal request for repeated requesting 553 */ 554 static void 555 get_withdrawal_task(void *cls) 556 { 557 struct GNUNET_SCHEDULER_Task *timeout_handle; 558 timeout_handle = cls; 559 560 get_withdrawal_handle = 561 TALER_BANK_get_withdrawal(curl_ctx, 562 cfg_bank_base_url, 563 state_ctx->wi.withdrawal_id, 564 on_get_withdrawal_status_done, 565 timeout_handle, 566 NULL); 567 return; 568 } 569 570 /** 571 * Timeout when QR is shown 572 * SCAN_QR_TIMEOUT_SECONDS without been scanned 573 * used in the ScanQR state 574 */ 575 static void 576 timeout_task(void *cls) 577 { 578 TALER_LOG_DEBUG ("Timeout of ScanQR\n"); 579 580 (void) cls; 581 582 state = DIGITIZER_STATE_CANCEL_WITHDRAWAL; 583 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 584 return; 585 } 586 587 /** 588 * @brief Cleanup when no task is scheduled anymore 589 * 590 * @param cls closure 591 */ 592 static void 593 shutdown_task (void *cls) 594 { 595 (void)cls; 596 gpiod_line_request_release(rl_ca_enable); 597 598 if (NULL != get_withdrawal_handle) 599 { 600 TALER_BANK_get_withdrawal_cancel (get_withdrawal_handle); 601 get_withdrawal_handle = NULL; 602 } 603 if (NULL != get_config_handle) 604 { 605 TALER_BANK_get_config_cancel (get_config_handle); 606 get_config_handle = NULL; 607 } 608 if (NULL != post_accounts_withdrawal_handle) 609 { 610 TALER_BANK_post_withdrawal_create_cancel(post_accounts_withdrawal_handle); 611 post_accounts_withdrawal_handle = NULL; 612 } 613 if (NULL != reschedule_ctx) 614 { 615 GNUNET_CURL_gnunet_rc_destroy (reschedule_ctx); 616 reschedule_ctx = NULL; 617 } 618 if (NULL != curl_ctx) 619 { 620 GNUNET_CURL_fini (curl_ctx); 621 curl_ctx = NULL; 622 } 623 DIGITIZER_bank_auth_free (cfg_bank_authentication); 624 GNUNET_free(cfg_bank_authentication); 625 GNUNET_free(state_ctx); 626 GNUNET_free(display_ctx); 627 } 628 629 /** 630 * Initialising state 631 * Init: Digitizer config, Bank config, Screen task, Touch task 632 */ 633 static void Init_state_task(void *cls) 634 { 635 TALER_LOG_DEBUG ("INIT state\n"); 636 637 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 638 639 if (GNUNET_OK != 640 GNUNET_CONFIGURATION_get_value_string (cfg, 641 "taler-digitizer", 642 "BANK_BASE_URL", 643 &cfg_bank_base_url)) 644 { 645 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 646 "taler-digitizer", 647 "BANK_BASE_URL"); 648 global_ret = EXIT_FAILURE; 649 return; 650 } 651 if (GNUNET_OK != 652 GNUNET_CONFIGURATION_get_value_string (cfg, 653 "taler-digitizer", 654 "BANK_ACCOUNT", 655 &cfg_bank_account_username)) 656 { 657 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 658 "taler-digitizer", 659 "BANK_ACCOUNT"); 660 global_ret = EXIT_FAILURE; 661 return; 662 } 663 if (GNUNET_OK != 664 DIGITIZER_bank_auth_parse_cfg (cfg, 665 "bank-authentication", 666 cfg_bank_authentication)) 667 { 668 global_ret = EXIT_FAILURE; 669 return; 670 } 671 if (GNUNET_OK != 672 GNUNET_CONFIGURATION_get_value_string (cfg, 673 "taler-digitizer", 674 "EXCHANGE_BASE_URL", 675 &cfg_exchange_base_url)) 676 { 677 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 678 "taler-digitizer", 679 "EXCHANGE_BASE_URL"); 680 global_ret = EXIT_FAILURE; 681 return; 682 } 683 if (GNUNET_OK != 684 GNUNET_CONFIGURATION_get_value_string (cfg, 685 "taler-digitizer", 686 "CURRENCY", 687 &cfg_currency)) 688 { 689 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 690 "taler-digitizer", 691 "CURRENCY"); 692 global_ret = EXIT_FAILURE; 693 return; 694 } 695 if(GNUNET_OK != TALER_check_currency(cfg_currency)) 696 { 697 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 698 "taler-digitizer", 699 "CURRENCY", 700 "malformed currency"); 701 global_ret = EXIT_FAILURE; 702 return; 703 } 704 char *limit_value; 705 if (GNUNET_OK != 706 GNUNET_CONFIGURATION_get_value_string (cfg, 707 "taler-digitizer", 708 "USER_WITHDRAWALLIMIT", 709 &limit_value)) 710 { 711 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 712 "taler-digitizer", 713 "USER_WITHDRAWALLIMIT"); 714 GNUNET_free(limit_value); 715 global_ret = EXIT_FAILURE; 716 return; 717 } 718 if (GNUNET_OK != 719 join_strings_to_amount(cfg_currency, 720 limit_value, 721 &cfg_user_withdrawallimit)) 722 { 723 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 724 "taler-digitizer", 725 "USER_WITHDRAWALLIMIT", 726 "malformed denomination"); 727 GNUNET_free(limit_value); 728 global_ret = EXIT_FAILURE; 729 return; 730 } 731 GNUNET_free(limit_value); 732 733 unsigned long long user_withdrawal_period_number; 734 if (GNUNET_OK != 735 GNUNET_CONFIGURATION_get_value_number (cfg, 736 "taler-digitizer", 737 "USER_WITHDRAWAL_PERIOD", 738 &user_withdrawal_period_number)) 739 { 740 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 741 "taler-digitizer", 742 "USER_WITHDRAWAL_PERIOD"); 743 global_ret = EXIT_FAILURE; 744 return; 745 } 746 cfg_user_withdrawal_period = GNUNET_TIME_relative_multiply ( 747 DIGITIZER_PERSON_WITHDRAWAL_PERIOD_TIME_UNIT, 748 user_withdrawal_period_number); 749 750 cfg_kyc_functionality = GNUNET_CONFIGURATION_get_value_yesno (cfg, 751 "taler-digitizer", 752 "KYC_FUNCTIONALITY"); 753 if (GNUNET_SYSERR == cfg_kyc_functionality) 754 { 755 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 756 "taler-digitizer", 757 "KYC_FUNCTIONALITY"); 758 global_ret = EXIT_FAILURE; 759 return; 760 } 761 if (GNUNET_OK != 762 GNUNET_CONFIGURATION_get_value_filename (cfg, 763 "taler-digitizer", 764 "FRAMEBUFFER_DEVICE", 765 &cfg_framebuffer_device)) 766 { 767 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 768 "taler-digitizer", 769 "FRAMEBUFFER_DEVICE"); 770 global_ret = EXIT_FAILURE; 771 return; 772 } 773 if (GNUNET_OK != 774 GNUNET_CONFIGURATION_get_value_filename (cfg, 775 "taler-digitizer", 776 "FRAMEBUFFER_BACKLIGHT", 777 &cfg_framebuffer_backlight)) 778 { 779 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 780 "taler-digitizer", 781 "FRAMEBUFFER_BACKLIGHT"); 782 global_ret = EXIT_FAILURE; 783 return; 784 } 785 cfg_ca_enable = GNUNET_CONFIGURATION_get_value_yesno (cfg, 786 "coin-acceptor", 787 "ENABLE"); 788 cfg_ba_enable = GNUNET_CONFIGURATION_get_value_yesno (cfg, 789 "bill-acceptor", 790 "ENABLE"); 791 if(GNUNET_OK != cfg_ba_enable) 792 { 793 cfg_ba_enable = GNUNET_NO; 794 join_strings_to_amount(cfg_currency, 795 "0", 796 &cfg_ba_max_denomination); 797 join_strings_to_amount(cfg_currency, 798 "0", 799 &cfg_ba_min_denomination); 800 if(GNUNET_OK != cfg_ca_enable) 801 { 802 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 803 "coin-acceptor or bill-acceptor", 804 "ENABLE"); 805 global_ret = EXIT_FAILURE; 806 return; 807 } 808 } 809 else 810 { 811 if (GNUNET_OK != 812 GNUNET_CONFIGURATION_get_value_filename (cfg, 813 "bill-acceptor", 814 "DEVICE", 815 &cfg_ba_device)) 816 { 817 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 818 "bill-acceptor", 819 "DEVICE"); 820 global_ret = EXIT_FAILURE; 821 return; 822 } 823 char *denomination_value; 824 if (GNUNET_OK != 825 GNUNET_CONFIGURATION_get_value_string (cfg, 826 "bill-acceptor", 827 "MAX_DENOMINATION", 828 &denomination_value)) 829 { 830 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 831 "bill-acceptor", 832 "MAX_DENOMINATION"); 833 GNUNET_free(denomination_value); 834 global_ret = EXIT_FAILURE; 835 return; 836 } 837 if (GNUNET_OK != 838 join_strings_to_amount(cfg_currency, 839 denomination_value, 840 &cfg_ba_max_denomination)) 841 { 842 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 843 "bill-acceptor", 844 "MAX_DENOMINATION", 845 "malformed denomination"); 846 GNUNET_free(denomination_value); 847 global_ret = EXIT_FAILURE; 848 return; 849 } 850 GNUNET_free(denomination_value); 851 if (GNUNET_OK != 852 GNUNET_CONFIGURATION_get_value_string (cfg, 853 "bill-acceptor", 854 "MIN_DENOMINATION", 855 &denomination_value)) 856 { 857 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 858 "bill-acceptor", 859 "MIN_DENOMINATION"); 860 GNUNET_free(denomination_value); 861 global_ret = EXIT_FAILURE; 862 return; 863 } 864 if (GNUNET_OK != 865 join_strings_to_amount(cfg_currency, 866 denomination_value, 867 &cfg_ba_min_denomination)) 868 { 869 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 870 "bill-acceptor", 871 "MIN_DENOMINATION", 872 "malformed denomination"); 873 GNUNET_free(denomination_value); 874 global_ret = EXIT_FAILURE; 875 return; 876 } 877 GNUNET_free(denomination_value); 878 } 879 if(GNUNET_OK != cfg_ca_enable) 880 { 881 cfg_ca_enable = GNUNET_NO; 882 join_strings_to_amount(cfg_currency, 883 "0", 884 &cfg_ca_max_denomination); 885 join_strings_to_amount(cfg_currency, 886 "0", 887 &cfg_ca_min_denomination); 888 } 889 else 890 { 891 char *denomination_value; 892 if (GNUNET_OK != 893 GNUNET_CONFIGURATION_get_value_string (cfg, 894 "coin-acceptor", 895 "MAX_DENOMINATION", 896 &denomination_value)) 897 { 898 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 899 "coin-acceptor", 900 "MAX_DENOMINATION"); 901 GNUNET_free(denomination_value); 902 global_ret = EXIT_FAILURE; 903 return; 904 } 905 if (GNUNET_OK != 906 join_strings_to_amount(cfg_currency, 907 denomination_value, 908 &cfg_ca_max_denomination)) 909 { 910 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 911 "coin-acceptor", 912 "MAX_DENOMINATION", 913 "malformed denomination"); 914 GNUNET_free(denomination_value); 915 global_ret = EXIT_FAILURE; 916 return; 917 } 918 if (GNUNET_OK != 919 GNUNET_CONFIGURATION_get_value_string (cfg, 920 "coin-acceptor", 921 "MIN_DENOMINATION", 922 &denomination_value)) 923 { 924 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 925 "coin-acceptor", 926 "MIN_DENOMINATION"); 927 GNUNET_free(denomination_value); 928 global_ret = EXIT_FAILURE; 929 return; 930 } 931 if (GNUNET_OK != 932 join_strings_to_amount(cfg_currency, 933 denomination_value, 934 &cfg_ca_min_denomination)) 935 { 936 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 937 "coin-acceptor", 938 "MIN_DENOMINATION", 939 "malformed denomination"); 940 GNUNET_free(denomination_value); 941 global_ret = EXIT_FAILURE; 942 return; 943 } 944 GNUNET_free(denomination_value); 945 if (GNUNET_OK != 946 GNUNET_CONFIGURATION_get_value_filename (cfg, 947 "coin-acceptor", 948 "DEVICE", 949 &cfg_ca_device)) 950 { 951 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 952 "coin-acceptor", 953 "DEVICE"); 954 global_ret = EXIT_FAILURE; 955 return; 956 } 957 if (GNUNET_OK != 958 GNUNET_CONFIGURATION_get_value_number (cfg, 959 "coin-acceptor", 960 "ENABLE_PIN", 961 &cfg_ca_en_pin)) 962 { 963 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 964 "coin-acceptor", 965 "ENABLE_PIN"); 966 global_ret = EXIT_FAILURE; 967 return; 968 } 969 if (GNUNET_OK != 970 GNUNET_CONFIGURATION_get_value_number (cfg, 971 "coin-acceptor", 972 "UART_RX_PIN", 973 &cfg_ca_rx_pin)) 974 { 975 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 976 "coin-acceptor", 977 "UART_RX_PIN"); 978 global_ret = EXIT_FAILURE; 979 return; 980 } 981 //CA Init 982 gpio_settings = gpiod_make_settings(GPIOD_LINE_DIRECTION_OUTPUT, 983 GPIOD_LINE_BIAS_PULL_UP, 984 GPIOD_LINE_DRIVE_OPEN_DRAIN,1); 985 if (NULL == gpio_settings) 986 { 987 TALER_LOG_ERROR("GPIO Init failed"); 988 gpiod_line_settings_free(gpio_settings); 989 global_ret = EXIT_FAILURE; 990 return; 991 } 992 rl_ca_enable = gpiod_make_line_request(cfg_ca_device, 993 cfg_ca_en_pin, 994 gpio_settings); 995 if (NULL == rl_ca_enable) 996 { 997 TALER_LOG_ERROR("GPIO Init failed"); 998 gpiod_line_request_release(rl_ca_enable); 999 global_ret = EXIT_FAILURE; 1000 return; 1001 } 1002 if(0 != gpiod_line_request_set_value(rl_ca_enable, 1003 (unsigned int)cfg_ca_en_pin, 1004 GPIOD_LINE_VALUE_INACTIVE)) 1005 { 1006 TALER_LOG_ERROR("failed to set GPIO active"); 1007 global_ret = EXIT_FAILURE; 1008 return; 1009 } 1010 } 1011 1012 state_ctx->min_insertion_amount = 1013 ((1 == TALER_amount_cmp(&cfg_ba_min_denomination, 1014 &cfg_ca_min_denomination))? 1015 cfg_ca_min_denomination : cfg_ba_min_denomination); 1016 1017 if(TALER_AAR_INVALID_NEGATIVE_RESULT == TALER_amount_add(&state_ctx->max_insertion_amount, 1018 &cfg_ba_min_denomination, 1019 &cfg_ca_min_denomination)) 1020 { 1021 GNUNET_log_config_invalid(GNUNET_ERROR_TYPE_ERROR, 1022 "coin-acceptor", 1023 "XXX_DENOMINATION", 1024 "could not determine min insertion amount"); 1025 global_ret = EXIT_FAILURE; 1026 return; 1027 } 1028 1029 //Screen INIT 1030 1031 //Touch INIT 1032 1033 //BA Init 1034 1035 1036 curl_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1037 &reschedule_ctx); 1038 reschedule_ctx = GNUNET_CURL_gnunet_rc_create (curl_ctx); 1039 1040 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 1041 NULL); 1042 1043 get_config_handle = TALER_BANK_get_config (curl_ctx, 1044 cfg_bank_base_url, 1045 &on_get_config_done, 1046 NULL); 1047 } 1048 1049 /** 1050 * Terminal Error state 1051 * When error accures due to config issues, or unrecoverable errors 1052 */ 1053 static void TerminalError_state_task(void *cls) 1054 { 1055 (void)cls; 1056 TALER_LOG_DEBUG ("start TerminalError state task\n"); 1057 global_ret = EXIT_FAILURE; 1058 GNUNET_SCHEDULER_shutdown(); 1059 } 1060 1061 /** 1062 * Idle state 1063 * prepare for new withdrawal process and 1064 * check for start signal from UI 1065 */ 1066 static void Idle_state_task(void *cls) 1067 { 1068 TALER_LOG_DEBUG("Idle state\n"); 1069 clear_screen(); 1070 (void)cls; 1071 TALER_amount_set_zero(cfg_currency,&state_ctx->digitizer_user_balance); 1072 get_accounts_handle = TALER_BANK_get_accounts (curl_ctx, 1073 cfg_bank_base_url, 1074 cfg_bank_account_username, 1075 cfg_bank_authentication, 1076 &on_get_accounts_done, 1077 NULL); 1078 } 1079 1080 /** 1081 * Create QR state 1082 * create QR code with a new WOPID 1083 * check for cancle from UI 1084 */ 1085 static void CreateQR_state_task(void *cls) 1086 { 1087 TALER_LOG_DEBUG("CreateQR state\n"); 1088 (void)cls; 1089 struct TALER_BANK_AccountCreateWithdrawalRequest *acwr; 1090 acwr = GNUNET_new(struct TALER_BANK_AccountCreateWithdrawalRequest); 1091 acwr->no_amount_to_wallet = true; 1092 1093 post_accounts_withdrawal_handle = 1094 TALER_BANK_post_accounts_withdrawal_create (curl_ctx, 1095 cfg_bank_base_url, 1096 cfg_bank_account_username, 1097 cfg_bank_authentication); 1098 if(GNUNET_OK != 1099 TALER_BANK_post_accounts_withdrawal(post_accounts_withdrawal_handle, 1100 acwr, 1101 on_post_accounts_withdrawal_done, 1102 NULL)) 1103 { 1104 TALER_LOG_ERROR("TALER_BANK_post_accounts_withdrawal canceled\n"); 1105 state = DIGITIZER_STATE_CANCEL_WITHDRAWAL; 1106 // give context for non terminal errors! 1107 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1108 } 1109 GNUNET_free(acwr); 1110 return; 1111 } 1112 1113 /** 1114 * Show QR state 1115 * show QR code on display 1116 * check for cancle from UI 1117 */ 1118 static void ScanQR_state_task(void *cls) 1119 { 1120 TALER_LOG_DEBUG ("ScanQR state\n"); 1121 (void)cls; 1122 1123 struct GNUNET_SCHEDULER_Task *timeout_handle; 1124 struct GNUNET_TIME_Relative delay; 1125 struct GNUNET_TIME_Relative poll; 1126 1127 delay = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,SCAN_QR_TIMEOUT_SECONDS); 1128 poll = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS,200); 1129 timeout_handle = GNUNET_SCHEDULER_add_delayed(delay,timeout_task,NULL); 1130 start_qr_show(SCAN_QR_TIMEOUT_SECONDS); 1131 1132 //char *buf; 1133 //GNUNET_asprintf(&buf,"%d",SCAN_QR_TIMEOUT_SECONDS*1000); 1134 1135 GNUNET_SCHEDULER_add_delayed(poll,get_withdrawal_task,timeout_handle); 1136 return; 1137 } 1138 1139 static void CountMoney_state_task(void *cls) 1140 { 1141 char* balance_str; 1142 struct TALER_Amount *temp_amount; 1143 temp_amount = GNUNET_new(struct TALER_Amount); 1144 balance_str = TALER_amount_to_string(&state_ctx->digitizer_user_balance); 1145 TALER_LOG_DEBUG ("CountMoney: %s\n",balance_str); 1146 GNUNET_free(balance_str); 1147 (void)cls; 1148 if(0 != gpiod_line_request_set_value(rl_ca_enable, 1149 (unsigned int)cfg_ca_en_pin, 1150 GPIOD_LINE_VALUE_INACTIVE)) 1151 { 1152 GNUNET_free(temp_amount); 1153 if(TALER_amount_is_zero(&state_ctx->digitizer_user_balance)) 1154 { 1155 TALER_LOG_ERROR("failed to set GPIO inactive"); 1156 state = DIGITIZER_STATE_CANCEL_WITHDRAWAL; 1157 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1158 } 1159 else 1160 { 1161 TALER_LOG_ERROR("failed to set GPIO active"); 1162 state = DIGITIZER_STATE_CASHING_UP; 1163 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1164 } 1165 } 1166 1167 TALER_amount_subtract(temp_amount, 1168 &state_ctx->withdrawal_limit, 1169 &state_ctx->digitizer_user_balance); 1170 if(-1 == TALER_amount_cmp(temp_amount, 1171 &state_ctx->max_insertion_amount)) 1172 { 1173 GNUNET_free(temp_amount); 1174 TALER_LOG_INFO("withdrawal limit reached"); 1175 state = DIGITIZER_STATE_CASHING_UP; 1176 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1177 }else 1178 { 1179 GNUNET_free(temp_amount); 1180 if(0 != gpiod_line_request_set_value(rl_ca_enable, 1181 (unsigned int)cfg_ca_en_pin, 1182 GPIOD_LINE_VALUE_ACTIVE)) 1183 { 1184 TALER_LOG_ERROR("failed to set GPIO active"); 1185 state = DIGITIZER_STATE_CASHING_UP; 1186 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1187 } 1188 state = DIGITIZER_STATE_ACCEPT_CASH; 1189 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1190 } 1191 } 1192 1193 static void AcceptCash_state_task(void *cls) 1194 { 1195 (void)cls; 1196 TALER_LOG_DEBUG("AcceptingCash state"); 1197 state = DIGITIZER_STATE_CASHING_UP; 1198 GNUNET_SCHEDULER_add_now(state_controller_task,NULL); 1199 } 1200 1201 /** 1202 * Switch between State tasks 1203 */ 1204 static void state_controller_task(void *cls) 1205 { 1206 struct GNUNET_CONFIGURATION_Handle *cfg = cls; 1207 1208 switch (state) 1209 { 1210 case DIGITIZER_STATE_INIT: 1211 { 1212 GNUNET_SCHEDULER_add_now(Init_state_task,cfg); 1213 return; 1214 } 1215 case DIGITIZER_STATE_IDLE: 1216 { 1217 GNUNET_SCHEDULER_add_now(Idle_state_task,NULL); 1218 return; 1219 } 1220 case DIGITIZER_STATE_TERMINAL_ERROR: 1221 { 1222 GNUNET_SCHEDULER_add_now(TerminalError_state_task,NULL); 1223 return; 1224 } 1225 case DIGITIZER_STATE_CREATE_QR: 1226 { 1227 GNUNET_SCHEDULER_add_now(CreateQR_state_task,NULL); 1228 return; 1229 } 1230 case DIGITIZER_STATE_SCAN_QR: 1231 { 1232 GNUNET_SCHEDULER_add_now(ScanQR_state_task,NULL); 1233 return; 1234 } 1235 case DIGITIZER_STATE_COUNT_MONEY: 1236 { 1237 GNUNET_SCHEDULER_add_now(CountMoney_state_task,NULL); 1238 return; 1239 } 1240 case DIGITIZER_STATE_ACCEPT_CASH: 1241 { 1242 GNUNET_SCHEDULER_add_now(AcceptCash_state_task,NULL); 1243 return; 1244 } 1245 1246 default: 1247 { 1248 TALER_LOG_ERROR("Gost-State in state machine\n"); 1249 global_ret = EXIT_FAILURE; 1250 GNUNET_SCHEDULER_shutdown (); 1251 } 1252 1253 } 1254 } 1255 1256 1257 /** 1258 * @brief Start the application 1259 * 1260 * @param cls closure 1261 * @param args arguments left 1262 * @param cfgfile config file name 1263 * @param cfg handle for the configuration 1264 */ 1265 static void 1266 run (void *cls, 1267 char *const *args, 1268 const char *cfgfile, 1269 const struct GNUNET_CONFIGURATION_Handle *cfg) 1270 { 1271 (void) cls; 1272 (void) args; 1273 (void) cfgfile; 1274 1275 state = DIGITIZER_STATE_INIT; 1276 state_ctx = GNUNET_new(struct DIGITIZER_StateContext); 1277 display_ctx = GNUNET_new(struct DIGITIZER_DisplayContext); 1278 cfg_bank_authentication = GNUNET_new(struct DIGITIZER_BankAuthenticationData); 1279 1280 GNUNET_SCHEDULER_add_now(state_controller_task,(void *) cfg); 1281 1282 return; 1283 } 1284 1285 1286 int 1287 main (int argc, 1288 char *const *argv) 1289 { 1290 int ret; 1291 1292 struct GNUNET_GETOPT_CommandLineOption options[] = { 1293 GNUNET_GETOPT_option_flag ('d', 1294 "enable-diagnostics", 1295 "enable diagnostics for debuging", 1296 &enable_diagnostics), 1297 GNUNET_GETOPT_OPTION_END 1298 }; 1299 1300 ret = GNUNET_PROGRAM_run (TALER_DIGITIZER_project_data (), 1301 argc, 1302 argv, 1303 "taler-digitizer", 1304 "This is an application for the Cash Digitizer." 1305 " It accepts cash and transfers it to the Taler Wallet.\n", 1306 options, 1307 &run, 1308 NULL); 1309 1310 if (GNUNET_NO == ret) 1311 return 0; 1312 if (GNUNET_OK != ret) 1313 return 1; 1314 return global_ret; 1315 }