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 <iostream>
26 #include <string>
27 #include <stdexcept>
28 
29 #include "zfm20.h"
30 
31 using namespace upm;
32 using namespace std;
33 
34 static const int defaultDelay = 100;     // max wait time for read
35 
ZFM20(int uart)36 ZFM20::ZFM20(int uart)
37 {
38   m_ttyFd = -1;
39 
40   if ( !(m_uart = mraa_uart_init(uart)) )
41     {
42       throw std::invalid_argument(std::string(__FUNCTION__) +
43                                   ": mraa_uart_init() failed");
44       return;
45     }
46 
47   // This requires a recent MRAA (1/2015)
48   const char *devPath = mraa_uart_get_dev_path(m_uart);
49 
50   if (!devPath)
51     {
52       throw std::runtime_error(std::string(__FUNCTION__) +
53                                ": mraa_uart_get_dev_path() failed");
54       return;
55     }
56 
57   // now open the tty
58   if ( (m_ttyFd = open(devPath, O_RDWR)) == -1)
59     {
60       throw std::runtime_error(std::string(__FUNCTION__) +
61                                ": open of " +
62                                string(devPath) + " failed: " +
63                                string(strerror(errno)));
64       return;
65     }
66 
67   // Set the default password and address
68   setPassword(ZFM20_DEFAULT_PASSWORD);
69   setAddress(ZFM20_DEFAULT_ADDRESS);
70 
71   initClock();
72 }
73 
~ZFM20()74 ZFM20::~ZFM20()
75 {
76   if (m_ttyFd != -1)
77     close(m_ttyFd);
78 
79   mraa_deinit();
80 }
81 
dataAvailable(unsigned int millis)82 bool ZFM20::dataAvailable(unsigned int millis)
83 {
84   if (m_ttyFd == -1)
85     return false;
86 
87   struct timeval timeout;
88 
89   // no waiting
90   timeout.tv_sec = 0;
91   timeout.tv_usec = millis * 1000;
92 
93   int nfds;
94   fd_set readfds;
95 
96   FD_ZERO(&readfds);
97 
98   FD_SET(m_ttyFd, &readfds);
99 
100   if (select(m_ttyFd + 1, &readfds, NULL, NULL, &timeout) > 0)
101     return true;                // data is ready
102   else
103     return false;
104 }
105 
readData(char * buffer,int len)106 int ZFM20::readData(char *buffer, int len)
107 {
108   if (m_ttyFd == -1)
109     return(-1);
110 
111   if (!dataAvailable(defaultDelay))
112     return 0;               // timed out
113 
114   int rv = read(m_ttyFd, buffer, len);
115 
116   if (rv < 0)
117     {
118       throw std::runtime_error(std::string(__FUNCTION__) +
119                                ": read() failed: " +
120                                string(strerror(errno)));
121       return rv;
122     }
123 
124   return rv;
125 }
126 
writeData(char * buffer,int len)127 int ZFM20::writeData(char *buffer, int len)
128 {
129   if (m_ttyFd == -1)
130     return(-1);
131 
132   // first, flush any pending but unread input
133   tcflush(m_ttyFd, TCIFLUSH);
134 
135   int rv = write(m_ttyFd, buffer, len);
136 
137   if (rv < 0)
138     {
139       throw std::runtime_error(std::string(__FUNCTION__) +
140                                ": write() failed: " +
141                                string(strerror(errno)));
142       return rv;
143     }
144 
145   if (rv == 0)
146     {
147       throw std::runtime_error(std::string(__FUNCTION__) +
148                                ": write() failed, no bytes written");
149       return rv;
150     }
151 
152   tcdrain(m_ttyFd);
153 
154   return rv;
155 }
156 
setupTty(speed_t baud)157 bool ZFM20::setupTty(speed_t baud)
158 {
159   if (m_ttyFd == -1)
160     return(false);
161 
162   struct termios termio;
163 
164   // get current modes
165   tcgetattr(m_ttyFd, &termio);
166 
167   // setup for a 'raw' mode.  81N, no echo or special character
168   // handling, such as flow control.
169   cfmakeraw(&termio);
170 
171   // set our baud rates
172   cfsetispeed(&termio, baud);
173   cfsetospeed(&termio, baud);
174 
175   // make it so
176   if (tcsetattr(m_ttyFd, TCSAFLUSH, &termio) < 0)
177     {
178       throw std::runtime_error(std::string(__FUNCTION__) +
179                                ": tcsetattr() failed: " +
180                                string(strerror(errno)));
181       return false;
182     }
183 
184   return true;
185 }
186 
writeCmdPacket(uint8_t * pkt,int len)187 int ZFM20::writeCmdPacket(uint8_t *pkt, int len)
188 {
189   uint8_t rPkt[ZFM20_MAX_PKT_LEN];
190 
191   rPkt[0] = ZFM20_START1;             // header bytes
192   rPkt[1] = ZFM20_START2;
193 
194   rPkt[2] = (m_address >> 24) & 0xff; // address
195   rPkt[3] = (m_address >> 16) & 0xff;
196   rPkt[4] = (m_address >> 8) & 0xff;
197   rPkt[5] = m_address & 0xff;
198 
199   rPkt[6] = PKT_COMMAND;
200 
201   rPkt[7] = ((len + 2) >> 8) & 0xff;  // length (+ len bytes)
202   rPkt[8] = (len + 2) & 0xff;
203 
204   // compute the starting checksum
205   uint16_t cksum = rPkt[7] + rPkt[8] + PKT_COMMAND;
206 
207   int j = 9;
208   for (int i=0; i<len; i++)
209     {
210       rPkt[j] = pkt[i];
211       cksum += rPkt[j];
212       j++;
213     }
214 
215   rPkt[j++] = (cksum >> 8) & 0xff;    // store the cksum
216   rPkt[j++] = cksum & 0xff;
217 
218   return writeData((char *)rPkt, j);
219 }
220 
initClock()221 void ZFM20::initClock()
222 {
223   gettimeofday(&m_startTime, NULL);
224 }
225 
getMillis()226 uint32_t ZFM20::getMillis()
227 {
228   struct timeval elapsed, now;
229   uint32_t elapse;
230 
231   // get current time
232   gettimeofday(&now, NULL);
233 
234   // compute the delta since m_startTime
235   if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 )
236     {
237       elapsed.tv_usec += 1000000;
238       elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1;
239     }
240   else
241     {
242       elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec;
243     }
244 
245   elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000));
246 
247   // never return 0
248   if (elapse == 0)
249     elapse = 1;
250 
251   return elapse;
252 }
253 
verifyPacket(uint8_t * pkt,int len)254 bool ZFM20::verifyPacket(uint8_t *pkt, int len)
255 {
256   // verify packet header
257   if (pkt[0] != ZFM20_START1 || pkt[1] != ZFM20_START2)
258     {
259       throw std::runtime_error(std::string(__FUNCTION__) +
260                                ": Invalid packet header");
261       return false;
262     }
263 
264   // check the ack byte
265   if (pkt[6] != PKT_ACK)
266     {
267       throw std::runtime_error(std::string(__FUNCTION__) +
268                                ": Invalid ACK code");
269       return false;
270     }
271 
272   return true;
273 }
274 
getResponse(uint8_t * pkt,int len)275 bool ZFM20::getResponse(uint8_t *pkt, int len)
276 {
277   char buf[ZFM20_MAX_PKT_LEN];
278 
279   initClock();
280 
281   int idx = 0;
282   int timer = 0;
283   int rv;
284   int plen = 0;
285 
286   while (idx < len)
287     {
288       // wait for some data
289       if (!dataAvailable(100))
290         {
291           timer += getMillis();
292           if (timer > ZFM20_TIMEOUT)
293             {
294               throw std::runtime_error(std::string(__FUNCTION__) +
295                                        ": Timed out waiting for packet");
296               return false;
297             }
298 
299           continue;
300         }
301 
302       if ((rv = readData(buf, ZFM20_MAX_PKT_LEN)) == 0)
303         {
304           throw std::runtime_error(std::string(__FUNCTION__) +
305                                    ": readData() failed, no data returned");
306           return false;
307         }
308 
309       // copy it into the user supplied buffer
310       for (int i=0; i<rv; i++)
311         {
312           pkt[idx++] = buf[i];
313           if (idx >= len)
314             break;
315         }
316     }
317 
318   // now verify it.
319   return verifyPacket(pkt, len);
320 }
321 
verifyPassword()322 bool ZFM20::verifyPassword()
323 {
324   const int pktLen = 5;
325   uint8_t pkt[pktLen] = {CMD_VERIFY_PASSWORD,
326                          static_cast<uint8_t>((m_password >> 24) & 0xff),
327                          static_cast<uint8_t>((m_password >> 16) & 0xff),
328                          static_cast<uint8_t>((m_password >> 8) & 0xff),
329                          static_cast<uint8_t>(m_password & 0xff) };
330 
331   writeCmdPacket(pkt, pktLen);
332 
333   // now read a response
334   const int rPktLen = 12;
335   uint8_t rPkt[rPktLen];
336 
337   getResponse(rPkt, rPktLen);
338 
339 
340   return true;
341 }
342 
getNumTemplates()343 int ZFM20::getNumTemplates()
344 {
345   const int pktLen = 1;
346   uint8_t pkt[pktLen] = {CMD_GET_TMPL_COUNT};
347 
348   writeCmdPacket(pkt, pktLen);
349 
350   // now read a response
351   const int rPktLen = 14;
352   uint8_t rPkt[rPktLen];
353 
354   getResponse(rPkt, rPktLen);
355 
356   // check confirmation code
357   if (rPkt[9] != 0x00)
358     {
359       throw std::runtime_error(std::string(__FUNCTION__) +
360                                ": Invalid confirmation code");
361       return 0;
362     }
363 
364   return ((rPkt[10] << 8) | rPkt[11]);
365 }
366 
setNewPassword(uint32_t pwd)367 bool ZFM20::setNewPassword(uint32_t pwd)
368 {
369   const int pktLen = 5;
370   uint8_t pkt[pktLen] = {CMD_SET_PASSWORD,
371                          static_cast<uint8_t>((pwd >> 24) & 0xff),
372                          static_cast<uint8_t>((pwd >> 16) & 0xff),
373                          static_cast<uint8_t>((pwd >> 8) & 0xff),
374                          static_cast<uint8_t>(pwd & 0xff) };
375 
376   writeCmdPacket(pkt, pktLen);
377 
378   // now read a response
379   const int rPktLen = 12;
380   uint8_t rPkt[rPktLen];
381 
382   getResponse(rPkt, rPktLen);
383 
384   // check confirmation code
385   if (rPkt[9] != 0x00)
386     {
387       throw std::runtime_error(std::string(__FUNCTION__) +
388                                ": Invalid confirmation code");
389       return false;
390     }
391 
392   m_password = pwd;
393 
394   return true;
395 }
396 
setNewAddress(uint32_t addr)397 bool ZFM20::setNewAddress(uint32_t addr)
398 {
399   const int pktLen = 5;
400   uint8_t pkt[pktLen] = {CMD_SET_ADDRESS,
401                          static_cast<uint8_t>((addr >> 24) & 0xff),
402                          static_cast<uint8_t>((addr >> 16) & 0xff),
403                          static_cast<uint8_t>((addr >> 8) & 0xff),
404                          static_cast<uint8_t>(addr & 0xff) };
405 
406   writeCmdPacket(pkt, pktLen);
407 
408   // now read a response
409   const int rPktLen = 12;
410   uint8_t rPkt[rPktLen];
411 
412   getResponse(rPkt, rPktLen);
413 
414   // check confirmation code
415   if (rPkt[9] != 0x00)
416     {
417       throw std::runtime_error(std::string(__FUNCTION__) +
418                                ": Invalid confirmation code");
419       return false;
420     }
421 
422   m_address = addr;
423 
424   return true;
425 }
426 
generateImage()427 uint8_t ZFM20::generateImage()
428 {
429   const int pktLen = 1;
430   uint8_t pkt[pktLen] = {CMD_GEN_IMAGE};
431 
432   writeCmdPacket(pkt, pktLen);
433 
434   // now read a response
435   const int rPktLen = 12;
436   uint8_t rPkt[rPktLen];
437 
438   getResponse(rPkt, rPktLen);
439 
440   return rPkt[9];
441 }
442 
image2Tz(int slot)443 uint8_t ZFM20::image2Tz(int slot)
444 {
445   if (slot != 1 && slot != 2)
446     {
447       throw std::out_of_range(std::string(__FUNCTION__) +
448                               ": slot must be 1 or 2");
449       return ERR_INTERNAL_ERR;
450     }
451 
452   const int pktLen = 2;
453   uint8_t pkt[pktLen] = {CMD_IMG2TZ,
454                          static_cast<uint8_t>(slot & 0xff)};
455 
456   writeCmdPacket(pkt, pktLen);
457 
458   // now read a response
459   const int rPktLen = 12;
460   uint8_t rPkt[rPktLen];
461 
462   getResponse(rPkt, rPktLen);
463 
464   return rPkt[9];
465 }
466 
createModel()467 uint8_t ZFM20::createModel()
468 {
469   const int pktLen = 1;
470   uint8_t pkt[pktLen] = {CMD_REGMODEL};
471 
472   writeCmdPacket(pkt, pktLen);
473 
474   // now read a response
475   const int rPktLen = 12;
476   uint8_t rPkt[rPktLen];
477 
478   getResponse(rPkt, rPktLen);
479 
480   return rPkt[9];
481 }
482 
storeModel(int slot,uint16_t id)483 uint8_t ZFM20::storeModel(int slot, uint16_t id)
484 {
485   if (slot != 1 && slot != 2)
486     {
487       throw std::out_of_range(std::string(__FUNCTION__) +
488                               ": slot must be 1 or 2");
489       return ERR_INTERNAL_ERR;
490     }
491 
492   const int pktLen = 4;
493   uint8_t pkt[pktLen] = {CMD_STORE,
494                          static_cast<uint8_t>(slot & 0xff),
495                          static_cast<uint8_t>((id >> 8) & 0xff),
496                          static_cast<uint8_t>(id & 0xff)};
497 
498   writeCmdPacket(pkt, pktLen);
499 
500   // now read a response
501   const int rPktLen = 12;
502   uint8_t rPkt[rPktLen];
503 
504   getResponse(rPkt, rPktLen);
505 
506   return rPkt[9];
507 }
508 
deleteModel(uint16_t id)509 uint8_t ZFM20::deleteModel(uint16_t id)
510 {
511   const int pktLen = 5;
512   uint8_t pkt[pktLen] = {CMD_DELETE_TMPL,
513                          static_cast<uint8_t>((id >> 8) & 0xff),
514                          static_cast<uint8_t>(id & 0xff),
515                          0x00,
516                          0x01};
517 
518   writeCmdPacket(pkt, pktLen);
519 
520   // now read a response
521   const int rPktLen = 12;
522   uint8_t rPkt[rPktLen];
523 
524   getResponse(rPkt, rPktLen);
525 
526   return rPkt[9];
527 }
528 
deleteDB()529 uint8_t ZFM20::deleteDB()
530 {
531   const int pktLen = 1;
532   uint8_t pkt[pktLen] = {CMD_EMPTYDB};
533 
534   writeCmdPacket(pkt, pktLen);
535 
536   // now read a response
537   const int rPktLen = 12;
538   uint8_t rPkt[rPktLen];
539 
540   getResponse(rPkt, rPktLen);
541 
542   return rPkt[9];
543 }
544 
search(int slot,uint16_t * id,uint16_t * score)545 uint8_t ZFM20::search(int slot, uint16_t *id, uint16_t *score)
546 {
547   *id = 0;
548   *score = 0;
549 
550   if (slot != 1 && slot != 2)
551     {
552       throw std::out_of_range(std::string(__FUNCTION__) +
553                               ": slot must be 1 or 2");
554       return ERR_INTERNAL_ERR;
555     }
556 
557   // search from page 0x0000 to page 0x00a3
558   const int pktLen = 6;
559   uint8_t pkt[pktLen] = {CMD_SEARCH,
560                          static_cast<uint8_t>(slot & 0xff),
561                          0x00,
562                          0x00,
563                          0x00,
564                          0xa3};
565 
566   writeCmdPacket(pkt, pktLen);
567 
568   // now read a response
569   const int rPktLen = 16;
570   uint8_t rPkt[rPktLen];
571 
572   getResponse(rPkt, rPktLen);
573 
574   // if it was found, extract the location and the score
575   if (rPkt[9] == ERR_OK)
576     {
577       *id = ((rPkt[10] << 8) & 0xff) | (rPkt[11] & 0xff);
578       *score = ((rPkt[12] << 8) & 0xff) | (rPkt[13] & 0xff);
579     }
580 
581   return rPkt[9];
582 }
583 
match(uint16_t * score)584 uint8_t ZFM20::match(uint16_t *score)
585 {
586   *score = 0;
587 
588   const int pktLen = 1;
589   uint8_t pkt[pktLen] = {CMD_MATCH};
590 
591   writeCmdPacket(pkt, pktLen);
592 
593   // now read a response
594   const int rPktLen = 14;
595   uint8_t rPkt[rPktLen];
596 
597   getResponse(rPkt, rPktLen);
598 
599   *score = ((rPkt[10] << 8) & 0xff) | (rPkt[11] & 0xff);
600 
601   return rPkt[9];
602 }
603 
604 
605