/* * Copyright (C) 2010-2014 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. */ /* * DAL I2C port implementation for linux * * Project: Trusted NFC Linux * */ #include <errno.h> #include <fcntl.h> #include <hardware/nfc.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/select.h> #include <termios.h> #include <unistd.h> #include <phNfcStatus.h> #include <phNxpLog.h> #include <phTmlNfc_i2c.h> #include <string.h> #include "phNxpNciHal_utils.h" #define CRC_LEN 2 #define NORMAL_MODE_HEADER_LEN 3 #define FW_DNLD_HEADER_LEN 2 #define FW_DNLD_LEN_OFFSET 1 #define NORMAL_MODE_LEN_OFFSET 2 #define FRAGMENTSIZE_MAX PHNFC_I2C_FRAGMENT_SIZE static bool_t bFwDnldFlag = false; extern phTmlNfc_i2cfragmentation_t fragmentation_enabled; /******************************************************************************* ** ** Function phTmlNfc_i2c_close ** ** Description Closes PN54X device ** ** Parameters pDevHandle - device handle ** ** Returns None ** *******************************************************************************/ void phTmlNfc_i2c_close(void* pDevHandle) { if (NULL != pDevHandle) { close((intptr_t)pDevHandle); } return; } /******************************************************************************* ** ** Function phTmlNfc_i2c_open_and_configure ** ** Description Open and configure PN54X device ** ** Parameters pConfig - hardware information ** pLinkHandle - device handle ** ** Returns NFC status: ** NFCSTATUS_SUCCESS - open_and_configure operation success ** NFCSTATUS_INVALID_DEVICE - device open operation failure ** *******************************************************************************/ NFCSTATUS phTmlNfc_i2c_open_and_configure(pphTmlNfc_Config_t pConfig, void** pLinkHandle) { int nHandle; NXPLOG_TML_D("Opening port=%s\n", pConfig->pDevName); /* open port */ nHandle = open((const char*)pConfig->pDevName, O_RDWR); if (nHandle < 0) { NXPLOG_TML_E("_i2c_open() Failed: retval %x", nHandle); *pLinkHandle = NULL; return NFCSTATUS_INVALID_DEVICE; } *pLinkHandle = (void*)((intptr_t)nHandle); /*Reset PN54X*/ phTmlNfc_i2c_reset((void*)((intptr_t)nHandle), 0); usleep(10 * 1000); phTmlNfc_i2c_reset((void*)((intptr_t)nHandle), 1); return NFCSTATUS_SUCCESS; } /******************************************************************************* ** ** Function phTmlNfc_i2c_read ** ** Description Reads requested number of bytes from PN54X device into given ** buffer ** ** Parameters pDevHandle - valid device handle ** pBuffer - buffer for read data ** nNbBytesToRead - number of bytes requested to be read ** ** Returns numRead - number of successfully read bytes ** -1 - read operation failure ** *******************************************************************************/ int phTmlNfc_i2c_read(void* pDevHandle, uint8_t* pBuffer, int nNbBytesToRead) { int ret_Read; int ret_Select; int numRead = 0; struct timeval tv; fd_set rfds; uint16_t totalBtyesToRead = 0; UNUSED(nNbBytesToRead); if (NULL == pDevHandle) { return -1; } if (bFwDnldFlag == false) { totalBtyesToRead = NORMAL_MODE_HEADER_LEN; } else { totalBtyesToRead = FW_DNLD_HEADER_LEN; } /* Read with 2 second timeout, so that the read thread can be aborted when the PN54X does not respond and we need to switch to FW download mode. This should be done via a control socket instead. */ FD_ZERO(&rfds); FD_SET((intptr_t)pDevHandle, &rfds); tv.tv_sec = 2; tv.tv_usec = 1; ret_Select = select((int)((intptr_t)pDevHandle + (int)1), &rfds, NULL, NULL, &tv); if (ret_Select < 0) { NXPLOG_TML_D("i2c select() errno : %x", errno); return -1; } else if (ret_Select == 0) { NXPLOG_TML_D("i2c select() Timeout"); return -1; } else { ret_Read = read((intptr_t)pDevHandle, pBuffer, totalBtyesToRead - numRead); if (ret_Read > 0) { numRead += ret_Read; } else if (ret_Read == 0) { NXPLOG_TML_E("_i2c_read() [hdr]EOF"); return -1; } else { NXPLOG_TML_E("_i2c_read() [hdr] errno : %x", errno); return -1; } if (bFwDnldFlag == false) { totalBtyesToRead = NORMAL_MODE_HEADER_LEN; } else { totalBtyesToRead = FW_DNLD_HEADER_LEN; } if (numRead < totalBtyesToRead) { ret_Read = read((intptr_t)pDevHandle, pBuffer, totalBtyesToRead - numRead); if (ret_Read != totalBtyesToRead - numRead) { NXPLOG_TML_E("_i2c_read() [hdr] errno : %x", errno); return -1; } else { numRead += ret_Read; } } if (bFwDnldFlag == true) { totalBtyesToRead = pBuffer[FW_DNLD_LEN_OFFSET] + FW_DNLD_HEADER_LEN + CRC_LEN; } else { totalBtyesToRead = pBuffer[NORMAL_MODE_LEN_OFFSET] + NORMAL_MODE_HEADER_LEN; } if ((totalBtyesToRead - numRead) != 0) { ret_Read = read((intptr_t)pDevHandle, (pBuffer + numRead), totalBtyesToRead - numRead); if (ret_Read > 0) { numRead += ret_Read; } else if (ret_Read == 0) { NXPLOG_TML_E("_i2c_read() [pyld] EOF"); return -1; } else { if (bFwDnldFlag == false) { NXPLOG_TML_D("_i2c_read() [hdr] received"); phNxpNciHal_print_packet("RECV", pBuffer, NORMAL_MODE_HEADER_LEN); } NXPLOG_TML_E("_i2c_read() [pyld] errno : %x", errno); return -1; } } else { NXPLOG_TML_E("_>>>>> Empty packet recieved !!"); } } return numRead; } /******************************************************************************* ** ** Function phTmlNfc_i2c_write ** ** Description Writes requested number of bytes from given buffer into ** PN54X device ** ** Parameters pDevHandle - valid device handle ** pBuffer - buffer for read data ** nNbBytesToWrite - number of bytes requested to be written ** ** Returns numWrote - number of successfully written bytes ** -1 - write operation failure ** *******************************************************************************/ int phTmlNfc_i2c_write(void* pDevHandle, uint8_t* pBuffer, int nNbBytesToWrite) { int ret; int numWrote = 0; int numBytes = nNbBytesToWrite; if (NULL == pDevHandle) { return -1; } if (fragmentation_enabled == I2C_FRAGMENATATION_DISABLED && nNbBytesToWrite > FRAGMENTSIZE_MAX) { NXPLOG_TML_D( "i2c_write() data larger than maximum I2C size,enable I2C " "fragmentation"); return -1; } while (numWrote < nNbBytesToWrite) { if (fragmentation_enabled == I2C_FRAGMENTATION_ENABLED && nNbBytesToWrite > FRAGMENTSIZE_MAX) { if (nNbBytesToWrite - numWrote > FRAGMENTSIZE_MAX) { numBytes = numWrote + FRAGMENTSIZE_MAX; } else { numBytes = nNbBytesToWrite; } } ret = write((intptr_t)pDevHandle, pBuffer + numWrote, numBytes - numWrote); if (ret > 0) { numWrote += ret; if (fragmentation_enabled == I2C_FRAGMENTATION_ENABLED && numWrote < nNbBytesToWrite) { usleep(500); } } else if (ret == 0) { NXPLOG_TML_D("_i2c_write() EOF"); return -1; } else { NXPLOG_TML_D("_i2c_write() errno : %x", errno); if (errno == EINTR || errno == EAGAIN) { continue; } return -1; } } return numWrote; } /******************************************************************************* ** ** Function phTmlNfc_i2c_reset ** ** Description Reset PN54X device, using VEN pin ** ** Parameters pDevHandle - valid device handle ** level - reset level ** ** Returns 0 - reset operation success ** -1 - reset operation failure ** *******************************************************************************/ int phTmlNfc_i2c_reset(void* pDevHandle, long level) { int ret; NXPLOG_TML_D("phTmlNfc_i2c_reset(), VEN level %ld", level); if (NULL == pDevHandle) { return -1; } ret = ioctl((intptr_t)pDevHandle, PN544_SET_PWR, level); if (level == 2 && ret == 0) { bFwDnldFlag = true; } else { bFwDnldFlag = false; } return ret; } /******************************************************************************* ** ** Function getDownloadFlag ** ** Description Returns the current mode ** ** Parameters none ** ** Returns Current mode download/NCI *******************************************************************************/ bool_t getDownloadFlag(void) { return bFwDnldFlag; }