shared/bap: Make bt_bap_select match the channel map

bt_bap_pac may actually map to multiple PAC records and each may have a
different channel count that needs to be matched separately, for
instance when trying with EarFun Air Pro:

< ACL Data TX: Handle 2048 flags 0x00 dlen 85
      ATT: Write Command (0x52) len 80
        Handle: 0x0098 Type: ASE Control Point (0x2bc6)
          Data: 010405020206000000000a020103020201030428000602020600000
	  0000a0201030202010304280001020206000000000a020103020201030428
	  0002020206000000000a02010302020103042800
            Opcode: Codec Configuration (0x01)
            Number of ASE(s): 4
            ASE: #0
            ASE ID: 0x05
            Target Latency: Balance Latency/Reliability (0x02)
            PHY: 0x02
            LE 2M PHY (0x02)
            Codec: LC3 (0x06)
            Codec Specific Configuration: #0: len 0x02 type 0x01
              Sampling Frequency: 16 Khz (0x03)
            Codec Specific Configuration: #1: len 0x02 type 0x02
              Frame Duration: 10 ms (0x01)
            Codec Specific Configuration: #2: len 0x03 type 0x04
              Frame Length: 40 (0x0028)
            ASE: #1
            ASE ID: 0x06
            Target Latency: Balance Latency/Reliability (0x02)
            PHY: 0x02
            LE 2M PHY (0x02)
            Codec: LC3 (0x06)
            Codec Specific Configuration: #0: len 0x02 type 0x01
              Sampling Frequency: 16 Khz (0x03)
            Codec Specific Configuration: #1: len 0x02 type 0x02
              Frame Duration: 10 ms (0x01)
            Codec Specific Configuration: #2: len 0x03 type 0x04
              Frame Length: 40 (0x0028)
            ASE: #2
            ASE ID: 0x01
            Target Latency: Balance Latency/Reliability (0x02)
            PHY: 0x02
            LE 2M PHY (0x02)
            Codec: LC3 (0x06)
            Codec Specific Configuration: #0: len 0x02 type 0x01
              Sampling Frequency: 16 Khz (0x03)
            Codec Specific Configuration: #1: len 0x02 type 0x02
              Frame Duration: 10 ms (0x01)
            Codec Specific Configuration: #2: len 0x03 type 0x04
              Frame Length: 40 (0x0028)
            ASE: #3
            ASE ID: 0x02
            Target Latency: Balance Latency/Reliability (0x02)
            PHY: 0x02
            LE 2M PHY (0x02)
            Codec: LC3 (0x06)
            Codec Specific Configuration: #0: len 0x02 type 0x01
              Sampling Frequency: 16 Khz (0x03)
            Codec Specific Configuration: #1: len 0x02 type 0x02
              Frame Duration: 10 ms (0x01)
            Codec Specific Configuration: #2: len 0x03 type 0x04
              Frame Length: 40 (0x0028)

Fixes: https://github.com/bluez/bluez/issues/612
This commit is contained in:
Luiz Augusto von Dentz 2023-11-29 10:23:32 -05:00
parent 511d58b06c
commit e7e8b2a3d7
5 changed files with 132 additions and 13 deletions

View File

@ -1290,10 +1290,8 @@ static bool pac_found(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
}
/* TODO: Cache LRU? */
if (btd_service_is_initiator(service)) {
if (!bt_bap_select(lpac, rpac, select_cb, ep))
ep->data->selecting++;
}
if (btd_service_is_initiator(service))
bt_bap_select(lpac, rpac, &ep->data->selecting, select_cb, ep);
return true;
}

View File

