Home | History | Annotate | Download | only in audio_extn
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "audio_hw_hfp"
     18 /*#define LOG_NDEBUG 0*/
     19 #define LOG_NDDEBUG 0
     20 
     21 #include <errno.h>
     22 #include <math.h>
     23 #include <cutils/log.h>
     24 
     25 #include "audio_hw.h"
     26 #include "platform.h"
     27 #include "platform_api.h"
     28 #include <stdlib.h>
     29 #include <cutils/str_parms.h>
     30 
     31 #define AUDIO_PARAMETER_HFP_ENABLE      "hfp_enable"
     32 #define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
     33 #define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume"
     34 
     35 static int32_t start_hfp(struct audio_device *adev,
     36                                struct str_parms *parms);
     37 
     38 static int32_t stop_hfp(struct audio_device *adev);
     39 
     40 struct hfp_module {
     41     struct pcm *hfp_sco_rx;
     42     struct pcm *hfp_sco_tx;
     43     struct pcm *hfp_pcm_rx;
     44     struct pcm *hfp_pcm_tx;
     45     float  hfp_volume;
     46     bool   is_hfp_running;
     47     audio_usecase_t ucid;
     48 };
     49 
     50 static struct hfp_module hfpmod = {
     51     .hfp_sco_rx = NULL,
     52     .hfp_sco_tx = NULL,
     53     .hfp_pcm_rx = NULL,
     54     .hfp_pcm_tx = NULL,
     55     .hfp_volume = 0,
     56     .is_hfp_running = 0,
     57     .ucid = USECASE_AUDIO_HFP_SCO,
     58 };
     59 static struct pcm_config pcm_config_hfp = {
     60     .channels = 1,
     61     .rate = 8000,
     62     .period_size = 240,
     63     .period_count = 2,
     64     .format = PCM_FORMAT_S16_LE,
     65     .start_threshold = 0,
     66     .stop_threshold = INT_MAX,
     67     .avail_min = 0,
     68 };
     69 
     70 static int32_t hfp_set_volume(struct audio_device *adev, float value)
     71 {
     72     int32_t vol, ret = 0;
     73     struct mixer_ctl *ctl;
     74     const char *mixer_ctl_name = "Internal HFP RX Volume";
     75 
     76     ALOGV("%s: entry", __func__);
     77     ALOGD("%s: (%f)\n", __func__, value);
     78 
     79     if (value < 0.0) {
     80         ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
     81         value = 0.0;
     82     } else {
     83         value = ((value > 15.000000) ? 1.0 : (value / 15));
     84         ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
     85     }
     86     vol  = lrint((value * 0x2000) + 0.5);
     87     hfpmod.hfp_volume = value;
     88 
     89     if (!hfpmod.is_hfp_running) {
     90         ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
     91         return -EIO;
     92     }
     93 
     94     ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
     95     ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
     96     if (!ctl) {
     97         ALOGE("%s: Could not get ctl for mixer cmd - %s",
     98               __func__, mixer_ctl_name);
     99         return -EINVAL;
    100     }
    101     if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
    102         ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
    103         return -EINVAL;
    104     }
    105 
    106     ALOGV("%s: exit", __func__);
    107     return ret;
    108 }
    109 
    110 static int32_t start_hfp(struct audio_device *adev,
    111                          struct str_parms *parms __unused)
    112 {
    113     int32_t i, ret = 0;
    114     struct audio_usecase *uc_info;
    115     int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
    116 
    117     ALOGD("%s: enter", __func__);
    118 
    119     uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
    120     uc_info->id = hfpmod.ucid;
    121     uc_info->type = PCM_HFP_CALL;
    122     uc_info->stream.out = adev->primary_output;
    123     uc_info->devices = adev->primary_output->devices;
    124     uc_info->in_snd_device = SND_DEVICE_NONE;
    125     uc_info->out_snd_device = SND_DEVICE_NONE;
    126 
    127     list_add_tail(&adev->usecase_list, &uc_info->list);
    128 
    129     select_devices(adev, hfpmod.ucid);
    130 
    131     pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
    132     pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
    133     pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
    134     pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
    135     if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
    136         pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
    137         ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
    138               __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
    139         ret = -EIO;
    140         goto exit;
    141     }
    142 
    143     ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
    144               __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
    145 
    146     ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
    147           __func__, adev->snd_card, pcm_dev_rx_id);
    148     hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
    149                                   pcm_dev_asm_rx_id,
    150                                   PCM_OUT, &pcm_config_hfp);
    151     if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
    152         ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
    153         ret = -EIO;
    154         goto exit;
    155     }
    156     ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
    157           __func__, adev->snd_card, pcm_dev_tx_id);
    158     hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
    159                                    pcm_dev_rx_id,
    160                                    PCM_OUT, &pcm_config_hfp);
    161     if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
    162         ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
    163         ret = -EIO;
    164         goto exit;
    165     }
    166     hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
    167                                   pcm_dev_asm_tx_id,
    168                                   PCM_IN, &pcm_config_hfp);
    169     if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
    170         ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
    171         ret = -EIO;
    172         goto exit;
    173     }
    174     ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
    175           __func__, adev->snd_card, pcm_dev_tx_id);
    176     hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
    177                                    pcm_dev_tx_id,
    178                                    PCM_IN, &pcm_config_hfp);
    179     if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
    180         ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
    181         ret = -EIO;
    182         goto exit;
    183     }
    184     pcm_start(hfpmod.hfp_sco_rx);
    185     pcm_start(hfpmod.hfp_sco_tx);
    186     pcm_start(hfpmod.hfp_pcm_rx);
    187     pcm_start(hfpmod.hfp_pcm_tx);
    188 
    189     hfpmod.is_hfp_running = true;
    190     hfp_set_volume(adev, hfpmod.hfp_volume);
    191 
    192     ALOGD("%s: exit: status(%d)", __func__, ret);
    193     return 0;
    194 
    195 exit:
    196     stop_hfp(adev);
    197     ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
    198     return ret;
    199 }
    200 
    201 static int32_t stop_hfp(struct audio_device *adev)
    202 {
    203     int32_t i, ret = 0;
    204     struct audio_usecase *uc_info;
    205 
    206     ALOGD("%s: enter", __func__);
    207     hfpmod.is_hfp_running = false;
    208 
    209     /* 1. Close the PCM devices */
    210     if (hfpmod.hfp_sco_rx) {
    211         pcm_close(hfpmod.hfp_sco_rx);
    212         hfpmod.hfp_sco_rx = NULL;
    213     }
    214     if (hfpmod.hfp_sco_tx) {
    215         pcm_close(hfpmod.hfp_sco_tx);
    216         hfpmod.hfp_sco_tx = NULL;
    217     }
    218     if (hfpmod.hfp_pcm_rx) {
    219         pcm_close(hfpmod.hfp_pcm_rx);
    220         hfpmod.hfp_pcm_rx = NULL;
    221     }
    222     if (hfpmod.hfp_pcm_tx) {
    223         pcm_close(hfpmod.hfp_pcm_tx);
    224         hfpmod.hfp_pcm_tx = NULL;
    225     }
    226 
    227     uc_info = get_usecase_from_list(adev, hfpmod.ucid);
    228     if (uc_info == NULL) {
    229         ALOGE("%s: Could not find the usecase (%d) in the list",
    230               __func__, hfpmod.ucid);
    231         return -EINVAL;
    232     }
    233 
    234     /* 2. Get and set stream specific mixer controls */
    235     disable_audio_route(adev, uc_info);
    236 
    237     /* 3. Disable the rx and tx devices */
    238     disable_snd_device(adev, uc_info->out_snd_device);
    239     disable_snd_device(adev, uc_info->in_snd_device);
    240 
    241     list_remove(&uc_info->list);
    242     free(uc_info);
    243 
    244     ALOGD("%s: exit: status(%d)", __func__, ret);
    245     return ret;
    246 }
    247 
    248 bool audio_extn_hfp_is_active(struct audio_device *adev)
    249 {
    250     struct audio_usecase *hfp_usecase = NULL;
    251     hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
    252 
    253     if (hfp_usecase != NULL)
    254         return true;
    255     else
    256         return false;
    257 }
    258 
    259 audio_usecase_t audio_extn_hfp_get_usecase()
    260 {
    261     return hfpmod.ucid;
    262 }
    263 
    264 void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
    265 {
    266     int ret;
    267     int rate;
    268     int val;
    269     float vol;
    270     char value[32]={0};
    271 
    272     ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
    273                             sizeof(value));
    274     if (ret >= 0) {
    275            if (!strncmp(value,"true",sizeof(value)))
    276                ret = start_hfp(adev,parms);
    277            else
    278                stop_hfp(adev);
    279     }
    280     memset(value, 0, sizeof(value));
    281     ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
    282                             sizeof(value));
    283     if (ret >= 0) {
    284            rate = atoi(value);
    285            if (rate == 8000){
    286                hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
    287                pcm_config_hfp.rate = rate;
    288            } else if (rate == 16000){
    289                hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
    290                pcm_config_hfp.rate = rate;
    291            } else
    292                ALOGE("Unsupported rate..");
    293     }
    294 
    295     if (hfpmod.is_hfp_running) {
    296         memset(value, 0, sizeof(value));
    297         ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
    298                                 value, sizeof(value));
    299         if (ret >= 0) {
    300             val = atoi(value);
    301             if (val > 0)
    302                 select_devices(adev, hfpmod.ucid);
    303         }
    304     }
    305 
    306     memset(value, 0, sizeof(value));
    307     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
    308                             value, sizeof(value));
    309     if (ret >= 0) {
    310         if (sscanf(value, "%f", &vol) != 1){
    311             ALOGE("%s: error in retrieving hfp volume", __func__);
    312             ret = -EIO;
    313             goto exit;
    314         }
    315         ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
    316         hfp_set_volume(adev, vol);
    317     }
    318 exit:
    319     ALOGV("%s Exit",__func__);
    320 }
    321