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
49static void configTtyParams();
50static int8_t setBaudRate(speed_t Inspeed);
51static int8_t writeToDevice(uint8_t *data, uint8_t len);
52static int8_t readFromDevice(uint8_t *readBuf, uint16_t readLen,
53                             uint8_t CmdOfset, uint16_t *retBytes);
54static int8_t sleepDevice(void);
55static int16_t formatBytes(uint8_t *ByteData, uint8_t *ByteDataRaw,
56                           int16_t lenData);
57
58
59static const char ttyPort[] = "/dev/ttyHS0";
60static uint8_t readwriteBuf[MAX_BUF_LEN];
61static uint8_t WakeStr = {0x00};
62static uint8_t* pWake = &WakeStr;
63static uint8_t TransmitStr = {0x88};
64static uint8_t* pTrm = &TransmitStr;
65static uint8_t CmdStr = {0x77};
66static uint8_t* pCmd = &CmdStr;
67static uint8_t SleepStr= {0xCC};
68static uint8_t InStr[41];
69static uint8_t* pIStr = InStr;
70static fd_set readfs;
71static struct termios termOptions;
72
73int ttyFd = -1;
74
75
76 /*  Sets up and configures the UART for use */
77int8_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
107int8_t SHAP_CloseChannel(void) {
108    int8_t ret = sleepDevice();
109    close(ttyFd);
110    return ret;
111}
112
113int8_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
148int8_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
177void 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 */
190static 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 */
257static 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 */
299static 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 */
321int8_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 */
391static 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
411void 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 */
421static 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
442static 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