/* * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include "common/assert.h" #include #include #include "host/hci_core.h" #include "host/conn_internal.h" #include "l2cap_br_internal.h" #include "rfcomm_internal.h" #include "at.h" #include "sco_internal.h" #include "hfp_internal.h" #include "hfp_ag_internal.h" #define LOG_LEVEL CONFIG_BT_HFP_AG_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_hfp_ag); typedef int (*bt_hfp_ag_parse_command_t)(struct bt_hfp_ag *ag, struct net_buf *buf); struct bt_hfp_ag_at_cmd_handler { const char *cmd; bt_hfp_ag_parse_command_t handler; }; static const struct { const char *name; const char *connector; uint32_t min; uint32_t max; } ag_ind[] = { {"service", "-", 0, 1}, /* BT_HFP_AG_SERVICE_IND */ {"call", ",", 0, 1}, /* BT_HFP_AG_CALL_IND */ {"callsetup", "-", 0, 3}, /* BT_HFP_AG_CALL_SETUP_IND */ {"callheld", "-", 0, 2}, /* BT_HFP_AG_CALL_HELD_IND */ {"signal", "-", 0, 5}, /* BT_HFP_AG_SIGNAL_IND */ {"roam", ",", 0, 1}, /* BT_HFP_AG_ROAM_IND */ {"battchg", "-", 0, 5} /* BT_HFP_AG_BATTERY_IND */ }; typedef void (*bt_hfp_ag_tx_cb_t)(struct bt_hfp_ag *ag, void *user_data); struct bt_ag_tx { sys_snode_t node; struct bt_hfp_ag *ag; struct net_buf *buf; bt_hfp_ag_tx_cb_t cb; void *user_data; int err; }; NET_BUF_POOL_FIXED_DEFINE(ag_pool, CONFIG_BT_HFP_AG_TX_BUF_COUNT, BT_RFCOMM_BUF_SIZE(BT_HF_CLIENT_MAX_PDU), CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL); static struct bt_hfp_ag bt_hfp_ag_pool[CONFIG_BT_MAX_CONN]; static struct bt_hfp_ag_cb *bt_ag; /* Sent but not acknowledged TX packets with a callback */ static struct bt_ag_tx ag_tx[CONFIG_BT_HFP_AG_TX_BUF_COUNT * 2]; static K_FIFO_DEFINE(ag_tx_free); static K_FIFO_DEFINE(ag_tx_notify); struct k_thread ag_thread; static K_KERNEL_STACK_MEMBER(ag_thread_stack, CONFIG_BT_HFP_AG_THREAD_STACK_SIZE); static k_tid_t ag_thread_id; static enum at_cme bt_hfp_ag_get_cme_err(int err) { enum at_cme cme_err; switch (err) { case -EOPNOTSUPP: cme_err = CME_ERROR_OPERATION_NOT_SUPPORTED; break; case -EFAULT: cme_err = CME_ERROR_AG_FAILURE; break; case -ENOSR: cme_err = CME_ERROR_MEMORY_FAILURE; break; case -ENOMEM: __fallthrough; case -ENOBUFS: cme_err = CME_ERROR_MEMORY_FULL; break; case -ENAMETOOLONG: cme_err = CME_ERROR_DIAL_STRING_TO_LONG; break; case -EINVAL: cme_err = CME_ERROR_INVALID_INDEX; break; case -ENOTSUP: cme_err = CME_ERROR_OPERATION_NOT_ALLOWED; break; case -ENOTCONN: cme_err = CME_ERROR_NO_CONNECTION_TO_PHONE; break; default: cme_err = CME_ERROR_AG_FAILURE; break; } return cme_err; } static void hfp_ag_lock(struct bt_hfp_ag *ag) { k_sem_take(&ag->lock, K_FOREVER); } static void hfp_ag_unlock(struct bt_hfp_ag *ag) { k_sem_give(&ag->lock); } 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); hfp_ag_lock(ag); ag->state = state; hfp_ag_unlock(ag); switch (state) { case BT_HFP_DISCONNECTED: if (bt_ag && bt_ag->disconnected) { bt_ag->disconnected(ag); } break; case BT_HFP_CONNECTING: break; case BT_HFP_CONFIG: break; case BT_HFP_CONNECTED: if (bt_ag && bt_ag->connected) { bt_ag->connected(ag); } break; case BT_HFP_DISCONNECTING: break; default: LOG_WRN("no valid (%u) state was set", state); break; } } 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); hfp_ag_lock(ag); ag->call_state = call_state; state = ag->state; hfp_ag_unlock(ag); switch (call_state) { case BT_HFP_CALL_TERMINATE: atomic_clear(ag->flags); k_work_cancel_delayable(&ag->deferred_work); break; case BT_HFP_CALL_OUTGOING: k_work_reschedule(&ag->deferred_work, K_SECONDS(CONFIG_BT_HFP_AG_OUTGOING_TIMEOUT)); break; case BT_HFP_CALL_INCOMING: k_work_reschedule(&ag->deferred_work, K_SECONDS(CONFIG_BT_HFP_AG_INCOMING_TIMEOUT)); break; case BT_HFP_CALL_ALERTING: k_work_reschedule(&ag->ringing_work, K_NO_WAIT); k_work_reschedule(&ag->deferred_work, K_SECONDS(CONFIG_BT_HFP_AG_ALERTING_TIMEOUT)); break; case BT_HFP_CALL_ACTIVE: k_work_cancel_delayable(&ag->ringing_work); k_work_cancel_delayable(&ag->deferred_work); break; case BT_HFP_CALL_HOLD: break; default: /* Invalid state */ break; } if (state == BT_HFP_DISCONNECTING) { int err = bt_rfcomm_dlc_disconnect(&ag->rfcomm_dlc); if (err) { LOG_ERR("Fail to disconnect DLC %p", &ag->rfcomm_dlc); } } } static struct bt_ag_tx *bt_ag_tx_alloc(void) { /* The TX context always get freed in the system workqueue, * so if we're in the same workqueue but there are no immediate * contexts available, there's no chance we'll get one by waiting. */ if (k_current_get() == &k_sys_work_q.thread) { return k_fifo_get(&ag_tx_free, K_NO_WAIT); } if (IS_ENABLED(CONFIG_BT_HFP_AG_LOG_LEVEL_DBG)) { struct bt_ag_tx *tx = k_fifo_get(&ag_tx_free, K_NO_WAIT); if (tx) { return tx; } LOG_WRN("Unable to get an immediate free bt_ag_tx"); } return k_fifo_get(&ag_tx_free, K_FOREVER); } static void bt_ag_tx_free(struct bt_ag_tx *tx) { LOG_DBG("Free tx buffer %p", tx); (void)memset(tx, 0, sizeof(*tx)); k_fifo_put(&ag_tx_free, tx); } static int hfp_ag_next_step(struct bt_hfp_ag *ag, bt_hfp_ag_tx_cb_t cb, void *user_data) { struct bt_ag_tx *tx; LOG_DBG("cb %p user_data %p", cb, user_data); tx = bt_ag_tx_alloc(); if (tx == NULL) { LOG_ERR("No tx buffers!"); return -ENOMEM; } LOG_DBG("Alloc tx buffer %p", tx); tx->ag = ag; tx->buf = NULL; tx->cb = cb; tx->user_data = user_data; tx->err = 0; k_fifo_put(&ag_tx_notify, tx); return 0; } static int hfp_ag_send(struct bt_hfp_ag *ag, struct bt_ag_tx *tx) { int err = bt_rfcomm_dlc_send(&ag->rfcomm_dlc, tx->buf); if (err < 0) { net_buf_unref(tx->buf); } return err; } static void bt_ag_tx_work(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct bt_hfp_ag *ag = CONTAINER_OF(dwork, struct bt_hfp_ag, tx_work); sys_snode_t *node; struct bt_ag_tx *tx; bt_hfp_state_t state; hfp_ag_lock(ag); state = ag->state; if ((state == BT_HFP_DISCONNECTED) || (state == BT_HFP_DISCONNECTING)) { LOG_ERR("AG %p is not connected", ag); goto unlock; } node = sys_slist_peek_head(&ag->tx_pending); if (!node) { LOG_DBG("No pending tx"); goto unlock; } tx = CONTAINER_OF(node, struct bt_ag_tx, node); if (!atomic_test_and_set_bit(ag->flags, BT_HFP_AG_TX_ONGOING)) { LOG_DBG("AG %p sending tx %p", ag, tx); int err = hfp_ag_send(ag, tx); if (err < 0) { LOG_ERR("Rfcomm send error :(%d)", err); sys_slist_find_and_remove(&ag->tx_pending, &tx->node); tx->err = err; k_fifo_put(&ag_tx_notify, tx); /* Clear the tx ongoing flag */ if (!atomic_test_and_clear_bit(ag->flags, BT_HFP_AG_TX_ONGOING)) { LOG_WRN("tx ongoing flag is not set"); } /* Due to the work is failed, restart the tx work */ k_work_reschedule(&ag->tx_work, K_NO_WAIT); } } unlock: hfp_ag_unlock(ag); } static int hfp_ag_send_data(struct bt_hfp_ag *ag, bt_hfp_ag_tx_cb_t cb, void *user_data, const char *format, ...) { struct net_buf *buf; struct bt_ag_tx *tx; va_list vargs; int err; bt_hfp_state_t state; LOG_DBG("AG %p sending data cb %p user_data %p", ag, cb, user_data); hfp_ag_lock(ag); state = ag->state; hfp_ag_unlock(ag); if ((state == BT_HFP_DISCONNECTED) || (state == BT_HFP_DISCONNECTING)) { LOG_ERR("AG %p is not connected", ag); return -ENOTCONN; } buf = bt_rfcomm_create_pdu(&ag_pool); if (!buf) { LOG_ERR("No Buffers!"); return -ENOMEM; } tx = bt_ag_tx_alloc(); if (tx == NULL) { LOG_ERR("No tx buffers!"); net_buf_unref(buf); return -ENOMEM; } LOG_DBG("buf %p tx %p", buf, tx); tx->ag = ag; tx->buf = buf; tx->cb = cb; tx->user_data = user_data; va_start(vargs, format); err = vsnprintk((char *)buf->data, (net_buf_tailroom(buf) - 1), format, vargs); va_end(vargs); if (err < 0) { LOG_ERR("Unable to format variable arguments"); net_buf_unref(buf); bt_ag_tx_free(tx); return err; } net_buf_add(buf, err); LOG_HEXDUMP_DBG(buf->data, buf->len, "Sending:"); hfp_ag_lock(ag); sys_slist_append(&ag->tx_pending, &tx->node); hfp_ag_unlock(ag); /* Always active tx work */ k_work_reschedule(&ag->tx_work, K_NO_WAIT); return 0; } static void skip_space(struct net_buf *buf) { while ((buf->len > 0) && (buf->data[0] == ' ')) { (void)net_buf_pull(buf, 1); } } static int get_number(struct net_buf *buf, uint32_t *number) { int err = -EINVAL; *number = 0; skip_space(buf); while (buf->len > 0) { if ((buf->data[0] >= '0') && (buf->data[0] <= '9')) { *number = *number * 10 + buf->data[0] - '0'; (void)net_buf_pull(buf, 1); err = 0; } else { break; } } skip_space(buf); return err; } static bool is_char(struct net_buf *buf, uint8_t c) { bool found = false; skip_space(buf); if (buf->len > 0) { if (buf->data[0] == c) { (void)net_buf_pull(buf, 1); found = true; } } skip_space(buf); return found; } static int bt_hfp_ag_brsf_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { uint32_t hf_features; int err; if (!is_char(buf, '=')) { return -ENOTSUP; } err = get_number(buf, &hf_features); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } hfp_ag_lock(ag); 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); } static int bt_hfp_ag_bac_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { uint32_t codec; uint32_t codec_ids = 0U; int err = 0; if (!is_char(buf, '=')) { return -ENOTSUP; } hfp_ag_lock(ag); if (!(ag->hf_features & BT_HFP_HF_FEATURE_CODEC_NEG) || !(ag->ag_features & BT_HFP_AG_FEATURE_CODEC_NEG)) { hfp_ag_unlock(ag); return -EOPNOTSUPP; } hfp_ag_unlock(ag); while (buf->len > 0) { err = get_number(buf, &codec); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, ',')) { if (!is_char(buf, '\r')) { return -ENOTSUP; } } if (codec < (sizeof(codec_ids) * 8)) { codec_ids |= BIT(codec); } } hfp_ag_lock(ag); ag->hf_codec_ids = codec_ids; hfp_ag_unlock(ag); if (bt_ag && bt_ag->codec) { bt_ag->codec(ag, ag->hf_codec_ids); } return 0; } static int bt_hfp_ag_cind_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; bool inquiry_status = true; if (is_char(buf, '=')) { inquiry_status = false; } if (!is_char(buf, '?')) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } if (!inquiry_status) { err = hfp_ag_send_data( ag, NULL, NULL, "\r\n+CIND:(\"%s\",(%d%s%d)),(\"%s\",(%d%s%d)),(\"%s\",(%d%s%d)),(\"%s\",(%" "d%s%d)),(\"%s\",(%d%s%d)),(\"%s\",(%d%s%d)),(\"%s\",(%d%s%d))\r\n", ag_ind[BT_HFP_AG_SERVICE_IND].name, ag_ind[BT_HFP_AG_SERVICE_IND].min, ag_ind[BT_HFP_AG_SERVICE_IND].connector, ag_ind[BT_HFP_AG_SERVICE_IND].max, ag_ind[BT_HFP_AG_CALL_IND].name, ag_ind[BT_HFP_AG_CALL_IND].min, ag_ind[BT_HFP_AG_CALL_IND].connector, ag_ind[BT_HFP_AG_CALL_IND].max, ag_ind[BT_HFP_AG_CALL_SETUP_IND].name, ag_ind[BT_HFP_AG_CALL_SETUP_IND].min, ag_ind[BT_HFP_AG_CALL_SETUP_IND].connector, ag_ind[BT_HFP_AG_CALL_SETUP_IND].max, ag_ind[BT_HFP_AG_CALL_HELD_IND].name, ag_ind[BT_HFP_AG_CALL_HELD_IND].min, ag_ind[BT_HFP_AG_CALL_HELD_IND].connector, ag_ind[BT_HFP_AG_CALL_HELD_IND].max, ag_ind[BT_HFP_AG_SIGNAL_IND].name, ag_ind[BT_HFP_AG_SIGNAL_IND].min, ag_ind[BT_HFP_AG_SIGNAL_IND].connector, ag_ind[BT_HFP_AG_SIGNAL_IND].max, ag_ind[BT_HFP_AG_ROAM_IND].name, ag_ind[BT_HFP_AG_ROAM_IND].min, ag_ind[BT_HFP_AG_ROAM_IND].connector, ag_ind[BT_HFP_AG_ROAM_IND].max, ag_ind[BT_HFP_AG_BATTERY_IND].name, ag_ind[BT_HFP_AG_BATTERY_IND].min, ag_ind[BT_HFP_AG_BATTERY_IND].connector, ag_ind[BT_HFP_AG_BATTERY_IND].max); } else { err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+CIND:%d,%d,%d,%d,%d,%d,%d\r\n", ag->indicator_value[BT_HFP_AG_SERVICE_IND], ag->indicator_value[BT_HFP_AG_CALL_IND], ag->indicator_value[BT_HFP_AG_CALL_SETUP_IND], ag->indicator_value[BT_HFP_AG_CALL_HELD_IND], ag->indicator_value[BT_HFP_AG_SIGNAL_IND], ag->indicator_value[BT_HFP_AG_ROAM_IND], ag->indicator_value[BT_HFP_AG_BATTERY_IND]); } return err; } static void bt_hfp_ag_set_in_band_ring(struct bt_hfp_ag *ag, void *user_data) { 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"); atomic_set_bit_to(ag->flags, BT_HFP_AG_INBAND_RING, err == 0); } } static int bt_hfp_ag_cmer_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { uint32_t number; int err; static const uint32_t command_line_prefix[] = {3, 0, 0}; if (!is_char(buf, '=')) { return -ENOTSUP; } for (int i = 0; i < ARRAY_SIZE(command_line_prefix); i++) { err = get_number(buf, &number); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, ',')) { return -ENOTSUP; } if (command_line_prefix[i] != number) { return -ENOTSUP; } } err = get_number(buf, &number); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } if (number == 1) { atomic_set_bit(ag->flags, BT_HFP_AG_CMER_ENABLE); bt_hfp_ag_set_state(ag, BT_HFP_CONNECTED); err = hfp_ag_next_step(ag, bt_hfp_ag_set_in_band_ring, NULL); return err; } else if (number == 0) { atomic_clear_bit(ag->flags, BT_HFP_AG_CMER_ENABLE); } else { return -ENOTSUP; } return 0; } static int bt_hfp_ag_chld_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { return -EOPNOTSUPP; } static int bt_hfp_ag_bind_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { uint32_t indicator; uint32_t hf_indicators = 0U; int err; char *data; uint32_t len; hfp_ag_lock(ag); if (!((ag->ag_features & BT_HFP_AG_FEATURE_HF_IND) && (ag->hf_features & BT_HFP_HF_FEATURE_HF_IND))) { hfp_ag_unlock(ag); return -EOPNOTSUPP; } hfp_ag_unlock(ag); if (is_char(buf, '?')) { if (!is_char(buf, '\r')) { return -ENOTSUP; } hfp_ag_lock(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 : (sizeof(hf_indicators) * 8); for (int i = 1; i < len; i++) { if (BIT(i) & hf_indicators) { err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BIND:%d,%d\r\n", i, 1); } else { err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BIND:%d,%d\r\n", i, 0); } if (err < 0) { return err; } if (hf_indicators == 0) { break; } } return 0; } if (!is_char(buf, '=')) { return -ENOTSUP; } if (is_char(buf, '?')) { if (!is_char(buf, '\r')) { return -ENOTSUP; } data = &ag->buffer[0]; *data = '('; data++; hfp_ag_lock(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 : (sizeof(hf_indicators) * 8); for (int i = 1; (i < len) && (hf_indicators != 0); i++) { if (BIT(i) & hf_indicators) { int length = snprintk( data, (char *)&ag->buffer[HF_MAX_BUF_LEN - 1] - data - 3, "%d", i); data += length; hf_indicators &= ~BIT(i); } if (hf_indicators != 0) { *data = ','; data++; } } *data = ')'; data++; *data = '\r'; data++; *data = '\0'; data++; err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BIND:%s\r\n", &ag->buffer[0]); return err; } while (buf->len > 0) { err = get_number(buf, &indicator); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, ',')) { if (!is_char(buf, '\r')) { return -ENOTSUP; } } if (indicator < (sizeof(hf_indicators) * 8)) { hf_indicators |= BIT(indicator); } } hfp_ag_lock(ag); ag->hf_indicators_of_hf = hf_indicators; hfp_ag_unlock(ag); return 0; } static int bt_hfp_ag_cmee_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { uint32_t cmee; int err; if (!is_char(buf, '=')) { return -ENOTSUP; } err = get_number(buf, &cmee); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } if (cmee > 1) { return -ENOTSUP; } atomic_set_bit_to(ag->flags, BT_HFP_AG_CMEE_ENABLE, cmee == 1); return 0; } static int hfp_ag_update_indicator(struct bt_hfp_ag *ag, enum bt_hfp_ag_indicator index, uint8_t value, bt_hfp_ag_tx_cb_t cb, void *user_data) { int err; uint8_t old_value; hfp_ag_lock(ag); old_value = ag->indicator_value[index]; if (value == old_value) { LOG_ERR("Duplicate value setting, indicator %d, old %d -> new %d", index, old_value, value); hfp_ag_unlock(ag); return -EINVAL; } ag->indicator_value[index] = value; hfp_ag_unlock(ag); LOG_DBG("indicator %d, old %d -> new %d", index, old_value, value); err = hfp_ag_send_data(ag, cb, user_data, "\r\n+CIEV:%d,%d\r\n", (uint8_t)index + 1, value); if (err) { hfp_ag_lock(ag); ag->indicator_value[index] = old_value; hfp_ag_unlock(ag); LOG_ERR("Fail to update indicator %d, current %d", index, old_value); } return err; } static void hfp_ag_close_sco(struct bt_hfp_ag *ag) { struct bt_conn *sco; LOG_DBG(""); hfp_ag_lock(ag); sco = ag->sco_chan.sco; 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); } } static void bt_hfp_ag_reject_cb(struct bt_hfp_ag *ag, void *user_data) { hfp_ag_close_sco(ag); bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_TERMINATE); if (bt_ag && bt_ag->reject) { bt_ag->reject(ag); } } static void bt_hfp_ag_call_reject(struct bt_hfp_ag *ag, void *user_data) { int err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_reject_cb, user_data); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } } static void bt_hfp_ag_terminate_cb(struct bt_hfp_ag *ag, void *user_data) { hfp_ag_close_sco(ag); bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_TERMINATE); if (bt_ag && bt_ag->terminate) { bt_ag->terminate(ag); } } static void bt_hfp_ag_unit_call_terminate(struct bt_hfp_ag *ag, void *user_data) { int err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, user_data); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } } static int bt_hfp_ag_chup_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; bt_hfp_call_state_t call_state; if (!is_char(buf, '\r')) { return -ENOTSUP; } 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)) { return -ENOTSUP; } err = hfp_ag_next_step(ag, bt_hfp_ag_call_reject, NULL); } 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); } else { return -ENOTSUP; } return err; } static uint8_t bt_hfp_get_call_state(struct bt_hfp_ag *ag) { uint8_t status = HFP_AG_CLCC_STATUS_INVALID; hfp_ag_lock(ag); switch (ag->call_state) { case BT_HFP_CALL_TERMINATE: break; case BT_HFP_CALL_OUTGOING: status = HFP_AG_CLCC_STATUS_DIALING; break; case BT_HFP_CALL_INCOMING: status = HFP_AG_CLCC_STATUS_INCOMING; break; case BT_HFP_CALL_ALERTING: if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { status = HFP_AG_CLCC_STATUS_WAITING; } else { status = HFP_AG_CLCC_STATUS_ALERTING; } break; case BT_HFP_CALL_ACTIVE: status = HFP_AG_CLCC_STATUS_ACTIVE; break; case BT_HFP_CALL_HOLD: status = HFP_AG_CLCC_STATUS_HELD; break; default: break; } hfp_ag_unlock(ag); return status; } static int bt_hfp_ag_clcc_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; uint8_t dir; uint8_t status; uint8_t mode; uint8_t mpty; if (!is_char(buf, '\r')) { return -ENOTSUP; } hfp_ag_lock(ag); if (ag->call_state == BT_HFP_CALL_TERMINATE) { /* AG shall always send OK response to HF */ hfp_ag_unlock(ag); return 0; } hfp_ag_unlock(ag); dir = atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL) ? 1 : 0; status = bt_hfp_get_call_state(ag); mode = 0; mpty = 0; err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+CLCC:%d,%d,%d,%d,%d\r\n", 1, dir, status, mode, mpty); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } /* AG shall always send OK response to HF */ return 0; } static int bt_hfp_ag_bia_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { uint32_t number; int err; int index = 0; uint32_t indicator; if (!is_char(buf, '=')) { return -ENOTSUP; } hfp_ag_lock(ag); indicator = ag->indicator; hfp_ag_unlock(ag); while (buf->len > 0) { err = get_number(buf, &number); if (err == 0) { /* Valid number */ if (number) { indicator |= BIT(index); } else { indicator &= ~BIT(index); } } if (is_char(buf, ',')) { index++; } else { if (!is_char(buf, '\r')) { return -ENOTSUP; } if (buf->len != 0) { return -ENOTSUP; } } } /* Force call, call setup and held call indicators are enabled. */ indicator = BIT(BT_HFP_AG_CALL_IND) | BIT(BT_HFP_AG_CALL_SETUP_IND) | BIT(BT_HFP_AG_CALL_HELD_IND); hfp_ag_lock(ag); ag->indicator = indicator; hfp_ag_unlock(ag); return 0; } static void bt_hfp_ag_accept_cb(struct bt_hfp_ag *ag, void *user_data) { bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_ACTIVE); if (bt_ag && bt_ag->accept) { bt_ag->accept(ag); } } static void bt_hfp_ag_call_ringing_cb(struct bt_hfp_ag *ag, bool in_bond) { if (bt_ag && bt_ag->ringing) { bt_ag->ringing(ag, in_bond); } } 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); 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_INCOMING) { bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_ALERTING); bt_hfp_ag_call_ringing_cb(ag, true); } if ((bt_ag) && bt_ag->sco_connected) { bt_ag->sco_connected(ag, chan->sco); } } 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); bt_hfp_call_state_t call_state; if ((bt_ag) && bt_ag->sco_disconnected) { bt_ag->sco_disconnected(ag); } 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); } } static struct bt_conn *bt_hfp_ag_create_sco(struct bt_hfp_ag *ag) { struct bt_conn *sco_conn; static struct bt_sco_chan_ops ops = { .connected = hfp_ag_sco_connected, .disconnected = hfp_ag_sco_disconnected, }; LOG_DBG(""); if (ag->sco_chan.sco == NULL) { ag->sco_chan.ops = &ops; /* create SCO connection*/ sco_conn = bt_conn_create_sco(&ag->acl_conn->br.dst, &ag->sco_chan); if (sco_conn != NULL) { LOG_DBG("Created sco %p", sco_conn); bt_conn_unref(sco_conn); } } else { sco_conn = ag->sco_chan.sco; } return sco_conn; } static int hfp_ag_open_sco(struct bt_hfp_ag *ag) { bool create_sco; if (atomic_test_bit(ag->flags, BT_HFP_AG_CREATING_SCO)) { LOG_WRN("SCO connection is creating!"); return 0; } hfp_ag_lock(ag); create_sco = (ag->sco_chan.sco == NULL) ? true : false; if (create_sco) { atomic_set_bit(ag->flags, BT_HFP_AG_CREATING_SCO); } hfp_ag_unlock(ag); if (create_sco) { struct bt_conn *sco_conn = bt_hfp_ag_create_sco(ag); atomic_clear_bit(ag->flags, BT_HFP_AG_CREATING_SCO); if (sco_conn == NULL) { LOG_ERR("Fail to create sco connection!"); return -ENOTCONN; } LOG_DBG("SCO connection created (%p)", sco_conn); } return 0; } static int bt_hfp_ag_codec_select(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); hfp_ag_lock(ag); if (ag->selected_codec_id == 0) { LOG_ERR("Codec is invalid"); hfp_ag_unlock(ag); return -EINVAL; } if (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) { LOG_ERR("Codec is unsupported (codec id %d)", ag->selected_codec_id); hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+BCS:%d\r\n", ag->selected_codec_id); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } return err; } static int bt_hfp_ag_create_audio_connection(struct bt_hfp_ag *ag) { int err; uint32_t hf_codec_ids; 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); err = bt_hfp_ag_codec_select(ag); } else { err = hfp_ag_open_sco(ag); } return err; } static void bt_hfp_ag_audio_connection(struct bt_hfp_ag *ag, void *user_data) { int err; err = bt_hfp_ag_create_audio_connection(ag); if (err) { bt_hfp_ag_unit_call_terminate(ag, user_data); } } static void bt_hfp_ag_unit_call_accept(struct bt_hfp_ag *ag, void *user_data) { int err; err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 1, bt_hfp_ag_accept_cb, user_data); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_audio_connection, user_data); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } } static int bt_hfp_ag_ata_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; if (!is_char(buf, '\r')) { return -ENOTSUP; } hfp_ag_lock(ag); if (ag->call_state != BT_HFP_CALL_ALERTING) { hfp_ag_unlock(ag); return -ENOTSUP; } hfp_ag_unlock(ag); if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { return -ENOTSUP; } err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_accept, NULL); return err; } static int bt_hfp_ag_cops_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; uint32_t number; if (is_char(buf, '=')) { static const uint32_t command_line_prefix[] = {3, 0}; for (int i = 0; i < ARRAY_SIZE(command_line_prefix); i++) { err = get_number(buf, &number); if (err != 0) { return -ENOTSUP; } if (command_line_prefix[i] != number) { return -ENOTSUP; } if (!is_char(buf, ',')) { if (!is_char(buf, '\r')) { return -ENOTSUP; } } } atomic_set_bit(ag->flags, BT_HFP_AG_COPS_SET); return 0; } if (!atomic_test_bit(ag->flags, BT_HFP_AG_COPS_SET)) { return -ENOTSUP; } if (!is_char(buf, '?')) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+COPS:%d,%d,\"%s\"\r\n", 0, 0, ag->operator); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } return err; } static int bt_hfp_ag_bcc_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; if (!is_char(buf, '\r')) { return -ENOTSUP; } hfp_ag_lock(ag); if ((ag->selected_codec_id == 0) || (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) || (ag->call_state == BT_HFP_CALL_TERMINATE) || (ag->sco_chan.sco != NULL)) { hfp_ag_unlock(ag); return -ENOTSUP; } hfp_ag_unlock(ag); err = hfp_ag_next_step(ag, bt_hfp_ag_audio_connection, NULL); return 0; } static void bt_hfp_ag_unit_codec_conn_setup(struct bt_hfp_ag *ag, void *user_data) { int err = hfp_ag_open_sco(ag); if (err) { bt_hfp_ag_call_reject(ag, user_data); } } static int bt_hfp_ag_bcs_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; uint32_t number; if (!is_char(buf, '=')) { return -ENOTSUP; } err = get_number(buf, &number); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } if (!atomic_test_bit(ag->flags, BT_HFP_AG_CODEC_CONN)) { return -ESRCH; } hfp_ag_lock(ag); if (ag->selected_codec_id != number) { LOG_ERR("Received codec id %d is not aligned with selected %d", number, ag->selected_codec_id); err = -ENOTSUP; } else if (!(ag->hf_codec_ids & BIT(ag->selected_codec_id))) { LOG_ERR("Selected codec id %d is unsupported %d", ag->selected_codec_id, ag->hf_codec_ids); 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_CHANGED); if (err == 0) { err = hfp_ag_next_step(ag, bt_hfp_ag_unit_codec_conn_setup, NULL); } else { 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); } } return err; } static void bt_hfp_ag_unit_call_outgoing(struct bt_hfp_ag *ag, void *user_data) { int err = bt_hfp_ag_outgoing(ag, ag->number); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } } static int bt_hfp_ag_atd_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; char *number = NULL; bool is_memory_dial = false; size_t len; if (buf->data[buf->len - 1] != '\r') { return -ENOTSUP; } if (is_char(buf, '>')) { is_memory_dial = true; } if ((buf->len - 1) > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN) { return -ENAMETOOLONG; } buf->data[buf->len - 1] = '\0'; if (is_memory_dial) { if (bt_ag && bt_ag->memory_dial) { err = bt_ag->memory_dial(ag, &buf->data[0], &number); if ((err != 0) || (number == NULL)) { return -ENOTSUP; } } else { return -ENOTSUP; } } else { number = &buf->data[0]; } len = strlen(number); if (len == 0) { return -ENOTSUP; } if (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN) { return -ENAMETOOLONG; } hfp_ag_lock(ag); if (ag->call_state != BT_HFP_CALL_TERMINATE) { hfp_ag_unlock(ag); return -EBUSY; } /* Copy number to ag->number including null-character */ memcpy(ag->number, number, len + 1); hfp_ag_unlock(ag); err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_outgoing, NULL); return err; } static int bt_hfp_ag_bldn_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; if (!is_char(buf, '\r')) { return -ENOTSUP; } hfp_ag_lock(ag); if (strlen(ag->number) == 0) { hfp_ag_unlock(ag); return -ENOSR; } if (ag->call_state != BT_HFP_CALL_TERMINATE) { hfp_ag_unlock(ag); return -EBUSY; } hfp_ag_unlock(ag); err = hfp_ag_next_step(ag, bt_hfp_ag_unit_call_outgoing, NULL); return err; } static int bt_hfp_ag_clip_handler(struct bt_hfp_ag *ag, struct net_buf *buf) { int err; uint32_t clip; err = get_number(buf, &clip); if (err != 0) { return -ENOTSUP; } if (!is_char(buf, '\r')) { return -ENOTSUP; } if (clip > 1) { return -ENOTSUP; } atomic_set_bit_to(ag->flags, BT_HFP_AG_CLIP_ENABLE, clip == 1); return err; } static struct bt_hfp_ag_at_cmd_handler cmd_handlers[] = { {"AT+BRSF", bt_hfp_ag_brsf_handler}, {"AT+BAC", bt_hfp_ag_bac_handler}, {"AT+CIND", bt_hfp_ag_cind_handler}, {"AT+CMER", bt_hfp_ag_cmer_handler}, {"AT+CHLD", bt_hfp_ag_chld_handler}, {"AT+BIND", bt_hfp_ag_bind_handler}, {"AT+CMEE", bt_hfp_ag_cmee_handler}, {"AT+CHUP", bt_hfp_ag_chup_handler}, {"AT+CLCC", bt_hfp_ag_clcc_handler}, {"AT+BIA", bt_hfp_ag_bia_handler}, {"ATA", bt_hfp_ag_ata_handler}, {"AT+COPS", bt_hfp_ag_cops_handler}, {"AT+BCC", bt_hfp_ag_bcc_handler}, {"AT+BCS", bt_hfp_ag_bcs_handler}, {"ATD", bt_hfp_ag_atd_handler}, {"AT+BLDN", bt_hfp_ag_bldn_handler}, {"AT+CLIP", bt_hfp_ag_clip_handler}, }; static void hfp_ag_connected(struct bt_rfcomm_dlc *dlc) { struct bt_hfp_ag *ag = CONTAINER_OF(dlc, struct bt_hfp_ag, rfcomm_dlc); bt_hfp_ag_set_state(ag, BT_HFP_CONFIG); LOG_DBG("AG %p", ag); } static void hfp_ag_disconnected(struct bt_rfcomm_dlc *dlc) { struct bt_hfp_ag *ag = CONTAINER_OF(dlc, struct bt_hfp_ag, rfcomm_dlc); bt_hfp_call_state_t call_state; sys_snode_t *node; struct bt_ag_tx *tx; k_work_cancel_delayable(&ag->tx_work); hfp_ag_lock(ag); node = sys_slist_get(&ag->tx_pending); hfp_ag_unlock(ag); tx = CONTAINER_OF(node, struct bt_ag_tx, node); while (tx) { if (tx->buf && !atomic_test_and_clear_bit(ag->flags, BT_HFP_AG_TX_ONGOING)) { net_buf_unref(tx->buf); } tx->err = -ESHUTDOWN; k_fifo_put(&ag_tx_notify, tx); hfp_ag_lock(ag); node = sys_slist_get(&ag->tx_pending); hfp_ag_unlock(ag); tx = CONTAINER_OF(node, struct bt_ag_tx, node); } bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTED); hfp_ag_lock(ag); 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); } LOG_DBG("AG %p", ag); } static void hfp_ag_recv(struct bt_rfcomm_dlc *dlc, struct net_buf *buf) { struct bt_hfp_ag *ag = CONTAINER_OF(dlc, struct bt_hfp_ag, rfcomm_dlc); uint8_t *data = buf->data; uint16_t len = buf->len; enum at_cme cme_err; int err = -EOPNOTSUPP; LOG_HEXDUMP_DBG(data, len, "Received:"); for (uint32_t index = 0; index < ARRAY_SIZE(cmd_handlers); index++) { if (strlen(cmd_handlers[index].cmd) > len) { continue; } if (strncmp((char *)data, cmd_handlers[index].cmd, strlen(cmd_handlers[index].cmd)) != 0) { continue; } if (NULL != cmd_handlers[index].handler) { (void)net_buf_pull(buf, strlen(cmd_handlers[index].cmd)); err = cmd_handlers[index].handler(ag, buf); LOG_DBG("AT commander is handled (err %d)", err); break; } } if ((err != 0) && atomic_test_bit(ag->flags, BT_HFP_AG_CMEE_ENABLE)) { cme_err = bt_hfp_ag_get_cme_err(err); err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+CME ERROR:%d\r\n", (uint32_t)cme_err); } else { err = hfp_ag_send_data(ag, NULL, NULL, "\r\n%s\r\n", (err == 0) ? "OK" : "ERROR"); } if (err != 0) { LOG_ERR("HFP AG send response err :(%d)", err); } } static void bt_hfp_ag_thread(void *p1, void *p2, void *p3) { struct bt_ag_tx *tx; bt_hfp_ag_tx_cb_t cb; struct bt_hfp_ag *ag; void *user_data; bt_hfp_state_t state; int err; while (true) { tx = (struct bt_ag_tx *)k_fifo_get(&ag_tx_notify, K_FOREVER); if (tx == NULL) { continue; } cb = tx->cb; ag = tx->ag; user_data = tx->user_data; err = tx->err; bt_ag_tx_free(tx); if (err < 0) { hfp_ag_lock(ag); state = ag->state; hfp_ag_unlock(ag); if ((state != BT_HFP_DISCONNECTED) && (state != BT_HFP_DISCONNECTING)) { bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTING); bt_rfcomm_dlc_disconnect(&ag->rfcomm_dlc); } } if (cb) { cb(ag, user_data); } } } static void hfp_ag_sent(struct bt_rfcomm_dlc *dlc, int err) { struct bt_hfp_ag *ag = CONTAINER_OF(dlc, struct bt_hfp_ag, rfcomm_dlc); sys_snode_t *node; struct bt_ag_tx *tx; hfp_ag_lock(ag); /* Clear the tx ongoing flag */ if (!atomic_test_and_clear_bit(ag->flags, BT_HFP_AG_TX_ONGOING)) { LOG_WRN("tx ongoing flag is not set"); hfp_ag_unlock(ag); return; } node = sys_slist_get(&ag->tx_pending); hfp_ag_unlock(ag); if (!node) { LOG_ERR("No pending tx"); return; } tx = CONTAINER_OF(node, struct bt_ag_tx, node); LOG_ERR("Completed pending tx %p", tx); /* Restart the tx work */ k_work_reschedule(&ag->tx_work, K_NO_WAIT); tx->err = err; k_fifo_put(&ag_tx_notify, tx); } static void bt_ag_deferred_work_cb(struct bt_hfp_ag *ag, void *user_data) { int err; bt_hfp_call_state_t call_state; LOG_DBG(""); hfp_ag_lock(ag); call_state = ag->call_state; hfp_ag_unlock(ag); switch (call_state) { case BT_HFP_CALL_TERMINATE: break; case BT_HFP_CALL_ACTIVE: break; case BT_HFP_CALL_HOLD: break; case BT_HFP_CALL_OUTGOING: __fallthrough; case BT_HFP_CALL_INCOMING: __fallthrough; case BT_HFP_CALL_ALERTING: __fallthrough; default: if (ag->indicator_value[BT_HFP_AG_CALL_SETUP_IND] && ag->indicator_value[BT_HFP_AG_CALL_IND]) { err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, NULL, NULL); if (err) { LOG_ERR("Fail to send indicator"); bt_hfp_ag_terminate_cb(ag, NULL); } else { err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL); if (err) { LOG_ERR("Fail to send indicator"); bt_hfp_ag_terminate_cb(ag, NULL); } } } else if (ag->indicator_value[BT_HFP_AG_CALL_SETUP_IND]) { err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_reject_cb, NULL); if (err) { LOG_ERR("Fail to send indicator"); bt_hfp_ag_terminate_cb(ag, NULL); } } else if (ag->indicator_value[BT_HFP_AG_CALL_IND]) { err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL); if (err) { LOG_ERR("Fail to send indicator"); bt_hfp_ag_terminate_cb(ag, NULL); } } break; } } static void bt_ag_deferred_work(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct bt_hfp_ag *ag = CONTAINER_OF(dwork, struct bt_hfp_ag, deferred_work); (void)hfp_ag_next_step(ag, bt_ag_deferred_work_cb, NULL); } static void bt_ag_ringing_work_cb(struct bt_hfp_ag *ag, void *user_data) { int err; bt_hfp_call_state_t call_state; LOG_DBG(""); 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)) { return; } k_work_reschedule(&ag->ringing_work, K_SECONDS(CONFIG_BT_HFP_AG_RING_NOTIFY_INTERVAL)); err = hfp_ag_send_data(ag, NULL, NULL, "\r\nRING\r\n"); if (err) { LOG_ERR("Fail to send RING %d", err); } else { if (atomic_test_bit(ag->flags, BT_HFP_AG_CLIP_ENABLE)) { err = hfp_ag_send_data(ag, NULL, NULL, "\r\n+CLIP:\"%s\",%d\r\n", ag->number, 0); if (err) { LOG_ERR("Fail to send CLIP %d", err); } } } } } static void bt_ag_ringing_work(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct bt_hfp_ag *ag = CONTAINER_OF(dwork, struct bt_hfp_ag, ringing_work); (void)hfp_ag_next_step(ag, bt_ag_ringing_work_cb, NULL); } int bt_hfp_ag_connect(struct bt_conn *conn, struct bt_hfp_ag **ag, uint8_t channel) { int i; int err; static struct bt_rfcomm_dlc_ops ops = { .connected = hfp_ag_connected, .disconnected = hfp_ag_disconnected, .recv = hfp_ag_recv, .sent = hfp_ag_sent, }; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } *ag = NULL; if (ag_thread_id == NULL) { k_fifo_init(&ag_tx_free); k_fifo_init(&ag_tx_notify); for (i = 0; i < ARRAY_SIZE(ag_tx); i++) { k_fifo_put(&ag_tx_free, &ag_tx[i]); } ag_thread_id = k_thread_create( &ag_thread, ag_thread_stack, K_KERNEL_STACK_SIZEOF(ag_thread_stack), bt_hfp_ag_thread, NULL, NULL, NULL, K_PRIO_COOP(CONFIG_BT_HFP_AG_THREAD_PRIO), 0, K_NO_WAIT); if (ag_thread_id == NULL) { return -ENOMEM; } k_thread_name_set(ag_thread_id, "HFP AG"); } for (i = 0; i < ARRAY_SIZE(bt_hfp_ag_pool); i++) { struct bt_hfp_ag *_ag = &bt_hfp_ag_pool[i]; if (_ag->rfcomm_dlc.session) { continue; } (void)memset(_ag, 0, sizeof(struct bt_hfp_ag)); sys_slist_init(&_ag->tx_pending); k_sem_init(&_ag->lock, 1, 1); _ag->rfcomm_dlc.ops = &ops; _ag->rfcomm_dlc.mtu = BT_HFP_MAX_MTU; /* Set the supported features*/ _ag->ag_features = BT_HFP_AG_SUPPORTED_FEATURES; /* If supported codec ids cannot be notified, disable codec negotiation. */ if (!(bt_ag && bt_ag->codec)) { _ag->ag_features &= ~BT_HFP_AG_FEATURE_CODEC_NEG; } _ag->hf_features = 0; _ag->hf_codec_ids = 0; _ag->acl_conn = conn; /* Set AG indicator value */ _ag->indicator_value[BT_HFP_AG_SERVICE_IND] = 0; _ag->indicator_value[BT_HFP_AG_CALL_IND] = 0; _ag->indicator_value[BT_HFP_AG_CALL_SETUP_IND] = 0; _ag->indicator_value[BT_HFP_AG_CALL_HELD_IND] = 0; _ag->indicator_value[BT_HFP_AG_SIGNAL_IND] = 0; _ag->indicator_value[BT_HFP_AG_ROAM_IND] = 0; _ag->indicator_value[BT_HFP_AG_BATTERY_IND] = 0; /* Set AG indicator status */ _ag->indicator = BIT(BT_HFP_AG_SERVICE_IND) | BIT(BT_HFP_AG_CALL_IND) | BIT(BT_HFP_AG_CALL_SETUP_IND) | BIT(BT_HFP_AG_CALL_HELD_IND) | BIT(BT_HFP_AG_SIGNAL_IND) | BIT(BT_HFP_AG_ROAM_IND) | BIT(BT_HFP_AG_BATTERY_IND); /* Set AG operator */ memcpy(_ag->operator, "UNKNOWN", sizeof("UNKNOWN")); /* Set Codec ID*/ _ag->selected_codec_id = BT_HFP_AG_CODEC_CVSD; /* Init delay work */ k_work_init_delayable(&_ag->deferred_work, bt_ag_deferred_work); k_work_init_delayable(&_ag->ringing_work, bt_ag_ringing_work); k_work_init_delayable(&_ag->tx_work, bt_ag_tx_work); atomic_set_bit(_ag->flags, BT_HFP_AG_CODEC_CHANGED); *ag = _ag; } if (*ag == NULL) { return -ENOMEM; } err = bt_rfcomm_dlc_connect(conn, &(*ag)->rfcomm_dlc, channel); if (err != 0) { (void)memset(*ag, 0, sizeof(struct bt_hfp_ag)); *ag = NULL; } else { bt_hfp_ag_set_state(*ag, BT_HFP_CONNECTING); } return err; } int bt_hfp_ag_disconnect(struct bt_hfp_ag *ag) { int err; bt_hfp_call_state_t call_state; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); call_state = ag->call_state; hfp_ag_unlock(ag); bt_hfp_ag_set_state(ag, BT_HFP_DISCONNECTING); 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, NULL); if (err != 0) { LOG_ERR("HFP AG send response err :(%d)", err); } return err; } 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, bt_hfp_ag_reject_cb, NULL); if (err != 0) { LOG_ERR("HFP AG send response err :(%d)", err); } return err; } return bt_rfcomm_dlc_disconnect(&ag->rfcomm_dlc); } int bt_hfp_ag_register(struct bt_hfp_ag_cb *cb) { if (!cb) { return -EINVAL; } if (bt_ag) { return -EALREADY; } bt_ag = cb; return 0; } static void bt_hfp_ag_incoming_cb(struct bt_hfp_ag *ag, void *user_data) { bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_INCOMING); if (bt_ag && bt_ag->incoming) { bt_ag->incoming(ag, ag->number); } if (atomic_test_bit(ag->flags, BT_HFP_AG_INBAND_RING)) { int err; err = bt_hfp_ag_create_audio_connection(ag); if (err) { bt_hfp_ag_call_reject(ag, NULL); } } else { bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_ALERTING); bt_hfp_ag_call_ringing_cb(ag, false); } } int bt_hfp_ag_remote_incoming(struct bt_hfp_ag *ag, const char *number) { int err = 0; size_t len; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if (ag->call_state != BT_HFP_CALL_TERMINATE) { hfp_ag_unlock(ag); return -EBUSY; } hfp_ag_unlock(ag); len = strlen(number); if ((len == 0) || (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN)) { return -EINVAL; } hfp_ag_lock(ag); /* Copy number to ag->number including null-character */ memcpy(ag->number, number, len + 1); hfp_ag_unlock(ag); atomic_set_bit(ag->flags, BT_HFP_AG_INCOMING_CALL); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_INCOMING, bt_hfp_ag_incoming_cb, NULL); if (err != 0) { atomic_clear_bit(ag->flags, BT_HFP_AG_INCOMING_CALL); } return err; } int bt_hfp_ag_reject(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if ((ag->call_state != BT_HFP_CALL_ALERTING) && (ag->call_state != BT_HFP_CALL_INCOMING)) { hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { return -EINVAL; } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_reject_cb, NULL); return err; } int bt_hfp_ag_accept(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if (ag->call_state != BT_HFP_CALL_ALERTING) { hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); if (!atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { return -EINVAL; } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 1, bt_hfp_ag_accept_cb, NULL); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_audio_connection, NULL); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } return err; } int bt_hfp_ag_terminate(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if ((ag->call_state != BT_HFP_CALL_ACTIVE) && (ag->call_state != BT_HFP_CALL_HOLD)) { hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL); return err; } static void bt_hfp_ag_outgoing_cb(struct bt_hfp_ag *ag, void *user_data) { bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_OUTGOING); if (bt_ag && bt_ag->outgoing) { bt_ag->outgoing(ag, ag->number); } if (atomic_test_bit(ag->flags, BT_HFP_AG_INBAND_RING)) { int err; err = bt_hfp_ag_create_audio_connection(ag); if (err) { bt_hfp_ag_call_reject(ag, NULL); } } } int bt_hfp_ag_outgoing(struct bt_hfp_ag *ag, const char *number) { int err = 0; size_t len; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if (ag->call_state != BT_HFP_CALL_TERMINATE) { hfp_ag_unlock(ag); return -EBUSY; } hfp_ag_unlock(ag); len = strlen(number); if ((len == 0) || (len > CONFIG_BT_HFP_AG_PHONE_NUMBER_MAX_LEN)) { return -EINVAL; } hfp_ag_lock(ag); /* Copy number to ag->number including null-character */ memcpy(ag->number, number, len + 1); hfp_ag_unlock(ag); atomic_clear_bit(ag->flags, BT_HFP_AG_INCOMING_CALL); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_OUTGOING, bt_hfp_ag_outgoing_cb, NULL); return err; } static void bt_hfp_ag_ringing_cb(struct bt_hfp_ag *ag, void *user_data) { bt_hfp_ag_set_call_state(ag, BT_HFP_CALL_ALERTING); if (atomic_test_bit(ag->flags, BT_HFP_AG_INBAND_RING)) { bt_hfp_ag_call_ringing_cb(ag, true); } else { bt_hfp_ag_call_ringing_cb(ag, false); } } int bt_hfp_ag_remote_ringing(struct bt_hfp_ag *ag) { int err = 0; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if (ag->call_state != BT_HFP_CALL_OUTGOING) { hfp_ag_unlock(ag); return -EBUSY; } if (atomic_test_bit(ag->flags, BT_HFP_AG_INBAND_RING)) { if (ag->sco_chan.sco == NULL) { hfp_ag_unlock(ag); return -ENOTCONN; } } hfp_ag_unlock(ag); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_REMOTE_ALERTING, bt_hfp_ag_ringing_cb, NULL); return err; } int bt_hfp_ag_remote_reject(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if ((ag->call_state != BT_HFP_CALL_ALERTING) && (ag->call_state != BT_HFP_CALL_OUTGOING)) { hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { return -EINVAL; } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_reject_cb, NULL); return err; } int bt_hfp_ag_remote_accept(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if (ag->call_state != BT_HFP_CALL_ALERTING) { hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); if (atomic_test_bit(ag->flags, BT_HFP_AG_INCOMING_CALL)) { return -EINVAL; } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 1, bt_hfp_ag_accept_cb, NULL); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_SETUP_IND, BT_HFP_CALL_SETUP_NONE, bt_hfp_ag_audio_connection, NULL); if (err != 0) { LOG_ERR("Fail to send err :(%d)", err); } return err; } int bt_hfp_ag_remote_terminate(struct bt_hfp_ag *ag) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if ((ag->call_state != BT_HFP_CALL_ACTIVE) && (ag->call_state != BT_HFP_CALL_HOLD)) { hfp_ag_unlock(ag); return -EINVAL; } hfp_ag_unlock(ag); err = hfp_ag_update_indicator(ag, BT_HFP_AG_CALL_IND, 0, bt_hfp_ag_terminate_cb, NULL); return err; } int bt_hfp_ag_set_indicator(struct bt_hfp_ag *ag, enum bt_hfp_ag_indicator index, uint8_t value) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } hfp_ag_unlock(ag); switch (index) { case BT_HFP_AG_SERVICE_IND: __fallthrough; case BT_HFP_AG_SIGNAL_IND: __fallthrough; case BT_HFP_AG_ROAM_IND: __fallthrough; case BT_HFP_AG_BATTERY_IND: if ((ag_ind[(uint8_t)index].min > value) || (ag_ind[(uint8_t)index].max < value)) { return -EINVAL; } break; case BT_HFP_AG_CALL_IND: __fallthrough; case BT_HFP_AG_CALL_SETUP_IND: __fallthrough; case BT_HFP_AG_CALL_HELD_IND: __fallthrough; default: return -EINVAL; } err = hfp_ag_update_indicator(ag, index, value, NULL, NULL); return err; } int bt_hfp_ag_set_operator(struct bt_hfp_ag *ag, char *name) { int len; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } len = strlen(name); len = MIN(sizeof(ag->operator) - 1, len); memcpy(ag->operator, name, len); ag->operator[len] = '\0'; hfp_ag_unlock(ag); return 0; } int bt_hfp_ag_select_codec(struct bt_hfp_ag *ag, uint8_t id) { int err; LOG_DBG(""); if (ag == NULL) { return -EINVAL; } hfp_ag_lock(ag); if (ag->state != BT_HFP_CONNECTED) { hfp_ag_unlock(ag); return -ENOTCONN; } if (!(ag->hf_codec_ids && BIT(id))) { hfp_ag_unlock(ag); return -ENOTSUP; } hfp_ag_unlock(ag); if (atomic_test_bit(ag->flags, BT_HFP_AG_CODEC_CONN)) { return -EBUSY; } hfp_ag_lock(ag); ag->selected_codec_id = id; hfp_ag_unlock(ag); atomic_set_bit(ag->flags, BT_HFP_AG_CODEC_CHANGED); err = bt_hfp_ag_create_audio_connection(ag); return err; }