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 #include <hardware/nfc.h>
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)(intptr_t) 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    int          nComStatus;
163    NFCSTATUS    nfcret = NFCSTATUS_SUCCESS;
164    int          ret;
165 
166    DAL_ASSERT_STR(gComPortContext.nOpened==0, "Trying to open but already done!");
167 
168    srand(time(NULL));
169 
170    /* open communication port handle */
171    gComPortContext.nHandle = open(pConfig->deviceNode, O_RDWR | O_NOCTTY);
172    if (gComPortContext.nHandle < 0)
173    {
174       *pLinkHandle = NULL;
175       return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
176    }
177 
178    gComPortContext.nOpened = 1;
179    *pLinkHandle = (void*)(intptr_t)gComPortContext.nHandle;
180 
181    /*
182     *  Now configure the com port
183     */
184    ret = tcgetattr(gComPortContext.nHandle, &gComPortContext.nIoConfigBackup); /* save the old io config */
185    if (ret == -1)
186    {
187       /* tcgetattr failed -- it is likely that the provided port is invalid */
188       *pLinkHandle = NULL;
189       return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
190    }
191    ret = fcntl(gComPortContext.nHandle, F_SETFL, 0); /* Makes the read blocking (default).  */
192    DAL_ASSERT_STR(ret != -1, "fcntl failed");
193    /* Configures the io */
194    memset((void *)&gComPortContext.nIoConfig, (int)0, (size_t)sizeof(struct termios));
195    /*
196     BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
197     CRTSCTS : output hardware flow control (only used if the cable has
198               all necessary lines. See sect. 7 of Serial-HOWTO)
199     CS8     : 8n1 (8bit,no parity,1 stopbit)
200     CLOCAL  : local connection, no modem contol
201     CREAD   : enable receiving characters
202    */
203    gComPortContext.nIoConfig.c_cflag = DAL_BAUD_RATE | CS8 | CLOCAL | CREAD;  /* Control mode flags */
204    gComPortContext.nIoConfig.c_iflag = IGNPAR;                                          /* Input   mode flags : IGNPAR  Ignore parity errors */
205    gComPortContext.nIoConfig.c_oflag = 0;                                               /* Output  mode flags */
206    gComPortContext.nIoConfig.c_lflag = 0;                                               /* Local   mode flags. Read mode : non canonical, no echo */
207    gComPortContext.nIoConfig.c_cc[VTIME] = 0;                                           /* Control characters. No inter-character timer */
208    gComPortContext.nIoConfig.c_cc[VMIN]  = 1;                                           /* Control characters. Read is blocking until X characters are read */
209 
210    /*
211       TCSANOW  Make changes now without waiting for data to complete
212       TCSADRAIN   Wait until everything has been transmitted
213       TCSAFLUSH   Flush input and output buffers and make the change
214    */
215    ret = tcsetattr(gComPortContext.nHandle, TCSANOW, &gComPortContext.nIoConfig);
216    DAL_ASSERT_STR(ret != -1, "tcsetattr failed");
217 
218    /*
219       On linux the DTR signal is set by default. That causes a problem for pn544 chip
220       because this signal is connected to "reset". So we clear it. (on windows it is cleared by default).
221    */
222    ret = ioctl(gComPortContext.nHandle, TIOCMGET, &nComStatus);
223    DAL_ASSERT_STR(ret != -1, "ioctl TIOCMGET failed");
224    nComStatus &= ~TIOCM_DTR;
225    ret = ioctl(gComPortContext.nHandle, TIOCMSET, &nComStatus);
226    DAL_ASSERT_STR(ret != -1, "ioctl TIOCMSET failed");
227    DAL_DEBUG("Com port status=%d\n", nComStatus);
228    usleep(10000); /* Mandatory sleep so that the DTR line is ready before continuing */
229 
230    return nfcret;
231 }
232 
233 /*
234   adb shell setprop debug.nfc.UART_ERROR_RATE X
235   will corrupt and drop bytes in uart_read(), to test the error handling
236   of DAL & LLC errors.
237  */
238 int property_error_rate = 0;
read_property()239 static void read_property() {
240     char value[PROPERTY_VALUE_MAX];
241     property_get("debug.nfc.UART_ERROR_RATE", value, "0");
242     property_error_rate = atoi(value);
243 }
244 
245 /* returns length of buffer after errors */
apply_errors(uint8_t * buffer,int length)246 static int apply_errors(uint8_t *buffer, int length) {
247     int i;
248     if (!property_error_rate) return length;
249 
250     for (i = 0; i < length; i++) {
251         if (rand() % 1000 < property_error_rate) {
252             if (rand() % 2) {
253                 // 50% chance of dropping byte
254                 length--;
255                 memcpy(&buffer[i], &buffer[i+1], length-i);
256                 ALOGW("dropped byte %d", i);
257             } else {
258                 // 50% chance of corruption
259                 buffer[i] = (uint8_t)rand();
260                 ALOGW("corrupted byte %d", i);
261             }
262         }
263     }
264     return length;
265 }
266 
timeval_remaining(struct timespec timeout)267 static struct timeval timeval_remaining(struct timespec timeout) {
268     struct timespec now;
269     struct timeval delta;
270     clock_gettime(CLOCK_MONOTONIC, &now);
271 
272     delta.tv_sec = timeout.tv_sec - now.tv_sec;
273     delta.tv_usec = (timeout.tv_nsec - now.tv_nsec) / (long)1000;
274 
275     if (delta.tv_usec < 0) {
276         delta.tv_usec += 1000000;
277         delta.tv_sec--;
278     }
279     if (delta.tv_sec < 0) {
280         delta.tv_sec = 0;
281         delta.tv_usec = 0;
282     }
283     return delta;
284 }
285 
286 static int libnfc_firmware_mode = 0;
287 
288 /*-----------------------------------------------------------------------------
289 
290 FUNCTION: phDal4Nfc_uart_read
291 
292 PURPOSE:  Reads nNbBytesToRead bytes and writes them in pBuffer.
293           Returns the number of bytes really read or -1 in case of error.
294 
295 -----------------------------------------------------------------------------*/
phDal4Nfc_uart_read(uint8_t * pBuffer,int nNbBytesToRead)296 int phDal4Nfc_uart_read(uint8_t * pBuffer, int nNbBytesToRead)
297 {
298     int ret;
299     int numRead = 0;
300     struct timeval tv;
301     struct timeval *ptv;
302     struct timespec timeout;
303     fd_set rfds;
304 
305     DAL_ASSERT_STR(gComPortContext.nOpened == 1, "read called but not opened!");
306     DAL_DEBUG("_uart_read() called to read %d bytes", nNbBytesToRead);
307 
308     read_property();
309 
310     // Read timeout:
311     // FW mode: 10s timeout
312     // 1 byte read: steady-state LLC length read, allowed to block forever
313     // >1 byte read: LLC payload, 100ms timeout (before pn544 re-transmit)
314     if (nNbBytesToRead > 1 && !libnfc_firmware_mode) {
315         clock_gettime(CLOCK_MONOTONIC, &timeout);
316         timeout.tv_nsec += 100000000;
317         if (timeout.tv_nsec > 1000000000) {
318             timeout.tv_sec++;
319             timeout.tv_nsec -= 1000000000;
320         }
321         ptv = &tv;
322     } else if (libnfc_firmware_mode) {
323         clock_gettime(CLOCK_MONOTONIC, &timeout);
324         timeout.tv_sec += 10;
325         ptv = &tv;
326     } else {
327         ptv = NULL;
328     }
329 
330     while (numRead < nNbBytesToRead) {
331        FD_ZERO(&rfds);
332        FD_SET(gComPortContext.nHandle, &rfds);
333 
334        if (ptv) {
335           tv = timeval_remaining(timeout);
336           ptv = &tv;
337        }
338 
339        ret = select(gComPortContext.nHandle + 1, &rfds, NULL, NULL, ptv);
340        if (ret < 0) {
341            DAL_DEBUG("select() errno=%d", errno);
342            if (errno == EINTR || errno == EAGAIN) {
343                continue;
344            }
345            return -1;
346        } else if (ret == 0) {
347            ALOGW("timeout!");
348            break;  // return partial response
349        }
350        ret = read(gComPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead);
351        if (ret > 0) {
352            ret = apply_errors(pBuffer + numRead, ret);
353 
354            DAL_DEBUG("read %d bytes", ret);
355            numRead += ret;
356        } else if (ret == 0) {
357            DAL_PRINT("_uart_read() EOF");
358            return 0;
359        } else {
360            DAL_DEBUG("_uart_read() errno=%d", errno);
361            if (errno == EINTR || errno == EAGAIN) {
362                continue;
363            }
364            return -1;
365        }
366     }
367 
368     return numRead;
369 }
370 
371 /*-----------------------------------------------------------------------------
372 
373 FUNCTION: phDal4Nfc_link_write
374 
375 PURPOSE:  Writes nNbBytesToWrite bytes from pBuffer to the link
376           Returns the number of bytes that have been wrote to the interface or -1 in case of error.
377 
378 -----------------------------------------------------------------------------*/
379 
phDal4Nfc_uart_write(uint8_t * pBuffer,int nNbBytesToWrite)380 int phDal4Nfc_uart_write(uint8_t * pBuffer, int nNbBytesToWrite)
381 {
382     int ret;
383     int numWrote = 0;
384 
385     DAL_ASSERT_STR(gComPortContext.nOpened == 1, "write called but not opened!");
386     DAL_DEBUG("_uart_write() called to write %d bytes\n", nNbBytesToWrite);
387 
388     while (numWrote < nNbBytesToWrite) {
389         ret = write(gComPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote);
390         if (ret > 0) {
391             DAL_DEBUG("wrote %d bytes", ret);
392             numWrote += ret;
393         } else if (ret == 0) {
394             DAL_PRINT("_uart_write() EOF");
395             return -1;
396         } else {
397             DAL_DEBUG("_uart_write() errno=%d", errno);
398             if (errno == EINTR || errno == EAGAIN) {
399                 continue;
400             }
401             return -1;
402         }
403     }
404 
405     return numWrote;
406 }
407 
408 /*-----------------------------------------------------------------------------
409 
410 FUNCTION: phDal4Nfc_uart_reset
411 
412 PURPOSE:  Reset the PN544, using the VEN pin
413 
414 -----------------------------------------------------------------------------*/
phDal4Nfc_uart_reset(long level)415 int phDal4Nfc_uart_reset(long level)
416 {
417     static const char NFC_POWER_PATH[] = "/sys/devices/platform/nfc-power/nfc_power";
418     int sz;
419     int fd = -1;
420     int ret = NFCSTATUS_FAILED;
421     char buffer[2];
422 
423     DAL_DEBUG("phDal4Nfc_uart_reset, VEN level = %ld", level);
424 
425     if (snprintf(buffer, sizeof(buffer), "%u", (unsigned int)level) != 1) {
426         ALOGE("Bad nfc power level (%u)", (unsigned int)level);
427         goto out;
428     }
429 
430     fd = open(NFC_POWER_PATH, O_WRONLY);
431     if (fd < 0) {
432         ALOGE("open(%s) for write failed: %s (%d)", NFC_POWER_PATH,
433                 strerror(errno), errno);
434         goto out;
435     }
436     sz = write(fd, &buffer, sizeof(buffer) - 1);
437     if (sz < 0) {
438         ALOGE("write(%s) failed: %s (%d)", NFC_POWER_PATH, strerror(errno),
439              errno);
440         goto out;
441     }
442     ret = NFCSTATUS_SUCCESS;
443     if (level == 2) {
444         libnfc_firmware_mode = 1;
445     } else {
446         libnfc_firmware_mode = 0;
447     }
448 
449 out:
450     if (fd >= 0) {
451         close(fd);
452     }
453     return ret;
454 }
455