blob: 97624cbf754c16f21625ac5721a814ea0d8ca0b7 [file] [log] [blame]
Quinn Male2e883752019-03-22 11:28:54 -07001/* st_hw_common.c
2 *
3 * This file contains common functionality between
4 * sound trigger hw and sound trigger extension interface.
5 *
Zhou Song7bf9fc32020-03-27 17:25:36 +08006 * Copyright (c) 2016, 2018-2020, The Linux Foundation. All rights reserved.
Quinn Male2e883752019-03-22 11:28:54 -07007 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of The Linux Foundation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#define LOG_TAG "sound_trigger_hw_common"
35/* #define LOG_NDEBUG 0 */
36#define LOG_NDDEBUG 0
37
38/* #define VERY_VERBOSE_LOGGING */
39#ifdef VERY_VERBOSE_LOGGING
40#define ALOGVV ALOGV
41#else
42#define ALOGVV(a...) do { } while(0)
43#endif
44
45#include <cutils/log.h>
46#include <hardware/sound_trigger.h>
47
48#include "st_hw_common.h"
49
50/*
51 * Maximum number of sessions supported in LPI mode:
52 * here each stages are counted individually,
53 * so session count for dual stage lsm session would be set to 2.
54 */
55#define LSM_MAX_LPI_BUDGET_AVAILABLE 2
56#define LSM_MAX_SS_USECASE_SUPPORTED 1
57
58#ifdef AUDIO_FEATURE_ENABLED_GCOV
59extern void __gcov_flush();
60static void enable_gcov()
61{
62 __gcov_flush();
63}
64#else
65static void enable_gcov()
66{
67}
68#endif
69
70bool st_hw_check_ses_ss_usecase_allowed(st_session_t *st_ses)
71{
72 struct sound_trigger_device *stdev;
73 struct listnode *node;
74 st_session_t *p_ses;
75 int lsm_ss_uc_count = 0;
76 /* TODO: get max_lsm_ss_uc_count from platform config xml */
77 int max_lsm_ss_uc_count = LSM_MAX_SS_USECASE_SUPPORTED;
78
79 /*
80 * Second stage is only supported on an adsp session,
81 * and when multi-stage support is available in lsm drivers.
82 */
Zhou Songdeddfcc2019-06-18 22:25:03 +080083 if (!st_ses || !st_ses->hw_proxy_ses ||
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -070084 !st_ses->hw_proxy_ses->hw_ses_adsp ||
85 !st_hw_check_multi_stage_lsm_support()) {
Quinn Male2e883752019-03-22 11:28:54 -070086 return false;
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -070087 }
Quinn Male2e883752019-03-22 11:28:54 -070088
89 stdev = st_ses->stdev;
90 list_for_each(node, &stdev->sound_model_list) {
91 p_ses = node_to_item(node, st_session_t, list_node);
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -070092 if (p_ses == NULL || p_ses == st_ses ||
93 p_ses->exec_mode != ST_EXEC_MODE_ADSP)
Quinn Male2e883752019-03-22 11:28:54 -070094 continue;
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -070095 pthread_mutex_lock(&p_ses->hw_proxy_ses->lock);
96 if (p_ses->hw_proxy_ses->hw_ses_adsp &&
97 !list_empty(&p_ses->hw_proxy_ses->hw_ses_adsp->lsm_ss_cfg_list))
Quinn Male2e883752019-03-22 11:28:54 -070098 lsm_ss_uc_count++;
99 if (lsm_ss_uc_count >= max_lsm_ss_uc_count) {
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700100 ALOGD("%s: max supported ss usecase count(%d) already active,"
101 "not allowing further", __func__, max_lsm_ss_uc_count);
102 pthread_mutex_unlock(&p_ses->hw_proxy_ses->lock);
Quinn Male2e883752019-03-22 11:28:54 -0700103 return false;
104 }
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700105 pthread_mutex_unlock(&p_ses->hw_proxy_ses->lock);
Quinn Male2e883752019-03-22 11:28:54 -0700106 }
107 ALOGD("%s: ss usecase allowed", __func__);
108 return true;
109}
110
111static int get_session_lpi_cost(st_session_t *ses)
112{
113 /* TODO: check if this works for all sound models, and update logic if required */
114 return ses->num_phrases > 1 ? ses->num_phrases : 1;
115}
116
117/*
118 * Check below cases to do LPI budgeting:
119 * 1. when asked for a session:
120 * - check if the session can be allowed with lpi mode
121 * 2. when session is NULL:
122 * - check if sessions in global list meets lpi constraint
123 * 3. for NULL case without any other active session:
124 * - return false as lpi mode is false by default
125 */
126static bool is_projected_lpi_budget_available
127(
128 struct sound_trigger_device *stdev,
129 st_session_t *st_ses
130)
131{
132 st_session_t *p_ses;
133 struct listnode *node;
134 int ses_lpi_cost = 0, sys_lpi_cost = 0;
135 /* TODO: max lpi budget from platform config xml */
136 int max_lpi_budget_available = LSM_MAX_LPI_BUDGET_AVAILABLE;
137
138 /*
139 * LPI mode is only allowed in following ADSP use cases:
140 * 1. Single session with single keyword and 2 stages, both in ADSP
141 * 2. Single session with two keywords but single stages in ADSP
142 * 2. Upto 2 sessions with single keyword each
143 */
144
Quinn Male2e883752019-03-22 11:28:54 -0700145 /* first check current session for LPI criteria if st_ses is available */
146 ses_lpi_cost = st_ses ? get_session_lpi_cost(st_ses) : 0;
147 if (ses_lpi_cost > max_lpi_budget_available) {
148 ALOGD("%s: ses lpi cost(%d) exceeds budget(%d), enable=false",
149 __func__, ses_lpi_cost, max_lpi_budget_available);
150 return false;
151 }
152 /* check for presence of any other session with remaining LPI budget*/
153 sys_lpi_cost += ses_lpi_cost;
154 list_for_each(node, &stdev->sound_model_list) {
155 p_ses = node_to_item(node, st_session_t, list_node);
156 if (p_ses == NULL || p_ses == st_ses || p_ses->exec_mode != ST_EXEC_MODE_ADSP)
157 continue;
158 /*
159 * Check for all sessions present in sound model list as
160 * LPI is load time decision.
161 */
162 if (p_ses->vendor_uuid_info->lpi_enable) {
163 sys_lpi_cost += get_session_lpi_cost(p_ses);
164 if (sys_lpi_cost > max_lpi_budget_available) {
165 ALOGD("%s: projected lpi cost exceeds budget(%d), enable=false",
166 __func__, max_lpi_budget_available);
167 return false;
168 }
169 }
170 }
171
172 /* system lpi cost 0 at this point means no valid lpi session, return false */
173 if (sys_lpi_cost == 0) {
174 ALOGV("%s: no session with lpi support, enable=false", __func__);
175 return false;
176 }
177
178 ALOGD("%s: projected lpi cost(%d) within budget(%d), enable=true",
179 __func__, sys_lpi_cost, max_lpi_budget_available);
180 return true;
181}
182
Quinn Malecc1affd2019-07-18 16:13:31 -0700183void st_hw_check_and_update_lpi
Quinn Male2e883752019-03-22 11:28:54 -0700184(
185 struct sound_trigger_device *stdev,
186 st_session_t *st_ses
187)
188{
Quinn Male3d7d9d42019-05-20 13:35:01 -0700189 st_session_t *ses = NULL;
190 struct listnode *ses_node = NULL;
191
Quinn Malecc1affd2019-07-18 16:13:31 -0700192 stdev->lpi_enable = false;
193 stdev->barge_in_mode = true;
Quinn Male3d7d9d42019-05-20 13:35:01 -0700194 /*
195 * ST_PLATFORM_LPI_NONE is used for backward compatibility. With this
196 * setting, the st_vendor_uuid->lpi_enable flag will be used.
197 */
198 if (stdev->platform_lpi_enable == ST_PLATFORM_LPI_DISABLE) {
199 ALOGD("%s: lpi NOT enabled in platform setting", __func__);
Quinn Malecc1affd2019-07-18 16:13:31 -0700200 return;
Quinn Male3d7d9d42019-05-20 13:35:01 -0700201 } else if ((stdev->platform_lpi_enable == ST_PLATFORM_LPI_NONE) &&
202 st_ses && !st_ses->vendor_uuid_info->lpi_enable) {
203 ALOGD("%s: lpi NOT enabled for ses %d", __func__, st_ses->sm_handle);
Quinn Malecc1affd2019-07-18 16:13:31 -0700204 return;
Quinn Male2e883752019-03-22 11:28:54 -0700205 }
206
Quinn Male439b0e42019-08-15 17:08:53 -0700207 if (stdev->rx_concurrency_active || stdev->conc_voice_active ||
Quinn Male893c4742020-09-21 14:32:10 -0700208 stdev->conc_voip_active ||
209 !platform_stdev_backend_reset_allowed(stdev->platform)) {
Quinn Male2e883752019-03-22 11:28:54 -0700210 ALOGD("%s: lpi NOT supported due to concurrency", __func__);
Quinn Malecc1affd2019-07-18 16:13:31 -0700211 return;
Quinn Male2e883752019-03-22 11:28:54 -0700212 }
213
Quinn Male3d7d9d42019-05-20 13:35:01 -0700214 if (stdev->is_charging &&
215 stdev->transit_to_non_lpi_on_battery_charging) {
216 ALOGD("%s: lpi NOT supported. battery status %d", __func__,
Quinn Male2e883752019-03-22 11:28:54 -0700217 stdev->is_charging);
Quinn Malecc1affd2019-07-18 16:13:31 -0700218 if (stdev->support_barge_in_mode)
219 stdev->barge_in_mode = false;
220 return;
Quinn Male2e883752019-03-22 11:28:54 -0700221 }
222
Quinn Male3d7d9d42019-05-20 13:35:01 -0700223 list_for_each(ses_node, &stdev->sound_model_list) {
224 ses = node_to_item(ses_node, st_session_t, list_node);
225
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700226 if (ses->client_req_det_mode == ST_DET_HIGH_PERF_MODE) {
Quinn Male3d7d9d42019-05-20 13:35:01 -0700227 ALOGD("%s:[%d] lpi NOT supported due to high perf mode", __func__,
228 ses->sm_handle);
Quinn Malecc1affd2019-07-18 16:13:31 -0700229 if (stdev->support_barge_in_mode)
230 stdev->barge_in_mode = false;
231 return;
Quinn Male3d7d9d42019-05-20 13:35:01 -0700232 }
233 }
234
Quinn Malecc1affd2019-07-18 16:13:31 -0700235 if (!stdev->screen_off && stdev->support_barge_in_mode) {
236 ALOGD("%s: lpi NOT supported. Screen is on", __func__);
237 stdev->barge_in_mode = false;
238 return;
239 }
240
Quinn Male3d7d9d42019-05-20 13:35:01 -0700241 if (stdev->platform_lpi_enable == ST_PLATFORM_LPI_NONE)
Quinn Malecc1affd2019-07-18 16:13:31 -0700242 stdev->lpi_enable = is_projected_lpi_budget_available(stdev, st_ses);
Quinn Male3d7d9d42019-05-20 13:35:01 -0700243 else
Quinn Malecc1affd2019-07-18 16:13:31 -0700244 stdev->lpi_enable = true;
245 stdev->barge_in_mode = !stdev->lpi_enable;
Quinn Male2e883752019-03-22 11:28:54 -0700246}
247
248bool st_hw_check_vad_support
249(
250 struct sound_trigger_device *stdev,
251 st_session_t *st_ses,
252 bool lpi_enable
253)
254{
255 st_session_t *p_ses;
256 struct listnode *node;
257 bool vad_enable = false;
258 st_profile_type_t profile_type;
259
260
261 if (stdev->rx_concurrency_active || stdev->tx_concurrency_active) {
262 ALOGD("%s: VAD NOT supported due to concurrency", __func__);
263 return false;
264 }
265
266 /*
267 * check for all sessions, unless vad check is requested for a particular session.
268 * For a session, return true if vad is enabled in platform config and
269 * profile type is NONE or lpi is disabled.
270 */
271 if (st_ses) {
272 profile_type = st_ses->vendor_uuid_info->profile_type;
273 vad_enable = st_ses->vendor_uuid_info->vad_enable &&
274 ((profile_type == ST_PROFILE_TYPE_NONE) || lpi_enable);
275 } else {
276 list_for_each(node, &stdev->sound_model_list) {
277 p_ses = node_to_item(node, st_session_t, list_node);
278 profile_type = p_ses->vendor_uuid_info->profile_type;
279 if (p_ses->exec_mode != ST_EXEC_MODE_ADSP) {
280 continue;
281 } else if (!p_ses->vendor_uuid_info->vad_enable ||
282 ((profile_type != ST_PROFILE_TYPE_NONE) && !lpi_enable)) {
283 ALOGD("%s: disable vad, session does not meet requirement", __func__);
284 vad_enable = false;
285 break;
286 } else {
287 vad_enable = true;
288 }
289 }
290 }
291
292 ALOGV("%s: vad_enable = %d", __func__, vad_enable);
293 return vad_enable;
294}
295
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700296void st_hw_check_and_set_lpi_mode(st_session_t *stc_ses)
Quinn Male2e883752019-03-22 11:28:54 -0700297{
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700298 st_proxy_session_t *st_ses = NULL;
299
300 if (!stc_ses || !stc_ses->hw_proxy_ses)
301 return;
302
303 st_ses = stc_ses->hw_proxy_ses;
304
305 pthread_mutex_lock(&st_ses->lock);
306 if (st_ses->hw_ses_adsp) {
Quinn Male3d7d9d42019-05-20 13:35:01 -0700307 if (st_ses->stdev->platform_lpi_enable == ST_PLATFORM_LPI_NONE) {
308 st_ses->hw_ses_adsp->lpi_enable =
309 (st_ses->vendor_uuid_info->lpi_enable &&
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700310 is_projected_lpi_budget_available(st_ses->stdev, stc_ses));
Quinn Male3d7d9d42019-05-20 13:35:01 -0700311 } else {
Quinn Male23026702019-07-19 10:51:16 -0700312 st_ses->hw_ses_adsp->lpi_enable = st_ses->stdev->lpi_enable;
Quinn Malecc1affd2019-07-18 16:13:31 -0700313 st_ses->hw_ses_adsp->barge_in_mode =
314 st_ses->stdev->barge_in_mode;
Quinn Male3d7d9d42019-05-20 13:35:01 -0700315 }
Quinn Male2e883752019-03-22 11:28:54 -0700316 }
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700317 pthread_mutex_unlock(&st_ses->lock);
Quinn Male2e883752019-03-22 11:28:54 -0700318}
319
Quinn Male2e883752019-03-22 11:28:54 -0700320int stop_other_sessions(struct sound_trigger_device *stdev,
321 st_session_t *cur_ses)
322{
323 st_session_t *p_ses;
324 struct listnode *p_ses_node;
325 int status = 0;
326
327 ALOGV("%s: list empty %s", __func__,
328 list_empty(&stdev->sound_model_list) ? "true" : "false");
329
330 if (!stdev->session_allowed)
331 return 0;
332
333 list_for_each(p_ses_node, &stdev->sound_model_list) {
334 p_ses = node_to_item(p_ses_node, st_session_t, list_node);
335
336 /* Current session can already be in the list during SSR */
337 if (p_ses == cur_ses)
338 continue;
339
340 status = st_session_pause(p_ses);
341 if (status) {
342 ALOGE("%s: error stopping session", __func__);
343 return -EIO;
344 }
345 }
346 return 0;
347}
348
349int start_other_sessions(struct sound_trigger_device *stdev,
350 st_session_t *cur_ses)
351{
352 st_session_t *p_ses;
353 struct listnode *p_ses_node;
354 int status = 0;
355
356 ALOGV("%s: list empty %s", __func__,
357 list_empty(&stdev->sound_model_list) ? "true" : "false");
358
359 if (!stdev->session_allowed)
360 return 0;
361
362 list_for_each(p_ses_node, &stdev->sound_model_list) {
363 p_ses = node_to_item(p_ses_node, st_session_t, list_node);
364
365 /* Current session can already be in the list during SSR */
366 if (p_ses == cur_ses)
367 continue;
368 status = st_session_resume(p_ses);
369 if (status) {
370 ALOGE("%s: error restarting session", __func__);
371 return -EIO;
372 }
373 }
374 return 0;
375}
376
377st_session_t* get_sound_trigger_session(
378 struct sound_trigger_device *stdev,
379 sound_model_handle_t sound_model_handle)
380
381{
382 st_session_t *st_session = NULL;
383 struct listnode *node;
384
385 list_for_each(node, &stdev->sound_model_list) {
386 st_session = node_to_item(node, st_session_t, list_node);
387 if (st_session->sm_handle == sound_model_handle)
388 return st_session;
389
390 }
391 return NULL;
392}
393
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700394/*
395 * This function is used to prepare the detection engine custom config payload for
396 * sound trigger sessions that have second stage sessions. If history buffering is not
397 * requested by the client, add it into the payload here. Second stage sessions require
398 * the keyword to be buffered.
399 */
400int st_hw_ses_get_hist_buff_payload
401(
402 st_hw_session_t *p_ses,
403 uint8_t *payload_buf,
404 size_t buff_size
405)
406{
407 struct st_hist_buffer_info *hist_buf = NULL;
408
409 if (!payload_buf || buff_size < sizeof(*hist_buf)) {
410 ALOGE("%s: buffer size(%zd) too small to fill payload(%zd)",
411 __func__, buff_size, sizeof(*hist_buf));
412 return -EINVAL;
413 }
414
415 hist_buf = (struct st_hist_buffer_info *) payload_buf;
416 hist_buf->version = DEFAULT_CUSTOM_CONFIG_MINOR_VERSION;
Quinn Male58749452020-03-26 17:14:56 -0700417 hist_buf->pre_roll_duration_msec = p_ses->max_preroll;
Zhou Song7bf9fc32020-03-27 17:25:36 +0800418
419 if (p_ses->is_generic_event &&
Quinn Male58749452020-03-26 17:14:56 -0700420 p_ses->max_preroll < PREROLL_LEN_WARNING)
Zhou Song7bf9fc32020-03-27 17:25:36 +0800421 ALOGW("%s: Client requested small preroll length %dms",
Quinn Male58749452020-03-26 17:14:56 -0700422 __func__, p_ses->max_preroll);
Zhou Song7bf9fc32020-03-27 17:25:36 +0800423
Quinn Male58749452020-03-26 17:14:56 -0700424 if (p_ses->max_hist_buf > 0) {
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700425 hist_buf->hist_buffer_duration_msec =
Quinn Male58749452020-03-26 17:14:56 -0700426 p_ses->max_hist_buf;
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700427
Zhou Song7bf9fc32020-03-27 17:25:36 +0800428 if (p_ses->is_generic_event &&
Quinn Male58749452020-03-26 17:14:56 -0700429 p_ses->max_hist_buf <= KW_LEN_WARNING)
430 ALOGW("%s: Client requested small hist buf length %dms",
431 __func__, p_ses->max_hist_buf);
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700432 } else {
433 hist_buf->hist_buffer_duration_msec =
434 p_ses->vendor_uuid_info->kw_duration;
Venkatesh Mangalappalie4f17532019-06-04 16:30:28 -0700435 }
436 ALOGD("%s: history buf duration %d, preroll %d", __func__,
437 hist_buf->hist_buffer_duration_msec,
438 hist_buf->pre_roll_duration_msec);
439 return 0;
440}
441
Quinn Male2e883752019-03-22 11:28:54 -0700442/* ---------------- hw session notify thread --------------- */
443#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
444
445typedef struct {
446 sound_model_handle_t handle;
447 st_session_event_id_t event;
448 struct listnode node; /* membership in queue or pool */
449 uint64_t when;
450} request_t;
451
452static struct {
453 pthread_t thread;
454 bool done;
455 pthread_mutex_t lock;
456 pthread_cond_t cond;
457 struct listnode queue;
458 struct listnode pool;
459 request_t requests[32];
460 hw_session_notify_callback_t callback;
461 bool inited;
462} hw_session_notifier;
463
464static inline int hw_session_notifier_lock()
465{
466 return pthread_mutex_lock(&hw_session_notifier.lock);
467}
468
469static inline int hw_session_notifier_unlock()
470{
471 return pthread_mutex_unlock(&hw_session_notifier.lock);
472}
473
474static inline int hw_session_notifier_wait_l()
475{
476 int ret = -EINVAL;
477
478 if (list_empty(&hw_session_notifier.queue)) {
479 pthread_cond_wait(&hw_session_notifier.cond,
480 &hw_session_notifier.lock);
481 return EINTR; /* wait can only be interrupted */
482 }
483
484 struct listnode *node = list_head(&hw_session_notifier.queue);
485 request_t *r = node_to_item(node, request_t, node);
486 int64_t interval = r->when - get_current_time_ns();
487
488 if (interval <= 0) {
489 ALOGV("early exit\n");
490 return 0;
491 }
492
493 struct timespec timeout;
494 GET_WAIT_TIMESPEC(timeout, interval);
495 ret = pthread_cond_timedwait(&hw_session_notifier.cond,
496 &hw_session_notifier.lock, &timeout);
497 switch (ret) {
498 case 0:
499 ret = EINTR; /* wait was interrupted */
500 break;
501 case ETIMEDOUT:
502 ret = 0; /* time passed expired, proceed with removing first entry from list */
503 break;
504 default:
505 ALOGE("%s: wait failed w/ ret %s\n", __func__, strerror(ret));
506 break;
507 }
508 return ret;
509}
510
511static inline int hw_session_notifier_signal()
512{
513 return pthread_cond_signal(&hw_session_notifier.cond);
514}
515
516int hw_session_notifier_enqueue(sound_model_handle_t handle,
517 st_session_event_id_t event,
518 uint64_t delay_ms)
519{
520 int ret = 0;
521
522 if (!hw_session_notifier.inited)
523 return -ENODEV;
524
525 hw_session_notifier_lock();
526 if (list_empty(&hw_session_notifier.pool)) {
527 ALOGE("%s: No space to queue request, try again", __func__);
528 ret = -EAGAIN;
529 goto exit;
530 }
531
532 struct listnode *req_node = list_head(&hw_session_notifier.pool);
533 list_remove(req_node);
534 request_t *r = node_to_item(req_node, request_t, node);
535 r->handle = handle;
536 r->event = event;
537 r->when = get_current_time_ns() + (delay_ms * NSECS_PER_MSEC);
538
539 struct listnode *node = list_head(&hw_session_notifier.queue);
540 while (node != list_tail(&hw_session_notifier.queue)) {
541 request_t *er = node_to_item(node, request_t, node);
542 if (r->when >= er->when) {
543 /* continue to next node */
544 node = list_head(node);
545 } else {
546 /* insert the element before this node */
547 break;
548 }
549 }
550 /*
551 * queue element before element "node". This is implicitly same
552 * as list->push_back if the element has to be pushed to the
553 * end of the list as in that case, node == head
554 */
555 list_add_tail(node, req_node);
556 hw_session_notifier_signal();
557exit:
558 hw_session_notifier_unlock();
559 return ret;
560}
561
562int hw_session_notifier_cancel(sound_model_handle_t handle,
563 st_session_event_id_t event) {
564 int ret = -1;
565 struct listnode *node, *tmp_node;
566
567 if (!hw_session_notifier.inited)
568 return -1;
569
570 hw_session_notifier_lock();
571 list_for_each_safe(node, tmp_node, &hw_session_notifier.queue) {
572 request_t *r = node_to_item(node, request_t, node);
573 if (r->handle == handle && r->event == event) {
574 ALOGV("%s: found req with handle %d and ev %d to cancel",
575 __func__, handle, event);
576 r->handle = -1;
577 list_remove(node);
578 list_add_tail(&hw_session_notifier.pool, node);
579 ret = 0;
580 }
581 }
582 hw_session_notifier_signal();
583 hw_session_notifier_unlock();
584 return ret;
585}
586
587static void hw_session_notify_process_once_l() {
588 struct listnode *req_node = list_head(&hw_session_notifier.queue);
589 request_t *r = node_to_item(req_node, request_t, node);
590
591 list_remove(req_node);
592 hw_session_notifier_unlock();
593 switch (r->event) {
594 case ST_SES_EV_DEFERRED_STOP:
595 ALOGI("%s:[%d] hw notify deferred stop", __func__, r->handle);
596 hw_session_notifier.callback(r->handle, ST_SES_EV_DEFERRED_STOP);
597 break;
598 default:
599 break;
600 }
601 hw_session_notifier_lock();
602 list_add_tail(&hw_session_notifier.pool, req_node);
603}
604
605void *hw_session_notifier_loop(void *arg __unused) {
606 hw_session_notifier_lock();
607 while (!hw_session_notifier.done) {
608 int ret = hw_session_notifier_wait_l();
609 switch (ret) {
610 case 0: /* timer expired */
611 hw_session_notify_process_once_l();
612 break;
613 case EINTR: /* timer interrupted due to next enqueue or some other event */
614 continue;
615 default:
616 ALOGE("%s: wait_l returned err %d, exit loop!", __func__, ret);
617 hw_session_notifier.done = true;
618 break;
619 }
620 }
621 hw_session_notifier_unlock();
622 ALOGI("%s thread loop exiting", __func__);
623 return NULL;
624}
625
626int hw_session_notifier_init(hw_session_notify_callback_t cb) {
627 pthread_attr_t attr;
628 pthread_condattr_t c_attr;
629
630 if (hw_session_notifier.inited)
631 return -EINVAL;
632
633 hw_session_notifier.inited = false;
634 hw_session_notifier.done = false;
635
636 pthread_mutex_init(&hw_session_notifier.lock, NULL);
637 pthread_condattr_init(&c_attr);
638 pthread_condattr_setclock(&c_attr, CLOCK_MONOTONIC);
639 pthread_cond_init(&hw_session_notifier.cond, &c_attr);
640 pthread_condattr_destroy(&c_attr);
641
642 list_init(&hw_session_notifier.queue);
643 list_init(&hw_session_notifier.pool);
644 for (uint32_t i = 0; i < ARRAY_SIZE(hw_session_notifier.requests); i++) {
645 list_init(&hw_session_notifier.requests[i].node);
646 hw_session_notifier.requests[i].handle = -1;
647 list_add_tail(&hw_session_notifier.pool,
648 &hw_session_notifier.requests[i].node);
649 }
650 pthread_attr_init(&attr);
651 if (pthread_create(&hw_session_notifier.thread, &attr,
652 hw_session_notifier_loop, NULL)) {
653 ALOGE("%s: Failed to create hw_notify thread w/ err %s",
654 __func__,
655 strerror(errno));
656 return -1;
657 }
658 hw_session_notifier.callback = cb;
659 hw_session_notifier.inited = true;
660 ALOGV("%s: completed", __func__);
661 enable_gcov();
662 return 0;
663}
664
665void hw_session_notifier_deinit()
666{
667 if (hw_session_notifier.inited) {
668 hw_session_notifier_lock();
669 hw_session_notifier.done = true;
670 hw_session_notifier_signal();
671 hw_session_notifier_unlock();
672 pthread_join(hw_session_notifier.thread, NULL);
673 hw_session_notifier.inited = false;
674 pthread_mutex_destroy(&hw_session_notifier.lock);
675 pthread_cond_destroy(&(hw_session_notifier.cond));
676 ALOGV("%s: completed", __func__);
677 }
678 enable_gcov();
679}