blob: 57d1f0c5ce361ecdf16d4c27383ecc12164e3f43 [file] [log] [blame]
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -08001/* hfp.c
Amit Shekharcae30b12014-02-07 17:03:21 -08002Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -08003
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are
6met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above
10 copyright notice, this list of conditions and the following
11 disclaimer in the documentation and/or other materials provided
12 with the distribution.
13 * Neither the name of The Linux Foundation nor the names of its
14 contributors may be used to endorse or promote products derived
15 from this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
28
29#define LOG_TAG "audio_hw_hfp"
30/*#define LOG_NDEBUG 0*/
31#define LOG_NDDEBUG 0
32
33#include <errno.h>
34#include <math.h>
35#include <cutils/log.h>
36
37#include "audio_hw.h"
38#include "platform.h"
39#include "platform_api.h"
40#include <stdlib.h>
41#include <cutils/str_parms.h>
42
43#ifdef HFP_ENABLED
44#define AUDIO_PARAMETER_HFP_ENABLE "hfp_enable"
Vimal Puthanveed47e64852013-12-20 13:23:39 -080045#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
Amit Shekharcae30b12014-02-07 17:03:21 -080046#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume"
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080047
Vimal Puthanveed584048b2013-12-11 17:00:50 -080048static int32_t start_hfp(struct audio_device *adev,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080049 struct str_parms *parms);
50
Vimal Puthanveed584048b2013-12-11 17:00:50 -080051static int32_t stop_hfp(struct audio_device *adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080052
53struct hfp_module {
54 struct pcm *hfp_sco_rx;
55 struct pcm *hfp_sco_tx;
56 struct pcm *hfp_pcm_rx;
57 struct pcm *hfp_pcm_tx;
58 bool is_hfp_running;
Amit Shekharcae30b12014-02-07 17:03:21 -080059 float hfp_volume;
Vimal Puthanveed47e64852013-12-20 13:23:39 -080060 audio_usecase_t ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080061};
62
63static struct hfp_module hfpmod = {
64 .hfp_sco_rx = NULL,
65 .hfp_sco_tx = NULL,
66 .hfp_pcm_rx = NULL,
67 .hfp_pcm_tx = NULL,
68 .hfp_volume = 0,
69 .is_hfp_running = 0,
Vimal Puthanveed47e64852013-12-20 13:23:39 -080070 .ucid = USECASE_AUDIO_HFP_SCO,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -080071};
72static struct pcm_config pcm_config_hfp = {
73 .channels = 1,
74 .rate = 8000,
75 .period_size = 240,
76 .period_count = 2,
77 .format = PCM_FORMAT_S16_LE,
78 .start_threshold = 0,
79 .stop_threshold = INT_MAX,
80 .avail_min = 0,
81};
82
Amit Shekharcae30b12014-02-07 17:03:21 -080083static int32_t hfp_set_volume(struct audio_device *adev, float value)
84{
85 int32_t vol, ret = 0;
86 struct mixer_ctl *ctl;
87 const char *mixer_ctl_name = "Internal HFP RX Volume";
88
89 ALOGV("%s: entry", __func__);
90 ALOGD("%s: (%f)\n", __func__, value);
91
92 if (value < 0.0) {
93 ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
94 value = 0.0;
95 } else {
96 value = ((value > 15.000000) ? 1.0 : (value / 15));
97 ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
98 }
99 vol = lrint((value * 0x2000) + 0.5);
100 hfpmod.hfp_volume = value;
101
102 if (!hfpmod.is_hfp_running) {
103 ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
104 return -EIO;
105 }
106
107 ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
108 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
109 if (!ctl) {
110 ALOGE("%s: Could not get ctl for mixer cmd - %s",
111 __func__, mixer_ctl_name);
112 return -EINVAL;
113 }
114 if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
115 ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
116 return -EINVAL;
117 }
118
119 ALOGV("%s: exit", __func__);
120 return ret;
121}
122
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800123static int32_t start_hfp(struct audio_device *adev,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800124 struct str_parms *parms)
125{
126 int32_t i, ret = 0;
127 struct audio_usecase *uc_info;
128 int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
129
130 ALOGD("%s: enter", __func__);
131
132 uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800133 uc_info->id = hfpmod.ucid;
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800134 uc_info->type = PCM_HFP_CALL;
135 uc_info->stream.out = adev->primary_output;
136 uc_info->devices = adev->primary_output->devices;
137 uc_info->in_snd_device = SND_DEVICE_NONE;
138 uc_info->out_snd_device = SND_DEVICE_NONE;
139
140 list_add_tail(&adev->usecase_list, &uc_info->list);
141
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800142 select_devices(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800143
144 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
145 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
146 pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
147 pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
148 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
149 pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
150 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
151 __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
152 ret = -EIO;
153 goto exit;
154 }
155
156 ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
157 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
158
159 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800160 __func__, adev->snd_card, pcm_dev_rx_id);
161 hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800162 pcm_dev_asm_rx_id,
163 PCM_OUT, &pcm_config_hfp);
164 if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
165 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
166 ret = -EIO;
167 goto exit;
168 }
169 ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800170 __func__, adev->snd_card, pcm_dev_tx_id);
171 hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800172 pcm_dev_rx_id,
173 PCM_OUT, &pcm_config_hfp);
174 if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
175 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
176 ret = -EIO;
177 goto exit;
178 }
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800179 hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800180 pcm_dev_asm_tx_id,
181 PCM_IN, &pcm_config_hfp);
182 if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
183 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
184 ret = -EIO;
185 goto exit;
186 }
187 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
Apoorv Raghuvanshi84fa2fe2013-12-04 11:57:47 -0800188 __func__, adev->snd_card, pcm_dev_tx_id);
189 hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800190 pcm_dev_tx_id,
191 PCM_IN, &pcm_config_hfp);
192 if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
193 ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
194 ret = -EIO;
195 goto exit;
196 }
197 pcm_start(hfpmod.hfp_sco_rx);
198 pcm_start(hfpmod.hfp_sco_tx);
199 pcm_start(hfpmod.hfp_pcm_rx);
200 pcm_start(hfpmod.hfp_pcm_tx);
201
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800202 hfpmod.is_hfp_running = true;
Amit Shekharcae30b12014-02-07 17:03:21 -0800203 hfp_set_volume(adev, hfpmod.hfp_volume);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800204
205 ALOGD("%s: exit: status(%d)", __func__, ret);
206 return 0;
207
208exit:
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800209 stop_hfp(adev);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800210 ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
211 return ret;
212}
213
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800214static int32_t stop_hfp(struct audio_device *adev)
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800215{
216 int32_t i, ret = 0;
217 struct audio_usecase *uc_info;
218
219 ALOGD("%s: enter", __func__);
220 hfpmod.is_hfp_running = false;
221
222 /* 1. Close the PCM devices */
223 if (hfpmod.hfp_sco_rx) {
224 pcm_close(hfpmod.hfp_sco_rx);
225 hfpmod.hfp_sco_rx = NULL;
226 }
227 if (hfpmod.hfp_sco_tx) {
228 pcm_close(hfpmod.hfp_sco_tx);
229 hfpmod.hfp_sco_tx = NULL;
230 }
231 if (hfpmod.hfp_pcm_rx) {
232 pcm_close(hfpmod.hfp_pcm_rx);
233 hfpmod.hfp_pcm_rx = NULL;
234 }
235 if (hfpmod.hfp_pcm_tx) {
236 pcm_close(hfpmod.hfp_pcm_tx);
237 hfpmod.hfp_pcm_tx = NULL;
238 }
239
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800240 uc_info = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800241 if (uc_info == NULL) {
242 ALOGE("%s: Could not find the usecase (%d) in the list",
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800243 __func__, hfpmod.ucid);
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800244 return -EINVAL;
245 }
246
247 /* 2. Get and set stream specific mixer controls */
248 disable_audio_route(adev, uc_info, true);
249
250 /* 3. Disable the rx and tx devices */
251 disable_snd_device(adev, uc_info->out_snd_device, false);
252 disable_snd_device(adev, uc_info->in_snd_device, true);
253
254 list_remove(&uc_info->list);
255 free(uc_info);
256
257 ALOGD("%s: exit: status(%d)", __func__, ret);
258 return ret;
259}
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800260
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800261bool audio_extn_hfp_is_active(struct audio_device *adev)
262{
263 struct audio_usecase *hfp_usecase = NULL;
Vimal Puthanveed739e7152014-01-23 15:56:53 -0800264 hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
Vimal Puthanveed37b4a1c2014-01-07 16:47:47 -0800265
266 if (hfp_usecase != NULL)
267 return true;
268 else
269 return false;
270}
271
Vimal Puthanveed739e7152014-01-23 15:56:53 -0800272audio_usecase_t audio_extn_hfp_get_usecase()
273{
274 return hfpmod.ucid;
275}
276
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800277void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
278{
279 int ret;
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800280 int rate;
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800281 int val;
Amit Shekharcae30b12014-02-07 17:03:21 -0800282 float vol;
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800283 char value[32]={0};
284
285 ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
286 sizeof(value));
287 if (ret >= 0) {
288 if(!strncmp(value,"true",sizeof(value)))
289 ret = start_hfp(adev,parms);
290 else
291 stop_hfp(adev);
292 }
Vimal Puthanveed47e64852013-12-20 13:23:39 -0800293 memset(value, 0, sizeof(value));
294 ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
295 sizeof(value));
296 if (ret >= 0) {
297 rate = atoi(value);
298 if (rate == 8000){
299 hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
300 pcm_config_hfp.rate = rate;
301 }
302 else if (rate == 16000){
303 hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
304 pcm_config_hfp.rate = rate;
305 }
306 else
307 ALOGE("Unsupported rate..");
308 }
Vimal Puthanveed21e5c762014-01-08 14:10:09 -0800309
310 if(hfpmod.is_hfp_running) {
311 memset(value, 0, sizeof(value));
312 ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
313 value, sizeof(value));
314 if (ret >= 0) {
315 val = atoi(value);
316 if(val > 0)
317 select_devices(adev, hfpmod.ucid);
318 }
319 }
Amit Shekharcae30b12014-02-07 17:03:21 -0800320
321 memset(value, 0, sizeof(value));
322 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
323 value, sizeof(value));
324 if (ret >= 0) {
325 if (sscanf(value, "%f", &vol) != 1){
326 ALOGE("%s: error in retrieving hfp volume", __func__);
327 ret = -EIO;
328 goto exit;
329 }
330 ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
331 hfp_set_volume(adev, vol);
332 }
333exit:
334 ALOGV("%s Exit",__func__);
Vimal Puthanveed584048b2013-12-11 17:00:50 -0800335}
Vimal Puthanveed5b4d3f12013-11-05 15:57:39 -0800336#endif /*HFP_ENABLED*/