Skip to content

Commit aaef7eb

Browse files
lylezhu2012kartben
authored andcommitted
Bluetooth: Classic: HGP_AG: change get_ongoing_call() to async mode
Change the callback `get_ongoing_call()` of the AG from synchronous to asynchronous mode. It will help to avoid the Bluetooth host stack be blocked in the context of callback `get_ongoing_call()`. Add a function `bt_hfp_ag_ongoing_calls()` to set the ongoing calls and reply the AT command `AT+CIND?` after the callback `get_ongoing_call()` has been notified. Add a delayable worker to avoid the AT command `AT+CIND?` never being replied. After the time exceeds @kconfig{CONFIG_BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT}, the response of the AT command `AT+CIND?` will be replied. Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
1 parent d6dc7fb commit aaef7eb

File tree

5 files changed

+220
-93
lines changed

5 files changed

+220
-93
lines changed

include/zephyr/bluetooth/classic/hfp_ag.h

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,19 +149,23 @@ struct bt_hfp_ag_cb {
149149

150150
/** Get ongoing call information Callback
151151
*
152-
* If this callback is provided it will be called whenever the response
153-
* of the AT command `AT+CIND=?` from HF has been sent. It is used to get all
154-
* ongoing calls one bye one from the upper layer.
155-
* Then set the `Call`, `Call Setup`, and `Held Call` indicators and report the
156-
* values int the response of AT command `AT+CIND?`. Then report all ongoing
157-
* calls in the `+CLCC` response.
152+
* If this callback is provided it will be called whenever the AT command `AT+CIND?` is
153+
* received from HF has been sent.
154+
* After the callback notified, the ongoing calls should be set via function
155+
* `bt_hfp_ag_ongoing_calls()` within the timeout
156+
* @kconfig{CONFIG_BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT}.
158157
*
159158
* @param ag HFP AG object.
160-
* @param call Pointer to store the ongoing call information.
161159
*
162-
* @return 0 in case of success and will get next one or negative value in case of error.
160+
* @note The AG is in SLC establishment phase. The AG callback `connected()` is not
161+
* notified at this time.
162+
*
163+
* @return 0 in case of success. The response `+CIND` will be sent after the function
164+
* `bt_hfp_ag_ongoing_calls()` called or after the time exceeds
165+
* @kconfig{CONFIG_BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT}. Or negative value in case
166+
* of error. The response `+CIND` will be replied immediately.
163167
*/
164-
int (*get_ongoing_call)(struct bt_hfp_ag *ag, struct bt_hfp_ag_ongoing_call *call);
168+
int (*get_ongoing_call)(struct bt_hfp_ag *ag);
165169

166170
/** HF memory dialing request Callback
167171
*
@@ -838,6 +842,19 @@ int bt_hfp_ag_service_availability(struct bt_hfp_ag *ag, bool available);
838842
*/
839843
int bt_hfp_ag_hf_indicator(struct bt_hfp_ag *ag, enum hfp_ag_hf_indicators indicator, bool enable);
840844

845+
/** @brief Set the ongoing calls
846+
*
847+
* It is used to set the ongoing calls when AT command `AT+CIND?` is received.
848+
*
849+
* @param ag HFP AG object.
850+
* @param calls Ongoing calls.
851+
* @param count Ongoing call count.
852+
*
853+
* @return 0 in case of success or negative value in case of error.
854+
*/
855+
int bt_hfp_ag_ongoing_calls(struct bt_hfp_ag *ag, struct bt_hfp_ag_ongoing_call *calls,
856+
size_t count);
857+
841858
#ifdef __cplusplus
842859
}
843860
#endif

subsys/bluetooth/host/classic/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,13 @@ config BT_HFP_AG_REJECT_CALL
419419
help
420420
This option enables ability to reject a call for HFP AG
421421

422+
config BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT
423+
int "Timeout after the get ongoing calls callback notified (milliseconds) [EXPERIMENTAL]"
424+
default 10000
425+
range 1000 10000
426+
help
427+
This option sets the timeout after the get ongoing calls callback notified
428+
422429
endif # BT_HFP_AG
423430

424431
config BT_AVDTP

subsys/bluetooth/host/classic/hfp_ag.c