@ -185,6 +185,7 @@ struct bt_bap_pac {
struct bt_bap_pac_qos qos;
struct iovec *data;
struct iovec *metadata;
struct queue *chan_map;
struct bt_bap_pac_ops *ops;
void *user_data;
};
@ -2417,6 +2418,33 @@ static void *ltv_merge(struct iovec *data, struct iovec *cont)
return iov_append(data, cont->iov_len, cont->iov_base);
}
static void bap_pac_foreach_channel(size_t i, uint8_t l, uint8_t t, uint8_t *v,
void *user_data)
{
struct bt_bap_pac *pac = user_data;
if (!v)
return;
if (!pac->chan_map)
pac->chan_map = queue_new();
printf("PAC %p chan_map 0x%02x\n", pac, *v);
queue_push_tail(pac->chan_map, UINT_TO_PTR(*v));
}
static void bap_pac_update_chan_map(struct bt_bap_pac *pac, struct iovec *data)
{
uint8_t type = 0x03;
if (!data)
return;
util_ltv_foreach(data->iov_base, data->iov_len, &type,
bap_pac_foreach_channel, pac);
}
static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data,
struct iovec *metadata)
{
@ -2426,6 +2454,9 @@ static void bap_pac_merge(struct bt_bap_pac *pac, struct iovec *data,
else
pac->data = util_iov_dup(data, 1);
/* Update channel map */
bap_pac_update_chan_map(pac, data);
/* Merge metadata into existing record */
if (pac->metadata)
ltv_merge(pac->metadata, metadata);
@ -2448,10 +2479,9 @@ static struct bt_bap_pac *bap_pac_new(struct bt_bap_db *bdb, const char *name,
pac->type = type;
if (codec)
pac->codec = *codec;
if (data)
pac->data = util_iov_dup(data, 1);
if (metadata)
pac->metadata = util_iov_dup(metadata, 1);
bap_pac_merge(pac, data, metadata);
if (qos)
pac->qos = *qos;
@ -2465,6 +2495,7 @@ static void bap_pac_free(void *data)
free(pac->name);
util_iov_free(pac->metadata, 1);
util_iov_free(pac->data, 1);
queue_destroy(pac->chan_map, NULL);
free(pac);
}
@ -4505,7 +4536,16 @@ static bool find_ep_pacs(const void *data, const void *user_data)
if (ep->stream->lpac != match->lpac)
return false;
return ep->stream->rpac == match->rpac;
if (ep->stream->rpac != match->rpac)
return false;
switch (ep->state) {
case BT_BAP_STREAM_STATE_CONFIG:
case BT_BAP_STREAM_STATE_QOS:
return true;
}
return false;
}
static struct bt_bap_req *bap_req_new(struct bt_bap_stream *stream,
@ -4626,16 +4666,47 @@ static bool match_pac(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
}
int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
bt_bap_pac_select_t func, void *user_data)
int *count, bt_bap_pac_select_t func,
void *user_data)
{
const struct queue_entry *lchan, *rchan;
if (!lpac || !rpac || !func)
return -EINVAL;
if (!lpac->ops || !lpac->ops->select)
return -EOPNOTSUPP;
lpac->ops->select(lpac, rpac, &rpac->qos,
func, user_data, lpac->user_data);
for (lchan = queue_get_entries(lpac->chan_map); lchan;
lchan = lchan->next) {
uint8_t lmap = PTR_TO_UINT(lchan->data);
for (rchan = queue_get_entries(rpac->chan_map); rchan;
rchan = rchan->next) {
uint8_t rmap = PTR_TO_UINT(rchan->data);
printf("lmap 0x%02x rmap 0x%02x\n", lmap, rmap);
/* Try matching the channel mapping */
if (lmap & rmap) {
lpac->ops->select(lpac, rpac, &rpac->qos,
func, user_data,
lpac->user_data);
if (count)
(*count)++;
/* Check if there are any channels left */
lmap &= ~(lmap & rmap);
if (!lmap)
break;
/* Check if device require AC*(i) settings */
if (rmap == 0x01)
lmap = lmap >> 1;
} else
break;
}
}
return 0;
}

View File

@ -234,7 +234,8 @@ void *bt_bap_pac_get_user_data(struct bt_bap_pac *pac);
/* Stream related functions */
int bt_bap_select(struct bt_bap_pac *lpac, struct bt_bap_pac *rpac,
bt_bap_pac_select_t func, void *user_data);
int *count, bt_bap_pac_select_t func,
void *user_data);
struct bt_bap_stream *bt_bap_stream_new(struct bt_bap *bap,
struct bt_bap_pac *lpac,

View File

@ -175,6 +175,49 @@ ltv_debugger(const struct util_ltv_debugger *debugger, size_t num, uint8_t type)
return NULL;
}
/* Helper to itertate over LTV entries */
bool util_ltv_foreach(const uint8_t *data, uint8_t len, uint8_t *type,
util_ltv_func_t func, void *user_data)
{
struct iovec iov;
int i;
if (!func)
return false;
iov.iov_base = (void *) data;
iov.iov_len = len;
for (i = 0; iov.iov_len; i++) {
uint8_t l, t, *v;
if (!util_iov_pull_u8(&iov, &l))
return false;
if (!l) {
func(i, l, 0, NULL, user_data);
continue;
}
if (!util_iov_pull_u8(&iov, &t))
return false;
l--;
if (l) {
v = util_iov_pull_mem(&iov, l);
if (!v)
return false;
} else
v = NULL;
if (!type || *type == t)
func(i, l, t, v, user_data);
}
return true;
}
/* Helper to print debug information of LTV entries */
bool util_debug_ltv(const uint8_t *data, uint8_t len,
const struct util_ltv_debugger *debugger, size_t num,

View File

@ -138,6 +138,12 @@ bool util_debug_ltv(const uint8_t *data, uint8_t len,
const struct util_ltv_debugger *debugger, size_t num,
util_debug_func_t function, void *user_data);
typedef void (*util_ltv_func_t)(size_t i, uint8_t l, uint8_t t, uint8_t *v,
void *user_data);
bool util_ltv_foreach(const uint8_t *data, uint8_t len, uint8_t *type,
util_ltv_func_t func, void *user_data);
unsigned char util_get_dt(const char *parent, const char *name);
ssize_t util_getrandom(void *buf, size_t buflen, unsigned int flags);