1 // Copyright (c) 2010, Atmel Corporation. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above copyright 9 // notice, this list of conditions and the following disclaimer in the 10 // documentation and/or other materials provided with the distribution. 11 // * Neither the name of Atmel nor the 12 // names of its contributors may be used to endorse or promote products 13 // derived from this software without specific prior written permission. 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <termios.h> 32 #include <time.h> 33 #include <unistd.h> 34 35 #include "SA_Phys_Linux.h" 36 #include "SHA_Status.h" 37 #include "SHA_TimeUtils.h" 38 #include "Whisper_AccyMain.h" 39 40 41 42 #define MAX_BUF_LEN 512 43 #define M_ONE_BIT 0x7F 44 #define M_ZERO_BIT 0x7D 45 #define OPPBAUD B230400 46 #define WAKEBAUD B115200 47 48 49 static void configTtyParams(); 50 static int8_t setBaudRate(speed_t Inspeed); 51 static int8_t writeToDevice(uint8_t *data, uint8_t len); 52 static int8_t readFromDevice(uint8_t *readBuf, uint16_t readLen, 53 uint8_t CmdOfset, uint16_t *retBytes); 54 static int8_t sleepDevice(void); 55 static int16_t formatBytes(uint8_t *ByteData, uint8_t *ByteDataRaw, 56 int16_t lenData); 57 58 59 static const char ttyPort[] = "/dev/ttyHS0"; 60 static uint8_t readwriteBuf[MAX_BUF_LEN]; 61 static uint8_t WakeStr = {0x00}; 62 static uint8_t* pWake = &WakeStr; 63 static uint8_t TransmitStr = {0x88}; 64 static uint8_t* pTrm = &TransmitStr; 65 static uint8_t CmdStr = {0x77}; 66 static uint8_t* pCmd = &CmdStr; 67 static uint8_t SleepStr= {0xCC}; 68 static uint8_t InStr[41]; 69 static uint8_t* pIStr = InStr; 70 static fd_set readfs; 71 static struct termios termOptions; 72 73 int ttyFd = -1; 74 75 76 /* Sets up and configures the UART for use */ 77 int8_t SHAP_OpenChannel(void) { 78 struct termios tty; 79 speed_t speed; 80 struct timespec ts; 81 82 ttyFd = open(ttyPort, O_RDWR); 83 if (ttyFd == -1) { 84 DBG_ERROR("Error unable to open device: %s", ttyPort); 85 return SHA_COMM_FAIL; 86 } 87 88 DBG_TRACE("%s opened with port %d", ttyPort, ttyFd); 89 if (tcflush(ttyFd, TCIOFLUSH) == 0) { 90 DBG_TRACE("The input and output queues have been flushed"); 91 } 92 else { 93 DBG_ERROR("tcflush() error"); 94 } 95 96 configTtyParams(); 97 98 ts.tv_sec = 0; 99 ts.tv_nsec = MAX_IO_TIMEOUT * 1000000; 100 nanosleep(&ts, NULL); 101 102 return SHA_SUCCESS; 103 } 104 105 106 107 int8_t SHAP_CloseChannel(void) { 108 int8_t ret = sleepDevice(); 109 close(ttyFd); 110 return ret; 111 } 112 113 int8_t SHAP_SendBytes(uint8_t count, uint8_t *buffer) { 114 uint16_t bytesRead; 115 int8_t i, retVal; 116 117 if (!count || !buffer) { 118 DBG_ERROR("Bad input"); 119 return SHA_BAD_PARAM; 120 } 121 122 if (tcflush(ttyFd, TCIOFLUSH) == 0) { 123 DBG_TRACE("The input and output queues have been flushed"); 124 } 125 else { 126 DBG_ERROR("tcflush() error"); 127 } 128 129 memmove(&buffer[1], buffer, count); 130 buffer[0] = CmdStr; 131 132 writeToDevice(buffer, count+1); 133 134 // Read the echo back ... 135 readFromDevice(readwriteBuf, 8*(count+1), 0, &bytesRead); 136 137 if (tcflush(ttyFd, TCIFLUSH) == 0) { 138 DBG_TRACE("The input queue has been flushed"); 139 } 140 else { 141 DBG_ERROR("tcflush() error"); 142 } 143 144 return SHA_SUCCESS; 145 } 146 147 148 int8_t SHAP_ReceiveBytes(uint8_t recCommLen, uint8_t *dataBuf) { 149 struct timespec ts; 150 uint16_t bytesRead; 151 int8_t i,iResVal, cmdLen = recCommLen; 152 153 if (!recCommLen || !dataBuf) { 154 return SHA_BAD_PARAM; 155 } 156 157 if (writeToDevice(pTrm, 1) == 1) { 158 DBG_TRACE("Test Write to %s successful", ttyPort); 159 } 160 else { 161 DBG_ERROR("Test Write to %s unsuccessful", ttyPort); 162 } 163 164 iResVal = readFromDevice(dataBuf, (cmdLen+1)*8, 8, &bytesRead); 165 166 if (iResVal == SHA_COMM_FAIL) { 167 DBG_ERROR("Read Error unable to read port: %d from device: %s", ttyFd, ttyPort); 168 return SHA_COMM_FAIL; 169 } 170 171 return SHA_SUCCESS; 172 } 173 174 175 176 177 void SHAP_CloseFile(void) { 178 struct timespec ts; 179 close(ttyFd); 180 181 ts.tv_sec = 0; 182 ts.tv_nsec = MAX_IO_TIMEOUT * 1000000; 183 nanosleep(&ts, NULL); 184 } 185 186 187 188 189 /* Reads the message from device. returns NULL if error */ 190 static int8_t readFromDevice(uint8_t *readBuf, uint16_t readLen, 191 uint8_t CmdOfset, uint16_t *retBytes) { 192 int8_t goOn = 1; 193 struct timeval Timeout; 194 uint16_t numBytesRead = 0; 195 int retVal; 196 uint16_t i; 197 198 Timeout.tv_usec = 200000; 199 Timeout.tv_sec = 0; 200 *retBytes = 0; 201 202 for (i = 0; i < sizeof(readwriteBuf); i++) { 203 readwriteBuf[i]= 0x00; 204 } 205 206 while (goOn) { 207 FD_SET(ttyFd, &readfs); 208 retVal = select(ttyFd+1, &readfs, NULL, NULL, &Timeout); 209 210 if (retVal == 0) { 211 DBG_ERROR("Timeout on select() occurred on port %d. Receive <%d> bytes", ttyFd, numBytesRead); 212 if (numBytesRead > 0) { 213 return SHA_SUCCESS; 214 } 215 else { 216 return SHA_COMM_FAIL; 217 } 218 } 219 220 if (retVal < 0 && errno == EINTR) { 221 DBG_ERROR("SELECT returned EINTR "); 222 continue; 223 } 224 225 if (FD_ISSET(ttyFd, &readfs)) { 226 do { 227 retVal = read(ttyFd, &readwriteBuf[numBytesRead], MAX_BUF_LEN); 228 } while (retVal < 0 && errno == EINTR); 229 230 if (retVal > 0) { 231 numBytesRead += retVal; 232 *retBytes = numBytesRead; 233 234 DBG_TRACE("REQ READ LEN = %d, NUM BYT READ = %d, retVal = %d offset = %d", 235 readLen, numBytesRead, retVal, CmdOfset); 236 237 if (numBytesRead >= (readLen)) { 238 DBG_TRACE("Read Success"); 239 break; 240 } 241 } 242 } 243 else { 244 DBG_ERROR("Select Error. ERRNO = %d", errno); 245 } 246 } 247 248 formatBytes(readBuf, &readwriteBuf[CmdOfset], readLen-CmdOfset); 249 250 return SHA_SUCCESS; 251 } 252 253 254 255 256 /* Transmits a message to be sent over tty */ 257 static int8_t writeToDevice(uint8_t *data, uint8_t len) { 258 uint16_t i,j; 259 int nbytes, nwritten; 260 uint8_t *byteptr; 261 262 // Every byte gets transferred into 8 bytes 263 if (len*8 > MAX_BUF_LEN) { 264 return SHA_COMM_FAIL; 265 } 266 267 for(i = 0; i < len; i++) { 268 for(j = 0; j < 8; j++) { 269 if (data[i] & (1 << j)) { 270 readwriteBuf[(i*8)+j] = M_ONE_BIT; 271 } 272 else { 273 readwriteBuf[(i*8)+j] = M_ZERO_BIT; 274 } 275 } 276 } 277 278 do { 279 nwritten = write(ttyFd, readwriteBuf, len*8); 280 } while (nwritten < 0 && errno == EINTR); 281 282 if (nwritten == -1) { 283 DBG_ERROR("Write Failed with errno = %d", errno); 284 return SHA_COMM_FAIL; 285 } 286 else if (nwritten != len*8) { 287 DBG_ERROR("ERROR. write less than requested<%d>. written: %i",nwritten, len*8); 288 } 289 290 nbytes = nwritten / 8; 291 292 return nbytes; 293 } 294 295 296 297 298 /* Formats the data received from UART to byte data */ 299 static int16_t formatBytes(uint8_t *ByteData, uint8_t *ByteDataRaw, 300 int16_t lenData) { 301 uint16_t i,j; 302 int16_t retLen = lenData; 303 304 for(j = 0; j < retLen/8;j++) { 305 for (i = 0; i < 8; i++) { 306 if ((ByteDataRaw[(8 *j)+i] ^ 0x7F) & 0x7C) { 307 ByteData[j] &= ~(1 << i); 308 } 309 else { 310 ByteData[j] |= (1 << i); 311 } 312 } 313 } 314 315 return SHA_SUCCESS; 316 } 317 318 319 320 /* Wakes the device */ 321 int8_t SHAP_WakeDevice(void) { 322 int iResVal; 323 struct timespec ts; 324 uint16_t bytes_read; 325 uint8_t bytes_written; 326 ssize_t osize; 327 328 tcflush(ttyFd, TCIOFLUSH); 329 330 // Set Start Token Speed 331 setBaudRate(WAKEBAUD); 332 333 // Send Start Token 334 do { 335 osize = write(ttyFd, pWake, 1); 336 } while (osize < 0 && errno == EINTR); 337 338 if (osize == -1) { 339 DBG_ERROR("Write Failed with errno = %d", errno); 340 return SHA_COMM_FAIL; 341 } 342 343 ts.tv_sec = 0; 344 ts.tv_nsec = 3000000; 345 nanosleep(&ts, NULL); 346 347 // set the Baud Rate to Comm speed 348 setBaudRate(OPPBAUD); 349 if (writeToDevice(pTrm, 1) == 1) { 350 DBG_TRACE("Wakeup Write to %s successful", ttyPort); 351 } 352 else { 353 DBG_TRACE("Wakeup Write to %s unsuccessful", ttyPort); 354 } 355 356 357 iResVal = readFromDevice(pIStr, 41, 9, &bytes_read); 358 359 if (iResVal == SHA_COMM_FAIL || bytes_read < 41) { 360 sleepDevice(); 361 DBG_ERROR("WakeUp Error unable to read port: %d, Bytes Read = %d", ttyFd, bytes_read); 362 return SHA_COMM_FAIL; 363 } 364 365 if (tcflush(ttyFd, TCIOFLUSH) == 0) { 366 DBG_TRACE("The input and output queues have been flushed."); 367 } 368 else { 369 DBG_ERROR("tcflush() error"); 370 } 371 372 if (pIStr[0] == 0x04 && pIStr[1] == 0x11) { 373 DBG_TRACE("WakeUp Done"); 374 return SHA_SUCCESS; 375 } 376 else { 377 DBG_ERROR("WakeUp Fail"); 378 sleepDevice(); 379 return SHA_CMD_FAIL; 380 } 381 } 382 383 384 385 /** 386 * Transmits a message to be sent over tty 387 * 388 * \param[out] Success or fail flag 389 * \return status of the operation 390 */ 391 static int8_t sleepDevice(void) 392 { 393 uint8_t *byteptr = &SleepStr; 394 ssize_t osize; 395 struct timespec ts; 396 do { 397 osize = write(ttyFd, byteptr, 1); 398 } while (osize < 0 && errno == EINTR); 399 400 if (osize == -1) { 401 DBG_ERROR("Write Failed errno = %d", errno); 402 return SHA_COMM_FAIL; 403 } 404 ts.tv_sec = 0; 405 ts.tv_nsec = 100000000; // sleep for 100ms 406 nanosleep(&ts, NULL); 407 408 return SHA_SUCCESS; 409 } 410 411 void SA_Delay(uint32_t delay) 412 { 413 struct timespec ts; 414 415 ts.tv_sec = 0; 416 ts.tv_nsec = delay*1000; // convert us to ns 417 nanosleep(&ts, NULL); 418 } 419 420 /* Sets the baudrate of the tty port */ 421 static int8_t setBaudRate(speed_t Inspeed) { 422 int8_t ret; 423 424 ret = tcgetattr( ttyFd, &termOptions ); 425 426 if (ret == -1) { 427 DBG_ERROR("Error returned by tcgetattr. errno = %d", errno); 428 return SHA_COMM_FAIL; 429 } 430 431 cfsetospeed(&termOptions, Inspeed); 432 cfsetispeed(&termOptions, Inspeed); 433 ret = tcsetattr(ttyFd, TCSANOW, &termOptions ); 434 if (ret == -1) { 435 DBG_ERROR("Error returned by tcsetattr. errno = %d", errno); 436 return SHA_COMM_FAIL; 437 } 438 439 return SHA_SUCCESS; 440 } 441 442 static void configTtyParams() 443 { 444 445 struct termios tty; 446 447 // Get the existing options // 448 tcgetattr(ttyFd, &tty); 449 450 // Reset Control mode to 0. And enable just what you need // 451 tty.c_cflag = 0; 452 tty.c_cflag |= CLOCAL; // ignore modem control lines // 453 tty.c_cflag |= CREAD; // enable receiver 454 tty.c_cflag |= CS7; // use 7 data bits 455 tty.c_cflag &= ~CRTSCTS; // do not use RTS and CTS handshake 456 457 tty.c_iflag = INPCK; 458 tty.c_cc[VINTR] = 0; // Ctrl-c 459 tty.c_cc[VQUIT] = 0; /* Ctrl-\ */ 460 tty.c_cc[VERASE] = 0; // del 461 tty.c_cc[VKILL] = 0; // @ 462 tty.c_cc[VEOF] = 0; // Ctrl-d 463 tty.c_cc[VTIME] = 0; /// inter-character timer unused 464 tty.c_cc[VMIN] = 1; // blocking read until 1 character arrives 465 tty.c_cc[VSWTC] = 0; // '\0' 466 tty.c_cc[VSTART] = 0; // Ctrl-q 467 tty.c_cc[VSTOP] = 0; // Ctrl-s 468 tty.c_cc[VSUSP] = 0; // Ctrl-z 469 tty.c_cc[VEOL] = 0; // '\0' 470 tty.c_cc[VREPRINT] = 0; // Ctrl-r 471 tty.c_cc[VDISCARD] = 0; // Ctrl-u 472 tty.c_cc[VWERASE] = 0; // Ctrl-w 473 tty.c_cc[VLNEXT] = 0; // Ctrl-v 474 tty.c_cc[VEOL2] = 0; // '\0' 475 476 477 // Reset Input mode to 0. And enalbe just what you need // 478 tty.c_iflag = 0; 479 tty.c_iflag |= IGNBRK; 480 tty.c_iflag |= INPCK; // Enable input parity checking // 481 482 483 // Reset output mode to 0. And enable just what you need // 484 tty.c_oflag = 0; 485 486 // Reset local mode to 0. And enable just what you need // 487 tty.c_lflag = 0; 488 489 tcflush(ttyFd, TCIFLUSH); 490 tcsetattr(ttyFd, TCSANOW, &tty); 491 } 492 493 494