Compare with EdgeFast Bluetooth PAL#
This document provides a detailed comparison between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL, highlighting the differences in features, examples, and APIs.
Features comparison#
The following table compares the features between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
Feature |
EdgeFast Open |
EdgeFast Bluetooth PAL |
|---|---|---|
Bluetooth Core Specification |
5.3 |
5.0 |
Bluetooth Host Stack |
Zephyr-based |
Proprietary with Zephyr API compatible |
Bluetooth Controller |
Supported (NXP controller) |
Supported (NXP controller) |
Mode |
LE and Classic |
LE and Classic |
Protocol Support |
L2CAP, GAP, GATT, RFCOMM, SDP, SM, SPP |
L2CAP, GAP, GATT, RFCOMM, SM, SPP |
Classic Profiles |
A2DP, HFP, AVRCP, AVRCP Cover Art, GOEP, BIP, MAP, PBAP |
A2DP, HFP, AVRCP, MAP, PBAP |
LE Profiles |
HTP, PXP, IPSP, HPS, GATT-based services |
HTP, PXP, IPSP, HPS |
Enhanced Attribute (EATT) |
Supported |
Supported |
LE Audio |
Supported |
Supported |
Examples comparison#
The following table compares the available examples between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
Example |
EdgeFast Open |
EdgeFast Bluetooth PAL |
|---|---|---|
a2dp_bridge |
Supported |
Not supported |
a2dp_sink |
Supported |
Supported |
a2dp_source |
Supported |
Supported |
bmr_4bis |
Supported |
Supported |
bms_4bis |
Supported |
Supported |
broadcast_media_receiver |
Supported |
Supported |
broadcast_media_sender |
Supported |
Supported |
call_gateway |
Supported |
Supported |
call_terminal |
Supported |
Supported |
central_fmp |
Supported |
Supported |
central_hpc |
Supported |
Supported |
central_ht |
Supported |
Supported |
central_ipsp |
Supported |
Supported |
central_pxm |
Supported |
Supported |
central_tip |
Supported |
Supported |
handsfree |
Supported |
Supported |
handsfree_ag |
Supported |
Supported |
map_mce |
Supported |
Supported |
map_mse |
Supported |
Supported |
pbap_pce |
Supported |
Supported |
pbap_pse |
Supported |
Supported |
peripheral_beacon |
Supported |
Supported |
peripheral_fmp |
Supported |
Supported |
peripheral_hps |
Supported |
Supported |
peripheral_ht |
Supported |
Supported |
peripheral_ipsp |
Supported |
Supported |
peripheral_pxr |
Supported |
Supported |
peripheral_tip |
Supported |
Supported |
sco_bridge |
Supported |
Not supported |
shell |
Supported |
Supported |
spp |
Supported |
Supported |
tmap_central |
Supported |
Supported |
tmap_peripheral |
Supported |
Supported |
umr2bms |
Supported |
Supported |
umr_4cis |
Supported |
Supported |
ums_4cis |
Supported |
Supported |
ums_microphone |
Supported |
Supported |
unicast_media_receiver |
Supported |
Supported |
unicast_media_sender |
Supported |
Supported |
wifi_cli_over_ble_wu |
Supported |
Supported |
wireless_uart |
Supported |
Supported |
APIs comparison#
Workflow#
The following workflow compares the key different of API between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
The following diagram is a typical Egdefast Open workflow:

The following diagram is a typical EdgeFast Bluetooth PAL workflow:

