Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f0aa55d authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "audio: Parse USB data service interval" into pi-dev

parents 7780106b 65f6b437
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -183,6 +183,10 @@ ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SND_MONITOR)), true)
    LOCAL_SRC_FILES += audio_extn/sndmonitor.c
endif

ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USB_SERVICE_INTERVAL)), true)
    LOCAL_CFLAGS += -DUSB_SERVICE_INTERVAL_ENABLED
endif

LOCAL_SHARED_LIBRARIES += libbase libhidlbase libhwbinder libutils android.hardware.power@1.2 liblog

LOCAL_SRC_FILES += audio_perf.cpp
+8 −0
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state);
#define audio_extn_usb_get_max_bit_width(dir)                          (0)
#define audio_extn_usb_sup_sample_rates(t, s, l)        ((t), (s), (l), 0) /* fix unused warn */
#define audio_extn_usb_alive(adev)                                     (false)
#define audio_extn_usb_find_service_interval(m, p)      ((m), (p), 0) /* fix unused warn */
#define audio_extn_usb_altset_for_service_interval(p, si, bw, sr, ch) (-1)
#else
void audio_extn_usb_init(void *adev);
void audio_extn_usb_deinit();
@@ -98,6 +100,12 @@ int audio_extn_usb_get_max_channels(bool is_playback);
int audio_extn_usb_get_max_bit_width(bool is_playback);
int audio_extn_usb_sup_sample_rates(bool is_playback, uint32_t *sr, uint32_t l);
bool audio_extn_usb_alive(int card);
unsigned long audio_extn_usb_find_service_interval(bool min, bool playback);
int audio_extn_usb_altset_for_service_interval(bool is_playback,
                                               unsigned long service_interval,
                                               uint32_t *bit_width,
                                               uint32_t *sample_rate,
                                               uint32_t *channel_count);
#endif


+147 −6
Original line number Diff line number Diff line
@@ -37,10 +37,13 @@
#define CHANNEL_NUMBER_STR      "Channels: "
#define PLAYBACK_PROFILE_STR    "Playback:"
#define CAPTURE_PROFILE_STR     "Capture:"
#define DATA_PACKET_INTERVAL_STR "Data packet interval: "
#define USB_SIDETONE_GAIN_STR   "usb_sidetone_gain"
#define ABS_SUB(A, B) (((A) > (B)) ? ((A) - (B)):((B) - (A)))
#define SAMPLE_RATE_8000          8000
#define SAMPLE_RATE_11025         11025
#define DEFAULT_SERVICE_INTERVAL_US    1000

/* TODO: dynamically populate supported sample rates */
static uint32_t supported_sample_rates[] =
    {192000, 176400, 96000, 88200, 64000, 48000, 44100};
@@ -50,10 +53,10 @@ static const uint32_t MAX_SAMPLE_RATE_SIZE =

// assert on sizeof bm v/s size of rates if needed

enum usb_usecase_type{
typedef enum usb_usecase_type{
    USB_PLAYBACK = 0,
    USB_CAPTURE,
};
} usb_usecase_type_t;

