1 /*
2 * Author: Jon Trulson <jtrulson@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
SM130(int uart,int reset)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
~SM130()52 SM130::~SM130()
53 {
54 }
55
setBaudRate(int baud)56 mraa::Result SM130::setBaudRate(int baud)
57 {
58 m_baud = baud;
59 return m_uart.setBaudRate(baud);
60 }
61
sendCommand(CMD_T cmd,string data)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
getFirmwareVersion()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
reset()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
hardwareReset()195 void SM130::hardwareReset()
196 {
197 m_gpioReset.write(1);
198 usleep(100000);
199 m_gpioReset.write(0);
200 }
201
select()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
authenticate(uint8_t block,KEY_TYPES_T keyType,string key)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
readBlock16(uint8_t block)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
readValueBlock(uint8_t block)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
writeBlock16(uint8_t block,string contents)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
writeValueBlock(uint8_t block,int32_t value)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
writeBlock4(uint8_t block,string contents)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
writeKey(uint8_t eepromSector,KEY_TYPES_T keyType,string key)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
adjustValueBlock(uint8_t block,int32_t value,bool incr)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
setAntennaPower(bool on)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
readPorts()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
writePorts(uint8_t val)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
haltTag()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
setSM130BaudRate(int baud)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
sleep()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
string2HexString(string input)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
initClock()806 void SM130::initClock()
807 {
808 gettimeofday(&m_startTime, NULL);
809 }
810
getMillis()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
waitForTag(uint32_t timeout)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
tag2String(TAG_TYPE_T tag)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