| // Copyright (c) 2010, Atmel Corporation. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // * Neither the name of Atmel nor the |
| // names of its contributors may be used to endorse or promote products |
| // derived from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY |
| // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <termios.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include "SA_Phys_Linux.h" |
| #include "SHA_Status.h" |
| #include "SHA_TimeUtils.h" |
| #include "Whisper_AccyMain.h" |
| |
| |
| |
| #define MAX_BUF_LEN 512 |
| #define M_ONE_BIT 0x7F |
| #define M_ZERO_BIT 0x7D |
| #define OPPBAUD B230400 |
| #define WAKEBAUD B115200 |
| |
| |
| static void configTtyParams(); |
| static int8_t setBaudRate(speed_t Inspeed); |
| static int8_t writeToDevice(uint8_t *data, uint8_t len); |
| static int8_t readFromDevice(uint8_t *readBuf, uint16_t readLen, |
| uint8_t CmdOfset, uint16_t *retBytes); |
| static int8_t sleepDevice(void); |
| static int16_t formatBytes(uint8_t *ByteData, uint8_t *ByteDataRaw, |
| int16_t lenData); |
| |
| |
| static const char ttyPort[] = "/dev/ttyHS0"; |
| static uint8_t readwriteBuf[MAX_BUF_LEN]; |
| static uint8_t WakeStr = {0x00}; |
| static uint8_t* pWake = &WakeStr; |
| static uint8_t TransmitStr = {0x88}; |
| static uint8_t* pTrm = &TransmitStr; |
| static uint8_t CmdStr = {0x77}; |
| static uint8_t* pCmd = &CmdStr; |
| static uint8_t SleepStr= {0xCC}; |
| static uint8_t InStr[41]; |
| static uint8_t* pIStr = InStr; |
| static fd_set readfs; |
| static struct termios termOptions; |
| |
| int ttyFd = -1; |
| |
| |
| /* Sets up and configures the UART for use */ |
| int8_t SHAP_OpenChannel(void) { |
| struct termios tty; |
| speed_t speed; |
| struct timespec ts; |
| |
| ttyFd = open(ttyPort, O_RDWR); |
| if (ttyFd == -1) { |
| DBG_ERROR("Error unable to open device: %s", ttyPort); |
| return SHA_COMM_FAIL; |
| } |
| |
| DBG_TRACE("%s opened with port %d", ttyPort, ttyFd); |
| if (tcflush(ttyFd, TCIOFLUSH) == 0) { |
| DBG_TRACE("The input and output queues have been flushed"); |
| } |
| else { |
| DBG_ERROR("tcflush() error"); |
| } |
| |
| configTtyParams(); |
| |
| ts.tv_sec = 0; |
| ts.tv_nsec = MAX_IO_TIMEOUT * 1000000; |
| nanosleep(&ts, NULL); |
| |
| return SHA_SUCCESS; |
| } |
| |
| |
| |
| int8_t SHAP_CloseChannel(void) { |
| int8_t ret = sleepDevice(); |
| close(ttyFd); |
| return ret; |
| } |
| |
| int8_t SHAP_SendBytes(uint8_t count, uint8_t *buffer) { |
| uint16_t bytesRead; |
| int8_t i, retVal; |
| |
| if (!count || !buffer) { |
| DBG_ERROR("Bad input"); |
| return SHA_BAD_PARAM; |
| } |
| |
| if (tcflush(ttyFd, TCIOFLUSH) == 0) { |
| DBG_TRACE("The input and output queues have been flushed"); |
| } |
| else { |
| DBG_ERROR("tcflush() error"); |
| } |
| |
| memmove(&buffer[1], buffer, count); |
| buffer[0] = CmdStr; |
| |
| writeToDevice(buffer, count+1); |
| |
| // Read the echo back ... |
| readFromDevice(readwriteBuf, 8*(count+1), 0, &bytesRead); |
| |
| if (tcflush(ttyFd, TCIFLUSH) == 0) { |
| DBG_TRACE("The input queue has been flushed"); |
| } |
| else { |
| DBG_ERROR("tcflush() error"); |
| } |
| |
| return SHA_SUCCESS; |
| } |
| |
| |
| int8_t SHAP_ReceiveBytes(uint8_t recCommLen, uint8_t *dataBuf) { |
| struct timespec ts; |
| uint16_t bytesRead; |
| int8_t i,iResVal, cmdLen = recCommLen; |
| |
| if (!recCommLen || !dataBuf) { |
| return SHA_BAD_PARAM; |
| } |
| |
| if (writeToDevice(pTrm, 1) == 1) { |
| DBG_TRACE("Test Write to %s successful", ttyPort); |
| } |
| else { |
| DBG_ERROR("Test Write to %s unsuccessful", ttyPort); |
| } |
| |
| iResVal = readFromDevice(dataBuf, (cmdLen+1)*8, 8, &bytesRead); |
| |
| if (iResVal == SHA_COMM_FAIL) { |
| DBG_ERROR("Read Error unable to read port: %d from device: %s", ttyFd, ttyPort); |
| return SHA_COMM_FAIL; |
| } |
| |
| return SHA_SUCCESS; |
| } |
| |
| |
| |
| |
| void SHAP_CloseFile(void) { |
| struct timespec ts; |
| close(ttyFd); |
| |
| ts.tv_sec = 0; |
| ts.tv_nsec = MAX_IO_TIMEOUT * 1000000; |
| nanosleep(&ts, NULL); |
| } |
| |
| |
| |
| |
| /* Reads the message from device. returns NULL if error */ |
| static int8_t readFromDevice(uint8_t *readBuf, uint16_t readLen, |
| uint8_t CmdOfset, uint16_t *retBytes) { |
| int8_t goOn = 1; |
| struct timeval Timeout; |
| uint16_t numBytesRead = 0; |
| int retVal; |
| uint16_t i; |
| |
| Timeout.tv_usec = 200000; |
| Timeout.tv_sec = 0; |
| *retBytes = 0; |
| |
| for (i = 0; i < sizeof(readwriteBuf); i++) { |
| readwriteBuf[i]= 0x00; |
| } |
| |
| while (goOn) { |
| FD_SET(ttyFd, &readfs); |
| retVal = select(ttyFd+1, &readfs, NULL, NULL, &Timeout); |
| |
| if (retVal == 0) { |
| DBG_ERROR("Timeout on select() occurred on port %d. Receive <%d> bytes", ttyFd, numBytesRead); |
| if (numBytesRead > 0) { |
| return SHA_SUCCESS; |
| } |
| else { |
| return SHA_COMM_FAIL; |
| } |
| } |
| |
| if (retVal < 0 && errno == EINTR) { |
| DBG_ERROR("SELECT returned EINTR "); |
| continue; |
| } |
| |
| if (FD_ISSET(ttyFd, &readfs)) { |
| do { |
| retVal = read(ttyFd, &readwriteBuf[numBytesRead], MAX_BUF_LEN); |
| } while (retVal < 0 && errno == EINTR); |
| |
| if (retVal > 0) { |
| numBytesRead += retVal; |
| *retBytes = numBytesRead; |
| |
| DBG_TRACE("REQ READ LEN = %d, NUM BYT READ = %d, retVal = %d offset = %d", |
| readLen, numBytesRead, retVal, CmdOfset); |
| |
| if (numBytesRead >= (readLen)) { |
| DBG_TRACE("Read Success"); |
| break; |
| } |
| } |
| } |
| else { |
| DBG_ERROR("Select Error. ERRNO = %d", errno); |
| } |
| } |
| |
| formatBytes(readBuf, &readwriteBuf[CmdOfset], readLen-CmdOfset); |
| |
| return SHA_SUCCESS; |
| } |
| |
| |
| |
| |
| /* Transmits a message to be sent over tty */ |
| static int8_t writeToDevice(uint8_t *data, uint8_t len) { |
| uint16_t i,j; |
| int nbytes, nwritten; |
| uint8_t *byteptr; |
| |
| // Every byte gets transferred into 8 bytes |
| if (len*8 > MAX_BUF_LEN) { |
| return SHA_COMM_FAIL; |
| } |
| |
| for(i = 0; i < len; i++) { |
| for(j = 0; j < 8; j++) { |
| if (data[i] & (1 << j)) { |
| readwriteBuf[(i*8)+j] = M_ONE_BIT; |
| } |
| else { |
| readwriteBuf[(i*8)+j] = M_ZERO_BIT; |
| } |
| } |
| } |
| |
| do { |
| nwritten = write(ttyFd, readwriteBuf, len*8); |
| } while (nwritten < 0 && errno == EINTR); |
| |
| if (nwritten == -1) { |
| DBG_ERROR("Write Failed with errno = %d", errno); |
| return SHA_COMM_FAIL; |
| } |
| else if (nwritten != len*8) { |
| DBG_ERROR("ERROR. write less than requested<%d>. written: %i",nwritten, len*8); |
| } |
| |
| nbytes = nwritten / 8; |
| |
| return nbytes; |
| } |
| |
| |
| |
| |
| /* Formats the data received from UART to byte data */ |
| static int16_t formatBytes(uint8_t *ByteData, uint8_t *ByteDataRaw, |
| int16_t lenData) { |
| uint16_t i,j; |
| int16_t retLen = lenData; |
| |
| for(j = 0; j < retLen/8;j++) { |
| for (i = 0; i < 8; i++) { |
| if ((ByteDataRaw[(8 *j)+i] ^ 0x7F) & 0x7C) { |
| ByteData[j] &= ~(1 << i); |
| } |
| else { |
| ByteData[j] |= (1 << i); |
| } |
| } |
| } |
| |
| return SHA_SUCCESS; |
| } |
| |
| |
| |
| /* Wakes the device */ |
| int8_t SHAP_WakeDevice(void) { |
| int iResVal; |
| struct timespec ts; |
| uint16_t bytes_read; |
| uint8_t bytes_written; |
| ssize_t osize; |
| |
| tcflush(ttyFd, TCIOFLUSH); |
| |
| // Set Start Token Speed |
| setBaudRate(WAKEBAUD); |
| |
| // Send Start Token |
| do { |
| osize = write(ttyFd, pWake, 1); |
| } while (osize < 0 && errno == EINTR); |
| |
| if (osize == -1) { |
| DBG_ERROR("Write Failed with errno = %d", errno); |
| return SHA_COMM_FAIL; |
| } |
| |
| ts.tv_sec = 0; |
| ts.tv_nsec = 3000000; |
| nanosleep(&ts, NULL); |
| |
| // set the Baud Rate to Comm speed |
| setBaudRate(OPPBAUD); |
| if (writeToDevice(pTrm, 1) == 1) { |
| DBG_TRACE("Wakeup Write to %s successful", ttyPort); |
| } |
| else { |
| DBG_TRACE("Wakeup Write to %s unsuccessful", ttyPort); |
| } |
| |
| |
| iResVal = readFromDevice(pIStr, 41, 9, &bytes_read); |
| |
| if (iResVal == SHA_COMM_FAIL || bytes_read < 41) { |
| sleepDevice(); |
| DBG_ERROR("WakeUp Error unable to read port: %d, Bytes Read = %d", ttyFd, bytes_read); |
| return SHA_COMM_FAIL; |
| } |
| |
| if (tcflush(ttyFd, TCIOFLUSH) == 0) { |
| DBG_TRACE("The input and output queues have been flushed."); |
| } |
| else { |
| DBG_ERROR("tcflush() error"); |
| } |
| |
| if (pIStr[0] == 0x04 && pIStr[1] == 0x11) { |
| DBG_TRACE("WakeUp Done"); |
| return SHA_SUCCESS; |
| } |
| else { |
| DBG_ERROR("WakeUp Fail"); |
| sleepDevice(); |
| return SHA_CMD_FAIL; |
| } |
| } |
| |
| |
| |
| /** |
| * Transmits a message to be sent over tty |
| * |
| * \param[out] Success or fail flag |
| * \return status of the operation |
| */ |
| static int8_t sleepDevice(void) |
| { |
| uint8_t *byteptr = &SleepStr; |
| ssize_t osize; |
| struct timespec ts; |
| do { |
| osize = write(ttyFd, byteptr, 1); |
| } while (osize < 0 && errno == EINTR); |
| |
| if (osize == -1) { |
| DBG_ERROR("Write Failed errno = %d", errno); |
| return SHA_COMM_FAIL; |
| } |
| ts.tv_sec = 0; |
| ts.tv_nsec = 100000000; // sleep for 100ms |
| nanosleep(&ts, NULL); |
| |
| return SHA_SUCCESS; |
| } |
| |
| void SA_Delay(uint32_t delay) |
| { |
| struct timespec ts; |
| |
| ts.tv_sec = 0; |
| ts.tv_nsec = delay*1000; // convert us to ns |
| nanosleep(&ts, NULL); |
| } |
| |
| /* Sets the baudrate of the tty port */ |
| static int8_t setBaudRate(speed_t Inspeed) { |
| int8_t ret; |
| |
| ret = tcgetattr( ttyFd, &termOptions ); |
| |
| if (ret == -1) { |
| DBG_ERROR("Error returned by tcgetattr. errno = %d", errno); |
| return SHA_COMM_FAIL; |
| } |
| |
| cfsetospeed(&termOptions, Inspeed); |
| cfsetispeed(&termOptions, Inspeed); |
| ret = tcsetattr(ttyFd, TCSANOW, &termOptions ); |
| if (ret == -1) { |
| DBG_ERROR("Error returned by tcsetattr. errno = %d", errno); |
| return SHA_COMM_FAIL; |
| } |
| |
| return SHA_SUCCESS; |
| } |
| |
| static void configTtyParams() |
| { |
| |
| struct termios tty; |
| |
| // Get the existing options // |
| tcgetattr(ttyFd, &tty); |
| |
| // Reset Control mode to 0. And enable just what you need // |
| tty.c_cflag = 0; |
| tty.c_cflag |= CLOCAL; // ignore modem control lines // |
| tty.c_cflag |= CREAD; // enable receiver |
| tty.c_cflag |= CS7; // use 7 data bits |
| tty.c_cflag &= ~CRTSCTS; // do not use RTS and CTS handshake |
| |
| tty.c_iflag = INPCK; |
| tty.c_cc[VINTR] = 0; // Ctrl-c |
| tty.c_cc[VQUIT] = 0; /* Ctrl-\ */ |
| tty.c_cc[VERASE] = 0; // del |
| tty.c_cc[VKILL] = 0; // @ |
| tty.c_cc[VEOF] = 0; // Ctrl-d |
| tty.c_cc[VTIME] = 0; /// inter-character timer unused |
| tty.c_cc[VMIN] = 1; // blocking read until 1 character arrives |
| tty.c_cc[VSWTC] = 0; // '\0' |
| tty.c_cc[VSTART] = 0; // Ctrl-q |
| tty.c_cc[VSTOP] = 0; // Ctrl-s |
| tty.c_cc[VSUSP] = 0; // Ctrl-z |
| tty.c_cc[VEOL] = 0; // '\0' |
| tty.c_cc[VREPRINT] = 0; // Ctrl-r |
| tty.c_cc[VDISCARD] = 0; // Ctrl-u |
| tty.c_cc[VWERASE] = 0; // Ctrl-w |
| tty.c_cc[VLNEXT] = 0; // Ctrl-v |
| tty.c_cc[VEOL2] = 0; // '\0' |
| |
| |
| // Reset Input mode to 0. And enalbe just what you need // |
| tty.c_iflag = 0; |
| tty.c_iflag |= IGNBRK; |
| tty.c_iflag |= INPCK; // Enable input parity checking // |
| |
| |
| // Reset output mode to 0. And enable just what you need // |
| tty.c_oflag = 0; |
| |
| // Reset local mode to 0. And enable just what you need // |
| tty.c_lflag = 0; |
| |
| tcflush(ttyFd, TCIFLUSH); |
| tcsetattr(ttyFd, TCSANOW, &tty); |
| } |
| |
| |