• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 NXP Semiconductors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * \file phDalNfc_uart.c
19  * \brief DAL com port implementation for linux
20  *
21  * Project: Trusted NFC Linux Lignt
22  *
23  * $Date: 07 aug 2009
24  * $Author: Jonathan roux
25  * $Revision: 1.0 $
26  *
27  */
28 
29 #define LOG_TAG "NFC_uart"
30 #include <cutils/log.h>
31 
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <termios.h>
36 #include <errno.h>
37 #include <sys/ioctl.h>
38 #include <sys/select.h>
39 #include <stdio.h>
40 #include <errno.h>
41 
42 #include <phDal4Nfc_debug.h>
43 #include <phDal4Nfc_uart.h>
44 #include <phOsalNfc.h>
45 #include <phNfcStatus.h>
46 #if defined(ANDROID)
47 #include <string.h>
48 #include <cutils/properties.h> // for property_get
49 #endif
50 
51 typedef struct
52 {
53    int  nHandle;
54    char nOpened;
55    struct termios nIoConfigBackup;
56    struct termios nIoConfig;
57 
58 } phDal4Nfc_ComPortContext_t;
59 
60 /*-----------------------------------------------------------------------------------
61                                 COM PORT CONFIGURATION
62 ------------------------------------------------------------------------------------*/
63 #define DAL_BAUD_RATE  B115200
64 
65 
66 
67 /*-----------------------------------------------------------------------------------
68                                       VARIABLES
69 ------------------------------------------------------------------------------------*/
70 static phDal4Nfc_ComPortContext_t gComPortContext;
71 
72 
73 
74 /*-----------------------------------------------------------------------------
75 
76 FUNCTION: phDal4Nfc_uart_set_open_from_handle
77 
78 PURPOSE:  Initialize internal variables
79 
80 -----------------------------------------------------------------------------*/
81 
phDal4Nfc_uart_initialize(void)82 void phDal4Nfc_uart_initialize(void)
83 {
84    memset(&gComPortContext, 0, sizeof(phDal4Nfc_ComPortContext_t));
85 }
86 
87 
88 /*-----------------------------------------------------------------------------
89 
90 FUNCTION: phDal4Nfc_uart_set_open_from_handle
91 
92 PURPOSE:  The application could have opened the link itself. So we just need
93           to get the handle and consider that the open operation has already
94           been done.
95 
96 -----------------------------------------------------------------------------*/
97 
phDal4Nfc_uart_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)98 void phDal4Nfc_uart_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)
99 {
100    gComPortContext.nHandle = (int) pDalHwContext->p_board_driver;
101    DAL_ASSERT_STR(gComPortContext.nHandle >= 0, "Bad passed com port handle");
102    gComPortContext.nOpened = 1;
103 }
104 
105 /*-----------------------------------------------------------------------------
106 
107 FUNCTION: phDal4Nfc_uart_is_opened
108 
109 PURPOSE:  Returns if the link is opened or not. (0 = not opened; 1 = opened)
110 
111 -----------------------------------------------------------------------------*/
112 
phDal4Nfc_uart_is_opened(void)113 int phDal4Nfc_uart_is_opened(void)
114 {
115    return gComPortContext.nOpened;
116 }
117 
118 /*-----------------------------------------------------------------------------
119 
120 FUNCTION: phDal4Nfc_uart_flush
121 
122 PURPOSE:  Flushes the link ; clears the link buffers
123 
124 -----------------------------------------------------------------------------*/
125 
phDal4Nfc_uart_flush(void)126 void phDal4Nfc_uart_flush(void)
127 {
128    int ret;
129    /* flushes the com port */
130    ret = tcflush(gComPortContext.nHandle, TCIFLUSH);
131    DAL_ASSERT_STR(ret!=-1, "tcflush failed");
132 }
133 
134 /*-----------------------------------------------------------------------------
135 
136 FUNCTION: phDal4Nfc_uart_close
137 
138 PURPOSE:  Closes the link
139 
140 -----------------------------------------------------------------------------*/
141 
phDal4Nfc_uart_close(void)142 void phDal4Nfc_uart_close(void)
143 {
144    if (gComPortContext.nOpened == 1)
145    {
146       close(gComPortContext.nHandle);
147       gComPortContext.nHandle = 0;
148       gComPortContext.nOpened = 0;
149    }
150 }
151 
152 /*-----------------------------------------------------------------------------
153 
154 FUNCTION: phDal4Nfc_uart_close
155 
156 PURPOSE:  Closes the link
157 
158 -----------------------------------------------------------------------------*/
159 
phDal4Nfc_uart_open_and_configure(pphDal4Nfc_sConfig_t pConfig,void ** pLinkHandle)160 NFCSTATUS phDal4Nfc_uart_open_and_configure(pphDal4Nfc_sConfig_t pConfig, void ** pLinkHandle)
161 {
162    char *       pComPort;
163    int          nComStatus;
164    NFCSTATUS    nfcret = NFCSTATUS_SUCCESS;
165    int          ret;
166 
167    DAL_ASSERT_STR(gComPortContext.nOpened==0, "Trying to open but already done!");
168 
169    srand(time(NULL));
170 
171    switch(pConfig->nLinkType)
172    {
173      case ENUM_DAL_LINK_TYPE_COM1:
174       pComPort = "/dev/ttyO0";
175       break;
176      case ENUM_DAL_LINK_TYPE_COM2:
177       pComPort = "/dev/ttyO1";
178       break;
179      case ENUM_DAL_LINK_TYPE_COM3:
180       pComPort = "/dev/ttyO2";
181       break;
182      case ENUM_DAL_LINK_TYPE_COM4:
183       pComPort = "/dev/ttyO3";
184       break;
185      case ENUM_DAL_LINK_TYPE_COM5:
186       pComPort = "/dev/ttyO4";
187       break;
188      case ENUM_DAL_LINK_TYPE_COM6:
189       pComPort = "/dev/ttyO5";
190       break;
191      case ENUM_DAL_LINK_TYPE_COM7:
192       pComPort = "/dev/ttyO6";
193       break;
194      case ENUM_DAL_LINK_TYPE_COM8:
195       pComPort = "/dev/ttyO7";
196       break;
197      case ENUM_DAL_LINK_TYPE_USB:
198       pComPort = "/dev/ttyUSB0";
199       break;
200      default:
201       return NFCSTATUS_INVALID_PARAMETER;
202    }
203 
204    /* open communication port handle */
205    gComPortContext.nHandle = open(pComPort, O_RDWR | O_NOCTTY);
206    if (gComPortContext.nHandle < 0)
207    {
208       *pLinkHandle = NULL;
209       return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
210    }
211 
212    gComPortContext.nOpened = 1;
213    *pLinkHandle = (void*)gComPortContext.nHandle;
214 
215    /*
216     *  Now configure the com port
217     */
218    ret = tcgetattr(gComPortContext.nHandle, &gComPortContext.nIoConfigBackup); /* save the old io config */
219    if (ret == -1)
220    {
221       /* tcgetattr failed -- it is likely that the provided port is invalid */
222       *pLinkHandle = NULL;
223       return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
224    }
225    ret = fcntl(gComPortContext.nHandle, F_SETFL, 0); /* Makes the read blocking (default).  */
226    DAL_ASSERT_STR(ret != -1, "fcntl failed");
227    /* Configures the io */
228    memset((void *)&gComPortContext.nIoConfig, (int)0, (size_t)sizeof(struct termios));
229    /*
230     BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
231     CRTSCTS : output hardware flow control (only used if the cable has
232               all necessary lines. See sect. 7 of Serial-HOWTO)
233     CS8     : 8n1 (8bit,no parity,1 stopbit)
234     CLOCAL  : local connection, no modem contol
235     CREAD   : enable receiving characters
236    */
237    gComPortContext.nIoConfig.c_cflag = DAL_BAUD_RATE | CS8 | CLOCAL | CREAD;  /* Control mode flags */
238    gComPortContext.nIoConfig.c_iflag = IGNPAR;                                          /* Input   mode flags : IGNPAR  Ignore parity errors */
239    gComPortContext.nIoConfig.c_oflag = 0;                                               /* Output  mode flags */
240    gComPortContext.nIoConfig.c_lflag = 0;                                               /* Local   mode flags. Read mode : non canonical, no echo */
241    gComPortContext.nIoConfig.c_cc[VTIME] = 0;                                           /* Control characters. No inter-character timer */
242    gComPortContext.nIoConfig.c_cc[VMIN]  = 1;                                           /* Control characters. Read is blocking until X characters are read */
243 
244    /*
245       TCSANOW  Make changes now without waiting for data to complete
246       TCSADRAIN   Wait until everything has been transmitted
247       TCSAFLUSH   Flush input and output buffers and make the change
248    */
249    ret = tcsetattr(gComPortContext.nHandle, TCSANOW, &gComPortContext.nIoConfig);
250    DAL_ASSERT_STR(ret != -1, "tcsetattr failed");
251 
252    /*
253       On linux the DTR signal is set by default. That causes a problem for pn544 chip
254       because this signal is connected to "reset". So we clear it. (on windows it is cleared by default).
255    */
256    ret = ioctl(gComPortContext.nHandle, TIOCMGET, &nComStatus);
257    DAL_ASSERT_STR(ret != -1, "ioctl TIOCMGET failed");
258    nComStatus &= ~TIOCM_DTR;
259    ret = ioctl(gComPortContext.nHandle, TIOCMSET, &nComStatus);
260    DAL_ASSERT_STR(ret != -1, "ioctl TIOCMSET failed");
261    DAL_DEBUG("Com port status=%d\n", nComStatus);
262    usleep(10000); /* Mandatory sleep so that the DTR line is ready before continuing */
263 
264    return nfcret;
265 }
266 
267 /*
268   adb shell setprop debug.nfc.UART_ERROR_RATE X
269   will corrupt and drop bytes in uart_read(), to test the error handling
270   of DAL & LLC errors.
271  */
272 int property_error_rate = 0;
read_property()273 static void read_property() {
274     char value[PROPERTY_VALUE_MAX];
275     property_get("debug.nfc.UART_ERROR_RATE", value, "0");
276     property_error_rate = atoi(value);
277 }
278 
279 /* returns length of buffer after errors */
apply_errors(uint8_t * buffer,int length)280 static int apply_errors(uint8_t *buffer, int length) {
281     int i;
282     if (!property_error_rate) return length;
283 
284     for (i = 0; i < length; i++) {
285         if (rand() % 1000 < property_error_rate) {
286             if (rand() % 2) {
287                 // 50% chance of dropping byte
288                 length--;
289                 memcpy(&buffer[i], &buffer[i+1], length-i);
290                 LOGW("dropped byte %d", i);
291             } else {
292                 // 50% chance of corruption
293                 buffer[i] = (uint8_t)rand();
294                 LOGW("corrupted byte %d", i);
295             }
296         }
297     }
298     return length;
299 }
300 
timeval_remaining(struct timespec timeout)301 static struct timeval timeval_remaining(struct timespec timeout) {
302     struct timespec now;
303     struct timeval delta;
304     clock_gettime(CLOCK_MONOTONIC, &now);
305 
306     delta.tv_sec = timeout.tv_sec - now.tv_sec;
307     delta.tv_usec = (timeout.tv_nsec - now.tv_nsec) / (long)1000;
308 
309     if (delta.tv_usec < 0) {
310         delta.tv_usec += 1000000;
311         delta.tv_sec--;
312     }
313     if (delta.tv_sec < 0) {
314         delta.tv_sec = 0;
315         delta.tv_usec = 0;
316     }
317     return delta;
318 }
319 
320 static int libnfc_firmware_mode = 0;
321 
322 /*-----------------------------------------------------------------------------
323 
324 FUNCTION: phDal4Nfc_uart_read
325 
326 PURPOSE:  Reads nNbBytesToRead bytes and writes them in pBuffer.
327           Returns the number of bytes really read or -1 in case of error.
328 
329 -----------------------------------------------------------------------------*/
phDal4Nfc_uart_read(uint8_t * pBuffer,int nNbBytesToRead)330 int phDal4Nfc_uart_read(uint8_t * pBuffer, int nNbBytesToRead)
331 {
332     int ret;
333     int numRead = 0;
334     struct timeval tv;
335     struct timeval *ptv;
336     struct timespec timeout;
337     fd_set rfds;
338 
339     DAL_ASSERT_STR(gComPortContext.nOpened == 1, "read called but not opened!");
340     DAL_DEBUG("_uart_read() called to read %d bytes", nNbBytesToRead);
341 
342     read_property();
343 
344     // Read timeout:
345     // FW mode: 10s timeout
346     // 1 byte read: steady-state LLC length read, allowed to block forever
347     // >1 byte read: LLC payload, 100ms timeout (before pn544 re-transmit)
348     if (nNbBytesToRead > 1 && !libnfc_firmware_mode) {
349         clock_gettime(CLOCK_MONOTONIC, &timeout);
350         timeout.tv_nsec += 100000000;
351         if (timeout.tv_nsec > 1000000000) {
352             timeout.tv_sec++;
353             timeout.tv_nsec -= 1000000000;
354         }
355         ptv = &tv;
356     } else if (libnfc_firmware_mode) {
357         clock_gettime(CLOCK_MONOTONIC, &timeout);
358         timeout.tv_sec += 10;
359         ptv = &tv;
360     } else {
361         ptv = NULL;
362     }
363 
364     while (numRead < nNbBytesToRead) {
365        FD_ZERO(&rfds);
366        FD_SET(gComPortContext.nHandle, &rfds);
367 
368        if (ptv) {
369           tv = timeval_remaining(timeout);
370           ptv = &tv;
371        }
372 
373        ret = select(gComPortContext.nHandle + 1, &rfds, NULL, NULL, ptv);
374        if (ret < 0) {
375            DAL_DEBUG("select() errno=%d", errno);
376            if (errno == EINTR || errno == EAGAIN) {
377                continue;
378            }
379            return -1;
380        } else if (ret == 0) {
381            LOGW("timeout!");
382            break;  // return partial response
383        }
384        ret = read(gComPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead);
385        if (ret > 0) {
386            ret = apply_errors(pBuffer + numRead, ret);
387 
388            DAL_DEBUG("read %d bytes", ret);
389            numRead += ret;
390        } else if (ret == 0) {
391            DAL_PRINT("_uart_read() EOF");
392            return 0;
393        } else {
394            DAL_DEBUG("_uart_read() errno=%d", errno);
395            if (errno == EINTR || errno == EAGAIN) {
396                continue;
397            }
398            return -1;
399        }
400     }
401 
402     return numRead;
403 }
404 
405 /*-----------------------------------------------------------------------------
406 
407 FUNCTION: phDal4Nfc_link_write
408 
409 PURPOSE:  Writes nNbBytesToWrite bytes from pBuffer to the link
410           Returns the number of bytes that have been wrote to the interface or -1 in case of error.
411 
412 -----------------------------------------------------------------------------*/
413 
phDal4Nfc_uart_write(uint8_t * pBuffer,int nNbBytesToWrite)414 int phDal4Nfc_uart_write(uint8_t * pBuffer, int nNbBytesToWrite)
415 {
416     int ret;
417     int numWrote = 0;
418 
419     DAL_ASSERT_STR(gComPortContext.nOpened == 1, "write called but not opened!");
420     DAL_DEBUG("_uart_write() called to write %d bytes\n", nNbBytesToWrite);
421 
422     while (numWrote < nNbBytesToWrite) {
423         ret = write(gComPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote);
424         if (ret > 0) {
425             DAL_DEBUG("wrote %d bytes", ret);
426             numWrote += ret;
427         } else if (ret == 0) {
428             DAL_PRINT("_uart_write() EOF");
429             return -1;
430         } else {
431             DAL_DEBUG("_uart_write() errno=%d", errno);
432             if (errno == EINTR || errno == EAGAIN) {
433                 continue;
434             }
435             return -1;
436         }
437     }
438 
439     return numWrote;
440 }
441 
442 /*-----------------------------------------------------------------------------
443 
444 FUNCTION: phDal4Nfc_uart_reset
445 
446 PURPOSE:  Reset the PN544, using the VEN pin
447 
448 -----------------------------------------------------------------------------*/
phDal4Nfc_uart_reset(long level)449 int phDal4Nfc_uart_reset(long level)
450 {
451     static const char NFC_POWER_PATH[] = "/sys/devices/platform/nfc-power/nfc_power";
452     int sz;
453     int fd = -1;
454     int ret = NFCSTATUS_FAILED;
455     char buffer[2];
456 
457     DAL_DEBUG("phDal4Nfc_uart_reset, VEN level = %ld", level);
458 
459     if (snprintf(buffer, sizeof(buffer), "%u", (unsigned int)level) != 1) {
460         LOGE("Bad nfc power level (%u)", (unsigned int)level);
461         goto out;
462     }
463 
464     fd = open(NFC_POWER_PATH, O_WRONLY);
465     if (fd < 0) {
466         LOGE("open(%s) for write failed: %s (%d)", NFC_POWER_PATH,
467                 strerror(errno), errno);
468         goto out;
469     }
470     sz = write(fd, &buffer, sizeof(buffer) - 1);
471     if (sz < 0) {
472         LOGE("write(%s) failed: %s (%d)", NFC_POWER_PATH, strerror(errno),
473              errno);
474         goto out;
475     }
476     ret = NFCSTATUS_SUCCESS;
477     if (level == 2) {
478         libnfc_firmware_mode = 1;
479     } else {
480         libnfc_firmware_mode = 0;
481     }
482 
483 out:
484     if (fd >= 0) {
485         close(fd);
486     }
487     return ret;
488 }
489