1 /* 2 * Author: Jon Trulson <jtrulson (at) ics.com> 3 * Copyright (c) 2015 Intel Corporation. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 */ 24 25 #include <stdexcept> 26 27 #include "sm130.h" 28 29 using namespace upm; 30 using namespace std; 31 32 // Uncomment the below to see packaet data sent and received from the SM130 33 // #define SM130_DEBUG 34 35 static const int defaultDelay = 1000; // ms for read 36 37 static const int maxLen = 64; // max number of bytes to read 38 39 SM130::SM130(int uart, int reset) : 40 m_uart(uart), m_gpioReset(reset) 41 { 42 m_tagType = TAG_NONE; 43 m_uidLen = 0; 44 m_uid.clear(); 45 clearError(); 46 initClock(); 47 48 m_gpioReset.dir(mraa::DIR_OUT); 49 m_gpioReset.write(0); 50 } 51 52 SM130::~SM130() 53 { 54 } 55 56 mraa::Result SM130::setBaudRate(int baud) 57 { 58 m_baud = baud; 59 return m_uart.setBaudRate(baud); 60 } 61 62 string SM130::sendCommand(CMD_T cmd, string data) 63 { 64 uint8_t cksum = 0; 65 string command; 66 67 // for uart, we need to add the sync bytes, 0xff, 0x00 68 command.push_back(0xff); 69 command.push_back(0x00); 70 71 // compute the length - command + data 72 uint8_t len = 1; // command 73 if (!data.empty()) 74 len += data.size(); 75 76 command.push_back(len); 77 78 cksum += len; 79 80 // now the command 81 command.push_back(cmd); 82 cksum += cmd; 83 84 // now the data if any 85 if (!data.empty()) 86 { 87 for (int i=0; i<data.size(); i++) 88 { 89 command.push_back(data[i]); 90 cksum += (uint8_t)data[i]; 91 } 92 } 93 94 // now add the checksum 95 command.push_back(cksum); 96 97 #ifdef SM130_DEBUG 98 cerr << "CMD: " << string2HexString(command) << endl; 99 #endif // SM130_DEBUG 100 101 // send it 102 m_uart.writeStr(command); 103 104 // if the command is SET_BAUD, then switch to the new baudrate here 105 // before attempting to read the response (and hope it worked). 106 if (cmd == CMD_SET_BAUD) 107 { 108 usleep(100000); // 100ms 109 setBaudRate(m_baud); 110 } 111 112 // now wait for a response 113 if (!m_uart.dataAvailable(defaultDelay)) 114 { 115 cerr << __FUNCTION__ << ": timeout waiting for response" << endl; 116 return ""; 117 } 118 119 string resp = m_uart.readStr(maxLen); 120 121 #ifdef SM130_DEBUG 122 cerr << "RSP: " << string2HexString(resp) << endl; 123 #endif // SM130_DEBUG 124 125 if (!((uint8_t)resp[0] == 0xff && (uint8_t)resp[1] == 0x00)) 126 { 127 cerr << __FUNCTION__ << ": invalid packet header" << endl; 128 return ""; 129 } 130 131 // check size - 2 header bytes + len + cksum. 132 if (resp.size() != ((uint8_t)resp[2] + 2 + 1 + 1)) 133 { 134 cerr << __FUNCTION__ << ": invalid packet length, expected " 135 << int((uint8_t)resp[2] + 2 + 1 + 1) 136 << ", got " << resp.size() << endl; 137 return ""; 138 } 139 140 // verify the cksum 141 cksum = 0; 142 for (int i=2; i<(resp.size() - 1); i++) 143 cksum += (uint8_t)resp[i]; 144 145 if (cksum != (uint8_t)resp[resp.size() - 1]) 146 { 147 cerr << __FUNCTION__ << ": invalid checksum, expected " 148 << int(cksum) << ", got " << (uint8_t)resp[resp.size()-1] << endl; 149 return ""; 150 } 151 152 // we could also verify that the command code returned was for the 153 // command submitted... 154 155 // now, remove the 2 header bytes and the checksum, leave the length 156 // and command. 157 resp.erase(resp.size() - 1, 1); // cksum 158 resp.erase(0, 2); // header bytes 159 160 // return the rest 161 return resp; 162 } 163 164 string SM130::getFirmwareVersion() 165 { 166 clearError(); 167 168 string resp = sendCommand(CMD_VERSION, ""); 169 170 if (resp.empty()) 171 { 172 cerr << __FUNCTION__ << ": failed" << endl; 173 return ""; 174 } 175 176 // delete the len and cmd, return the rest 177 resp.erase(0, 2); 178 return resp; 179 } 180 181 bool SM130::reset() 182 { 183 clearError(); 184 185 string resp = sendCommand(CMD_RESET, ""); 186 if (resp.empty()) 187 { 188 cerr << __FUNCTION__ << ": failed" << endl; 189 return false; 190 } 191 192 return true; 193 } 194 195 void SM130::hardwareReset() 196 { 197 m_gpioReset.write(1); 198 usleep(100000); 199 m_gpioReset.write(0); 200 } 201 202 bool SM130::select() 203 { 204 clearError(); 205 206 m_tagType = TAG_NONE; 207 m_uidLen = 0; 208 m_uid.clear(); 209 210 string resp = sendCommand(CMD_SELECT_TAG, ""); 211 212 if (resp.empty()) 213 { 214 cerr << __FUNCTION__ << ": failed" << endl; 215 return false; 216 } 217 218 if ((uint8_t)resp[0] == 2) 219 { 220 // then we got an error of some sort, store the error code, str 221 // and bail. 222 m_lastErrorCode = resp[2]; 223 224 switch (m_lastErrorCode) 225 { 226 case 'N': m_lastErrorString = "No tag present"; 227 break; 228 case 'U': m_lastErrorString = "Access failed, RF field is off"; 229 break; 230 default: m_lastErrorString = "Unknown error code"; 231 break; 232 } 233 return false; 234 } 235 236 // if we are here, then store the uid info and tag type. 237 m_tagType = (TAG_TYPE_T)resp[2]; 238 239 if ((uint8_t)resp[0] == 6) 240 m_uidLen = 4; // 4 byte uid 241 else 242 m_uidLen = 7; // 7 byte 243 244 for (int i=0; i<m_uidLen; i++) 245 m_uid.push_back(resp[i+3]); 246 247 return true; 248 } 249 250 bool SM130::authenticate(uint8_t block, KEY_TYPES_T keyType, string key) 251 { 252 clearError(); 253 254 // A little sanity checking... 255 if (keyType == KEY_TYPE_A || keyType == KEY_TYPE_B) 256 { 257 if (key.empty()) 258 throw std::invalid_argument(string(__FUNCTION__) + 259 ": You must specify a key for type A or B"); 260 if (key.size() != 6) 261 throw std::invalid_argument(string(__FUNCTION__) + 262 ": Key size must be 6"); 263 264 return false; // probably not reached :) 265 } 266 else 267 { 268 // make sure the key is empty for any other key type 269 key.clear(); 270 } 271 272 string data; 273 data.push_back(block); 274 data.push_back(keyType); 275 data += key; 276 277 string resp = sendCommand(CMD_AUTHENTICATE, data); 278 279 if (resp.empty()) 280 { 281 cerr << __FUNCTION__ << ": failed" << endl; 282 return false; 283 } 284 285 // response len is always 2, 'L' means auth was successful 286 if (resp[2] != 'L') 287 { 288 // then we got an error of some sort, store the error code, str 289 // and bail. 290 m_lastErrorCode = resp[2]; 291 292 switch (m_lastErrorCode) 293 { 294 case 'N': m_lastErrorString = "No tag present, or login failed"; 295 break; 296 case 'U': m_lastErrorString = "Login failed"; 297 break; 298 case 'E': m_lastErrorString = "Invalid key format in EEPROM"; 299 break; 300 default: m_lastErrorString = "Unknown error code"; 301 break; 302 } 303 return false; 304 } 305 306 return true; 307 } 308 309 string SM130::readBlock16(uint8_t block) 310 { 311 clearError(); 312 313 string data; 314 315 data.push_back(block); 316 317 string resp = sendCommand(CMD_READ16, data); 318 319 if (resp.empty()) 320 { 321 cerr << __FUNCTION__ << ": failed" << endl; 322 return ""; 323 } 324 325 if ((uint8_t)resp[0] == 2) 326 { 327 // then we got an error of some sort, store the error code, str 328 // and bail. 329 m_lastErrorCode = resp[2]; 330 331 switch (m_lastErrorCode) 332 { 333 case 'N': m_lastErrorString = "No tag present"; 334 break; 335 case 'F': m_lastErrorString = "Read failed"; 336 break; 337 default: m_lastErrorString = "Unknown error code"; 338 break; 339 } 340 341 return ""; 342 } 343 344 // trim off the len, cmd, and block # bytes and return the rest 345 resp.erase(0, 3); 346 return resp; 347 } 348 349 int32_t SM130::readValueBlock(uint8_t block) 350 { 351 clearError(); 352 353 string data; 354 355 data.push_back(block); 356 357 string resp = sendCommand(CMD_READ_VALUE, data); 358 359 if (resp.empty()) 360 { 361 cerr << __FUNCTION__ << ": failed" << endl; 362 return 0; 363 } 364 365 if ((uint8_t)resp[0] == 2) 366 { 367 // then we got an error of some sort, store the error code, str 368 // and bail. 369 m_lastErrorCode = resp[2]; 370 371 switch (m_lastErrorCode) 372 { 373 case 'N': m_lastErrorString = "No tag present"; 374 break; 375 case 'F': m_lastErrorString = "Read failed"; 376 break; 377 case 'I': m_lastErrorString = "Invalid Value Block"; 378 break; 379 default: m_lastErrorString = "Unknown error code"; 380 break; 381 } 382 383 return 0; 384 } 385 386 int32_t rv; 387 rv = ((uint8_t)resp[3] | 388 ((uint8_t)resp[4] << 8) | 389 ((uint8_t)resp[5] << 16) | 390 ((uint8_t)resp[6] << 24)); 391 392 return rv; 393 } 394 395 bool SM130::writeBlock16(uint8_t block, string contents) 396 { 397 clearError(); 398 399 // A little sanity checking... 400 if (contents.size() != 16) 401 { 402 throw std::invalid_argument(string(__FUNCTION__) + 403 ": You must supply 16 bytes for block content"); 404 405 return false; 406 } 407 408 string data; 409 data.push_back(block); 410 data += contents; 411 412 string resp = sendCommand(CMD_WRITE16, data); 413 414 if (resp.empty()) 415 { 416 cerr << __FUNCTION__ << ": failed" << endl; 417 return false; 418 } 419 420 if ((uint8_t)resp[0] == 2) 421 { 422 // then we got an error of some sort, store the error code, str 423 // and bail. 424 m_lastErrorCode = resp[2]; 425 426 switch (m_lastErrorCode) 427 { 428 case 'F': m_lastErrorString = "Write failed"; 429 break; 430 case 'N': m_lastErrorString = "No tag present"; 431 break; 432 case 'U': m_lastErrorString = "Read after write failed"; 433 break; 434 case 'X': m_lastErrorString = "Unable to read after write"; 435 break; 436 default: m_lastErrorString = "Unknown error code"; 437 break; 438 } 439 return false; 440 } 441 442 return true; 443 } 444 445 bool SM130::writeValueBlock(uint8_t block, int32_t value) 446 { 447 clearError(); 448 449 string data; 450 data.push_back(block); 451 // put the value in, LSB first 452 data += (value & 0xff); 453 data += ((value >> 8) & 0xff); 454 data += ((value >> 16) & 0xff); 455 data += ((value >> 24) & 0xff); 456 457 string resp = sendCommand(CMD_WRITE_VALUE, data); 458 459 if (resp.empty()) 460 { 461 cerr << __FUNCTION__ << ": failed" << endl; 462 return false; 463 } 464 465 if ((uint8_t)resp[0] == 2) 466 { 467 // then we got an error of some sort, store the error code, str 468 // and bail. 469 m_lastErrorCode = resp[2]; 470 471 switch (m_lastErrorCode) 472 { 473 case 'F': m_lastErrorString = "Read failed during verification"; 474 break; 475 case 'N': m_lastErrorString = "No tag present"; 476 break; 477 case 'I': m_lastErrorString = "Invalid value block"; 478 break; 479 default: m_lastErrorString = "Unknown error code"; 480 break; 481 } 482 return false; 483 } 484 485 return true; 486 } 487 488 bool SM130::writeBlock4(uint8_t block, string contents) 489 { 490 clearError(); 491 492 // A little sanity checking... 493 if (contents.size() != 4) 494 { 495 throw std::invalid_argument(string(__FUNCTION__) + 496 ": You must supply 4 bytes for block content"); 497 498 return false; 499 } 500 501 string data; 502 data.push_back(block); 503 data += contents; 504 505 string resp = sendCommand(CMD_WRITE4, data); 506 507 if (resp.empty()) 508 { 509 cerr << __FUNCTION__ << ": failed" << endl; 510 return false; 511 } 512 513 if ((uint8_t)resp[0] == 2) 514 { 515 // then we got an error of some sort, store the error code, str 516 // and bail. 517 m_lastErrorCode = resp[2]; 518 519 switch (m_lastErrorCode) 520 { 521 case 'F': m_lastErrorString = "Write failed"; 522 break; 523 case 'N': m_lastErrorString = "No tag present"; 524 break; 525 case 'U': m_lastErrorString = "Read after write failed"; 526 break; 527 case 'X': m_lastErrorString = "Unable to read after write"; 528 break; 529 default: m_lastErrorString = "Unknown error code"; 530 break; 531 } 532 return false; 533 } 534 535 return true; 536 } 537 538 bool SM130::writeKey(uint8_t eepromSector, KEY_TYPES_T keyType, string key) 539 { 540 clearError(); 541 542 // A little sanity checking... 543 eepromSector &= 0x0f; // Only 0x00-0x0f is valid 544 545 if (!(keyType == KEY_TYPE_A || keyType == KEY_TYPE_B)) 546 { 547 throw std::invalid_argument(string(__FUNCTION__) + 548 ": Key type must be A or B"); 549 550 return false; 551 } 552 553 if (key.size() != 6) 554 { 555 throw std::invalid_argument(string(__FUNCTION__) + 556 ": Key must be 6 bytes"); 557 558 return false; 559 } 560 561 string data; 562 data.push_back(eepromSector); 563 data += keyType; 564 data += key; 565 566 string resp = sendCommand(CMD_WRITE_KEY, data); 567 568 if (resp.empty()) 569 { 570 cerr << __FUNCTION__ << ": failed" << endl; 571 return false; 572 } 573 574 // reponse len is always 2 575 if ((uint8_t)resp[2] != 'L') 576 { 577 // then we got an error of some sort, store the error code, str 578 // and bail. 579 m_lastErrorCode = resp[2]; 580 581 switch (m_lastErrorCode) 582 { 583 case 'N': m_lastErrorString = "Write master key failed"; 584 break; 585 default: m_lastErrorString = "Unknown error code"; 586 break; 587 } 588 return false; 589 } 590 591 return true; 592 } 593 594 int32_t SM130::adjustValueBlock(uint8_t block, int32_t value, bool incr) 595 { 596 clearError(); 597 598 string data; 599 data.push_back(block); 600 // put the value in, LSB first 601 data += (value & 0xff); 602 data += ((value >> 8) & 0xff); 603 data += ((value >> 16) & 0xff); 604 data += ((value >> 24) & 0xff); 605 606 string resp = sendCommand(((incr) ? CMD_INC_VALUE : CMD_DEC_VALUE), data); 607 608 if (resp.empty()) 609 { 610 cerr << __FUNCTION__ << ": failed" << endl; 611 return 0; 612 } 613 614 if ((uint8_t)resp[0] == 2) 615 { 616 // then we got an error of some sort, store the error code, str 617 // and bail. 618 m_lastErrorCode = resp[2]; 619 620 switch (m_lastErrorCode) 621 { 622 case 'F': m_lastErrorString = "Read failed during verification"; 623 break; 624 case 'N': m_lastErrorString = "No tag present"; 625 break; 626 case 'I': m_lastErrorString = "Invalid value block"; 627 break; 628 default: m_lastErrorString = "Unknown error code"; 629 break; 630 } 631 return 0; 632 } 633 634 // now unpack the new value, LSB first 635 int32_t rv; 636 rv = ((uint8_t)resp[3] | 637 ((uint8_t)resp[4] << 8) | 638 ((uint8_t)resp[5] << 16) | 639 ((uint8_t)resp[6] << 24)); 640 641 return rv; 642 } 643 644 bool SM130::setAntennaPower(bool on) 645 { 646 clearError(); 647 648 string resp = sendCommand(CMD_ANTENNA_POWER, ""); 649 650 if (resp.empty()) 651 { 652 cerr << __FUNCTION__ << ": failed" << endl; 653 return false; 654 } 655 656 return true; 657 } 658 659 uint8_t SM130::readPorts() 660 { 661 clearError(); 662 663 string resp = sendCommand(CMD_READ_PORT, ""); 664 665 if (resp.empty()) 666 { 667 cerr << __FUNCTION__ << ": failed" << endl; 668 return 0; 669 } 670 671 // only the first 2 bits are valid 672 return (resp[2] & 3); 673 } 674 675 bool SM130::writePorts(uint8_t val) 676 { 677 clearError(); 678 679 // only the first 2 bits are valid 680 val &= 3; 681 682 string data; 683 data.push_back(val); 684 685 string resp = sendCommand(CMD_WRITE_PORT, data); 686 687 if (resp.empty()) 688 { 689 cerr << __FUNCTION__ << ": failed" << endl; 690 return false; 691 } 692 693 return true; 694 } 695 696 bool SM130::haltTag() 697 { 698 clearError(); 699 700 string resp = sendCommand(CMD_HALT_TAG, ""); 701 702 if (resp.empty()) 703 { 704 cerr << __FUNCTION__ << ": failed" << endl; 705 return false; 706 } 707 708 // reponse len is always 2 709 if ((uint8_t)resp[2] != 'L') 710 { 711 // then we got an error of some sort, store the error code, str 712 // and bail. 713 m_lastErrorCode = resp[2]; 714 715 switch (m_lastErrorCode) 716 { 717 case 'U': m_lastErrorString = "Can not halt, RF field is off"; 718 break; 719 default: m_lastErrorString = "Unknown error code"; 720 break; 721 } 722 return false; 723 } 724 725 return true; 726 } 727 728 bool SM130::setSM130BaudRate(int baud) 729 { 730 clearError(); 731 732 uint8_t newBaud; 733 734 switch (baud) 735 { 736 case 9600: newBaud = 0x00; 737 break; 738 case 19200: newBaud = 0x01; 739 break; 740 case 38400: newBaud = 0x02; 741 break; 742 case 57600: newBaud = 0x03; 743 break; 744 case 115200: newBaud = 0x04; 745 break; 746 default: 747 throw std::invalid_argument(string(__FUNCTION__) + 748 ": Invalid SM130 baud rate specified"); 749 } 750 751 // WARNING: this is a dangerous command 752 int oldBaud = m_baud; 753 m_baud = baud; 754 755 string data; 756 data.push_back(newBaud); 757 758 string resp = sendCommand(CMD_SET_BAUD, data); 759 760 if (resp.empty()) 761 { 762 cerr << __FUNCTION__ << ": failed" << endl; 763 cerr << __FUNCTION__ << ": restoring old baud rate" << endl; 764 765 setBaudRate(oldBaud); 766 return false; 767 } 768 769 // otherwise assume success, possibly incorrectly 770 return true; 771 } 772 773 bool SM130::sleep() 774 { 775 clearError(); 776 777 string resp = sendCommand(CMD_SLEEP, ""); 778 779 if (resp.empty()) 780 { 781 cerr << __FUNCTION__ << ": failed" << endl; 782 return false; 783 } 784 785 return true; 786 } 787 788 string SM130::string2HexString(string input) 789 { 790 static const char* const lut = "0123456789abcdef"; 791 size_t len = input.size(); 792 793 string output; 794 output.reserve(3 * len); 795 for (size_t i = 0; i < len; ++i) 796 { 797 const unsigned char c = input[i]; 798 output.push_back(lut[c >> 4]); 799 output.push_back(lut[c & 15]); 800 output.push_back(' '); 801 } 802 803 return output; 804 } 805 806 void SM130::initClock() 807 { 808 gettimeofday(&m_startTime, NULL); 809 } 810 811 uint32_t SM130::getMillis() 812 { 813 struct timeval elapsed, now; 814 uint32_t elapse; 815 816 // get current time 817 gettimeofday(&now, NULL); 818 819 // compute the delta since m_startTime 820 if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 ) 821 { 822 elapsed.tv_usec += 1000000; 823 elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1; 824 } 825 else 826 { 827 elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec; 828 } 829 830 elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000)); 831 832 // never return 0 833 if (elapse == 0) 834 elapse = 1; 835 836 return elapse; 837 } 838 839 bool SM130::waitForTag(uint32_t timeout) 840 { 841 initClock(); 842 843 do 844 { 845 if (select()) 846 { 847 // success 848 return true; 849 } 850 else 851 { 852 // If there was an error, fail if it's anything other than a 853 // tag not present 854 if (getLastErrorCode() != 'N') 855 return false; 856 857 // otherwise, sleep for 100ms and try again 858 usleep(100000); 859 } 860 } while (getMillis() <= timeout); 861 862 return false; 863 } 864 865 string SM130::tag2String(TAG_TYPE_T tag) 866 { 867 switch (tag) 868 { 869 case TAG_MIFARE_ULTRALIGHT: return "MiFare Ultralight"; 870 case TAG_MIFARE_1K: return "MiFare 1K"; 871 case TAG_MIFARE_4K: return "MiFare 4K"; 872 case TAG_UNKNOWN: return "Unknown Tag Type"; 873 default: return "Invalid Tag Type"; 874 } 875 } 876 877 878 879