Bluetooth: HFP_AG: Protect the consistency of AG state/value

Use hfp_ag_lock/hfp_ag_unlock to protect the consistency
of AG state or value.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
This commit is contained in:
Lyle Zhu 2024-04-25 14:41:37 +08:00 committed by Alberto Escolar
commit fa6df6a51a

View file

@ -135,7 +135,9 @@ static void bt_hfp_ag_set_state(struct bt_hfp_ag *ag, bt_hfp_state_t state)
{ {
LOG_DBG("update state %p, old %d -> new %d", ag, ag->state, state); LOG_DBG("update state %p, old %d -> new %d", ag, ag->state, state);
hfp_ag_lock(ag);
ag->state = state; ag->state = state;
hfp_ag_unlock(ag);
switch (state) { switch (state) {
case BT_HFP_DISCONNECTED: case BT_HFP_DISCONNECTED:
@ -162,9 +164,14 @@ static void bt_hfp_ag_set_state(struct bt_hfp_ag *ag, bt_hfp_state_t state)
static void bt_hfp_ag_set_call_state(struct bt_hfp_ag *ag, bt_hfp_call_state_t call_state) static void bt_hfp_ag_set_call_state(struct bt_hfp_ag *ag, bt_hfp_call_state_t call_state)
{ {
bt_hfp_state_t state;
LOG_DBG("update call state %p, old %d -> new %d", ag, ag->call_state, call_state); LOG_DBG("update call state %p, old %d -> new %d", ag, ag->call_state, call_state);
hfp_ag_lock(ag);
ag->call_state = call_state; ag->call_state = call_state;
state = ag->state;
hfp_ag_unlock(ag);
switch (call_state) { switch (call_state) {
case BT_HFP_CALL_TERMINATE: case BT_HFP_CALL_TERMINATE:
@ -192,7 +199,7 @@ static void bt_hfp_ag_set_call_state(struct bt_hfp_ag *ag, bt_hfp_call_state_t c
break; break;
} }
if (ag->state == BT_HFP_DISCONNECTING) { if (state == BT_HFP_DISCONNECTING) {
int err = bt_rfcomm_dlc_disconnect(&ag->rfcomm_dlc); int err = bt_rfcomm_dlc_disconnect(&ag->rfcomm_dlc);
if (err) { if (err) {
@ -387,7 +394,9 @@ static int bt_hfp_ag_brsf_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
ag->hf_features = hf_features; ag->hf_features = hf_features;
hfp_ag_unlock(ag);
return hfp_ag_send_data(ag, NULL, NULL, "\r\n+BRSF:%d\r\n", ag->ag_features); return hfp_ag_send_data(ag, NULL, NULL, "\r\n+BRSF:%d\r\n", ag->ag_features);
} }
@ -402,10 +411,13 @@ static int bt_hfp_ag_bac_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
if (!(ag->hf_features & BT_HFP_HF_FEATURE_CODEC_NEG) || if (!(ag->hf_features & BT_HFP_HF_FEATURE_CODEC_NEG) ||
!(ag->ag_features & BT_HFP_AG_FEATURE_CODEC_NEG)) { !(ag->ag_features & BT_HFP_AG_FEATURE_CODEC_NEG)) {
hfp_ag_unlock(ag);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
hfp_ag_unlock(ag);
while (err == 0) { while (err == 0) {
err = get_number(buf, &codec); err = get_number(buf, &codec);
@ -424,7 +436,9 @@ static int bt_hfp_ag_bac_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
} }
} }
hfp_ag_lock(ag);
ag->hf_codec_ids = codec_ids; ag->hf_codec_ids = codec_ids;
hfp_ag_unlock(ag);
if (bt_ag && bt_ag->codec) { if (bt_ag && bt_ag->codec) {
bt_ag->codec(ag, ag->hf_codec_ids); bt_ag->codec(ag, ag->hf_codec_ids);
@ -487,7 +501,13 @@ static int bt_hfp_ag_cind_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
static void bt_hfp_ag_set_in_band_ring(struct bt_hfp_ag *ag, void *user_data) static void bt_hfp_ag_set_in_band_ring(struct bt_hfp_ag *ag, void *user_data)
{ {
if ((ag->ag_features & BT_HFP_AG_FEATURE_INBAND_RINGTONE) != 0) { bool is_inband_ringtone;
hfp_ag_lock(ag);
is_inband_ringtone = (ag->ag_features & BT_HFP_AG_FEATURE_INBAND_RINGTONE) ? true : false;
hfp_ag_unlock(ag);
if (is_inband_ringtone) {
int err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BSIR:1\r\n"); int err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BSIR:1\r\n");
atomic_set_bit_to(ag->flags, BT_HFP_AG_INBAND_RING, err == 0); atomic_set_bit_to(ag->flags, BT_HFP_AG_INBAND_RING, err == 0);
@ -555,17 +575,22 @@ static int bt_hfp_ag_bind_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
char *data; char *data;
uint32_t len; uint32_t len;
hfp_ag_lock(ag);
if (!((ag->ag_features & BT_HFP_AG_FEATURE_HF_IND) && if (!((ag->ag_features & BT_HFP_AG_FEATURE_HF_IND) &&
(ag->hf_features & BT_HFP_HF_FEATURE_HF_IND))) { (ag->hf_features & BT_HFP_HF_FEATURE_HF_IND))) {
hfp_ag_unlock(ag);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
hfp_ag_unlock(ag);
if (is_char(buf, '?')) { if (is_char(buf, '?')) {
if (!is_char(buf, '\r')) { if (!is_char(buf, '\r')) {
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
hf_indicators = ag->hf_indicators_of_hf & ag->hf_indicators_of_ag; hf_indicators = ag->hf_indicators_of_hf & ag->hf_indicators_of_ag;
hfp_ag_unlock(ag);
len = (sizeof(hf_indicators) * 8) > HFP_HF_IND_MAX ? HFP_HF_IND_MAX len = (sizeof(hf_indicators) * 8) > HFP_HF_IND_MAX ? HFP_HF_IND_MAX
: (sizeof(hf_indicators) * 8); : (sizeof(hf_indicators) * 8);
for (int i = 1; i < len; i++) { for (int i = 1; i < len; i++) {
@ -596,7 +621,9 @@ static int bt_hfp_ag_bind_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
data = &ag->buffer[0]; data = &ag->buffer[0];
*data = '('; *data = '(';
data++; data++;
hfp_ag_lock(ag);
hf_indicators = ag->hf_indicators_of_ag; hf_indicators = ag->hf_indicators_of_ag;
hfp_ag_unlock(ag);
len = (sizeof(hf_indicators) * 8) > HFP_HF_IND_MAX ? HFP_HF_IND_MAX len = (sizeof(hf_indicators) * 8) > HFP_HF_IND_MAX ? HFP_HF_IND_MAX
: (sizeof(hf_indicators) * 8); : (sizeof(hf_indicators) * 8);
for (int i = 1; (i < len) && (hf_indicators != 0); i++) { for (int i = 1; (i < len) && (hf_indicators != 0); i++) {
@ -640,7 +667,9 @@ static int bt_hfp_ag_bind_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
} }
} }
hfp_ag_lock(ag);
ag->hf_indicators_of_hf = hf_indicators; ag->hf_indicators_of_hf = hf_indicators;
hfp_ag_unlock(ag);
return 0; return 0;
} }
@ -705,11 +734,17 @@ static int hfp_ag_update_indicator(struct bt_hfp_ag *ag, enum bt_hfp_ag_indicato
static void hfp_ag_close_sco(struct bt_hfp_ag *ag) static void hfp_ag_close_sco(struct bt_hfp_ag *ag)
{ {
struct bt_conn *sco;
LOG_DBG(""); LOG_DBG("");
if (NULL != ag->sco_chan.sco) {
LOG_DBG("Disconnect sco %p", ag->sco_chan.sco); hfp_ag_lock(ag);
bt_conn_disconnect(ag->sco_chan.sco, BT_HCI_ERR_LOCALHOST_TERM_CONN); sco = ag->sco_chan.sco;
ag->sco_chan.sco = NULL; ag->sco_chan.sco = NULL;
hfp_ag_unlock(ag);
if (sco != NULL) {
LOG_DBG("Disconnect sco %p", sco);
bt_conn_disconnect(sco, BT_HCI_ERR_LOCALHOST_TERM_CONN);
} }
} }
@ -754,17 +789,22 @@ static void bt_hfp_ag_unit_call_terminate(struct bt_hfp_ag *ag, void *user_data)
static int bt_hfp_ag_chup_handler(struct bt_hfp_ag *ag, struct net_buf *buf) static int bt_hfp_ag_chup_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
{ {
int err; int err;
bt_hfp_call_state_t call_state;
if (!is_char(buf, '\r')) { if (!is_char(buf, '\r')) {
return -ENOTSUP; return -ENOTSUP;
} }
if (ag->call_state == BT_HFP_CALL_ALERTING) { hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
if (call_state == BT_HFP_CALL_ALERTING) {
if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return -ENOTSUP; return -ENOTSUP;
} }
err = hfp_ag_next_step(ag, bt_hfp_ag_call_reject, NULL); err = hfp_ag_next_step(ag, bt_hfp_ag_call_reject, NULL);
} else if ((ag->call_state == BT_HFP_CALL_ACTIVE) || (ag->call_state == BT_HFP_CALL_HOLD)) { } else if ((call_state == BT_HFP_CALL_ACTIVE) || (call_state == BT_HFP_CALL_HOLD)) {
err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_terminate, NULL); err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_terminate, NULL);
} else { } else {
return -ENOTSUP; return -ENOTSUP;
@ -776,6 +816,7 @@ static uint8_t bt_hfp_get_call_state(struct bt_hfp_ag *ag)
{ {
uint8_t status = HFP_AG_CLCC_STATUS_INVALID; uint8_t status = HFP_AG_CLCC_STATUS_INVALID;
hfp_ag_lock(ag);
switch (ag->call_state) { switch (ag->call_state) {
case BT_HFP_CALL_TERMINATE: case BT_HFP_CALL_TERMINATE:
break; break;
@ -801,6 +842,7 @@ static uint8_t bt_hfp_get_call_state(struct bt_hfp_ag *ag)
default: default:
break; break;
} }
hfp_ag_unlock(ag);
return status; return status;
} }
@ -817,10 +859,13 @@ static int bt_hfp_ag_clcc_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
if (ag->call_state == BT_HFP_CALL_TERMINATE) { if (ag->call_state == BT_HFP_CALL_TERMINATE) {
/* AG shall always send OK response to HF */ /* AG shall always send OK response to HF */
hfp_ag_unlock(ag);
return 0; return 0;
} }
hfp_ag_unlock(ag);
dir = atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL) ? 1 : 0; dir = atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL) ? 1 : 0;
status = bt_hfp_get_call_state(ag); status = bt_hfp_get_call_state(ag);
@ -841,12 +886,16 @@ static int bt_hfp_ag_bia_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
uint32_t number; uint32_t number;
int err; int err;
int index = 0; int index = 0;
uint32_t indicator = ag->indicator; uint32_t indicator;
if (!is_char(buf, '=')) { if (!is_char(buf, '=')) {
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
indicator = ag->indicator;
hfp_ag_unlock(ag);
while (buf->len > 0) { while (buf->len > 0) {
err = get_number(buf, &number); err = get_number(buf, &number);
if (err == 0) { if (err == 0) {
@ -873,7 +922,10 @@ static int bt_hfp_ag_bia_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
/* Force call, call setup and held call indicators are enabled. */ /* Force call, call setup and held call indicators are enabled. */
indicator = BIT(BT_HFP_AG_CALL_IND) | BIT(BT_HFP_AG_CALL_SETUP_IND) | indicator = BIT(BT_HFP_AG_CALL_IND) | BIT(BT_HFP_AG_CALL_SETUP_IND) |
BIT(BT_HFP_AG_CALL_HELD_IND); BIT(BT_HFP_AG_CALL_HELD_IND);
hfp_ag_lock(ag);
ag->indicator = indicator; ag->indicator = indicator;
hfp_ag_unlock(ag);
return 0; return 0;
} }
@ -897,8 +949,12 @@ static void bt_hfp_ag_call_ringing_cb(struct bt_hfp_ag *ag, bool in_bond)
static void hfp_ag_sco_connected(struct bt_sco_chan *chan) static void hfp_ag_sco_connected(struct bt_sco_chan *chan)
{ {
struct bt_hfp_ag *ag = CONTAINER_OF(chan, struct bt_hfp_ag, sco_chan); struct bt_hfp_ag *ag = CONTAINER_OF(chan, struct bt_hfp_ag, sco_chan);
bt_hfp_call_state_t call_state;
if (ag->call_state == BT_HFP_CALL_INCOMING) { hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
if (call_state == BT_HFP_CALL_INCOMING) {
bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_ALERTING); bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_ALERTING);
bt_hfp_ag_call_ringing_cb(ag, true); bt_hfp_ag_call_ringing_cb(ag, true);
} }
@ -911,12 +967,16 @@ static void hfp_ag_sco_connected(struct bt_sco_chan *chan)
static void hfp_ag_sco_disconnected(struct bt_sco_chan *chan, uint8_t reason) static void hfp_ag_sco_disconnected(struct bt_sco_chan *chan, uint8_t reason)
{ {
struct bt_hfp_ag *ag = CONTAINER_OF(chan, struct bt_hfp_ag, sco_chan); struct bt_hfp_ag *ag = CONTAINER_OF(chan, struct bt_hfp_ag, sco_chan);
bt_hfp_call_state_t call_state;
if ((bt_ag) && bt_ag->sco_disconnected) { if ((bt_ag) && bt_ag->sco_disconnected) {
bt_ag->sco_disconnected(ag); bt_ag->sco_disconnected(ag);
} }
if ((ag->call_state == BT_HFP_CALL_INCOMING) || (ag->call_state == BT_HFP_CALL_OUTGOING)) { hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
if ((call_state == BT_HFP_CALL_INCOMING) || (call_state == BT_HFP_CALL_OUTGOING)) {
bt_hfp_ag_call_reject(ag, NULL); bt_hfp_ag_call_reject(ag, NULL);
} }
} }
@ -950,16 +1010,19 @@ static struct bt_conn *bt_hfp_ag_create_sco(struct bt_hfp_ag *ag)
static int hfp_ag_open_sco(struct bt_hfp_ag *ag) static int hfp_ag_open_sco(struct bt_hfp_ag *ag)
{ {
hfp_ag_lock(ag);
if (ag->sco_chan.sco == NULL) { if (ag->sco_chan.sco == NULL) {
struct bt_conn *sco_conn = bt_hfp_ag_create_sco(ag); struct bt_conn *sco_conn = bt_hfp_ag_create_sco(ag);
if (sco_conn == NULL) { if (sco_conn == NULL) {
LOG_ERR("Fail to create sco connection!"); LOG_ERR("Fail to create sco connection!");
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
LOG_DBG("SCO connection created (%p)", sco_conn); LOG_DBG("SCO connection created (%p)", sco_conn);
} }
hfp_ag_unlock(ag);
return 0; return 0;
} }
@ -970,15 +1033,19 @@ static int bt_hfp_ag_codec_select(struct bt_hfp_ag *ag)
LOG_DBG(""); LOG_DBG("");
hfp_ag_lock(ag);
if (ag->selected_codec_id == 0) { if (ag->selected_codec_id == 0) {
LOG_ERR("Codec is invalid"); LOG_ERR("Codec is invalid");
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
if (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) { if (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) {
LOG_ERR("Codec is unsupported (codec id %d)", ag->selected_codec_id); LOG_ERR("Codec is unsupported (codec id %d)", ag->selected_codec_id);
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BCS:%d\r\n", ag->selected_codec_id); err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BCS:%d\r\n", ag->selected_codec_id);
if (err != 0) { if (err != 0) {
@ -990,8 +1057,13 @@ static int bt_hfp_ag_codec_select(struct bt_hfp_ag *ag)
static int bt_hfp_ag_create_audio_connection(struct bt_hfp_ag *ag) static int bt_hfp_ag_create_audio_connection(struct bt_hfp_ag *ag)
{ {
int err; int err;
uint32_t hf_codec_ids;
if ((ag->hf_codec_ids != 0) && atomic_test_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED)) { hfp_ag_lock(ag);
hf_codec_ids = ag->hf_codec_ids;
hfp_ag_unlock(ag);
if ((hf_codec_ids != 0) && atomic_test_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED)) {
atomic_set_bit(ag->flags, BT_HFP_AG_CODEC_CONN); atomic_set_bit(ag->flags, BT_HFP_AG_CODEC_CONN);
err = bt_hfp_ag_codec_select(ag); err = bt_hfp_ag_codec_select(ag);
} else { } else {
@ -1035,9 +1107,12 @@ static int bt_hfp_ag_ata_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
if (ag->call_state != BT_HFP_CALL_ALERTING) { if (ag->call_state != BT_HFP_CALL_ALERTING) {
hfp_ag_unlock(ag);
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_unlock(ag);
if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return -ENOTSUP; return -ENOTSUP;
@ -1105,21 +1180,15 @@ static int bt_hfp_ag_bcc_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENOTSUP; return -ENOTSUP;
} }
if (ag->selected_codec_id == 0) { hfp_ag_lock(ag);
return -ENOTSUP; if ((ag->selected_codec_id == 0) ||
} (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) ||
(ag->call_state == BT_HFP_CALL_TERMINATE) ||
if (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) { (ag->sco_chan.sco != NULL)) {
return -ENOTSUP; hfp_ag_unlock(ag);
}
if (ag->call_state == BT_HFP_CALL_TERMINATE) {
return -ENOTSUP;
}
if (ag->sco_chan.sco != NULL) {
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_unlock(ag);
err = hfp_ag_next_step(ag, bt_hfp_ag_audio_connection, NULL); err = hfp_ag_next_step(ag, bt_hfp_ag_audio_connection, NULL);
@ -1157,6 +1226,7 @@ static int bt_hfp_ag_bcs_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ESRCH; return -ESRCH;
} }
hfp_ag_lock(ag);
if (ag->selected_codec_id != number) { if (ag->selected_codec_id != number) {
LOG_ERR("Received codec id %d is not aligned with selected %d", LOG_ERR("Received codec id %d is not aligned with selected %d",
number, ag->selected_codec_id); number, ag->selected_codec_id);
@ -1166,6 +1236,7 @@ static int bt_hfp_ag_bcs_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
ag->selected_codec_id, ag->hf_codec_ids); ag->selected_codec_id, ag->hf_codec_ids);
err = -ENOTSUP; err = -ENOTSUP;
} }
hfp_ag_unlock(ag);
atomic_clear_bit(ag->flags, BT_HFP_AG_CODEC_CONN); atomic_clear_bit(ag->flags, BT_HFP_AG_CODEC_CONN);
atomic_clear_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED); atomic_clear_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED);
@ -1173,7 +1244,12 @@ static int bt_hfp_ag_bcs_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
if (err == 0) { if (err == 0) {
err = hfp_ag_next_step(ag, bt_hfp_ag_unit_codec_conn_setup, NULL); err = hfp_ag_next_step(ag, bt_hfp_ag_unit_codec_conn_setup, NULL);
} else { } else {
if (ag->call_state != BT_HFP_CALL_TERMINATE) { bt_hfp_call_state_t call_state;
hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
if (call_state != BT_HFP_CALL_TERMINATE) {
(void)hfp_ag_next_step(ag, bt_hfp_ag_unit_call_terminate, NULL); (void)hfp_ag_next_step(ag, bt_hfp_ag_unit_call_terminate, NULL);
} }
} }
@ -1233,12 +1309,15 @@ static int bt_hfp_ag_atd_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENAMETOOLONG; return -ENAMETOOLONG;
} }
hfp_ag_lock(ag);
if (ag->call_state != BT_HFP_CALL_TERMINATE) { if (ag->call_state != BT_HFP_CALL_TERMINATE) {
hfp_ag_unlock(ag);
return -EBUSY; return -EBUSY;
} }
/* Copy number to ag->number including null-character */ /* Copy number to ag->number including null-character */
memcpy(ag->number, number, len + 1); memcpy(ag->number, number, len + 1);
hfp_ag_unlock(ag);
err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_outgoing, NULL); err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_outgoing, NULL);
@ -1253,13 +1332,17 @@ static int bt_hfp_ag_bldn_handler(struct bt_hfp_ag *ag, struct net_buf *buf)
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_lock(ag);
if (strlen(ag->number) == 0) { if (strlen(ag->number) == 0) {
hfp_ag_unlock(ag);
return -ENOSR; return -ENOSR;
} }
if (ag->call_state != BT_HFP_CALL_TERMINATE) { if (ag->call_state != BT_HFP_CALL_TERMINATE) {
hfp_ag_unlock(ag);
return -EBUSY; return -EBUSY;
} }
hfp_ag_unlock(ag);
err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_outgoing, NULL); err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_outgoing, NULL);
@ -1313,11 +1396,15 @@ static void hfp_ag_connected(struct bt_rfcomm_dlc *dlc)
static void hfp_ag_disconnected(struct bt_rfcomm_dlc *dlc) static void hfp_ag_disconnected(struct bt_rfcomm_dlc *dlc)
{ {
struct bt_hfp_ag *ag = CONTAINER_OF(dlc, struct bt_hfp_ag, rfcomm_dlc); struct bt_hfp_ag *ag = CONTAINER_OF(dlc, struct bt_hfp_ag, rfcomm_dlc);
bt_hfp_call_state_t call_state;
bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTED); bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTED);
if ((ag->call_state == BT_HFP_CALL_ALERTING) || (ag->call_state == BT_HFP_CALL_ACTIVE) || hfp_ag_lock(ag);
(ag->call_state == BT_HFP_CALL_HOLD)) { call_state = ag->call_state;
hfp_ag_unlock(ag);
if ((call_state == BT_HFP_CALL_ALERTING) || (call_state == BT_HFP_CALL_ACTIVE) ||
(call_state == BT_HFP_CALL_HOLD)) {
bt_hfp_ag_terminate_cb(ag, NULL); bt_hfp_ag_terminate_cb(ag, NULL);
} }
@ -1420,10 +1507,15 @@ static void hfp_ag_sent(struct bt_rfcomm_dlc *dlc, struct net_buf *buf, int err)
static void bt_ag_deferred_work_cb(struct bt_hfp_ag *ag, void *user_data) static void bt_ag_deferred_work_cb(struct bt_hfp_ag *ag, void *user_data)
{ {
int err; int err;
bt_hfp_call_state_t call_state;
LOG_DBG(""); LOG_DBG("");
switch (ag->call_state) { hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
switch (call_state) {
case BT_HFP_CALL_TERMINATE: case BT_HFP_CALL_TERMINATE:
break; break;
case BT_HFP_CALL_ACTIVE: case BT_HFP_CALL_ACTIVE:
@ -1483,10 +1575,14 @@ static void bt_ag_deferred_work(struct k_work *work)
static void bt_ag_ringing_work_cb(struct bt_hfp_ag *ag, void *user_data) static void bt_ag_ringing_work_cb(struct bt_hfp_ag *ag, void *user_data)
{ {
int err; int err;
bt_hfp_call_state_t call_state;
LOG_DBG(""); LOG_DBG("");
if (ag->call_state == BT_HFP_CALL_ALERTING) { hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
if (call_state == BT_HFP_CALL_ALERTING) {
if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return; return;
@ -1635,6 +1731,7 @@ int bt_hfp_ag_connect(struct bt_conn *conn, struct bt_hfp_ag **ag, uint8_t chann
int bt_hfp_ag_disconnect(struct bt_hfp_ag *ag) int bt_hfp_ag_disconnect(struct bt_hfp_ag *ag)
{ {
int err; int err;
bt_hfp_call_state_t call_state;
LOG_DBG(""); LOG_DBG("");
@ -1642,16 +1739,20 @@ int bt_hfp_ag_disconnect(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
call_state = ag->call_state;
hfp_ag_unlock(ag);
bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTING); bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTING);
if ((ag->call_state == BT_HFP_CALL_ACTIVE) || (ag->call_state == BT_HFP_CALL_HOLD)) { if ((call_state == BT_HFP_CALL_ACTIVE) || (call_state == BT_HFP_CALL_HOLD)) {
err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb,
NULL); NULL);
if (err != 0) { if (err != 0) {
LOG_ERR("HFP AG send response err :(%d)", err); LOG_ERR("HFP AG send response err :(%d)", err);
} }
return err; return err;
} else if (ag->call_state != BT_HFP_CALL_TERMINATE) { } else if (call_state != BT_HFP_CALL_TERMINATE) {
err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE,
bt_hfp_ag_reject_cb, NULL); bt_hfp_ag_reject_cb, NULL);
if (err != 0) { if (err != 0) {
@ -1710,21 +1811,27 @@ int bt_hfp_ag_remote_incoming(struct bt_hfp_ag *ag, const char *number)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if (ag->call_state != BT_HFP_CALL_TERMINATE) { if (ag->call_state != BT_HFP_CALL_TERMINATE) {
hfp_ag_unlock(ag);
return -EBUSY; return -EBUSY;
} }
hfp_ag_unlock(ag);
len = strlen(number); len = strlen(number);
if ((len == 0) || (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN)) { if ((len == 0) || (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN)) {
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
/* Copy number to ag->number including null-character */ /* Copy number to ag->number including null-character */
memcpy(ag->number, number, len + 1); memcpy(ag->number, number, len + 1);
hfp_ag_unlock(ag);
atomic_set_bit(ag->flags, BT_HFP_AG_INCOMING_CALL); atomic_set_bit(ag->flags, BT_HFP_AG_INCOMING_CALL);
@ -1747,13 +1854,17 @@ int bt_hfp_ag_reject(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if ((ag->call_state != BT_HFP_CALL_ALERTING) && (ag->call_state != BT_HFP_CALL_INCOMING)) { if ((ag->call_state != BT_HFP_CALL_ALERTING) && (ag->call_state != BT_HFP_CALL_INCOMING)) {
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return -EINVAL; return -EINVAL;
@ -1775,13 +1886,17 @@ int bt_hfp_ag_accept(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if (ag->call_state != BT_HFP_CALL_ALERTING) { if (ag->call_state != BT_HFP_CALL_ALERTING) {
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return -EINVAL; return -EINVAL;
@ -1811,13 +1926,17 @@ int bt_hfp_ag_terminate(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if ((ag->call_state != BT_HFP_CALL_ACTIVE) || (ag->call_state != BT_HFP_CALL_HOLD)) { if ((ag->call_state != BT_HFP_CALL_ACTIVE) || (ag->call_state != BT_HFP_CALL_HOLD)) {
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL);
@ -1853,21 +1972,27 @@ int bt_hfp_ag_outgoing(struct bt_hfp_ag *ag, const char *number)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if (ag->call_state != BT_HFP_CALL_TERMINATE) { if (ag->call_state != BT_HFP_CALL_TERMINATE) {
hfp_ag_unlock(ag);
return -EBUSY; return -EBUSY;
} }
hfp_ag_unlock(ag);
len = strlen(number); len = strlen(number);
if ((len == 0) || (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN)) { if ((len == 0) || (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN)) {
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
/* Copy number to ag->number including null-character */ /* Copy number to ag->number including null-character */
memcpy(ag->number, number, len + 1); memcpy(ag->number, number, len + 1);
hfp_ag_unlock(ag);
atomic_clear_bit(ag->flags, BT_HFP_AG_INCOMING_CALL); atomic_clear_bit(ag->flags, BT_HFP_AG_INCOMING_CALL);
@ -1898,19 +2023,24 @@ int bt_hfp_ag_remote_ringing(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if (BT_HFP_CALL_OUTGOING != ag->call_state) { if (ag->call_state != BT_HFP_CALL_OUTGOING) {
hfp_ag_unlock(ag);
return -EBUSY; return -EBUSY;
} }
if (atomic_test_bit(ag->flags, BT_HFP_AG_INBAND_RING)) { if (atomic_test_bit(ag->flags, BT_HFP_AG_INBAND_RING)) {
if (ag->sco_chan.sco == NULL) { if (ag->sco_chan.sco == NULL) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
} }
hfp_ag_unlock(ag);
err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND,
BT_HFP_CALL_SETUP_REMOTE_ALERTING, bt_hfp_ag_ringing_cb, BT_HFP_CALL_SETUP_REMOTE_ALERTING, bt_hfp_ag_ringing_cb,
@ -1929,13 +2059,17 @@ int bt_hfp_ag_remote_reject(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if ((ag->call_state != BT_HFP_CALL_ALERTING) && (ag->call_state != BT_HFP_CALL_OUTGOING)) { if ((ag->call_state != BT_HFP_CALL_ALERTING) && (ag->call_state != BT_HFP_CALL_OUTGOING)) {
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return -EINVAL; return -EINVAL;
@ -1957,13 +2091,17 @@ int bt_hfp_ag_remote_accept(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if (ag->call_state != BT_HFP_CALL_ALERTING) { if (ag->call_state != BT_HFP_CALL_ALERTING) {
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) {
return -EINVAL; return -EINVAL;
@ -1993,13 +2131,17 @@ int bt_hfp_ag_remote_terminate(struct bt_hfp_ag *ag)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if ((ag->call_state != BT_HFP_CALL_ACTIVE) || (ag->call_state != BT_HFP_CALL_HOLD)) { if ((ag->call_state != BT_HFP_CALL_ACTIVE) || (ag->call_state != BT_HFP_CALL_HOLD)) {
hfp_ag_unlock(ag);
return -EINVAL; return -EINVAL;
} }
hfp_ag_unlock(ag);
err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL);
@ -2016,9 +2158,12 @@ int bt_hfp_ag_set_indicator(struct bt_hfp_ag *ag, enum bt_hfp_ag_indicator index
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
hfp_ag_unlock(ag);
switch (index) { switch (index) {
case BT_HFP_AG_SERVICE_IND: case BT_HFP_AG_SERVICE_IND:
@ -2057,7 +2202,9 @@ int bt_hfp_ag_set_operator(struct bt_hfp_ag *ag, char *name)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
@ -2065,6 +2212,7 @@ int bt_hfp_ag_set_operator(struct bt_hfp_ag *ag, char *name)
len = MIN(sizeof(ag->operator) - 1, len); len = MIN(sizeof(ag->operator) - 1, len);
memcpy(ag->operator, name, len); memcpy(ag->operator, name, len);
ag->operator[len] = '\0'; ag->operator[len] = '\0';
hfp_ag_unlock(ag);
return 0; return 0;
} }
@ -2079,19 +2227,26 @@ int bt_hfp_ag_select_codec(struct bt_hfp_ag *ag, uint8_t id)
return -EINVAL; return -EINVAL;
} }
hfp_ag_lock(ag);
if (ag->state != BT_HFP_CONNECTED) { if (ag->state != BT_HFP_CONNECTED) {
hfp_ag_unlock(ag);
return -ENOTCONN; return -ENOTCONN;
} }
if (!(ag->hf_codec_ids && BIT(id))) { if (!(ag->hf_codec_ids && BIT(id))) {
hfp_ag_unlock(ag);
return -ENOTSUP; return -ENOTSUP;
} }
hfp_ag_unlock(ag);
if (atomic_test_bit(ag->flags, BT_HFP_AG_CODEC_CONN)) { if (atomic_test_bit(ag->flags, BT_HFP_AG_CODEC_CONN)) {
return -EBUSY; return -EBUSY;
} }
hfp_ag_lock(ag);
ag->selected_codec_id = id; ag->selected_codec_id = id;
hfp_ag_unlock(ag);
atomic_set_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED); atomic_set_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED);
err = bt_hfp_ag_create_audio_connection(ag); err = bt_hfp_ag_create_audio_connection(ag);