Skip to content

Bluetooth: HCI: Use H:4 encoding for buffers #88710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

jhedberg
Copy link
Member

@jhedberg jhedberg commented Apr 16, 2025

This PR changes the encoding of HCI packet types from net_buf metadata into the net_buf payload as a H:4 prefix byte. The bt_buf_get/set_type() APIs are deprecated but kept compatible with the change (i.e. they do net_buf_pull/push_u8() internally).

The PR also removes the experimental H:4 encoding support from the USB transport for several reasons:

  • It's non-standard
  • It doesn't have any real host-side counter part implementation (even BlueZ upstream doesn't support it)
  • It comes with substantial maintenance overhead
  • It complicates the HCI raw API needlessly
  • It had no CI coverage

Testing status for the drivers. Please check the box once you've tested your respective driver with this PR:

@jhedberg
Copy link
Member Author

This implements the first item in #88048, which was originally discussed at the recent Bluetooth WG F2F meeting.

@jhedberg
Copy link
Member Author

One positive realization I had when I worked on this change, is that actually most of our HCI drivers already internally used the H:4 encoding. This is the primary reason why the PR ends up removing about 300 lines of code.

@jhedberg jhedberg added area: Bluetooth Host Bluetooth Host (excluding BR/EDR) area: Bluetooth HCI Bluetooth HCI Driver labels Apr 16, 2025
@alwa-nordic
Copy link
Collaborator

alwa-nordic commented Apr 16, 2025

The existing bt_buf_set/get_type functions are kept compatible with the change, except that the get() function can only be called once, since it now pulls the byte from the buffer.

I get that this allows us to be partially backwards compatible, but the name of these functions is misleading now. I think the value of keeping them backwards compatible is worth more than a better name, but there may be a way so the function can be invoked multiple times: Doesn't the H4 type always land in the space net_buf_reserve-ed by buf.c? If so, we can just overwrite that byte if the reserved space is already used. Thoughts?