Lines changed: 145 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,77 +1016,28 @@ static int bt_hfp_ag_bac_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
10161016
return 0;
10171017
}
10181018

1019-
static void bt_hfp_ag_get_ongoing_calls(struct bt_hfp_ag *ag)
1019+
static int bt_hfp_ag_get_ongoing_calls(struct bt_hfp_ag *ag)
10201020
{
1021-
struct bt_hfp_ag_ongoing_call *call;
10221021
int err;
1023-
bool valid;
1024-
size_t len;
10251022

10261023
ag->ongoing_call_count = 0;
10271024

10281025
if ((bt_ag == NULL) || (bt_ag->get_ongoing_call == NULL)) {
10291026
LOG_DBG("No ongoing call retrieval method available");
1030-
return;
1027+
return -EINVAL;
10311028
}
10321029

1033-
do {
1034-
call = &ag->ongoing_calls[ag->ongoing_call_count];
1035-
memset(call, 0, sizeof(*call));
1036-
1037-
valid = true;
1038-
1039-
err = bt_ag->get_ongoing_call(ag, call);
1040-
if (err != 0) {
1041-
LOG_DBG("No ongoing call retrieved");
1042-
break;
1043-
}
1044-
1045-
len = strlen(call->number);
1046-
if ((len == 0) || (len >= ARRAY_SIZE(call->number))) {
1047-
LOG_WRN("Invalid call number");
1048-
break;
1049-
}
1050-
1051-
switch (call->status) {
1052-
case BT_HFP_AG_CALL_STATUS_DIALING:
1053-
case BT_HFP_AG_CALL_STATUS_ALERTING:
1054-
if (call->dir == BT_HFP_AG_CALL_DIR_INCOMING) {
1055-
LOG_ERR("Dialing call cannot be incoming");
1056-
valid = false;
1057-
}
1058-
break;
1059-
case BT_HFP_AG_CALL_STATUS_INCOMING:
1060-
case BT_HFP_AG_CALL_STATUS_WAITING:
1061-
case BT_HFP_AG_CALL_STATUS_INCOMING_HELD:
1062-
if (call->dir == BT_HFP_AG_CALL_DIR_OUTGOING) {
1063-
LOG_ERR("Incoming call cannot be outgoing");
1064-
valid = false;
1065-
}
1066-
break;
1067-
default:
1068-
break;
1069-
}
1070-
1071-
if (!valid) {
1072-
continue;
1073-
}
1074-
1075-
ag->ongoing_call_count++;
1076-
} while (ag->ongoing_call_count < ARRAY_SIZE(ag->ongoing_calls));
1030+
if (ag->state == BT_HFP_CONNECTED) {
1031+
LOG_ERR("Only works during SLC establishment phase");
1032+
return -EINVAL;
1033+
}
10771034

1078-
if (ag->ongoing_call_count > 1) {
1079-
switch (ag->ongoing_calls[0].status) {
1080-
case BT_HFP_AG_CALL_STATUS_ACTIVE:
1081-
case BT_HFP_AG_CALL_STATUS_HELD:
1082-
case BT_HFP_AG_CALL_STATUS_INCOMING_HELD:
1083-
break;
1084-
default:
1085-
LOG_ERR("Unexpected call status for multiple calls");
1086-
ag->ongoing_call_count = 1;
1087-
break;
1088-
}
1035+
err = bt_ag->get_ongoing_call(ag);
1036+
if (err != 0) {
1037+
LOG_DBG("No ongoing call retrieved");
10891038
}
1039+
1040+
return err;
10901041
}
10911042

10921043
static int bt_hfp_ag_notify_cind_value(struct bt_hfp_ag *ag)
@@ -1195,10 +1146,16 @@ static int bt_hfp_ag_cind_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
11951146
ag_ind[BT_HFP_AG_ROAM_IND].max, ag_ind[BT_HFP_AG_BATTERY_IND].name,
11961147
ag_ind[BT_HFP_AG_BATTERY_IND].min, ag_ind[BT_HFP_AG_BATTERY_IND].connector,
11971148
ag_ind[BT_HFP_AG_BATTERY_IND].max);
1198-
1199-
bt_hfp_ag_get_ongoing_calls(ag);
12001149
} else {
1201-
err = bt_hfp_ag_notify_cind_value(ag);
1150+
err = bt_hfp_ag_get_ongoing_calls(ag);
1151+
if (err != 0) {
1152+
err = bt_hfp_ag_notify_cind_value(ag);
1153+
} else {
1154+
err = -EINPROGRESS;
1155+
atomic_set_bit(ag->flags, BT_HGP_AG_ONGOING_CALLS);
1156+
k_work_reschedule(&ag->ongoing_call_work,
1157+
K_MSEC(CONFIG_BT_HFP_AG_GET_ONGOING_CALL_TIMEOUT));
1158+
}
12021159
}
12031160

