/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include #include "cras_audio_area.h" #include "cras_hfp_ag_profile.h" #include "cras_hfp_iodev.h" #include "cras_hfp_info.h" #include "cras_hfp_slc.h" #include "cras_iodev.h" #include "cras_system_state.h" #include "cras_util.h" #include "utlist.h" /* Implementation of bluetooth hands-free profile iodev. * Members: * base - The cras_iodev structure base class. * device - The assciated bt_device. * slc - Handle to the HFP service level connection. * info - hfp_info taking care of SCO data read/write. * drain_complete - Flag to indicate if valid samples are drained * in no stream state. Only used for output. * filled_zeros - Number of zero data in frames have been filled * to buffer of hfp_info in no stream state. Only used for output */ struct hfp_io { struct cras_iodev base; struct cras_bt_device *device; struct hfp_slc_handle *slc; struct hfp_info *info; bool drain_complete; unsigned int filled_zeros; }; static int update_supported_formats(struct cras_iodev *iodev) { struct hfp_io *hfpio = (struct hfp_io *)iodev; free(iodev->supported_rates); iodev->supported_rates = (size_t *)malloc(2 * sizeof(size_t)); /* 16 bit, mono, 8kHz for narrowband and 16KHz for wideband */ iodev->supported_rates[0] = (hfp_slc_get_selected_codec(hfpio->slc) == HFP_CODEC_ID_MSBC) ? 16000 : 8000; iodev->supported_rates[1] = 0; free(iodev->supported_channel_counts); iodev->supported_channel_counts = (size_t *)malloc(2 * sizeof(size_t)); iodev->supported_channel_counts[0] = 1; iodev->supported_channel_counts[1] = 0; free(iodev->supported_formats); iodev->supported_formats = (snd_pcm_format_t *)malloc(2 * sizeof(snd_pcm_format_t)); iodev->supported_formats[0] = SND_PCM_FORMAT_S16_LE; iodev->supported_formats[1] = 0; return 0; } static int no_stream(struct cras_iodev *iodev, int enable) { struct hfp_io *hfpio = (struct hfp_io *)iodev; struct timespec hw_tstamp; unsigned int hw_level; unsigned int level_target; if (iodev->direction != CRAS_STREAM_OUTPUT) return 0; hw_level = iodev->frames_queued(iodev, &hw_tstamp); if (enable) { if (!hfpio->drain_complete && (hw_level <= hfpio->filled_zeros)) hfpio->drain_complete = 1; hfpio->filled_zeros += hfp_fill_output_with_zeros( hfpio->info, iodev->buffer_size); return 0; } /* Leave no stream state.*/ level_target = iodev->min_cb_level; if (hfpio->drain_complete) { hfp_force_output_level(hfpio->info, level_target); } else { unsigned int valid_samples = 0; if (hw_level > hfpio->filled_zeros) valid_samples = hw_level - hfpio->filled_zeros; level_target = MAX(level_target, valid_samples); if (level_target > hw_level) hfp_fill_output_with_zeros(hfpio->info, level_target - hw_level); else hfp_force_output_level(hfpio->info, level_target); } hfpio->drain_complete = 0; hfpio->filled_zeros = 0; return 0; } static int frames_queued(const struct cras_iodev *iodev, struct timespec *tstamp) { struct hfp_io *hfpio = (struct hfp_io *)iodev; if (!hfp_info_running(hfpio->info)) return -1; /* Do not enable timestamp mechanism on HFP device because last time * stamp might be a long time ago and it is not really useful. */ clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); return hfp_buf_queued(hfpio->info, iodev->direction); } static int output_underrun(struct cras_iodev *iodev) { /* Handle it the same way as cras_iodev_output_underrun(). */ return cras_iodev_fill_odev_zeros(iodev, iodev->min_cb_level); } static int configure_dev(struct cras_iodev *iodev) { struct hfp_io *hfpio = (struct hfp_io *)iodev; int sk, err, mtu; /* Assert format is set before opening device. */ if (iodev->format == NULL) return -EINVAL; iodev->format->format = SND_PCM_FORMAT_S16_LE; cras_iodev_init_audio_area(iodev, iodev->format->num_channels); if (hfp_info_running(hfpio->info)) goto add_dev; /* * Might require a codec negotiation before building the sco connection. */ hfp_slc_codec_connection_setup(hfpio->slc); sk = cras_bt_device_sco_connect(hfpio->device, hfp_slc_get_selected_codec(hfpio->slc)); if (sk < 0) goto error; mtu = cras_bt_device_sco_packet_size( hfpio->device, sk, hfp_slc_get_selected_codec(hfpio->slc)); /* Start hfp_info */ err = hfp_info_start(sk, mtu, hfp_slc_get_selected_codec(hfpio->slc), hfpio->info); if (err) goto error; hfpio->drain_complete = 0; hfpio->filled_zeros = 0; add_dev: hfp_info_add_iodev(hfpio->info, iodev->direction, iodev->format); hfp_set_call_status(hfpio->slc, 1); iodev->buffer_size = hfp_buf_size(hfpio->info, iodev->direction); return 0; error: syslog(LOG_ERR, "Failed to open HFP iodev"); return -1; } static int close_dev(struct cras_iodev *iodev) { struct hfp_io *hfpio = (struct hfp_io *)iodev; hfp_info_rm_iodev(hfpio->info, iodev->direction); if (hfp_info_running(hfpio->info) && !hfp_info_has_iodev(hfpio->info)) { hfp_info_stop(hfpio->info); hfp_set_call_status(hfpio->slc, 0); } cras_iodev_free_format(iodev); cras_iodev_free_audio_area(iodev); return 0; } static void set_hfp_volume(struct cras_iodev *iodev) { size_t volume; struct hfp_io *hfpio = (struct hfp_io *)iodev; volume = cras_system_get_volume(); if (iodev->active_node) volume = cras_iodev_adjust_node_volume(iodev->active_node, volume); hfp_event_speaker_gain(hfpio->slc, volume); } static int delay_frames(const struct cras_iodev *iodev) { struct timespec tstamp; return frames_queued(iodev, &tstamp); } static int get_buffer(struct cras_iodev *iodev, struct cras_audio_area **area, unsigned *frames) { struct hfp_io *hfpio = (struct hfp_io *)iodev; uint8_t *dst = NULL; if (!hfp_info_running(hfpio->info)) return -1; hfp_buf_acquire(hfpio->info, iodev->direction, &dst, frames); iodev->area->frames = *frames; /* HFP is mono only. */ iodev->area->channels[0].step_bytes = cras_get_format_bytes(iodev->format); iodev->area->channels[0].buf = dst; *area = iodev->area; return 0; } static int put_buffer(struct cras_iodev *iodev, unsigned nwritten) { struct hfp_io *hfpio = (struct hfp_io *)iodev; if (!hfp_info_running(hfpio->info)) return -1; hfp_buf_release(hfpio->info, iodev->direction, nwritten); return 0; } static int flush_buffer(struct cras_iodev *iodev) { struct hfp_io *hfpio = (struct hfp_io *)iodev; unsigned nframes; if (iodev->direction == CRAS_STREAM_INPUT) { nframes = hfp_buf_queued(hfpio->info, iodev->direction); hfp_buf_release(hfpio->info, iodev->direction, nframes); } return 0; } static void update_active_node(struct cras_iodev *iodev, unsigned node_idx, unsigned dev_enabled) { } int hfp_iodev_is_hsp(struct cras_iodev *iodev) { struct hfp_io *hfpio = (struct hfp_io *)iodev; return hfp_slc_is_hsp(hfpio->slc); } void hfp_free_resources(struct hfp_io *hfpio) { struct cras_ionode *node; node = hfpio->base.active_node; if (node) { cras_iodev_rm_node(&hfpio->base, node); free(node); } free(hfpio->base.supported_channel_counts); free(hfpio->base.supported_rates); free(hfpio->base.supported_formats); cras_iodev_free_resources(&hfpio->base); } struct cras_iodev *hfp_iodev_create(enum CRAS_STREAM_DIRECTION dir, struct cras_bt_device *device, struct hfp_slc_handle *slc, enum cras_bt_device_profile profile, struct hfp_info *info) { struct hfp_io *hfpio; struct cras_iodev *iodev; struct cras_ionode *node; const char *name; hfpio = (struct hfp_io *)calloc(1, sizeof(*hfpio)); if (!hfpio) goto error; iodev = &hfpio->base; iodev->direction = dir; hfpio->device = device; hfpio->slc = slc; /* Set iodev's name to device readable name or the address. */ name = cras_bt_device_name(device); if (!name) name = cras_bt_device_object_path(device); snprintf(iodev->info.name, sizeof(iodev->info.name), "%s", name); iodev->info.name[ARRAY_SIZE(iodev->info.name) - 1] = 0; iodev->info.stable_id = cras_bt_device_get_stable_id(device); iodev->configure_dev = configure_dev; iodev->frames_queued = frames_queued; iodev->delay_frames = delay_frames; iodev->get_buffer = get_buffer; iodev->put_buffer = put_buffer; iodev->flush_buffer = flush_buffer; iodev->no_stream = no_stream; iodev->close_dev = close_dev; iodev->update_supported_formats = update_supported_formats; iodev->update_active_node = update_active_node; iodev->set_volume = set_hfp_volume; iodev->output_underrun = output_underrun; node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; strcpy(node->name, iodev->info.name); node->plugged = 1; /* If headset mic doesn't support the wideband speech, report a * different node type so UI can set different plug priority. */ node->type = CRAS_NODE_TYPE_BLUETOOTH; if (!hfp_slc_get_wideband_speech_supported(hfpio->slc) && (dir == CRAS_STREAM_INPUT)) node->type = CRAS_NODE_TYPE_BLUETOOTH_NB_MIC; node->volume = 100; gettimeofday(&node->plugged_time, NULL); /* Prepare active node before append, so bt_io can extract correct * info from HFP iodev and node. */ cras_iodev_add_node(iodev, node); cras_iodev_set_active_node(iodev, node); cras_bt_device_append_iodev(device, iodev, profile); hfpio->info = info; /* Record max supported channels into cras_iodev_info. */ iodev->info.max_supported_channels = 1; ewma_power_disable(&iodev->ewma); return iodev; error: if (hfpio) { hfp_free_resources(hfpio); free(hfpio); } return NULL; } void hfp_iodev_destroy(struct cras_iodev *iodev) { struct hfp_io *hfpio = (struct hfp_io *)iodev; cras_bt_device_rm_iodev(hfpio->device, iodev); hfp_free_resources(hfpio); free(hfpio); }