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_i2c.c
19  * \brief DAL I2C port implementation for linux
20  *
21  * Project: Trusted NFC Linux
22  *
23  */
24 
25 #define LOG_TAG "NFC_i2c"
26 #include <cutils/log.h>
27 #include <hardware/nfc.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <termios.h>
32 #include <sys/ioctl.h>
33 #include <sys/select.h>
34 #include <errno.h>
35 
36 #include <phDal4Nfc_debug.h>
37 #include <phDal4Nfc_i2c.h>
38 #include <phOsalNfc.h>
39 #include <phNfcStatus.h>
40 #if defined(ANDROID)
41 #include <string.h>
42 #endif
43 
44 typedef struct
45 {
46    int  nHandle;
47    char nOpened;
48 
49 } phDal4Nfc_I2cPortContext_t;
50 
51 
52 /*-----------------------------------------------------------------------------------
53                                       VARIABLES
54 ------------------------------------------------------------------------------------*/
55 static phDal4Nfc_I2cPortContext_t gI2cPortContext;
56 
57 
58 
59 /*-----------------------------------------------------------------------------
60 
61 FUNCTION: phDal4Nfc_i2c_set_open_from_handle
62 
63 PURPOSE:  Initialize internal variables
64 
65 -----------------------------------------------------------------------------*/
66 
phDal4Nfc_i2c_initialize(void)67 void phDal4Nfc_i2c_initialize(void)
68 {
69    memset(&gI2cPortContext, 0, sizeof(phDal4Nfc_I2cPortContext_t));
70 }
71 
72 
73 /*-----------------------------------------------------------------------------
74 
75 FUNCTION: phDal4Nfc_i2c_set_open_from_handle
76 
77 PURPOSE:  The application could have opened the link itself. So we just need
78           to get the handle and consider that the open operation has already
79           been done.
80 
81 -----------------------------------------------------------------------------*/
82 
phDal4Nfc_i2c_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)83 void phDal4Nfc_i2c_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)
84 {
85    gI2cPortContext.nHandle = (int)(intptr_t) pDalHwContext->p_board_driver;
86    DAL_ASSERT_STR(gI2cPortContext.nHandle >= 0, "Bad passed com port handle");
87    gI2cPortContext.nOpened = 1;
88 }
89 
90 /*-----------------------------------------------------------------------------
91 
92 FUNCTION: phDal4Nfc_i2c_is_opened
93 
94 PURPOSE:  Returns if the link is opened or not. (0 = not opened; 1 = opened)
95 
96 -----------------------------------------------------------------------------*/
97 
phDal4Nfc_i2c_is_opened(void)98 int phDal4Nfc_i2c_is_opened(void)
99 {
100    return gI2cPortContext.nOpened;
101 }
102 
103 /*-----------------------------------------------------------------------------
104 
105 FUNCTION: phDal4Nfc_i2c_flush
106 
107 PURPOSE:  Flushes the link ; clears the link buffers
108 
109 -----------------------------------------------------------------------------*/
110 
phDal4Nfc_i2c_flush(void)111 void phDal4Nfc_i2c_flush(void)
112 {
113    /* Nothing to do (driver has no internal buffers) */
114 }
115 
116 /*-----------------------------------------------------------------------------
117 
118 FUNCTION: phDal4Nfc_i2c_close
119 
120 PURPOSE:  Closes the link
121 
122 -----------------------------------------------------------------------------*/
123 
phDal4Nfc_i2c_close(void)124 void phDal4Nfc_i2c_close(void)
125 {
126    DAL_PRINT("Closing port\n");
127    if (gI2cPortContext.nOpened == 1)
128    {
129       close(gI2cPortContext.nHandle);
130       gI2cPortContext.nHandle = 0;
131       gI2cPortContext.nOpened = 0;
132    }
133 }
134 
135 /*-----------------------------------------------------------------------------
136 
137 FUNCTION: phDal4Nfc_i2c_open_and_configure
138 
139 PURPOSE:  Closes the link
140 
141 -----------------------------------------------------------------------------*/
142 
phDal4Nfc_i2c_open_and_configure(pphDal4Nfc_sConfig_t pConfig,void ** pLinkHandle)143 NFCSTATUS phDal4Nfc_i2c_open_and_configure(pphDal4Nfc_sConfig_t pConfig, void ** pLinkHandle)
144 {
145    DAL_ASSERT_STR(gI2cPortContext.nOpened==0, "Trying to open but already done!");
146 
147    DAL_DEBUG("Opening port=%s\n", pConfig->deviceNode);
148 
149    /* open port */
150    gI2cPortContext.nHandle = open(pConfig->deviceNode, O_RDWR | O_NOCTTY);
151    if (gI2cPortContext.nHandle < 0)
152    {
153        DAL_DEBUG("Open failed: open() returned %d\n", gI2cPortContext.nHandle);
154       *pLinkHandle = NULL;
155       return PHNFCSTVAL(CID_NFC_DAL, NFCSTATUS_INVALID_DEVICE);
156    }
157 
158    gI2cPortContext.nOpened = 1;
159    *pLinkHandle = (void*)(intptr_t)gI2cPortContext.nHandle;
160 
161    DAL_PRINT("Open succeed\n");
162 
163    return NFCSTATUS_SUCCESS;
164 }
165 
166 
167 /*-----------------------------------------------------------------------------
168 
169 FUNCTION: phDal4Nfc_i2c_read
170 
171 PURPOSE:  Reads nNbBytesToRead bytes and writes them in pBuffer.
172           Returns the number of bytes really read or -1 in case of error.
173 
174 -----------------------------------------------------------------------------*/
175 
phDal4Nfc_i2c_read(uint8_t * pBuffer,int nNbBytesToRead)176 int phDal4Nfc_i2c_read(uint8_t * pBuffer, int nNbBytesToRead)
177 {
178     int ret;
179     int numRead = 0;
180     struct timeval tv;
181     fd_set rfds;
182 
183     DAL_ASSERT_STR(gI2cPortContext.nOpened == 1, "read called but not opened!");
184     DAL_DEBUG("_i2c_read() called to read %d bytes", nNbBytesToRead);
185 
186     // Read with 2 second timeout, so that the read thread can be aborted
187     // when the pn544 does not respond and we need to switch to FW download
188     // mode. This should be done via a control socket instead.
189     while (numRead < nNbBytesToRead) {
190         FD_ZERO(&rfds);
191         FD_SET(gI2cPortContext.nHandle, &rfds);
192         tv.tv_sec = 2;
193         tv.tv_usec = 0;
194         ret = select(gI2cPortContext.nHandle + 1, &rfds, NULL, NULL, &tv);
195         if (ret < 0) {
196             DAL_DEBUG("select() errno=%d", errno);
197             if (errno == EINTR || errno == EAGAIN) {
198                 continue;
199             }
200             return -1;
201         } else if (ret == 0) {
202             DAL_PRINT("timeout!");
203             return -1;
204         }
205         ret = read(gI2cPortContext.nHandle, pBuffer + numRead, nNbBytesToRead - numRead);
206         if (ret > 0) {
207             DAL_DEBUG("read %d bytes", ret);
208             numRead += ret;
209         } else if (ret == 0) {
210             DAL_PRINT("_i2c_read() EOF");
211             return -1;
212         } else {
213             DAL_DEBUG("_i2c_read() errno=%d", errno);
214             if (errno == EINTR || errno == EAGAIN) {
215                 continue;
216             }
217             return -1;
218         }
219     }
220     return numRead;
221 }
222 
223 /*-----------------------------------------------------------------------------
224 
225 FUNCTION: phDal4Nfc_i2c_write
226 
227 PURPOSE:  Writes nNbBytesToWrite bytes from pBuffer to the link
228           Returns the number of bytes that have been wrote to the interface or -1 in case of error.
229 
230 -----------------------------------------------------------------------------*/
231 
phDal4Nfc_i2c_write(uint8_t * pBuffer,int nNbBytesToWrite)232 int phDal4Nfc_i2c_write(uint8_t * pBuffer, int nNbBytesToWrite)
233 {
234     int ret;
235     int numWrote = 0;
236 
237     DAL_ASSERT_STR(gI2cPortContext.nOpened == 1, "write called but not opened!");
238     DAL_DEBUG("_i2c_write() called to write %d bytes\n", nNbBytesToWrite);
239 
240     while (numWrote < nNbBytesToWrite) {
241         ret = write(gI2cPortContext.nHandle, pBuffer + numWrote, nNbBytesToWrite - numWrote);
242         if (ret > 0) {
243             DAL_DEBUG("wrote %d bytes", ret);
244             numWrote += ret;
245         } else if (ret == 0) {
246             DAL_PRINT("_i2c_write() EOF");
247             return -1;
248         } else {
249             DAL_DEBUG("_i2c_write() errno=%d", errno);
250             if (errno == EINTR || errno == EAGAIN) {
251                 continue;
252             }
253             return -1;
254         }
255     }
256 
257     return numWrote;
258 }
259 
260 /*-----------------------------------------------------------------------------
261 
262 FUNCTION: phDal4Nfc_i2c_reset
263 
264 PURPOSE:  Reset the PN544, using the VEN pin
265 
266 -----------------------------------------------------------------------------*/
267 #define PN544_SET_PWR _IOW(0xe9, 0x01, unsigned int)
phDal4Nfc_i2c_reset(long level)268 int phDal4Nfc_i2c_reset(long level)
269 {
270     DAL_DEBUG("phDal4Nfc_i2c_reset, VEN level = %ld", level);
271 
272     return ioctl(gI2cPortContext.nHandle, PN544_SET_PWR, level);
273 }
274