This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2640R2F: GAP_DeviceDiscoveryCancel in BLE-Stack 3.00.01.25 does not generate GAP_DEVICE_DISCOVERY_EVENT as it used to on BLE-Stack 2.x

Part Number: CC2640R2F
Other Parts Discussed in Thread: CC2640, BLE-STACK

Hi,

In our existing product we have been using BLE-Stack 2.x with the CC2640, but I am working on upgrading our application to use BLE-Stack 3.x with the CC2640R2F (I'm still on 3.00.01.25, but based on release notes have no reason to believe our issue is any different in the newer versions).

We have a use case in which we need to restart discovery with different timing parameters so we call GAP_DeviceDiscoveryCancel and wait for the GAP_DEVICE_DISCOVERY_EVENT to notify us that we can restart discovery. In BLE-Stack 2.x this worked fine, but I've noticed that on BLE-Stack 3.x (at least in our firmware) we are never receiving the GAP_DEVICE_DISCOVERY_EVENT.

I'm wondering if this is an issue you're already aware of, or if you can reproduce it on your side? This forum post (from a year ago) seems to point to a similar problem but I don't think it was ever resolved: https://e2e.ti.com/support/wireless_connectivity/bluetooth_low_energy/f/538/t/530711

Any help would be appreciated!

Thanks,
Josh

P.S. Not as important, but worth noting. Even though the correct event is generated in BLE-Stack 2.x, the event status being returned bleGAPUserCanceled. Based on the name it makes sense that this is the status returned, but there is no mention of this in the documentation, someone might want to update the documentation to reflect this.

  • Hello Josh,

    You should receive the following GAP_DEVICE_DISCOVERY_EVENT event and status if you cancel the scan in BLE 3.0.1. Below is the output I captured from BTool using HostTest from SDK 1.35 / BLE-Stack 3.0.1:
    --------------------------------------------------------------------
    [40] : <Rx> - 02:12:51.445
    -Type : 0x04 (Event)
    -EventCode : 0x00FF (HCI_LE_ExtEvent)
    -Data Length : 0x06 (6) bytes(s)
    Event : 0x067F (1663) (GAP_HCI_ExtentionCommandStatus)
    Status : 0x00 (0) (Success)
    OpCode : 0xFE05 (GAP_DeviceDiscoveryCancel)
    DataLength : 0x00 (0)
    Dump(Rx):
    0000:04 FF 06 7F 06 00 05 FE 00 .........
    --------------------------------------------------------------------
    [41] : <Rx> - 02:12:51.455
    -Type : 0x04 (Event)
    -EventCode : 0x00FF (HCI_LE_ExtEvent)
    -Data Length : 0x04 (4) bytes(s)
    Event : 0x0601 (1537) (GAP_DeviceDiscoveryDone)
    Status : 0x30 (48) (The User Canceled The Task)
    NumDevs : 0x00 (0)
    Dump(Rx):
    0000:04 FF 04 01 06 30 00 .....0.
    --------------------------------------------------------------------

    Note that event 0x0601 is GAP_DEVICE_DISCOVERY_EVENT [hci_ext.h].

    Best wishes
  • Yeah, based on my experience with BLE-Stack 2.x this is exactly what I'd expect to see, unfortunately I still never receive the GAP_DEVICE_DISCOVERY_EVENT using BLE-Stack 3.x even after trying many different things.

    Some more background, the app and stack I am building is OBSERVER only. It's modeled after the simple_observer/simple_broadcaster example apps except we call GAP_* stack methods directly (using a single GAP message handler task instead of delegating to callbacks, to save overhead). This application code has been working for months in production code on a couple thousand CC2640 devices so I'm pretty confident that it should work as is, the CC2640R2F and BLE-Stack 3.x are the only new variables introduced (and the TI-RTOS ICALL_EVENT changes that went with it).

    Some things I've already tried (with no luck):

    • I've added error logging around all significant functions (GAP_DeviceDiscoveryRequest, GAP_DeviceDiscoveryCancel) and I always get SUCCESS, so there are no errors being reported
    • we keep our own local discovery state (IDLE, CANCELLING, ACTIVE), I tried switching to using the BLE-Stack's internal gapState in case we were out of sync (i.e. cancelling when not scanning, etc), but this changed nothing
    • I added a 1 second delay before calling GAP_DeviceDiscoveryCancel in case there was some race condition where discovery had not yet actually started, changed nothing
    • after seeing your response I also tried switching to CENTRAL + PERIPHERAL as in host_test (while also modifying our osal_icall_ble.c file) to see if it's a problem with just OBSERVER, no change

    I don't have any R2F evaluation modules to try out the simple_observer application, maybe someone with easier access can try that out? From looking at the firmware I believe you can start and cancel a 4000ms long scan using the right-key.

    Or if you have more visibility into the pre-compiled BLE-Stack binaries/ROM, maybe you can see any conditions that would prevent this event from being generated?

    In the meantime, I might try to make a dumbed-down version of our app (like simple_observer), and see if I can reproduce it on that (and maybe share code at some point).

    Thanks,
    Josh

  • I found the root cause of this issue and it's a bug in all existing versions of BLE-Stack 3.x that may affect anything using ICALL_LITE and ICALL_EVENTS.

    GAP_DeviceDiscoveryCancel with ICALL_LITE is just a macro that calls to the function icall_directAPI, which then dispatches API calls and waits for a response using ICall_waitMatch.

    The functions ICall_waitMatch and ICall_primWaitMatch both wait for a specific message type to be posted (specifically ICALL_LITE_DIRECT_API_DONE_CMD_ID). In the process of waiting for this message to post they dequeue messages and save off any that are irrelevant to them, finally prepending the irrelevant messages back onto the queue once they detect their specific message, or a timeout has expired.

    The way it is intended to work is that once any irrelevant messages are prepended back onto the task queue, the ICALL_MSG_EVENT_ID event is supposed to be reposted by ICall_primRepostSync. If the "prependQueue" is empty (or NULL) re-post does nothing, this is exactly what I saw happening in my case. Even though I saw the GAP_DEVICE_DISCOVERY_EVENT event being successfully queued, the ICALL_MSG_EVENT_ID to notify my application task that it should be dequeued was being swallowed by ICall_waitMatch.

    The bug is that the ICall_msgPrepend should always be called before ICall_primRepostSync, but in all version I looked at the order is reversed, so any messages that were on the queue before ICALL_LITE_DIRECT_API_DONE_CMD_ID will be dropped.

    The fix is to move the line (in BLE-Stack 3.00.01.25 it's at ICall_waitMatch:3619 and ICall_primWaitMatch:1563):

    ICall_msgPrepend(&taskentry->queue, prependQueue);

    before:

    #ifdef ICALL_EVENTS
    /*
    * Because Events are binary semaphores, the task's queue must be checked for
    * any remaining messages. If there are, the ICall event flag must be
    * re-posted due to it being cleared on the last pend.
    */
    ICall_primRepostSync();
    #endif //ICALL_EVENTS

    Once I made this modification my issue went away and I get the expected discovery event with status bleGAPUserCanceled.

    The reason you didn't see this bug is that it will not be seen if the first message in the queue is the expected wait message, or if the queue is not completely emptied when checking whether to repost the event. I'm sure the architecture of our application code is different enough from the sample apps that the order in which messages are queued may be wildly different.

    Hopefully I explained this well enough, let me know if there's anything else you need from me. We're going to start using a patched version of icall.c that fixes this until an official release is made.

    Thanks,
    Josh

  • Any updates on this? I wanted to make sure that just because I marked the issue as resolved that it doesn't fall through the cracks and never get fixed.

    Thanks,
    Josh

  • Hi Josh, I will start investigating this tomorrow and give you an update.
  • Hi Josh, 

    I'm missing on how switching the order of ICall_msgPrepend() and ICall_primRepostSync() fixes your issue. ICall_primRepostSync() simply posts a TI-RTOS event to its own task, which wouldn't cause any context changes or alter on what gets placed onto the queue.

    From what you're saying, your issue is that prependQueue is NULL. The only way I can see prependQueue to be NULL is if there are no messages were fetched and therefore nothing needs to be prepended.

    Can you confirm we are dealing with the same modifications?

    /c/ti/simplelink_cc2640r2_sdk_1_30_00_25/source/ti/blestack/icall/src/icall.c

  • Hi Tom,

    In my case prependQueue was not NULL, it contained the GAP_DEVICE_DISCOVERY_EVENT that I was expecting to receive but never did because ICall_waitMatch() did not restore the "event pending" status before it returned. It mistakenly did not do this because ICall_msgPrepend() was called after ICall_primRepostSync(), and the state of the event queue was empty.

    As you said ICall_primRepostSync() posts a TI-RTOS event (sets a flag to notify the application there are events to dequeue), however it only does this if the queue has events in it at the moment the function is called. Since the events that are stored in prependQueue are only added after calling this function (and there are no other events in the event queue), the function does nothing, even though the event pending flag should have been restored (since prependQueue was not empty).

    Here are the changes I ultimately made:

      /* Prepend retrieved irrelevant messages */
      ICall_msgPrepend(&taskentry->queue, prependQueue);
    
    #ifdef ICALL_EVENTS
    
      /* Because Events are binary semaphores, the task's queue must be checked for
       * any remaining messages.  If there are, the ICall event flag must be
       * re-posted due to it being cleared on the last pend.
       */
      ICall_primRepostSync();
    
    #else
    
      /* Re-increment the consumed semaphores */
      for (; consumedCount > 0; consumedCount--)
      {
        Semaphore_post(taskentry->syncHandle);
      }
    
    #endif /* ICALL_EVENTS */

    It also helps to see that the code under the ICALL_EVENTS compilation switch are attempting to do the same thing (post if prependQueue was not empty), but the (ICALL_EVENTS == FALSE) is called after the ICall_msgPrepend (even though it doesn't matter because it doesn't look at the current state of the queue, only that some events were dequeued which consumedCount tells you).

    Thanks,
    Josh