2 * Copyright (C) 2010 NXP Semiconductors
3 * Copyright (C) 2012 Samsung Elevtronics Co., Ltd
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * \file phDalNfc_uart.c
20 * \brief DAL com port implementation for linux
22 * Project: Trusted NFC Linux Lignt
25 * $Author: Jonathan roux
29 #define LOG_TAG "NFC_uart"
35 #include <sys/ioctl.h>
36 #include <sys/select.h>
40 #include <phDal4Nfc_debug.h>
41 #include <phDal4Nfc_uart.h>
42 #include <phOsalNfc.h>
43 #include <phNfcStatus.h>
52 struct termios nIoConfigBackup;
53 struct termios nIoConfig;
55 } phDal4Nfc_ComPortContext_t;
57 /*-----------------------------------------------------------------------------------
58 COM PORT CONFIGURATION
59 ------------------------------------------------------------------------------------*/
60 #define DAL_BAUD_RATE B115200
64 /*-----------------------------------------------------------------------------------
66 ------------------------------------------------------------------------------------*/
67 static phDal4Nfc_ComPortContext_t gComPortContext;
71 /*-----------------------------------------------------------------------------
73 FUNCTION: phDal4Nfc_uart_set_open_from_handle
75 PURPOSE: Initialize internal variables
77 -----------------------------------------------------------------------------*/
79 void phDal4Nfc_uart_initialize(void)
81 memset(&gComPortContext, 0, sizeof(phDal4Nfc_ComPortContext_t));
85 /*-----------------------------------------------------------------------------
87 FUNCTION: phDal4Nfc_uart_set_open_from_handle
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
93 -----------------------------------------------------------------------------*/
95 void phDal4Nfc_uart_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)
97 gComPortContext.nHandle = (int) pDalHwContext->p_board_driver;
98 DAL_ASSERT_STR(gComPortContext.nHandle >= 0, "Bad passed com port handle");
99 gComPortContext.nOpened = 1;
102 /*-----------------------------------------------------------------------------
104 FUNCTION: phDal4Nfc_uart_is_opened
106 PURPOSE: Returns if the link is opened or not. (0 = not opened; 1 = opened)
108 -----------------------------------------------------------------------------*/
110 int phDal4Nfc_uart_is_opened(void)
112 return gComPortContext.nOpened;
115 /*-----------------------------------------------------------------------------
117 FUNCTION: phDal4Nfc_uart_flush
119 PURPOSE: Flushes the link ; clears the link buffers
121 -----------------------------------------------------------------------------*/
123 void phDal4Nfc_uart_flush(void)
126 /* flushes the com port */
127 ret = tcflush(gComPortContext.nHandle, TCIFLUSH);
128 DAL_ASSERT_STR(ret!=-1, "tcflush failed");
131 /*-----------------------------------------------------------------------------
133 FUNCTION: phDal4Nfc_uart_close
135 PURPOSE: Closes the link
137 -----------------------------------------------------------------------------*/
139 void phDal4Nfc_uart_close(void)
141 if (gComPortContext.nOpened == 1)
143 close(gComPortContext.nHandle);
144 gComPortContext.nHandle = 0;
145 gComPortContext.nOpened = 0;
149 /*-----------------------------------------------------------------------------
151 FUNCTION: phDal4Nfc_uart_close
153 PURPOSE: Closes the link
155 -----------------------------------------------------------------------------*/
157 NFCSTATUS phDal4Nfc_uart_open_and_configure(pphDal4Nfc_sConfig_t pConfig, void ** pLinkHandle)
160 NFCSTATUS nfcret = NFCSTATUS_SUCCESS;
163 DAL_ASSERT_STR(gComPortContext.nOpened==0, "Trying to open but already done!");
165 // srand(time(NULL));
167 /* open communication port handle */
168 gComPortContext.nHandle = open(pConfig->deviceNode, O_RDWR | O_NOCTTY);
169 if (gComPortContext.nHandle < 0)
172 return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
175 gComPortContext.nOpened = 1;
176 *pLinkHandle = (void*)gComPortContext.nHandle;
179 * Now configure the com port
181 ret = tcgetattr(gComPortContext.nHandle, &gComPortContext.nIoConfigBackup); /* save the old io config */
184 /* tcgetattr failed -- it is likely that the provided port is invalid */
186 return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
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));
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
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 */
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
212 ret = tcsetattr(gComPortContext.nHandle, TCSANOW, &gComPortContext.nIoConfig);
213 DAL_ASSERT_STR(ret != -1, "tcsetattr failed");
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).
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 */
231 adb shell setprop debug.nfc.UART_ERROR_RATE X
232 will corrupt and drop bytes in uart_read(), to test the error handling
235 int property_error_rate = 0;
236 static void read_property() {
238 char value[PROPERTY_VALUE_MAX];
239 property_get("debug.nfc.UART_ERROR_RATE", value, "0");
240 property_error_rate = atoi(value);
244 /* returns length of buffer after errors */
245 static int apply_errors(uint8_t *buffer, int length) {
247 if (!property_error_rate) return length;
249 for (i = 0; i < length; i++) {
250 if (rand() % 1000 < property_error_rate) {
252 // 50% chance of dropping byte
254 memcpy(&buffer[i], &buffer[i+1], length-i);
255 LOGW("dropped byte %d", i);
257 // 50% chance of corruption
258 buffer[i] = (uint8_t)rand();
259 LOGW("corrupted byte %d", i);
266 static struct timeval timeval_remaining(struct timespec timeout) {
268 struct timeval delta;
269 // clock_gettime(CLOCK_MONOTONIC, &now);
271 delta.tv_sec = timeout.tv_sec - now.tv_sec;
272 delta.tv_usec = (timeout.tv_nsec - now.tv_nsec) / (long)1000;
274 if (delta.tv_usec < 0) {
275 delta.tv_usec += 1000000;
278 if (delta.tv_sec < 0) {
285 static int libnfc_firmware_mode = 0;
287 /*-----------------------------------------------------------------------------
289 FUNCTION: phDal4Nfc_uart_read
291 PURPOSE: Reads nNbBytesToRead bytes and writes them in pBuffer.
292 Returns the number of bytes really read or -1 in case of error.
294 -----------------------------------------------------------------------------*/
296 int phDal4Nfc_uart_read(uint8_t * pBuffer, int nNbBytesToRead)
302 struct timespec timeout;
305 DAL_ASSERT_STR(gComPortContext.nOpened == 1, "read called but not opened!");
306 DAL_DEBUG("_uart_read() called to read %d bytes", nNbBytesToRead);
308 /* Samsung modify for TIZEN */
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) {
321 timeout.tv_nsec -= 1000000000;
324 } else if (libnfc_firmware_mode) {
325 clock_gettime(CLOCK_MONOTONIC, &timeout);
326 timeout.tv_sec += 10;
332 while (numRead < nNbBytesToRead) {
334 FD_SET(gComPortContext.nHandle, &rfds);
337 tv = timeval_remaining(timeout);
341 ret = select(gComPortContext.nHandle + 1, &rfds, NULL, NULL, ptv);
343 DAL_DEBUG("select() errno=%d", errno);
344 if (errno == EINTR || errno == EAGAIN) {
348 } else if (ret == 0) {
350 break; // return partial response
352 ret = read(gComPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead);
354 ret = apply_errors(pBuffer + numRead, ret);
356 DAL_DEBUG("read %d bytes", ret);
358 } else if (ret == 0) {
359 DAL_PRINT("_uart_read() EOF");
362 DAL_DEBUG("_uart_read() errno=%d", errno);
363 if (errno == EINTR || errno == EAGAIN) {
374 /*-----------------------------------------------------------------------------
376 FUNCTION: phDal4Nfc_link_write
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.
381 -----------------------------------------------------------------------------*/
383 int phDal4Nfc_uart_write(uint8_t * pBuffer, int nNbBytesToWrite)
388 DAL_ASSERT_STR(gComPortContext.nOpened == 1, "write called but not opened!");
389 DAL_DEBUG("_uart_write() called to write %d bytes\n", nNbBytesToWrite);
391 /* Samsung modify for TIZEN */
393 while (numWrote < nNbBytesToWrite) {
394 ret = write(gComPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote);
396 DAL_DEBUG("wrote %d bytes", ret);
398 } else if (ret == 0) {
399 DAL_PRINT("_uart_write() EOF");
402 DAL_DEBUG("_uart_write() errno=%d", errno);
403 if (errno == EINTR || errno == EAGAIN) {
413 /*-----------------------------------------------------------------------------
415 FUNCTION: phDal4Nfc_uart_reset
417 PURPOSE: Reset the PN544, using the VEN pin
419 -----------------------------------------------------------------------------*/
420 int phDal4Nfc_uart_reset(long level)
422 static const char NFC_POWER_PATH[] = "/sys/devices/platform/nfc-power/nfc_power";
425 int ret = NFCSTATUS_FAILED;
428 /* Samsung modify for TIZEN */
430 DAL_DEBUG("phDal4Nfc_uart_reset, VEN level = %ld", level);
432 if (snprintf(buffer, sizeof(buffer), "%u", (unsigned int)level) != 1) {
433 LOGE("Bad nfc power level (%u)", (unsigned int)level);
437 fd = open(NFC_POWER_PATH, O_WRONLY);
439 LOGE("open(%s) for write failed: %s (%d)", NFC_POWER_PATH,
440 strerror(errno), errno);
443 sz = write(fd, &buffer, sizeof(buffer) - 1);
445 LOGE("write(%s) failed: %s (%d)", NFC_POWER_PATH, strerror(errno),
449 ret = NFCSTATUS_SUCCESS;
451 libnfc_firmware_mode = 1;
453 libnfc_firmware_mode = 0;