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"
1898ca8d42b782987cd1ceb370c10460c3eb79223ePaul 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
2598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#include "include/alsa_device_proxy.h"
2698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#include "include/alsa_logging.h"
2898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
2998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#define DEFAULT_PERIOD_SIZE     1024
3098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#define DEFAULT_PERIOD_COUNT    2
3198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
328785fe142d519d8e011eecdc340a638978fb9272Andy Hung#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
338785fe142d519d8e011eecdc340a638978fb9272Andy Hung
348785fe142d519d8e011eecdc340a638978fb9272Andy Hungstatic const unsigned format_byte_size_map[] = {
358785fe142d519d8e011eecdc340a638978fb9272Andy Hung    2, /* PCM_FORMAT_S16_LE */
368785fe142d519d8e011eecdc340a638978fb9272Andy Hung    4, /* PCM_FORMAT_S32_LE */
378785fe142d519d8e011eecdc340a638978fb9272Andy Hung    1, /* PCM_FORMAT_S8 */
388785fe142d519d8e011eecdc340a638978fb9272Andy Hung    4, /* PCM_FORMAT_S24_LE */
398785fe142d519d8e011eecdc340a638978fb9272Andy Hung    3, /* PCM_FORMAT_S24_3LE */
408785fe142d519d8e011eecdc340a638978fb9272Andy Hung};
418785fe142d519d8e011eecdc340a638978fb9272Andy Hung
4298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanvoid proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile,
4398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean                   struct pcm_config * config)
4498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
4598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    ALOGV("proxy_prepare(c:%d, d:%d)", profile->card, profile->device);
4698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
4798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->profile = profile;
4898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
4998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#ifdef LOG_PCM_PARAMS
5098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    log_pcm_config(config, "proxy_setup()");
5198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#endif
5298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
53a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    if (config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)) {
54a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.format = config->format;
55a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    } else {
56a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        ALOGW("Invalid format %d - using default %d.",
57a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean              config->format, profile->default_config.format);
58a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.format = profile->default_config.format;
59a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    }
60a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean
61a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    if (config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)) {
62a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.rate = config->rate;
63a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    } else {
64a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        ALOGW("Invalid sample rate %u - using default %u.",
65a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean              config->rate, profile->default_config.rate);
66a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.rate = profile->default_config.rate;
67a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    }
68a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean
69a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    if (config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)) {
70a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.channels = config->channels;
71a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    } else {
72a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        ALOGW("Invalid channel count %u - using default %u.",
73a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean              config->channels, profile->default_config.channels);
74a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean        proxy->alsa_config.channels = profile->default_config.channels;
75a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean
76a70650a4e01987a5f5096fdeef0c32e55ba45a09Paul McLean    }
7798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
7898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->alsa_config.period_count = profile->default_config.period_count;
7998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->alsa_config.period_size =
8098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean            profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
8198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
8298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    // Hack for USB accessory audio.
8398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    // Here we set the correct value for period_count if tinyalsa fails to get it from the
8498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    // f_audio_source driver.
8598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (proxy->alsa_config.period_count == 0) {
8698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        proxy->alsa_config.period_count = 4;
8798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
8898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
8998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    proxy->pcm = NULL;
908785fe142d519d8e011eecdc340a638978fb9272Andy Hung    // config format should be checked earlier against profile.
918785fe142d519d8e011eecdc340a638978fb9272Andy Hung    if (config->format >= 0 && (size_t)config->format < ARRAY_SIZE(format_byte_size_map)) {
928785fe142d519d8e011eecdc340a638978fb9272Andy Hung        proxy->frame_size = format_byte_size_map[config->format] * proxy->alsa_config.channels;
938785fe142d519d8e011eecdc340a638978fb9272Andy Hung    } else {
948785fe142d519d8e011eecdc340a638978fb9272Andy Hung        proxy->frame_size = 1;
958785fe142d519d8e011eecdc340a638978fb9272Andy Hung    }
9698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
9798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
9898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanint proxy_open(alsa_device_proxy * proxy)
9998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
10098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    alsa_device_profile* profile = proxy->profile;
10198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
10298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean          profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
10398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
10498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (profile->card < 0 || profile->device < 0) {
10598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        return -EINVAL;
10698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
10798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
1088785fe142d519d8e011eecdc340a638978fb9272Andy Hung    proxy->pcm = pcm_open(profile->card, profile->device,
1098785fe142d519d8e011eecdc340a638978fb9272Andy Hung            profile->direction | PCM_MONOTONIC, &proxy->alsa_config);
11098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (proxy->pcm == NULL) {
11198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        return -ENOMEM;
11298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
11398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
11498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (!pcm_is_ready(proxy->pcm)) {
11598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        ALOGE("  proxy_open() pcm_open() failed: %s", pcm_get_error(proxy->pcm));
11698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#if defined(LOG_PCM_PARAMS)
11798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        log_pcm_config(&proxy->alsa_config, "config");
11898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean#endif
11998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        pcm_close(proxy->pcm);
12098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        proxy->pcm = NULL;
12198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        return -ENOMEM;
12298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
12398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
12498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return 0;
12598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
12698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
12798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanvoid proxy_close(alsa_device_proxy * proxy)
12898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
12998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    ALOGV("proxy_close() [pcm:%p]", proxy->pcm);
13098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
13198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    if (proxy->pcm != NULL) {
13298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        pcm_close(proxy->pcm);
13398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean        proxy->pcm = NULL;
13498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    }
13598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
13698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
13798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
13898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Sample Rate
13998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
14098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
14198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
14298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.rate;
14398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
14498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
14598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
14698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Format
14798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
14898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanenum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
14998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
15098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.format;
15198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
15298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
15398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
15498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Channel Count
15598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
15698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
15798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
15898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.channels;
15998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
16098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
16198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
16298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * Other
16398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
16498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
16598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
16698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.period_size;
16798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
16898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
16998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
17098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
17198ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return proxy->alsa_config.period_count;
17298ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
17398ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
17498ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanunsigned proxy_get_latency(const alsa_device_proxy * proxy)
17598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
17698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
17798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean               / proxy_get_sample_rate(proxy);
17898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
17998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
1808785fe142d519d8e011eecdc340a638978fb9272Andy Hungint proxy_get_presentation_position(const alsa_device_proxy * proxy,
1818785fe142d519d8e011eecdc340a638978fb9272Andy Hung        uint64_t *frames, struct timespec *timestamp)
1828785fe142d519d8e011eecdc340a638978fb9272Andy Hung{
1838785fe142d519d8e011eecdc340a638978fb9272Andy Hung    int ret = -EPERM; // -1
1848785fe142d519d8e011eecdc340a638978fb9272Andy Hung    unsigned int avail;
1858785fe142d519d8e011eecdc340a638978fb9272Andy Hung    if (proxy->pcm != NULL
1868785fe142d519d8e011eecdc340a638978fb9272Andy Hung            && pcm_get_htimestamp(proxy->pcm, &avail, timestamp) == 0) {
1878785fe142d519d8e011eecdc340a638978fb9272Andy Hung        const size_t kernel_buffer_size =
1888785fe142d519d8e011eecdc340a638978fb9272Andy Hung                proxy->alsa_config.period_size * proxy->alsa_config.period_count;
1898785fe142d519d8e011eecdc340a638978fb9272Andy Hung        if (avail > kernel_buffer_size) {
1908785fe142d519d8e011eecdc340a638978fb9272Andy Hung            ALOGE("available frames(%u) > buffer size(%zu)", avail, kernel_buffer_size);
1918785fe142d519d8e011eecdc340a638978fb9272Andy Hung        } else {
1928785fe142d519d8e011eecdc340a638978fb9272Andy Hung            int64_t signed_frames = proxy->transferred - kernel_buffer_size + avail;
1938785fe142d519d8e011eecdc340a638978fb9272Andy Hung            // It is possible to compensate for additional driver and device delay
1948785fe142d519d8e011eecdc340a638978fb9272Andy Hung            // by changing signed_frames.  Example:
1958785fe142d519d8e011eecdc340a638978fb9272Andy Hung            // signed_frames -= 20 /* ms */ * proxy->alsa_config.rate / 1000;
1968785fe142d519d8e011eecdc340a638978fb9272Andy Hung            if (signed_frames >= 0) {
1978785fe142d519d8e011eecdc340a638978fb9272Andy Hung                *frames = signed_frames;
1988785fe142d519d8e011eecdc340a638978fb9272Andy Hung                ret = 0;
1998785fe142d519d8e011eecdc340a638978fb9272Andy Hung            }
2008785fe142d519d8e011eecdc340a638978fb9272Andy Hung        }
2018785fe142d519d8e011eecdc340a638978fb9272Andy Hung    }
2028785fe142d519d8e011eecdc340a638978fb9272Andy Hung    return ret;
2038785fe142d519d8e011eecdc340a638978fb9272Andy Hung}
2048785fe142d519d8e011eecdc340a638978fb9272Andy Hung
20598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean/*
20698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean * I/O
20798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean */
2088785fe142d519d8e011eecdc340a638978fb9272Andy Hungint proxy_write(alsa_device_proxy * proxy, const void *data, unsigned int count)
20998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
2108785fe142d519d8e011eecdc340a638978fb9272Andy Hung    int ret = pcm_write(proxy->pcm, data, count);
2118785fe142d519d8e011eecdc340a638978fb9272Andy Hung    if (ret == 0) {
2128785fe142d519d8e011eecdc340a638978fb9272Andy Hung        proxy->transferred += count / proxy->frame_size;
2138785fe142d519d8e011eecdc340a638978fb9272Andy Hung    }
2148785fe142d519d8e011eecdc340a638978fb9272Andy Hung    return ret;
21598ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
21698ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean
21798ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLeanint proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
21898ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean{
21998ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean    return pcm_read(proxy->pcm, data, count);
22098ca8d42b782987cd1ceb370c10460c3eb79223ePaul McLean}
221