Home | History | Annotate | Download | only in whisper
      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