@@ -29,26 +30,19 @@ extern "C" {
#endif

/** Possible types of buffers passed around the Bluetooth stack in a form of bitmask. */
enum bt_buf_type {
enum __packed bt_buf_type {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not know we had packed enums. That's cool. We could potentially use these in hci_types.h instead of plain uint8_t.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not know we had packed enums. That's cool. We could potentially use these in hci_types.h instead of plain uint8_t.

Sounds like a good idea.

* @param timeout Non-negative waiting period to obtain a buffer or one of the
* special values K_NO_WAIT and K_FOREVER.
* @param data Initial data to append to buffer.
* @param size Initial data size.
* @return A new buffer.
*/
struct net_buf *bt_buf_get_tx(enum bt_buf_type type, k_timeout_t timeout,
struct net_buf *bt_buf_get_tx(uint8_t type, k_timeout_t timeout,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to maintain some level of type information here. Either we should rename the argument to h4_type, or seeing as the enum is packed, we keep using the enum here. I suggest adding BUILD_ASSERT(sizeof(enum bt_buf_type) == sizeof(uint8_t)) in the latter case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to maintain some level of type information here. Either we should rename the argument to h4_type, or seeing as the enum is packed, we keep using the enum here. I suggest adding BUILD_ASSERT(sizeof(enum bt_buf_type) == sizeof(uint8_t)) in the latter case.

I think it'd be nice to try to use the enum everywhere, combined with the build assert.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved from uint8_t to enum bt_buf_type for these APIs.

Copy link
Collaborator

@alwa-nordic alwa-nordic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks good!

I have a minor fix that I nack for:

The other discussions are not hard nacking.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified sample/bluetooth/peripheral_hr worked normally on apollo4p_blue_kxr_evb and apollo3_evb with the change.

Comment on lines 241 to 246
have been deprecated, but are still usable, with the exception that the latter can only be
called once per buffer.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of the functions have the same caveat, not just the latter.

Can we be more precise here about the caveat? I think we have to mention that these function will now silently corrupt data unless the following assumptions hold.

  • bt_buf_get_type(buf) has to be called exactly once before buf is accessed.
  • bt_buf_set_type(buf) has to be called exactly once before it's handed to bt_hci_recv_t, with no accesses to buf in between.

Also, this is the migration guide, not the change log, so we need to say how to migrate away from the deprecated behavior. I.e. Verifying the above assumptions and substitute calls to bt_buf_get_type and bt_buf_set_type with net_buf_pull_u8 and net_buf_push_u8.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bt_buf_set_type(buf) has to be called exactly once before it's handed to bt_hci_recv_t, with no accesses to buf in between.

HCI drivers don't generally use this API at all, rather they use the various buffer allocation APIs (like bt_buf_get_rx()) which implicitly add the type prefix to the buffer. If you grep under drivers/bluetooth you wont find a single user of it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of the functions have the same caveat, not just the latter.

Fixed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alwa-nordic I did some more thinking about this, and I think hci_types.h is a justifiable place for the packet type prefix byte. I initially felt against this, since it's different from the rest of the HCI payload, which the drivers will just proxy unmodified back-and-forth, whereas the type is something that needs to be separately handled if the underlying transport doesn't use this format. However, while I made the claim that drivers/bluetooth.h should be the only interface that drivers should look at, I failed to consider the fact that most drivers either way have to be aware of the packet type-specific header, in order to determine the packet length, and the type-specific header (event, ACL data, etc) is part of hci_types.h. Drivers currently also need to peek at the event definitions to make the "discardable" vs "not discardable" distinction.

So, I'll take another stab at keeping buf.h (i.e. enum bt_buf_type) backwards compatible. I'm still not quite comfortable with keeping "H4" in the other enumeration name, since "H:4" really is about an entire transport and not just about packet types. I'd rather just strictly document that the values defined based on those values from the transport spec. We don't even have to document it just in reference to H:4, but can say that "the possible values are are commands (1), ACL (2), SCO (3), events (4) and ISO (5) as specified in the UART and 3-Wire UART transport specs" (and extend the reference list to other transports if/when they show up in future specs with the same mapping from value to packet type). So while the documentation makes these references, I think I'd still prefer to keep the enum name generic, i.e. enum bt_hci_pkt_type, however that could be done as a follow-up step and just use our existing H4 defines from hci_types.h for now.

Copy link
Member Author

@jhedberg jhedberg Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed these changes now. Doesn't look that bad, however there's some inefficiency introduced with the conversions. In some case we even have to do the conversion twice, such as for some bt_hci_raw users like the hci_uart app: For data coming from the host the app first has to convert to enum bt_buf_type to be able to call bt_buf_get_tx(), which in turn internally has convert back to H:4 to be able to prepend the right byte.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I've now pushed a rebased & squashed patch set. I had to simplify some parts in the commit split, since it wasn't (trivially) possible to include all changes cleanly to the original commit set (I basically recreated all commits as far as it made sense).

@kartben kartben removed their assignment Apr 23, 2025
@jhedberg jhedberg force-pushed the bt_buf_type branch 4 times, most recently from e2b87df to 5484227 Compare April 23, 2025 15:57
@jhedberg
Copy link
Member Author

@andrzej-kaczmarek @sylvioalves @npal-cy @yeaissa @HoZHel @asm5878 @dchat-nordic: Could you please take a look at the HCI driver testing checklist in this PR description, and mark each driver as tested once you (or someone else with the respective HW) have tested it with this PR? Thanks!

@jhedberg jhedberg force-pushed the bt_buf_type branch 6 times, most recently from ca64efb to d105150 Compare April 25, 2025 07:04
@yeaissa
Copy link
Contributor

yeaissa commented Apr 25, 2025

@andrzej-kaczmarek @sylvioalves @npal-cy @yeaissa @HoZHel @asm5878 @dchat-nordic: Could you please take a look at the HCI driver testing checklist in this PR description, and mark each driver as tested once you (or someone else with the respective HW) have tested it with this PR? Thanks!

I checked on NXP platform using hci_nxp.c, no regression noticed. Thanks for the change @jhedberg.
I'm not sure I can make the change on your description. Please go ahead and mark it. Thanks.

Copy link
Collaborator

@Thalley Thalley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jhedberg have you seen any significant changes in memory usage with this PR?

Comment on lines 55 to 58
/** Direction of HCI packets */
enum bt_buf_dir {
BT_BUF_IN,
BT_BUF_OUT,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the point of view of the host?

We could consider making the directions absolute rather than relative like we have for ISO data paths:

#define BT_HCI_DATAPATH_DIR_HOST_TO_CTLR 0x00
#define BT_HCI_DATAPATH_DIR_CTLR_TO_HOST 0x01

The same argument can be applied for the ACL and ISO IN/OUT, but if we modify one, we should probably modify all, which seems outside the scope of this PR

Copy link
Member Author

@jhedberg jhedberg Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the point of view of the host?

From the point of view of BT_BUF_*_IN/OUT values when converting from a H:4 type (which don't convey any direction info by themselves).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a clarifying sentence to the docstring above the enum definition.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also worth noting that this entire public header will hopefully disappear soon, so I wouldn't put too much effort into trying to micro-optimize it :)

@Thalley Thalley requested a review from Copilot April 25, 2025 08:54
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR revises the HCI packet type encoding by moving the H:4 type byte from net_buf metadata into the net_buf payload. It deprecates the bt_buf_get/set_type() APIs and removes the experimental H:4 USB transport support.

  • Remove H:4 mode enums, APIs, and raw-mode functions from hci_raw.h
  • Update buffer API functions in buf.h to use H:4 prefix encoding
  • Modify multiple HCI transport drivers to directly use the payload’s first byte for packet type

Reviewed Changes

Copilot reviewed 52 out of 53 changed files in this pull request and generated no comments.

File Description
include/zephyr/bluetooth/hci_raw.h Removed legacy H:4 mode definitions and raw-channel APIs
include/zephyr/bluetooth/buf.h Updated buffer type conversion functions and deprecation of old APIs
drivers/bluetooth/hci/* Replaced bt_buf_get/set_type() invocations with direct payload access
drivers/bluetooth/hci/h4.c Removed redundant type handling before UART transmission
Files not reviewed (1)
  • doc/releases/migration-guide-4.2.rst: Language not supported

@jhedberg
Copy link
Member Author

@jhedberg have you seen any significant changes in memory usage with this PR?

I haven't done any profiling, but I'd expect to see at least a decrease in code size (if you just look at the diffstat). On aggregate it's a big reduction in code, however it'll be spread out over many platforms (a little for each HCI driver) so any single build wont show the same magnitude as the PR as a whole.

Encode the packet type as a H:4 payload prefix for buffers passing to &
from HCI drivers. The existing bt_buf_set/get_type functions are
deprecated, but kept compatible with the change, except that they can only
be called once, since they modify the buffer payload.

Signed-off-by: Johan Hedberg <[email protected]>
Update all HCI drivers to use the new H:4 encoding for buffers passing
to/from drivers.

One behavioral change that's done in favor of simplicity, is that where
there's previously been switch statements that could return an error for
unsupported packet types now simply pass any received packet unchanged to
lower layers of the controller (or the HCI transport). Handling this is
now the responsibility of the lower layers, however in practice hitting
such scenarios means that there's a mismatch between configured host and
controller features.

Signed-off-by: Johan Hedberg <[email protected]>
Update the Bluetooth tests to assume the new H:4 encoding for data that's
passing between HCI drivers and their users (normally the host stack).

Signed-off-by: Johan Hedberg <[email protected]>
This non-standard feature never had any proper host side implementation
(e.g. it was never upstreamed to BlueZ), and since it comes with notable
maintenance overhead it's fair to just remove it.

Signed-off-by: Johan Hedberg <[email protected]>
Mention the changes that have happened to the Bluetooth HCI driver
interface regarding packet type encoding.

Signed-off-by: Johan Hedberg <[email protected]>
The documentation for the HCI raw interface was incorrectly talking about
the need to define buffer pools, when in fact the hci_raw module provides
the necessary API to perform buffer allocation.

Signed-off-by: Johan Hedberg <[email protected]>
Copy link
Collaborator

@alwa-nordic alwa-nordic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am quite happy with how this looks now. I found a small mistake:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Bluetooth HCI Bluetooth HCI Driver area: Bluetooth Host Bluetooth Host (excluding BR/EDR)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants