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