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