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
30 #define LOG_TAG "NFC_uart"
31 #include <cutils/log.h>
\r
32 //#include <hardware/nfc.h>
\r
38 #include <sys/ioctl.h>
39 #include <sys/select.h>
43 #include <phDal4Nfc_debug.h>
44 #include <phDal4Nfc_uart.h>
45 #include <phOsalNfc.h>
46 #include <phNfcStatus.h>
49 //#include <cutils/properties.h> // for property_get
\r
56 struct termios nIoConfigBackup;
57 struct termios nIoConfig;
59 } phDal4Nfc_ComPortContext_t;
61 /*-----------------------------------------------------------------------------------
62 COM PORT CONFIGURATION
63 ------------------------------------------------------------------------------------*/
64 #define DAL_BAUD_RATE B115200
68 /*-----------------------------------------------------------------------------------
70 ------------------------------------------------------------------------------------*/
71 static phDal4Nfc_ComPortContext_t gComPortContext;
75 /*-----------------------------------------------------------------------------
77 FUNCTION: phDal4Nfc_uart_set_open_from_handle
79 PURPOSE: Initialize internal variables
81 -----------------------------------------------------------------------------*/
83 void phDal4Nfc_uart_initialize(void)
85 memset(&gComPortContext, 0, sizeof(phDal4Nfc_ComPortContext_t));
89 /*-----------------------------------------------------------------------------
91 FUNCTION: phDal4Nfc_uart_set_open_from_handle
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
97 -----------------------------------------------------------------------------*/
99 void phDal4Nfc_uart_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)
101 gComPortContext.nHandle = (int) pDalHwContext->p_board_driver;
102 DAL_ASSERT_STR(gComPortContext.nHandle >= 0, "Bad passed com port handle");
103 gComPortContext.nOpened = 1;
106 /*-----------------------------------------------------------------------------
108 FUNCTION: phDal4Nfc_uart_is_opened
110 PURPOSE: Returns if the link is opened or not. (0 = not opened; 1 = opened)
112 -----------------------------------------------------------------------------*/
114 int phDal4Nfc_uart_is_opened(void)
116 return gComPortContext.nOpened;
119 /*-----------------------------------------------------------------------------
121 FUNCTION: phDal4Nfc_uart_flush
123 PURPOSE: Flushes the link ; clears the link buffers
125 -----------------------------------------------------------------------------*/
127 void phDal4Nfc_uart_flush(void)
130 /* flushes the com port */
131 ret = tcflush(gComPortContext.nHandle, TCIFLUSH);
132 DAL_ASSERT_STR(ret!=-1, "tcflush failed");
135 /*-----------------------------------------------------------------------------
137 FUNCTION: phDal4Nfc_uart_close
139 PURPOSE: Closes the link
141 -----------------------------------------------------------------------------*/
143 void phDal4Nfc_uart_close(void)
145 if (gComPortContext.nOpened == 1)
147 close(gComPortContext.nHandle);
148 gComPortContext.nHandle = 0;
149 gComPortContext.nOpened = 0;
153 /*-----------------------------------------------------------------------------
155 FUNCTION: phDal4Nfc_uart_close
157 PURPOSE: Closes the link
159 -----------------------------------------------------------------------------*/
161 NFCSTATUS phDal4Nfc_uart_open_and_configure(pphDal4Nfc_sConfig_t pConfig, void ** pLinkHandle)
164 NFCSTATUS nfcret = NFCSTATUS_SUCCESS;
167 DAL_ASSERT_STR(gComPortContext.nOpened==0, "Trying to open but already done!");
169 // srand(time(NULL));
171 /* open communication port handle */
172 gComPortContext.nHandle = open(pConfig->deviceNode, O_RDWR | O_NOCTTY);
173 if (gComPortContext.nHandle < 0)
176 return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
179 gComPortContext.nOpened = 1;
180 *pLinkHandle = (void*)gComPortContext.nHandle;
183 * Now configure the com port
185 ret = tcgetattr(gComPortContext.nHandle, &gComPortContext.nIoConfigBackup); /* save the old io config */
188 /* tcgetattr failed -- it is likely that the provided port is invalid */
190 return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
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));
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
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 */
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
216 ret = tcsetattr(gComPortContext.nHandle, TCSANOW, &gComPortContext.nIoConfig);
217 DAL_ASSERT_STR(ret != -1, "tcsetattr failed");
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).
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 */
235 adb shell setprop debug.nfc.UART_ERROR_RATE X
236 will corrupt and drop bytes in uart_read(), to test the error handling
239 int property_error_rate = 0;
240 static void read_property() {
242 char value[PROPERTY_VALUE_MAX];
243 property_get("debug.nfc.UART_ERROR_RATE", value, "0");
244 property_error_rate = atoi(value);
248 /* returns length of buffer after errors */
249 static int apply_errors(uint8_t *buffer, int length) {
251 if (!property_error_rate) return length;
253 for (i = 0; i < length; i++) {
254 if (rand() % 1000 < property_error_rate) {
256 // 50% chance of dropping byte
258 memcpy(&buffer[i], &buffer[i+1], length-i);
259 LOGW("dropped byte %d", i);
261 // 50% chance of corruption
262 buffer[i] = (uint8_t)rand();
263 LOGW("corrupted byte %d", i);
270 static struct timeval timeval_remaining(struct timespec timeout) {
272 struct timeval delta;
273 // clock_gettime(CLOCK_MONOTONIC, &now);
275 delta.tv_sec = timeout.tv_sec - now.tv_sec;
276 delta.tv_usec = (timeout.tv_nsec - now.tv_nsec) / (long)1000;
278 if (delta.tv_usec < 0) {
279 delta.tv_usec += 1000000;
282 if (delta.tv_sec < 0) {
289 static int libnfc_firmware_mode = 0;
291 /*-----------------------------------------------------------------------------
293 FUNCTION: phDal4Nfc_uart_read
295 PURPOSE: Reads nNbBytesToRead bytes and writes them in pBuffer.
296 Returns the number of bytes really read or -1 in case of error.
298 -----------------------------------------------------------------------------*/
299 int phDal4Nfc_uart_read(uint8_t * pBuffer, int nNbBytesToRead)
305 struct timespec timeout;
308 DAL_ASSERT_STR(gComPortContext.nOpened == 1, "read called but not opened!");
309 DAL_DEBUG("_uart_read() called to read %d bytes", nNbBytesToRead);
311 /* Samsung modify for TIZEN */
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) {
324 timeout.tv_nsec -= 1000000000;
327 } else if (libnfc_firmware_mode) {
328 clock_gettime(CLOCK_MONOTONIC, &timeout);
329 timeout.tv_sec += 10;
335 while (numRead < nNbBytesToRead) {
337 FD_SET(gComPortContext.nHandle, &rfds);
340 tv = timeval_remaining(timeout);
344 ret = select(gComPortContext.nHandle + 1, &rfds, NULL, NULL, ptv);
346 DAL_DEBUG("select() errno=%d", errno);
347 if (errno == EINTR || errno == EAGAIN) {
351 } else if (ret == 0) {
353 break; // return partial response
355 ret = read(gComPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead);
357 ret = apply_errors(pBuffer + numRead, ret);
359 DAL_DEBUG("read %d bytes", ret);
361 } else if (ret == 0) {
362 DAL_PRINT("_uart_read() EOF");
365 DAL_DEBUG("_uart_read() errno=%d", errno);
366 if (errno == EINTR || errno == EAGAIN) {
377 /*-----------------------------------------------------------------------------
379 FUNCTION: phDal4Nfc_link_write
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.
384 -----------------------------------------------------------------------------*/
386 int phDal4Nfc_uart_write(uint8_t * pBuffer, int nNbBytesToWrite)
391 DAL_ASSERT_STR(gComPortContext.nOpened == 1, "write called but not opened!");
392 DAL_DEBUG("_uart_write() called to write %d bytes\n", nNbBytesToWrite);
394 /* Samsung modify for TIZEN */
396 while (numWrote < nNbBytesToWrite) {
397 ret = write(gComPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote);
399 DAL_DEBUG("wrote %d bytes", ret);
401 } else if (ret == 0) {
402 DAL_PRINT("_uart_write() EOF");
405 DAL_DEBUG("_uart_write() errno=%d", errno);
406 if (errno == EINTR || errno == EAGAIN) {
416 /*-----------------------------------------------------------------------------
418 FUNCTION: phDal4Nfc_uart_reset
420 PURPOSE: Reset the PN544, using the VEN pin
422 -----------------------------------------------------------------------------*/
423 int phDal4Nfc_uart_reset(long level)
425 static const char NFC_POWER_PATH[] = "/sys/devices/platform/nfc-power/nfc_power";
428 int ret = NFCSTATUS_FAILED;
431 /* Samsung modify for TIZEN */
433 DAL_DEBUG("phDal4Nfc_uart_reset, VEN level = %ld", level);
435 if (snprintf(buffer, sizeof(buffer), "%u", (unsigned int)level) != 1) {
436 LOGE("Bad nfc power level (%u)", (unsigned int)level);
440 fd = open(NFC_POWER_PATH, O_WRONLY);
442 LOGE("open(%s) for write failed: %s (%d)", NFC_POWER_PATH,
443 strerror(errno), errno);
446 sz = write(fd, &buffer, sizeof(buffer) - 1);
448 LOGE("write(%s) failed: %s (%d)", NFC_POWER_PATH, strerror(errno),
452 ret = NFCSTATUS_SUCCESS;
454 libnfc_firmware_mode = 1;
456 libnfc_firmware_mode = 0;