12041161
return err;
@@ -3534,6 +3491,11 @@ static void hfp_ag_recv(struct bt_rfcomm_dlc *dlc, struct net_buf *buf)
35343491
}
35353492
}
35363493

3494+
if (err == -EINPROGRESS) {
3495+
LOG_DBG("OK code will be replied later");
3496+
return;
3497+
}
3498+
35373499
if ((err != 0) && atomic_test_bit(ag->flags, BT_HFP_AG_CMEE_ENABLE)) {
35383500
cme_err = bt_hfp_ag_get_cme_err(err);
35393501
err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+CME ERROR:%d\r\n", (uint32_t)cme_err);
@@ -3784,6 +3746,29 @@ static void bt_ag_ringing_work(struct k_work *work)
37843746
(void)hfp_ag_next_step(call->ag, bt_ag_ringing_work_cb, call);
37853747
}
37863748

3749+
static void bt_ag_send_ok_code(struct bt_hfp_ag *ag)
3750+
{
3751+
if (hfp_ag_send_data(ag, NULL, NULL, "\r\nOK\r\n") != 0) {
3752+
LOG_ERR("Failed to send OK code");
3753+
}
3754+
}
3755+
3756+
static void bt_ag_ongoing_call_work(struct k_work *work)
3757+
{
3758+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
3759+
struct bt_hfp_ag *ag = CONTAINER_OF(dwork, struct bt_hfp_ag, ongoing_call_work);
3760+
3761+
LOG_DBG("");
3762+
3763+
if (!atomic_test_and_clear_bit(ag->flags, BT_HGP_AG_ONGOING_CALLS)) {
3764+
return;
3765+
}
3766+
3767+
ag->ongoing_call_count = 0;
3768+
bt_hfp_ag_notify_cind_value(ag);
3769+
bt_ag_send_ok_code(ag);
3770+
}
3771+
37873772
static K_KERNEL_STACK_MEMBER(ag_thread_stack, CONFIG_BT_HFP_AG_THREAD_STACK_SIZE);
37883773

37893774
static struct bt_hfp_ag *hfp_ag_create(struct bt_conn *conn)
@@ -3883,6 +3868,8 @@ static struct bt_hfp_ag *hfp_ag_create(struct bt_conn *conn)
38833868

38843869
/* Init delay work */
38853870
k_work_init_delayable(&ag->tx_work, bt_ag_tx_work);
3871+
/* Init delay work */
3872+
k_work_init_delayable(&ag->ongoing_call_work, bt_ag_ongoing_call_work);
38863873

