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 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 98 void phDal4Nfc_uart_set_open_from_handle(phHal_sHwReference_t * pDalHwContext) 99 { 100 gComPortContext.nHandle = (int) 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 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 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 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 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*)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; 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 */ 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 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 -----------------------------------------------------------------------------*/ 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 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 -----------------------------------------------------------------------------*/ 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