Last missing piece to HFP HF implementation.
Lukasz Rymanowski (5):
android/handsfree-client: Add SCO to handsfree client
android/handsfree-client: Add handle incoming SCO connection
android/handsfree-client: Implement audio connect/disconnect
android/handsfree-client: Send AT+BCC to start codec negotiation
android/README: Update status of HAL HFP HF implementation
android/README | 2 +-
android/handsfree-client.c | 203 ++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 182 insertions(+), 23 deletions(-)
--
1.8.4
Hi Szymon,
On Thu, Nov 27, 2014 at 9:50 PM, Szymon Janc <[email protected]> wrote:
> Hi Łukasz,
>
> On Tuesday 25 November 2014 11:47:27 Lukasz Rymanowski wrote:
>> With this patch we start codec negotation on audio connect if both sides
>> does support it.
>>
>> This patch also moves cmd_complete_cb and codec_negotiation_supported
>> functions up in the file
>> ---
>> android/handsfree-client.c | 107
>> +++++++++++++++++++++++---------------------- 1 file changed, 54
>> insertions(+), 53 deletions(-)
>>
>> diff --git a/android/handsfree-client.c b/android/handsfree-client.c
>> index 4118b7e..d942a21 100644
>> --- a/android/handsfree-client.c
>> +++ b/android/handsfree-client.c
>> @@ -292,11 +292,63 @@ done:
>> HAL_OP_HF_CLIENT_DISCONNECT, status);
>> }
>>
>> +static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
>> + void *user_data)
>> +{
>> + struct hal_ev_hf_client_command_complete ev;
>> +
>> + DBG("");
>> + memset(&ev, 0, sizeof(ev));
>> +
>> + switch (result) {
>> + case HFP_RESULT_OK:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
>> + break;
>> + case HFP_RESULT_NO_CARRIER:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
>> + break;
>> + case HFP_RESULT_ERROR:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
>> + break;
>> + case HFP_RESULT_BUSY:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
>> + break;
>> + case HFP_RESULT_NO_ANSWER:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
>> + break;
>> + case HFP_RESULT_DELAYED:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
>> + break;
>> + case HFP_RESULT_BLACKLISTED:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
>> + break;
>> + case HFP_RESULT_CME_ERROR:
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
>> + ev.cme = cme_err;
>> + break;
>> + default:
>> + error("hf-client: Unknown error code %d", result);
>> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
>> + break;
>> + }
>> +
>> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
>> + HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
>> +}
>> +
>> +static bool codec_negotiation_supported(struct device *dev)
>> +{
>> + return (dev->features & HFP_AG_FEAT_CODEC) &&
>> + (hfp_hf_features & HFP_HF_FEAT_CODEC);
>> +}
>> +
>> static bool connect_sco(struct device *dev)
>> {
>> - /* TODO: handle codec negotiation */
>> + if (codec_negotiation_supported(dev))
>> + return hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
>> + "AT+BCC");
>
> This looks strange. It would result in calling command complete callback
> without pending AT command from framework. Shouldn't this be dedicated cb that
> would handle (unlikely, but still..) error and set audio state back to
> disconnected?
You are right. Somehow I missed that.
Also we should handle case when AG replied OK but didn't open
> SCO eg with timeout. This is to avoid getting stuck in connecting audio state.
I wouldn't be paranoid on that unless you have seen such IOT issue? If
not I would not complicate here.
\Lukasz
>
>>
>> - return bt_sco_connect(sco, &dev->bdaddr, 0);
>> + return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT);
>> }
>>
>> static void set_audio_state(struct device *dev, uint8_t state)
>> @@ -377,51 +429,6 @@ done:
>> HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
>> }
>>
>> -static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
>> - void *user_data)
>> -{
>> - struct hal_ev_hf_client_command_complete ev;
>> -
>> - DBG("");
>> -
>> - memset(&ev, 0, sizeof(ev));
>> -
>> - switch (result) {
>> - case HFP_RESULT_OK:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
>> - break;
>> - case HFP_RESULT_NO_CARRIER:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
>> - break;
>> - case HFP_RESULT_ERROR:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
>> - break;
>> - case HFP_RESULT_BUSY:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
>> - break;
>> - case HFP_RESULT_NO_ANSWER:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
>> - break;
>> - case HFP_RESULT_DELAYED:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
>> - break;
>> - case HFP_RESULT_BLACKLISTED:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
>> - break;
>> - case HFP_RESULT_CME_ERROR:
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
>> - ev.cme = cme_err;
>> - break;
>> - default:
>> - error("hf-client: Unknown error code %d", result);
>> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
>> - break;
>> - }
>> -
>> - ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
>> - HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
>> -}
>> -
>> static void handle_start_vr(const void *buf, uint16_t len)
>> {
>> struct device *dev;
>> @@ -1653,12 +1660,6 @@ static void slc_brsf_cb(struct hfp_context *context,
>> void *user_data) dev->features = feat;
>> }
>>
>> -static bool codec_negotiation_supported(struct device *dev)
>> -{
>> - return (dev->features & HFP_AG_FEAT_CODEC) &&
>> - (hfp_hf_features & HFP_HF_FEAT_CODEC);
>> -}
>> -
>> static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err,
>> void *user_data)
>> {
>
> --
> Szymon K. Janc
> [email protected]
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Łukasz,
On Tuesday 25 November 2014 11:47:26 Lukasz Rymanowski wrote:
> In this patch also set_audio_state function has been moved
One more thing: set_audio_state() was introduced in PATCH 2/5. Why not put it
in right place from beginning?
> ---
> android/handsfree-client.c | 100
> +++++++++++++++++++++++++++++++++------------ 1 file changed, 75
> insertions(+), 25 deletions(-)
>
> diff --git a/android/handsfree-client.c b/android/handsfree-client.c
> index 432b9a1..4118b7e 100644
> --- a/android/handsfree-client.c
> +++ b/android/handsfree-client.c
> @@ -292,19 +292,89 @@ done:
> HAL_OP_HF_CLIENT_DISCONNECT, status);
> }
>
> +static bool connect_sco(struct device *dev)
> +{
> + /* TODO: handle codec negotiation */
> +
> + return bt_sco_connect(sco, &dev->bdaddr, 0);
> +}
> +
> +static void set_audio_state(struct device *dev, uint8_t state)
> +{
> + struct hal_ev_hf_client_audio_state ev;
> + char address[18];
> +
> + if (dev->audio_state == state)
> + return;
> +
> + dev->audio_state = state;
> +
> + ba2str(&dev->bdaddr, address);
> + DBG("device %s audio state %u", address, state);
> +
> + bdaddr2android(&dev->bdaddr, ev.bdaddr);
> + ev.state = state;
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> + HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
> +}
> +
> static void handle_connect_audio(const void *buf, uint16_t len)
> {
> - DBG("Not Implemented");
> + const struct hal_cmd_hf_client_connect_audio *cmd = (void *) buf;
> + struct device *dev;
> + uint8_t status;
> + bdaddr_t bdaddr;
> +
> + DBG("");
> +
> + android2bdaddr(&cmd->bdaddr, &bdaddr);
> +
> + dev = find_device(&bdaddr);
> + if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED ||
> + dev->audio_state != HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
> + error("hf-client: Cannot create SCO, check SLC or audio state");
> + status = HAL_STATUS_FAILED;
> + goto done;
> + }
> +
> + if (connect_sco(dev)) {
> + status = HAL_STATUS_SUCCESS;
> + set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
> + } else {
> + status = HAL_STATUS_FAILED;
> + }
> +
> +done:
> ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> - HAL_OP_HF_CLIENT_CONNECT_AUDIO, HAL_STATUS_UNSUPPORTED);
> + HAL_OP_HF_CLIENT_CONNECT_AUDIO, status);
> }
>
> static void handle_disconnect_audio(const void *buf, uint16_t len)
> {
> - DBG("Not Implemented");
> + const struct hal_cmd_hf_client_disconnect_audio *cmd = (void *) buf;
> + struct device *dev;
> + uint8_t status;
> + bdaddr_t bdaddr;
> +
> + DBG("");
> +
> + android2bdaddr(&cmd->bdaddr, &bdaddr);
> +
> + dev = find_device(&bdaddr);
> + if (!dev ||
> + dev->audio_state == HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
> + error("hf-client: Device not found or audio not connected");
> + status = HAL_STATUS_FAILED;
> + goto done;
> + }
> +
> + bt_sco_disconnect(sco);
> + status = HAL_STATUS_SUCCESS;
> +
> +done:
> ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> - HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
> - HAL_STATUS_UNSUPPORTED);
> + HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
> }
>
> static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
> @@ -2011,26 +2081,6 @@ static void cleanup_hfp_hf(void)
> }
> }
>
> -static void set_audio_state(struct device *dev, uint8_t state)
> -{
> - struct hal_ev_hf_client_audio_state ev;
> - char address[18];
> -
> - if (dev->audio_state == state)
> - return;
> -
> - dev->audio_state = state;
> -
> - ba2str(&dev->bdaddr, address);
> - DBG("device %s audio state %u", address, state);
> -
> - bdaddr2android(&dev->bdaddr, ev.bdaddr);
> - ev.state = state;
> -
> - ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> - HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
> -}
> -
> static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
> {
> struct device *dev;
--
Szymon K. Janc
[email protected]
Hi Łukasz,
On Tuesday 25 November 2014 11:47:27 Lukasz Rymanowski wrote:
> With this patch we start codec negotation on audio connect if both sides
> does support it.
>
> This patch also moves cmd_complete_cb and codec_negotiation_supported
> functions up in the file
> ---
> android/handsfree-client.c | 107
> +++++++++++++++++++++++---------------------- 1 file changed, 54
> insertions(+), 53 deletions(-)
>
> diff --git a/android/handsfree-client.c b/android/handsfree-client.c
> index 4118b7e..d942a21 100644
> --- a/android/handsfree-client.c
> +++ b/android/handsfree-client.c
> @@ -292,11 +292,63 @@ done:
> HAL_OP_HF_CLIENT_DISCONNECT, status);
> }
>
> +static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
> + void *user_data)
> +{
> + struct hal_ev_hf_client_command_complete ev;
> +
> + DBG("");
> + memset(&ev, 0, sizeof(ev));
> +
> + switch (result) {
> + case HFP_RESULT_OK:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
> + break;
> + case HFP_RESULT_NO_CARRIER:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
> + break;
> + case HFP_RESULT_ERROR:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
> + break;
> + case HFP_RESULT_BUSY:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
> + break;
> + case HFP_RESULT_NO_ANSWER:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
> + break;
> + case HFP_RESULT_DELAYED:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
> + break;
> + case HFP_RESULT_BLACKLISTED:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
> + break;
> + case HFP_RESULT_CME_ERROR:
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
> + ev.cme = cme_err;
> + break;
> + default:
> + error("hf-client: Unknown error code %d", result);
> + ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
> + break;
> + }
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> + HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
> +}
> +
> +static bool codec_negotiation_supported(struct device *dev)
> +{
> + return (dev->features & HFP_AG_FEAT_CODEC) &&
> + (hfp_hf_features & HFP_HF_FEAT_CODEC);
> +}
> +
> static bool connect_sco(struct device *dev)
> {
> - /* TODO: handle codec negotiation */
> + if (codec_negotiation_supported(dev))
> + return hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
> + "AT+BCC");
This looks strange. It would result in calling command complete callback
without pending AT command from framework. Shouldn't this be dedicated cb that
would handle (unlikely, but still..) error and set audio state back to
disconnected? Also we should handle case when AG replied OK but didn't open
SCO eg with timeout. This is to avoid getting stuck in connecting audio state.
>
> - return bt_sco_connect(sco, &dev->bdaddr, 0);
> + return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT);
> }
>
> static void set_audio_state(struct device *dev, uint8_t state)
> @@ -377,51 +429,6 @@ done:
> HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
> }
>
> -static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
> - void *user_data)
> -{
> - struct hal_ev_hf_client_command_complete ev;
> -
> - DBG("");
> -
> - memset(&ev, 0, sizeof(ev));
> -
> - switch (result) {
> - case HFP_RESULT_OK:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
> - break;
> - case HFP_RESULT_NO_CARRIER:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
> - break;
> - case HFP_RESULT_ERROR:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
> - break;
> - case HFP_RESULT_BUSY:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
> - break;
> - case HFP_RESULT_NO_ANSWER:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
> - break;
> - case HFP_RESULT_DELAYED:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
> - break;
> - case HFP_RESULT_BLACKLISTED:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
> - break;
> - case HFP_RESULT_CME_ERROR:
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
> - ev.cme = cme_err;
> - break;
> - default:
> - error("hf-client: Unknown error code %d", result);
> - ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
> - break;
> - }
> -
> - ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> - HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
> -}
> -
> static void handle_start_vr(const void *buf, uint16_t len)
> {
> struct device *dev;
> @@ -1653,12 +1660,6 @@ static void slc_brsf_cb(struct hfp_context *context,
> void *user_data) dev->features = feat;
> }
>
> -static bool codec_negotiation_supported(struct device *dev)
> -{
> - return (dev->features & HFP_AG_FEAT_CODEC) &&
> - (hfp_hf_features & HFP_HF_FEAT_CODEC);
> -}
> -
> static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err,
> void *user_data)
> {
--
Szymon K. Janc
[email protected]
Hi Łukasz,
On Tuesday 25 November 2014 11:47:25 Lukasz Rymanowski wrote:
> ---
> android/handsfree-client.c | 93
> ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93
> insertions(+)
>
> diff --git a/android/handsfree-client.c b/android/handsfree-client.c
> index 9e67c7f..432b9a1 100644
> --- a/android/handsfree-client.c
> +++ b/android/handsfree-client.c
> @@ -116,6 +116,7 @@ struct device {
> bdaddr_t bdaddr;
> struct hfp_hf *hf;
> uint8_t state;
> + uint8_t audio_state;
>
> uint8_t negotiated_codec;
> uint32_t features;
> @@ -184,6 +185,7 @@ static struct device *device_create(const bdaddr_t
> *bdaddr)
>
> bacpy(&dev->bdaddr, bdaddr);
> dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED;
> + dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
>
> init_codecs(dev);
>
> @@ -2009,6 +2011,93 @@ static void cleanup_hfp_hf(void)
> }
> }
>
> +static void set_audio_state(struct device *dev, uint8_t state)
> +{
> + struct hal_ev_hf_client_audio_state ev;
> + char address[18];
> +
> + if (dev->audio_state == state)
> + return;
> +
> + dev->audio_state = state;
> +
> + ba2str(&dev->bdaddr, address);
> + DBG("device %s audio state %u", address, state);
> +
> + bdaddr2android(&dev->bdaddr, ev.bdaddr);
> + ev.state = state;
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> + HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
> +}
> +
> +static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
> +{
> + struct device *dev;
> +
> + DBG("");
> +
> + dev = find_device(addr);
> + if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) {
> + error("hf-client: No device or SLC not ready");
> + return false;
> + }
> +
> + set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
> +
> + if (!(codec_negotiation_supported(dev)))
Those extra brackets are not needed.
> + *voice_settings = 0;
> + else if (dev->negotiated_codec != CODEC_ID_CVSD)
> + *voice_settings = BT_VOICE_TRANSPARENT;
> + else
> + *voice_settings = BT_VOICE_CVSD_16BIT;
> +
> + return true;
> +}
> +
> +static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
> +{
> + struct device *dev;
> + uint8_t audio_state;
> +
> + DBG("SCO Status %u", status);
> +
> + /* Device shall be there, just sanity check */
> + dev = find_device(addr);
> + if (!dev) {
> + error("hf-client: There is no device?");
> + return;
> + }
> +
> + if (status != SCO_STATUS_OK) {
> + audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
> + goto done;
> + }
> +
> + if (dev->negotiated_codec == CODEC_ID_MSBC)
> + audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
> + else
> + audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED;
> +
> +done:
> + set_audio_state(dev, audio_state);
> +}
> +
> +static void disconnect_sco_cb(const bdaddr_t *addr)
> +{
> + struct device *dev;
> +
> + DBG("");
> +
> + dev = get_device(addr);
> + if (!dev) {
> + error("hf-client: No device");
> + return;
> + }
> +
> + set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
> +}
> +
> bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
> {
> DBG("");
> @@ -2030,6 +2119,10 @@ bool bt_hf_client_register(struct ipc *ipc, const
> bdaddr_t *addr) goto failed;
> }
>
> + bt_sco_set_confirm_cb(sco, confirm_sco_cb);
> + bt_sco_set_connect_cb(sco, connect_sco_cb);
> + bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
> +
> hal_ipc = ipc;
> ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
> G_N_ELEMENTS(cmd_handlers));
--
Szymon K. Janc
[email protected]
Hi Łukasz,
On Tuesday 25 November 2014 11:47:28 Lukasz Rymanowski wrote:
> ---
> android/README | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/android/README b/android/README
> index b1ac886..760c2ec 100644
> --- a/android/README
> +++ b/android/README
> @@ -276,7 +276,7 @@ health bt_hl.h complete
> pan bt_pan.h complete
> avrcp bt_rc.h complete
> socket bt_sock.h complete
> -hf_client bt_hf_client.h initial
> +hf_client bt_hf_client.h complete
> map_client bt_mce.h complete
Since I've updated documentation with L information this patch would need to
be rebased.
--
Szymon K. Janc
[email protected]
On Tue, Nov 25, 2014 at 11:47 AM, Lukasz Rymanowski
<[email protected]> wrote:
> Last missing piece to HFP HF implementation.
>
> Lukasz Rymanowski (5):
> android/handsfree-client: Add SCO to handsfree client
> android/handsfree-client: Add handle incoming SCO connection
> android/handsfree-client: Implement audio connect/disconnect
> android/handsfree-client: Send AT+BCC to start codec negotiation
> android/README: Update status of HAL HFP HF implementation
>
ping
> android/README | 2 +-
> android/handsfree-client.c | 203 ++++++++++++++++++++++++++++++++++++++++-----
> 2 files changed, 182 insertions(+), 23 deletions(-)
>
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi,
On Tue, Nov 25, 2014 at 11:47 AM, Lukasz Rymanowski
<[email protected]> wrote:
> ---
> android/handsfree-client.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 93 insertions(+)
>
> diff --git a/android/handsfree-client.c b/android/handsfree-client.c
> index 9e67c7f..432b9a1 100644
> --- a/android/handsfree-client.c
> +++ b/android/handsfree-client.c
> @@ -116,6 +116,7 @@ struct device {
> bdaddr_t bdaddr;
> struct hfp_hf *hf;
> uint8_t state;
> + uint8_t audio_state;
>
> uint8_t negotiated_codec;
> uint32_t features;
> @@ -184,6 +185,7 @@ static struct device *device_create(const bdaddr_t *bdaddr)
>
> bacpy(&dev->bdaddr, bdaddr);
> dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED;
> + dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
>
> init_codecs(dev);
>
> @@ -2009,6 +2011,93 @@ static void cleanup_hfp_hf(void)
> }
> }
>
> +static void set_audio_state(struct device *dev, uint8_t state)
> +{
> + struct hal_ev_hf_client_audio_state ev;
> + char address[18];
> +
> + if (dev->audio_state == state)
> + return;
> +
> + dev->audio_state = state;
> +
> + ba2str(&dev->bdaddr, address);
> + DBG("device %s audio state %u", address, state);
> +
> + bdaddr2android(&dev->bdaddr, ev.bdaddr);
> + ev.state = state;
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
> + HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
> +}
> +
> +static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
> +{
> + struct device *dev;
> +
> + DBG("");
> +
> + dev = find_device(addr);
> + if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) {
> + error("hf-client: No device or SLC not ready");
> + return false;
> + }
> +
> + set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
> +
> + if (!(codec_negotiation_supported(dev)))
> + *voice_settings = 0;
> + else if (dev->negotiated_codec != CODEC_ID_CVSD)
> + *voice_settings = BT_VOICE_TRANSPARENT;
> + else
> + *voice_settings = BT_VOICE_CVSD_16BIT;
> +
> + return true;
> +}
> +
> +static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
> +{
> + struct device *dev;
> + uint8_t audio_state;
> +
> + DBG("SCO Status %u", status);
> +
> + /* Device shall be there, just sanity check */
> + dev = find_device(addr);
> + if (!dev) {
> + error("hf-client: There is no device?");
> + return;
> + }
> +
> + if (status != SCO_STATUS_OK) {
> + audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
> + goto done;
> + }
> +
> + if (dev->negotiated_codec == CODEC_ID_MSBC)
> + audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
> + else
> + audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED;
> +
> +done:
> + set_audio_state(dev, audio_state);
> +}
> +
> +static void disconnect_sco_cb(const bdaddr_t *addr)
> +{
> + struct device *dev;
> +
> + DBG("");
> +
> + dev = get_device(addr);
Just noticed that It should be find_device() here. Will fix it in v2
but I'm waiting for other comments.
\Lukasz
> + if (!dev) {
> + error("hf-client: No device");
> + return;
> + }
> +
> + set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
> +}
> +
> bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
> {
> DBG("");
> @@ -2030,6 +2119,10 @@ bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
> goto failed;
> }
>
> + bt_sco_set_confirm_cb(sco, confirm_sco_cb);
> + bt_sco_set_connect_cb(sco, connect_sco_cb);
> + bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
> +
> hal_ipc = ipc;
> ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
> G_N_ELEMENTS(cmd_handlers));
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
---
android/handsfree-client.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/android/handsfree-client.c b/android/handsfree-client.c
index 283519f..9e67c7f 100644
--- a/android/handsfree-client.c
+++ b/android/handsfree-client.c
@@ -47,6 +47,7 @@
#include "bluetooth.h"
#include "hal-msg.h"
#include "handsfree-client.h"
+#include "sco.h"
#define HFP_HF_CHANNEL 7
@@ -139,6 +140,8 @@ static uint32_t hfp_hf_record_id = 0;
static struct queue *devices = NULL;
static GIOChannel *hfp_hf_server = NULL;
+static struct bt_sco *sco = NULL;
+
static struct device *find_default_device(void)
{
return queue_peek_head(devices);
@@ -1999,6 +2002,11 @@ static void cleanup_hfp_hf(void)
bt_adapter_remove_record(hfp_hf_record_id);
hfp_hf_record_id = 0;
}
+
+ if (sco) {
+ bt_sco_unref(sco);
+ sco = NULL;
+ }
}
bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
@@ -2016,6 +2024,12 @@ bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
if (!enable_hf_client())
goto failed;
+ sco = bt_sco_new(addr);
+ if (!sco) {
+ error("hf-client: Cannot create SCO. HFP AG is in use ?");
+ goto failed;
+ }
+
hal_ipc = ipc;
ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));
@@ -2023,6 +2037,7 @@ bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
return true;
failed:
+ cleanup_hfp_hf();
queue_destroy(devices, free);
devices = NULL;
--
1.8.4
---
android/handsfree-client.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+)
diff --git a/android/handsfree-client.c b/android/handsfree-client.c
index 9e67c7f..432b9a1 100644
--- a/android/handsfree-client.c
+++ b/android/handsfree-client.c
@@ -116,6 +116,7 @@ struct device {
bdaddr_t bdaddr;
struct hfp_hf *hf;
uint8_t state;
+ uint8_t audio_state;
uint8_t negotiated_codec;
uint32_t features;
@@ -184,6 +185,7 @@ static struct device *device_create(const bdaddr_t *bdaddr)
bacpy(&dev->bdaddr, bdaddr);
dev->state = HAL_HF_CLIENT_CONN_STATE_DISCONNECTED;
+ dev->audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
init_codecs(dev);
@@ -2009,6 +2011,93 @@ static void cleanup_hfp_hf(void)
}
}
+static void set_audio_state(struct device *dev, uint8_t state)
+{
+ struct hal_ev_hf_client_audio_state ev;
+ char address[18];
+
+ if (dev->audio_state == state)
+ return;
+
+ dev->audio_state = state;
+
+ ba2str(&dev->bdaddr, address);
+ DBG("device %s audio state %u", address, state);
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
+{
+ struct device *dev;
+
+ DBG("");
+
+ dev = find_device(addr);
+ if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED) {
+ error("hf-client: No device or SLC not ready");
+ return false;
+ }
+
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+
+ if (!(codec_negotiation_supported(dev)))
+ *voice_settings = 0;
+ else if (dev->negotiated_codec != CODEC_ID_CVSD)
+ *voice_settings = BT_VOICE_TRANSPARENT;
+ else
+ *voice_settings = BT_VOICE_CVSD_16BIT;
+
+ return true;
+}
+
+static void connect_sco_cb(enum sco_status status, const bdaddr_t *addr)
+{
+ struct device *dev;
+ uint8_t audio_state;
+
+ DBG("SCO Status %u", status);
+
+ /* Device shall be there, just sanity check */
+ dev = find_device(addr);
+ if (!dev) {
+ error("hf-client: There is no device?");
+ return;
+ }
+
+ if (status != SCO_STATUS_OK) {
+ audio_state = HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED;
+ goto done;
+ }
+
+ if (dev->negotiated_codec == CODEC_ID_MSBC)
+ audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC;
+ else
+ audio_state = HAL_HF_CLIENT_AUDIO_STATE_CONNECTED;
+
+done:
+ set_audio_state(dev, audio_state);
+}
+
+static void disconnect_sco_cb(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ DBG("");
+
+ dev = get_device(addr);
+ if (!dev) {
+ error("hf-client: No device");
+ return;
+ }
+
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED);
+}
+
bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
{
DBG("");
@@ -2030,6 +2119,10 @@ bool bt_hf_client_register(struct ipc *ipc, const bdaddr_t *addr)
goto failed;
}
+ bt_sco_set_confirm_cb(sco, confirm_sco_cb);
+ bt_sco_set_connect_cb(sco, connect_sco_cb);
+ bt_sco_set_disconnect_cb(sco, disconnect_sco_cb);
+
hal_ipc = ipc;
ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));
--
1.8.4
With this patch we start codec negotation on audio connect if both sides
does support it.
This patch also moves cmd_complete_cb and codec_negotiation_supported
functions up in the file
---
android/handsfree-client.c | 107 +++++++++++++++++++++++----------------------
1 file changed, 54 insertions(+), 53 deletions(-)
diff --git a/android/handsfree-client.c b/android/handsfree-client.c
index 4118b7e..d942a21 100644
--- a/android/handsfree-client.c
+++ b/android/handsfree-client.c
@@ -292,11 +292,63 @@ done:
HAL_OP_HF_CLIENT_DISCONNECT, status);
}
+static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
+ void *user_data)
+{
+ struct hal_ev_hf_client_command_complete ev;
+
+ DBG("");
+ memset(&ev, 0, sizeof(ev));
+
+ switch (result) {
+ case HFP_RESULT_OK:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
+ break;
+ case HFP_RESULT_NO_CARRIER:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
+ break;
+ case HFP_RESULT_ERROR:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+ break;
+ case HFP_RESULT_BUSY:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
+ break;
+ case HFP_RESULT_NO_ANSWER:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
+ break;
+ case HFP_RESULT_DELAYED:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
+ break;
+ case HFP_RESULT_BLACKLISTED:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
+ break;
+ case HFP_RESULT_CME_ERROR:
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
+ ev.cme = cme_err;
+ break;
+ default:
+ error("hf-client: Unknown error code %d", result);
+ ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
+ break;
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
+}
+
+static bool codec_negotiation_supported(struct device *dev)
+{
+ return (dev->features & HFP_AG_FEAT_CODEC) &&
+ (hfp_hf_features & HFP_HF_FEAT_CODEC);
+}
+
static bool connect_sco(struct device *dev)
{
- /* TODO: handle codec negotiation */
+ if (codec_negotiation_supported(dev))
+ return hfp_hf_send_command(dev->hf, cmd_complete_cb, NULL,
+ "AT+BCC");
- return bt_sco_connect(sco, &dev->bdaddr, 0);
+ return bt_sco_connect(sco, &dev->bdaddr, BT_VOICE_CVSD_16BIT);
}
static void set_audio_state(struct device *dev, uint8_t state)
@@ -377,51 +429,6 @@ done:
HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
}
-static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
- void *user_data)
-{
- struct hal_ev_hf_client_command_complete ev;
-
- DBG("");
-
- memset(&ev, 0, sizeof(ev));
-
- switch (result) {
- case HFP_RESULT_OK:
- ev.type = HAL_HF_CLIENT_CMD_COMP_OK;
- break;
- case HFP_RESULT_NO_CARRIER:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_CARRIER;
- break;
- case HFP_RESULT_ERROR:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
- break;
- case HFP_RESULT_BUSY:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BUSY;
- break;
- case HFP_RESULT_NO_ANSWER:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_NO_ANSWER;
- break;
- case HFP_RESULT_DELAYED:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_DELAYED;
- break;
- case HFP_RESULT_BLACKLISTED:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_BACKLISTED;
- break;
- case HFP_RESULT_CME_ERROR:
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR_CME;
- ev.cme = cme_err;
- break;
- default:
- error("hf-client: Unknown error code %d", result);
- ev.type = HAL_HF_CLIENT_CMD_COMP_ERR;
- break;
- }
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
- HAL_EV_CLIENT_COMMAND_COMPLETE, sizeof(ev), &ev);
-}
-
static void handle_start_vr(const void *buf, uint16_t len)
{
struct device *dev;
@@ -1653,12 +1660,6 @@ static void slc_brsf_cb(struct hfp_context *context, void *user_data)
dev->features = feat;
}
-static bool codec_negotiation_supported(struct device *dev)
-{
- return (dev->features & HFP_AG_FEAT_CODEC) &&
- (hfp_hf_features & HFP_HF_FEAT_CODEC);
-}
-
static void slc_brsf_resp(enum hfp_result result, enum hfp_error cme_err,
void *user_data)
{
--
1.8.4
---
android/README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/android/README b/android/README
index b1ac886..760c2ec 100644
--- a/android/README
+++ b/android/README
@@ -276,7 +276,7 @@ health bt_hl.h complete
pan bt_pan.h complete
avrcp bt_rc.h complete
socket bt_sock.h complete
-hf_client bt_hf_client.h initial
+hf_client bt_hf_client.h complete
map_client bt_mce.h complete
--
1.8.4
In this patch also set_audio_state function has been moved
---
android/handsfree-client.c | 100 +++++++++++++++++++++++++++++++++------------
1 file changed, 75 insertions(+), 25 deletions(-)
diff --git a/android/handsfree-client.c b/android/handsfree-client.c
index 432b9a1..4118b7e 100644
--- a/android/handsfree-client.c
+++ b/android/handsfree-client.c
@@ -292,19 +292,89 @@ done:
HAL_OP_HF_CLIENT_DISCONNECT, status);
}
+static bool connect_sco(struct device *dev)
+{
+ /* TODO: handle codec negotiation */
+
+ return bt_sco_connect(sco, &dev->bdaddr, 0);
+}
+
+static void set_audio_state(struct device *dev, uint8_t state)
+{
+ struct hal_ev_hf_client_audio_state ev;
+ char address[18];
+
+ if (dev->audio_state == state)
+ return;
+
+ dev->audio_state = state;
+
+ ba2str(&dev->bdaddr, address);
+ DBG("device %s audio state %u", address, state);
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
+ HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
+}
+
static void handle_connect_audio(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct hal_cmd_hf_client_connect_audio *cmd = (void *) buf;
+ struct device *dev;
+ uint8_t status;
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev || dev->state != HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED ||
+ dev->audio_state != HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+ error("hf-client: Cannot create SCO, check SLC or audio state");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (connect_sco(dev)) {
+ status = HAL_STATUS_SUCCESS;
+ set_audio_state(dev, HAL_HF_CLIENT_AUDIO_STATE_CONNECTING);
+ } else {
+ status = HAL_STATUS_FAILED;
+ }
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
- HAL_OP_HF_CLIENT_CONNECT_AUDIO, HAL_STATUS_UNSUPPORTED);
+ HAL_OP_HF_CLIENT_CONNECT_AUDIO, status);
}
static void handle_disconnect_audio(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct hal_cmd_hf_client_disconnect_audio *cmd = (void *) buf;
+ struct device *dev;
+ uint8_t status;
+ bdaddr_t bdaddr;
+
+ DBG("");
+
+ android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+ dev = find_device(&bdaddr);
+ if (!dev ||
+ dev->audio_state == HAL_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
+ error("hf-client: Device not found or audio not connected");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ bt_sco_disconnect(sco);
+ status = HAL_STATUS_SUCCESS;
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
- HAL_OP_HF_CLIENT_DISCONNECT_AUDIO,
- HAL_STATUS_UNSUPPORTED);
+ HAL_OP_HF_CLIENT_DISCONNECT_AUDIO, status);
}
static void cmd_complete_cb(enum hfp_result result, enum hfp_error cme_err,
@@ -2011,26 +2081,6 @@ static void cleanup_hfp_hf(void)
}
}
-static void set_audio_state(struct device *dev, uint8_t state)
-{
- struct hal_ev_hf_client_audio_state ev;
- char address[18];
-
- if (dev->audio_state == state)
- return;
-
- dev->audio_state = state;
-
- ba2str(&dev->bdaddr, address);
- DBG("device %s audio state %u", address, state);
-
- bdaddr2android(&dev->bdaddr, ev.bdaddr);
- ev.state = state;
-
- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE_CLIENT,
- HAL_EV_HF_CLIENT_AUDIO_STATE, sizeof(ev), &ev);
-}
-
static bool confirm_sco_cb(const bdaddr_t *addr, uint16_t *voice_settings)
{
struct device *dev;
--
1.8.4