1 /*
2  * Copyright (C) 2012-2019 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 #include <phNxpLog.h>
18 #include <phNxpNciHal.h>
19 #include <phNxpNciHal_NfcDepSWPrio.h>
20 
21 /* Timeout value to wait for NFC-DEP detection.*/
22 #define CUSTOM_POLL_TIMEOUT 160
23 #define CLEAN_UP_TIMEOUT 250
24 #define MAX_WRITE_RETRY 5
25 
26 #define MAX_POLL_CMD_LEN 64
27 #define NCI_HEADER_SIZE 3
28 /******************* Global variables *****************************************/
29 extern phNxpNciHal_Control_t nxpncihal_ctrl;
30 extern NFCSTATUS phNxpNciHal_send_ext_cmd(uint16_t cmd_len, uint8_t* p_cmd);
31 static uint8_t cmd_stop_rf_discovery[] = {0x21, 0x06, 0x01, 0x00}; /* IDLE */
32 static uint8_t cmd_resume_rf_discovery[] = {0x21, 0x06, 0x01,
33                                             0x03}; /* RF_DISCOVER */
34 
35 /*RF_DISCOVER_SELECT_CMD*/
36 static uint8_t cmd_select_rf_discovery[] = {0x21, 0x04, 0x03, 0x01, 0x04, 0x02};
37 
38 static uint8_t cmd_poll[MAX_POLL_CMD_LEN];
39 static uint8_t cmd_poll_len = 0;
40 int discover_type = 0xFF;
41 uint32_t cleanup_timer;
42 
43 /*PRIO LOGIC related dead functions undefined*/
44 #ifdef P2P_PRIO_LOGIC_HAL_IMP
45 
46 static int iso_dep_detected = 0x00;
47 static int poll_timer_fired = 0x00;
48 static uint8_t bIgnorep2plogic = 0;
49 static uint8_t* p_iso_ntf_buff = NULL; /* buffer to store second notification */
50 static uint8_t bIgnoreIsoDep = 0;
51 static uint32_t custom_poll_timer;
52 
53 /************** NFC-DEP SW PRIO functions *************************************/
54 
55 static NFCSTATUS phNxpNciHal_start_polling_loop(void);
56 static NFCSTATUS phNxpNciHal_stop_polling_loop(void);
57 static NFCSTATUS phNxpNciHal_resume_polling_loop(void);
58 static void phNxpNciHal_NfcDep_store_ntf(uint8_t* p_cmd_data, uint16_t cmd_len);
59 
60 /*******************************************************************************
61 **
62 ** Function         cleanup_timer_handler
63 **
64 ** Description      Callback function for cleanup timer.
65 **
66 ** Returns          None
67 **
68 *******************************************************************************/
cleanup_timer_handler(uint32_t timerId,void * pContext)69 static void cleanup_timer_handler(uint32_t timerId, void* pContext) {
70   NXPLOG_NCIHAL_D(">> cleanup_timer_handler.");
71 
72   NXPLOG_NCIHAL_D(
73       ">> cleanup_timer_handler. ISO_DEP not detected second time.");
74 
75   phOsalNfc_Timer_Delete(cleanup_timer);
76   cleanup_timer = 0;
77   iso_dep_detected = 0x00;
78   EnableP2P_PrioLogic = false;
79   return;
80 }
81 
82 /*******************************************************************************
83 **
84 ** Function         custom_poll_timer_handler
85 **
86 ** Description      Callback function for custom poll timer.
87 **
88 ** Returns          None
89 **
90 *******************************************************************************/
custom_poll_timer_handler(uint32_t timerId,void * pContext)91 static void custom_poll_timer_handler(uint32_t timerId, void* pContext) {
92   NXPLOG_NCIHAL_D(">> custom_poll_timer_handler.");
93 
94   NXPLOG_NCIHAL_D(
95       ">> custom_poll_timer_handler. NFC_DEP not detected. so giving early "
96       "chance to ISO_DEP.");
97 
98   phOsalNfc_Timer_Delete(custom_poll_timer);
99 
100   if (iso_dep_detected == 0x01) {
101     poll_timer_fired = 0x01;
102 
103     /*
104      * Restart polling loop.
105      * When the polling loop is stopped, polling will be restarted.
106      */
107     NXPLOG_NCIHAL_D(">> custom_poll_timer_handler - restart polling loop.");
108 
109     phNxpNciHal_stop_polling_loop();
110   } else {
111     NXPLOG_NCIHAL_D(
112         ">> custom_poll_timer_handler - invalid flag state (iso_dep_detected)");
113   }
114 
115   return;
116 }
117 /*******************************************************************************
118 **
119 ** Function         phNxpNciHal_stop_polling_loop
120 **
121 ** Description      Sends stop polling cmd to NFCC
122 **
123 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
124 **
125 *******************************************************************************/
phNxpNciHal_stop_polling_loop()126 static NFCSTATUS phNxpNciHal_stop_polling_loop() {
127   NFCSTATUS status = NFCSTATUS_SUCCESS;
128   phNxpNciHal_Sem_t cb_data;
129   pthread_t pthread;
130   discover_type = STOP_POLLING;
131 
132   pthread_attr_t attr;
133   pthread_attr_init(&attr);
134   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
135   if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) {
136     NXPLOG_NCIHAL_E("fail to create pthread");
137   }
138   pthread_attr_destroy(&attr);
139   return status;
140 }
141 
142 /*******************************************************************************
143 **
144 ** Function         phNxpNciHal_resume_polling_loop
145 **
146 ** Description      Sends resume polling cmd to NFCC
147 **
148 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
149 **
150 *******************************************************************************/
phNxpNciHal_resume_polling_loop()151 static NFCSTATUS phNxpNciHal_resume_polling_loop() {
152   NFCSTATUS status = NFCSTATUS_SUCCESS;
153   phNxpNciHal_Sem_t cb_data;
154   pthread_t pthread;
155   discover_type = RESUME_POLLING;
156 
157   pthread_attr_t attr;
158   pthread_attr_init(&attr);
159   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
160   if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) {
161     NXPLOG_NCIHAL_E("fail to create pthread");
162   }
163   pthread_attr_destroy(&attr);
164   return status;
165 }
166 
167 /*******************************************************************************
168 **
169 ** Function         phNxpNciHal_start_polling_loop
170 **
171 ** Description      Sends start polling cmd to NFCC
172 **
173 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
174 **
175 *******************************************************************************/
phNxpNciHal_start_polling_loop()176 NFCSTATUS phNxpNciHal_start_polling_loop() {
177   NFCSTATUS status = NFCSTATUS_FAILED;
178   phNxpNciHal_Sem_t cb_data;
179   pthread_t pthread;
180   discover_type = START_POLLING;
181 
182   pthread_attr_t attr;
183   pthread_attr_init(&attr);
184   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
185   if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) {
186     NXPLOG_NCIHAL_E("fail to create pthread");
187   }
188   pthread_attr_destroy(&attr);
189   return status;
190 }
191 
192 /*******************************************************************************
193 **
194 ** Function         phNxpNciHal_NfcDep_rsp_ext
195 **
196 ** Description      Implements algorithm for NFC-DEP protocol priority over
197 **                  ISO-DEP protocol.
198 **                  Following the algorithm:
199 **                  IF ISO-DEP detected first time,set the ISO-DEP detected flag
200 **                  and resume polling loop with 60ms timeout value.
201 **                      a) if than NFC-DEP detected than send the response to
202 **                         libnfc-nci stack and stop the timer.
203 **                      b) if NFC-DEP not detected with in 60ms, than restart
204 **                         the polling loop to give early chance to ISO-DEP with
205 **                         a cleanup timer.
206 **                      c) if ISO-DEP detected second time send the response to
207 **                         libnfc-nci stack and stop the cleanup timer.
208 **                      d) if ISO-DEP not detected with in cleanup timeout, than
209 **                         clear the ISO-DEP detection flag.
210 **
211 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
212 **
213 *******************************************************************************/
phNxpNciHal_NfcDep_rsp_ext(uint8_t * p_ntf,uint16_t * p_len)214 NFCSTATUS phNxpNciHal_NfcDep_rsp_ext(uint8_t* p_ntf, uint16_t* p_len) {
215   NFCSTATUS status = NFCSTATUS_INVALID_PARAMETER;
216 
217   NXPLOG_NCIHAL_D(">> p_ntf[0]=%02x , p_ntf[1]=%02x", p_ntf[0], p_ntf[1]);
218 
219   if (p_ntf[0] == 0x41 && p_ntf[1] == 0x04) {
220     // Tag selected, Disable P2P Prio logic.
221     bIgnoreIsoDep = 1;
222     NXPLOG_NCIHAL_D(">> Tag selected, Disable P2P Prio logic.");
223 
224   } else if (((p_ntf[0] == 0x61 && p_ntf[1] == 0x06) ||
225               (p_ntf[0] == 0x41 && p_ntf[1] == 0x06)) &&
226              bIgnoreIsoDep == 1) {
227     // Tag deselected, enable P2P Prio logic.
228     bIgnoreIsoDep = 0x00;
229     NXPLOG_NCIHAL_D(">> Tag deselected, enable P2P Prio logic.");
230   }
231   if (bIgnoreIsoDep == 0x00 && p_ntf[0] == 0x61 && p_ntf[1] == 0x05 &&
232       *p_len > 5) {
233     if (p_ntf[5] == 0x04 && p_ntf[6] < 0x80) {
234       NXPLOG_NCIHAL_D(">> ISO DEP detected.");
235 
236       if (iso_dep_detected == 0x00) {
237         NXPLOG_NCIHAL_D(">> ISO DEP detected first time. Resume polling loop");
238 
239         iso_dep_detected = 0x01;
240         status = phNxpNciHal_resume_polling_loop();
241 
242         custom_poll_timer = phOsalNfc_Timer_Create();
243         NXPLOG_NCIHAL_D("custom poll timer started - %d", custom_poll_timer);
244 
245         status = phOsalNfc_Timer_Start(custom_poll_timer, CUSTOM_POLL_TIMEOUT,
246                                        &custom_poll_timer_handler, NULL);
247 
248         if (NFCSTATUS_SUCCESS == status) {
249           NXPLOG_NCIHAL_D("custom poll timer started");
250         } else {
251           NXPLOG_NCIHAL_E("custom poll timer not started!!!");
252           status = NFCSTATUS_FAILED;
253         }
254 
255         status = NFCSTATUS_FAILED;
256       } else {
257         NXPLOG_NCIHAL_D(">> ISO DEP detected second time.");
258         /* Store notification */
259         phNxpNciHal_NfcDep_store_ntf(p_ntf, *p_len);
260 
261         /* Stop Cleanup_timer */
262         phOsalNfc_Timer_Stop(cleanup_timer);
263         phOsalNfc_Timer_Delete(cleanup_timer);
264         cleanup_timer = 0;
265         EnableP2P_PrioLogic = false;
266         iso_dep_detected = 0;
267         status = NFCSTATUS_SUCCESS;
268       }
269     } else if (p_ntf[5] == 0x05) {
270       NXPLOG_NCIHAL_D(">> NFC-DEP Detected - stopping the custom poll timer");
271 
272       phOsalNfc_Timer_Stop(custom_poll_timer);
273       phOsalNfc_Timer_Delete(custom_poll_timer);
274       EnableP2P_PrioLogic = false;
275       iso_dep_detected = 0;
276       status = NFCSTATUS_SUCCESS;
277     } else {
278       NXPLOG_NCIHAL_D(
279           ">>  detected other technology- stopping the custom poll timer");
280       phOsalNfc_Timer_Stop(custom_poll_timer);
281       phOsalNfc_Timer_Delete(custom_poll_timer);
282       EnableP2P_PrioLogic = false;
283       iso_dep_detected = 0;
284       status = NFCSTATUS_INVALID_PARAMETER;
285     }
286   } else if (bIgnoreIsoDep == 0x00 &&
287              ((p_ntf[0] == 0x41 && p_ntf[1] == 0x06) ||
288               (p_ntf[0] == 0x61 && p_ntf[1] == 0x06))) {
289     NXPLOG_NCIHAL_D(">> RF disabled");
290     if (poll_timer_fired == 0x01) {
291       poll_timer_fired = 0x00;
292 
293       NXPLOG_NCIHAL_D(">>restarting polling loop.");
294 
295       /* start polling loop */
296       phNxpNciHal_start_polling_loop();
297       EnableP2P_PrioLogic = false;
298       NXPLOG_NCIHAL_D(
299           ">> NFC DEP NOT  detected - custom poll timer expired - RF disabled");
300 
301       cleanup_timer = phOsalNfc_Timer_Create();
302 
303       /* Start cleanup_timer */
304       NFCSTATUS status = phOsalNfc_Timer_Start(cleanup_timer, CLEAN_UP_TIMEOUT,
305                                                &cleanup_timer_handler, NULL);
306 
307       if (NFCSTATUS_SUCCESS == status) {
308         NXPLOG_NCIHAL_D("cleanup timer started");
309       } else {
310         NXPLOG_NCIHAL_E("cleanup timer not started!!!");
311         status = NFCSTATUS_FAILED;
312       }
313 
314       status = NFCSTATUS_FAILED;
315     } else {
316       status = NFCSTATUS_SUCCESS;
317     }
318   }
319   if (bIgnoreIsoDep == 0x00 && iso_dep_detected == 1) {
320     if ((p_ntf[0] == 0x41 && p_ntf[1] == 0x06) ||
321         (p_ntf[0] == 0x61 && p_ntf[1] == 0x06)) {
322       NXPLOG_NCIHAL_D(">>iso_dep_detected Disconnect related notification");
323       status = NFCSTATUS_FAILED;
324     } else {
325       NXPLOG_NCIHAL_W("Never come here");
326     }
327   }
328 
329   return status;
330 }
331 /*******************************************************************************
332 **
333 ** Function         phNxpNciHal_NfcDep_store_ntf
334 **
335 ** Description      Stores the iso dep notification locally.
336 **
337 ** Returns          None
338 **
339 *******************************************************************************/
phNxpNciHal_NfcDep_store_ntf(uint8_t * p_cmd_data,uint16_t cmd_len)340 static void phNxpNciHal_NfcDep_store_ntf(uint8_t* p_cmd_data,
341                                          uint16_t cmd_len) {
342   p_iso_ntf_buff = NULL;
343 
344   p_iso_ntf_buff = malloc(sizeof(uint8_t) * cmd_len);
345   if (p_iso_ntf_buff == NULL) {
346     NXPLOG_NCIHAL_E("Error allocating memory (p_iso_ntf_buff)");
347     return;
348   }
349   memcpy(p_iso_ntf_buff, p_cmd_data, cmd_len);
350   bIgnorep2plogic = 1;
351 }
352 
353 /*******************************************************************************
354 **
355 ** Function         phNxpNciHal_NfcDep_comapre_ntf
356 **
357 ** Description      Compare the notification with previous iso dep notification.
358 **
359 ** Returns          NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED
360 **
361 *******************************************************************************/
phNxpNciHal_NfcDep_comapre_ntf(uint8_t * p_cmd_data,uint16_t cmd_len)362 NFCSTATUS phNxpNciHal_NfcDep_comapre_ntf(uint8_t* p_cmd_data,
363                                          uint16_t cmd_len) {
364   NFCSTATUS status = NFCSTATUS_FAILED;
365   int32_t ret_val = -1;
366 
367   if (bIgnorep2plogic == 1) {
368     ret_val = memcmp(p_cmd_data, p_iso_ntf_buff, cmd_len);
369     if (ret_val != 0) {
370       NXPLOG_NCIHAL_E("Third notification is not equal to last");
371     } else {
372       NXPLOG_NCIHAL_D(
373           "Third notification is equal to last (disable p2p logic)");
374       status = NFCSTATUS_SUCCESS;
375     }
376     bIgnorep2plogic = 0;
377   }
378   if (p_iso_ntf_buff != NULL) {
379     free(p_iso_ntf_buff);
380     p_iso_ntf_buff = NULL;
381   }
382 
383   return status;
384 }
385 
phNxpNciHal_clean_P2P_Prio()386 extern NFCSTATUS phNxpNciHal_clean_P2P_Prio() {
387   NFCSTATUS status = NFCSTATUS_SUCCESS;
388 
389   iso_dep_detected = 0x00;
390   EnableP2P_PrioLogic = false;
391   poll_timer_fired = 0x00;
392   bIgnorep2plogic = 0x00;
393   bIgnoreIsoDep = 0x00;
394 
395   status = phOsalNfc_Timer_Stop(cleanup_timer);
396   status |= phOsalNfc_Timer_Delete(cleanup_timer);
397 
398   status |= phOsalNfc_Timer_Stop(custom_poll_timer);
399   status |= phOsalNfc_Timer_Delete(custom_poll_timer);
400   cleanup_timer = 0;
401   return status;
402 }
403 
404 #endif
405 
406 /*******************************************************************************
407  **
408  ** Function         tmp_thread
409  **
410  ** Description      Thread to execute custom poll commands .
411  **
412  ** Returns          None
413  **
414  *******************************************************************************/
tmp_thread(void * tmp)415 void* tmp_thread(void* tmp) {
416   NFCSTATUS status = NFCSTATUS_SUCCESS;
417   uint16_t data_len;
418   NXPLOG_NCIHAL_W("tmp_thread: enter type=0x0%x", *((int*)tmp));
419   usleep(10 * 1000);
420 
421   switch (*((int*)tmp)) {
422     case START_POLLING: {
423       CONCURRENCY_LOCK();
424       data_len =
425           phNxpNciHal_write_unlocked(cmd_poll_len, cmd_poll, ORIG_NXPHAL);
426       CONCURRENCY_UNLOCK();
427 
428       if (data_len != cmd_poll_len) {
429         NXPLOG_NCIHAL_E("phNxpNciHal_start_polling_loop: data len mismatch");
430         status = NFCSTATUS_FAILED;
431       }
432     } break;
433 
434     case RESUME_POLLING: {
435       CONCURRENCY_LOCK();
436       data_len =
437           phNxpNciHal_write_unlocked(sizeof(cmd_resume_rf_discovery),
438                                      cmd_resume_rf_discovery, ORIG_NXPHAL);
439       CONCURRENCY_UNLOCK();
440 
441       if (data_len != sizeof(cmd_resume_rf_discovery)) {
442         NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop: data len mismatch");
443         status = NFCSTATUS_FAILED;
444       }
445     } break;
446 
447     case STOP_POLLING: {
448       CONCURRENCY_LOCK();
449       data_len = phNxpNciHal_write_unlocked(sizeof(cmd_stop_rf_discovery),
450                                             cmd_stop_rf_discovery, ORIG_NXPHAL);
451       CONCURRENCY_UNLOCK();
452 
453       if (data_len != sizeof(cmd_stop_rf_discovery)) {
454         NXPLOG_NCIHAL_E("phNxpNciHal_stop_polling_loop: data len mismatch");
455         status = NFCSTATUS_FAILED;
456       }
457     } break;
458 
459     case DISCOVER_SELECT: {
460       CONCURRENCY_LOCK();
461       data_len =
462           phNxpNciHal_write_unlocked(sizeof(cmd_select_rf_discovery),
463                                      cmd_select_rf_discovery, ORIG_NXPHAL);
464       CONCURRENCY_UNLOCK();
465 
466       if (data_len != sizeof(cmd_resume_rf_discovery)) {
467         NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop: data len mismatch");
468         status = NFCSTATUS_FAILED;
469       }
470     } break;
471 
472     default:
473       NXPLOG_NCIHAL_E("No Matching case");
474       status = NFCSTATUS_FAILED;
475       break;
476   }
477 
478   NXPLOG_NCIHAL_W("tmp_thread: exit");
479   pthread_exit(NULL);
480   return NULL;
481 }
482 /*******************************************************************************
483  **
484  ** Function         phNxpNciHal_select_RF_Discovery
485  **
486  ** Description     Sends RF_DISCOVER_SELECT_CMD
487  ** Parameters    RfID ,  RfProtocolType
488  ** Returns          NFCSTATUS_PENDING if success
489  **
490  *******************************************************************************/
phNxpNciHal_select_RF_Discovery(unsigned int RfID,unsigned int RfProtocolType)491 NFCSTATUS phNxpNciHal_select_RF_Discovery(unsigned int RfID,
492                                           unsigned int RfProtocolType) {
493   NFCSTATUS status = NFCSTATUS_SUCCESS;
494   pthread_t pthread;
495   discover_type = DISCOVER_SELECT;
496   cmd_select_rf_discovery[3] = RfID;
497   cmd_select_rf_discovery[4] = RfProtocolType;
498 
499   pthread_attr_t attr;
500   pthread_attr_init(&attr);
501   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
502   if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) {
503     NXPLOG_NCIHAL_E("fail to create pthread");
504   }
505   pthread_attr_destroy(&attr);
506   return status;
507 }
508 /*******************************************************************************
509 **
510 ** Function         phNxpNciHal_NfcDep_cmd_ext
511 **
512 ** Description      Stores the polling loop configuration locally.
513 **
514 ** Returns          None
515 **
516 *******************************************************************************/
phNxpNciHal_NfcDep_cmd_ext(uint8_t * p_cmd_data,uint16_t * cmd_len)517 void phNxpNciHal_NfcDep_cmd_ext(uint8_t* p_cmd_data, uint16_t* cmd_len) {
518   if (*cmd_len < NCI_HEADER_SIZE) return;
519   if (p_cmd_data[0] == 0x21 && p_cmd_data[1] == 0x03) {
520     if (*cmd_len == 6 && p_cmd_data[3] == 0x01 && p_cmd_data[4] == 0x02 &&
521         p_cmd_data[5] == 0x01) {
522       /* DO NOTHING */
523     } else {
524       if (*cmd_len > MAX_POLL_CMD_LEN) {
525         NXPLOG_NCIHAL_E("invalid cmd_len");
526         return;
527       }
528       /* Store the polling loop configuration */
529       cmd_poll_len = *cmd_len;
530       memset(&cmd_poll, 0, cmd_poll_len);
531       memcpy(&cmd_poll, p_cmd_data, cmd_poll_len);
532     }
533   }
534 
535   return;
536 }
537