38873874
return ag;
38883875
}
@@ -5208,3 +5195,98 @@ int bt_hfp_ag_hf_indicator(struct bt_hfp_ag *ag, enum hfp_ag_hf_indicators indic
52085195
return -ENOTSUP;
52095196
#endif /* CONFIG_BT_HFP_HF_HF_INDICATORS */
52105197
}
5198+
5199+
int bt_hfp_ag_ongoing_calls(struct bt_hfp_ag *ag, struct bt_hfp_ag_ongoing_call *calls,
5200+
size_t count)
5201+
{
5202+
struct bt_hfp_ag_ongoing_call *call;
5203+
bool valid;
5204+
size_t len;
5205+
int err = -EINVAL;
5206+
5207+
LOG_DBG("");
5208+
5209+
if (ag == NULL) {
5210+
LOG_ERR("Invalid AG object");
5211+
return -EINVAL;
5212+
}
5213+
5214+
if (count > ARRAY_SIZE(ag->ongoing_calls)) {
5215+
LOG_ERR("Out of buffer (%u > %u)", count, ARRAY_SIZE(ag->ongoing_calls));
5216+
return -ENOMEM;
5217+
}
5218+
5219+
if (!atomic_test_and_clear_bit(ag->flags, BT_HGP_AG_ONGOING_CALLS)) {
5220+
LOG_ERR("Cannot set ongoing calls");
5221+
return -ESRCH;
5222+
}
5223+
5224+
for (ag->ongoing_call_count = 0; ag->ongoing_call_count < count; ag->ongoing_call_count++) {
5225+
call = &calls[ag->ongoing_call_count];
5226+
valid = true;
5227+
5228+
len = strlen(call->number);
5229+
if ((len == 0) || (len >= ARRAY_SIZE(call->number))) {
5230+
LOG_WRN("Invalid call number");
5231+
/* Clear all calls */
5232+
ag->ongoing_call_count = 0;
5233+
goto failed;
5234+
}
5235+
5236+
switch (call->status) {
5237+
case BT_HFP_AG_CALL_STATUS_DIALING:
5238+
case BT_HFP_AG_CALL_STATUS_ALERTING:
5239+
if (call->dir == BT_HFP_AG_CALL_DIR_INCOMING) {
5240+
LOG_ERR("Dialing call cannot be incoming");
5241+
valid = false;
5242+
}
5243+
break;
5244+
case BT_HFP_AG_CALL_STATUS_INCOMING:
5245+
case BT_HFP_AG_CALL_STATUS_WAITING:
5246+
case BT_HFP_AG_CALL_STATUS_INCOMING_HELD:
5247+
if (call->dir == BT_HFP_AG_CALL_DIR_OUTGOING) {
5248+
LOG_ERR("Incoming call cannot be outgoing");
5249+
valid = false;
5250+
}
5251+
break;
5252+
case BT_HFP_AG_CALL_STATUS_ACTIVE:
5253+
case BT_HFP_AG_CALL_STATUS_HELD:
5254+
break;
5255+
default:
5256+
LOG_ERR("Invalid status");
5257+
valid = false;
5258+
break;
5259+
}
5260+
5261+
if (!valid) {
5262+
/* Clear all calls */
5263+
ag->ongoing_call_count = 0;
5264+
goto failed;
5265+
}
5266+
5267+
memcpy(&ag->ongoing_calls[ag->ongoing_call_count], call,
5268+
sizeof(ag->ongoing_calls[ag->ongoing_call_count]));
5269+
}
5270+
5271+
if (ag->ongoing_call_count > 1) {
5272+
switch (ag->ongoing_calls[0].status) {
5273+
case BT_HFP_AG_CALL_STATUS_ACTIVE:
5274+
case BT_HFP_AG_CALL_STATUS_HELD:
5275+
case BT_HFP_AG_CALL_STATUS_INCOMING_HELD:
5276+
break;
5277+
default:
5278+
LOG_ERR("Unexpected call status for multiple calls");
5279+
/* Clear all calls */
5280+
ag->ongoing_call_count = 0;
5281+
goto failed;
5282+
}
5283+
}
5284+
5285+
err = 0;
5286+
5287+
failed:
5288+
k_work_cancel_delayable(&ag->ongoing_call_work);
5289+
bt_hfp_ag_notify_cind_value(ag);
5290+
bt_ag_send_ok_code(ag);
5291+
return err;
5292+
}

subsys/bluetooth/host/classic/hfp_ag_internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ enum {
136136
BT_HFP_AG_CREATING_SCO, /* SCO is creating */
137137
BT_HFP_AG_VRE_ACTIVATE, /* VRE is activated */
138138
BT_HFP_AG_VRE_R2A, /* HF is ready to accept audio */
139+
BT_HGP_AG_ONGOING_CALLS, /* Waiting ongoing calls */
139140

140141
/* Total number of flags - must be at the end of the enum */
141142
BT_HFP_AG_NUM_FLAGS,
@@ -239,6 +240,8 @@ struct bt_hfp_ag {
239240
/* ongoing calls */
240241
struct bt_hfp_ag_ongoing_call ongoing_calls[CONFIG_BT_HFP_AG_MAX_CALLS];
241242
size_t ongoing_call_count;
243+
/* ongoing calls work */
244+
struct k_work_delayable ongoing_call_work;
242245

243246
/* last dialing number and type */
244247
char last_number[CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN + 1];

0 commit comments

Comments
 (0)