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 #define _GNU_SOURCE
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 <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/ioctl.h>
31 #include <unistd.h>
32 #include <pthread.h>
33 
34 #include "halcore.h"
35 #include "halcore_private.h"
36 #include "android_logmsg.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 {
81     bool closeThread = false;
82     HALHANDLE hHAL = (HALHANDLE)arg;
83     STLOG_HAL_V("echo thread started...\n");
84     bool readOk= false;
85 
86     do {
87         event_table[0].fd = fidI2c;
88         event_table[0].events = POLLIN;
89         event_table[0].revents = 0;
90 
91         event_table[1].fd = cmdPipe[0];
92         event_table[1].events = POLLIN;
93         event_table[1].revents = 0;
94 
95         STLOG_HAL_D("echo thread go to sleep...\n");
96 
97         int poll_status = poll(event_table, 2, -1);
98 
99         if (-1 == poll_status) {
100             STLOG_HAL_E("error in poll call\n");
101             return false;
102         }
103 
104         if (event_table[0].revents & POLLIN) {
105             STLOG_HAL_D("echo thread wakeup from chip...\n");
106 
107             uint8_t buffer[300];
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("Idle data: 2nd byte is 0x%02x\n, reading next 2 bytes",
119                                   buffer[1]);
120                             buffer[0] = buffer[1];
121                             buffer[1] = buffer[2];
122                             bytesRead = i2cRead(fidI2c, buffer + 2, 1);
123                             if (bytesRead == 1) {
124                                 readOk = true;
125                             }
126                         } else if (buffer[2] != 0x7E) {
127                             STLOG_HAL_W("Idle data: 3rd byte is 0x%02x\n, reading next  byte",
128                                   buffer[2]);
129                             buffer[0] = buffer[2];
130                             bytesRead = i2cRead(fidI2c, buffer + 1, 2);
131                             if (bytesRead == 2) {
132                                 readOk = true;
133                             }
134                         } else {
135                             STLOG_HAL_W("received idle data\n");
136                         }
137                     }
138 
139                     if (readOk == true) {
140                         int remaining = buffer[2];
141 
142                         // read and pass to HALCore
143                         bytesRead = i2cRead(fidI2c, buffer + 3, remaining);
144                         if (bytesRead == remaining) {
145                             DispHal("RX DATA", buffer, 3 + bytesRead);
146                             HalSendUpstream(hHAL, buffer, 3 + bytesRead);
147                         } else {
148                             readOk = false;
149                             STLOG_HAL_E("! didn't read expected bytes from i2c\n");
150                         }
151                     }
152 
153                 } else {
154                     STLOG_HAL_E("! didn't read 3 requested bytes from i2c\n");
155                 }
156 
157                 readOk = false;
158                 memset(buffer, 0xca, sizeof(buffer));
159 
160                 /* read while we have data available */
161             } while (i2cGetGPIOState(fidI2c) == 1);
162         }
163 
164         if (event_table[1].revents & POLLIN) {
165             STLOG_HAL_V("thread received command.. \n");
166 
167             char cmd = 0;
168             read(cmdPipe[0], &cmd, 1);
169 
170             switch (cmd) {
171                 case 'X':
172                     STLOG_HAL_D("received close command\n");
173                     closeThread = true;
174                     break;
175 
176                 case 'W': {
177                     size_t length;
178                     uint8_t buffer[MAX_BUFFER_SIZE];
179                     STLOG_HAL_V("received write command\n");
180                     read(cmdPipe[0], &length, sizeof(length));
181                     if (length <= MAX_BUFFER_SIZE)
182                       {
183                         read(cmdPipe[0], buffer, length);
184                         i2cWrite(fidI2c, buffer, length);
185                       }
186                     else {
187                         STLOG_HAL_E("! received bigger data than expected!! Data not transmitted to NFCC \n");
188                         size_t bytes_read = 1;
189                         // Read all the data to empty but do not use it as not expected
190                         while((bytes_read > 0) && (length > 0))
191                           {
192                             bytes_read = read(cmdPipe[0],buffer,MAX_BUFFER_SIZE);
193                             length = length - bytes_read;
194                           }
195                     }
196                 }
197                 break;
198             }
199         }
200 
201     } while (!closeThread);
202 
203     close(fidI2c);
204     close(cmdPipe[0]);
205     close(cmdPipe[1]);
206 
207     HalDestroy(hHAL);
208     STLOG_HAL_D("thread exit\n");
209     return 0;
210 }
211 
212 /**
213  * Put command into queue for worker thread to process it.
214  * @param x Command 'X' to close I2C layer or 'W' to write data down to I2C
215  * layer followed by data frame
216  * @param len Size of command or data
217  * @return
218  */
I2cWriteCmd(const uint8_t * x,size_t len)219 int I2cWriteCmd(const uint8_t* x, size_t len)
220 {
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 {
232     uint32_t NoDbgFlag = HAL_FLAG_DEBUG;
233 
234       (void) pthread_mutex_lock(&i2ctransport_mtx);
235     fidI2c = open("/dev/st21nfc", O_RDWR);
236     if (fidI2c < 0) {
237         STLOG_HAL_W("unable to open /dev/st21nfc\n");
238         return false;
239     }
240 
241     i2cSetPolarity(fidI2c, false, true);
242     i2cResetPulse(fidI2c);
243 
244     if ((pipe(cmdPipe) == -1)) {
245         STLOG_HAL_W("unable to open cmdpipe\n");
246         return false;
247     }
248 
249     *pHandle = HalCreate(dev, callb, NoDbgFlag);
250 
251     if (!*pHandle) {
252         STLOG_HAL_E("failed to create NFC HAL Core \n");
253         return false;
254     }
255 
256       (void) pthread_mutex_unlock(&i2ctransport_mtx);
257 
258     return (pthread_create(&threadHandle, NULL, I2cWorkerThread, *pHandle) == 0);
259 }
260 
261 /**
262  * Terminates the I2C layer.
263  */
I2cCloseLayer()264 void I2cCloseLayer()
265 {
266     uint8_t cmd = 'X';
267     int ret;
268     ALOGD("%s: enter\n", __func__);
269 
270     (void)pthread_mutex_lock(&i2ctransport_mtx);
271 
272     if (threadHandle == (pthread_t)NULL)
273         return;
274 
275     I2cWriteCmd(&cmd, sizeof(cmd));
276     /* wait for terminate */
277     ret = pthread_join(threadHandle,(void**)NULL);
278     if (ret != 0) {
279         ALOGE("%s: failed to wait for thread (%d)", __func__, ret);
280     }
281     threadHandle = (pthread_t)NULL;
282     (void)pthread_mutex_unlock(&i2ctransport_mtx);
283 }
284 /**************************************************************************************************
285  *
286  *                                      Private API Definition
287  *
288  **************************************************************************************************/
289 /**
290  * Call the st21nfc driver to adjust wake-up polarity.
291  * @param fid File descriptor for NFC device
292  * @param low Polarity (HIGH or LOW)
293  * @param edge Polarity (RISING or FALLING)
294  * @return Result of IOCTL system call (0 if ok)
295  */
i2cSetPolarity(int fid,bool low,bool edge)296 static int i2cSetPolarity(int fid, bool low, bool edge)
297 {
298     int result;
299     unsigned int io_code;
300 
301     if (low) {
302         if (edge) {
303             io_code = ST21NFC_SET_POLARITY_FALLING;
304         } else {
305             io_code = ST21NFC_SET_POLARITY_LOW;
306         }
307 
308     } else {
309         if (edge) {
310             io_code = ST21NFC_SET_POLARITY_RISING;
311         } else {
312             io_code = ST21NFC_SET_POLARITY_HIGH;
313         }
314     }
315 
316     if (-1 == (result = ioctl(fid, io_code, NULL))) {
317         result = -1;
318     }
319 
320     return result;
321 } /* i2cSetPolarity*/
322 
323 /**
324  * Call the st21nfc driver to generate a 30ms pulse on RESET line.
325  * @param fid File descriptor for NFC device
326  * @return Result of IOCTL system call (0 if ok)
327  */
i2cResetPulse(int fid)328 static int i2cResetPulse(int fid)
329 {
330     int result;
331 
332     if (-1 == (result = ioctl(fid, ST21NFC_PULSE_RESET, NULL))) {
333         result = -1;
334     }
335     STLOG_HAL_D("! i2cResetPulse!!, result = %d", result);
336     return result;
337 } /* i2cResetPulse*/
338 
339 /**
340  * Write data to st21nfc, on failure do max 3 retries.
341  * @param fid File descriptor for NFC device
342  * @param pvBuffer Data to write
343  * @param length Data size
344  * @return 0 if bytes written, -1 if error
345  */
i2cWrite(int fid,const uint8_t * pvBuffer,int length)346 static int i2cWrite(int fid, const uint8_t* pvBuffer, int length)
347 {
348     int retries = 0;
349     int result = 0;
350 
351     while (retries < 3) {
352         result = write(fid, pvBuffer, length);
353 
354         if (result < 0) {
355             char msg[LINUX_DBGBUFFER_SIZE];
356 
357             strerror_r(errno, msg, LINUX_DBGBUFFER_SIZE);
358             STLOG_HAL_W("! i2cWrite!!, errno is '%s'", msg);
359             usleep(4000);
360             retries++;
361         } else if (result > 0) {
362             result = 0;
363             return result;
364         } else {
365             STLOG_HAL_W("write on i2c failed, retrying\n");
366             usleep(4000);
367             retries++;
368         }
369     }
370 
371     return -1;
372 } /* i2cWrite */
373 
374 /**
375  * Read data from st21nfc, on failure do max 3 retries.
376  *
377  * @param fid File descriptor for NFC device
378  * @param pvBuffer Buffer where to copy read data
379  * @param length Data size to read
380  * @return Length of read data, -1 if error
381  */
i2cRead(int fid,uint8_t * pvBuffer,int length)382 static int i2cRead(int fid, uint8_t* pvBuffer, int length)
383 {
384     int retries = 0;
385     int result = -1;
386 
387     while ((retries < 3) && (result < 0)) {
388         result = read(fid, pvBuffer, length);
389 
390         if (result == -1) {
391             int e = errno;
392             if (e == EAGAIN) {
393                 /* File is nonblocking, and no data is available.
394                  * This is not an error condition!
395                  */
396                 result = 0;
397                 STLOG_HAL_D("## i2cRead - got EAGAIN. No data available. return 0 bytes");
398             } else {
399                 /* unexpected result */
400                 char msg[LINUX_DBGBUFFER_SIZE];
401                 strerror_r(e, msg, LINUX_DBGBUFFER_SIZE);
402                 STLOG_HAL_W("## i2cRead returns %d errno %d (%s)", result, e, msg);
403             }
404         }
405 
406         if (result < 0) {
407             if (retries < 3) {
408                 /* delays are different and increasing for the three retries. */
409                 static const uint8_t delayTab[] = {2, 3, 5};
410                 int delay = delayTab[retries];
411 
412                 retries++;
413                 STLOG_HAL_W("## i2cRead retry %d/3 in %d milliseconds.", retries, delay);
414                 usleep(delay * 1000);
415                 continue;
416             }
417         }
418     }
419     return result;
420 } /* i2cRead */
421 
422 /**
423  * Get the activation status of wake-up pin from st21nfc.
424  *  The decision 'active' depends on selected polarity.
425  *  The decision is handled inside the driver(st21nfc).
426  * @param fid File descriptor for NFC device
427  * @return
428  *  Result < 0:     Error condition
429  *  Result > 0:     Pin active
430  *  Result = 0:     Pin not active
431  */
i2cGetGPIOState(int fid)432 static int i2cGetGPIOState(int fid)
433 {
434     int result;
435 
436     if (-1 == (result = ioctl(fid, ST21NFC_GET_WAKEUP, NULL))) {
437         result = -1;
438     }
439 
440     return result;
441 } /* i2cGetGPIOState */
442