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 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 char * pComPort; 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 switch(pConfig->nLinkType) 172 { 173 case ENUM_DAL_LINK_TYPE_COM1: 174 pComPort = "/dev/ttyO0"; 175 break; 176 case ENUM_DAL_LINK_TYPE_COM2: 177 pComPort = "/dev/ttyO1"; 178 break; 179 case ENUM_DAL_LINK_TYPE_COM3: 180 pComPort = "/dev/ttyO2"; 181 break; 182 case ENUM_DAL_LINK_TYPE_COM4: 183 pComPort = "/dev/ttyO3"; 184 break; 185 case ENUM_DAL_LINK_TYPE_COM5: 186 pComPort = "/dev/ttyO4"; 187 break; 188 case ENUM_DAL_LINK_TYPE_COM6: 189 pComPort = "/dev/ttyO5"; 190 break; 191 case ENUM_DAL_LINK_TYPE_COM7: 192 pComPort = "/dev/ttyO6"; 193 break; 194 case ENUM_DAL_LINK_TYPE_COM8: 195 pComPort = "/dev/ttyO7"; 196 break; 197 case ENUM_DAL_LINK_TYPE_USB: 198 pComPort = "/dev/ttyUSB0"; 199 break; 200 default: 201 return NFCSTATUS_INVALID_PARAMETER; 202 } 203 204 /* open communication port handle */ 205 gComPortContext.nHandle = open(pComPort, O_RDWR | O_NOCTTY); 206 if (gComPortContext.nHandle < 0) 207 { 208 *pLinkHandle = NULL; 209 return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE); 210 } 211 212 gComPortContext.nOpened = 1; 213 *pLinkHandle = (void*)gComPortContext.nHandle; 214 215 /* 216 * Now configure the com port 217 */ 218 ret = tcgetattr(gComPortContext.nHandle, &gComPortContext.nIoConfigBackup); /* save the old io config */ 219 if (ret == -1) 220 { 221 /* tcgetattr failed -- it is likely that the provided port is invalid */ 222 *pLinkHandle = NULL; 223 return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE); 224 } 225 ret = fcntl(gComPortContext.nHandle, F_SETFL, 0); /* Makes the read blocking (default). */ 226 DAL_ASSERT_STR(ret != -1, "fcntl failed"); 227 /* Configures the io */ 228 memset((void *)&gComPortContext.nIoConfig, (int)0, (size_t)sizeof(struct termios)); 229 /* 230 BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed. 231 CRTSCTS : output hardware flow control (only used if the cable has 232 all necessary lines. See sect. 7 of Serial-HOWTO) 233 CS8 : 8n1 (8bit,no parity,1 stopbit) 234 CLOCAL : local connection, no modem contol 235 CREAD : enable receiving characters 236 */ 237 gComPortContext.nIoConfig.c_cflag = DAL_BAUD_RATE | CS8 | CLOCAL | CREAD; /* Control mode flags */ 238 gComPortContext.nIoConfig.c_iflag = IGNPAR; /* Input mode flags : IGNPAR Ignore parity errors */ 239 gComPortContext.nIoConfig.c_oflag = 0; /* Output mode flags */ 240 gComPortContext.nIoConfig.c_lflag = 0; /* Local mode flags. Read mode : non canonical, no echo */ 241 gComPortContext.nIoConfig.c_cc[VTIME] = 0; /* Control characters. No inter-character timer */ 242 gComPortContext.nIoConfig.c_cc[VMIN] = 1; /* Control characters. Read is blocking until X characters are read */ 243 244 /* 245 TCSANOW Make changes now without waiting for data to complete 246 TCSADRAIN Wait until everything has been transmitted 247 TCSAFLUSH Flush input and output buffers and make the change 248 */ 249 ret = tcsetattr(gComPortContext.nHandle, TCSANOW, &gComPortContext.nIoConfig); 250 DAL_ASSERT_STR(ret != -1, "tcsetattr failed"); 251 252 /* 253 On linux the DTR signal is set by default. That causes a problem for pn544 chip 254 because this signal is connected to "reset". So we clear it. (on windows it is cleared by default). 255 */ 256 ret = ioctl(gComPortContext.nHandle, TIOCMGET, &nComStatus); 257 DAL_ASSERT_STR(ret != -1, "ioctl TIOCMGET failed"); 258 nComStatus &= ~TIOCM_DTR; 259 ret = ioctl(gComPortContext.nHandle, TIOCMSET, &nComStatus); 260 DAL_ASSERT_STR(ret != -1, "ioctl TIOCMSET failed"); 261 DAL_DEBUG("Com port status=%d\n", nComStatus); 262 usleep(10000); /* Mandatory sleep so that the DTR line is ready before continuing */ 263 264 return nfcret; 265 } 266 267 /* 268 adb shell setprop debug.nfc.UART_ERROR_RATE X 269 will corrupt and drop bytes in uart_read(), to test the error handling 270 of DAL & LLC errors. 271 */ 272 int property_error_rate = 0; 273 static void read_property() { 274 char value[PROPERTY_VALUE_MAX]; 275 property_get("debug.nfc.UART_ERROR_RATE", value, "0"); 276 property_error_rate = atoi(value); 277 } 278 279 /* returns length of buffer after errors */ 280 static int apply_errors(uint8_t *buffer, int length) { 281 int i; 282 if (!property_error_rate) return length; 283 284 for (i = 0; i < length; i++) { 285 if (rand() % 1000 < property_error_rate) { 286 if (rand() % 2) { 287 // 50% chance of dropping byte 288 length--; 289 memcpy(&buffer[i], &buffer[i+1], length-i); 290 LOGW("dropped byte %d", i); 291 } else { 292 // 50% chance of corruption 293 buffer[i] = (uint8_t)rand(); 294 LOGW("corrupted byte %d", i); 295 } 296 } 297 } 298 return length; 299 } 300 301 static struct timeval timeval_remaining(struct timespec timeout) { 302 struct timespec now; 303 struct timeval delta; 304 clock_gettime(CLOCK_MONOTONIC, &now); 305 306 delta.tv_sec = timeout.tv_sec - now.tv_sec; 307 delta.tv_usec = (timeout.tv_nsec - now.tv_nsec) / (long)1000; 308 309 if (delta.tv_usec < 0) { 310 delta.tv_usec += 1000000; 311 delta.tv_sec--; 312 } 313 if (delta.tv_sec < 0) { 314 delta.tv_sec = 0; 315 delta.tv_usec = 0; 316 } 317 return delta; 318 } 319 320 static int libnfc_firmware_mode = 0; 321 322 /*----------------------------------------------------------------------------- 323 324 FUNCTION: phDal4Nfc_uart_read 325 326 PURPOSE: Reads nNbBytesToRead bytes and writes them in pBuffer. 327 Returns the number of bytes really read or -1 in case of error. 328 329 -----------------------------------------------------------------------------*/ 330 int phDal4Nfc_uart_read(uint8_t * pBuffer, int nNbBytesToRead) 331 { 332 int ret; 333 int numRead = 0; 334 struct timeval tv; 335 struct timeval *ptv; 336 struct timespec timeout; 337 fd_set rfds; 338 339 DAL_ASSERT_STR(gComPortContext.nOpened == 1, "read called but not opened!"); 340 DAL_DEBUG("_uart_read() called to read %d bytes", nNbBytesToRead); 341 342 read_property(); 343 344 // Read timeout: 345 // FW mode: 10s timeout 346 // 1 byte read: steady-state LLC length read, allowed to block forever 347 // >1 byte read: LLC payload, 100ms timeout (before pn544 re-transmit) 348 if (nNbBytesToRead > 1 && !libnfc_firmware_mode) { 349 clock_gettime(CLOCK_MONOTONIC, &timeout); 350 timeout.tv_nsec += 100000000; 351 if (timeout.tv_nsec > 1000000000) { 352 timeout.tv_sec++; 353 timeout.tv_nsec -= 1000000000; 354 } 355 ptv = &tv; 356 } else if (libnfc_firmware_mode) { 357 clock_gettime(CLOCK_MONOTONIC, &timeout); 358 timeout.tv_sec += 10; 359 ptv = &tv; 360 } else { 361 ptv = NULL; 362 } 363 364 while (numRead < nNbBytesToRead) { 365 FD_ZERO(&rfds); 366 FD_SET(gComPortContext.nHandle, &rfds); 367 368 if (ptv) { 369 tv = timeval_remaining(timeout); 370 ptv = &tv; 371 } 372 373 ret = select(gComPortContext.nHandle + 1, &rfds, NULL, NULL, ptv); 374 if (ret < 0) { 375 DAL_DEBUG("select() errno=%d", errno); 376 if (errno == EINTR || errno == EAGAIN) { 377 continue; 378 } 379 return -1; 380 } else if (ret == 0) { 381 LOGW("timeout!"); 382 break; // return partial response 383 } 384 ret = read(gComPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead); 385 if (ret > 0) { 386 ret = apply_errors(pBuffer + numRead, ret); 387 388 DAL_DEBUG("read %d bytes", ret); 389 numRead += ret; 390 } else if (ret == 0) { 391 DAL_PRINT("_uart_read() EOF"); 392 return 0; 393 } else { 394 DAL_DEBUG("_uart_read() errno=%d", errno); 395 if (errno == EINTR || errno == EAGAIN) { 396 continue; 397 } 398 return -1; 399 } 400 } 401 402 return numRead; 403 } 404 405 /*----------------------------------------------------------------------------- 406 407 FUNCTION: phDal4Nfc_link_write 408 409 PURPOSE: Writes nNbBytesToWrite bytes from pBuffer to the link 410 Returns the number of bytes that have been wrote to the interface or -1 in case of error. 411 412 -----------------------------------------------------------------------------*/ 413 414 int phDal4Nfc_uart_write(uint8_t * pBuffer, int nNbBytesToWrite) 415 { 416 int ret; 417 int numWrote = 0; 418 419 DAL_ASSERT_STR(gComPortContext.nOpened == 1, "write called but not opened!"); 420 DAL_DEBUG("_uart_write() called to write %d bytes\n", nNbBytesToWrite); 421 422 while (numWrote < nNbBytesToWrite) { 423 ret = write(gComPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote); 424 if (ret > 0) { 425 DAL_DEBUG("wrote %d bytes", ret); 426 numWrote += ret; 427 } else if (ret == 0) { 428 DAL_PRINT("_uart_write() EOF"); 429 return -1; 430 } else { 431 DAL_DEBUG("_uart_write() errno=%d", errno); 432 if (errno == EINTR || errno == EAGAIN) { 433 continue; 434 } 435 return -1; 436 } 437 } 438 439 return numWrote; 440 } 441 442 /*----------------------------------------------------------------------------- 443 444 FUNCTION: phDal4Nfc_uart_reset 445 446 PURPOSE: Reset the PN544, using the VEN pin 447 448 -----------------------------------------------------------------------------*/ 449 int phDal4Nfc_uart_reset(long level) 450 { 451 static const char NFC_POWER_PATH[] = "/sys/devices/platform/nfc-power/nfc_power"; 452 int sz; 453 int fd = -1; 454 int ret = NFCSTATUS_FAILED; 455 char buffer[2]; 456 457 DAL_DEBUG("phDal4Nfc_uart_reset, VEN level = %ld", level); 458 459 if (snprintf(buffer, sizeof(buffer), "%u", (unsigned int)level) != 1) { 460 LOGE("Bad nfc power level (%u)", (unsigned int)level); 461 goto out; 462 } 463 464 fd = open(NFC_POWER_PATH, O_WRONLY); 465 if (fd < 0) { 466 LOGE("open(%s) for write failed: %s (%d)", NFC_POWER_PATH, 467 strerror(errno), errno); 468 goto out; 469 } 470 sz = write(fd, &buffer, sizeof(buffer) - 1); 471 if (sz < 0) { 472 LOGE("write(%s) failed: %s (%d)", NFC_POWER_PATH, strerror(errno), 473 errno); 474 goto out; 475 } 476 ret = NFCSTATUS_SUCCESS; 477 if (level == 2) { 478 libnfc_firmware_mode = 1; 479 } else { 480 libnfc_firmware_mode = 0; 481 } 482 483 out: 484 if (fd >= 0) { 485 close(fd); 486 } 487 return ret; 488 } 489