API Differences#
Core Protocols#
Core Protocols Comparison#
There are no differences in the following protocols, including GAP, SM, SPP, and L2CAP (CBFC).
L2CAP Classic Comparison#
The following table compares the L2CAP Classic differences between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
Feature |
EdgeFast Open |
EdgeFast Bluetooth PAL |
|---|---|---|
Connection-oriented channels in Basic Mode |
Supported |
Supported |
Connection-oriented channels in Retransmission Mode |
Supported |
Not supported |
Connection-oriented channels in Flow control Mode |
Supported |
Not supported |
Connection-oriented channels in Enhanced Retransmission Mode |
Supported |
Supported |
Connection-oriented channels in Streaming Mode |
Supported |
Supported |
Connectionless data channel in Basic L2CAP mode |
Supported |
Not supported |
Signaling packet ECHO |
Supported |
Not supported |
The following compares the L2CAP Classic differences between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
Aspect |
EdgeFast Open |
EdgeFast Bluetooth PAL |
|---|---|---|
L2CAP architecture |
Zephyr native L2CAP |
PAL adds explicit L2CAP config negotiation |
Channel config |
Static fields in chan.rx.* |
Callback‑based config exchange |
Mode negotiation |
Implicit |
Explicit (get_cfg / cfg_req / cfg_rsp) |
Who drives config |
Local side before connect |
Both sides during config phase |
L2CAP Classic in EdgeFast Open#
static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
}
static void l2cap_connected(struct bt_l2cap_chan *chan)
{
}
static void l2cap_disconnected(struct bt_l2cap_chan *chan)
{
}
static struct net_buf *l2cap_alloc_buf(struct bt_l2cap_chan *chan)
{
}
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
static void seg_recv(struct bt_l2cap_chan *chan, size_t sdu_len, off_t seg_offset,
struct net_buf_simple *seg)
{
}
#endif /* CONFIG_BT_L2CAP_SEG_RECV */
static const struct bt_l2cap_chan_ops l2cap_ops = {
.alloc_buf = l2cap_alloc_buf,
.recv = l2cap_recv,
.connected = l2cap_connected,
.disconnected = l2cap_disconnected,
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
.seg_recv = seg_recv,
#endif /* CONFIG_BT_L2CAP_SEG_RECV */
};
static struct bt_l2cap_br_chan chan = {
.chan.ops = &l2cap_ops,
.rx.mtu = DATA_BREDR_MTU,
},
chan.rx.mode = <Mode>;
chan.rx.max_transmit = <max_transmit>;
chan.rx.optional = <Mode optional settings>;
chan.rx.extended_control = <extended_control>;
chan.rx.max_window = <max_window>;
int err = bt_l2cap_chan_connect(default_conn, &chan.chan, psm);
if (err < 0) {
PRINTF("Unable to connect to psm %u (err %d)\n", psm, err);
} else {
PRINTF("L2CAP connection pending\n");
}
L2CAP Classic in EdgeFast Bluetooth PAL#
void l2cap_mode_get_cfg(struct bt_l2cap_chan *chan, struct bt_l2cap_cfg_options *cfg)
{
/* Channel configuration handler */
/* `cfg` argument needs to be filled with the configuration options */
}
void l2cap_mode_cfg_req(struct bt_l2cap_chan *chan, struct bt_l2cap_cfg_options *cfg, struct bt_l2cap_cfg_options *rsp)
{
/* Channel configuration response handler */
/* `rsp` argument needs to be filled with the response configuration options */
}
void l2cap_mode_cfg_rsp(struct bt_l2cap_chan *chan, struct bt_l2cap_cfg_options *rsp)
{
/* Channel configuration response handler */
}
static const struct bt_l2cap_chan_ops l2cap_mode_ops = {
.alloc_buf = l2cap_alloc_buf,
.recv = l2cap_recv,
.connected = l2cap_connected,
.disconnected = l2cap_disconnected,
.get_cfg = l2cap_mode_get_cfg,
.cfg_req = l2cap_mode_cfg_req,
.cfg_rsp = l2cap_mode_cfg_rsp
};
static struct bt_l2cap_br_chan chan = {
.chan.ops = &l2cap_mode_ops,
.rx.mtu = DATA_BREDR_MTU,
},
int err = bt_l2cap_chan_connect(default_conn, &chan.chan, psm);
if (err < 0) {
PRINTF("Unable to connect to psm %u (err %d)\n", psm, err);
} else {
PRINTF("L2CAP connection pending\n");
}
Parent topic:Core Protocols Comparison
SDP#
The following compares the SDP differences between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
Feature |
EdgeFast Open |
EdgeFast Bluetooth PAL |
|---|---|---|
SDP record discovery |
Support Attribute ID list and range settings |
Not supported |
SDP record parse |
bt_sdp_record_parse |
Not supported |
Check attribute |
bt_sdp_has_attr |
Not supported |
Parse attribute value |
bt_sdp_get_attr bt_sdp_attr_value_parse bt_sdp_attr_has_uuid bt_sdp_attr_read |
Not supported |
Parse Additional Protocol Descriptor List attribute |
bt_sdp_attr_addl_proto_parse bt_sdp_attr_addl_proto_count bt_sdp_attr_addl_proto_read |
Not supported |
Parent topic:Core Protocols Comparison
Parent topic:Compare with EdgeFast Bluetooth PAL
LE Profiles#
No differences between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL for LE profiles.
LE Audio Profiles#
No differences between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL for LE Audio profiles.
Classic Profiles#
The following compares the available Classic profiles between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
A2DP#
Edgefast Bluetooth vs Edgefast Open - A2DP API Differences#
Source:
middleware/edgefast_bluetooth/include/bluetooth/a2dp.h+bluetooth/a2dp_codec_sbc.hvsmiddleware/edgefast_open/include/zephyr/bluetooth/classic/a2dp.h+classic/a2dp_codec_sbc.h
Badge |
Stack |
|---|---|
edgefast_bluetooth |
NXP Ethermind stack |
edgefast_open |
Zephyr open-source stack |
Legend: 🟢 Only in edgefast_bluetooth | 🔴 Only in edgefast_open | 🟡 Equivalent concept, different API
Contents#
1. Architecture & Design Philosophy#
⚠️ Fundamental difference: edgefast_bluetooth uses a monolithic endpoint model -
struct bt_a2dp_endpointbundles codec capability, configuration, codec buffers, and all control/data callbacks into one object; AVDTP signalling is hidden behind high-level helpers (bt_a2dp_configure,bt_a2dp_start,bt_a2dp_stop). edgefast_open uses a split endpoint + stream model -struct bt_a2dp_epdescribes codec capability only whilestruct bt_a2dp_streamcarries per-connection state; every AVDTP signal has its own named function (bt_a2dp_stream_establish,bt_a2dp_stream_start,bt_a2dp_stream_suspend,bt_a2dp_stream_release,bt_a2dp_stream_abort).
ℹ️ Codec integration: edgefast_bluetooth integrates an optional internal SBC encoder/decoder (controlled by
A2DP_CODEC_EXTERNALandBT_A2DP_CODEC_CONTROL); the app can feed raw PCM viabt_a2dp_src_media_write. edgefast_open provides no built-in codec; the app is always responsible for encoding/decoding and usesbt_a2dp_stream_sendwith a pre-builtnet_buf.
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Primary endpoint object |
|
|
Per-connection stream state |
Embedded inside |
Separate |
AVDTP signal exposure |
Hidden; app calls |
Explicit per-signal: |
Callback registration |
Per-endpoint struct fields ( |
Global |
Media TX |
|
|
Media RX |
|
|
PDU buffer allocation |
Not exposed; internal to stack |
|
MTU query |
Not exposed |
|
Automatic endpoint selection |
|
App explicitly selects both local and remote EP in |
Peer endpoint discovery |
|
|
Codec buffers |
App allocates and provides |
Not needed; codec is external |
SBC channel mode spelling |
|
|
Error codes |
Not defined in |
|
2. Header File Layout & Constants#
Item |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
A2DP main header |
|
|
A2DP SBC codec header |
|
|
SBC IE length |
|
|
MPEG-1,2 IE length |
|
|
MPEG-2,4 IE length |
|
|
Max IE length |
- not present - |
|
Codec ID enum |
|
|
Media type enum |
|
Uses |
Role enum |
|
Uses |
Error codes |
- not present - |
|
SBC channel mode typo |
|
|
SBC bitpool min/max |
- not present in header - |
|
SBC media packet header struct |
|
- not present - (macros only) |
SBC config decode macros |
Parameterised as |
Parameterised as |
SBC helper functions return types |
|
|
Codec buffer size constants |
|
- not present - |
3. Endpoint / Stream Object Model#
ℹ️ This is the most structurally significant difference between the two stacks.
edgefast_bluetooth - single struct bt_a2dp_endpoint embeds everything:
struct bt_a2dp_endpoint {
uint8_t codec_id; /* codec type */
struct bt_a2dp_ep_info info; /* SEP info (media_type, tsep) */
struct bt_a2dp_codec_ie *capabilities;/* codec capability IE */
struct bt_a2dp_codec_ie *config; /* default config IE for SOURCE */
struct bt_a2dp_ep_cb control_cbs; /* configured/start_play/stop_play/sink_streamer_data */
uint8_t *codec_buffer; /* cached codec working buffer */
uint8_t *codec_buffer_nocached; /* non-cached codec working buffer */
/* optional service fields gated by Kconfig: */
/* cp_ie, recovery_ie, reporting_service_enable, */
/* delay_reporting_service_enable, header_compression_cap, multiplexing_service_enable */
};
edgefast_open - struct bt_a2dp_ep (capability descriptor) + struct bt_a2dp_stream (per-connection state):
struct bt_a2dp_ep {
uint8_t codec_type; /* codec type */
struct bt_avdtp_sep sep; /* SEP from AVDTP layer */
struct bt_a2dp_codec_ie *codec_cap; /* codec capability IE */
struct bt_a2dp_stream *stream; /* pointer to currently linked stream (NULL if idle) */
bool delay_report; /* delay report capability flag */
};
struct bt_a2dp_stream {
struct bt_a2dp_ep *local_ep; /* the registered local endpoint */
struct bt_a2dp_stream_ops *ops; /* stream event callbacks */
struct bt_a2dp *a2dp; /* back-reference to the A2DP connection */
struct bt_a2dp_codec_ie codec_config; /* negotiated configuration */
};
In edgefast_open the stream object is app-allocated and passed to
bt_a2dp_stream_config. The samebt_a2dp_epcan be reused across connections (a new stream object is passed each time), whereas in edgefast_bluetooth thebt_a2dp_endpointis bound to one active connection at a time.
4. Connection Management & Callback Registration#
ℹ️ Both stacks use
struct bt_conn *for the initial L2CAP connect and return astruct bt_a2dp *handle. The connect callback registration API differs.
edgefast_bluetooth
struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn);
int bt_a2dp_disconnect(struct bt_a2dp *a2dp);
int bt_a2dp_register_connect_callback(struct bt_a2dp_connect_cb *cb);
edgefast_open
struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn);
int bt_a2dp_disconnect(struct bt_a2dp *a2dp);
int bt_a2dp_register_cb(struct bt_a2dp_cb *cb);
bt_a2dp_connectandbt_a2dp_disconnectsignatures are identical in both stacks. The callback registration function is renamed (bt_a2dp_register_connect_callback→bt_a2dp_register_cb) and the callback struct type changes (struct bt_a2dp_connect_cb→struct bt_a2dp_cb).
Callback struct field |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Connection established |
|
|
Connection released |
|
|
Remote endpoint discovered |
- (separate discovery callback type) |
|
5. Endpoint Registration & Discovery#
Endpoint Registration#
edgefast_bluetooth
int bt_a2dp_register_endpoint(struct bt_a2dp_endpoint *endpoint,
uint8_t media_type, uint8_t role);
Takes explicit
media_typeandroleparameters because the endpoint macro may embed them but the registration function re-declares them. The endpoint registered first has highest priority inbt_a2dp_configure.
edgefast_open
int bt_a2dp_register_ep(struct bt_a2dp_ep *ep, uint8_t media_type, uint8_t tsep);
Functionally equivalent - registers a local SEP with the AVDTP layer. Parameter names differ (
role→tsep). No priority concept; app always explicitly selects the EP inbt_a2dp_stream_config.
Peer Endpoint Discovery#
edgefast_bluetooth
/* callback type */
typedef uint8_t (*bt_a2dp_discover_peer_endpoint_cb_t)(
struct bt_a2dp *a2dp,
struct bt_a2dp_endpoint *endpoint,
struct bt_a2dp_endpoint *peer_endpoint);
int bt_a2dp_discover_peer_endpoints(struct bt_a2dp *a2dp,
bt_a2dp_discover_peer_endpoint_cb_t cb);
Callback returns
BT_A2DP_DISCOVER_ENDPOINT_STOPorBT_A2DP_DISCOVER_ENDPOINT_CONTINUEto control iteration. Both local matching endpoint and remote endpoint are passed together.
edgefast_open
int bt_a2dp_discover(struct bt_a2dp *a2dp, struct bt_a2dp_cb *cb);
/* result arrives in bt_a2dp_cb.discovery_result(a2dp, ep, err) */
Discovery result is delivered through the pre-registered
bt_a2dp_cbstruct rather than a dedicated function pointer. Only the remoteepis provided; the app matches it to a local EP manually.
Automatic vs Manual Configuration#
edgefast_bluetooth only 🟢
/* Automatically picks best local EP by priority and configures it */
int bt_a2dp_configure(struct bt_a2dp *a2dp, void (*result_cb)(int err));
/* Manual override: configure a specific local + remote EP pair */
int bt_a2dp_configure_endpoint(struct bt_a2dp *a2dp,
struct bt_a2dp_endpoint *endpoint,
struct bt_a2dp_endpoint *peer_endpoint,
struct bt_a2dp_endpoint_config *config);
edgefast_open has no automatic selection equivalent - the app always calls
bt_a2dp_stream_configwith explicit local and remote EP pointers.
6. AVDTP Stream Control Functions#
⚠️ This section shows the deepest API divergence. edgefast_bluetooth maps multiple AVDTP signals into fewer coarser functions; edgefast_open exposes each AVDTP signal as its own function.
AVDTP Signal |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
SET_CONFIGURATION |
Part of |
|
GET_CONFIGURATION |
- not exposed - |
|
OPEN (L2CAP media channel) |
Part of |
|
START |
|
|
SUSPEND |
|
|
CLOSE (release) |
|
|
RECONFIGURE |
|
|
ABORT |
- not exposed - |
|
edgefast_bluetooth
int bt_a2dp_start(struct bt_a2dp_endpoint *endpoint);
int bt_a2dp_stop(struct bt_a2dp_endpoint *endpoint);
int bt_a2dp_deconfigure(struct bt_a2dp_endpoint *endpoint);
int bt_a2dp_reconfigure(struct bt_a2dp_endpoint *endpoint,
struct bt_a2dp_endpoint_config *config);
edgefast_open
int bt_a2dp_stream_config(struct bt_a2dp *a2dp, struct bt_a2dp_stream *stream,
struct bt_a2dp_ep *local_ep, struct bt_a2dp_ep *remote_ep,
struct bt_a2dp_codec_cfg *config);
int bt_a2dp_stream_get_config(struct bt_a2dp_stream *stream);
int bt_a2dp_stream_establish(struct bt_a2dp_stream *stream);
int bt_a2dp_stream_start(struct bt_a2dp_stream *stream);
int bt_a2dp_stream_suspend(struct bt_a2dp_stream *stream);
int bt_a2dp_stream_release(struct bt_a2dp_stream *stream);
int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream,
struct bt_a2dp_codec_cfg *config);
int bt_a2dp_stream_abort(struct bt_a2dp_stream *stream);
edgefast_open separates
establish(AVDTP OPEN + L2CAP media channel) fromconfig(AVDTP SET_CONFIGURATION), matching the AVDTP state machine precisely. edgefast_bluetooth merges both into thebt_a2dp_configure/bt_a2dp_configure_endpointpath.bt_a2dp_stream_abortandbt_a2dp_stream_get_confighave no equivalent in edgefast_bluetooth.
7. Media Data Transfer#
⚠️ The media I/O model is fundamentally different. edgefast_bluetooth optionally integrates an SBC encoder/decoder; edgefast_open is always codec-agnostic.
edgefast_bluetooth - Source TX
/* If CONFIG_A2DP_CODEC_EXTERNAL is NOT set and codec is SBC: pass raw PCM */
/* Otherwise: pass BT_AVDTP_MEDIA_HDR_SIZE + 1-byte SBC payload header + SBC frames */
int bt_a2dp_src_media_write(struct bt_a2dp_endpoint *endpoint,
uint8_t *data, uint16_t datalen);
/* Multi-endpoint write (SBC only, CONFIG_BT_A2DP_WRITE_EXT) */
int bt_a2dp_src_media_write_ext(struct bt_a2dp_endpoint *endpoints[],
uint8_t *data, uint16_t datalen);
edgefast_open - Source TX
/* Allocate net_buf with AVDTP/L2CAP headroom pre-reserved */
struct net_buf *bt_a2dp_stream_create_pdu(struct net_buf_pool *pool, k_timeout_t timeout);
/* Send the RTP payload (caller fills seq_num and ts) */
int bt_a2dp_stream_send(struct bt_a2dp_stream *stream, struct net_buf *buf,
uint16_t seq_num, uint32_t ts);
edgefast_open exposes RTP sequence number and timestamp explicitly so the application controls packetisation. edgefast_bluetooth manages the RTP header internally.
edgefast_bluetooth - Sink RX (callback field in endpoint struct)
/* Registered as endpoint->control_cbs.sink_streamer_data */
void sink_streamer_data(struct bt_a2dp_endpoint *endpoint,
struct bt_a2dp_streaming_ctx *ctx);
/* After processing, notify the stack the buffer was consumed */
int bt_a2dp_snk_media_sync(struct bt_a2dp_endpoint *endpoint,
uint8_t *data, uint16_t datalen);
edgefast_open - Sink RX (callback in bt_a2dp_stream_ops)
/* Delivered directly as a net_buf with seq_num and timestamp */
void recv(struct bt_a2dp_stream *stream, struct net_buf *buf,
uint16_t seq_num, uint32_t ts);
edgefast_bluetooth requires an explicit
bt_a2dp_snk_media_synccall after processing each received buffer. edgefast_open has no such acknowledgement - thenet_bufreference is managed by the stack.
7.1 SBC Codec Responsibility#
⚠️ Critical difference: edgefast_bluetooth contains a built-in SBC encoder (source) and SBC decoder (sink) inside the stack. edgefast_open has no built-in codec - the application must link and drive an external SBC library (
zephyr/bluetooth/sbc.h) for every encode/decode operation.
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
SBC encoder location |
Internal to stack; app feeds raw PCM |
Application - app calls |
SBC decoder location |
Internal to stack; stack decodes before calling |
Application - app calls |
PCM input for source |
|
App encodes PCM → SBC frames → inserts into |
PCM output for sink |
Stack delivers decoded PCM in |
App decodes SBC frames from |
Encoder bit-rate tuning |
|
App passes |
SBC library |
Bundled and hidden |
Explicit |
edgefast_open Source - Application-driven SBC encoding (examples/a2dp_source/app_a2dp_source.c)#
#include <zephyr/bluetooth/sbc.h>
/* App owns the encoder state */
struct sbc_encoder encoder;
/* After stream is configured, app initialises the encoder from negotiated SBC params */
static void sbc_stream_configured(struct bt_a2dp_stream *stream)
{
struct sbc_encoder_init_param param;
struct bt_a2dp_codec_sbc_params *sbc_config =
(struct bt_a2dp_codec_sbc_params *)&sbc_cfg_default.codec_config->codec_ie[0];
a2dp_src_sf = bt_a2dp_sbc_get_sampling_frequency(sbc_config);
a2dp_src_nc = bt_a2dp_sbc_get_channel_num(sbc_config);
param.bit_rate = CONFIG_BT_A2DP_SOURCE_SBC_BIT_RATE_DEFAULT;
param.samp_freq = a2dp_src_sf;
param.blk_len = bt_a2dp_sbc_get_block_length(sbc_config);
param.subband = bt_a2dp_sbc_get_subband_num(sbc_config);
param.alloc_mthd = bt_a2dp_sbc_get_allocation_method(sbc_config);
param.ch_mode = bt_a2dp_sbc_get_channel_mode(sbc_config); /* typed enum */
param.ch_num = bt_a2dp_sbc_get_channel_num(sbc_config);
param.min_bitpool = sbc_config->min_bitpool;
param.max_bitpool = sbc_config->max_bitpool;
sbc_setup_encoder(&encoder, ¶m); /* app initialises encoder */
bt_a2dp_stream_establish(stream); /* then opens the media channel */
}
/* In the periodic send callback, app encodes PCM → SBC frames into a net_buf */
static void audio_work_handler(struct k_work *work)
{
struct net_buf *buf = bt_a2dp_stream_create_pdu(&a2dp_tx_pool, K_FOREVER);
uint8_t *sbc_hdr = net_buf_add(buf, 1u); /* reserve SBC media header byte */
for (uint8_t i = 0; i < frame_num; i++) {
/* App calls sbc_encode() - stack does NOT encode */
uint32_t out_size = sbc_encode(&encoder,
&pcm_data[i * pcm_frame_size],
net_buf_tail(buf));
net_buf_add(buf, out_size);
}
*sbc_hdr = BT_A2DP_SBC_MEDIA_HDR_ENCODE(frame_num, 0, 0, 0);
bt_a2dp_stream_send(&app_a2dps[0].sbc_stream, buf, send_count, send_samples_count);
}
edgefast_open Sink - Application-driven SBC decoding (examples/a2dp_sink/audio_buf.c)#
#include <zephyr/bluetooth/sbc.h>
/* App owns the decoder state */
static struct sbc_decoder decoder;
/* App resets/initialises the decoder when a stream starts */
void audio_buf_reset(uint32_t fs)
{
sbc_setup_decoder(&decoder); /* app initialises decoder - stack does NOT decode */
/* ... configure pcm ring-buffer size based on sample frequency ... */
}
/* In the recv callback, app iterates over SBC frames and calls sbc_decode() itself */
void audio_process_sbc_buf(uint8_t sbc_hdr, uint8_t *data, size_t len,
uint16_t seq_num, uint32_t ts, uint8_t channel_num)
{
uint8_t num_frames = BT_A2DP_SBC_MEDIA_HDR_NUM_FRAMES_GET(sbc_hdr);
for (uint8_t i = 0; i < num_frames; i++) {
uint32_t out_size = sizeof(pcm_frame_buffer);
/* App calls sbc_decode() - stack delivers raw SBC bytes, not PCM */
int err = sbc_decode(&decoder, &in_data, &len,
pcm_frame_buffer, &out_size);
if (err == 0) {
audio_add_pcm_data((uint8_t *)pcm_frame_buffer, out_size);
}
}
}
In contrast, edgefast_bluetooth handles encoding/decoding internally. When
A2DP_CODEC_EXTERNALis not set (default), the app simply callsbt_a2dp_src_media_write(endpoint, pcm_data, len)with raw PCM and the stack encodes it using its built-in SBC encoder. On the sink side, thesink_streamer_datacallback delivers already-decoded PCM data. The app only needs to allocatecodec_bufferandcodec_buffer_nocachedin the endpoint struct; it never calls any SBC function directly.
MTU query - edgefast_open only 🔴
uint32_t bt_a2dp_get_mtu(struct bt_a2dp_stream *stream);
No equivalent in edgefast_bluetooth.
8. Delay Reporting#
edgefast_bluetooth (gated by CONFIG_BT_A2DP_DR_SERVICE)
/* Set the initial delay before streaming starts */
int bt_a2dp_set_initial_delay_report(struct bt_a2dp_endpoint *endpoint, int16_t delay);
/* Send a delay report during streaming */
int bt_a2dp_send_delay_report(struct bt_a2dp_endpoint *endpoint, int16_t delay);
Uses
int16_t delaywith unspecified units (implementation-defined). Feature is optional viaBT_A2DP_DR_SERVICE.
edgefast_open (capability flag delay_report on struct bt_a2dp_ep)
/* Sink sends delay report to source (value in 1/10 ms) */
int bt_a2dp_stream_delay_report(struct bt_a2dp_stream *stream, uint16_t delay);
/* Source receives delay via bt_a2dp_stream_ops callback */
void delay_report(struct bt_a2dp_stream *stream, uint16_t value);
edgefast_open uses
uint16_tin units of 1/10 milliseconds (per A2DP spec). Delay reporting capability is advertised per-EP via thedelay_reportbool field instruct bt_a2dp_ep. No separate Kconfig symbol is needed - it is always compiled in.
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
API name |
|
|
Units |
|
|
Initial setup |
|
Not needed; first call to |
RX on source side |
Not exposed |
|
Kconfig gate |
|
Always compiled in |
9. Optional / Extended Services#
🟢 The following services exist only in edgefast_bluetooth with no edgefast_open equivalent.
Service |
edgefast_bluetooth function |
Kconfig gate |
|---|---|---|
Content Protection header |
|
|
Internal codec enable/disable |
|
|
Multi-endpoint SBC write |
|
|
Recovery service (config only) |
Endpoint field |
|
Reporting service (config only) |
Endpoint field |
|
Header compression (config only) |
Endpoint field |
|
Multiplexing service (config only) |
Endpoint field |
|
10. Callback Structures Comparison#
Connection-level callbacks#
Event |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Connected |
|
|
Disconnected |
|
|
Discovery result |
Separate callback type |
|
Endpoint / Stream-level callbacks#
edgefast_bluetooth - struct bt_a2dp_ep_cb (fields in struct bt_a2dp_endpoint)
struct bt_a2dp_ep_cb {
void (*configured)(struct bt_a2dp_endpoint *endpoint);
void (*start_play)(struct bt_a2dp_endpoint *endpoint,
struct bt_a2dp_streaming_ctx *ctx);
void (*stop_play)(struct bt_a2dp_endpoint *endpoint);
void (*sink_streamer_data)(struct bt_a2dp_endpoint *endpoint,
struct bt_a2dp_streaming_ctx *ctx);
/* Optional (Kconfig-gated): */
void (*sink_delay_report_cb)(struct bt_a2dp_endpoint *endpoint,
uint16_t delay); /* BT_A2DP_DR_SERVICE */
};
edgefast_open - struct bt_a2dp_stream_ops (registered per-stream)
struct bt_a2dp_stream_ops {
void (*configured)(struct bt_a2dp_stream *stream);
void (*established)(struct bt_a2dp_stream *stream); /* AVDTP OPEN complete */
void (*released)(struct bt_a2dp_stream *stream); /* AVDTP CLOSE/ABORT complete */
void (*started)(struct bt_a2dp_stream *stream);
void (*suspended)(struct bt_a2dp_stream *stream);
/* Sink only: */
void (*recv)(struct bt_a2dp_stream *stream, struct net_buf *buf,
uint16_t seq_num, uint32_t ts);
/* Source only: */
void (*sent)(struct bt_a2dp_stream *stream); /* TX SDU completed by controller */
void (*delay_report)(struct bt_a2dp_stream *stream, uint16_t value);
};
edgefast_open has separate
established(OPEN),released(CLOSE or ABORT),started, andsuspendedcallbacks for every AVDTP state transition. edgefast_bluetooth collapses start/stop intostart_play/stop_playand has noestablishedorreleasedcallbacks.
Callback correspondence table#
Event |
edgefast_bluetooth callback |
edgefast_open callback |
|---|---|---|
SET_CONFIGURATION accepted |
|
|
OPEN / media channel ready |
- not separate - |
|
START accepted |
|
|
SUSPEND accepted |
|
|
CLOSE / ABORT done |
- not exposed - |
|
Sink media data received |
|
|
Source TX completed |
- not exposed - |
|
Delay report received (source) |
|
|
11. Full Function Comparison Table#
Function |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
|
|
🟡 Identical |
|
|
|
🟡 Identical |
Register connect callback |
|
|
🟡 Renamed; struct type changed |
Register endpoint |
|
|
🟡 Renamed; struct type changed |
Discover peer endpoints |
|
|
🟡 Renamed; callback delivery differs |
Auto configure |
|
- not present - |
🟢 edgefast_bluetooth only |
Configure specific EP |
|
|
🟡 stream object added in edgefast_open |
Get configuration |
- not present - |
|
🔴 edgefast_open only |
Open / establish media channel |
Part of |
|
🔴 edgefast_open only |
Start streaming |
|
|
🟡 Renamed; argument type changed |
Stop / suspend |
|
|
🟡 Renamed; argument type changed |
Release / close |
|
|
🟡 Renamed; argument type changed |
Reconfigure |
|
|
🟡 Renamed; argument type changed |
Abort |
- not present - |
|
🔴 edgefast_open only |
Register stream callbacks |
Embedded in endpoint struct ( |
|
🟡 Per-stream in edgefast_open |
Source TX |
|
|
🟡 net_buf + explicit RTP fields in edgefast_open |
Source TX (multi-endpoint) |
|
- not present - |
🟢 edgefast_bluetooth only |
Allocate TX PDU buffer |
- not present - |
|
🔴 edgefast_open only |
Sink RX ack |
|
- not needed (net_buf managed by stack) - |
🟢 edgefast_bluetooth only |
Get MTU |
- not present - |
|
🔴 edgefast_open only |
Send delay report |
|
|
🟡 Renamed; units clarified |
Set initial delay report |
|
- not needed - |
🟢 edgefast_bluetooth only |
Content protection header |
|
- not present - |
🟢 edgefast_bluetooth only |
Internal codec control |
|
- not present - |
🟢 edgefast_bluetooth only |
SBC get channel mode |
|
|
🔴 |
SBC get allocation method |
returns |
returns |
🟡 Typed return in edgefast_open |
12. A2DP Kconfig Configuration Comparison#
⚠️ Source files: edgefast_bluetooth -
middleware/edgefast_bluetooth/include/Kconfig| edgefast_open -middleware/edgefast_open/source/bluetooth/host/classic/Kconfig
ℹ️ Key difference: edgefast_bluetooth exposes many optional service Kconfig symbols (content protection, recovery, reporting, delay report, header compression, multiplexing, task tuning, SBC codec buffers) that are absent from edgefast_open. edgefast_open is simpler - it only exposes core protocol enables plus two AVDTP transport tunables.
12.1 Core Protocol Enablement#
Symbol |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
bool, default 0 |
bool (no default) |
AVDTP layer. edgefast_bluetooth is explicit |
|
bool, default 0 |
bool (no default); |
A2DP master enable. edgefast_open auto-selects AVDTP and implies LIBSBC |
|
bool, default 0 |
bool (no default) |
Source role enable. Semantically identical |
|
bool, default 0 |
bool (no default) |
Sink role enable. Semantically identical |
12.2 AVDTP Transport Tunables#
Symbol |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
- not present - |
int, default 2 |
RTP protocol version. edgefast_open exposes this; edgefast_bluetooth hard-codes it |
|
- not present - |
int, default 512, range 48–65535 |
Max AVDTP signalling SDU size. Only in edgefast_open |
12.3 Connection & Endpoint Limits#
Symbol |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
int, default |
- not present - |
edgefast_bluetooth caps simultaneous A2DP connections; edgefast_open derives the limit from the underlying ACL pool |
|
int, default 2 |
- not present - |
edgefast_bluetooth limits registered SEPs; edgefast_open has no equivalent symbol |
12.4 Codec Integration (edgefast_bluetooth only) 🟢#
Symbol |
Type |
Default |
Notes |
|---|---|---|---|
|
bool |
0 |
When set, disables the internal SBC encoder/decoder; app provides pre-encoded frames |
|
bool |
0 |
Enables |
|
bool |
0 |
Enables |
|
int |
0 |
PCM input buffer size for internal SBC encoder (source) |
|
int |
328 |
Target SBC bit-rate for internal encoder (source) |
|
int |
0 |
PCM output buffer size for internal SBC decoder (sink) |
|
int |
328 |
Number of SBC data indications buffered (sink) |
|
int |
328 |
Samples per data indication (sink) |
None of these symbols exist in edgefast_open because edgefast_open never integrates a codec internally.
12.5 Task / Thread Tuning (edgefast_bluetooth only) 🟢#
Symbol |
Type |
Default |
Notes |
|---|---|---|---|
|
int |
4 |
A2DP task priority (processes streamer data and retries) |
|
int |
2048 |
A2DP task stack size in bytes |
edgefast_open uses the Zephyr workqueue/thread model and does not expose separate A2DP task configuration symbols.
12.6 Optional AVDTP Services (edgefast_bluetooth only) 🟢#
Symbol |
Type |
Default |
Notes |
|---|---|---|---|
|
bool |
0 |
Content Protection service (SCMS-T etc.) - config only |
|
bool |
0 |
Recovery service - config only (data transfer not supported) |
|
bool |
0 |
Reporting service - config only (data transfer not supported) |
|
bool |
0 |
Delay Reporting service - enables |
|
bool |
0 |
Header Compression service - config only (data transfer not supported) |
|
bool |
0 |
Multiplexing service - config only (data transfer not supported) |
edgefast_open has none of these optional service symbols. Delay reporting is always available via
bt_a2dp_stream_delay_reportwith no Kconfig gate.
12.7 Side-by-Side Symbol Summary#
Symbol |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
|
bool, default 0 |
bool |
|
- |
int, default 2 |
|
- |
int, default 512 |
|
bool, default 0 |
bool |
|
bool, default 0 |
bool |
|
bool, default 0 |
bool |
|
int, default BT_MAX_CONN |
- |
|
int, default 2 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
|
int, default 0 |
- |
|
int, default 328 |
- |
|
int, default 0 |
- |
|
int, default 328 |
- |
|
int, default 328 |
- |
|
int, default 4 |
- |
|
int, default 2048 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
|
bool, default 0 |
- |
AVRCP#
Edgefast Bluetooth vs Edgefast Open - AVRCP API Differences#
Source:
middleware/edgefast_bluetooth/include/bluetooth/avrcp.hvsmiddleware/edgefast_open/include/zephyr/bluetooth/classic/avrcp.h+avrcp_cover_art.h
Badge |
Stack |
|---|---|
edgefast_bluetooth |
NXP Ethermind stack |
edgefast_open |
Zephyr open-source stack |
Legend: 🟢 Only in edgefast_bluetooth | 🔴 Only in edgefast_open | 🟡 Equivalent concept, different API
Contents#
1. Architecture & Design Philosophy#
⚠️ Fundamental difference: edgefast_bluetooth uses a unified flat model - a single
struct bt_conn *handle covers all AVRCP operations and onestruct bt_avrcp_cbhandles all events edgefast_open uses a role-split object model - CT (Controller) and TG (Target) are distinct typed objects with separate callback structures and function namespaces.
ℹ️ Cover Art: edgefast_bluetooth integrates cover art in
avrcp.husing a numericuint8_t handleedgefast_open puts cover art in a separateavrcp_cover_art.husing typed objects that embed BIP (Basic Imaging Profile), with explicit separate L2CAP and OBEX connection steps.
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Connection handle |
|
|
Role separation |
Single unified API; send functions implicitly act as CT, responses handled in same callback |
Explicit CT namespace ( |
Callback registration |
Single |
Two separate: |
Transaction labels |
Implicit - library manages internally |
Explicit |
Command data passing |
Typed structs per command (e.g. |
Pre-serialized |
Fragmentation / continuation |
App calls |
Library reassembles internally; no continuation API exposed to application |
Passthrough vendor-unique |
Inline |
Separate |
Cover art handle |
Numeric |
Typed |
Cover art dependency |
Self-contained in |
Depends on |
2. Header File Layout & Constants#
Item |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
AVRCP main header |
|
|
Cover Art header |
Integrated in |
Separate |
Operation IDs |
|
|
Notification events |
|
|
AV/C response codes |
|
|
AV/C command types |
|
|
Error / status codes |
Two separate sets: |
Single unified |
Button state |
Raw |
|
Capability IDs |
|
|
Scopes |
|
Embedded as |
3. Connection Management#
ℹ️ The browsing connect/disconnect functions have identical signatures in both stacks Only the control-channel connect name differs (
bt_avrcp_control_connectvsbt_avrcp_connect).
Control & Browsing Channel Connect / Disconnect + Callback Registration#
edgefast_bluetooth
int bt_avrcp_control_connect(struct bt_conn *conn)
int bt_avrcp_control_disconnect(struct bt_conn *conn)
int bt_avrcp_browsing_connect(struct bt_conn *conn)
int bt_avrcp_browsing_disconnect(struct bt_conn *conn)
int bt_avrcp_register_callback(struct bt_avrcp_cb *cb)
edgefast_open
int bt_avrcp_connect(struct bt_conn *conn)
int bt_avrcp_disconnect(struct bt_conn *conn)
int bt_avrcp_browsing_connect(struct bt_conn *conn)
int bt_avrcp_browsing_disconnect(struct bt_conn *conn)
int bt_avrcp_ct_register_cb(const struct bt_avrcp_ct_cb *cb)
int bt_avrcp_tg_register_cb(const struct bt_avrcp_tg_cb *cb)
4. CT (Controller) Functions#
ℹ️ edgefast_bluetooth sends CT commands via
struct bt_conn *with implicit role edgefast_open uses explicitstruct bt_avrcp_ct *cttyped objects with an explicituint8_t tidon every call All TG response functions are in the separate Section 5.
Both take
struct bt_conn *. In edgefast_open the resulting typedstruct bt_avrcp_ct */struct bt_avrcp_tg *objects are delivered viaconnectedcallbacks. Callback registration is split into two role-specific calls in edgefast_open vs one combined call in edgefast_bluetooth.
edgefast_bluetooth
int bt_avrcp_send_unit_info(struct bt_conn *conn)
int bt_avrcp_send_subunit_info(struct bt_conn *conn)
int bt_avrcp_response_info(struct bt_conn *conn, uint8_t subunit, uint8_t subunit_type, uint8_t tl)
CT send + TG response on same handle/callback.
edgefast_open
int bt_avrcp_ct_get_unit_info(struct bt_avrcp_ct *ct, uint8_t tid)
int bt_avrcp_ct_get_subunit_info(struct bt_avrcp_ct *ct, uint8_t tid)
int bt_avrcp_tg_send_unit_info_rsp(struct bt_avrcp_tg *tg, uint8_t tid, struct bt_avrcp_unit_info_rsp *rsp)
int bt_avrcp_tg_send_subunit_info_rsp(struct bt_avrcp_tg *tg, uint8_t tid)
CT request and TG response on completely separate typed objects and callback structures.
Both take
struct bt_conn *. In edgefast_open the resulting typedstruct bt_avrcp_ct */struct bt_avrcp_tg *objects are delivered viaconnectedcallbacks. Callback registration is split into two role-specific calls in edgefast_open vs one combined call in edgefast_bluetooth.
Passthrough (CT send / TG respond)#
edgefast_bluetooth
int bt_avrcp_send_passthrough(struct bt_conn *conn, uint8_t op_id, uint8_t vendor_op_id, uint8_t state_flag)
int bt_avrcp_response_passthrough(struct bt_conn *conn, uint8_t rsp_type, struct bt_avrcp_control_msg *cmd)
Both CT send and TG response use same
struct bt_conn *. Vendor-unique op ID is a separate inline parameter.
edgefast_open
int bt_avrcp_ct_passthrough(struct bt_avrcp_ct *ct, uint8_t tid, uint8_t opid, uint8_t state, const uint8_t *payload, uint8_t len)
int bt_avrcp_tg_send_passthrough_rsp(struct bt_avrcp_tg *tg, uint8_t tid, bt_avrcp_rsp_t result, struct net_buf *buf)
CT and TG are fully separate. For standard operations pass
NULL/0forpayload/len; for vendor-unique ops, pre-serialize the vendor data into the payload bytes. TG response pre-serializes the passthrough response (opid + state + optional opvu data) into anet_bufallocated viabt_avrcp_create_pdu(), then passes it tobt_avrcp_tg_send_passthrough_rsp. Theresultparameter usesbt_avrcp_rsp_tenum for the AV/C response code.
Capabilities Query#
edgefast_bluetooth
int bt_avrcp_get_company_id_supported(struct bt_conn *conn)
int bt_avrcp_get_event_supported(struct bt_conn *conn)
Two separate functions for company IDs (cap_id=0x02) and events (cap_id=0x03).
edgefast_open
int bt_avrcp_ct_get_caps(struct bt_avrcp_ct *ct, uint8_t tid, uint8_t cap_id)
int bt_avrcp_tg_get_caps(struct bt_avrcp_tg *tg, uint8_t tid, uint8_t status, struct net_buf *buf)
Single CT function with
cap_idparameter. TG responds with pre-serializednet_buf.
Play Status, Notifications & Continuation#
edgefast_bluetooth
int bt_avrcp_get_play_status(struct bt_conn *conn)
int bt_avrcp_register_notification(struct bt_conn *conn, uint8_t event, uint32_t playback_interval)
int bt_avrcp_request_continuing_rsp(struct bt_conn *conn, uint8_t pdu_id)
int bt_avrcp_abort_continuing_rsp(struct bt_conn *conn, uint8_t pdu_id)
edgefast_open
int bt_avrcp_ct_get_play_status(struct bt_avrcp_ct *ct, uint8_t tid)
int bt_avrcp_ct_register_notification(struct bt_avrcp_ct *ct, uint8_t tid, uint8_t event_id, uint32_t interval, bt_avrcp_notify_changed_cb_t cb)
int bt_avrcp_tg_notification(struct bt_avrcp_tg *tg, uint8_t tid, uint8_t status, uint8_t event_id, struct bt_avrcp_event_data *data)
No continuation APIs - library handles fragmentation internally.
bt_avrcp_ct_register_notificationtakes an optional per-registrationbt_avrcp_notify_changed_cb_tcallback (can beNULLto use the global CT callback). TG sends notifications viabt_avrcp_tg_notification()with a typedstruct bt_avrcp_event_data *(not anet_buf); the library serializes it internally.
Vendor Dependent / Player App Settings / Element Attributes#
edgefast_bluetooth
int bt_avrcp_send_vendor_dependent(struct bt_conn *conn, uint8_t pdu_id, void *parameter)
struct bt_avrcp_vendor* bt_avrcp_vendor_rsp_parse(struct bt_avrcp_vendor_header *header, struct net_buf *buf, uint16_t buf_len)
Generic dispatcher: single function for all vendor-dependent PDUs (player app setting, element attrs, etc). Caller casts
parameterto the correct struct per PDU ID. A parse helper is provided for responses.
edgefast_open CT
bt_avrcp_ct_list_player_app_setting_attrs(ct, tid)bt_avrcp_ct_list_player_app_setting_vals(ct, tid, attr_id)bt_avrcp_ct_get_curr_player_app_setting_val(ct, tid, buf)bt_avrcp_ct_set_player_app_setting_val(ct, tid, buf)bt_avrcp_ct_get_player_app_setting_attr_text(ct, tid, buf)bt_avrcp_ct_get_player_app_setting_val_text(ct, tid, buf)bt_avrcp_ct_inform_displayable_char_set(ct, tid, buf)bt_avrcp_ct_inform_batt_status_of_ct(ct, tid, batt_status)bt_avrcp_ct_get_element_attrs(ct, tid, buf)bt_avrcp_ct_set_addressed_player(ct, tid, player_id)bt_avrcp_ct_set_absolute_volume(ct, tid, absolute_volume)bt_avrcp_ct_play_item(ct, tid, buf)bt_avrcp_ct_add_to_now_playing(ct, tid, buf)
edgefast_open explodes the generic vendor-dependent PDU into individual strongly-typed functions per PDU ID. No generic dispatcher; each command has its own function with typed parameters or
struct net_buf *.bt_avrcp_set_addressed_playerandbt_avrcp_set_volumefrom edgefast_bluetooth map tobt_avrcp_ct_set_addressed_playerandbt_avrcp_ct_set_absolute_volumerespectively.
5. TG (Target) Functions#
⚠️ edgefast_bluetooth has no explicit TG role The TG-side response functions (
bt_avrcp_response_info,bt_avrcp_response_passthrough,bt_avrcp_response_vendor_dependent) are present but share the samestruct bt_conn *and callback edgefast_open has a complete, separate TG API namespace.
TG Response Functions (target-side responses to CT requests)#
edgefast_bluetooth (TG-side functions)
int bt_avrcp_response_info(struct bt_conn *conn, uint8_t subunit, uint8_t subunit_type, uint8_t tl)
int bt_avrcp_response_passthrough(struct bt_conn *conn, uint8_t rsp_type, struct bt_avrcp_control_msg *cmd)
int bt_avrcp_response_vendor_dependent(struct bt_conn *conn, uint8_t pdu_id, uint8_t rsp_type, uint8_t packet_type, uint8_t tl, void *rsp_param, uint16_t param_len)
TG responses use same
struct bt_conn *as CT sends.response_vendor_dependentis a generic dispatcher covering all vendor-dependent response PDU IDs.
edgefast_open (bt_avrcp_tg_ functions)*
bt_avrcp_tg_send_unit_info_rsp(tg, tid, rsp)bt_avrcp_tg_send_subunit_info_rsp(tg, tid)bt_avrcp_tg_send_passthrough_rsp(tg, tid, result, buf)bt_avrcp_tg_get_caps(tg, tid, status, buf)bt_avrcp_tg_notification(tg, tid, status, event_id, data)bt_avrcp_tg_list_player_app_setting_attrs(tg, tid, status, buf)bt_avrcp_tg_list_player_app_setting_vals(tg, tid, status, buf)bt_avrcp_tg_get_curr_player_app_setting_val(tg, tid, status, buf)bt_avrcp_tg_set_player_app_setting_val(tg, tid, status)bt_avrcp_tg_get_player_app_setting_attr_text(tg, tid, status, buf)bt_avrcp_tg_get_player_app_setting_val_text(tg, tid, status, buf)bt_avrcp_tg_inform_displayable_char_set(tg, tid, status)bt_avrcp_tg_inform_batt_status_of_ct(tg, tid, status)bt_avrcp_tg_absolute_volume(tg, tid, status, absolute_volume)bt_avrcp_tg_get_element_attrs(tg, tid, status, buf)bt_avrcp_tg_get_play_status(tg, tid, status, buf)bt_avrcp_tg_set_addressed_player(tg, tid, status)bt_avrcp_tg_play_item(tg, tid, status)bt_avrcp_tg_add_to_now_playing(tg, tid, status)
Each TG response function corresponds 1:1 to a specific AVRCP PDU. The response uses the same
tidreceived in the request callback. Data is pre-serialized asstruct net_buf *bufwhere non-trivial, or a simple status code for acknowledgment-only responses.
6. Browsing Channel Functions#
ℹ️ Both stacks implement a full browsing channel API The fundamental design difference (typed CT/TG objects vs bt_conn, explicit tid) applies to browsing too edgefast_bluetooth uses typed structs for browsing commands; edgefast_open uses net_buf for complex browsing commands.
Browsing Channel Commands & Responses#
edgefast_bluetooth
int bt_avrcp_get_folder_items(struct bt_conn *conn, struct bt_avrcp_get_folder_items_cmd *param)
int bt_avrcp_change_path(struct bt_conn *conn, struct bt_avrcp_change_path_cmd *param)
int bt_avrcp_set_borwsed_player(struct bt_conn *conn, uint16_t player_id)
int bt_avrcp_get_items_attribute(struct bt_conn *conn, struct bt_avrcp_get_item_attrs_cmd *param)
int bt_avrcp_search(struct bt_conn *conn, struct bt_avrcp_search_cmd *param)
int bt_avrcp_get_total_num_of_items(struct bt_conn *conn, uint8_t scope)
int bt_avrcp_response_browsing(struct bt_conn *conn, uint8_t pdu_id, uint8_t tl, void *rsp_param, uint16_t param_len)
CT send functions use typed command structs. TG response is a single generic
bt_avrcp_response_browsing()dispatcher (void* parameter cast per PDU ID). Note:bt_avrcp_set_borwsed_playerhas a typo (“borwsed”) in the original header.
edgefast_open CT + TG browsing
int bt_avrcp_ct_set_browsed_player(struct bt_avrcp_ct *ct, uint8_t tid, uint16_t player_id)
int bt_avrcp_ct_get_folder_items(struct bt_avrcp_ct *ct, uint8_t tid, struct net_buf *buf)
int bt_avrcp_ct_change_path(struct bt_avrcp_ct *ct, uint8_t tid, struct net_buf *buf)
int bt_avrcp_ct_get_item_attrs(struct bt_avrcp_ct *ct, uint8_t tid, struct net_buf *buf)
int bt_avrcp_ct_get_total_number_of_items(struct bt_avrcp_ct *ct, uint8_t tid, uint8_t scope)
int bt_avrcp_ct_search(struct bt_avrcp_ct *ct, uint8_t tid, struct net_buf *buf)
int bt_avrcp_tg_set_browsed_player(struct bt_avrcp_tg *tg, uint8_t tid, struct net_buf *buf)
int bt_avrcp_tg_get_folder_items(struct bt_avrcp_tg *tg, uint8_t tid, struct net_buf *buf)
int bt_avrcp_tg_change_path(struct bt_avrcp_tg *tg, uint8_t tid, uint8_t status, uint32_t num_items)
int bt_avrcp_tg_get_item_attrs(struct bt_avrcp_tg *tg, uint8_t tid, struct net_buf *buf)
int bt_avrcp_tg_get_total_number_of_items(struct bt_avrcp_tg *tg, uint8_t tid, struct net_buf *buf)
int bt_avrcp_tg_search(struct bt_avrcp_tg *tg, uint8_t tid, struct net_buf *buf)
int bt_avrcp_tg_browsing_general_reject(struct bt_avrcp_tg *tg, uint8_t tid, uint8_t status)
edgefast_open fixes the typo:
bt_avrcp_ct_set_browsed_player(correct spelling). TG responses are per-PDU. A newbt_avrcp_tg_browsing_general_reject()API allows sending a GENERAL_REJECT browsing response.
Key Browsing API Design Differences#
Function |
edgefast_bluetooth signature |
edgefast_open equivalent |
Notes |
|---|---|---|---|
Set Browsed Player (CT) |
|
|
Typo fixed in edgefast_open. Explicit tid added. |
Get Folder Items (CT) |
|
|
Typed struct vs pre-serialized net_buf. |
Change Path (CT) |
|
|
Typed struct vs net_buf. |
Get Item Attrs (CT) |
|
|
Name simplified in edgefast_open. |
Search (CT) |
|
|
Typed struct vs net_buf. |
Get Total Num Items (CT) |
|
|
Name slightly different; tid added. |
Browsing response (TG) |
|
Per-PDU TG functions: |
edgefast_open splits into per-PDU TG calls. |
General Reject (TG) |
Not present |
|
New in edgefast_open. |
7. Cover Art Functions#
⚠️ Cover art is optional in both stacks edgefast_bluetooth guards cover art with Kconfig (
CONFIG_BT_AVRCP_COVER_ART_INITIATOR/_RESPONDER) edgefast_open uses a separate header file (avrcp_cover_art.h) that depends onclassic/bip.h.
Cover Art API Comparison
edgefast_bluetooth (in avrcp.h, handle-based)
int bt_avrcp_register_cover_art_cb(struct bt_avrcp_cover_art_cb *cb)
int bt_avrcp_cover_art_start_initiator(uint8_t *handle)
int bt_avrcp_cover_art_stop_initiator(uint8_t handle)
int bt_avrcp_cover_art_start_responder(uint8_t *handle)
int bt_avrcp_cover_art_stop_responder(uint8_t handle)
int bt_avrcp_cover_art_connect(uint8_t handle, struct bt_conn *conn, struct bt_avrcp_cover_art_connect *cmd)
int bt_avrcp_cover_art_disconnect(uint8_t handle)
int bt_avrcp_get_image_property(uint8_t handle, struct bt_avrcp_get_image_property *param)
int bt_avrcp_get_image(uint8_t handle, struct bt_avrcp_get_image *param)
int bt_avrcp_get_linked_thumbnail(uint8_t handle, struct bt_avrcp_get_linked_thumbnail *param)
int bt_avrcp_abort(uint8_t handle)
int bt_avrcp_send_request(uint8_t handle, uint8_t wait, struct bt_avrcp_cover_art_rsp *rsp)
int bt_avrcp_response_cover_art(uint8_t handle, uint8_t cmd, uint8_t response, void *rsp)
edgefast_open (in avrcp_cover_art.h, typed objects)
int bt_avrcp_cover_art_ct_cb_register(struct bt_avrcp_cover_art_ct_cb *cb)
int bt_avrcp_cover_art_tg_cb_register(struct bt_avrcp_cover_art_tg_cb *cb)
int bt_avrcp_cover_art_ct_l2cap_connect(struct bt_avrcp_ct *ct, struct bt_avrcp_cover_art_ct **cover_art_ct, uint16_t psm)
int bt_avrcp_cover_art_ct_l2cap_disconnect(struct bt_avrcp_cover_art_ct *ct)
int bt_avrcp_cover_art_tg_l2cap_disconnect(struct bt_avrcp_cover_art_tg *tg)
struct net_buf *bt_avrcp_cover_art_ct_create_pdu(struct bt_avrcp_cover_art_ct *ct, struct net_buf_pool *pool)
struct net_buf *bt_avrcp_cover_art_tg_create_pdu(struct bt_avrcp_cover_art_tg *tg, struct net_buf_pool *pool)
int bt_avrcp_cover_art_ct_connect(struct bt_avrcp_cover_art_ct *ct)
int bt_avrcp_cover_art_tg_connect(struct bt_avrcp_cover_art_tg *tg, uint8_t rsp_code)
int bt_avrcp_cover_art_ct_disconnect(struct bt_avrcp_cover_art_ct *ct)
int bt_avrcp_cover_art_tg_disconnect(struct bt_avrcp_cover_art_tg *tg, uint8_t rsp_code)
int bt_avrcp_cover_art_ct_abort(struct bt_avrcp_cover_art_ct *ct, struct net_buf *buf)
int bt_avrcp_cover_art_tg_abort(struct bt_avrcp_cover_art_tg *tg, uint8_t rsp_code, struct net_buf *buf)
int bt_avrcp_cover_art_ct_get_image_properties(struct bt_avrcp_cover_art_ct *ct, bool final, struct net_buf *buf)
int bt_avrcp_cover_art_tg_get_image_properties(struct bt_avrcp_cover_art_tg *tg, uint8_t rsp_code, struct net_buf *buf)
int bt_avrcp_cover_art_ct_get_image(struct bt_avrcp_cover_art_ct *ct, bool final, struct net_buf *buf)
int bt_avrcp_cover_art_tg_get_image(struct bt_avrcp_cover_art_tg *tg, uint8_t rsp_code, struct net_buf *buf)
int bt_avrcp_cover_art_ct_get_linked_thumbnail(struct bt_avrcp_cover_art_ct *ct, bool final, struct net_buf *buf)
int bt_avrcp_cover_art_tg_get_linked_thumbnail(struct bt_avrcp_cover_art_tg *tg, uint8_t rsp_code, struct net_buf *buf)
Every operation has a paired CT (initiator) and TG (responder) function. L2CAP transport connect/disconnect is explicit and separate from OBEX-layer connect/disconnect. PDU buffers are allocated via bt_avrcp_cover_art_ct/tg_create_pdu().
Cover Art Design Differences#
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Object type |
Numeric |
Typed struct pointers |
Role creation |
|
CT object delivered in |
L2CAP vs OBEX |
Single connect function combining L2CAP + OBEX |
Explicit separate steps: |
Callback structure |
Single |
Separate |
Get image property name |
|
|
Buffer model |
Typed parameter structs per command |
Pre-allocated |
PDU buffer allocation |
No explicit allocation API |
|
8. Callback Structures Comparison#
ℹ️ edgefast_bluetooth has one unified
struct bt_avrcp_cbcovering all events for both CT and TG roles edgefast_open has separatestruct bt_avrcp_ct_cbandstruct bt_avrcp_tg_cb, each with per-operation callback members matching the corresponding send function.
Event |
edgefast_bluetooth (bt_avrcp_cb) |
edgefast_open (bt_avrcp_ct_cb / bt_avrcp_tg_cb) |
|---|---|---|
Connected |
|
CT: |
Disconnected |
|
CT: |
Browsing connected |
|
CT: |
Browsing disconnected |
|
CT: |
Control received (TG) |
|
TG: per-command callbacks - |
Control rsp received (CT) |
|
CT: per-command response callbacks - |
Vendor dep. continue (CT) |
|
Not exposed - handled internally |
Browsing received (TG) |
|
TG: per-command callbacks - |
Browsing rsp received (CT) |
|
CT: per-command response callbacks - |
Cover art callbacks |
Single |
Separate |
9. Full Function Comparison Table#
ℹ️ Yellow = conceptually equivalent but different signature Green = only in edgefast_bluetooth Red = only in edgefast_open.
edgefast_bluetooth function |
edgefast_open equivalent |
Key difference |
|---|---|---|
bt_avrcp_register_callback |
bt_avrcp_ct_register_cb + bt_avrcp_tg_register_cb |
Split into two role-specific registrations |
bt_avrcp_control_connect(conn) |
bt_avrcp_connect(conn) |
Renamed only |
bt_avrcp_control_disconnect(conn) |
bt_avrcp_disconnect(conn) |
Renamed only |
bt_avrcp_browsing_connect(conn) |
bt_avrcp_browsing_connect(conn) |
Identical |
bt_avrcp_browsing_disconnect(conn) |
bt_avrcp_browsing_disconnect(conn) |
Identical |
bt_avrcp_send_unit_info(conn) |
bt_avrcp_ct_get_unit_info(ct, tid) |
Typed CT object + tid added |
bt_avrcp_send_subunit_info(conn) |
bt_avrcp_ct_get_subunit_info(ct, tid) |
Typed CT object + tid added |
bt_avrcp_response_info(conn, subunit, type, tl) |
bt_avrcp_tg_send_unit_info_rsp(tg, tid, rsp) |
Separate TG typed object; typed response struct |
bt_avrcp_send_passthrough(conn, op_id, vendor_op_id, state) |
bt_avrcp_ct_passthrough(ct, tid, opid, state, payload, len) |
Raw byte payload instead of typed struct ptr; tid; typed ct |
bt_avrcp_response_passthrough(conn, rsp_type, cmd*) |
bt_avrcp_tg_send_passthrough_rsp(tg, tid, result, buf) |
Pre-serialized net_buf instead of opid/state params; rsp_type is bt_avrcp_rsp_t enum |
bt_avrcp_get_company_id_supported(conn) |
bt_avrcp_ct_get_caps(ct, tid, BT_AVRCP_CAP_COMPANY_ID) |
Merged into single function with cap_id param |
bt_avrcp_get_event_supported(conn) |
bt_avrcp_ct_get_caps(ct, tid, BT_AVRCP_CAP_EVENTS_SUPPORTED) |
Merged into single function with cap_id param |
bt_avrcp_get_play_status(conn) |
bt_avrcp_ct_get_play_status(ct, tid) |
Typed CT object + tid |
bt_avrcp_register_notification(conn, event, interval) |
bt_avrcp_ct_register_notification(ct, tid, event_id, interval, cb) |
Typed CT object + tid + per-registration callback added |
bt_avrcp_request_continuing_rsp(conn, pdu_id) |
No equivalent - handled internally |
Library manages fragmentation in edgefast_open |
bt_avrcp_abort_continuing_rsp(conn, pdu_id) |
No equivalent - handled internally |
Library manages fragmentation in edgefast_open |
bt_avrcp_set_volume(conn, volume) |
bt_avrcp_ct_set_absolute_volume(ct, tid, absolute_volume) |
Renamed; typed CT object + tid |
bt_avrcp_set_addressed_player(conn, player_id) |
bt_avrcp_ct_set_addressed_player(ct, tid, player_id) |
Typed CT object + tid |
bt_avrcp_send_vendor_dependent(conn, pdu_id, void*) |
Individual per-PDU functions: bt_avrcp_ct_list_player_app_setting_attrs, bt_avrcp_ct_get_element_attrs, etc. |
Generic dispatcher replaced by per-PDU typed functions |
bt_avrcp_vendor_rsp_parse(header*, buf, len) |
No equivalent - responses delivered via per-PDU callbacks |
Not needed in edgefast_open due to per-operation callbacks |
bt_avrcp_response_vendor_dependent(conn, pdu_id, rsp_type, pkt_type, tl, param*, len) |
Individual per-PDU TG functions: bt_avrcp_tg_get_play_status, bt_avrcp_tg_get_element_attrs, etc. |
Generic dispatcher replaced by per-PDU TG functions |
bt_avrcp_set_borwsed_player(conn, player_id) [typo] |
bt_avrcp_ct_set_browsed_player(ct, tid, player_id) |
Typo fixed; typed CT object + tid |
bt_avrcp_get_folder_items(conn, cmd*) |
bt_avrcp_ct_get_folder_items(ct, tid, buf) |
Typed struct replaced by net_buf |
bt_avrcp_change_path(conn, cmd*) |
bt_avrcp_ct_change_path(ct, tid, buf) |
Typed struct replaced by net_buf |
bt_avrcp_get_items_attribute(conn, cmd*) |
bt_avrcp_ct_get_item_attrs(ct, tid, buf) |
Renamed + typed struct replaced by net_buf |
bt_avrcp_search(conn, cmd*) |
bt_avrcp_ct_search(ct, tid, buf) |
Typed struct replaced by net_buf |
bt_avrcp_get_total_num_of_items(conn, scope) |
bt_avrcp_ct_get_total_number_of_items(ct, tid, scope) |
Slightly renamed; typed CT + tid |
bt_avrcp_response_browsing(conn, pdu_id, tl, void*, len) |
Per-PDU: bt_avrcp_tg_get_folder_items, bt_avrcp_tg_change_path, bt_avrcp_tg_search, etc. |
Generic dispatcher split into per-PDU TG functions |
No equivalent |
bt_avrcp_tg_browsing_general_reject(tg, tid, status) |
New in edgefast_open |
bt_avrcp_register_cover_art_cb(cb) |
bt_avrcp_cover_art_ct_cb_register(cb) + bt_avrcp_cover_art_tg_cb_register(cb) |
Split into CT and TG |
bt_avrcp_cover_art_start_initiator(handle*) |
Implicit - ct object created on l2cap_connected callback |
Different lifecycle model |
bt_avrcp_cover_art_stop_initiator(handle) |
No direct equivalent |
Lifecycle managed by L2CAP disconnect |
bt_avrcp_cover_art_start_responder(handle*) |
Implicit - tg object created on l2cap_connected callback |
Different lifecycle model |
bt_avrcp_cover_art_stop_responder(handle) |
No direct equivalent |
Lifecycle managed by L2CAP disconnect |
bt_avrcp_cover_art_connect(handle, conn, cmd*) |
bt_avrcp_cover_art_ct_l2cap_connect(ct, cover_art_ct**, psm) then bt_avrcp_cover_art_ct_connect(ct) |
L2CAP and OBEX connect are separate steps |
bt_avrcp_cover_art_disconnect(handle) |
bt_avrcp_cover_art_ct_l2cap_disconnect(ct) |
Typed CT object |
bt_avrcp_get_image_property(handle, param*) |
bt_avrcp_cover_art_ct_get_image_properties(ct, final, buf) |
Typed CT; net_buf; bool final for fragmentation |
bt_avrcp_get_image(handle, param*) |
bt_avrcp_cover_art_ct_get_image(ct, final, buf) |
Typed CT; net_buf; bool final |
bt_avrcp_get_linked_thumbnail(handle, param*) |
bt_avrcp_cover_art_ct_get_linked_thumbnail(ct, final, buf) |
Typed CT; net_buf; bool final |
bt_avrcp_abort(handle) |
bt_avrcp_cover_art_ct_abort(ct, buf) |
Typed CT; optional net_buf for abort data |
bt_avrcp_send_request(handle, wait, rsp*) |
No equivalent - OBEX continuation handled via bool final parameter |
Different continuation model |
bt_avrcp_response_cover_art(handle, cmd, response, void*) |
bt_avrcp_cover_art_tg_get_image_properties / _get_image / _get_linked_thumbnail / _abort |
Generic response split into per-operation TG functions |
10. Architecture Summary#
ℹ️ Key architectural distinction: edgefast_bluetooth wraps the NXP Ethermind proprietary Bluetooth stack with a unified
struct bt_conn *-based API edgefast_open is the upstream Zephyr Bluetooth stack with a fully role-split, object-oriented API design where CT and TG are first-class typed objects throughout.
Dimension |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Underlying stack |
NXP Ethermind (proprietary) |
Zephyr BT Host (open-source) |
Header location |
|
|
Connection object |
|
Typed |
Role model |
Unified flat namespace; implicit role by function chosen |
Explicit CT/TG namespaces; same operation has two typed functions |
Transaction IDs |
Library-managed, not exposed to application |
Explicit |
Callback model |
Single |
Separate |
Vendor-dependent PDUs |
Generic |
Individual per-PDU typed functions (13+ CT functions) |
Browsing TG responses |
Single |
Per-PDU TG response functions; new |
Fragmentation control |
App calls |
Internal; |
Data passing |
Typed structs per command (e.g. |
|
Cover art header |
Integrated in |
Separate |
Set browsed player typo |
|
|
Constants style |
|
|
Total AVRCP functions |
~30 control + browsing + cover art functions |
55+ CT and TG functions across control, browsing, and cover art |
11. edgefast_open API Usage Examples#
ℹ️ Each snippet below shows a minimal but complete usage pattern for edgefast_open AVRCP APIs that differ from edgefast_bluetooth All snippets assume Zephyr RTOS conventions (
struct net_buf,sys_put_be*, etc.).
bt_avrcp_ct_register_cb / bt_avrcp_tg_register_cb CT + TG
Register role-split callbacks before any connection. CT and TG callbacks are separate structures with per-operation function pointers.
/* ── Define CT callback structure ── */
static void avrcp_ct_connected(struct bt_avrcp_ct *ct) {
/* Save ct pointer for later sends */
my_ct = ct;
}
static void avrcp_ct_unit_info_rsp(struct bt_avrcp_ct *ct, uint8_t tid,
struct bt_avrcp_unit_info_rsp *rsp) {
printk("Unit type=0x%02x subunit_id=0x%02x\n", rsp->unit_type, rsp->subunit_id);
}
static const struct bt_avrcp_ct_cb ct_cb = {
.connected = avrcp_ct_connected,
.unit_info = avrcp_ct_unit_info_rsp,
/* ... fill remaining per-operation callbacks ... */
};
/* ── Define TG callback structure ── */
static void avrcp_tg_connected(struct bt_avrcp_tg *tg) { my_tg = tg; }
static const struct bt_avrcp_tg_cb tg_cb = {
.connected = avrcp_tg_connected,
/* ... fill remaining per-operation callbacks ... */
};
/* ── Register (call once at init, before bt_enable) ── */
bt_avrcp_ct_register_cb(&ct_cb);
bt_avrcp_tg_register_cb(&tg_cb);
bt_avrcp_connect / bt_avrcp_disconnect Control channel
Open and close the AVRCP control channel. Both take a plain struct bt_conn *. The resulting typed CT/TG objects are delivered in the connected callbacks.
/* In BR/EDR connection callback - initiate AVRCP after ACL is up */
static void br_connected(struct bt_conn *conn, uint8_t err) {
if (!err) {
bt_avrcp_connect(conn); /* triggers ct_cb.connected / tg_cb.connected */
}
}
/* Close control channel */
bt_avrcp_disconnect(conn);
/* Optional: open browsing channel after control is up */
bt_avrcp_browsing_connect(conn);
bt_avrcp_browsing_disconnect(conn);
bt_avrcp_ct_get_unit_info CT
Query the target’s unit info. tid is the transaction label (0-15); the same tid is echoed back in the unit_info response callback so requests and responses can be matched.
uint8_t tid = 0; /* application-chosen, 0-15 */
int err = bt_avrcp_ct_get_unit_info(my_ct, tid);
if (err) {
printk("Unit info request failed: %d\n", err);
}
/* Result arrives in ct_cb.unit_info(ct, tid, rsp) */
bt_avrcp_ct_passthrough CT
Send a passthrough command (e.g. PLAY, PAUSE). Pass NULL/0 for payload/len for standard operations. For vendor-unique operations, pre-serialize the struct bt_avrcp_passthrough_opvu_data fields into a byte buffer and pass it as payload with the appropriate len.
/* Send PLAY key press - no vendor-unique payload */
bt_avrcp_ct_passthrough(my_ct, tid,
BT_AVRCP_OPID_PLAY,
BT_AVRCP_BUTTON_PRESSED,
NULL, 0); /* no vendor-unique data */
/* Follow immediately with key release (per AVRCP spec) */
bt_avrcp_ct_passthrough(my_ct, tid + 1,
BT_AVRCP_OPID_PLAY,
BT_AVRCP_BUTTON_RELEASED,
NULL, 0);
/* Result arrives in ct_cb.passthrough(ct, tid, rsp) */
bt_avrcp_tg_send_passthrough_rsp TG
Respond to a passthrough command received in tg_cb.passthrough(tg, tid, buf). The response body (opid + state + optional vendor-unique data) must be pre-serialized into a net_buf allocated with bt_avrcp_create_pdu(). Use the BT_AVRCP_PASSTHROUGH_SET_STATE_OPID() macro to pack the state and opid fields.
/* Inside tg_cb.passthrough callback: */
static void tg_passthrough(struct bt_avrcp_tg *tg, uint8_t tid,
struct net_buf *cmd_buf) {
struct bt_avrcp_passthrough_cmd *cmd;
struct bt_avrcp_passthrough_rsp *rsp;
bt_avrcp_opid_t opid;
bt_avrcp_button_state_t state;
struct net_buf *buf;
int err;
cmd = net_buf_pull_mem(cmd_buf, sizeof(*cmd));
state = BT_AVRCP_PASSTHROUGH_GET_STATE(cmd);
opid = BT_AVRCP_PASSTHROUGH_GET_OPID(cmd);
buf = bt_avrcp_create_pdu(NULL);
if (!buf) { return; }
rsp = net_buf_add(buf, sizeof(*rsp));
BT_AVRCP_PASSTHROUGH_SET_STATE_OPID(rsp, state, opid);
rsp->data_len = 0;
/* Accept PLAY, reject everything else */
bt_avrcp_rsp_t result = (opid == BT_AVRCP_OPID_PLAY)
? BT_AVRCP_RSP_ACCEPTED
: BT_AVRCP_RSP_NOT_IMPLEMENTED;
err = bt_avrcp_tg_send_passthrough_rsp(tg, tid, result, buf);
if (err < 0) { net_buf_unref(buf); }
}
bt_avrcp_ct_get_caps CT
Single function replaces edgefast_bluetooth’s two separate functions. Use BT_AVRCP_CAP_COMPANY_ID or BT_AVRCP_CAP_EVENTS_SUPPORTED as the cap_id argument.
/* Query which events the TG supports */
bt_avrcp_ct_get_caps(my_ct, tid, BT_AVRCP_CAP_EVENTS_SUPPORTED);
/* Query company IDs */
bt_avrcp_ct_get_caps(my_ct, tid + 1, BT_AVRCP_CAP_COMPANY_ID);
/* Result arrives in ct_cb.get_caps(ct, tid, rsp) */
bt_avrcp_ct_get_play_status / bt_avrcp_tg_get_play_status CT + TG
CT queries current play status; TG responds with a pre-serialized net_buf containing song length, position, and play status.
/* CT side: send request */
bt_avrcp_ct_get_play_status(my_ct, tid);
/* Response in ct_cb.get_play_status(ct, tid, rsp) */
/* TG side: respond in tg_cb.get_play_status(tg, tid) callback */
static void tg_get_play_status_cb(struct bt_avrcp_tg *tg, uint8_t tid) {
struct bt_avrcp_get_play_status_rsp *rsp;
struct net_buf *buf;
buf = bt_avrcp_create_vendor_pdu(&avrcp_tx_pool);
if (buf == NULL) { return; }
rsp = net_buf_add(buf, sizeof(*rsp));
rsp->song_length = sys_cpu_to_be32(song_length_ms);
rsp->song_position = sys_cpu_to_be32(song_position_ms);
rsp->play_status = BT_AVRCP_PLAYBACK_STATUS_PLAYING;
bt_avrcp_tg_get_play_status(tg, tid, BT_AVRCP_STATUS_OK, buf);
net_buf_unref(buf);
}
bt_avrcp_ct_register_notification / bt_avrcp_tg_notification CT + TG
CT registers for a specific event notification. The fifth parameter is an optional per-registration bt_avrcp_notify_changed_cb_t callback (pass NULL to skip per-registration CHANGED delivery). TG calls bt_avrcp_tg_notification() with a typed struct bt_avrcp_event_data *; the library handles serialization internally. The first call with BT_AVRCP_STATUS_SUCCESS sends an INTERIM response (delivered to the CT notification callback); the next call for the same event sends a CHANGED response (delivered to bt_avrcp_notify_changed_cb_t). A non-SUCCESS status sends a REJECTED response (also delivered to the notification callback). No continuation API is needed.
/* Per-registration CHANGED callback for playback status events */
static void playback_status_changed(struct bt_avrcp_ct *ct, uint8_t event_id,
struct bt_avrcp_event_data *data)
{
printk("Playback status changed: %d\n", data->play_status);
}
/* CT: register for playback status change events */
bt_avrcp_ct_register_notification(my_ct, tid,
BT_AVRCP_EVT_PLAYBACK_STATUS_CHANGED,
0, /* playback_interval=0 (not used for this event) */
playback_status_changed); /* invoked on CHANGED notifications */
/* TG: send interim notification from tg_cb.register_notification callback */
static void tg_register_notif(struct bt_avrcp_tg *tg, uint8_t tid,
uint8_t event_id, uint32_t interval) {
struct bt_avrcp_event_data data;
memset(&data, 0, sizeof(data));
data.play_status = BT_AVRCP_PLAYBACK_STATUS_PLAYING;
/* BT_AVRCP_STATUS_SUCCESS causes library to send INTERIM first,
then CHANGED on subsequent calls */
bt_avrcp_tg_notification(tg, tid, BT_AVRCP_STATUS_SUCCESS,
event_id, &data);
}
/* TG: send volume-changed notification */
struct bt_avrcp_event_data vdata;
memset(&vdata, 0, sizeof(vdata));
vdata.absolute_volume = current_volume;
bt_avrcp_tg_notification(my_tg, saved_tid, BT_AVRCP_STATUS_SUCCESS,
BT_AVRCP_EVT_VOLUME_CHANGED, &vdata);
bt_avrcp_ct_set_absolute_volume / bt_avrcp_tg_absolute_volume CT + TG
CT sends a Set Absolute Volume command (renamed from bt_avrcp_set_volume in edgefast_bluetooth). TG confirms the accepted volume level.
/* CT: set absolute volume to 50% (0x3F out of 0x7F) */
bt_avrcp_ct_set_absolute_volume(my_ct, tid, 0x3F);
/* Response in ct_cb.set_absolute_volume(ct, tid, accepted_vol) */
/* TG: respond from tg_cb.set_absolute_volume(tg, tid, requested_vol) */
static void tg_set_volume(struct bt_avrcp_tg *tg, uint8_t tid,
uint8_t vol) {
uint8_t accepted = apply_volume(vol); /* clamp to hw range */
bt_avrcp_tg_absolute_volume(tg, tid, BT_AVRCP_STATUS_OK, accepted);
}
bt_avrcp_ct_set_addressed_player CT
Direct per-PDU function replacing the generic bt_avrcp_send_vendor_dependent(pdu_id, void*) dispatcher from edgefast_bluetooth.
/* Select player with ID 1 as the addressed player */
bt_avrcp_ct_set_addressed_player(my_ct, tid, 1);
/* Response in ct_cb.set_addressed_player(ct, tid, status) */
bt_avrcp_ct_get_element_attrs CT
Request track metadata (title, artist, album, etc.). The command payload is serialized into a net_buf using a typed struct bt_avrcp_get_element_attrs_cmd pointer, followed by optional per-attribute uint32_t IDs appended with net_buf_add_be32.
struct bt_avrcp_get_element_attrs_cmd *cmd;
struct net_buf *buf;
uint64_t identifier = 0; /* 0 = currently playing item */
buf = bt_avrcp_create_vendor_pdu(&avrcp_tx_pool);
if (buf == NULL) { return -ENOMEM; }
cmd = net_buf_add(buf, sizeof(*cmd));
memcpy(cmd->identifier, &identifier, sizeof(identifier));
cmd->num_attrs = 2; /* request Title + Artist */
net_buf_add_be32(buf, 0x01); /* TITLE */
net_buf_add_be32(buf, 0x02); /* ARTIST */
bt_avrcp_ct_get_element_attrs(my_ct, tid, buf);
/* Response in ct_cb.get_element_attrs(ct, tid, rsp) */
bt_avrcp_ct_list_player_app_setting_attrs CT
One of the per-PDU replacements for the generic bt_avrcp_send_vendor_dependent dispatcher. No payload required for listing attributes.
/* Step 1: list available player app setting attribute IDs */
bt_avrcp_ct_list_player_app_setting_attrs(my_ct, tid);
/* Response in ct_cb.list_player_app_setting_attrs(ct, tid, rsp) */
/* Step 2 (in callback): list values for attribute 0x01 (Equalizer) */
bt_avrcp_ct_list_player_app_setting_vals(my_ct, tid + 1, 0x01);
/* Response in ct_cb.list_player_app_setting_vals(ct, tid, rsp) */
bt_avrcp_ct_set_browsed_player CT (Browsing)
Fixes the bt_avrcp_set_borwsed_player typo from edgefast_bluetooth. Requires the browsing channel to be established first via bt_avrcp_browsing_connect.
/* Must have browsing channel open already */
bt_avrcp_ct_set_browsed_player(my_ct, tid, player_id);
/* Response in ct_cb.set_browsed_player(ct, tid, rsp) */
bt_avrcp_ct_get_folder_items / bt_avrcp_tg_get_folder_items CT + TG (Browsing)
Replaces the typed-struct approach of edgefast_bluetooth with a pre-serialized net_buf. Fields are written through a typed struct bt_avrcp_get_folder_items_cmd pointer cast from net_buf_add.
struct bt_avrcp_get_folder_items_cmd *cmd;
struct net_buf *buf;
buf = bt_avrcp_create_pdu(&avrcp_tx_pool);
if (buf == NULL) { return -ENOMEM; }
cmd = net_buf_add(buf, sizeof(*cmd));
cmd->scope = BT_AVRCP_SCOPE_VFS; /* Media Player Virtual File System */
cmd->start_item = sys_cpu_to_be32(0);
cmd->end_item = sys_cpu_to_be32(9);
cmd->attr_count = 0x00; /* 0 = return all attributes */
bt_avrcp_ct_get_folder_items(my_ct, tid, buf);
/* Response in ct_cb.get_folder_items(ct, tid, rsp) */
/* TG: respond from tg_cb.get_folder_items(tg, tid, cmd) callback */
static void tg_get_folder_items(struct bt_avrcp_tg *tg, uint8_t tid,
const struct bt_avrcp_get_folder_items_cmd *cmd) {
struct net_buf *rsp_buf = build_folder_items_response();
bt_avrcp_tg_get_folder_items(tg, tid, rsp_buf);
net_buf_unref(rsp_buf);
}
bt_avrcp_ct_change_path CT (Browsing)
Navigate into a folder on the browsed player. Fields are written through a typed struct bt_avrcp_change_path_cmd pointer cast from net_buf_add.
struct bt_avrcp_change_path_cmd *cmd;
struct net_buf *buf;
buf = bt_avrcp_create_pdu(&avrcp_tx_pool);
if (buf == NULL) { return -ENOMEM; }
cmd = net_buf_add(buf, sizeof(*cmd));
cmd->uid_counter = sys_cpu_to_be16(uid_counter); /* from set_browsed_player rsp */
cmd->direction = BT_AVRCP_CHANGE_PATH_CHILD; /* 0x01 = down into folder */
memcpy(cmd->folder_uid, &folder_uid, sizeof(cmd->folder_uid));
bt_avrcp_ct_change_path(my_ct, tid, buf);
/* Response in ct_cb.change_path(ct, tid, rsp) - rsp contains new item count */
bt_avrcp_ct_search CT (Browsing)
Search for items matching a string. The fixed header fields are written through a typed struct bt_avrcp_search_cmd pointer, then the search string bytes are appended with net_buf_add_mem.
struct bt_avrcp_search_cmd *cmd;
struct net_buf *buf;
const char *term = "Beatles";
uint16_t slen = (uint16_t)strlen(term);
buf = bt_avrcp_create_pdu(&avrcp_tx_pool);
if (buf == NULL) { return -ENOMEM; }
cmd = net_buf_add(buf, sizeof(*cmd));
cmd->charset_id = sys_cpu_to_be16(BT_AVRCP_CHARSET_UTF8);
cmd->search_str_len = sys_cpu_to_be16(slen);
net_buf_add_mem(buf, term, slen);
bt_avrcp_ct_search(my_ct, tid, buf);
/* Response in ct_cb.search(ct, tid, rsp) - rsp.num_items = match count */
bt_avrcp_tg_browsing_general_reject TG (Browsing) - new in edgefast_open
New API with no equivalent in edgefast_bluetooth. Sends a GENERAL_REJECT browsing response when the TG cannot handle the incoming browsing PDU at all.
/* Inside tg_cb.browsing_received - reject an unsupported PDU */
static void tg_browsing_received(struct bt_avrcp_tg *tg, uint8_t tid,
const struct bt_avrcp_browsing_cmd *cmd) {
if (cmd->pdu_id == UNSUPPORTED_PDU) {
/* BT_AVRCP_STATUS_INVALID_CMD = 0x20 */
bt_avrcp_tg_browsing_general_reject(tg, tid,
BT_AVRCP_STATUS_INVALID_CMD);
return;
}
/* ... handle other PDUs ... */
}
bt_avrcp_cover_art_ct_l2cap_connect - bt_avrcp_cover_art_ct_connect Cover Art CT
Two-step connection (vs edgefast_bluetooth’s single bt_avrcp_cover_art_connect). First establish L2CAP transport, then in the l2cap_connected callback establish the OBEX session.
/* Step 1: initiate L2CAP connection (call after AVRCP control is up) */
struct bt_avrcp_cover_art_ct *cover_art_ct;
bt_avrcp_cover_art_ct_l2cap_connect(my_ct, &cover_art_ct, psm);
/* Step 2: inside bt_avrcp_cover_art_ct_cb.l2cap_connected() callback */
static void ca_ct_l2cap_connected(struct bt_avrcp_cover_art_ct *ct) {
/* Now open the OBEX session over the L2CAP channel */
bt_avrcp_cover_art_ct_connect(ct);
}
/* Step 3: inside bt_avrcp_cover_art_ct_cb.connect() callback - ready */
static void ca_ct_connected(struct bt_avrcp_cover_art_ct *ct, uint8_t rsp_code) {
if (rsp_code == 0x20) { /* OBEX_RSP_SUCCESS */
g_cover_art_ct = ct; /* save for image requests */
}
}
bt_avrcp_cover_art_ct_get_image_properties Cover Art CT
Replaces bt_avrcp_get_image_property(handle, param*). The request body is serialized into a net_buf (OBEX GET request headers). bool final is true for single-packet requests, false when sending headers across multiple OBEX packets.
/* Allocate PDU buffer from the pool */
struct net_buf *buf = bt_avrcp_cover_art_ct_create_pdu(g_cover_art_ct,
&cover_art_pdu_pool);
if (!buf) { return -ENOMEM; }
/* Add OBEX headers - e.g. image handle header (0x30) */
net_buf_add_u8(buf, 0x30); /* ImgHandle header ID */
net_buf_add_be16(buf, 2 + img_handle_len * 2);
net_buf_add_mem(buf, img_handle_utf16, img_handle_len * 2);
/* final=true: this is the only (and last) OBEX packet */
bt_avrcp_cover_art_ct_get_image_properties(g_cover_art_ct, true, buf);
net_buf_unref(buf);
/* Response in bt_avrcp_cover_art_ct_cb.get_image_properties(ct, rsp_code, buf) */
bt_avrcp_cover_art_ct_get_image Cover Art CT
Replaces bt_avrcp_get_image(handle, param*). Similar pattern to get_image_properties but includes an image descriptor XML body in the OBEX GET request.
const char *img_desc =
""
""
"";
uint16_t desc_len = (uint16_t)strlen(img_desc);
struct net_buf *buf = bt_avrcp_cover_art_ct_create_pdu(g_cover_art_ct,
&cover_art_pdu_pool);
/* ImgHandle header */
net_buf_add_u8(buf, 0x30);
net_buf_add_be16(buf, 3 + img_handle_len * 2);
net_buf_add_mem(buf, img_handle_utf16, img_handle_len * 2);
net_buf_add_u8(buf, 0x00); /* NUL terminator */
/* ImgDescriptor header (0x71 = Type body, application/x-bt-coverart-imagedescriptor) */
net_buf_add_u8(buf, 0x48); /* Body header 0x48 (non-final) or 0x49 (final) */
net_buf_add_be16(buf, 3 + desc_len);
net_buf_add_mem(buf, img_desc, desc_len);
bt_avrcp_cover_art_ct_get_image(g_cover_art_ct, true, buf);
net_buf_unref(buf);
/* Image data arrives in bt_avrcp_cover_art_ct_cb.get_image(ct, rsp_code, buf) */
12. AVRCP Kconfig Configuration Comparison#
⚠️ Source files: edgefast_bluetooth -
middleware/edgefast_bluetooth/include/Kconfig| edgefast_open -middleware/edgefast_open/source/bluetooth/host/classic/Kconfig
ℹ️ Key naming difference: edgefast_bluetooth uses short abbreviations (
BT_AVRCP_CT,BT_AVRCP_TG) while edgefast_open uses full words (BT_AVRCP_CONTROLLER,BT_AVRCP_TARGET) edgefast_open also adds finer-grained cover art sub-operation symbols that are absent from edgefast_bluetooth.
12.1 Core Protocol Enablement#
Symbol |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
bool, default n |
bool (no default) |
Audio/Video Distribution Transport Protocol - prerequisite for A2DP and AVRCP in both stacks |
|
- not present - |
bool (no default) |
Audio/Video Control Transport Protocol layer. edgefast_open exposes this as a separate Kconfig knob; edgefast_bluetooth enables it implicitly when BT_AVRCP is set |
|
bool, default n |
bool (no default) |
Master AVRCP enable. Both stacks gate all AVRCP sub-features on this symbol. edgefast_bluetooth forces an explicit |
|
bool, default n |
bool (no default) |
Enables the AVRCP Browsing Channel (separate L2CAP PSM). Semantically identical in both stacks |
|
- not present - |
int, default 339 |
edgefast_open lets the application tune the Browsing Channel L2CAP MTU. edgefast_bluetooth fixes this at compile time in source code |
|
- not present - |
int, default 1024 |
Vendor-dependent data receive buffer size (bytes). edgefast_open exposes this tunable; edgefast_bluetooth uses a hard-coded internal value |
12.2 Role Enablement (Controller / Target)#
edgefast_bluetooth symbol |
edgefast_open equivalent |
Type |
Notes |
|---|---|---|---|
|
|
bool |
Enables the AVRCP Controller (CT) role. Symbol renamed: short form vs full word. No default in either |
|
|
bool |
Enables the AVRCP Target (TG) role. Same rename pattern. No default in either |
|
- not present - |
int, default BT_MAX_CONN |
edgefast_bluetooth caps simultaneous AVRCP connections. edgefast_open derives the limit from the underlying L2CAP/ACL connection pool instead |
12.3 Cover Art Kconfig Symbols#
⚠️ edgefast_open provides finer-grained cover art Kconfig symbols, splitting CT and TG cover art, and further breaking down CT cover art into individual operation enables edgefast_bluetooth provides a single shared symbol plus separate CT/TG gating symbols only.
edgefast_bluetooth symbol |
edgefast_open equivalent |
Type |
Notes |
|---|---|---|---|
|
- no direct equivalent - |
bool, default n |
edgefast_bluetooth top-level cover art gate. Selecting this enables the shared cover art OBEX/BIP transport. edgefast_open replaces this with per-role enables ( |
|
|
bool, default n |
Cover art initiator (CT side). edgefast_open renames to |
|
|
bool, default n |
Cover art responder (TG side). Renamed in edgefast_open |
- not present - |
|
bool |
edgefast_open only - enables the CT Get Image OBEX operation individually. edgefast_bluetooth bundles all cover art CT operations under |
- not present - |
|
bool |
edgefast_open only - enables the CT Get Image Properties OBEX operation individually |
- not present - |
|
bool |
edgefast_open only - enables the CT Get Linked Thumbnail OBEX operation individually |
12.4 Side-by-Side Symbol Summary#
Symbol |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
|
bool, default n |
bool |
|
- |
bool |
|
bool, default n |
bool |
|
bool, default n |
bool |
|
- |
int, default 339 |
|
- |
int, default 1024 |
|
bool, default n |
- BT_AVRCP_CONTROLLER |
|
bool, default n |
- BT_AVRCP_TARGET |
|
int, default BT_MAX_CONN |
- |
|
bool, default n |
- (split into CT/TG) |
|
bool, default n |
- BT_AVRCP_CT_COVER_ART |
|
bool, default n |
- BT_AVRCP_TG_COVER_ART |
|
- |
bool |
|
- |
bool |
|
- |
bool |
HFP#
Edgefast Bluetooth vs Edgefast Open - HFP API Differences#
Source:
middleware/edgefast_bluetooth/include/bluetooth/hfp_hf.h+hfp_ag.hvsmiddleware/edgefast_open/include/zephyr/bluetooth/classic/hfp_hf.h+hfp_ag.h
Badge |
Stack |
|---|---|
edgefast_bluetooth |
NXP Ethermind stack |
edgefast_open |
Zephyr open-source stack |
Legend: 🟢 Only in edgefast_bluetooth | 🔴 Only in edgefast_open | 🟡 Equivalent concept, different API
Contents#
1. Architecture & Design Philosophy#
⚠️ Fundamental difference: edgefast_bluetooth uses a mixed handle model -
struct bt_conn *is used for all HF operations, while AG uses a typedstruct bt_hfp_ag *object. Call operations (accept/reject/dial) are implicit, driven by AT commands sent through a single callback. edgefast_open uses a fully typed object model - both HF and AG have dedicated typed objects (struct bt_hfp_hf *,struct bt_hfp_ag *) and both introducestruct bt_hfp_hf_call */struct bt_hfp_ag_call *per-call objects for fine-grained call lifecycle management.
ℹ️ Call lifecycle: edgefast_bluetooth exposes broad AT command wrappers (e.g.,
bt_hfp_hf_send_cmd) withenum bt_hfp_hf_at_cmdfor ATA/CHUP, and explicit dial/multiparty APIs. edgefast_open models each call as a first-class object and provides dedicated typed functions per call state (bt_hfp_hf_accept,bt_hfp_hf_reject,bt_hfp_hf_terminate,bt_hfp_hf_hold_incoming), eliminating raw AT command strings.
ℹ️ Voice recognition text: edgefast_open adds enhanced voice recognition text support (
vre_state,vre_textual_representation,textual_representationcallback,ready_to_accept_audio) that has no equivalent in edgefast_bluetooth.
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
HF connection handle |
|
|
AG connection handle |
|
|
Per-call handle |
None - call state tracked implicitly via AT indicators |
|
HF callback registration |
|
|
AG callback registration |
|
|
HF connect signature |
|
|
AG connect signature |
|
|
AG init/deinit |
|
No separate init/deinit - lifecycle managed via |
Call accept/reject (HF) |
|
|
Dialing (HF) |
|
|
CLIP / call waiting |
Separate enable/disable functions: |
Single boolean toggle: |
Volume control (HF) |
|
|
Volume control (AG) |
|
|
3-way call (HF) |
|
Individual functions: |
Codec handling |
|
|
ECNR |
None in HF header |
|
Indicator activation |
None |
|
HF indicators (battery/safety) |
Via |
|
Voice recognition text |
Not supported |
|
Subscriber number |
None (HF) / None (AG standalone query) |
|
Network operator |
None (HF) / |
|
AG incoming call notify |
|
|
DTMF |
None (HF) / |
|
2. Header File Layout & Constants#
Item |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
HF main header |
|
|
AG main header |
|
|
HF AT command enum |
|
No AT command enum - replaced by typed function calls |
Volume type |
|
No volume type enum - split into |
Multiparty call options |
|
No enum - individual named functions per CHLD value |
Call status (AG indicator) |
|
|
Call direction |
No enum |
|
Call mode |
No enum |
|
AG indicators enum |
None in header |
|
HF indicators enum |
None |
|
Codec IDs (AG) |
Not defined in header (passed via |
|
AG CIND struct |
|
No CIND struct - indicators set via individual functions ( |
HF config struct |
|
No config struct - initial values set via |
AG config struct |
|
No config struct - configuration via Kconfig and individual callbacks |
Current call info (HF) |
|
|
Ongoing call info (AG) |
None - AG uses raw |
|
Subscriber callback type |
None |
|
Waiting call struct |
|
Folded into |
Indicator status struct |
|
No struct - individual callbacks per indicator ( |
HF indicator IDs |
|
|
3. HF Connection Management#
ℹ️ Both stacks share the same function name
bt_hfp_hf_registerandbt_hfp_hf_connect/bt_hfp_hf_disconnect, but the handle type passed and returned changes fundamentally. edgefast_open’sconnectreturns a typedstruct bt_hfp_hf *object while edgefast_bluetooth uses the genericstruct bt_conn *throughout.
HF Register, Connect, and Disconnect#
edgefast_bluetooth
int bt_hfp_hf_register(struct bt_hfp_hf_cb *cb)
int bt_hfp_hf_connect(struct bt_conn *conn, uint8_t channel)
int bt_hfp_hf_disconnect(struct bt_conn *conn)
int bt_hfp_sco_disconnect(struct bt_conn *conn)
int bt_hfp_hf_discover(struct bt_conn *conn, bt_hfp_hf_discover_callback discoverCallback)
edgefast_open
int bt_hfp_hf_register(struct bt_hfp_hf_cb *cb)
int bt_hfp_hf_connect(struct bt_conn *conn, struct bt_hfp_hf **hf, uint8_t channel)
int bt_hfp_hf_disconnect(struct bt_hfp_hf *hf)
edgefast_bluetooth exposes a separate
bt_hfp_sco_disconnectand abt_hfp_hf_discoverfor manual SDP discovery. edgefast_open integrates SCO lifecycle entirely withinsco_connected/sco_disconnectedcallbacks and providesbt_hfp_hf_audio_connectinstead; no separate discover API is exposed.
4. HF Functions#
ℹ️ edgefast_open replaces edgefast_bluetooth’s raw AT command dispatch (
bt_hfp_hf_send_cmd) with per-operation typed functions. The call object (struct bt_hfp_hf_call *) replaces thestruct bt_conn *as the handle for call-level operations.
Audio Connection#
edgefast_bluetooth
int bt_hfp_hf_trigger_codec_connection(struct bt_conn *conn)
int bt_hfp_hf_open_audio(struct bt_conn *conn, uint8_t codec)
int bt_hfp_hf_close_audio(struct bt_conn *sco_conn)
edgefast_open
int bt_hfp_hf_audio_connect(struct bt_hfp_hf *hf)
int bt_hfp_hf_select_codec(struct bt_hfp_hf *hf, uint8_t codec_id)
int bt_hfp_hf_set_codecs(struct bt_hfp_hf *hf, uint8_t codec_ids)
edgefast_bluetooth provides
bt_hfp_hf_open_audio/bt_hfp_hf_close_audiofor direct SCO audio control (noted as PTS-only). edgefast_open usesbt_hfp_hf_audio_connectwhich handles codec negotiation (AT+BCC) internally when supported; codec IDs are set separately viabt_hfp_hf_set_codecs.
Call Accept / Reject / Terminate#
edgefast_bluetooth
int bt_hfp_hf_send_cmd(struct bt_conn *conn, enum bt_hfp_hf_at_cmd cmd)
/* cmd = BT_HFP_HF_ATA to accept, BT_HFP_HF_AT_CHUP to reject/terminate */
edgefast_open
int bt_hfp_hf_accept(struct bt_hfp_hf_call *call)
int bt_hfp_hf_reject(struct bt_hfp_hf_call *call)
int bt_hfp_hf_terminate(struct bt_hfp_hf_call *call)
int bt_hfp_hf_hold_incoming(struct bt_hfp_hf_call *call)
int bt_hfp_hf_query_respond_hold_status(struct bt_hfp_hf *hf)
edgefast_bluetooth uses a single
bt_hfp_hf_send_cmdwith an enum for ATA and AT+CHUP. edgefast_open models each call-state transition as a dedicated typed function operating on astruct bt_hfp_hf_call *, including the Response and Hold flow (bt_hfp_hf_hold_incoming,bt_hfp_hf_query_respond_hold_status).
Dialing#
edgefast_bluetooth
int bt_hfp_hf_dial(struct bt_conn *conn, const char *number)
int bt_hfp_hf_last_dial(struct bt_conn *conn)
int bt_hfp_hf_dial_memory(struct bt_conn *conn, int location)
edgefast_open
int bt_hfp_hf_number_call(struct bt_hfp_hf *hf, const char *number)
int bt_hfp_hf_redial(struct bt_hfp_hf *hf)
int bt_hfp_hf_memory_dial(struct bt_hfp_hf *hf, const char *location)
Functionally equivalent, but edgefast_bluetooth uses
int locationfor memory dial while edgefast_open usesconst char *location. Handle changes fromstruct bt_conn *tostruct bt_hfp_hf *. edgefast_open renamesbt_hfp_hf_dial-bt_hfp_hf_number_callandbt_hfp_hf_last_dial-bt_hfp_hf_redial.
Volume Control#
edgefast_bluetooth
int bt_hfp_hf_volume_update(struct bt_conn *conn, hf_volume_type_t type, int volume)
edgefast_open
int bt_hfp_hf_vgm(struct bt_hfp_hf *hf, uint8_t gain)
int bt_hfp_hf_vgs(struct bt_hfp_hf *hf, uint8_t gain)
edgefast_bluetooth uses a single
volume_updatewith a type enum. edgefast_open splits intovgm(microphone) andvgs(speaker) matching the AT+VGM / AT+VGS command names directly.
CLIP & Call Waiting Notifications#
edgefast_bluetooth
int bt_hfp_hf_enable_clip_notification(struct bt_conn *conn)
int bt_hfp_hf_disable_clip_notification(struct bt_conn *conn)
int bt_hfp_hf_enable_call_waiting_notification(struct bt_conn *conn)
int bt_hfp_hf_disable_call_waiting_notification(struct bt_conn *conn)
edgefast_open
int bt_hfp_hf_cli(struct bt_hfp_hf *hf, bool enable)
int bt_hfp_hf_call_waiting_notify(struct bt_hfp_hf *hf, bool enable)
edgefast_bluetooth uses separate enable/disable functions. edgefast_open merges each pair into a single boolean-toggled function.
3-Way Call (CHLD)#
edgefast_bluetooth
int bt_hfp_hf_multiparty_call_option(struct bt_conn *conn, hf_multiparty_call_option_t option)
/* option 1=Release+UDUB, 2=Release active+accept held/waiting,
3=Place active on hold+accept held/waiting, 4=Add to conference,
5=Connect two calls and disconnect subscriber */
edgefast_open
int bt_hfp_hf_release_all_held(struct bt_hfp_hf *hf) /* AT+CHLD=0 */
int bt_hfp_hf_set_udub(struct bt_hfp_hf *hf) /* AT+CHLD=0 */
int bt_hfp_hf_release_active_accept_other(struct bt_hfp_hf *hf) /* AT+CHLD=1 */
int bt_hfp_hf_hold_active_accept_other(struct bt_hfp_hf *hf) /* AT+CHLD=2 */
int bt_hfp_hf_join_conversation(struct bt_hfp_hf *hf) /* AT+CHLD=3 */
int bt_hfp_hf_explicit_call_transfer(struct bt_hfp_hf *hf) /* AT+CHLD=4 */
int bt_hfp_hf_release_specified_call(struct bt_hfp_hf_call *call) /* AT+CHLD=1<idx> */
int bt_hfp_hf_private_consultation_mode(struct bt_hfp_hf_call *call) /* AT+CHLD=2<idx> */
edgefast_bluetooth compresses all CHLD options into one enum-based function. edgefast_open exposes each CHLD variant as an individually named function, adds the ECC-specific per-call variants (
bt_hfp_hf_release_specified_call,bt_hfp_hf_private_consultation_mode) operating onstruct bt_hfp_hf_call *, and separatesrelease_all_heldfromset_udub(both map to AT+CHLD=0).
Voice Recognition#
edgefast_bluetooth
int bt_hfp_hf_start_voice_recognition(struct bt_conn *conn)
int bt_hfp_hf_stop_voice_recognition(struct bt_conn *conn)
edgefast_open
int bt_hfp_hf_voice_recognition(struct bt_hfp_hf *hf, bool activate)
int bt_hfp_hf_ready_to_accept_audio(struct bt_hfp_hf *hf)
edgefast_open merges start/stop into a single boolean-toggled function and adds
bt_hfp_hf_ready_to_accept_audiofor Enhanced Voice Recognition (AT+BVRA=2), which has no equivalent in edgefast_bluetooth.
Functions Only in edgefast_bluetooth (HF) 🟢#
int bt_hfp_hf_send_cmd(struct bt_conn *conn, enum bt_hfp_hf_at_cmd cmd)
int bt_hfp_hf_get_last_voice_tag_number(struct bt_conn *conn)
int bt_hfp_hf_get_peer_indicator_status(struct bt_conn *conn)
bt_hfp_hf_send_cmddispatches raw AT commands (ATA, AT+CHUP, AT+BAC); edgefast_open replaces this with typed functions.bt_hfp_hf_get_last_voice_tag_numberis replaced bybt_hfp_hf_request_phone_numberin edgefast_open.bt_hfp_hf_get_peer_indicator_status(sends AT+CIND? after SLC) has no direct equivalent.
Functions Only in edgefast_open (HF) 🔴#
int bt_hfp_hf_get_operator(struct bt_hfp_hf *hf)
int bt_hfp_hf_turn_off_ecnr(struct bt_hfp_hf *hf)
int bt_hfp_hf_indicator_status(struct bt_hfp_hf *hf, uint8_t status)
int bt_hfp_hf_enhanced_safety(struct bt_hfp_hf *hf, bool enable)
int bt_hfp_hf_battery(struct bt_hfp_hf *hf, uint8_t level)
int bt_hfp_hf_request_phone_number(struct bt_hfp_hf *hf)
int bt_hfp_hf_transmit_dtmf_code(struct bt_hfp_hf_call *call, char code)
int bt_hfp_hf_query_subscriber(struct bt_hfp_hf *hf)
int bt_hfp_hf_query_list_of_current_calls(struct bt_hfp_hf *hf)
💡 Reference code - edgefast_open HF-only APIs
/* bt_hfp_hf_get_operator - send AT+COPS? to read network operator name.
* Result delivered via .operator callback: (hf, mode, format, operator_str) */
int err = bt_hfp_hf_get_operator(hf);
if (err < 0) {
printk("bt_hfp_hf_get_operator failed: %d\n", err);
}
/* bt_hfp_hf_turn_off_ecnr - send AT+NREC=0 to disable AG echo-cancellation/noise-reduction.
* Result delivered via .ecnr_turn_off callback: (hf, err) */
int err = bt_hfp_hf_turn_off_ecnr(hf);
if (err < 0) {
printk("bt_hfp_hf_turn_off_ecnr failed: %d\n", err);
}
/* bt_hfp_hf_indicator_status - send AT+BIA to (de)activate AG indicators.
* 'status' is a bitmask: bit N = indicator index N. Example: deactivate index 3. */
uint8_t mask = 0xFF & ~(1U << 3); /* activate all except index 3 */
int err = bt_hfp_hf_indicator_status(hf, mask);
if (err < 0) {
printk("bt_hfp_hf_indicator_status failed: %d\n", err);
}
/* bt_hfp_hf_enhanced_safety - send AT+BIEV=1,<val> for Enhanced Driver Safety HF indicator */
int err = bt_hfp_hf_enhanced_safety(hf, true); /* true = enable */
if (err < 0) {
printk("bt_hfp_hf_enhanced_safety failed: %d\n", err);
}
/* bt_hfp_hf_battery - send AT+BIEV=2,<level> for Battery Level HF indicator (0-100) */
int err = bt_hfp_hf_battery(hf, 85); /* report 85% battery */
if (err < 0) {
printk("bt_hfp_hf_battery failed: %d\n", err);
}
/* bt_hfp_hf_request_phone_number - send AT+BINP=1 to request voice-tag phone number from AG.
* Result delivered via .request_phone_number callback: (hf, number) */
int err = bt_hfp_hf_request_phone_number(hf);
if (err < 0) {
printk("bt_hfp_hf_request_phone_number failed: %d\n", err);
}
/* bt_hfp_hf_transmit_dtmf_code - send AT+VTS=<code> on an active call.
* 'call' is the active call object; 'code' is a single DTMF char (0-9, *, #, A-D). */
int err = bt_hfp_hf_transmit_dtmf_code(call, '5');
if (err < 0) {
printk("bt_hfp_hf_transmit_dtmf_code failed: %d\n", err);
}
/* bt_hfp_hf_query_subscriber - send AT+CNUM to query subscriber numbers.
* Results delivered via .subscriber_number callback: (hf, number, type, service) */
int err = bt_hfp_hf_query_subscriber(hf);
if (err < 0) {
printk("bt_hfp_hf_query_subscriber failed: %d\n", err);
}
/* bt_hfp_hf_query_list_of_current_calls - send AT+CLCC to enumerate active calls.
* Each call delivered via .query_call callback: (hf, struct bt_hfp_hf_current_call *).
* Callback is invoked with NULL to signal end of list. */
int err = bt_hfp_hf_query_list_of_current_calls(hf);
if (err < 0) {
printk("bt_hfp_hf_query_list_of_current_calls failed: %d\n", err);
}
5. HF Callback Structures Comparison#
Callback |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
|
|
edgefast_open delivers the HF object here; no error code |
|
|
|
Handle type changes |
|
|
|
First param changes |
|
|
|
Identical |
|
|
|
Handle type changes |
|
|
Not present as raw indicator - replaced by |
edgefast_open lifts call state to typed per-call callbacks |
|
|
Not present - folded into |
- |
|
|
Not present as raw indicator - replaced by |
- |
|
|
|
Handle type changes |
|
|
|
Handle type changes |
|
|
|
Handle type changes |
|
|
|
Renamed; edgefast_open uses |
|
|
|
Renamed; |
|
|
Not present |
No equivalent in edgefast_open |
|
|
|
Struct replaced by inline params |
|
|
|
Param changes to call object |
|
|
|
Split by direction; type enum removed |
|
|
Not present as batch struct - each indicator has its own callback |
Decomposed into individual callbacks |
|
|
|
Narrowed to dial result; general AT cmd completion removed |
|
|
|
Renamed; |
|
|
Not present |
Config eliminated; use |
|
|
|
Renamed; pointer - value |
|
|
Not present |
No direct equivalent |
|
|
|
Renamed; struct now uses typed enums |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open |
|
Not present |
|
New in edgefast_open - Enhanced VR engine state |
|
Not present |
|
New in edgefast_open - VR text |
|
Not present |
|
New in edgefast_open |
6. AG Connection Management#
ℹ️ edgefast_bluetooth’s
bt_hfp_ag_connecttakes a fullhfp_ag_get_configstruct with 10+ fields at connect time and also embeds the callback. edgefast_open’sbt_hfp_ag_connectonly needs thestruct bt_conn *and RFCOMMchannel; configuration is distributed into callbacks (get_indicator_value,get_ongoing_call) and Kconfig settings. edgefast_bluetooth also requires explicitbt_hfp_ag_init()/bt_hfp_ag_deinit()lifecycle calls.
AG Register, Connect, and Disconnect#
edgefast_bluetooth
int bt_hfp_ag_init(void)
int bt_hfp_ag_deinit(void)
int bt_hfp_ag_register_cb(struct bt_hfp_ag_cb *cb)
int bt_hfp_ag_connect(struct bt_conn *conn, hfp_ag_get_config *config,
struct bt_hfp_ag_cb *cb, struct bt_hfp_ag **phfp_ag)
int bt_hfp_ag_disconnect(struct bt_hfp_ag *hfp_ag)
struct bt_conn *bt_hfp_ag_get_conn(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_discover(struct bt_conn *conn, bt_hfp_ag_discover_callback discoverCallback)
edgefast_open
int bt_hfp_ag_register(struct bt_hfp_ag_cb *cb)
int bt_hfp_ag_connect(struct bt_conn *conn, struct bt_hfp_ag **ag, uint8_t channel)
int bt_hfp_ag_disconnect(struct bt_hfp_ag *ag)
edgefast_bluetooth provides
bt_hfp_ag_get_connto retrieve the underlying ACL connection from an AG object. edgefast_open does not expose this. edgefast_bluetooth includes abt_hfp_ag_discoverfor manual SDP discovery; edgefast_open has no equivalent.
7. AG Functions#
ℹ️ edgefast_open refactors the AG notification model: instead of single flat status-setter functions (e.g.,
bt_hfp_ag_call_status_pl), it provides role-named event functions (bt_hfp_ag_remote_incoming,bt_hfp_ag_remote_ringing,bt_hfp_ag_remote_accept,bt_hfp_ag_remote_reject,bt_hfp_ag_remote_terminate) plus per-call control functions operating onstruct bt_hfp_ag_call *.
Audio Connection (AG)#
edgefast_bluetooth
void bt_hfp_ag_open_audio(struct bt_hfp_ag *hfp_ag, uint8_t codec)
void bt_hfp_ag_close_audio(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_codec_selector(struct bt_hfp_ag *hfp_ag, uint8_t value)
edgefast_open
int bt_hfp_ag_audio_connect(struct bt_hfp_ag *ag, uint8_t id)
edgefast_bluetooth’s
open_audio/close_audioarevoidreturn. edgefast_open merges codec selection intobt_hfp_ag_audio_connectand returnsint. Separatebt_hfp_ag_codec_selectordoes not exist in edgefast_open; codec is passed directly toaudio_connect.
AG Call Lifecycle#
edgefast_bluetooth
void bt_hfp_ag_call_status_pl(struct bt_hfp_ag *hfp_ag, hfp_ag_call_status_t status)
void bt_hfp_ag_send_callring(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_send_ccwa_indicator(struct bt_hfp_ag *hfp_ag, char *number)
edgefast_open
int bt_hfp_ag_remote_incoming(struct bt_hfp_ag *ag, const char *number)
int bt_hfp_ag_remote_ringing(struct bt_hfp_ag_call *call)
int bt_hfp_ag_remote_accept(struct bt_hfp_ag_call *call)
int bt_hfp_ag_remote_reject(struct bt_hfp_ag_call *call)
int bt_hfp_ag_remote_terminate(struct bt_hfp_ag_call *call)
int bt_hfp_ag_hold_incoming(struct bt_hfp_ag_call *call)
int bt_hfp_ag_reject(struct bt_hfp_ag_call *call)
int bt_hfp_ag_accept(struct bt_hfp_ag_call *call)
int bt_hfp_ag_terminate(struct bt_hfp_ag_call *call)
int bt_hfp_ag_retrieve(struct bt_hfp_ag_call *call)
int bt_hfp_ag_hold(struct bt_hfp_ag_call *call)
int bt_hfp_ag_outgoing(struct bt_hfp_ag *ag, const char *number)
edgefast_bluetooth uses a single
bt_hfp_ag_call_status_plwith an enum to drive the entire call lifecycle. edgefast_open introduces per-event functions and separates HF-initiated events (remote_*) from AG-initiated events (e.g.,bt_hfp_ag_hold,bt_hfp_ag_outgoing).
AG Indicator Notifications#
edgefast_bluetooth
int bt_hfp_ag_send_call_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_send_callsetup_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_send_callheld_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_send_service_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_send_signal_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_send_roaming_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_send_battery_indicator(struct bt_hfp_ag *hfp_ag, uint8_t value)
int bt_hfp_ag_get_cind_setting(struct bt_hfp_ag *hfp_ag, hfp_ag_cind_t *cind_setting)
int bt_hfp_ag_set_cind_setting(struct bt_hfp_ag *hfp_ag, hfp_ag_cind_t *cind_setting)
edgefast_open
int bt_hfp_ag_service_availability(struct bt_hfp_ag *ag, bool available)
int bt_hfp_ag_signal_strength(struct bt_hfp_ag *ag, uint8_t strength)
int bt_hfp_ag_roaming_status(struct bt_hfp_ag *ag, uint8_t status)
int bt_hfp_ag_battery_level(struct bt_hfp_ag *ag, uint8_t level)
edgefast_bluetooth explicitly sends
call,callsetup, andcallheldindicators via dedicated functions and also provides a bulk CIND get/set viahfp_ag_cind_t. In edgefast_open, call/callsetup/callheld indicators are managed internally by the stack as part of the call object lifecycle - no explicit indicator send functions for these are exposed. Only network-related indicators (service, signal, roam, battery) require application notification.
AG Voice Recognition#
edgefast_bluetooth
int bt_hfp_ag_send_enable_voice_recognition(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_send_disable_voice_recognition(struct bt_hfp_ag *hfp_ag)
edgefast_open
int bt_hfp_ag_voice_recognition(struct bt_hfp_ag *ag, bool activate)
int bt_hfp_ag_vre_state(struct bt_hfp_ag *ag, uint8_t state)
int bt_hfp_ag_vre_textual_representation(struct bt_hfp_ag *ag, uint8_t state, const char *id,
uint8_t type, uint8_t operation, const char *text)
edgefast_bluetooth uses separate enable/disable functions. edgefast_open merges into a boolean toggle and adds
bt_hfp_ag_vre_stateandbt_hfp_ag_vre_textual_representationfor Enhanced Voice Recognition, which are absent in edgefast_bluetooth.
AG Volume, Network Operator, Inband Ring#
edgefast_bluetooth
int bt_hfp_ag_set_volume_control(struct bt_hfp_ag *hfp_ag, hf_ag_volume_type_t type, int value)
int bt_hfp_ag_set_cops(struct bt_hfp_ag *hfp_ag, char *name)
int bt_hfp_ag_set_inband_ring_tone(struct bt_hfp_ag *hfp_ag, int value)
edgefast_open
int bt_hfp_ag_vgm(struct bt_hfp_ag *ag, uint8_t vgm)
int bt_hfp_ag_vgs(struct bt_hfp_ag *ag, uint8_t vgs)
int bt_hfp_ag_set_operator(struct bt_hfp_ag *ag, uint8_t mode, char *name)
int bt_hfp_ag_inband_ringtone(struct bt_hfp_ag *ag, bool inband)
Volume control split from one type-enum function into two named functions.
set_copsrenamed toset_operatorand gains amodeparameter. Inband ring tone changes fromint valuetobool inband.
Functions Only in edgefast_bluetooth (AG) 🟢#
int bt_hfp_ag_init(void)
int bt_hfp_ag_deinit(void)
struct bt_conn *bt_hfp_ag_get_conn(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_discover(struct bt_conn *conn, bt_hfp_ag_discover_callback discoverCallback)
int bt_hfp_ag_register_supp_features(struct bt_hfp_ag *hfp_ag, uint32_t supported_features)
uint32_t bt_hfp_ag_get_peer_supp_features(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_register_cind_features(struct bt_hfp_ag *hfp_ag, char *cind)
int bt_hfp_ag_send_enable_voice_ecnr(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_send_disable_voice_ecnr(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_set_phnum_tag(struct bt_hfp_ag *hfp_ag, char *name)
int bt_hfp_ag_handle_btrh(struct bt_hfp_ag *hfp_ag, uint8_t option)
int bt_hfp_ag_handle_indicator_enable(struct bt_hfp_ag *hfp_ag, uint8_t index, uint8_t enable)
void bt_hfp_ag_send_callring(struct bt_hfp_ag *hfp_ag)
int bt_hfp_ag_send_clip(struct bt_hfp_ag *hfp_ag, uint8_t *clip_result)
int bt_hfp_ag_set_clcc(struct bt_hfp_ag *hfp_ag, char *call_list)
int bt_hfp_ag_unknown_at_response(struct bt_hfp_ag *hfp_ag, uint8_t *unknow_at_rsp, uint16_t unknow_at_rsplen)
int bt_hfp_ag_set_hf_indicator(struct bt_hfp_ag *hfp_ag, uint16_t hf_indicator, uint8_t enable)
Functions Only in edgefast_open (AG) 🔴#
int bt_hfp_ag_remote_incoming(struct bt_hfp_ag *ag, const char *number)
int bt_hfp_ag_remote_ringing(struct bt_hfp_ag_call *call)
int bt_hfp_ag_remote_accept(struct bt_hfp_ag_call *call)
int bt_hfp_ag_remote_reject(struct bt_hfp_ag_call *call)
int bt_hfp_ag_remote_terminate(struct bt_hfp_ag_call *call)
int bt_hfp_ag_hold_incoming(struct bt_hfp_ag_call *call)
int bt_hfp_ag_reject(struct bt_hfp_ag_call *call)
int bt_hfp_ag_accept(struct bt_hfp_ag_call *call)
int bt_hfp_ag_terminate(struct bt_hfp_ag_call *call)
int bt_hfp_ag_retrieve(struct bt_hfp_ag_call *call)
int bt_hfp_ag_hold(struct bt_hfp_ag_call *call)
int bt_hfp_ag_outgoing(struct bt_hfp_ag *ag, const char *number)
int bt_hfp_ag_explicit_call_transfer(struct bt_hfp_ag *ag)
int bt_hfp_ag_vre_state(struct bt_hfp_ag *ag, uint8_t state)
int bt_hfp_ag_vre_textual_representation(struct bt_hfp_ag *ag, uint8_t state, const char *id,
uint8_t type, uint8_t operation, const char *text)
int bt_hfp_ag_ongoing_calls(struct bt_hfp_ag *ag, struct bt_hfp_ag_ongoing_call *calls, size_t count)
int bt_hfp_ag_hf_indicator(struct bt_hfp_ag *ag, enum hfp_ag_hf_indicators indicator, bool enable)
💡 Reference code - edgefast_open AG-only APIs
/* bt_hfp_ag_remote_incoming - notify HF that a remote party is calling (AG-originated).
* Creates a new struct bt_hfp_ag_call * object delivered via .incoming callback.
* Use for a phone call arriving at the AG from the cellular network. */
int err = bt_hfp_ag_remote_incoming(ag, "123456789");
if (err) {
printk("bt_hfp_ag_remote_incoming failed: %d\n", err);
}
/* bt_hfp_ag_remote_ringing - notify HF that remote party is now ringing (outgoing call).
* Call object obtained from the .outgoing callback. Transitions call to ALERTING. */
int err = bt_hfp_ag_remote_ringing(call);
if (err) {
printk("bt_hfp_ag_remote_ringing failed: %d\n", err);
}
/* bt_hfp_ag_remote_accept - notify HF that remote party accepted (outgoing call - ACTIVE). */
int err = bt_hfp_ag_remote_accept(call);
if (err) {
printk("bt_hfp_ag_remote_accept failed: %d\n", err);
}
/* bt_hfp_ag_remote_reject - notify HF that remote party rejected the outgoing call. */
int err = bt_hfp_ag_remote_reject(call);
if (err) {
printk("bt_hfp_ag_remote_reject failed: %d\n", err);
}
/* bt_hfp_ag_remote_terminate - notify HF that remote party hung up. */
int err = bt_hfp_ag_remote_terminate(call);
if (err) {
printk("bt_hfp_ag_remote_terminate failed: %d\n", err);
}
/* bt_hfp_ag_accept - accept an incoming call on behalf of the AG (response to AT+ATA from HF).
* Called from .accept callback to confirm the HF-initiated accept. */
int err = bt_hfp_ag_accept(call);
if (err) {
printk("bt_hfp_ag_accept failed: %d\n", err);
}
/* bt_hfp_ag_reject - reject a call not yet active (AT+CHUP, call in INCOMING state).
* bt_hfp_ag_terminate - terminate an active/held call (AT+CHUP, call was ACTIVE).
* Choose based on current call state (from app_handsfree_ag.c pattern): */
int err;
if (hfp_in_calling) {
err = bt_hfp_ag_terminate(call);
} else {
err = bt_hfp_ag_reject(call);
}
if (err) {
printk("ag stop call failed: %d\n", err);
}
/* bt_hfp_ag_hold - place active call on hold (AT+CHLD=2 from HF).
* bt_hfp_ag_retrieve - retrieve a held call (AT+CHLD=2, swap). */
int err = bt_hfp_ag_hold(call); /* or bt_hfp_ag_retrieve(call) */
if (err) {
printk("ag hold/retrieve failed: %d\n", err);
}
/* bt_hfp_ag_outgoing - initiate an outgoing call from the AG side.
* Creates a new struct bt_hfp_ag_call * delivered via .outgoing callback. */
int err = bt_hfp_ag_outgoing(ag, "987654321");
if (err) {
printk("bt_hfp_ag_outgoing failed: %d\n", err);
}
/* bt_hfp_ag_explicit_call_transfer - perform AT+CHLD=4 (connect two calls, disconnect AG).
* Typically called from .explicit_call_transfer callback. */
int err = bt_hfp_ag_explicit_call_transfer(ag);
if (err) {
printk("bt_hfp_ag_explicit_call_transfer failed: %d\n", err);
}
/* bt_hfp_ag_audio_connect - open SCO audio connection with the specified codec.
* codec: BT_HFP_AG_CODEC_CVSD(1), BT_HFP_AG_CODEC_MSBC(2), BT_HFP_AG_CODEC_LC3_SWB(3) */
int err = bt_hfp_ag_audio_connect(ag, BT_HFP_AG_CODEC_MSBC);
if (err) {
printk("bt_hfp_ag_audio_connect failed: %d\n", err);
}
/* bt_hfp_ag_vre_state - send +BVRA:<state> Enhanced VR engine state update to HF.
* state: 0=deactivated, 1=activated, 2=ready_to_accept_audio */
int err = bt_hfp_ag_vre_state(ag, 1); /* VR engine activated */
if (err) {
printk("bt_hfp_ag_vre_state failed: %d\n", err);
}
/* bt_hfp_ag_vre_textual_representation - send +BVRA textual representation to HF.
* Used for Enhanced VR to display recognized text on HF. */
int err = bt_hfp_ag_vre_textual_representation(ag,
1, /* state: activated */
"msg-1", /* text ID */
0, /* type: speech */
0, /* operation: add */
"Call John");
if (err) {
printk("bt_hfp_ag_vre_textual_representation failed: %d\n", err);
}
/* bt_hfp_ag_hf_indicator - enable or disable a specific HF indicator reported by the HF.
* indicator: HFP_AG_ENHANCED_SAFETY_IND (1) or HFP_AG_BATTERY_LEVEL_IND (2).
* Returns -EOPNOTSUPP if the HF did not register support for this indicator. */
int err = bt_hfp_ag_hf_indicator(ag, HFP_AG_BATTERY_LEVEL_IND, true);
if (err == -EOPNOTSUPP) {
printk("HF does not support battery indicator\n");
} else if (err) {
printk("bt_hfp_ag_hf_indicator failed: %d\n", err);
}
8. AG Callback Structures Comparison#
Callback |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
|
|
edgefast_open also delivers the ACL conn; no error code |
|
|
|
Equivalent; parameter renamed |
|
|
|
Identical |
|
|
|
edgefast_open passes SCO conn + reason code; edgefast_bluetooth passes AG object |
|
|
Not present - replaced by |
Config struct eliminated |
|
Not present |
|
New in edgefast_open - initial indicator values at SLC |
|
Not present |
|
New in edgefast_open - replaces |
|
|
|
Split by direction |
|
|
Not present |
Remote HF feature bits not surfaced as callback in edgefast_open |
|
|
|
Renamed; param changes to call object |
|
|
|
Decomposed by call state |
|
|
|
Renamed; |
|
|
|
edgefast_open passes location as string + requests number back |
|
|
|
edgefast_open expects app to fill in the last number |
|
Not present |
|
New - call object created here |
|
Not present |
|
New - incoming call object |
|
Not present |
|
New |
|
Not present |
|
New - replaces |
|
Not present |
|
New |
|
Not present |
|
New |
|
Not present |
|
New |
|
Not present |
|
New |
|
|
|
Renamed; |
|
|
|
Renamed; value removed (always turn-off) |
|
|
|
param changes from codec ID to result code |
|
|
|
Renamed |
|
|
|
Identical |
|
|
|
edgefast_open handles most CHLD operations without app callback |
|
|
|
Renamed; edgefast_open uses |
|
|
|
Renamed |
|
|
Not present |
No unknown AT passthrough in edgefast_open |
|
|
|
Renamed; index to enum |
|
Not present |
|
New - AT+BINP=1 handler |
|
Not present |
|
New - AT+CNUM handler |
|
Not present |
|
New - Enhanced VR |
|
Not present |
|
New - replaces brva |
|
Not present |
|
New - AT+CHLD=4 |
|
Not present |
|
New |
|
Not present |
|
New |
|
Not present |
|
New |
|
Not present |
|
New |
|
Not present |
|
New |
9. Full Function Comparison Table#
HF Functions#
Function |
edgefast_bluetooth |
edgefast_open |
Status |
|---|---|---|---|
Register HF callbacks |
|
|
Same name, different struct |
Connect |
|
|
Returns HF object |
Disconnect |
|
|
Handle type changes |
SCO disconnect |
|
- (managed via callbacks) |
Only in edgefast_bluetooth |
SDP discover |
|
- |
Only in edgefast_bluetooth |
Accept call |
|
|
Renamed + typed |
Reject call |
|
|
Renamed + typed |
Terminate call |
|
|
Renamed + typed |
Hold incoming |
- |
|
Only in edgefast_open |
Query R&H status |
- |
|
Only in edgefast_open |
Dial number |
|
|
Renamed |
Redial |
|
|
Renamed |
Memory dial |
|
|
Location type changes |
Volume update |
|
|
Split by direction |
CLIP enable |
|
|
Merged with bool |
CLIP disable |
|
|
Merged with bool |
Call waiting enable |
|
|
Merged with bool |
Call waiting disable |
|
|
Merged with bool |
3-way call |
|
8 individual functions |
Decomposed |
Start voice recognition |
|
|
Merged with bool |
Stop voice recognition |
|
|
Merged with bool |
Ready to accept audio |
- |
|
Only in edgefast_open |
Codec connection |
|
|
Renamed |
Open audio |
|
- (merged into audio_connect) |
Only in edgefast_bluetooth (PTS) |
Close audio |
|
- |
Only in edgefast_bluetooth (PTS) |
Select codec |
- |
|
Only in edgefast_open |
Set codecs |
- |
|
Only in edgefast_open |
Turn off ECNR |
- |
|
Only in edgefast_open |
Indicator status |
- |
|
Only in edgefast_open |
Enhanced safety |
- |
|
Only in edgefast_open |
Battery level |
- |
|
Only in edgefast_open |
Get operator |
- |
|
Only in edgefast_open |
Voice tag number |
|
|
Renamed |
DTMF |
- |
|
Only in edgefast_open |
Query subscriber |
- |
|
Only in edgefast_open |
Peer indicator status |
|
- |
Only in edgefast_bluetooth |
Send raw AT cmd |
|
- (replaced by typed functions) |
Only in edgefast_bluetooth |
Query current calls |
|
|
Renamed |
AG Functions#
Function |
edgefast_bluetooth |
edgefast_open |
Status |
|---|---|---|---|
Init |
|
- |
Only in edgefast_bluetooth |
Deinit |
|
- |
Only in edgefast_bluetooth |
Register callbacks |
|
|
Renamed |
Connect |
|
|
Simplified |
Disconnect |
|
|
Equivalent |
Get ACL conn |
|
- |
Only in edgefast_bluetooth |
SDP discover |
|
- |
Only in edgefast_bluetooth |
Open audio |
|
|
Renamed; returns int |
Close audio |
|
- (managed via SCO callbacks) |
Only in edgefast_bluetooth |
Codec select |
|
- (merged into audio_connect) |
Only in edgefast_bluetooth |
Call status |
|
- (replaced by per-event functions) |
Only in edgefast_bluetooth |
Notify incoming |
- |
|
Only in edgefast_open |
Notify ringing |
|
|
Renamed + typed |
Notify remote accept |
- |
|
Only in edgefast_open |
Notify remote reject |
- |
|
Only in edgefast_open |
Notify remote terminate |
- |
|
Only in edgefast_open |
Hold incoming |
- |
|
Only in edgefast_open |
Reject call |
- |
|
Only in edgefast_open |
Accept call |
- |
|
Only in edgefast_open |
Terminate call |
- |
|
Only in edgefast_open |
Retrieve call |
- |
|
Only in edgefast_open |
Hold call |
- |
|
Only in edgefast_open |
Outgoing call |
- |
|
Only in edgefast_open |
Explicit call transfer |
- |
|
Only in edgefast_open |
CCWA indicator |
|
- (managed internally by stack) |
Only in edgefast_bluetooth |
Send call indicator |
|
- (managed internally) |
Only in edgefast_bluetooth |
Send callsetup indicator |
|
- (managed internally) |
Only in edgefast_bluetooth |
Send callheld indicator |
|
- (managed internally) |
Only in edgefast_bluetooth |
Send service indicator |
|
|
Renamed; uint8 to bool |
Send signal indicator |
|
|
Renamed |
Send roam indicator |
|
|
Renamed |
Send battery indicator |
|
|
Renamed |
Get CIND setting |
|
- |
Only in edgefast_bluetooth |
Set CIND setting |
|
- |
Only in edgefast_bluetooth |
Set ongoing calls |
|
|
Renamed; typed struct |
Volume control |
|
|
Split by direction |
Set operator |
|
|
Renamed; mode added |
Inband ring |
|
|
Renamed; int to bool |
Set CLIP |
|
- (managed internally) |
Only in edgefast_bluetooth |
Enable VR |
|
|
Merged with bool |
Disable VR |
|
|
Merged with bool |
VR engine state |
- |
|
Only in edgefast_open |
VR text |
- |
|
Only in edgefast_open |
Enable ECNR |
|
- |
Only in edgefast_bluetooth |
Disable ECNR |
|
- |
Only in edgefast_bluetooth |
Supp features register |
|
- |
Only in edgefast_bluetooth |
Get peer features |
|
- |
Only in edgefast_bluetooth |
CIND string register |
|
- |
Only in edgefast_bluetooth |
Phone number tag |
|
- (handled via request_phone_number callback) |
Only in edgefast_bluetooth |
BTRH handler |
|
- |
Only in edgefast_bluetooth |
HF indicator enable |
|
|
Renamed; index to enum |
HF indicator set |
|
|
Merged |
Unknown AT response |
|
- |
Only in edgefast_bluetooth |
10. Architecture Summary#
Topic |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
HF handle |
|
|
AG handle |
|
|
Call handle |
No per-call object |
|
Call indicator management |
App explicitly sends |
Stack manages |
AT command exposure |
Raw AT commands dispatched via |
No raw AT exposure - all operations via typed functions |
Config at connect |
AG: full |
AG: only |
Lifecycle management |
AG requires |
No separate init/deinit |
Codec IDs |
Numeric |
Named macros |
Enhanced VR |
Not supported |
Full support: |
3-way call (HF) |
One function with enum (5 options) |
8 individually named functions per CHLD value |
Volume |
Single function with type enum for both HF and AG |
|
CLIP/CCWA toggle |
Separate enable/disable functions |
Single |
SDP discover |
Manual: |
Not exposed - caller passes |
Unknown AT handling |
|
Not exposed in edgefast_open |
CLIP sending (AG) |
|
Managed internally by stack |
11. HFP Kconfig Configuration Comparison#
Feature |
edgefast_bluetooth Kconfig |
edgefast_open Kconfig |
|---|---|---|
HF enable |
|
|
AG enable |
|
|
HF indicators |
|
|
Codec negotiation (HF) |
|
|
CLI notification (HF) |
No dedicated Kconfig - controlled via |
|
Volume (HF) |
No dedicated Kconfig - always available |
|
3-way call (HF) |
No dedicated Kconfig |
|
ECC (HF) |
No dedicated Kconfig |
|
ECNR (HF) |
No dedicated Kconfig in HF header |
|
Voice recognition (HF) |
No dedicated Kconfig in HF header |
|
Enhanced VR (HF) |
Not supported |
|
VR text (HF) |
Not supported |
|
ECS (current calls, HF) |
No dedicated Kconfig |
|
3-way call (AG) |
No dedicated Kconfig |
|
ECNR (AG) |
No dedicated Kconfig |
|
Voice recognition (AG) |
No dedicated Kconfig |
|
Enhanced VR (AG) |
Not supported |
|
VR text (AG) |
Not supported |
|
Voice tag (AG) |
No dedicated Kconfig |
|
HF indicators (AG) |
|
|
Phone number max len |
Not configurable (hardcoded |
|
Ongoing call timeout |
Not configurable |
|
MAP#
Edgefast Bluetooth vs Edgefast Open — MAP API Differences#
Source:
middleware/edgefast_bluetooth/include/bluetooth/map_mce.h+map_mse.hvsmiddleware/edgefast_open/include/zephyr/bluetooth/classic/map.h
Badge |
Stack |
|---|---|
edgefast_bluetooth |
NXP Ethermind stack |
edgefast_open |
Zephyr open-source stack |
Legend: 🟢 Only in edgefast_bluetooth | 🔴 Only in edgefast_open | 🟡 Equivalent concept, different API
Contents#
1. Architecture & Design Philosophy #
⚠️ Fundamental difference: edgefast_bluetooth uses a stack-managed opaque object model — the stack allocates
struct bt_map_mce_mas */struct bt_map_mse_mas *on connection and returns it via callback; a single globalregister(cb)call covers all instances. edgefast_open uses an application-owned instance model — the app pre-allocates the instance struct, registers callbacks per-instance, then explicitly performs transport connect and OBEX connect as two separate steps.
ℹ️ Transport abstraction: edgefast_bluetooth hides the RFCOMM/L2CAP transport difference behind a single
connected/disconnectedcallback pair. edgefast_open exposes four separate transport callbacks (rfcomm_connected,rfcomm_disconnected,l2cap_connected,l2cap_disconnected) plus additional OBEX-levelconnect/disconnectcallbacks, giving the application full visibility of each connection phase.
Aspect |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Object lifecycle |
Stack allocates instance on connect; app receives pointer via callback |
App pre-allocates |
Callback registration |
Single global call: |
Per-instance: |
Transport connection (MCE) |
Single step: |
Two steps: |
Transport events (MCE) |
Single |
Split: |
Server registration |
Single global call: |
Per-server call: |
MAP header encoding |
Stack adds OBEX Connection ID, SRM, SRMP, Name and Type headers automatically based on input parameters and provides dedicated helper macros ( |
App calls OBEX APIs directly ( |
Fragmentation control |
|
|
SRM state management |
Stack adds SRM header and maintains SRM state internally |
App calls |
Callback behaviour when SRM enabled |
Stack generates a synthetic callback immediately after app sends a request (client side) or response (server side) to prompt the next transfer, even without an actual response from the peer |
No synthetic request/response callback — callback fires only when a real packet is received; app must schedule the next outgoing request or response by itself rather than waiting for a callback |
Buffer reservation macros |
|
Not present — replaced by |
Max packet length query |
|
Not present — maximum packet length returned via |
Abort |
|
|
MSE disconnect |
|
|
2. Header File Layout & Constants #
Item |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
MCE header |
|
Unified |
MSE header |
|
Same unified |
Shared types header |
|
All types defined inline in |
OBEX dependency |
|
|
RSV_LEN macros |
|
Not present |
Type headers |
|
|
Supported features |
|
|
Application param tag IDs |
|
|
Add application parameter helper |
|
Not present as named macro — app calls OBEX layer directly: |
Add body helper |
|
Not present as named macro — app calls OBEX layer directly: |
Parse application parameter helper |
|
Not present as named macro — app calls OBEX layer directly: |
Get body helper |
|
Not present as named macro — app calls OBEX layer directly: |
3. MCE MAS — Connection Management #
ℹ️ The most significant architectural change: edgefast_bluetooth uses a one-step opaque connect where the stack allocates the MAS object and delivers it via
connectedcallback. edgefast_open requires the app to allocate the instance, register callbacks, initiate transport connection, then separately initiate the OBEX session.
MCE MAS Connect / Disconnect#
edgefast_bluetooth
/* Register global callbacks (once, before any connection) */
int bt_map_mce_mas_register(struct bt_map_mce_mas_cb *cb);
int bt_map_mce_mas_unregister(void);
/* Initiate L2CAP connection — stack allocates *mce_mas */
int bt_map_mce_psm_connect(struct bt_conn *conn, uint16_t psm,
uint32_t supported_features,
struct bt_map_mce_mas **mce_mas);
/* Initiate RFCOMM connection — stack allocates *mce_mas */
int bt_map_mce_scn_connect(struct bt_conn *conn, uint8_t scn,
uint32_t supported_features,
struct bt_map_mce_mas **mce_mas);
/* Disconnect (OBEX + transport combined) */
int bt_map_mce_disconnect(struct bt_map_mce_mas *mce_mas);
/* Abort current operation */
int bt_map_mce_abort(struct bt_map_mce_mas *mce_mas);
/* Query maximum OBEX packet length */
int bt_map_mce_get_max_pkt_len(struct bt_map_mce_mas *mce_mas, uint16_t *max_pkt_len);
edgefast_open
/* Per-instance callback registration */
int bt_map_mce_mas_cb_register(struct bt_map_mce_mas *mce_mas,
const struct bt_map_mce_mas_cb *cb);
/* Step 1a: initiate RFCOMM transport connection (app owns mce_mas) */
int bt_map_mce_mas_rfcomm_connect(struct bt_conn *conn,
struct bt_map_mce_mas *mce_mas,
uint8_t channel);
/* Step 1b: initiate L2CAP transport connection */
int bt_map_mce_mas_l2cap_connect(struct bt_conn *conn,
struct bt_map_mce_mas *mce_mas,
uint16_t psm);
/* Step 2: send OBEX CONNECT (call from rfcomm_connected / l2cap_connected cb) */
int bt_map_mce_mas_connect(struct bt_map_mce_mas *mce_mas, struct net_buf *buf);
/* OBEX disconnect */
int bt_map_mce_mas_disconnect(struct bt_map_mce_mas *mce_mas, struct net_buf *buf);
/* Transport disconnect */
int bt_map_mce_mas_rfcomm_disconnect(struct bt_map_mce_mas *mce_mas);
int bt_map_mce_mas_l2cap_disconnect(struct bt_map_mce_mas *mce_mas);
/* Abort current operation (takes optional extra-header buf) */
int bt_map_mce_mas_abort(struct bt_map_mce_mas *mce_mas, struct net_buf *buf);
/* OBEX connect callback returns maximum OBEX packet length */
void (*connect)(struct bt_map_mce_mas *mce_mas, uint8_t rsp_code, uint8_t version,
uint16_t mopl, struct net_buf *buf);
edgefast_open has no
bt_map_mce_mas_unregister(), nosupported_featuresparameter on transport connect (passed separately in OBEX CONNECT headers viabuf), and noget_max_pkt_len— themoplvalue is delivered in theconnectcallback.
4. MCE MAS — MAP Operations #
ℹ️ MAP header encoding: edgefast_bluetooth adds OBEX Connection ID, SRM, SRMP, Name and Type headers based on input parameters and provides dedicated helper macros (
BT_MAP_ADD_*,BT_MAP_ADD_BODY, etc.) to add OBEX body and application parameter headers. edgefast_open requires direct OBEX API calls (bt_obex_add_header_conn_id,bt_obex_add_header_srm,bt_obex_add_header_srm_param,bt_obex_add_header_name,bt_obex_add_header_type,bt_obex_add_header_app_param,bt_obex_add_header_body, etc.), giving the app full control over header construction and placement within thestruct net_buf.
ℹ️ Fragmentation control model: For multi-packet operations, the fragmentation control mechanism has been redesigned. In edgefast_bluetooth, segmentation is controlled through
enum bt_obex_req_flags flags(e.g.,BT_OBEX_REQ_START/BT_OBEX_REQ_END) together with abool waitflag for SRMP handling. In edgefast_open, this is simplified to a singlebool final(true = last packet, false = more packets to follow). SRMP handling is no longer part of the API in edgefast_open — app must explicitly add the SRMP header tobufwhen required.
ℹ️ SRM state management: In edgefast_bluetooth, SRM is automatically handled by the stack: it adds the SRM header based on the transport type (L2CAP or RFCOMM) and maintains SRM state internally. In edgefast_open, full responsibility is shifted to the app — all headers, including SRM, must be explicitly added to
buf, and SRM state must be tracked and managed by the app.
ℹ️ Callback behavior differences with SRM enabled: On the client side, edgefast_bluetooth generates a synthetic response callback after sending a request to prompt the app to send the next request, even if no actual response has been received from the peer; edgefast_open does not generate a response callback unless an actual response is received, requiring the app to schedule the next request. On the server side, edgefast_bluetooth immediately generates a synthetic request callback after sending a response to prompt the app to send the next response, even without an actual request from the peer; edgefast_open only generates a request callback when an actual request is received, and the app must explicitly send subsequent responses.
edgefast_bluetooth
int bt_map_mce_set_folder(struct bt_map_mce_mas *mce_mas, char *name);
int bt_map_mce_get_folder_listing(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, bool wait,
enum bt_obex_req_flags flags);
int bt_map_mce_get_msg_listing(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, char *name,
bool wait, enum bt_obex_req_flags flags);
int bt_map_mce_get_msg(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, char *name,
bool wait, enum bt_obex_req_flags flags);
int bt_map_mce_set_msg_status(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, char *name,
enum bt_obex_req_flags flags);
int bt_map_mce_push_msg(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, char *name,
enum bt_obex_req_flags flags);
int bt_map_mce_set_ntf_reg(struct bt_map_mce_mas *mce_mas, struct net_buf *buf);
int bt_map_mce_update_inbox(struct bt_map_mce_mas *mce_mas);
int bt_map_mce_get_mas_inst_info(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, bool wait,
enum bt_obex_req_flags flags);
int bt_map_mce_set_owner_status(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf,
enum bt_obex_req_flags flags);
int bt_map_mce_get_owner_status(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, bool wait,
enum bt_obex_req_flags flags);
int bt_map_mce_get_convo_listing(struct bt_map_mce_mas *mce_mas,
struct net_buf *buf, bool wait,
enum bt_obex_req_flags flags);
int bt_map_mce_set_ntf_filter(struct bt_map_mce_mas *mce_mas, struct net_buf *buf);
edgefast_open
int bt_map_mce_mas_set_folder(struct bt_map_mce_mas *mce_mas, uint8_t flags,
struct net_buf *buf);
int bt_map_mce_mas_get_folder_listing(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_get_msg_listing(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_get_msg(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_set_msg_status(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_push_msg(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_set_ntf_reg(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_update_inbox(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_get_mas_inst_info(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_set_owner_status(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_get_owner_status(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_get_convo_listing(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
int bt_map_mce_mas_set_ntf_filter(struct bt_map_mce_mas *mce_mas, bool final,
struct net_buf *buf);
5. MCE MNS — Notification Server #
ℹ️ Summary of changes: In edgefast_bluetooth, all MCE MNS servers are registered by a single call
bt_map_mce_mns_register(cb)and it only receivesconnected/disconnectedandsend_eventcallbacks. In edgefast_open, the MCE MNS server must explicitly register typed server structs (one for RFCOMM, one for L2CAP), accept incoming connections in anacceptcallback, register per-instance callbacks, and respond to OBEX CONNECT / DISCONNECT / ABORT requests.
ℹ️ SRM state management: Same as MCE MAS — MAP Operations
ℹ️ Callback behavior differences with SRM enabled: Same as MCE MAS — MAP Operations
edgefast_bluetooth
/* Register global callbacks and all MCE MNS servers */
int bt_map_mce_mns_register(struct bt_map_mce_mns_cb *cb);
int bt_map_mce_mns_unregister(void);
/* Disconnect incoming MNS */
int bt_map_mce_mns_disconnect(struct bt_map_mce_mns *mce_mns);
/* Respond to SendEvent from MSE */
int bt_map_mce_send_event_response(struct bt_map_mce_mns *mce_mns,
uint8_t result, bool wait);
edgefast_open
/* Per-server registration */
int bt_map_mce_mns_rfcomm_register(struct bt_map_mce_mns_rfcomm_server *server);
int bt_map_mce_mns_l2cap_register(struct bt_map_mce_mns_l2cap_server *server);
/* Per-instance callback registration */
int bt_map_mce_mns_cb_register(struct bt_map_mce_mns *mce_mns,
const struct bt_map_mce_mns_cb *cb);
/* Respond to OBEX CONNECT request */
int bt_map_mce_mns_connect(struct bt_map_mce_mns *mce_mns,
uint8_t rsp_code, struct net_buf *buf);
/* Respond to OBEX DISCONNECT request */
int bt_map_mce_mns_disconnect(struct bt_map_mce_mns *mce_mns,
uint8_t rsp_code, struct net_buf *buf);
/* Respond to OBEX ABORT request */
int bt_map_mce_mns_abort(struct bt_map_mce_mns *mce_mns,
uint8_t rsp_code, struct net_buf *buf);
/* Respond to SendEvent from MSE */
int bt_map_mce_mns_send_event(struct bt_map_mce_mns *mce_mns,
uint8_t rsp_code, struct net_buf *buf);
/* Transport disconnect */
int bt_map_mce_mns_rfcomm_disconnect(struct bt_map_mce_mns *mce_mns);
int bt_map_mce_mns_l2cap_disconnect(struct bt_map_mce_mns *mce_mns);
/* Allocate PDU buffer */
struct net_buf *bt_map_mce_mns_create_pdu(struct bt_map_mce_mns *mce_mns,
struct net_buf_pool *pool);
Function renamed
bt_map_mce_send_event_response→bt_map_mce_mns_send_event.bool waitremoved;bufadded for SRMP headers. edgefast_open requires explicit OBEX handshake responses (CONNECT / DISCONNECT / ABORT) that edgefast_bluetooth handled internally.
6. MSE MAS — Server Registration & Responses #
ℹ️ Connection management: In edgefast_bluetooth, MSE MAS is registered with a single global callback and the stack delivers
mse_masobject viaconnectedcallback. In edgefast_open, the app registers typed RFCOMM/L2CAP server structs with anacceptcallback that allocates the instance and registers per-instance callbacks.
ℹ️ MAP header encoding: Same as MCE MAS — MAP Operations
ℹ️ SRM state management: Same as MCE MAS — MAP Operations
ℹ️ Callback behavior differences with SRM enabled: Same as MCE MAS — MAP Operations
edgefast_bluetooth
/* Single global registration */
int bt_map_mse_mas_register(struct bt_map_mse_mas_cb *cb);
int bt_map_mse_mas_unregister(void);
/* Disconnect (combined) */
int bt_map_mse_disconnect(struct bt_map_mse_mas *mse_mas);
/* Query maximum OBEX packet length */
int bt_map_mse_get_max_pkt_len(struct bt_map_mse_mas *mse_mas, uint16_t *max_pkt_len);
/* Response functions */
int bt_map_mse_set_folder_response(struct bt_map_mse_mas *mse_mas, uint8_t result);
int bt_map_mse_get_folder_listing_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, struct net_buf *buf, bool wait);
int bt_map_mse_get_msg_listing_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, struct net_buf *buf, bool wait);
int bt_map_mse_get_msg_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, struct net_buf *buf, bool wait);
int bt_map_mse_set_msg_status_response(struct bt_map_mse_mas *mse_mas, uint8_t result);
int bt_map_mse_push_msg_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, char *name, bool wait);
int bt_map_mse_set_ntf_reg_response(struct bt_map_mse_mas *mse_mas, uint8_t result);
int bt_map_mse_update_inbox_response(struct bt_map_mse_mas *mse_mas, uint8_t result);
int bt_map_mse_get_mas_inst_info_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, struct net_buf *buf, bool wait);
int bt_map_mse_set_owner_status_response(struct bt_map_mse_mas *mse_mas, uint8_t result);
int bt_map_mse_get_owner_status_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, struct net_buf *buf, bool wait);
int bt_map_mse_get_convo_listing_response(struct bt_map_mse_mas *mse_mas,
uint8_t result, struct net_buf *buf, bool wait);
int bt_map_mse_set_ntf_filter_response(struct bt_map_mse_mas *mse_mas, uint8_t result);
edgefast_open
/* Per-server registration */
int bt_map_mse_mas_rfcomm_register(struct bt_map_mse_mas_rfcomm_server *server);
int bt_map_mse_mas_l2cap_register(struct bt_map_mse_mas_l2cap_server *server);
/* Per-instance callback registration */
int bt_map_mse_mas_cb_register(struct bt_map_mse_mas *mse_mas,
const struct bt_map_mse_mas_cb *cb);
/* Explicit OBEX handshake responses */
int bt_map_mse_mas_connect(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_disconnect(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_abort(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
/* Transport disconnect */
int bt_map_mse_mas_rfcomm_disconnect(struct bt_map_mse_mas *mse_mas);
int bt_map_mse_mas_l2cap_disconnect(struct bt_map_mse_mas *mse_mas);
/* Response functions */
int bt_map_mse_mas_set_folder(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_get_folder_listing(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_get_msg_listing(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_get_msg(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_set_msg_status(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_push_msg(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_set_ntf_reg(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_update_inbox(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_get_mas_inst_info(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_set_owner_status(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_get_owner_status(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_get_convo_listing(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
int bt_map_mse_mas_set_ntf_filter(struct bt_map_mse_mas *mse_mas,
uint8_t rsp_code, struct net_buf *buf);
/* Allocate PDU buffer */
struct net_buf *bt_map_mse_mas_create_pdu(struct bt_map_mse_mas *mse_mas,
struct net_buf_pool *pool);
All response functions renamed from
bt_map_mse_*_response()→bt_map_mse_mas_*().bool waitremoved.bufalways present.char *nameremoved — name encoded inbuf.
7. MSE MNS — Notification Client #
ℹ️ MAP header encoding: Same as MCE MAS — MAP Operations
ℹ️ SRM state management: Same as MCE MAS — MAP Operations
ℹ️ Callback behavior differences with SRM enabled: Same as MCE MAS — MAP Operations
edgefast_bluetooth
/* Register global callbacks */
int bt_map_mse_mns_register(struct bt_map_mse_mns_cb *cb);
int bt_map_mse_mns_unregister(void);
/* Initiate L2CAP connection to MCE MNS */
int bt_map_mse_mns_psm_connect(struct bt_conn *conn, uint16_t psm,
uint32_t supported_features,
struct bt_map_mse_mns **mse_mns);
/* Initiate RFCOMM connection to MCE MNS */
int bt_map_mse_mns_scn_connect(struct bt_conn *conn, uint8_t scn,
uint32_t supported_features,
struct bt_map_mse_mns **mse_mns);
/* Disconnect */
int bt_map_mse_mns_disconnect(struct bt_map_mse_mns *mse_mns);
/* Send event to MCE (fragmented with flags) */
int bt_map_mse_send_event(struct bt_map_mse_mns *mse_mns,
struct net_buf *buf,
enum bt_obex_req_flags flags);
/* Query negotiated MTU */
int bt_map_mse_mns_get_max_pkt_len(struct bt_map_mse_mns *mse_mns,
uint16_t *max_pkt_len);
/* Reserve space: BT_MAP_MSE_RSV_LEN_SEND_EVENT(mse_mns, flags) */
edgefast_open
/* Per-instance callback registration */
int bt_map_mse_mns_cb_register(struct bt_map_mse_mns *mse_mns,
const struct bt_map_mse_mns_cb *cb);
/* Step 1a: initiate RFCOMM transport connection (app owns mse_mns) */
int bt_map_mse_mns_rfcomm_connect(struct bt_conn *conn,
struct bt_map_mse_mns *mse_mns,
uint8_t channel);
/* Step 1b: initiate L2CAP transport connection */
int bt_map_mse_mns_l2cap_connect(struct bt_conn *conn,
struct bt_map_mse_mns *mse_mns,
uint16_t psm);
/* Step 2: send OBEX CONNECT */
int bt_map_mse_mns_connect(struct bt_map_mse_mns *mse_mns, struct net_buf *buf);
/* OBEX disconnect */
int bt_map_mse_mns_disconnect(struct bt_map_mse_mns *mse_mns, struct net_buf *buf);
/* Transport disconnect */
int bt_map_mse_mns_rfcomm_disconnect(struct bt_map_mse_mns *mse_mns);
int bt_map_mse_mns_l2cap_disconnect(struct bt_map_mse_mns *mse_mns);
/* OBEX abort */
int bt_map_mse_mns_abort(struct bt_map_mse_mns *mse_mns, struct net_buf *buf);
/* Send event to MCE (fragmented with bool final) */
int bt_map_mse_mns_send_event(struct bt_map_mse_mns *mse_mns, bool final,
struct net_buf *buf);
/* Allocate PDU buffer */
struct net_buf *bt_map_mse_mns_create_pdu(struct bt_map_mse_mns *mse_mns,
struct net_buf_pool *pool);
Function renamed
bt_map_mse_send_event→bt_map_mse_mns_send_event.enum bt_obex_req_flags flags→bool final. Two-step transport + OBEX connect replaces one-steppsm_connect/scn_connect. MSE MNS callbacksconnected(mse_mns)/disconnected(mse_mns, result)/send_event(mse_mns, result)in edgefast_bluetooth become split transport + OBEX callbacks in edgefast_open.
8. Callback Structures Comparison #
8.1 MCE MAS Callbacks#
Callback |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
Transport connected |
|
|
🟡 edgefast_open exposes |
Transport disconnected |
|
|
🟡 No result code in edgefast_open transport disconnect |
OBEX connect |
— (implicit in |
|
🟡 edgefast_open explicit OBEX connect response with OBEX version + MOPL |
OBEX disconnect |
— (implicit in |
|
🟡 edgefast_open explicit OBEX disconnect response |
OBEX abort response |
|
|
🟡 edgefast_open adds |
get_folder_listing |
|
|
🟡 Identical semantics; parameter renamed |
set_folder |
|
|
🟡 edgefast_open adds |
get_msg_listing |
|
|
🟡 Identical |
get_msg |
|
|
🟡 Identical |
set_msg_status |
|
|
🟡 edgefast_open adds |
push_msg |
|
|
🟡 |
set_ntf_reg |
|
|
🟡 edgefast_open adds |
update_inbox |
|
|
🟡 edgefast_open adds |
get_mas_inst_info |
|
|
🟡 Identical |
set_owner_status |
|
|
🟡 edgefast_open adds |
get_owner_status |
|
|
🟡 Identical |
get_convo_listing |
|
|
🟡 Identical |
set_ntf_filter |
|
|
🟡 edgefast_open adds |
8.2 MCE MNS Callbacks#
Callback |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
Transport connected |
|
|
🟡 edgefast_open exposes |
Transport disconnected |
|
|
🟡 No result code in edgefast_open transport disconnect |
OBEX connect request |
— (implicit in |
|
🟡 edgefast_open receives explicit CONNECT request and must respond |
OBEX disconnect request |
— (implicit in |
|
🟡 edgefast_open receives explicit DISCONNECT request and must respond |
OBEX abort request |
— |
|
🔴 edgefast_open only |
send_event |
|
|
🟡 edgefast_bluetooth uses |
8.3 MSE MAS Callbacks#
Callback |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
Transport connected |
|
|
🟡 edgefast_bluetooth delivers assigned PSM/SCN in callback; edgefast_open exposes |
Transport disconnected |
|
|
🟡 No result code in edgefast_open transport disconnect |
OBEX connect request |
— (implicit in |
|
🟡 edgefast_open requires explicit CONNECT handshake response |
OBEX disconnect request |
— (implicit in |
|
🟡 edgefast_open requires explicit DISCONNECT handshake response |
OBEX abort request |
|
|
🟡 edgefast_bluetooth handles abort response internally; edgefast_open requires explicit response |
get_folder_listing |
|
|
🟡 edgefast_open uses |
set_folder |
|
|
🟡 edgefast_bluetooth delivers name string; edgefast_open delivers flags + raw buf with name encoded in |
get_msg_listing |
|
|
🟡 |
get_msg |
|
|
🟡 |
set_msg_status |
|
|
🟡 |
push_msg |
|
|
🟡 |
set_ntf_reg |
|
|
🟡 Identical |
update_inbox |
|
|
🟡 edgefast_open passes |
get_mas_inst_info |
|
|
🟡 Identical |
set_owner_status |
|
|
🟡 Identical |
get_owner_status |
|
|
🟡 Identical |
get_convo_listing |
|
|
🟡 Identical |
set_ntf_filter |
|
|
🟡 Identical |
8.4 MSE MNS Callbacks#
Callback |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
Transport connected |
|
|
🟡 edgefast_open exposes |
Transport disconnected |
|
|
🟡 No result code in edgefast_open transport disconnect |
OBEX connect response |
— (implicit in |
|
🟡 edgefast_open explicit OBEX connect response with OBEX version + MOPL |
OBEX disconnect response |
— (implicit in |
|
🟡 edgefast_open explicit OBEX disconnect response |
OBEX abort response |
— |
|
🔴 edgefast_open only |
send_event response |
|
|
🟡 edgefast_open adds |
9. Full Function Comparison Table #
9.1 MCE MAS Functions#
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|
|
|
🟡 Global register replaced by per-instance |
|
— |
🟢 |
|
|
🟡 Stack allocates vs app allocates; no |
|
|
🟡 |
— |
|
🔴 Explicit OBEX CONNECT request |
|
|
🟡 edgefast_open takes |
— |
|
🔴 |
— |
|
🔴 |
|
|
🟡 edgefast_open takes |
|
— |
🟢 MOPL delivered in OBEX |
|
|
🟡 PDU buffer helper |
|
|
🟡 Name string → OBEX Name header in buf; explicit |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 edgefast_open adds |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
9.2 MCE MNS Functions#
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|
|
|
🟡 edgefast_open registers individual servers and per-instance callbacks; edgefast_bluetooth resgiter global callbacks and all servers |
|
— |
🟢 |
|
|
🟡 Split by transport |
— |
|
🔴 Explicit OBEX CONNECT response |
— |
|
🔴 Explicit OBEX DISCONNECT response |
— |
|
🔴 Abort response was internal in edgefast_bluetooth |
|
|
🟡 Renamed; |
|
— |
🟢 MOPL delivered in OBEX |
|
|
🟡 PDU buffer helper |
9.3 MSE MAS Functions#
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|
|
|
🟡 edgefast_open registers individual servers and per-instance callbacks; edgefast_bluetooth resgiters global callbacks and all servers |
|
— |
🟢 |
|
|
🟡 Split by transport |
— |
|
🔴 Explicit OBEX CONNECT response |
— |
|
🔴 Explicit OBEX DISCONNECT response |
— |
|
🔴 Abort response was internal in edgefast_bluetooth |
|
— |
🟢 MOPL delivered in OBEX |
|
|
🟡 PDU buffer helper |
|
|
🟡 edgefast_open adds |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 edgefast_open adds |
|
|
🟡 |
|
|
🟡 edgefast_open adds |
|
|
🟡 edgefast_open adds |
|
|
🟡 |
|
|
🟡 edgefast_open adds |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 edgefast_open adds |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
|
|
🟡 |
9.4 MSE MNS Functions#
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|
|
|
🟡 Global register replaced by per-instance |
|
— |
🟢 |
|
|
🟡 Stack allocates vs app allocates; |
|
|
🟡 |
— |
|
🔴 Explicit OBEX CONNECT request |
|
|
🟡 edgefast_open OBEX disconnect takes |
— |
|
🔴 |
— |
|
🔴 |
— |
|
🔴 Explicit OBEX ABORT |
|
|
🟡 Renamed; |
|
— |
🟢 MOPL delivered in OBEX |
|
|
🟡 PDU buffer helper |
10. Architecture Summary #
ℹ️ A quick reference comparing the two stacks across every dimension of MAP API design.
Dimension |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
Object model |
Stack-managed opaque pointers; app receives |
App-owned structs embedded in app context |
Instance allocation |
Stack allocates on incoming connection or |
App pre-allocates array; instance pointer passed at |
Callback registration |
One |
Per-instance |
Transport layer |
Transparent; single |
Explicit: |
OBEX layer |
Implicit within |
Explicit: |
OBEX DISCONNECT/ABORT |
Handled internally by stack |
App must explicitly call |
Server registration |
Single |
Typed server structs ( |
SRM management |
Automatic: stack adds SRM header and manages SRM state |
Manual: app adds |
SRMP (wait) |
|
Manual: app adds |
Fragmentation |
|
|
Max packet length |
|
Delivered in OBEX |
Buffer allocation |
App pre-allocates with |
App calls |
Name / handle passing |
Dedicated |
Encoded as OBEX Name header inside |
App parameter helpers |
|
|
Body helpers |
|
|
App param parsing |
|
|
SDP registration |
Managed by stack |
App calls |
Migration Checklist (edgefast_bluetooth → edgefast_open)#
☐ Replace single
bt_map_mce_mas_register(cb)with per-instancebt_map_mce_mas_cb_register(mce_mas, cb)called before transport connect☐ Split
bt_map_mce_psm_connect()intobt_map_mce_mas_l2cap_connect()(transport) +bt_map_mce_mas_connect(buf)(OBEX)☐ Add Target UUID and supported-features app param TLV to OBEX CONNECT
buf☐ Add Connection ID, SRM(if required), SRMP(if required) and Type headers to OBEX PUT/GET/SETPATH
bufusingbt_obex_add_header_*☐ Store
moplfromconnect(rsp_code, version, mopl, buf)callback; replaceget_max_pkt_len()with this value☐ Replace
BT_MAP_MCE_RSV_LEN_*+net_buf_reservewithbt_map_mce_mas_create_pdu(mce_mas, pool)☐ Replace
BT_MAP_ADD_*TLV macros withstruct bt_obex_tlv[]+bt_obex_add_header_app_param()☐ Replace
BT_MAP_ADD_BODY/BT_MAP_ADD_END_OF_BODYmacros withbt_obex_add_header_body/bt_obex_add_header_end_body/bt_obex_add_header_body_or_end_body☐ Replace
char *nameparameters withbt_obex_add_header_name(buf, unicode_len, unicode_name)(name must be UTF-16BE encoded)☐ Replace
flags/waitparameters withbool final; manage SRM/SRMP headers manually☐ Replace
bt_map_mce_mns_register(cb)with typed server registration:bt_map_mce_mns_rfcomm_register(server)+bt_map_mce_mns_l2cap_register(server)withacceptcallback☐ Add explicit
bt_map_mce_mns_connect(mce_mns, rsp_code, buf)inconnectcallback; adddisconnectandabortresponse calls☐ Replace
bt_map_mce_send_event_response(mce_mns, result, wait)withbt_map_mce_mns_send_event(mce_mns, rsp_code, buf)☐ On MSE side: replace
bt_map_mse_mas_register(cb)with typedbt_map_mse_mas_rfcomm_register(server)/bt_map_mse_mas_l2cap_register(server)+acceptcallback☐ Add explicit
bt_map_mse_mas_connect(rsp_code, buf)/disconnect(rsp_code, buf)/abort(rsp_code, buf)response calls☐ Rename all
bt_map_mse_*_response(result, buf, wait)→bt_map_mse_mas_*(rsp_code, buf)removingwait☐ Register SDP records with
bt_sdp_register_service()after server registration
11. edgefast_open API Usage Examples (Code Snapshots) #
⚠️ Source files:
middleware/edgefast_open/source/bluetooth/host/classic/shell/map.c
ℹ️ All examples use
NET_BUF_POOL_FIXED_DEFINEpools andbt_map_*_create_pdu()for buffer allocation. Theconn_idis stored from the OBEXconnectcallback and inserted in every subsequent request. SRM/SRMP headers are managed manually by tracking auint8_t srmbitmask.
11.1 MCE MAS — Connection Setup (RFCOMM)#
Two-step connection: first establish RFCOMM transport, then in the transport callback send OBEX CONNECT with Target UUID and supported features.
/* Step 1: allocate app-owned instance, register callbacks, initiate transport */
struct mce_mas_instance {
struct bt_map_mce_mas mce_mas; /* must be first — embedded MAP object */
struct bt_conn *conn;
uint32_t conn_id;
uint16_t mopl;
uint8_t srm;
};
static struct mce_mas_instance g_inst;
static struct bt_map_mce_mas_cb mce_mas_cb = {
.rfcomm_connected = mce_mas_rfcomm_connected,
.rfcomm_disconnected = mce_mas_rfcomm_disconnected,
.l2cap_connected = mce_mas_l2cap_connected,
.l2cap_disconnected = mce_mas_l2cap_disconnected,
.connect = mce_mas_connect,
.disconnect = mce_mas_disconnect,
.abort = mce_mas_abort,
/* ... MAP operation callbacks ... */
};
/* Register per-instance callbacks and start RFCOMM transport */
bt_map_mce_mas_cb_register(&g_inst.mce_mas, &mce_mas_cb);
bt_map_mce_mas_rfcomm_connect(default_conn, &g_inst.mce_mas, channel);
/* Step 2: in rfcomm_connected callback — send OBEX CONNECT */
static void mce_mas_rfcomm_connected(struct bt_conn *conn,
struct bt_map_mce_mas *mce_mas)
{
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
const struct bt_uuid_128 *uuid = BT_MAP_UUID_MAS;
uint8_t val[BT_UUID_SIZE_128];
/* Add Target header: MAP MAS UUID (byte-swapped) */
sys_memcpy_swap(val, uuid->val, sizeof(val));
bt_obex_add_header_target(buf, sizeof(val), val);
/* Add MAP supported features as application parameter TLV */
uint32_t features = sys_cpu_to_be32(0x0077FFFF);
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_MAP_SUPPORTED_FEATURES,
sizeof(features), (const uint8_t *)&features },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
bt_map_mce_mas_connect(mce_mas, buf); /* buf ownership transferred */
}
/* Step 3: in connect callback — store mopl and conn_id */
static void mce_mas_connect(struct bt_map_mce_mas *mce_mas, uint8_t rsp_code,
uint8_t version, uint16_t mopl, struct net_buf *buf)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
inst->mopl = mopl;
bt_obex_get_header_conn_id(buf, &inst->conn_id);
}
11.2 MCE MAS — SetFolder#
Navigate the remote folder tree. Path logic determines flags; name is UTF-16BE encoded into buf.
static void do_set_folder(struct bt_map_mce_mas *mce_mas, const char *path)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
uint8_t flags;
const char *name;
/* Determine flags and name from path string */
if (strcmp(path, "/") == 0) {
flags = BT_MAP_SET_FOLDER_FLAGS_ROOT;
name = NULL;
} else if (strncmp(path, "..", 2) == 0) {
flags = BT_MAP_SET_FOLDER_FLAGS_UP;
name = (path[2] == '/') ? &path[3] : NULL; /* "../child" or ".." */
} else {
flags = BT_MAP_SET_FOLDER_FLAGS_DOWN;
name = path; /* "telecom" or "msg" */
}
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
bt_obex_add_header_conn_id(buf, inst->conn_id);
/* Encode name as UTF-16BE in OBEX Name header */
if (name && strlen(name) > 0) {
char unicode[64];
uint16_t name_len = strlen(name);
memset(unicode, 0, sizeof(unicode));
for (int i = 0; i < name_len; i++) {
unicode[i * 2 + 1] = name[i]; /* ASCII → UTF-16BE */
}
bt_obex_add_header_name(buf, name_len * 2 + 2, unicode);
} else {
bt_obex_add_header_name(buf, 0, NULL);
}
bt_map_mce_mas_set_folder(mce_mas, flags, buf);
/* Response in mce_mas_cb.set_folder(mce_mas, rsp_code, buf) */
}
11.3 MCE MAS — SetNotificationRegistration#
Enable/disable MSE notifications. Requires Type header, NtfStatus TLV, and a 1-byte filler body.
static void do_set_ntf_reg(struct bt_map_mce_mas *mce_mas, uint8_t ntf_status)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
uint16_t len = 0;
bt_obex_add_header_conn_id(buf, inst->conn_id);
/* Type header: x-bt/MAP-NotificationRegistration */
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_SET_NTF_REG),
BT_MAP_HDR_TYPE_SET_NTF_REG);
/* Application parameter: NtfStatus (0x01 = ON, 0x00 = OFF) */
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_NTF_STATUS, sizeof(ntf_status),
(const uint8_t *)&ntf_status },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
/* Filler body (MAP spec requires a body for PUT operations) */
bt_obex_add_header_body_or_end_body(buf, inst->mopl, 1,
BT_MAP_FILLER_BYTE, &len);
bt_map_mce_mas_set_ntf_reg(mce_mas, true /* final */, buf);
/* Response in mce_mas_cb.set_ntf_reg(mce_mas, rsp_code, buf) */
}
11.4 MCE MAS — GetFolderListing (with SRM)#
GET operation with optional SRM acceleration. SRM header added on first request; subsequent continuation handled by checking rsp_code.
static void do_get_folder_listing(struct bt_map_mce_mas *mce_mas)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
if (inst->rsp_code == BT_OBEX_RSP_CODE_CONTINUE) {
/* Continuation packet — SRM enabled, no need to resend headers */
if (is_srm_no_wait_enabled(inst->srm)) {
net_buf_unref(buf);
return; /* SRM in no-wait mode — server sends without MCE re-request */
}
/* SRM disabled or waiting: resend without initial headers */
bt_map_mce_mas_get_folder_listing(mce_mas, true, buf);
return;
}
/* First request: add all headers */
bt_obex_add_header_conn_id(buf, inst->conn_id);
/* Request SRM if using L2CAP (GOEP v2) */
if (inst->goep_v2) {
bt_obex_add_header_srm(buf, BT_OBEX_SRM_ENABLE);
inst->srm |= LOCAL_SRM_ENABLED;
}
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_GET_FOLDER_LISTING),
BT_MAP_HDR_TYPE_GET_FOLDER_LISTING);
/* Optional: add MaxListCount / ListStartOffset as app params */
uint16_t max_list_count = sys_cpu_to_be16(1024);
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_MAX_LIST_CNT, sizeof(max_list_count),
(const uint8_t *)&max_list_count },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
bt_map_mce_mas_get_folder_listing(mce_mas, true /* final */, buf);
}
/* In get_folder_listing response callback */
static void mce_mas_get_folder_listing(struct bt_map_mce_mas *mce_mas,
uint8_t rsp_code, struct net_buf *buf)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
/* Parse folder-listing XML body from buf here */
inst->rsp_code = rsp_code;
if (rsp_code == BT_OBEX_RSP_CODE_CONTINUE) {
/* Check if remote accepted SRM */
parse_header_srm_rsp(buf, &inst->srm);
parse_header_srm_param_rsp(buf, &inst->srm);
/* If SRM no-wait not enabled, app must call do_get_folder_listing again */
} else {
inst->srm = 0; /* Reset SRM state at end of operation */
}
}
11.5 MCE MAS — GetMessageListing#
Similar to GetFolderListing but with a subfolder Name header and message-listing-specific app params.
static void do_get_msg_listing(struct bt_map_mce_mas *mce_mas, const char *folder_name)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
bt_obex_add_header_conn_id(buf, inst->conn_id);
if (inst->goep_v2) {
bt_obex_add_header_srm(buf, BT_OBEX_SRM_ENABLE);
inst->srm |= LOCAL_SRM_ENABLED;
}
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_GET_MSG_LISTING),
BT_MAP_HDR_TYPE_GET_MSG_LISTING);
/* Encode subfolder name as UTF-16BE (NULL name = current folder) */
if (folder_name && strlen(folder_name) > 0) {
uint16_t name_len = strlen(folder_name);
char unicode[64] = {0};
for (int i = 0; i < name_len; i++) {
unicode[i * 2 + 1] = folder_name[i];
}
bt_obex_add_header_name(buf, name_len * 2 + 2, unicode);
} else {
bt_obex_add_header_name(buf, 0, NULL);
}
/* App params: MaxListCount=100, FilterMessageType=0 (all types) */
uint16_t max_cnt = sys_cpu_to_be16(100);
uint8_t filter_type = 0x00;
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_MAX_LIST_CNT, sizeof(max_cnt),
(const uint8_t *)&max_cnt },
{ BT_MAP_APPL_PARAM_TAG_ID_FILTER_MSG_TYPE, sizeof(filter_type),
(const uint8_t *)&filter_type },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
bt_map_mce_mas_get_msg_listing(mce_mas, true /* final */, buf);
/* Response in mce_mas_cb.get_msg_listing(mce_mas, rsp_code, buf) */
}
11.6 MCE MAS — GetMessage#
Retrieve a specific message by handle (encoded as Name header). Charset and Attachment app params control the response format.
static void do_get_msg(struct bt_map_mce_mas *mce_mas, const char *msg_handle)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
bt_obex_add_header_conn_id(buf, inst->conn_id);
if (inst->goep_v2) {
bt_obex_add_header_srm(buf, BT_OBEX_SRM_ENABLE);
inst->srm |= LOCAL_SRM_ENABLED;
}
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_GET_MSG),
BT_MAP_HDR_TYPE_GET_MSG);
/* Message handle as UTF-16BE Name header */
uint16_t handle_len = strlen(msg_handle);
char unicode[32] = {0};
for (int i = 0; i < handle_len; i++) {
unicode[i * 2 + 1] = msg_handle[i];
}
bt_obex_add_header_name(buf, handle_len * 2 + 2, unicode);
/* App params: Charset=UTF-8, Attachment=1 */
uint8_t charset = BT_MAP_CHARSET_UTF8;
uint8_t attachment = BT_MAP_ATTACHMENT_ON;
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_CHARSET, sizeof(charset),
(const uint8_t *)&charset },
{ BT_MAP_APPL_PARAM_TAG_ID_ATTACHMENT, sizeof(attachment),
(const uint8_t *)&attachment },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
bt_map_mce_mas_get_msg(mce_mas, true /* final */, buf);
/* Message body arrives in mce_mas_cb.get_msg(mce_mas, rsp_code, buf) */
}
11.7 MCE MAS — SetMessageStatus#
Mark a message as read. StatusIndicator and StatusValue TLVs control which attribute to change.
static void do_set_msg_status(struct bt_map_mce_mas *mce_mas,
const char *msg_handle)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
uint16_t len = 0;
bt_obex_add_header_conn_id(buf, inst->conn_id);
/* Encode message handle as UTF-16BE Name header */
uint16_t handle_len = strlen(msg_handle);
char unicode[32] = {0};
for (int i = 0; i < handle_len; i++) {
unicode[i * 2 + 1] = msg_handle[i];
}
bt_obex_add_header_name(buf, handle_len * 2 + 2, unicode);
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_SET_MSG_STATUS),
BT_MAP_HDR_TYPE_SET_MSG_STATUS);
/* StatusIndicator=READ (0), StatusValue=YES (1) → mark as read */
uint8_t status_ind = BT_MAP_STATUS_IND_READ;
uint8_t status_val = BT_MAP_STATUS_VAL_YES;
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_STATUS_IND, sizeof(status_ind),
(const uint8_t *)&status_ind },
{ BT_MAP_APPL_PARAM_TAG_ID_STATUS_VAL, sizeof(status_val),
(const uint8_t *)&status_val },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
/* Filler body required for PUT operations */
bt_obex_add_header_body_or_end_body(buf, inst->mopl, 1, BT_MAP_FILLER_BYTE, &len);
bt_map_mce_mas_set_msg_status(mce_mas, true /* final */, buf);
/* Response in mce_mas_cb.set_msg_status(mce_mas, rsp_code, buf) */
}
11.8 MCE MAS — PushMessage (fragmented body)#
Large message bodies are split across multiple OBEX PUT packets. The bool final is determined by whether bt_obex_add_header_body_or_end_body placed an end-of-body header.
static const char *MSG_BODY =
"BEGIN:BMSG\r\n"
"VERSION:1.0\r\nSTATUS:UNREAD\r\nTYPE:SMS_GSM\r\nFOLDER:\r\n"
"BEGIN:VCARD\r\nVERSION:2.1\r\nN;CHARSET=UTF-8:\r\nTEL;CHARSET=UTF-8:\r\n"
"END:VCARD\r\n"
"BEGIN:BENV\r\nBEGIN:VCARD\r\nVERSION:2.1\r\nFN;CHARSET=UTF-8:+0123456789\r\n"
"TEL:+0123456789\r\nEND:VCARD\r\n"
"BEGIN:BBODY\r\nCHARSET:UTF-8\r\nLENGTH:50\r\n"
"BEGIN:MSG\r\nHello from edgefast_open MAP!\r\nEND:MSG\r\n"
"END:BBODY\r\nEND:BENV\r\nEND:BMSG";
static uint16_t g_tx_cnt = 0; /* bytes already sent */
static void do_push_msg(struct bt_map_mce_mas *mce_mas, const char *folder)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
uint16_t len = 0;
bool final;
if (g_tx_cnt > 0) {
/* Continuation: only body data needed */
goto continue_req;
}
/* First packet: headers + first body chunk */
bt_obex_add_header_conn_id(buf, inst->conn_id);
if (inst->goep_v2) {
bt_obex_add_header_srm(buf, BT_OBEX_SRM_ENABLE);
inst->srm |= LOCAL_SRM_ENABLED;
}
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_PUSH_MSG),
BT_MAP_HDR_TYPE_PUSH_MSG);
/* Target folder as UTF-16BE Name header */
if (folder && strlen(folder) > 0) {
uint16_t flen = strlen(folder);
char unicode[64] = {0};
for (int i = 0; i < flen; i++) {
unicode[i * 2 + 1] = folder[i];
}
bt_obex_add_header_name(buf, flen * 2 + 2, unicode);
} else {
bt_obex_add_header_name(buf, 0, NULL);
}
/* App params: Charset=UTF-8 */
uint8_t charset = BT_MAP_CHARSET_UTF8;
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_CHARSET, sizeof(charset),
(const uint8_t *)&charset },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
continue_req:
/* Add as much body as fits; function places Body or End-Body header */
bt_obex_add_header_body_or_end_body(
buf, inst->mopl,
strlen(MSG_BODY) - g_tx_cnt,
MSG_BODY + g_tx_cnt,
&len);
/* final=true when End-Body header was placed */
final = bt_obex_has_header(buf, BT_OBEX_HEADER_ID_END_BODY);
if (bt_map_mce_mas_push_msg(mce_mas, final, buf) == 0) {
if (!final) {
g_tx_cnt += len;
} else {
g_tx_cnt = 0; /* reset for next push */
}
}
/* Response in mce_mas_cb.push_msg(mce_mas, rsp_code, buf) */
}
11.9 MCE MAS — UpdateInbox#
Single-packet PUT with Type header and filler body.
static void do_update_inbox(struct bt_map_mce_mas *mce_mas)
{
struct mce_mas_instance *inst =
CONTAINER_OF(mce_mas, struct mce_mas_instance, mce_mas);
struct net_buf *buf = bt_map_mce_mas_create_pdu(mce_mas, &mas_tx_pool);
uint16_t len = 0;
bt_obex_add_header_conn_id(buf, inst->conn_id);
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_UPDATE_INBOX),
BT_MAP_HDR_TYPE_UPDATE_INBOX);
/* Filler body */
bt_obex_add_header_body_or_end_body(buf, inst->mopl, 1, BT_MAP_FILLER_BYTE, &len);
bt_map_mce_mas_update_inbox(mce_mas, true /* final */, buf);
/* Response in mce_mas_cb.update_inbox(mce_mas, rsp_code, buf) */
}
11.10 MCE MNS — Server Registration & Responding to SendEvent#
MCE registers a typed server struct with an accept callback. In accept, the app allocates the instance and registers per-instance callbacks. In the send_event callback, MCE responds with success or continue.
static struct bt_map_mce_mns_l2cap_server mce_l2cap_server;
static struct mce_mns_instance g_mns_inst;
/* accept callback: allocate instance and register per-instance callbacks */
static int mce_mns_l2cap_accept(struct bt_conn *conn,
struct bt_map_mce_mns_l2cap_server *server,
struct bt_map_mce_mns **mce_mns)
{
g_mns_inst.conn = bt_conn_ref(conn);
bt_map_mce_mns_cb_register(&g_mns_inst.mce_mns, &mce_mns_cb);
*mce_mns = &g_mns_inst.mce_mns;
return 0;
}
/* Register L2CAP server (call once at startup) */
static void register_mce_mns(void)
{
mce_l2cap_server.accept = mce_mns_l2cap_accept;
bt_map_mce_mns_l2cap_register(&mce_l2cap_server);
bt_sdp_register_service(&mce_mns_sdp_rec);
}
/* OBEX CONNECT request arrived — respond with SUCCESS */
static void mce_mns_connect(struct bt_map_mce_mns *mce_mns,
uint8_t version, uint16_t mopl, struct net_buf *buf)
{
bt_map_mce_mns_connect(mce_mns, BT_OBEX_RSP_CODE_SUCCESS, NULL);
}
/* OBEX DISCONNECT request arrived — respond and clean up */
static void mce_mns_disconnect(struct bt_map_mce_mns *mce_mns, struct net_buf *buf)
{
bt_map_mce_mns_disconnect(mce_mns, BT_OBEX_RSP_CODE_SUCCESS, NULL);
}
/* MSE sent an event — respond with SUCCESS (or CONTINUE for multi-packet) */
static void mce_mns_send_event(struct bt_map_mce_mns *mce_mns,
bool final, struct net_buf *buf)
{
struct mce_mns_instance *inst =
CONTAINER_OF(mce_mns, struct mce_mns_instance, mce_mns);
/* Parse event-report XML body from buf here */
uint8_t rsp_code = final ? BT_OBEX_RSP_CODE_SUCCESS
: BT_OBEX_RSP_CODE_CONTINUE;
struct net_buf *rsp_buf = bt_map_mce_mns_create_pdu(mce_mns, &mns_tx_pool);
/* Add SRM header in response if remote requested it */
if (is_remote_srm_enabled(inst->srm) && !is_srm_enabled(inst->srm)) {
bt_obex_add_header_srm(rsp_buf, BT_OBEX_SRM_ENABLE);
inst->srm |= LOCAL_SRM_ENABLED;
}
bt_map_mce_mns_send_event(mce_mns, rsp_code, rsp_buf);
}
11.11 MSE MAS — Server Registration & Responding to Requests#
MSE registers typed server structs. In the connect callback, MSE responds to OBEX CONNECT. For each MAP operation callback, MSE builds a response buf and calls the corresponding response function.
static struct bt_map_mse_mas_rfcomm_server mse_rfcomm_server;
static struct bt_map_mse_mas_l2cap_server mse_l2cap_server;
static struct mse_mas_instance g_mse_inst;
/* accept callback for RFCOMM */
static int mse_mas_rfcomm_accept(struct bt_conn *conn,
struct bt_map_mse_mas_rfcomm_server *server,
struct bt_map_mse_mas **mse_mas)
{
g_mse_inst.conn = bt_conn_ref(conn);
bt_map_mse_mas_cb_register(&g_mse_inst.mse_mas, &mse_mas_cb);
*mse_mas = &g_mse_inst.mse_mas;
return 0;
}
/* Register servers at startup */
static void register_mse_mas(void)
{
mse_rfcomm_server.accept = mse_mas_rfcomm_accept;
mse_l2cap_server.accept = mse_mas_l2cap_accept;
bt_map_mse_mas_rfcomm_register(&mse_rfcomm_server);
bt_map_mse_mas_l2cap_register(&mse_l2cap_server);
bt_sdp_register_service(&mse_mas_sdp_rec);
}
/* Respond to OBEX CONNECT */
static void mse_mas_connect(struct bt_map_mse_mas *mse_mas,
uint8_t version, uint16_t mopl, struct net_buf *buf)
{
struct mse_mas_instance *inst =
CONTAINER_OF(mse_mas, struct mse_mas_instance, mse_mas);
inst->mopl = mopl;
bt_map_mse_mas_connect(mse_mas, BT_OBEX_RSP_CODE_SUCCESS, NULL);
}
/* Respond to GetFolderListing — send folder XML */
static void mse_mas_get_folder_listing(struct bt_map_mse_mas *mse_mas,
bool final, struct net_buf *buf)
{
struct mse_mas_instance *inst =
CONTAINER_OF(mse_mas, struct mse_mas_instance, mse_mas);
struct net_buf *rsp_buf = bt_map_mse_mas_create_pdu(mse_mas, &mas_tx_pool);
/* Optionally accept SRM from client request */
parse_header_srm_req(buf, &inst->srm);
if (is_remote_srm_enabled(inst->srm) && !is_srm_enabled(inst->srm)) {
bt_obex_add_header_srm(rsp_buf, BT_OBEX_SRM_ENABLE);
inst->srm |= LOCAL_SRM_ENABLED;
}
/* App params: FolderListingSize */
uint16_t listing_size = sys_cpu_to_be16(5);
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_FOLDER_LISTING_SIZE, sizeof(listing_size),
(const uint8_t *)&listing_size },
};
bt_obex_add_header_app_param(rsp_buf, ARRAY_SIZE(appl_params), appl_params);
/* Add folder-listing XML as body */
const char *xml = "<folder-listing version=\"1.0\"></folder-listing>";
uint16_t len = 0;
bt_obex_add_header_body_or_end_body(rsp_buf, inst->mopl, strlen(xml), xml, &len);
bt_map_mse_mas_get_folder_listing(mse_mas, BT_OBEX_RSP_CODE_SUCCESS, rsp_buf);
}
/* Respond to SetNotificationRegistration */
static void mse_mas_set_ntf_reg(struct bt_map_mse_mas *mse_mas,
bool final, struct net_buf *buf)
{
/* Parse NtfStatus TLV from application parameters */
bt_obex_tlv_parse(/* len */, /* data */, parse_ntf_status_cb, NULL);
/* Respond SUCCESS — if ntf_status == 1, initiate MNS connection */
bt_map_mse_mas_set_ntf_reg(mse_mas, BT_OBEX_RSP_CODE_SUCCESS, NULL);
}
11.12 MSE MNS — Connecting to MCE and Sending Events#
After receiving SetNotificationRegistration ON, MSE initiates MNS connection to MCE. Once OBEX is established, MSE sends event-report XML via bt_map_mse_mns_send_event.
static struct mse_mns_instance g_mse_mns_inst;
/* Initiate MNS connection to MCE after SetNotificationRegistration ON */
static void initiate_mns_connection(struct bt_conn *conn,
uint16_t psm_from_sdp)
{
bt_map_mse_mns_cb_register(&g_mse_mns_inst.mse_mns, &mse_mns_cb);
/* Step 1: L2CAP transport connect */
bt_map_mse_mns_l2cap_connect(conn, &g_mse_mns_inst.mse_mns, psm_from_sdp);
}
/* Step 2: in l2cap_connected callback — send OBEX CONNECT */
static void mse_mns_l2cap_connected(struct bt_conn *conn,
struct bt_map_mse_mns *mse_mns)
{
struct net_buf *buf = bt_map_mse_mns_create_pdu(mse_mns, &mns_tx_pool);
/* Target: MAP MNS UUID */
const struct bt_uuid_128 *uuid = BT_MAP_UUID_MNS;
uint8_t val[BT_UUID_SIZE_128];
sys_memcpy_swap(val, uuid->val, sizeof(val));
bt_obex_add_header_target(buf, sizeof(val), val);
bt_map_mse_mns_connect(mse_mns, buf);
}
/* Step 3: in connect callback — store mopl, start sending events */
static void mse_mns_connect(struct bt_map_mse_mns *mse_mns, uint8_t rsp_code,
uint8_t version, uint16_t mopl, struct net_buf *buf)
{
struct mse_mns_instance *inst =
CONTAINER_OF(mse_mns, struct mse_mns_instance, mse_mns);
if (rsp_code == BT_OBEX_RSP_CODE_SUCCESS) {
inst->mopl = mopl;
/* Now ready to send events */
}
}
/* Send a new-message event notification */
static void send_new_message_event(struct bt_map_mse_mns *mse_mns)
{
struct mse_mns_instance *inst =
CONTAINER_OF(mse_mns, struct mse_mns_instance, mse_mns);
struct net_buf *buf = bt_map_mse_mns_create_pdu(mse_mns, &mns_tx_pool);
bt_obex_add_header_conn_id(buf, inst->conn_id);
/* Type header */
bt_obex_add_header_type(buf, sizeof(BT_MAP_HDR_TYPE_SEND_EVENT),
BT_MAP_HDR_TYPE_SEND_EVENT);
/* App params: MasInstanceId */
uint8_t mas_id = 0;
struct bt_obex_tlv appl_params[] = {
{ BT_MAP_APPL_PARAM_TAG_ID_MAS_INST_ID, sizeof(mas_id),
(const uint8_t *)&mas_id },
};
bt_obex_add_header_app_param(buf, ARRAY_SIZE(appl_params), appl_params);
/* Event-report XML as body */
const char *event_xml =
"<MAP-event-report version=\"1.0\">"
"<event type=\"NewMessage\" handle=\"0001\" folder=\"INBOX\""
" msg_type=\"SMS_GSM\"/>"
"</MAP-event-report>";
uint16_t len = 0;
bt_obex_add_header_body_or_end_body(buf, inst->mopl,
strlen(event_xml), event_xml, &len);
bt_map_mse_mns_send_event(mse_mns, true /* final */, buf);
/* Response in mse_mns_cb.send_event(mse_mns, rsp_code, buf) */
}
12. MAP Kconfig Configuration Comparison #
⚠️ Source files: edgefast_bluetooth —
middleware/edgefast_bluetooth/include/Kconfig| edgefast_open —middleware/edgefast_open/source/bluetooth/host/classic/Kconfig
ℹ️ Key differences: edgefast_bluetooth provides fine-grained instance/MTU/supported-features tunables per role. edgefast_open has minimal Kconfig symbols — instance limits are controlled by application-level array sizes, and MTU is governed by the shared
BT_GOEP_RFCOMM_MTU/BT_GOEP_L2CAP_MTUsymbols.
12.1 Core Protocol Enablement#
Symbol |
edgefast_bluetooth |
edgefast_open |
Notes |
|---|---|---|---|
|
— (implicit) |
bool, no default |
edgefast_open exposes GOEP as a separate Kconfig knob; edgefast_bluetooth enables it implicitly |
|
— (hard-coded per role) |
int, default 255 |
edgefast_open shared RFCOMM MTU for all GOEP profiles; replaces per-role |
|
— (hard-coded per role) |
int, default 255 |
edgefast_open shared L2CAP MTU for all GOEP profiles |
|
bool, default 0 |
bool (no default; select |
Master MAP enable. Both stacks gate all MAP sub-features on this symbol |
|
bool, default 0 |
bool (no default; select |
Enables MCE (client) role |
|
bool, default 0 |
bool (no default; select |
Enables MSE (server) role |
12.2 Instance and MTU Tunables#
edgefast_bluetooth symbol |
edgefast_open equivalent |
Type |
Notes |
|---|---|---|---|
|
— |
int, default 2 |
edgefast_bluetooth limits the number of concurrent MCE MAS connections. edgefast_open manages this in app-level |
|
— |
int, default 1 |
edgefast_bluetooth MCE MNS instance limit. edgefast_open manages this in app-level |
|
|
int, default 512 (EB) / 255 (EO) |
edgefast_bluetooth per-connection MTU for MCE MAS. edgefast_open uses shared GOEP MTU |
|
|
int, default 1790 (EB) / 255 (EO) |
edgefast_bluetooth per-connection MTU for MCE MNS. edgefast_open uses shared GOEP MTU |
|
— |
int, default 2 |
edgefast_bluetooth MSE MAS instance limit. edgefast_open manages this in app-level |
|
— |
int, default 1 |
edgefast_bluetooth MSE MNS instance limit. edgefast_open manages this in app-level |
|
|
int, default 1790 (EB) / 255 (EO) |
edgefast_bluetooth per-connection MTU for MSE MAS. edgefast_open uses shared GOEP MTU |
|
|
int, default 512 (EB) / 255 (EO) |
edgefast_bluetooth per-connection MTU for MSE MNS. edgefast_open uses shared GOEP MTU |
12.3 Supported Features Defaults#
edgefast_bluetooth symbol |
edgefast_open equivalent |
Type |
Notes |
|---|---|---|---|
|
— |
hex, default |
edgefast_bluetooth bakes default supported features into Kconfig. edgefast_open defines this in app-level |
|
— |
hex, default |
Same pattern for MSE role |
12.4 Side-by-Side Symbol Summary#
Symbol |
edgefast_bluetooth |
edgefast_open |
|---|---|---|
|
— (implicit) |
bool |
|
— |
int, default 255 |
|
— |
int, default 255 |
|
bool, default 0 |
bool |
|
bool, default 0 |
bool |
|
bool, default 0 |
bool |
|
int, default 2 |
— |
|
int, default 1 |
— |
|
int, default 512 |
→ |
|
int, default 1790 |
→ |
|
hex, default |
defined in app-level |
|
int, default 2 |
— |
|
int, default 1 |
— |
|
int, default 1790 |
→ |
|
int, default 512 |
→ |
|
hex, default |
defined in app-level |
PBAP#
PBAP Comparison#
The following compares the PBAP differences between EdgeFast Open Bluetooth Host stack and EdgeFast Bluetooth PAL.
1. Architectural Differences (Core Changes)#
Dimension |
EdgeFast Open |
EdgeFast Bluetooth PAL |
Migration impact |
|---|---|---|---|
API abstraction |
PDU-/protocol-driven: most APIs take only |
Parameter-driven: function parameters directly carry |
Old: “pass parameters”; New: “build PDUs + add headers + parse buffers”. |
Header construction |
Application builds 100% of headers (Name/Type/AppParams/SRMP/Body/EndBody/auth, etc.) |
OBEX/PBAP headers are built internally based on API parameters; application mainly reserves headroom |
Main migration task: map old API parameters into a sequence of |
Connection responsibility |
Clearly split: transport bearer (RFCOMM/L2CAP) + OBEX CONNECT/DISCONNECT/ABORT PDU handling |
Connect APIs often bundle object creation, authentication, feature info, and transport setup |
Connection procedure becomes longer (one or two extra steps). |
Segmentation/chunking |
Final-bit / End-of-Body and segmentation are decided by the application’s PDU construction; PBAP APIs only transmit |
Driven by |
Requires redesign of “first packet / subsequent packet / last packet” strategy. |
SRMP (“wait”) |
Not a parameter anymore; application adds SRMP header |
|
|
Name/path |
Not a function parameter; carried in the OBEX Name header inside |
|
|
Authentication |
New provides nonce/digest/verify helpers; auth headers are added by the application |
Old callback includes |
Auth shifts from “configuration/struct callback” to “parse challenge + compute + add headers”. |
2. PCE (Client) API Alignment Differences (Function Level)#
Function |
EdgeFast Open API ( |
EdgeFast Bluetooth PAL API ( |
Key differences (incl. migration notes) |
|---|---|---|---|
Callback registration / init |
(No equivalent global |
|
Old: global callback registration; New: more instance-oriented, |
RFCOMM connect (SCN/channel) |
|
|
Old creates/returns the PCE object and takes |
L2CAP connect (PSM) |
|
|
Same pattern as RFCOMM connect. |
Disconnect (high-level) |
|
|
New requires application-provided |
Disconnect (transport bearer) |
|
(No dedicated API) |
New separates bearer teardown from OBEX DISCONNECT PDU. |
OBEX CONNECT (PBAP session) |
|
(Typically implicit in old connect flow) |
Application must construct the OBEX CONNECT PDU and headers via |
ABORT |
|
|
Application constructs ABORT PDU headers. |
Pull Phonebook |
|
|
|
SetPath |
|
|
Old passes |
Pull vCard Listing |
|
|
Same as Pull Phonebook: parameters → headers. |
Pull vCard Entry |
|
|
Same as above. |
Get max packet length |
(No same-name API found) |
|
Old provides direct query; New may obtain it via OBEX CONNECT negotiation (e.g., MOPl) or OBEX layer APIs. |
Helper: parse App Params |
(No same-name macro in |
|
New encourages direct OBEX/AppParams header manipulation/parsing by the application. |
Helper: get Body |
(No same-name macro in |
|
New callbacks deliver |
3. PSE (Server) API Alignment Differences (Function Level)#
Function |
EdgeFast Open API ( |
EdgeFast Bluetooth PAL API ( |
Key differences (incl. migration notes) |
|---|---|---|---|
Callback registration / init |
|
|
New requires a PSE instance, i.e., a more instance-based server model. |
Server bearer registration |
|
(Not provided in old header) |
New adds explicit RFCOMM/L2CAP server registration and accept model. |
Disconnect |
|
|
New is response/PDU oriented; application builds the response PDU and headers. |
CONNECT/ABORT response |
|
(More abstracted in old version) |
New requires explicit response PDU construction and transmission. |
Pull phonebook response |
|
|
|
SetPath response |
|
|
New requires a response |
Pull vCard listing response |
|
|
|
Pull vCard entry response |
|
|
Same as above. |
Get max packet length |
(No same-name API found) |
|
New may rely on OBEX/GOEP layer APIs. |
Get peer supported features |
(No same-name API found) |
|
New may expose this via negotiation/callback/buffer parsing or internal state. |
5. Callback Model Differences: From “High-level Events” to “Protocol Events + Buffer-driven”#
5.1 PCE callbacks#
Aspect |
EdgeFast Open |
EdgeFast Bluetooth PAL |
Migration impact |
|---|---|---|---|
Bearer connected |
|
|
New distinguishes bearer type and provides |
Bearer disconnected |
|
|
The disconnect reason/result is more reflected in protocol |
OBEX CONNECT result |
|
(Not typically exposed explicitly) |
New exposes negotiated parameters and delivers the raw buffer. |
pull/set/abort callbacks |
uniformly |
result + buf (some callbacks without buf) |
Application must parse OBEX headers/body from |
Auth info retrieval |
(Not present) |
|
New expects application-driven OBEX auth header handling (parse challenge, compute, add response). |
5.2 PSE callbacks#
Aspect |
EdgeFast Open |
EdgeFast Bluetooth PAL |
Migration impact |
|---|---|---|---|
Pull request callback params |
|
|
New requires parsing Name/Type/AppParams/SRMP/segmentation from |
SetPath request |
|
|
Flags are standardized; folder name is parsed from Name header in |
connect/disconnect/abort |
New provides buffer-level callbacks and explicit response APIs |
More abstracted |
New behaves like an OBEX server role: app participates in handshake and PDU construction. |
6. Migration Mapping: “Old parameters” → “New headers (obex_add_header_xxx)”#
Old parameter/concept |
EdgeFast Open |
EdgeFast Bluetooth PAL |
Application action (conceptual) |
|---|---|---|---|
|
Not a function parameter anymore |
API parameter |
Add/parse OBEX Name header in request/response PDUs. |
|
|
Often internal in old stack |
Add OBEX Type header ( |
App Parameters (filter/format/order/search, etc.) |
enums in |
via |
Encode/decode PBAP AppParams TLVs inside the OBEX App Parameters header. |
|
Not present as parameter |
|
Add/parse SRMP header to control Server Response Mode Parameter. |
|
Not present as parameter |
|
Express segmentation via PDU construction (Final-bit, Body vs End-of-Body, multi-PDU flow). |
|
unified |
|
Choose OBEX response code based on whether more PDUs follow; build Body/End-of-Body accordingly. |
|
nonce/digest/verify helpers |
connect parameter or |
Build OBEX auth headers in PDUs; compute/verify using provided helpers. |
|
not in prototypes |
connect parameter or explicit getter |
Likely obtained via negotiation/SDP/buffer parsing/internal state; avoid injecting via connect signature. |
7. Newly Added / Enhanced APIs & Capabilities in EdgeFast Open#
Item |
EdgeFast Open API/structure |
Value |
|---|---|---|
PDU allocation helper |
|
Provides a PDU container to be populated by the application via |
PSE bearer server registration |
|
More complete server integration model (multi-connection / custom accept). |
OBEX-level response APIs (PSE) |
|
Application can explicitly control OBEX response codes and attached headers. |
Authentication utilities |
|
Facilitates application-implemented OBEX/PBAP authentication headers and validation. |
8. Migration Recommendations (High-level)#
Refactor connection sequencing (PCE):
Establish bearer first:
bt_pbap_pce_rfcomm_connectorbt_pbap_pce_l2cap_connectThen build the OBEX CONNECT PDU (application uses
obex_add_header_xxx()) and callbt_pbap_pce_connect
Change request transmission to “build PDU first, then call PBAP API”:
bt_pbap_pce_create_pdu()→obex_add_header_xxx()(Name/Type/AppParams/SRMP/Body…) →bt_pbap_pce_pull_*()
Change response sending (PSE) to “application fully owns headers/body/end-of-body”:
Request callbacks deliver only
buf; parse Name/Type/AppParamsBuild response
buf: add Body/End-of-Body and required headers viaobex_add_header_xxx()→ callbt_pbap_pse_*_rsp()
Segmentation/CONTINUE strategy migration:
Old:
flag + wait + resultNew: application decides whether to include End-of-Body, whether more PDUs follow, and selects
rsp_code(CONTINUE/SUCCESS)
Authentication migration:
Old:
get_auth_info+struct bt_pbap_authNew: application parses OBEX auth challenge headers, computes digests via
bt_pbap_calculate_*, and adds auth response/challenge headers viaobex_add_header_xxx()
Parent topic:Compare with EdgeFast Bluetooth PAL
Parent topic:Overview