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
phDal4Nfc_uart_initialize(void)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
phDal4Nfc_uart_set_open_from_handle(phHal_sHwReference_t * pDalHwContext)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
phDal4Nfc_uart_is_opened(void)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
phDal4Nfc_uart_flush(void)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
phDal4Nfc_uart_close(void)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
phDal4Nfc_uart_open_and_configure(pphDal4Nfc_sConfig_t pConfig,void ** pLinkHandle)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;
read_property()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 */
apply_errors(uint8_t * buffer,int length)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
timeval_remaining(struct timespec timeout)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 -----------------------------------------------------------------------------*/
phDal4Nfc_uart_read(uint8_t * pBuffer,int nNbBytesToRead)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
phDal4Nfc_uart_write(uint8_t * pBuffer,int nNbBytesToWrite)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 -----------------------------------------------------------------------------*/
phDal4Nfc_uart_reset(long level)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