enum {
    USB_SIDETONE_ENABLE_INDEX = 0,
@@ -67,6 +70,8 @@ struct usb_device_config {
    unsigned int channel_count;
    unsigned int rate_size;
    unsigned int rates[MAX_SAMPLE_RATE_SIZE];
    unsigned long service_interval_us;
    usb_usecase_type_t type;
};

struct usb_card_config {
@@ -271,6 +276,43 @@ static int usb_get_sample_rates(int type, char *rates_str,
    return 0;
}

static int usb_get_service_interval(const char *interval_str_start,
                                    struct usb_device_config *usb_device_info)
{
    unsigned long interval = 0;
    char time_unit[8] = {0};
    int multiplier = 0;
    char *eol = strchr(interval_str_start, '\n');
    if (!eol) {
        ALOGE("%s: No EOL found", __func__);
        return -1;
    }
    char *tmp = (char *)calloc(1, eol-interval_str_start+1);
    if (!tmp) {
        ALOGE("%s: failed to allocate tmp", __func__);
        return -1;
    }
    memcpy(tmp, interval_str_start, eol-interval_str_start);
    sscanf(tmp, "%lu %2s", &interval, &time_unit[0]);
    if (!strcmp(time_unit, "us")) {
        multiplier = 1;
    } else if (!strcmp(time_unit, "ms")) {
        multiplier = 1000;
    } else if (!strcmp(time_unit, "s")) {
        multiplier = 1000000;
    } else {
        ALOGE("%s: unknown time_unit %s, assume default", __func__, time_unit);
        interval = DEFAULT_SERVICE_INTERVAL_US;
        multiplier = 1;
    }
    interval *= multiplier;
    ALOGV("%s: set service_interval_us %lu", __func__, interval);
    usb_device_info->service_interval_us = interval;
    free(tmp);
    return 0;
}


static int usb_get_capability(int type,
                              struct usb_card_config *usb_card_info,
                              int card)
@@ -286,6 +328,7 @@ static int usb_get_capability(int type,
    char *target = NULL;
    char *read_buf = NULL;
    char *rates_str = NULL;
    char *interval_str_start = NULL;
    char path[128];
    int ret = 0;
    char *bit_width_str = NULL;
@@ -366,6 +409,7 @@ static int usb_get_capability(int type,
            ret = -ENOMEM;
            break;
        }
        usb_device_info->type = type;
        /* Bit bit_width parsing */
        bit_width_start = strstr(str_start, "Format: ");
        if (bit_width_start == NULL) {
@@ -443,6 +487,18 @@ static int usb_get_capability(int type,
            free(usb_device_info);
            continue;
        }
        // Data packet interval is an optional field.
        // Assume 1ms interval if this cannot be read
        usb_device_info->service_interval_us = DEFAULT_SERVICE_INTERVAL_US;
        interval_str_start = strstr(str_start, DATA_PACKET_INTERVAL_STR);
        if (interval_str_start != NULL) {
            interval_str_start += strlen(DATA_PACKET_INTERVAL_STR);
            ret = usb_get_service_interval(interval_str_start, usb_device_info);
            if (ret < 0) {
                ALOGE("%s: error unable to get service interval, assume default",
                      __func__);
            }
        }
        /* Add to list if every field is valid */
        list_add_tail(&usb_card_info->usb_device_conf_list,
                      &usb_device_info->list);
@@ -702,7 +758,9 @@ static bool usb_get_best_match_for_sample_rate(
                            unsigned int bit_width,
                            unsigned int channel_count,
                            unsigned int stream_sample_rate,
                            unsigned int *sr)
                            unsigned int *sr,
                            unsigned int service_interval,
                            bool do_service_interval_check)
{
    struct listnode *node_i;
    struct usb_device_config *dev_info;
@@ -720,7 +778,10 @@ static bool usb_get_best_match_for_sample_rate(
                 "%s: USB ch(%d)bw(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
                 __func__, dev_info->channel_count, dev_info->bit_width,
                 channel_count, bit_width, stream_sample_rate, candidate);
        if ((dev_info->bit_width != bit_width) || dev_info->channel_count != channel_count)
        if ((dev_info->bit_width != bit_width) ||
            (dev_info->channel_count != channel_count) ||
            (do_service_interval_check && (dev_info->service_interval_us !=
                                           service_interval)))
            continue;

        candidate = 0;
@@ -787,7 +848,9 @@ static bool usb_audio_backend_apply_policy(struct listnode *dev_list,
                                       *bit_width,
                                       *channel_count,
                                       *sample_rate,
                                       sample_rate);
                                       sample_rate,
                                       0 /*service int*/,
                                       false /*do service int check*/);
exit:
    ALOGV("%s: Updated sample rate per profile: bit-width(%d) rate(%d) chs(%d)",
           __func__, *bit_width, *sample_rate, *channel_count);
@@ -1110,6 +1173,84 @@ bool audio_extn_usb_alive(int card) {
    return access(path, F_OK) == 0;
}

unsigned long audio_extn_usb_find_service_interval(bool min,
                                                   bool playback) {
    struct usb_card_config *card_info;
    struct usb_device_config *dev_info;
    struct listnode *node_i;
    struct listnode *node_j;
    unsigned long interval_us = min ? ULONG_MAX : 1; // 0 is invalid
    list_for_each(node_i, &usbmod->usb_card_conf_list) {
        card_info = node_to_item(node_i, struct usb_card_config, list);
        list_for_each(node_j, &card_info->usb_device_conf_list) {
            dev_info = node_to_item(node_j, struct usb_device_config, list);
            if ((playback && (dev_info->type == USB_PLAYBACK)) ||
                (!playback && (dev_info->type == USB_CAPTURE))) {
                interval_us = min ?
                        _MIN(interval_us, dev_info->service_interval_us) :
                        _MAX(interval_us, dev_info->service_interval_us);
            }
        }
        break;
    }
    return interval_us;
}

int audio_extn_usb_altset_for_service_interval(bool playback,
                                               unsigned long service_interval,
                                               uint32_t *bit_width,
                                               uint32_t *sample_rate,
                                               uint32_t *channel_count)
{
    struct usb_card_config *card_info;
    struct usb_device_config *dev_info;
    struct listnode *node_i;
    struct listnode *node_j;
    uint32_t bw = 0;
    uint32_t ch = 0;
    uint32_t sr = 0;
    list_for_each(node_i, &usbmod->usb_card_conf_list) {
        /* Currently only apply the first playback sound card configuration */
        card_info = node_to_item(node_i, struct usb_card_config, list);
        list_for_each(node_j, &card_info->usb_device_conf_list) {
            dev_info = node_to_item(node_j, struct usb_device_config, list);
            if ((playback && dev_info->type == USB_PLAYBACK) ||
                (!playback && dev_info->type == USB_CAPTURE)) {
                if (dev_info->service_interval_us != service_interval)
                    continue;
                if (dev_info->bit_width > bw) {
                    bw = dev_info->bit_width;
                    ch = dev_info->channel_count;
                } else if (dev_info->bit_width == bw &&
                           dev_info->channel_count > ch) {
                    ch = dev_info->channel_count;
                }
            }
        }
        break;
    }
    if (bw == 0 || ch == 0)
        return -1;
    list_for_each(node_i, &usbmod->usb_card_conf_list) {
        /* Currently only apply the first playback sound card configuration */
        card_info = node_to_item(node_i, struct usb_card_config, list);
        if ((playback && usb_output_device(card_info->usb_device_type)) ||
            (!playback && usb_input_device(card_info->usb_device_type))) {
            usb_get_best_match_for_sample_rate(&card_info->usb_device_conf_list,
                                               bw, ch, sr, &sr,
                                               service_interval,
                                               true);
        }
        break;
    }
    if (sr == 0)
        return -1;
    *bit_width = bw;
    *sample_rate = sr;
    *channel_count = ch;
    return 0;
}

void audio_extn_usb_init(void *adev)
{
    if (usbmod == NULL) {
+62 −1
Original line number Diff line number Diff line
@@ -1869,6 +1869,54 @@ static int check_and_set_hdmi_channels(struct audio_device *adev,
    return 0;
}

static int check_and_set_usb_service_interval(struct audio_device *adev,
                                              struct audio_usecase *uc_info,
                                              bool min)
{
    struct listnode *node;
    struct audio_usecase *usecase;
    bool switch_usecases = false;
    bool reconfig = false;

    if ((uc_info->id != USECASE_AUDIO_PLAYBACK_MMAP) &&
        (uc_info->id != USECASE_AUDIO_PLAYBACK_ULL))
        return -1;

    /* set if the valid usecase do not already exist */
    list_for_each(node, &adev->usecase_list) {
        usecase = node_to_item(node, struct audio_usecase, list);
        if (usecase->type == PCM_PLAYBACK &&
            (audio_is_usb_out_device(usecase->devices & AUDIO_DEVICE_OUT_ALL_USB))) {
            switch (usecase->id) {
                case USECASE_AUDIO_PLAYBACK_MMAP:
                case USECASE_AUDIO_PLAYBACK_ULL:
                    // cannot reconfig while mmap/ull is present.
                    return -1;
                default:
                    switch_usecases = true;
                    break;
            }
        }
        if (switch_usecases)
            break;
    }
    /*
     * client can try to set service interval in start_output_stream
     * to min or to 0 (i.e reset) in stop_output_stream .
     */
    unsigned long service_interval =
            audio_extn_usb_find_service_interval(min, true /*playback*/);
    int ret = platform_set_usb_service_interval(adev->platform,
                                                true /*playback*/,
                                                service_interval,
                                                &reconfig);
    /* no change or not supported or no active usecases */
    if (ret || !reconfig || !switch_usecases)
        return -1;
    return 0;
#undef VALID_USECASE
}

static int stop_output_stream(struct stream_out *out)
{
    int i, ret = 0;
@@ -1901,7 +1949,6 @@ static int stop_output_stream(struct stream_out *out)
    disable_snd_device(adev, uc_info->out_snd_device);

    list_remove(&uc_info->list);
    free(uc_info);

    audio_extn_extspk_update(adev->extspk);

@@ -1916,8 +1963,17 @@ static int stop_output_stream(struct stream_out *out)
            if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER)
                select_devices(adev, usecase->id);
        }
    } else if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
        ret = check_and_set_usb_service_interval(adev, uc_info, false /*min*/);
        if (ret == 0) {
            /* default service interval was successfully updated,
               reopen USB backend with new service interval */
            check_and_route_playback_usecases(adev, uc_info, uc_info->out_snd_device);
        }
        ret = 0;
    }

    free(uc_info);
    ALOGV("%s: exit: status(%d)", __func__, ret);
    return ret;
}
@@ -1971,6 +2027,11 @@ int start_output_stream(struct stream_out *out)
    /* This must be called before adding this usecase to the list */
    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
        check_and_set_hdmi_channels(adev, out->config.channels);
    else if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
        check_and_set_usb_service_interval(adev, uc_info, true /*min*/);
        /* USB backend is not reopened immediately.
           This is eventually done as part of select_devices */
    }

    list_add_tail(&adev->usecase_list, &uc_info->list);

+16 −0
Original line number Diff line number Diff line
@@ -2512,3 +2512,19 @@ int platform_get_active_microphones(void *platform __unused,
                                    size_t *mic_count __unused) {
    return -ENOSYS;
}

int platform_set_usb_service_interval(void *platform __unused,
                                      bool playback __unused,
                                      unsigned long service_interval __unused,
                                      bool *reconfig)
{
    *reconfig = false;
    return 0;
}

int platform_set_backend_cfg(const struct audio_device* adev,
                             snd_device_t snd_device,
                             const struct audio_backend_cfg *backend_cfg)
{
    return -1;
}
Loading