/* * Copyright (C) 2010 NXP Semiconductors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * \file phDalNfc_i2c.c * \brief DAL I2C port implementation for linux * * Project: Trusted NFC Linux * */ #define LOG_TAG "NFC_i2c" #include <cutils/log.h> #include <hardware/nfc.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <sys/ioctl.h> #include <sys/select.h> #include <errno.h> #include <phDal4Nfc_debug.h> #include <phDal4Nfc_i2c.h> #include <phOsalNfc.h> #include <phNfcStatus.h> #if defined(ANDROID) #include <string.h> #endif typedef struct { int nHandle; char nOpened; } phDal4Nfc_I2cPortContext_t; /*----------------------------------------------------------------------------------- VARIABLES ------------------------------------------------------------------------------------*/ static phDal4Nfc_I2cPortContext_t gI2cPortContext; /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_set_open_from_handle PURPOSE: Initialize internal variables -----------------------------------------------------------------------------*/ void phDal4Nfc_i2c_initialize(void) { memset(&gI2cPortContext, 0, sizeof(phDal4Nfc_I2cPortContext_t)); } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_set_open_from_handle PURPOSE: The application could have opened the link itself. So we just need to get the handle and consider that the open operation has already been done. -----------------------------------------------------------------------------*/ void phDal4Nfc_i2c_set_open_from_handle(phHal_sHwReference_t * pDalHwContext) { gI2cPortContext.nHandle = (int)(intptr_t) pDalHwContext->p_board_driver; DAL_ASSERT_STR(gI2cPortContext.nHandle >= 0, "Bad passed com port handle"); gI2cPortContext.nOpened = 1; } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_is_opened PURPOSE: Returns if the link is opened or not. (0 = not opened; 1 = opened) -----------------------------------------------------------------------------*/ int phDal4Nfc_i2c_is_opened(void) { return gI2cPortContext.nOpened; } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_flush PURPOSE: Flushes the link ; clears the link buffers -----------------------------------------------------------------------------*/ void phDal4Nfc_i2c_flush(void) { /* Nothing to do (driver has no internal buffers) */ } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_close PURPOSE: Closes the link -----------------------------------------------------------------------------*/ void phDal4Nfc_i2c_close(void) { DAL_PRINT("Closing port\n"); if (gI2cPortContext.nOpened == 1) { close(gI2cPortContext.nHandle); gI2cPortContext.nHandle = 0; gI2cPortContext.nOpened = 0; } } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_open_and_configure PURPOSE: Closes the link -----------------------------------------------------------------------------*/ NFCSTATUS phDal4Nfc_i2c_open_and_configure(pphDal4Nfc_sConfig_t pConfig, void ** pLinkHandle) { DAL_ASSERT_STR(gI2cPortContext.nOpened==0, "Trying to open but already done!"); DAL_DEBUG("Opening port=%s\n", pConfig->deviceNode); /* open port */ gI2cPortContext.nHandle = open(pConfig->deviceNode, O_RDWR | O_NOCTTY); if (gI2cPortContext.nHandle < 0) { DAL_DEBUG("Open failed: open() returned %d\n", gI2cPortContext.nHandle); *pLinkHandle = NULL; return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE); } gI2cPortContext.nOpened = 1; *pLinkHandle = (void*)(intptr_t)gI2cPortContext.nHandle; DAL_PRINT("Open succeed\n"); return NFCSTATUS_SUCCESS; } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_read PURPOSE: Reads nNbBytesToRead bytes and writes them in pBuffer. Returns the number of bytes really read or -1 in case of error. -----------------------------------------------------------------------------*/ int phDal4Nfc_i2c_read(uint8_t * pBuffer, int nNbBytesToRead) { int ret; int numRead = 0; struct timeval tv; fd_set rfds; DAL_ASSERT_STR(gI2cPortContext.nOpened == 1, "read called but not opened!"); DAL_DEBUG("_i2c_read() called to read %d bytes", nNbBytesToRead); // Read with 2 second timeout, so that the read thread can be aborted // when the pn544 does not respond and we need to switch to FW download // mode. This should be done via a control socket instead. while (numRead < nNbBytesToRead) { FD_ZERO(&rfds); FD_SET(gI2cPortContext.nHandle, &rfds); tv.tv_sec = 2; tv.tv_usec = 0; ret = select(gI2cPortContext.nHandle + 1, &rfds, NULL, NULL, &tv); if (ret < 0) { DAL_DEBUG("select() errno=%d", errno); if (errno == EINTR || errno == EAGAIN) { continue; } return -1; } else if (ret == 0) { DAL_PRINT("timeout!"); return -1; } ret = read(gI2cPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead); if (ret > 0) { DAL_DEBUG("read %d bytes", ret); numRead += ret; } else if (ret == 0) { DAL_PRINT("_i2c_read() EOF"); return -1; } else { DAL_DEBUG("_i2c_read() errno=%d", errno); if (errno == EINTR || errno == EAGAIN) { continue; } return -1; } } return numRead; } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_write PURPOSE: Writes nNbBytesToWrite bytes from pBuffer to the link Returns the number of bytes that have been wrote to the interface or -1 in case of error. -----------------------------------------------------------------------------*/ int phDal4Nfc_i2c_write(uint8_t * pBuffer, int nNbBytesToWrite) { int ret; int numWrote = 0; DAL_ASSERT_STR(gI2cPortContext.nOpened == 1, "write called but not opened!"); DAL_DEBUG("_i2c_write() called to write %d bytes\n", nNbBytesToWrite); while (numWrote < nNbBytesToWrite) { ret = write(gI2cPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote); if (ret > 0) { DAL_DEBUG("wrote %d bytes", ret); numWrote += ret; } else if (ret == 0) { DAL_PRINT("_i2c_write() EOF"); return -1; } else { DAL_DEBUG("_i2c_write() errno=%d", errno); if (errno == EINTR || errno == EAGAIN) { continue; } return -1; } } return numWrote; } /*----------------------------------------------------------------------------- FUNCTION: phDal4Nfc_i2c_reset PURPOSE: Reset the PN544, using the VEN pin -----------------------------------------------------------------------------*/ #define PN544_SET_PWR _IOW(0xe9, 0x01, unsigned int) int phDal4Nfc_i2c_reset(long level) { DAL_DEBUG("phDal4Nfc_i2c_reset, VEN level = %ld", level); return ioctl(gI2cPortContext.nHandle, PN544_SET_PWR, level); }