Home | History | Annotate | Download | only in Linux_x86
      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