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