| 1 | /* |
| 2 | * Copyright (C) 2012 Jolla Ltd. |
| 3 | * Contact: Aaron McCarthy <aaron.mccarthy@jollamobile.com> |
| 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 | #include <assert.h> |
| 20 | #include <stdio.h> |
| 21 | #include <pthread.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <getopt.h> |
| 24 | |
| 25 | #include <hardware/hardware.h> |
| 26 | #include <hardware/nfc.h> |
| 27 | #include <libnfc-nxp/phLibNfc.h> |
| 28 | #include <libnfc-nxp/phDal4Nfc_messageQueueLib.h> |
| 29 | |
| 30 | static int messageThreadRunning = 0; |
| 31 | static int numberOfDiscoveredTargets = 0; |
| 32 | static phLibNfc_RemoteDevList_t *discoveredTargets = 0; |
| 33 | static NFCSTATUS targetStatus = 0xFFFF; |
| 34 | static phLibNfc_sRemoteDevInformation_t *connectedRemoteDevInfo = 0; |
| 35 | static phLibNfc_ChkNdef_Info_t ndefInfo; |
| 36 | static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; |
| 37 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
| 38 | |
| 39 | static void *messageThreadFunc(void *arg) |
| 40 | { |
| 41 | int *clientId = (int *)arg; |
| 42 | messageThreadRunning = 1; |
| 43 | while (messageThreadRunning) { |
| 44 | phDal4Nfc_Message_Wrapper_t message; |
| 45 | int ret = phDal4Nfc_msgrcv(*clientId, &message, |
| 46 | sizeof(phLibNfc_Message_t), 0, 0); |
| 47 | if (ret == -1) { |
| 48 | fprintf(stderr, "Failed to receive message from NFC stack.\n"); |
| 49 | continue; |
| 50 | } |
| 51 | |
| 52 | switch (message.msg.eMsgType) { |
| 53 | case PH_LIBNFC_DEFERREDCALL_MSG: { |
| 54 | phLibNfc_DeferredCall_t *msg = (phLibNfc_DeferredCall_t *)message.msg.pMsgData; |
| 55 | if (msg->pCallback) |
| 56 | msg->pCallback(msg->pParameter); |
| 57 | break; |
| 58 | } |
| 59 | default: |
| 60 | fprintf(stderr, "Unknown message type %d.", message.msg.eMsgType); |
| 61 | } |
| 62 | |
| 63 | pthread_cond_signal(&cond); |
| 64 | } |
| 65 | |
| 66 | return 0; |
| 67 | } |
| 68 | |
| 69 | static pthread_t createMessageThread(void *arg) |
| 70 | { |
| 71 | pthread_t thread_id; |
| 72 | int error = pthread_create(&thread_id, NULL, messageThreadFunc, arg); |
| 73 | if (error != 0) |
| 74 | return 0; |
| 75 | |
| 76 | return thread_id; |
| 77 | } |
| 78 | |
| 79 | static void terminateMessageThread(int clientId) |
| 80 | { |
| 81 | messageThreadRunning = 0; |
| 82 | |
| 83 | phDal4Nfc_Message_Wrapper_t message; |
| 84 | phLibNfc_DeferredCall_t *msg; |
| 85 | |
| 86 | msg = (phLibNfc_DeferredCall_t *)malloc(sizeof(phLibNfc_DeferredCall_t)); |
| 87 | msg->pCallback = 0; |
| 88 | msg->pParameter = 0; |
| 89 | message.msg.eMsgType = PH_LIBNFC_DEFERREDCALL_MSG; |
| 90 | message.msg.pMsgData = msg; |
| 91 | message.msg.Size = sizeof(phLibNfc_DeferredCall_t); |
| 92 | |
| 93 | phDal4Nfc_msgsnd(clientId, &message, sizeof(phLibNfc_Message_t), 0); |
| 94 | } |
| 95 | |
| 96 | void initializeCallback(void *pContext, NFCSTATUS status) |
| 97 | { |
| 98 | NFCSTATUS *callbackStatus = (NFCSTATUS *)pContext; |
| 99 | *callbackStatus = status; |
| 100 | } |
| 101 | |
| 102 | void discoveryNotificationCallback(void *pContext, phLibNfc_RemoteDevList_t *psRemoteDevList, |
| 103 | uint8_t uNofRemoteDev, NFCSTATUS status) |
| 104 | { |
| 105 | if (status == NFCSTATUS_DESELECTED) { |
| 106 | fprintf(stderr, "Target deselected\n"); |
| 107 | } else { |
| 108 | fprintf(stderr, "Discovered %d targets\n", uNofRemoteDev); |
| 109 | |
| 110 | uint8_t i; |
| 111 | for (i = 0; i < uNofRemoteDev; ++i) { |
| 112 | fprintf(stderr, "Target[%d]\n\tType: %d\n\tSession: %d\n", i, |
| 113 | psRemoteDevList[i].psRemoteDevInfo->RemDevType, |
| 114 | psRemoteDevList[i].psRemoteDevInfo->SessionOpened); |
| 115 | } |
| 116 | numberOfDiscoveredTargets = uNofRemoteDev; |
| 117 | discoveredTargets = psRemoteDevList; |
| 118 | targetStatus = status; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | void discoveryCallback(void *pContext, NFCSTATUS status) |
| 123 | { |
| 124 | fprintf(stderr, "discoveryCallback %d\n", status); |
| 125 | } |
| 126 | |
| 127 | void remoteDevConnectCallback(void *pContext, phLibNfc_Handle hRemoteDev, |
| 128 | phLibNfc_sRemoteDevInformation_t *psRemoteDevInfo, NFCSTATUS status) |
| 129 | { |
| 130 | targetStatus = status; |
| 131 | connectedRemoteDevInfo = psRemoteDevInfo; |
| 132 | } |
| 133 | |
| 134 | void remoteDevNdefReadCheckCallback(void *pContext, phLibNfc_ChkNdef_Info_t Ndef_Info, |
| 135 | NFCSTATUS status) |
| 136 | { |
| 137 | ndefInfo = Ndef_Info; |
| 138 | targetStatus = status; |
| 139 | } |
| 140 | |
| 141 | void remoteDevNdefReadCallback(void *pContext, NFCSTATUS status) |
| 142 | { |
| 143 | targetStatus = status; |
| 144 | } |
| 145 | |
| 146 | void testNfc(int readNdefMessages) |
| 147 | { |
| 148 | printf("Starting test_nfc.\n"); |
| 149 | |
| 150 | const hw_module_t *hwModule = 0; |
| 151 | nfc_pn544_device_t *nfcDevice = 0; |
| 152 | |
| 153 | printf("Finding NFC hardware module.\n"); |
| 154 | hw_get_module(NFC_HARDWARE_MODULE_ID, &hwModule); |
| 155 | assert(hwModule != NULL); |
| 156 | |
| 157 | printf("Opening NFC device.\n"); |
| 158 | assert(nfc_pn544_open(hwModule, &nfcDevice) == 0); |
| 159 | assert(nfcDevice != 0); |
| 160 | |
| 161 | assert(nfcDevice->num_eeprom_settings != 0); |
| 162 | assert(nfcDevice->eeprom_settings); |
| 163 | |
| 164 | printf("Configuring NFC driver.\n"); |
| 165 | phLibNfc_sConfig_t driverConfig; |
| 166 | driverConfig.nClientId = phDal4Nfc_msgget(0, 0600); |
| 167 | assert(driverConfig.nClientId); |
| 168 | |
| 169 | void *hwRef; |
| 170 | NFCSTATUS status = phLibNfc_Mgt_ConfigureDriver(&driverConfig, &hwRef); |
| 171 | assert(hwRef); |
| 172 | assert(status == NFCSTATUS_SUCCESS); |
| 173 | |
| 174 | pthread_t messageThread = createMessageThread(&driverConfig.nClientId); |
| 175 | |
| 176 | printf("Initializing NFC stack.\n"); |
| 177 | NFCSTATUS callbackStatus = 0xFFFF; |
| 178 | status = phLibNfc_Mgt_Initialize(hwRef, initializeCallback, &callbackStatus); |
| 179 | assert(status == NFCSTATUS_PENDING); |
| 180 | |
| 181 | pthread_mutex_lock(&mut); |
| 182 | while (callbackStatus == 0xFFFF) |
| 183 | pthread_cond_wait(&cond, &mut); |
| 184 | pthread_mutex_unlock(&mut); |
| 185 | |
| 186 | assert(callbackStatus == NFCSTATUS_SUCCESS); |
| 187 | |
| 188 | printf("Getting NFC stack capabilities.\n"); |
| 189 | phLibNfc_StackCapabilities_t capabilities; |
| 190 | status = phLibNfc_Mgt_GetstackCapabilities(&capabilities, &callbackStatus); |
| 191 | assert(status == NFCSTATUS_SUCCESS); |
| 192 | |
| 193 | printf("NFC capabilities:\n" |
| 194 | "\tHAL version: %u\n" |
| 195 | "\tFW version: %u\n" |
| 196 | "\tHW version: %u\n" |
| 197 | "\tModel: %u\n" |
| 198 | "\tHCI version: %u\n" |
| 199 | "\tVendor: %s\n" |
| 200 | "\tFull version: %u %u\n" |
| 201 | "\tFW Update: %u\n", |
| 202 | capabilities.psDevCapabilities.hal_version, capabilities.psDevCapabilities.fw_version, |
| 203 | capabilities.psDevCapabilities.hw_version, capabilities.psDevCapabilities.model_id, |
| 204 | capabilities.psDevCapabilities.hci_version, |
| 205 | capabilities.psDevCapabilities.vendor_name, |
| 206 | capabilities.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-1], |
| 207 | capabilities.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-2], |
| 208 | capabilities.psDevCapabilities.firmware_update_info); |
| 209 | |
| 210 | if (readNdefMessages) { |
| 211 | /* Start tag discovery */ |
| 212 | phLibNfc_Registry_Info_t registryInfo; |
| 213 | |
| 214 | registryInfo.MifareUL = 1; |
| 215 | registryInfo.MifareStd = 1; |
| 216 | registryInfo.ISO14443_4A = 1; |
| 217 | registryInfo.ISO14443_4B = 1; |
| 218 | registryInfo.Jewel = 1; |
| 219 | registryInfo.Felica = 1; |
| 220 | registryInfo.NFC = 1; |
| 221 | registryInfo.ISO15693 = 1; |
| 222 | |
| 223 | int context; |
| 224 | status = phLibNfc_RemoteDev_NtfRegister(®istryInfo, discoveryNotificationCallback, &context); |
| 225 | assert(status == NFCSTATUS_SUCCESS); |
| 226 | |
| 227 | phLibNfc_sADD_Cfg_t discoveryConfig; |
| 228 | discoveryConfig.NfcIP_Mode = phNfc_eP2P_ALL; |
| 229 | discoveryConfig.NfcIP_Target_Mode = 0x0E; |
| 230 | discoveryConfig.Duration = 300000; |
| 231 | discoveryConfig.NfcIP_Tgt_Disable = 0; |
| 232 | discoveryConfig.PollDevInfo.PollCfgInfo.EnableIso14443A = 1; |
| 233 | discoveryConfig.PollDevInfo.PollCfgInfo.EnableIso14443B = 1; |
| 234 | discoveryConfig.PollDevInfo.PollCfgInfo.EnableFelica212 = 1; |
| 235 | discoveryConfig.PollDevInfo.PollCfgInfo.EnableFelica424 = 1; |
| 236 | discoveryConfig.PollDevInfo.PollCfgInfo.EnableIso15693 = 1; |
| 237 | discoveryConfig.PollDevInfo.PollCfgInfo.EnableNfcActive = 1; |
| 238 | discoveryConfig.PollDevInfo.PollCfgInfo.DisableCardEmulation = 1; |
| 239 | |
| 240 | targetStatus = 0xFFFF; |
| 241 | status = phLibNfc_Mgt_ConfigureDiscovery(NFC_DISCOVERY_CONFIG, discoveryConfig, |
| 242 | discoveryCallback, &context); |
| 243 | |
| 244 | for (;;) { |
| 245 | pthread_mutex_lock(&mut); |
| 246 | while (targetStatus == 0xFFFF) |
| 247 | pthread_cond_wait(&cond, &mut); |
| 248 | pthread_mutex_unlock(&mut); |
| 249 | |
| 250 | fprintf(stderr, "Discovered %d targets\n", numberOfDiscoveredTargets); |
| 251 | if (numberOfDiscoveredTargets > 0) { |
| 252 | targetStatus = 0xFFFF; |
| 253 | status = phLibNfc_RemoteDev_Connect(discoveredTargets[0].hTargetDev, |
| 254 | remoteDevConnectCallback, &context); |
| 255 | if (status == NFCSTATUS_PENDING) { |
| 256 | pthread_mutex_lock(&mut); |
| 257 | while (targetStatus == 0xFFFF) |
| 258 | pthread_cond_wait(&cond, &mut); |
| 259 | pthread_mutex_unlock(&mut); |
| 260 | |
| 261 | if (targetStatus == NFCSTATUS_SUCCESS) { |
| 262 | targetStatus = 0xFFFF; |
| 263 | status = phLibNfc_Ndef_CheckNdef(discoveredTargets[0].hTargetDev, |
| 264 | remoteDevNdefReadCheckCallback, &context); |
| 265 | |
| 266 | pthread_mutex_lock(&mut); |
| 267 | while (targetStatus == 0xFFFF) |
| 268 | pthread_cond_wait(&cond, &mut); |
| 269 | pthread_mutex_unlock(&mut); |
| 270 | |
| 271 | if (targetStatus == NFCSTATUS_SUCCESS && |
| 272 | (ndefInfo.NdefCardState == PHLIBNFC_NDEF_CARD_READ_WRITE || |
| 273 | ndefInfo.NdefCardState == PHLIBNFC_NDEF_CARD_READ_ONLY)) { |
| 274 | phLibNfc_Data_t ndefBuffer; |
| 275 | ndefBuffer.length = ndefInfo.MaxNdefMsgLength; |
| 276 | ndefBuffer.buffer = malloc(ndefBuffer.length); |
| 277 | |
| 278 | targetStatus = 0xFFFF; |
| 279 | status = phLibNfc_Ndef_Read(discoveredTargets[0].hTargetDev, &ndefBuffer, |
| 280 | phLibNfc_Ndef_EBegin, remoteDevNdefReadCallback, &context); |
| 281 | |
| 282 | pthread_mutex_lock(&mut); |
| 283 | while (targetStatus == 0xFFFF) |
| 284 | pthread_cond_wait(&cond, &mut); |
| 285 | pthread_mutex_unlock(&mut); |
| 286 | |
| 287 | if (targetStatus == NFCSTATUS_SUCCESS) { |
| 288 | int i; |
| 289 | fprintf(stderr, "NDEF: "); |
| 290 | for (i = 0; i < ndefBuffer.length; ++i) |
| 291 | fprintf(stderr, "%02x", ndefBuffer.buffer[i]); |
| 292 | fprintf(stderr, "\n"); |
| 293 | } |
| 294 | |
| 295 | free(ndefBuffer.buffer); |
| 296 | } |
| 297 | } |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | if (status == NFCSTATUS_FAILED) { |
| 302 | fprintf(stderr, "Failed to connect to remote device\n"); |
| 303 | break; |
| 304 | } |
| 305 | |
| 306 | targetStatus = 0xFFFF; |
| 307 | status = phLibNfc_Mgt_ConfigureDiscovery(NFC_DISCOVERY_RESUME, discoveryConfig, |
| 308 | discoveryCallback, &context); |
| 309 | assert(status == NFCSTATUS_SUCCESS || status == NFCSTATUS_PENDING); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | printf("Deinitializing NFC stack.\n"); |
| 314 | callbackStatus = 0xFFFF; |
| 315 | status = phLibNfc_Mgt_DeInitialize(hwRef, initializeCallback, &callbackStatus); |
| 316 | assert(status == NFCSTATUS_PENDING); |
| 317 | |
| 318 | pthread_mutex_lock(&mut); |
| 319 | while (callbackStatus == 0xFFFF) |
| 320 | pthread_cond_wait(&cond, &mut); |
| 321 | pthread_mutex_unlock(&mut); |
| 322 | |
| 323 | assert(callbackStatus == NFCSTATUS_SUCCESS); |
| 324 | |
| 325 | terminateMessageThread(driverConfig.nClientId); |
| 326 | pthread_join(messageThread, NULL); |
| 327 | |
| 328 | printf("Unconfiguring NFC driver.\n"); |
| 329 | status = phLibNfc_Mgt_UnConfigureDriver(hwRef); |
| 330 | assert(status == NFCSTATUS_SUCCESS); |
| 331 | |
| 332 | int result = phDal4Nfc_msgctl(driverConfig.nClientId, 0, 0); |
| 333 | assert(result == 0); |
| 334 | |
| 335 | printf("Closing NFC device.\n"); |
| 336 | nfc_pn544_close(nfcDevice); |
| 337 | } |
| 338 | |
| 339 | int main(int argc, char **argv) |
| 340 | { |
| 341 | int opt; |
| 342 | int readNdefMessages = 0; |
| 343 | int restartOnFailure = 0; |
| 344 | |
| 345 | while ((opt = getopt(argc, argv, "nrh")) != -1) { |
| 346 | switch (opt) { |
| 347 | case 'n': |
| 348 | readNdefMessages = 1; |
| 349 | fprintf(stdout, "Reading NDEF messages from targets\n"); |
| 350 | break; |
| 351 | case 'r': |
| 352 | restartOnFailure = 1; |
| 353 | readNdefMessages = 1; |
| 354 | fprintf(stdout, "Restarting on NDEF read failure, implies -n\n"); |
| 355 | break; |
| 356 | case 'h': |
| 357 | default: |
| 358 | fprintf(stderr, "\n Usage: %s \n" |
| 359 | "\t-n Read NDEF message from targets,\n" |
| 360 | "\t-r Restart on NDEF read failure.\n", argv[0]); |
| 361 | return 1; |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | do { |
| 366 | testNfc(readNdefMessages); |
| 367 | } while (restartOnFailure && readNdefMessages); |
| 368 | |
| 369 | return 0; |
| 370 | } |