• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** ----------------------------------------------------------------------
2  *
3  * Copyright (C) 2013 ST Microelectronics S.A.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *
18  ----------------------------------------------------------------------*/
19 
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <linux/input.h> /* not required for all builds */
25 #include <poll.h>
26 #include <pthread.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <unistd.h>
33 
34 #include "android_logmsg.h"
35 #include "halcore.h"
36 #include "halcore_private.h"
37 
38 #define ST21NFC_MAGIC 0xEA
39 
40 #define ST21NFC_GET_WAKEUP _IOR(ST21NFC_MAGIC, 0x01, unsigned int)
41 #define ST21NFC_PULSE_RESET _IOR(ST21NFC_MAGIC, 0x02, unsigned int)
42 #define ST21NFC_SET_POLARITY_RISING _IOR(ST21NFC_MAGIC, 0x03, unsigned int)
43 #define ST21NFC_SET_POLARITY_FALLING _IOR(ST21NFC_MAGIC, 0x04, unsigned int)
44 #define ST21NFC_SET_POLARITY_HIGH _IOR(ST21NFC_MAGIC, 0x05, unsigned int)
45 #define ST21NFC_SET_POLARITY_LOW _IOR(ST21NFC_MAGIC, 0x06, unsigned int)
46 
47 #define LINUX_DBGBUFFER_SIZE 300
48 
49 static int fidI2c = 0;
50 static int cmdPipe[2] = {0, 0};
51 
52 static struct pollfd event_table[2];
53 static pthread_t threadHandle = (pthread_t)NULL;
54 pthread_mutex_t i2ctransport_mtx = PTHREAD_MUTEX_INITIALIZER;
55 
56 /**************************************************************************************************
57  *
58  *                                      Private API Declaration
59  *
60  **************************************************************************************************/
61 
62 static int i2cSetPolarity(int fid, bool low, bool edge);
63 static int i2cResetPulse(int fid);
64 static int i2cRead(int fid, uint8_t* pvBuffer, int length);
65 static int i2cGetGPIOState(int fid);
66 static int i2cWrite(int fd, const uint8_t* pvBuffer, int length);
67 
68 /**************************************************************************************************
69  *
70  *                                      Public API Entry-Points
71  *
72  **************************************************************************************************/
73 
74 /**
75  * Worker thread for I2C data processing.
76  * On exit of this thread, destroy the HAL thread instance.
77  * @param arg  Handle of the HAL layer
78  */
I2cWorkerThread(void * arg)79 static void* I2cWorkerThread(void* arg) {
80   bool closeThread = false;
81   HALHANDLE hHAL = (HALHANDLE)arg;
82   STLOG_HAL_D("echo thread started...\n");
83   bool readOk = false;
84 
85   do {
86     event_table[0].fd = fidI2c;
87     event_table[0].events = POLLIN;
88     event_table[0].revents = 0;
89 
90     event_table[1].fd = cmdPipe[0];
91     event_table[1].events = POLLIN;
92     event_table[1].revents = 0;
93 
94     STLOG_HAL_V("echo thread go to sleep...\n");
95 
96     int poll_status = poll(event_table, 2, -1);
97 
98     if (-1 == poll_status) {
99       STLOG_HAL_E("error in poll call\n");
100       break;
101     }
102 
103     if (event_table[0].revents & POLLIN) {
104       STLOG_HAL_V("echo thread wakeup from chip...\n");
105 
106       uint8_t buffer[300];
107       int count = 0;
108 
109       do {
110         // load first four bytes:
111         int bytesRead = i2cRead(fidI2c, buffer, 3);
112 
113         if (bytesRead == 3) {
114           if ((buffer[0] != 0x7E) && (buffer[1] != 0x7E)) {
115             readOk = true;
116           } else {
117             if (buffer[1] != 0x7E) {
118               STLOG_HAL_W(
119                   "Idle data: 2nd byte is 0x%02x\n, reading next 2 bytes",
120                   buffer[1]);
121               buffer[0] = buffer[1];
122               buffer[1] = buffer[2];
123               bytesRead = i2cRead(fidI2c, buffer + 2, 1);
124               if (bytesRead == 1) {
125                 readOk = true;
126               }
127             } else if (buffer[2] != 0x7E) {
128               STLOG_HAL_W("Idle data: 3rd byte is 0x%02x\n, reading next  byte",
129                           buffer[2]);
130               buffer[0] = buffer[2];
131               bytesRead = i2cRead(fidI2c, buffer + 1, 2);
132               if (bytesRead == 2) {
133                 readOk = true;
134               }
135             } else {
136               STLOG_HAL_W("received idle data\n");
137             }
138           }
139 
140           if (readOk == true) {
141             int remaining = buffer[2];
142             bytesRead = 0;
143             if (remaining != 0) {
144               // read and pass to HALCore
145               bytesRead = i2cRead(fidI2c, buffer + 3, remaining);
146             }
147             if (bytesRead == remaining) {
148               DispHal("RX DATA", buffer, 3 + bytesRead);
149               HalSendUpstream(hHAL, buffer, 3 + bytesRead);
150             } else {
151               readOk = false;
152               STLOG_HAL_E("! didn't read expected bytes from i2c\n");
153             }
154           }
155 
156         } else {
157           STLOG_HAL_E("! didn't read 3 requested bytes from i2c\n");
158         }
159 
160         readOk = false;
161         memset(buffer, 0xca, sizeof(buffer));
162 
163         /* read while we have data available, up to 2 times then allow writes */
164       } while ((i2cGetGPIOState(fidI2c) == 1) && (count++ < 2));
165     }
166 
167     if (event_table[1].revents & POLLIN) {
168       STLOG_HAL_V("thread received command.. \n");
169 
170       char cmd = 0;
171       read(cmdPipe[0], &cmd, 1);
172 
173       switch (cmd) {
174         case 'X':
175           STLOG_HAL_D("received close command\n");
176           closeThread = true;
177           break;
178 
179         case 'W': {
180           size_t length;
181           uint8_t buffer[MAX_BUFFER_SIZE];
182           STLOG_HAL_V("received write command\n");
183           read(cmdPipe[0], &length, sizeof(length));
184           if (length <= MAX_BUFFER_SIZE) {
185             read(cmdPipe[0], buffer, length);
186             i2cWrite(fidI2c, buffer, length);
187           } else {
188             STLOG_HAL_E(
189                 "! received bigger data than expected!! Data not transmitted "
190                 "to NFCC \n");
191             size_t bytes_read = 1;
192             // Read all the data to empty but do not use it as not expected
193             while ((bytes_read > 0) && (length > 0)) {
194               bytes_read = read(cmdPipe[0], buffer, MAX_BUFFER_SIZE);
195               length = length - bytes_read;
196             }
197           }
198         } break;
199       }
200     }
201 
202   } while (!closeThread);
203 
204   close(fidI2c);
205   close(cmdPipe[0]);
206   close(cmdPipe[1]);
207 
208   HalDestroy(hHAL);
209   STLOG_HAL_D("thread exit\n");
210   return 0;
211 }
212 
213 /**
214  * Put command into queue for worker thread to process it.
215  * @param x Command 'X' to close I2C layer or 'W' to write data down to I2C
216  * layer followed by data frame
217  * @param len Size of command or data
218  * @return
219  */
I2cWriteCmd(const uint8_t * x,size_t len)220 int I2cWriteCmd(const uint8_t* x, size_t len) {
221   return write(cmdPipe[1], x, len);
222 }
223 
224 /**
225  * Initialize the I2C layer.
226  * @param dev NFC NCI device context, NFC callbacks for control/data, HAL handle
227  * @param callb HAL Core callback upon reception on I2C
228  * @param pHandle HAL context handle
229  */
I2cOpenLayer(void * dev,HAL_CALLBACK callb,HALHANDLE * pHandle)230 bool I2cOpenLayer(void* dev, HAL_CALLBACK callb, HALHANDLE* pHandle) {
231   uint32_t NoDbgFlag = HAL_FLAG_DEBUG;
232 
233   (void)pthread_mutex_lock(&i2ctransport_mtx);
234   fidI2c = open("/dev/st21nfc", O_RDWR);
235   if (fidI2c < 0) {
236     STLOG_HAL_W("unable to open /dev/st21nfc  (%s) \n", strerror(errno));
237     (void)pthread_mutex_unlock(&i2ctransport_mtx);
238     return false;
239   }
240 
241   i2cSetPolarity(fidI2c, false, false);
242   i2cResetPulse(fidI2c);
243 
244   if ((pipe(cmdPipe) == -1)) {
245     STLOG_HAL_W("unable to open cmdpipe\n");
246     (void)pthread_mutex_unlock(&i2ctransport_mtx);
247     return false;
248   }
249 
250   *pHandle = HalCreate(dev, callb, NoDbgFlag);
251 
252   if (!*pHandle) {
253     STLOG_HAL_E("failed to create NFC HAL Core \n");
254     (void)pthread_mutex_unlock(&i2ctransport_mtx);
255     return false;
256   }
257 
258   (void)pthread_mutex_unlock(&i2ctransport_mtx);
259 
260   return (pthread_create(&threadHandle, NULL, I2cWorkerThread, *pHandle) == 0);
261 }
262 
263 /**
264  * Terminates the I2C layer.
265  */
I2cCloseLayer()266 void I2cCloseLayer() {
267   uint8_t cmd = 'X';
268   int ret;
269   ALOGD("%s: enter\n", __func__);
270 
271   (void)pthread_mutex_lock(&i2ctransport_mtx);
272 
273   if (threadHandle == (pthread_t)NULL) {
274     (void)pthread_mutex_unlock(&i2ctransport_mtx);
275     return;
276   }
277 
278   I2cWriteCmd(&cmd, sizeof(cmd));
279   /* wait for terminate */
280   ret = pthread_join(threadHandle, (void**)NULL);
281   if (ret != 0) {
282     ALOGE("%s: failed to wait for thread (%d)", __func__, ret);
283   }
284   threadHandle = (pthread_t)NULL;
285   (void)pthread_mutex_unlock(&i2ctransport_mtx);
286 }
287 
288 /**
289  * Terminates the I2C layer.
290  */
I2cResetPulse()291 void I2cResetPulse() {
292   ALOGD("%s: enter\n", __func__);
293 
294   (void)pthread_mutex_lock(&i2ctransport_mtx);
295 
296   i2cResetPulse(fidI2c);
297   (void)pthread_mutex_unlock(&i2ctransport_mtx);
298 }
299 /**************************************************************************************************
300  *
301  *                                      Private API Definition
302  *
303  **************************************************************************************************/
304 /**
305  * Call the st21nfc driver to adjust wake-up polarity.
306  * @param fid File descriptor for NFC device
307  * @param low Polarity (HIGH or LOW)
308  * @param edge Polarity (RISING or FALLING)
309  * @return Result of IOCTL system call (0 if ok)
310  */
i2cSetPolarity(int fid,bool low,bool edge)311 static int i2cSetPolarity(int fid, bool low, bool edge) {
312   int result;
313   unsigned int io_code;
314 
315   if (low) {
316     if (edge) {
317       io_code = ST21NFC_SET_POLARITY_FALLING;
318     } else {
319       io_code = ST21NFC_SET_POLARITY_LOW;
320     }
321 
322   } else {
323     if (edge) {
324       io_code = ST21NFC_SET_POLARITY_RISING;
325     } else {
326       io_code = ST21NFC_SET_POLARITY_HIGH;
327     }
328   }
329 
330   if (-1 == (result = ioctl(fid, io_code, NULL))) {
331     result = -1;
332   }
333 
334   return result;
335 } /* i2cSetPolarity*/
336 
337 /**
338  * Call the st21nfc driver to generate a 30ms pulse on RESET line.
339  * @param fid File descriptor for NFC device
340  * @return Result of IOCTL system call (0 if ok)
341  */
i2cResetPulse(int fid)342 static int i2cResetPulse(int fid) {
343   int result;
344 
345   if (-1 == (result = ioctl(fid, ST21NFC_PULSE_RESET, NULL))) {
346     result = -1;
347   }
348   STLOG_HAL_D("! i2cResetPulse!!, result = %d", result);
349   return result;
350 } /* i2cResetPulse*/
351 
352 /**
353  * Write data to st21nfc, on failure do max 3 retries.
354  * @param fid File descriptor for NFC device
355  * @param pvBuffer Data to write
356  * @param length Data size
357  * @return 0 if bytes written, -1 if error
358  */
i2cWrite(int fid,const uint8_t * pvBuffer,int length)359 static int i2cWrite(int fid, const uint8_t* pvBuffer, int length) {
360   int retries = 0;
361   int result = 0;
362   int halfsecs = 0;
363 
364 redo:
365   while (retries < 3) {
366     result = write(fid, pvBuffer, length);
367 
368     if (result < 0) {
369       char msg[LINUX_DBGBUFFER_SIZE];
370 
371       strerror_r(errno, msg, LINUX_DBGBUFFER_SIZE);
372       STLOG_HAL_W("! i2cWrite!!, errno is '%s'", msg);
373       usleep(4000);
374       retries++;
375     } else if (result > 0) {
376       result = 0;
377       return result;
378     } else {
379       STLOG_HAL_W("write on i2c failed, retrying\n");
380       usleep(4000);
381       retries++;
382     }
383   }
384   /* If we're here, we failed to write to NFCC. Retry after 500ms because some
385   CPUs have shown such long unavailability sometimes */
386   if (halfsecs < 10) {
387     usleep(500000);
388     halfsecs++;
389     goto redo;
390   }
391   /* The CLF did not recover, give up */
392   return -1;
393 } /* i2cWrite */
394 
395 /**
396  * Read data from st21nfc, on failure do max 3 retries.
397  *
398  * @param fid File descriptor for NFC device
399  * @param pvBuffer Buffer where to copy read data
400  * @param length Data size to read
401  * @return Length of read data, -1 if error
402  */
i2cRead(int fid,uint8_t * pvBuffer,int length)403 static int i2cRead(int fid, uint8_t* pvBuffer, int length) {
404   int retries = 0;
405   int result = -1;
406 
407   while ((retries < 3) && (result < 0)) {
408     result = read(fid, pvBuffer, length);
409 
410     if (result == -1) {
411       int e = errno;
412       if (e == EAGAIN) {
413         /* File is nonblocking, and no data is available.
414          * This is not an error condition!
415          */
416         result = 0;
417         STLOG_HAL_D(
418             "## i2cRead - got EAGAIN. No data available. return 0 bytes");
419       } else {
420         /* unexpected result */
421         char msg[LINUX_DBGBUFFER_SIZE];
422         strerror_r(e, msg, LINUX_DBGBUFFER_SIZE);
423         STLOG_HAL_W("## i2cRead returns %d errno %d (%s)", result, e, msg);
424       }
425     }
426 
427     if (result < 0) {
428       if (retries < 3) {
429         /* delays are different and increasing for the three retries. */
430         static const uint8_t delayTab[] = {2, 3, 5};
431         int delay = delayTab[retries];
432 
433         retries++;
434         STLOG_HAL_W("## i2cRead retry %d/3 in %d milliseconds.", retries,
435                     delay);
436         usleep(delay * 1000);
437         continue;
438       }
439     }
440   }
441   return result;
442 } /* i2cRead */
443 
444 /**
445  * Get the activation status of wake-up pin from st21nfc.
446  *  The decision 'active' depends on selected polarity.
447  *  The decision is handled inside the driver(st21nfc).
448  * @param fid File descriptor for NFC device
449  * @return
450  *  Result < 0:     Error condition
451  *  Result > 0:     Pin active
452  *  Result = 0:     Pin not active
453  */
i2cGetGPIOState(int fid)454 static int i2cGetGPIOState(int fid) {
455   int result;
456 
457   if (-1 == (result = ioctl(fid, ST21NFC_GET_WAKEUP, NULL))) {
458     result = -1;
459   }
460 
461   return result;
462 } /* i2cGetGPIOState */
463