Zephyr: Application callback on PDU transmission complete

Created on 18 May 2018  路  12Comments  路  Source: zephyrproject-rtos/zephyr

A callback that notifies the application that a PDU (L2CAP or ATT) has been successfully transmitted to the peer. It would essentially follow the flow of HCI Number of Completed Packets with additional counting to make sure a single callback per PDU is issued.

Feature Bluetooth

Most helpful comment

Now about the use case. To create a responsive HID device:

  • we need to be able to create a round robin of notifications,
  • we also cannot allow more then one notification to be in the transmission buffer.

With current infrastructure we could create a thread that sends the notifications and pass data to it to be sent. This way we would be able to perform round robin as we would control the flow. There are however two problems here.

  • First, we will block on a second notifications being sent. Imagine data creation module creates and sends packet1 (accepted), then immediately packet2 (this will block). After packet1 is sent, packet2 is accepted and creator blocks on packet3. And so on. Note that in this case data producer is always 1 interval late (pipeline is always 2 elements - one being sent and one that is blocked).
  • Second, we need a thread to block, and this requires RAM for stack and complicates the design.

With callback we race with the next interval to create data but data is always fresh. Also as we are notified no thread is be needed and RAM is spared.

All 12 comments

@pdunaj we would like to have a usecase for this functionality. Can you let us know why exactly you need this? Thanks!

Hi @carlescufi ,

This is needed for a proper generation of a key events sequence sent to the host via HOG service.
In case the sequence of keys is pressed fast we need to make sure that:
1) each key event is transmitted separately (to maintain the order in which sequence was recorded)
2) no key event is missed (obvious reasons).

For example. Let's say that user pressed A quickly. We should send two notifications: first notification with "A" and then empty notification (indicating key released). If due to earlier events notification cannot be added to the queue we will lose the key press or key release (or both).

Perhaps Im missing something but if bt_gatt_notify cannot fail due to not be able to be added to the queue, it will block until a buffer becomes available, or perhaps let me ask if there has been reproduced already or it a theoretical problem? Also what guarantee do we have that once a key failed to be delivered it won't fail again? If the keys are pressed in a quick succession and one of them fails to be sent, would you resend it? That means one will have to reference all buffers queued and so they can be resent before the others which I don't think it a good application API, so I wonder if for this use case the application actually has to queue by itself and not flush everything to bt_gatt_notify and risk it to block.

Also because bt_gatt_notify blocks it must not be used in interrupt context which I assume is where the inputs would be generated, that means it will need to be deferred to a thread which might already be using a fifo/queue, that way there should already be a queue in between so when that thread is using bt_gatt_notify if that blocks nothing is lost since the inputs can still be queued and the thread will be resumed once a buffer becomes available and then bt_gatt_notify can be called again, so I don't see how we can fail to send a packet because of queueing, except if interrupt cannot queue anything anymore but that has nothing to do with Bluetooth.

Hi @Vudentz , I don't think bt_gatt_notify blocks. I don't see any wait in the code and practical test (I tried to send 50 notifies in a loop) shows only 15 notifications are queued. Rest is simply ignored with no notification to the application.
As for the application side, I don't really mind to queue the notifications here. I only would like to get notified when there is no space to send key event sequence. This way we would be able to react (e.g. drop remaining events, clear state, etc.) to maintain the sanity of the state.
Note that, to save energy, we don't want to transmit the state periodically. If we sent key release but this notification is dropped host may keep key pressed (until we press and release the key again on the device).

Hi @pdunaj Im pretty sure it blocks if you send more packets than there are buffers:

https://github.com/zephyrproject-rtos/zephyr/blob/master/subsys/bluetooth/host/gatt.c#L630
https://github.com/zephyrproject-rtos/zephyr/blob/master/subsys/bluetooth/host/att.c#L1944
https://github.com/zephyrproject-rtos/zephyr/blob/master/subsys/bluetooth/host/conn.c#L1935

If it would not block it would fail immediately so it is really surprising to me that you could lose packets since we never discard them or anything. In the other hand if bt_gatt_notify blocks as it should it would stop sending and then resume as soon as the driver free a buffer, and you can actually have multiple threads blocking in the same manner.

Hi all,
I would like to get back to this issue. I have run tests today and the code blocks. I must have done some mistake when I tried it earlier.

Now about the use case. To create a responsive HID device:

  • we need to be able to create a round robin of notifications,
  • we also cannot allow more then one notification to be in the transmission buffer.

With current infrastructure we could create a thread that sends the notifications and pass data to it to be sent. This way we would be able to perform round robin as we would control the flow. There are however two problems here.

  • First, we will block on a second notifications being sent. Imagine data creation module creates and sends packet1 (accepted), then immediately packet2 (this will block). After packet1 is sent, packet2 is accepted and creator blocks on packet3. And so on. Note that in this case data producer is always 1 interval late (pipeline is always 2 elements - one being sent and one that is blocked).
  • Second, we need a thread to block, and this requires RAM for stack and complicates the design.

With callback we race with the next interval to create data but data is always fresh. Also as we are notified no thread is be needed and RAM is spared.

@pdunaj thanks for the explanation, this is clear enough. Adding @joerchan.

@carlescufi I would prefer to have something like HVN_TX_COMPLETE from SDK. This was used in SR3 to trigger data processing. Having this callback would give us just that. If there is any cheaper way of telling that we are CTS maybe we can use it but blocking on notify seems not good option.

@carlescufi Thank you. Sorry it took so long - I lost focus on BLE part for some time but we need to catch up with it reasonably fast.

@pdunaj yep, makes sense to me to have the (optional) callback.

@Vudentz would you mind taking a look at this comment?

Was this page helpful?
0 / 5 - 0 ratings