198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Copyright (C) 2014 The Android Open Source Project
398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean *
498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Licensed under the Apache License, Version 2.0 (the "License");
598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * you may not use this file except in compliance with the License.
698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * You may obtain a copy of the License at
798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean *
898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean *      http://www.apache.org/licenses/LICENSE-2.0
998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean *
1098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Unless required by applicable law or agreed to in writing, software
1198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * distributed under the License is distributed on an "AS IS" BASIS,
1298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * See the License for the specific language governing permissions and
1498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * limitations under the License.
1598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
1698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
1798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#define LOG_TAG "alsa_device_proxy"
182a3925e95a9d4954be789dc4594233d4cc2d251cPaul Mclean/*#define LOG_NDEBUG 0*/
1998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*#define LOG_PCM_PARAMS 0*/
2098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#include <log/log.h>
2298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#include <errno.h>
2498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2582d945007ff47d7e7fa9390b8a8afe8620c1fc5eTri Vo#include <stdio.h>
2682d945007ff47d7e7fa9390b8a8afe8620c1fc5eTri Vo
2798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#include "include/alsa_device_proxy.h"
2898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#include "include/alsa_logging.h"
3098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
3198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#define DEFAULT_PERIOD_SIZE     1024
3298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#define DEFAULT_PERIOD_COUNT    2
3398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
348785fe142d519d8e011eecdc340a638978fb9272Andy Hung#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
358785fe142d519d8e011eecdc340a638978fb9272Andy Hung
368785fe142d519d8e011eecdc340a638978fb9272Andy Hungstatic const unsigned format_byte_size_map[] = {
378785fe142d519d8e011eecdc340a638978fb9272Andy Hung    2, /* PCM_FORMAT_S16_LE */
388785fe142d519d8e011eecdc340a638978fb9272Andy Hung    4, /* PCM_FORMAT_S32_LE */
398785fe142d519d8e011eecdc340a638978fb9272Andy Hung    1, /* PCM_FORMAT_S8 */
408785fe142d519d8e011eecdc340a638978fb9272Andy Hung    4, /* PCM_FORMAT_S24_LE */
418785fe142d519d8e011eecdc340a638978fb9272Andy Hung    3, /* PCM_FORMAT_S24_3LE */
428785fe142d519d8e011eecdc340a638978fb9272Andy Hung};
438785fe142d519d8e011eecdc340a638978fb9272Andy Hung
4449bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurentint proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile,
4598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean                   struct pcm_config * config)
4698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
4749bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent    int ret = 0;
4849bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent
4998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device);
502a3925e95a9d4954be789dc4594233d4cc2d251cPaul Mclean
5198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->profile = profile;
5298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
5398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#ifdef LOG_PCM_PARAMS
5498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    log_pcm_config(config, "proxy_setup()");
5598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#endif
5698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
57a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) {
58a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.format = config->format;
59a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    } else {
6049bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        proxy->alsa_config.format = profile->default_config.format;
61a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        ALOGW("Invalid format %d - using default %d.",
62a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean              config->format, profile->default_config.format);
6349bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        // Indicate override when default format was not requested
6449bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        if (config->format != PCM_FORMAT_INVALID) {
6549bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent            ret = -EINVAL;
6649bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        }
67a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    }
68a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean
69a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) {
70a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.rate = config->rate;
71a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    } else {
7249bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        proxy->alsa_config.rate = profile->default_config.rate;
73a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        ALOGW("Invalid sample rate %u - using default %u.",
74a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean              config->rate, profile->default_config.rate);
7549bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        // Indicate override when default rate was not requested
7649bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        if (config->rate != 0) {
7749bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent            ret = -EINVAL;
7849bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        }
79a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    }
80a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean
81a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) {
82a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.channels = config->channels;
83a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    } else {
84eafa18a241f1e1f82f8586d7686f16470506640ePaul McLean        proxy->alsa_config.channels = profile_get_closest_channel_count(profile, config->channels);
85eafa18a241f1e1f82f8586d7686f16470506640ePaul McLean        ALOGW("Invalid channel count %u - using closest %u.",
86eafa18a241f1e1f82f8586d7686f16470506640ePaul McLean              config->channels, proxy->alsa_config.channels);
8749bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        // Indicate override when default channel count was not requested
8849bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        if (config->channels != 0) {
8949bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent            ret = -EINVAL;
9049bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        }
91a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    }
9298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
9398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->alsa_config.period_count = profile->default_config.period_count;
9498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->alsa_config.period_size =
9598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean            profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
9698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
9798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    // Hack for USB accessory audio.
9898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    // Here we set the correct value for period_count if tinyalsa fails to get it from the
9998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    // f_audio_source driver.
10098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (proxy->alsa_config.period_count == 0) {
10198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        proxy->alsa_config.period_count = 4;
10298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
10398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
10498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->pcm = NULL;
1058785fe142d519d8e011eecdc340a638978fb9272Andy Hung    // config format should be checked earlier against profile.
1068785fe142d519d8e011eecdc340a638978fb9272Andy Hung    if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
1078785fe142d519d8e011eecdc340a638978fb9272Andy Hung        proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
1088785fe142d519d8e011eecdc340a638978fb9272Andy Hung    } else {
1098785fe142d519d8e011eecdc340a638978fb9272Andy Hung        proxy->frame_size = 1;
1108785fe142d519d8e011eecdc340a638978fb9272Andy Hung    }
111948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
112948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    // let's check to make sure we can ACTUALLY use the maximum rate (with the channel count)
113948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    // Note that profile->sample_rates is sorted highest to lowest, so the scan will get
114948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    // us the highest working rate
115948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    int max_rate_index = proxy_scan_rates(proxy, profile->sample_rates);
116948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    if (max_rate_index >= 0) {
11749bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        if (proxy->alsa_config.rate > profile->sample_rates[max_rate_index]) {
11849bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent            ALOGW("Limiting samplnig rate from %u to %u.",
11949bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent                  proxy->alsa_config.rate, profile->sample_rates[max_rate_index]);
12049bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent            proxy->alsa_config.rate = profile->sample_rates[max_rate_index];
12149bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent            ret = -EINVAL;
12249bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent        }
123948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    }
12449bc03cfdc9f748c53bd989a3aaf4cd7ec4f4692Eric Laurent    return ret;
12598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
12698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
12798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanint proxy_open(alsa_device_proxy * proxy)
12898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
12998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    alsa_device_profile* profile = proxy->profile;
13098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
13198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean          profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
13298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
13398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (profile->card < 0 || profile->device < 0) {
13498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        return -EINVAL;
13598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
13698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
1378785fe142d519d8e011eecdc340a638978fb9272Andy Hung    proxy->pcm = pcm_open(profile->card, profile->device,
1388785fe142d519d8e011eecdc340a638978fb9272Andy Hung            profile->direction | PCM_MONOTONIC, &proxy->alsa_config);
13998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (proxy->pcm == NULL) {
14098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        return -ENOMEM;
14198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
14298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
14398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (!pcm_is_ready(proxy->pcm)) {
144948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        ALOGE("  proxy_open() pcm_is_ready() failed: %s", pcm_get_error(proxy->pcm));
14598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#if defined(LOG_PCM_PARAMS)
14698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        log_pcm_config(&proxy->alsa_config, "config");
14798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#endif
14898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        pcm_close(proxy->pcm);
14998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        proxy->pcm = NULL;
15098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        return -ENOMEM;
15198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
15298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
15398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return 0;
15498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
15598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
15698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanvoid proxy_close(alsa_device_proxy * proxy)
15798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
15898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    ALOGV("proxy_close() [pcm:%p]", proxy->pcm);
15998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
16098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (proxy->pcm != NULL) {
16198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        pcm_close(proxy->pcm);
16298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        proxy->pcm = NULL;
16398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
16498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
16598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
16698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
16798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Sample Rate
16898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
16998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
17098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
17198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.rate;
17298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
17398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
17498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
17598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Format
17698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
17798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanenum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
17898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
17998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.format;
18098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
18198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
18298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
18398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Channel Count
18498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
18598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
18698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
18798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.channels;
18898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
18998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
19098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
19198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Other
19298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
19398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
19498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
19598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.period_size;
19698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
19798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
19898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
19998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
20098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.period_count;
20198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
20298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
20398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned proxy_get_latency(const alsa_device_proxy * proxy)
20498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
20598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
20698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean               / proxy_get_sample_rate(proxy);
20798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
20898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2098785fe142d519d8e011eecdc340a638978fb9272Andy Hungint proxy_get_presentation_position(const alsa_device_proxy * proxy,
2108785fe142d519d8e011eecdc340a638978fb9272Andy Hung        uint64_t *frames, struct timespec *timestamp)
2118785fe142d519d8e011eecdc340a638978fb9272Andy Hung{
2128785fe142d519d8e011eecdc340a638978fb9272Andy Hung    int ret = -EPERM; // -1
2138785fe142d519d8e011eecdc340a638978fb9272Andy Hung    unsigned int avail;
2148785fe142d519d8e011eecdc340a638978fb9272Andy Hung    if (proxy->pcm != NULL
2158785fe142d519d8e011eecdc340a638978fb9272Andy Hung            && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) {
2168785fe142d519d8e011eecdc340a638978fb9272Andy Hung        const size_t kernel_buffer_size =
2178785fe142d519d8e011eecdc340a638978fb9272Andy Hung                proxy->alsa_config.period_size * proxy->alsa_config.period_count;
2188785fe142d519d8e011eecdc340a638978fb9272Andy Hung        if (avail > kernel_buffer_size) {
2198785fe142d519d8e011eecdc340a638978fb9272Andy Hung            ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size);
2208785fe142d519d8e011eecdc340a638978fb9272Andy Hung        } else {
2218785fe142d519d8e011eecdc340a638978fb9272Andy Hung            int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
2228785fe142d519d8e011eecdc340a638978fb9272Andy Hung            // It is possible to compensate for additional driver and device delay
2238785fe142d519d8e011eecdc340a638978fb9272Andy Hung            // by changing signed_frames.  Example:
2248785fe142d519d8e011eecdc340a638978fb9272Andy Hung            // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
2258785fe142d519d8e011eecdc340a638978fb9272Andy Hung            if (signed_frames >= 0) {
2268785fe142d519d8e011eecdc340a638978fb9272Andy Hung                *frames = signed_frames;
2278785fe142d519d8e011eecdc340a638978fb9272Andy Hung                ret = 0;
2288785fe142d519d8e011eecdc340a638978fb9272Andy Hung            }
2298785fe142d519d8e011eecdc340a638978fb9272Andy Hung        }
2308785fe142d519d8e011eecdc340a638978fb9272Andy Hung    }
2318785fe142d519d8e011eecdc340a638978fb9272Andy Hung    return ret;
2328785fe142d519d8e011eecdc340a638978fb9272Andy Hung}
2338785fe142d519d8e011eecdc340a638978fb9272Andy Hung
23498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
23598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * I/O
23698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
2378785fe142d519d8e011eecdc340a638978fb9272Andy Hungint proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
23898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
2398785fe142d519d8e011eecdc340a638978fb9272Andy Hung    int ret = pcm_write(proxy->pcm, data, count);
2408785fe142d519d8e011eecdc340a638978fb9272Andy Hung    if (ret == 0) {
2418785fe142d519d8e011eecdc340a638978fb9272Andy Hung        proxy->transferred += count / proxy->frame_size;
2428785fe142d519d8e011eecdc340a638978fb9272Andy Hung    }
2438785fe142d519d8e011eecdc340a638978fb9272Andy Hung    return ret;
24498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
24598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
24698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanint proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
24798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
24898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return pcm_read(proxy->pcm, data, count);
24998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
25021b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean
25121b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean/*
25221b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean * Debugging
25321b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean */
25421b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLeanvoid proxy_dump(const alsa_device_proxy* proxy, int fd)
25521b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean{
25621b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean    if (proxy != NULL) {
25721b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean        dprintf(fd, "  channels: %d\n", proxy->alsa_config.channels);
25821b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean        dprintf(fd, "  rate: %d\n", proxy->alsa_config.rate);
25921b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean        dprintf(fd, "  period_size: %d\n", proxy->alsa_config.period_size);
26021b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean        dprintf(fd, "  period_count: %d\n", proxy->alsa_config.period_count);
26121b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean        dprintf(fd, "  format: %d\n", proxy->alsa_config.format);
26221b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean    }
26321b04ade57e5cbf33eff11f3f400f77eed5e2e75Paul McLean}
264948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
265948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLeanint proxy_scan_rates(alsa_device_proxy * proxy, unsigned sample_rates[]) {
266948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    alsa_device_profile* profile = proxy->profile;
267948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    if (profile->card < 0 || profile->device < 0) {
268948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        return -EINVAL;
269948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    }
270948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
271948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    struct pcm_config alsa_config;
272948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    memcpy(&alsa_config, &proxy->alsa_config, sizeof(alsa_config));
273948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
274948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    struct pcm * alsa_pcm;
275948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    int rate_index = 0;
276948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    while (sample_rates[rate_index] != 0) {
277948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        alsa_config.rate = sample_rates[rate_index];
278948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        alsa_pcm = pcm_open(profile->card, profile->device,
279948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean                profile->direction | PCM_MONOTONIC, &alsa_config);
280948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        if (alsa_pcm != NULL) {
281948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean            if (pcm_is_ready(alsa_pcm)) {
282948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean                pcm_close(alsa_pcm);
283948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean                return rate_index;
284948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean            }
285948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
286948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean            pcm_close(alsa_pcm);
287948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        }
288948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
289948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean        rate_index++;
290948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    }
291948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean
292948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean    return -EINVAL;
293948a6a4ba0fdda99dc36bfc2ff9dec121792c051Paul McLean}
294