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 <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 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 74 ZFM20::~ZFM20() 75 { 76 if (m_ttyFd != -1) 77 close(m_ttyFd); 78 79 mraa_deinit(); 80 } 81 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 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 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 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 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 221 void ZFM20::initClock() 222 { 223 gettimeofday(&m_startTime, NULL); 224 } 225 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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