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 #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)(intptr_t) 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*)(intptr_t)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