1 /*
2 INTEL CONFIDENTIAL
3 Copyright 2009 Intel Corporation All Rights Reserved.
4 The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material contains trade secrets and proprietary and confidential information of Intel or its suppliers and licensors. The Material is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel’s prior express written permission.
5
6 No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing.
7 */
8
9 /**
10 * SECTION:mixaudio
11 * @short_description: Object to support a single stream playback using hardware accelerated decoder.
12 * @include: mixaudio.h
13 *
14 * #MixAudio object provide thread-safe API for application and/or multimedia framework to take advantage of Intel Smart Sound Technology(TM) driver for hardware audio decode and render.
15 *
16 * Each #MixAudio object represents one streaming session with the Intel Smart Sound driver and provides configuration and control of the decoding and playback options.
17 *
18 * The #MixAudio object also support integration with Intel Audio Manager service.
19 *
20 * An application can utilize the #MixAudio object by calling the following sequence:
21 * <orderedlist numeration="arabic">
22 * <listitem>mix_audio_new() to create a #MixAudio instance.</listitem>
23 * <listitem>mix_audio_initialize() to allocate Intel Smart Sound Technology resource.</listitem>
24 * <listitem>mix_audio_configure() to configure stream parameters.</listitem>
25 * <listitem>mix_audio_decode() can be called repeatedly for decoding and, optionally, rendering.</listitem>
26 * <listitem>mix_audio_start() is called after the 1st mix_audio_decode() method to start rendering.</listitem>
27 * <listitem>mix_audio_stop_drain() is called after the last buffer is passed for decoding in with mix_audio_decode(). </listitem>
28 * <listitem>mix_audio_deinitialize() to free resource once playback is completed.</listitem>
29 * </orderedlist>
30 *
31 * Since mix_audio_decode() is a blocking call during playback, the following methods are called in a seperate thread to control progress:
32 * <itemizedlist>
33 * <listitem>mix_audio_start()</listitem>
34 * <listitem>mix_audio_pause()</listitem>
35 * <listitem>mix_audio_resume()</listitem>
36 * <listitem>mix_audio_stop_drop()</listitem>
37 * </itemizedlist>
38 */
39
40 /**
41 * SECTION:mixaudiotypes
42 * @title: Mix Audio Types
43 * @short_description: Miscellanous types used by #MixAudio API.
44 * @include: mixaudiotypes.h
45 *
46 * Miscellanous types used by #MixAudio API.
47 */
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <fcntl.h>
52 #include <sys/ioctl.h>
53 #include <errno.h>
54 #include <unistd.h>
55 #include <sys/uio.h>
56 #include <string.h>
57
58 #include <glib.h>
59 #include <glib/gprintf.h>
60 #include <mixlog.h>
61 #include "mixaudio.h"
62
63 #ifdef AUDIO_MANAGER
64 #include "amhelper.h"
65 #endif
66
67 #ifndef MIXAUDIO_CURRENT
68 #define MIXAUDIO_CURRENT 0
69 #endif
70 #ifndef MIXAUDIO_AGE
71 #define MIXAUDIO_AGE 0
72 #endif
73
74 /* Include this now but it will change when driver updates.
75 We would want to build against a kernel dev package if that
76 is available.
77 */
78 #include <linux/types.h>
79 #include "intel_sst_ioctl.h"
80 #include "sst_proxy.h"
81
82 #ifdef G_LOG_DOMAIN
83 #undef G_LOG_DOMAIN
84 #define G_LOG_DOMAIN ((gchar*)"mixaudio")
85 #endif
86
87 /**
88 * LPE_DEVICE:
89 *
90 * LPE Device location.
91 */
92 static const char* LPE_DEVICE="/dev/lpe";
93 /* #define LPE_DEVICE "/dev/lpe" */
94
95 #define _LOCK(obj) g_static_rec_mutex_lock(obj);
96 #define _UNLOCK(obj) g_static_rec_mutex_unlock(obj);
97
98 #define _UNLOCK_RETURN(obj, res) { _UNLOCK(obj); return res; }
99
100 typedef enum {
101 MIX_STREAM_PAUSED_DRAINING = MIX_STREAM_LAST,
102 MIX_STREAM_INTERNAL_LAST
103 } MixStreamStateInternal;
104
105
106 MIX_RESULT mix_audio_initialize_default(MixAudio *mix, MixCodecMode mode, MixAudioInitParams *aip, MixDrmParams *drminitparams);
107 MIX_RESULT mix_audio_configure_default(MixAudio *mix, MixAudioConfigParams *audioconfigparams, MixDrmParams *drmparams);
108 MIX_RESULT mix_audio_decode_default(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize);
109 MIX_RESULT mix_audio_capture_encode_default(MixAudio *mix, MixIOVec *iovout, gint iovoutcnt);
110 MIX_RESULT mix_audio_start_default(MixAudio *mix);
111 MIX_RESULT mix_audio_stop_drop_default(MixAudio *mix);
112 MIX_RESULT mix_audio_stop_drain_default(MixAudio *mix);
113 MIX_RESULT mix_audio_pause_default(MixAudio *mix);
114 MIX_RESULT mix_audio_resume_default(MixAudio *mix);
115 MIX_RESULT mix_audio_get_timestamp_default(MixAudio *mix, guint64 *msecs);
116 MIX_RESULT mix_audio_set_mute_default(MixAudio *mix, gboolean mute);
117 MIX_RESULT mix_audio_get_mute_default(MixAudio *mix, gboolean* muted);
118 MIX_RESULT mix_audio_get_max_vol_default(MixAudio *mix, gint *maxvol);
119 MIX_RESULT mix_audio_get_min_vol_default(MixAudio *mix, gint *minvol);
120 MIX_RESULT mix_audio_get_volume_default(MixAudio *mix, gint *currvol, MixVolType type);
121 MIX_RESULT mix_audio_set_volume_default(MixAudio *mix, gint currvol, MixVolType type, gulong msecs, MixVolRamp ramptype);
122 MIX_RESULT mix_audio_deinitialize_default(MixAudio *mix);
123 MIX_RESULT mix_audio_get_stream_state_default(MixAudio *mix, MixStreamState *streamState);
124 MIX_RESULT mix_audio_get_state_default(MixAudio *mix, MixState *state);
125 MIX_RESULT mix_audio_is_am_available_default(MixAudio *mix, MixAudioManager am, gboolean *avail);
126 MIX_RESULT mix_audio_get_output_configuration_default(MixAudio *mix, MixAudioConfigParams **audioconfigparams);
127
128 static gboolean g_IAM_available = FALSE;
129 MIX_RESULT mix_audio_am_unregister(MixAudio *mix, MixAudioConfigParams *audioconfigparams);
130 MIX_RESULT mix_audio_am_register(MixAudio *mix, MixAudioConfigParams *audioconfigparams);
131 MIX_RESULT mix_audio_AM_Change(MixAudioConfigParams *oldparams, MixAudioConfigParams *newparams);
132
133 static void mix_audio_finalize(GObject *obj);
134 G_DEFINE_TYPE (MixAudio, mix_audio, G_TYPE_OBJECT);
135
136 static gboolean has_FW_INFO = FALSE;
137 static struct snd_sst_fw_info cur_FW_INFO = {{0}};
138
139 static MIX_RESULT mix_audio_FW_INFO(MixAudio *mix);
140 static MIX_RESULT mix_audio_SST_SET_PARAMS(MixAudio *mix, MixAudioConfigParams *params);
141 static MIX_RESULT mix_audio_SST_writev(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize);
142 static MIX_RESULT mix_audio_SST_STREAM_DECODE(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize);
143 static void mix_audio_debug_dump(MixAudio *mix);
144
145 static guint g_log_handler=0;
146 static void mix_audio_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data);
147
148 /**
149 * mix_acp_print_params:
150 * @obj: TBD
151 *
152 * This method is to print acp param. It is a hidden implementation within MixAudioConfigParams.
153 */
154 void mix_acp_print_params(MixAudioConfigParams *obj);
155
mix_audio_init(MixAudio * self)156 static void mix_audio_init (MixAudio *self)
157 {
158 self->useIAM = FALSE;
159 self->streamID = 0; // TODO: Find out the invalid value for stream ID when integrates with IAM.
160 self->amStreamID = 0; // TODO: as above
161 self->streamState = MIX_STREAM_NULL;
162 self->encoding = NULL;
163 self->fileDescriptor = -1;
164 self->state = MIX_STATE_UNINITIALIZED;
165 self->codecMode = MIX_CODING_INVALID;
166 self->am_registered = FALSE;
167
168 /* private member initialization */
169 g_static_rec_mutex_init (&self->streamlock);
170 g_static_rec_mutex_init (&self->controllock);
171
172 self->audioconfigparams = NULL;
173 self->deviceState = MIX_AUDIO_DEV_CLOSED;
174
175 #ifdef LPESTUB
176 g_message("MixAudio running in stub mode!");
177 self->ts_last = 0;
178 self->ts_elapsed = 0;
179 #endif
180
181 self->bytes_written=0;
182
183 }
184
185 void _mix_aip_initialize (void);
186
mix_audio_class_init(MixAudioClass * klass)187 static void mix_audio_class_init (MixAudioClass *klass)
188 {
189 GObjectClass *gobject_class = (GObjectClass*)klass;
190
191 gobject_class->finalize = mix_audio_finalize;
192
193 // Init thread before any threads/sync object are used.
194 if (!g_thread_supported ()) g_thread_init (NULL);
195
196 /* Init some global vars */
197 g_IAM_available = FALSE;
198
199 // base implementations
200 klass->initialize = mix_audio_initialize_default;
201 klass->configure = mix_audio_configure_default;
202 klass->decode = mix_audio_decode_default;
203 klass->capture_encode = mix_audio_capture_encode_default;
204 klass->start = mix_audio_start_default;
205 klass->stop_drop = mix_audio_stop_drop_default;
206 klass->stop_drain = mix_audio_stop_drain_default;
207 klass->pause = mix_audio_pause_default;
208 klass->resume = mix_audio_resume_default;
209 klass->get_timestamp = mix_audio_get_timestamp_default;
210 klass->set_mute = mix_audio_set_mute_default;
211 klass->get_mute = mix_audio_get_mute_default;
212 klass->get_max_vol = mix_audio_get_max_vol_default;
213 klass->get_min_vol = mix_audio_get_min_vol_default;
214 klass->get_volume = mix_audio_get_volume_default;
215 klass->set_volume = mix_audio_set_volume_default;
216 klass->deinitialize = mix_audio_deinitialize_default;
217 klass->get_stream_state = mix_audio_get_stream_state_default;
218 klass->get_state = mix_audio_get_state_default;
219 klass->is_am_available = mix_audio_is_am_available_default;
220 klass->get_output_configuration = mix_audio_get_output_configuration_default;
221
222 // Set log handler...
223 if (!g_log_handler)
224 {
225 // Get Environment variable
226 // See mix_audio_log for details
227 const gchar* loglevel = g_getenv("MIX_AUDIO_DEBUG");
228 guint64 ll = 0;
229 if (loglevel)
230 {
231 if (g_strstr_len(loglevel,-1, "0x") == loglevel)
232 {
233 // Hex string
234 ll = g_ascii_strtoull(loglevel+2, NULL, 16);
235 }
236 else
237 {
238 // Decimal string
239 ll = g_ascii_strtoull(loglevel, NULL, 10);
240 }
241 }
242 guint32 mask = (guint32)ll;
243 g_log_handler = g_log_set_handler(G_LOG_DOMAIN, 0xffffffff, mix_audio_log, (gpointer)mask);
244 /*
245 g_debug("DEBUG Enabled");
246 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%s", "LOG Enabled");
247 g_message("MESSAGE Enabled");
248 g_warning("WARNING Enabled");
249 g_critical("CRITICAL Enabled");
250 g_error("ERROR Enabled");
251 */
252 }
253 }
254
mix_audio_log(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)255 static void mix_audio_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
256 {
257 // Log message based on a mask.
258 // Mask could be read from MIX_AUDIO_DEBUG environment variable
259 // mask is a bit mask specifying the message to print. The lsb (0) is "ERROR" and graduating increasing
260 // value as describe in GLogLevelFlags structure. Not that lsb in GLogLevelFlags is not "ERROR" and
261 // here we shifted the log_level to ignore the first 2 values in GLogLevelFlags, making ERROR align to
262 // the lsb.
263 static const gchar* lognames[] = {"error", "critical", "warning", "message", "log", "debug"};
264 guint32 mask = (guint32)user_data & ((G_LOG_LEVEL_MASK & log_level) >> 2);
265 gint index = 0;
266
267 GTimeVal t = {0};
268
269 // convert bit mask back to index.
270 index = ffs(mask) - 1;
271
272 if ((index<0) || (index >= (sizeof(lognames)/sizeof(lognames[0])))) return;
273
274 g_get_current_time(&t);
275 g_printerr("%" G_GUINT64_FORMAT ":%s-%s: %s\n",
276 ((guint64)1000000 * t.tv_sec + (guint64)t.tv_usec),
277 log_domain?log_domain:G_LOG_DOMAIN,
278 lognames[index],
279 message?message:"NULL");
280 }
281
mix_audio_new(void)282 MixAudio *mix_audio_new(void)
283 {
284 MixAudio *ret = g_object_new(MIX_TYPE_AUDIO, NULL);
285
286 return ret;
287 }
288
mix_audio_finalize(GObject * obj)289 void mix_audio_finalize(GObject *obj)
290 {
291 /* clean up here. */
292 MixAudio *mix = MIX_AUDIO(obj);
293
294 if (G_UNLIKELY(!mix)) return;
295
296 /*
297 We are not going to check the thread lock anymore in this method.
298 If a thread is accessing the object it better still have a ref on this
299 object and in that case, this method won't be called.
300
301 The application have to risk access violation if it calls the methods in
302 a thread without actually holding a reference.
303 */
304
305 g_debug("_finalized(). bytes written=%" G_GUINT64_FORMAT, mix->bytes_written);
306
307 g_static_rec_mutex_free (&mix->streamlock);
308 g_static_rec_mutex_free (&mix->controllock);
309
310 if (mix->audioconfigparams)
311 {
312 mix_acp_unref(mix->audioconfigparams);
313 mix->audioconfigparams = NULL;
314 }
315 }
316
mix_audio_ref(MixAudio * mix)317 MixAudio *mix_audio_ref(MixAudio *mix)
318 {
319 if (G_UNLIKELY(!mix)) return NULL;
320
321 return (MixAudio*)g_object_ref(G_OBJECT(mix));
322 }
323
mix_audio_initialize_default(MixAudio * mix,MixCodecMode mode,MixAudioInitParams * aip,MixDrmParams * drminitparams)324 MIX_RESULT mix_audio_initialize_default(MixAudio *mix, MixCodecMode mode, MixAudioInitParams *aip, MixDrmParams *drminitparams)
325 {
326 MIX_RESULT ret = MIX_RESULT_FAIL;
327
328 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
329
330 // TODO: parse and process MixAudioInitParams. It is ignored for now.
331
332 // initialized must be called with both thread-lock held, so no other operation is allowed.
333
334 // try lock stream thread. If failed, a pending _decode/_encode/_drain is ongoing.
335 if (!g_static_rec_mutex_trylock(&mix->streamlock)) return MIX_RESULT_WRONG_STATE;
336
337 // also lock the control thread lock.
338 _LOCK(&mix->controllock);
339
340 if (mix->state == MIX_STATE_UNINITIALIZED)
341 {
342 // Only allowed in uninitialized state.
343 switch (mode)
344 {
345 case MIX_CODING_DECODE:
346 case MIX_CODING_ENCODE:
347 {
348 // Open device. Same flags to open for decode and encode?
349 #ifdef LPESTUB
350 //g_debug("Reading env var LPESTUB_FILE for data output file.\n");
351 //const char* filename = g_getenv("LPESTUB_FILE");
352 gchar *filename = NULL;
353 GError *err = NULL;
354 const gchar* fn = NULL;
355 fn = g_getenv("MIX_AUDIO_OUTPUT");
356 if (fn)
357 mix->fileDescriptor = open(fn, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
358
359 if (mix->fileDescriptor == -1)
360 {
361 mix->fileDescriptor = g_file_open_tmp ("mixaudio.XXXXXX", &filename, &err);
362
363 if (err)
364 {
365 g_warning("Oops, cannot open temp file: Error message: %s", err->message);
366 }
367 else
368 {
369 g_debug("Opening %s as output data file.\n", filename);
370 }
371 }
372 else
373 {
374 g_debug("Opening %s as output data file.\n", fn);
375 }
376 if (filename) g_free(filename);
377 #else
378 g_debug("Opening %s\n", LPE_DEVICE);
379 mix->fileDescriptor = open(LPE_DEVICE, O_RDWR);
380 #endif
381 if (mix->fileDescriptor != -1)
382 {
383 mix->codecMode = mode;
384 mix->state = MIX_STATE_INITIALIZED;
385 ret = MIX_RESULT_SUCCESS;
386 g_debug("open() succeeded. fd=%d", mix->fileDescriptor);
387 }
388 else
389 {
390 ret = MIX_RESULT_LPE_NOTAVAIL;
391 }
392 }
393 break;
394 default:
395 ret = MIX_RESULT_INVALID_PARAM;
396 break;
397 }
398 }
399 else
400 {
401 ret = MIX_RESULT_WRONG_STATE;
402 }
403
404 _UNLOCK(&mix->controllock);
405 _UNLOCK(&mix->streamlock);
406
407 return ret;
408 }
409
mix_audio_am_is_available(void)410 gboolean mix_audio_am_is_available(void)
411 {
412 // return FALSE for now until IAM is available for integration.
413 // TODO: Check IAM
414 return FALSE;
415 }
416
mix_audio_base_am_is_enabled(MixAudio * mix)417 gboolean mix_audio_base_am_is_enabled(MixAudio *mix)
418 {
419 // TODO: Check IAM usage
420 return FALSE;
421 }
422
423 /**
424 * mix_audio_SST_SET_PARAMS:
425 * @mix: #MixAudio object.
426 * @params: Audio parameter used to configure SST.
427 * @returns: #MIX_RESULT indicating configuration result.
428 *
429 * This method setup up a SST stream with the given parameters. Note that even though
430 * this method could succeed and SST stream is setup properly, client may still not be able
431 * to use the session if other condition are met, such as a successfully set-up IAM, if used.
432 */
mix_audio_SST_SET_PARAMS(MixAudio * mix,MixAudioConfigParams * params)433 MIX_RESULT mix_audio_SST_SET_PARAMS(MixAudio *mix, MixAudioConfigParams *params)
434 {
435 MIX_RESULT ret = MIX_RESULT_SUCCESS;
436
437 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
438
439 if (mix->state == MIX_STATE_UNINITIALIZED) return MIX_RESULT_NOT_INIT;
440
441 if (!MIX_IS_AUDIOCONFIGPARAMS(params)) return MIX_RESULT_INVALID_PARAM;
442
443 mix_acp_print_params(params);
444
445 struct snd_sst_params sst_params = {0};
446
447 gboolean converted = mix_sst_params_convert(params, &sst_params);
448
449 if (converted)
450 {
451 // Setup the driver structure
452 // We are assuming the configstream will always be called after open so the codec mode
453 // should already been setup.
454 sst_params.stream_id = mix->streamID;
455 // We are not checking the codecMODE here for out-of-range...assuming we check that
456 // during init...
457 if (mix->codecMode == MIX_CODING_ENCODE)
458 sst_params.ops = STREAM_OPS_CAPTURE;
459 else sst_params.ops = STREAM_OPS_PLAYBACK;
460
461 // hard-coded to support music only.
462 sst_params.stream_type = 0x0; // stream_type 0x00 is STREAM_TYPE_MUSIC per SST doc.
463
464 // SET_PARAMS
465 int retVal = 0;
466
467 #ifdef LPESTUB
468 // Not calling the ioctl
469 #else
470 g_debug("Calling SNDRV_SST_STREAM_SET_PARAMS. fd=%d", mix->fileDescriptor);
471 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_SET_PARAMS, &sst_params);
472 g_debug("_SET_PARAMS returned %d", retVal);
473 #endif
474
475 if (!retVal)
476 {
477 // IOCTL success.
478 switch (sst_params.result)
479 {
480 // Please refers to SST API doc for return value definition.
481 case 5:
482 g_debug("SET_PARAMS succeeded with Stream Parameter Modified.");
483 case 0:
484 // driver says ok, too.
485 ret = MIX_RESULT_SUCCESS;
486 mix->deviceState = MIX_AUDIO_DEV_ALLOCATED;
487 mix->streamState = MIX_STREAM_STOPPED;
488 mix->streamID = sst_params.stream_id;
489 // clear old params
490 if (MIX_IS_AUDIOCONFIGPARAMS(mix->audioconfigparams))
491 {
492 mix_acp_unref(mix->audioconfigparams);
493 mix->audioconfigparams=NULL;
494 }
495 // replace with new one.
496 mix->audioconfigparams = MIX_AUDIOCONFIGPARAMS(mix_params_dup(MIX_PARAMS(params)));
497 // Note: do not set mix->state here because this state may rely op other than SET_PARAMS
498 g_debug("SET_PARAMS succeeded streamID=%d.", mix->streamID);
499 break;
500 case 1:
501 ret = MIX_RESULT_STREAM_NOTAVAIL;
502 g_debug("SET_PARAMS failed STREAM not available.");
503 break;
504 case 2:
505 ret = MIX_RESULT_CODEC_NOTAVAIL;
506 g_debug("SET_PARAMS failed CODEC not available.");
507 break;
508 case 3:
509 ret = MIX_RESULT_CODEC_NOTSUPPORTED;
510 g_debug("SET_PARAMS failed CODEC not supported.");
511 break;
512 case 4:
513 ret = MIX_RESULT_INVALID_PARAM;
514 g_debug("SET_PARAMS failed Invalid Stream Parameters.");
515 break;
516 case 6:
517 g_debug("SET_PARAMS failed Invalid Stream ID.");
518 default:
519 ret = MIX_RESULT_FAIL;
520 g_critical("SET_PARAMS failed unexpectedly. Result code: %u\n", sst_params.result);
521 break;
522 }
523 }
524 else
525 {
526 // log errors
527 ret = MIX_RESULT_SYSTEM_ERRNO;
528 g_debug("Failed to SET_PARAMS. errno:0x%08x. %s\n", errno, strerror(errno));
529 }
530 }
531 else
532 {
533 ret = MIX_RESULT_INVALID_PARAM;
534 }
535
536 return ret;
537 }
538
mix_audio_get_state_default(MixAudio * mix,MixState * state)539 MIX_RESULT mix_audio_get_state_default(MixAudio *mix, MixState *state)
540 {
541 MIX_RESULT ret = MIX_RESULT_SUCCESS;
542 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
543
544 if (state)
545 *state = mix->state;
546 else
547 ret = MIX_RESULT_NULL_PTR;
548
549 return ret;
550 }
551
mix_audio_decode_default(MixAudio * mix,const MixIOVec * iovin,gint iovincnt,guint64 * insize,MixIOVec * iovout,gint iovoutcnt,guint64 * outsize)552 MIX_RESULT mix_audio_decode_default(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize)
553 {
554 MIX_RESULT ret = MIX_RESULT_FAIL;
555
556 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
557
558 if (!g_static_rec_mutex_trylock(&mix->streamlock)) return MIX_RESULT_WRONG_STATE;
559
560 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->streamlock, MIX_RESULT_WRONG_STATE);
561
562 if (MIX_ACP_DECODEMODE(mix->audioconfigparams) == MIX_DECODE_DIRECTRENDER)
563 ret = mix_audio_SST_writev(mix, iovin, iovincnt, insize);
564 else
565 ret = mix_audio_SST_STREAM_DECODE(mix, iovin, iovincnt, insize, iovout, iovoutcnt, outsize);
566
567 _UNLOCK(&mix->streamlock);
568
569 return ret;
570 }
571
mix_audio_deinitialize_default(MixAudio * mix)572 MIX_RESULT mix_audio_deinitialize_default(MixAudio *mix)
573 {
574 MIX_RESULT ret = MIX_RESULT_SUCCESS;
575 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
576
577 if (!g_static_rec_mutex_trylock(&mix->streamlock)) return MIX_RESULT_WRONG_STATE;
578
579 #ifdef AUDIO_MANAGER
580 if (mix->amStreamID && (lpe_stream_unregister(mix->amStreamID) < 0)) {
581 g_debug("lpe_stream_unregister failed\n");
582 //return MIX_RESULT_FAIL; // TODO: not sure what to do here
583 }
584 #endif
585
586 _LOCK(&mix->controllock);
587
588 if (mix->state == MIX_STATE_UNINITIALIZED)
589 ret = MIX_RESULT_SUCCESS;
590 else if ((mix->streamState != MIX_STREAM_STOPPED) && (mix->streamState != MIX_STREAM_NULL))
591 ret = MIX_RESULT_WRONG_STATE;
592 else
593 {
594 if (mix->fileDescriptor != -1)
595 {
596 g_debug("Closing fd=%d\n", mix->fileDescriptor);
597 close(mix->fileDescriptor);
598 mix->fileDescriptor = -1;
599 mix->deviceState = MIX_AUDIO_DEV_CLOSED;
600 }
601 mix->state = MIX_STATE_UNINITIALIZED;
602 }
603
604 mix->bytes_written = 0;
605
606 _UNLOCK(&mix->controllock);
607 _UNLOCK(&mix->streamlock);
608
609 return ret;
610 }
611
612
mix_audio_stop_drop_default(MixAudio * mix)613 MIX_RESULT mix_audio_stop_drop_default(MixAudio *mix)
614 {
615 MIX_RESULT ret = MIX_RESULT_FAIL;
616
617 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
618
619 _LOCK(&mix->controllock);
620
621 if (mix->state != MIX_STATE_CONFIGURED)
622 _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
623
624 // Will call DROP even if we are already stopped. It is needed to unblock any pending write() call.
625 // if (mix->streamState == MIX_STREAM_DRAINING)
626 // ret = MIX_RESULT_WRONG_STATE;
627 // else
628 {
629 int retVal = 0;
630 #ifdef LPESTUB
631 // Not calling ioctl.
632 #else
633 g_debug("Calling SNDRV_SST_STREAM_DROP. fd=%d", mix->fileDescriptor);
634 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_DROP);
635 g_debug("_DROP returned %d", retVal);
636 #endif
637
638 if (!retVal)
639 {
640 mix->streamState = MIX_STREAM_STOPPED;
641 ret = MIX_RESULT_SUCCESS;
642 }
643 else
644 {
645 ret = MIX_RESULT_SYSTEM_ERRNO;
646 g_debug("Failed to stop stream. Error:0x%08x. Unknown stream state.", errno);
647 }
648 }
649
650 _UNLOCK(&mix->controllock);
651
652 return ret;
653 }
654
mix_audio_stop_drain_default(MixAudio * mix)655 MIX_RESULT mix_audio_stop_drain_default(MixAudio *mix)
656 {
657 MIX_RESULT ret = MIX_RESULT_FAIL;
658 int retVal = 0;
659
660 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
661
662 // No need to lock to check vars that won't be changed in this function
663
664 if (g_static_rec_mutex_trylock(&mix->streamlock))
665 {
666 gboolean doDrain = FALSE;
667
668 if (mix->state != MIX_STATE_CONFIGURED)
669 _UNLOCK_RETURN(&mix->streamlock, MIX_RESULT_NOT_CONFIGURED);
670
671 _LOCK(&mix->controllock);
672 {
673 if (mix->streamState == MIX_STREAM_STOPPED)
674 ret = MIX_RESULT_SUCCESS;
675 else if ((mix->streamState == MIX_STREAM_DRAINING) || mix->streamState == MIX_STREAM_PAUSED_DRAINING)
676 ret = MIX_RESULT_WRONG_STATE;
677 else
678 {
679 doDrain = TRUE;
680 g_debug("MIX stream is DRAINING");
681 mix->streamState = MIX_STREAM_DRAINING;
682 }
683 }
684 _UNLOCK(&mix->controllock);
685
686
687 if (doDrain)
688 {
689 // Calling the blocking DRAIN without holding the controllock
690 // TODO: remove this ifdef when API becomes available.
691 #ifdef LPESTUB
692
693 #else
694 //g_debug("Calling SNDRV_SST_STREAM_DRAIN. fd=0x%08x", mix->fileDescriptor);
695 //retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_DRAIN);
696 // g_warning("Calling SNDRV_SST_STREAM_DROP instead of SNDRV_SST_STREAM_DRAIN here since DRAIN is not yet integrated. There may be data loss. fd=%d", mix->fileDescriptor);
697 g_debug("Calling SNDRV_SST_STREAM_DRAIN fd=%d", mix->fileDescriptor);
698 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_DRAIN);
699 g_debug("_DRAIN returned %d", retVal);
700 #endif
701
702 if (retVal)
703 {
704 _LOCK(&mix->controllock);
705 if (mix->streamState != MIX_STREAM_STOPPED)
706 {
707 // DRAIN could return failed if DROP is called during DRAIN.
708 // Any state resulting as a failed DRAIN would be error, execpt STOPPED.
709 ret = MIX_RESULT_SYSTEM_ERRNO;
710 g_debug("Failed to drain stream. Error:0x%08x. Unknown stream state.", errno);
711 }
712 _UNLOCK(&mix->controllock);
713 }
714 else
715 {
716 _LOCK(&mix->controllock);
717 if ((mix->streamState != MIX_STREAM_DRAINING) &&
718 (mix->streamState != MIX_STREAM_STOPPED))
719 {
720 // State is changed while in DRAINING. This should not be allowed and is a bug.
721 g_warning("MIX Internal state error! DRAIN state(%u) changed!",mix->streamState);
722 ret = MIX_RESULT_FAIL;
723 }
724 else
725 {
726 mix->streamState = MIX_STREAM_STOPPED;
727 ret = MIX_RESULT_SUCCESS;
728 }
729 _UNLOCK(&mix->controllock);
730 }
731 }
732
733 _UNLOCK(&mix->streamlock);
734 }
735 else
736 {
737 // Cannot obtain stream lock meaning there's a pending _decode/_encode.
738 // Will not proceed.
739 ret = MIX_RESULT_WRONG_STATE;
740 }
741
742 return ret;
743 }
744
mix_audio_start_default(MixAudio * mix)745 MIX_RESULT mix_audio_start_default(MixAudio *mix)
746 {
747 MIX_RESULT ret = MIX_RESULT_FAIL;
748
749 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
750
751 _LOCK(&mix->controllock);
752
753 if (mix->state != MIX_STATE_CONFIGURED)
754 _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
755
756 if (MIX_ACP_DECODEMODE(mix->audioconfigparams) == MIX_DECODE_DECODERETURN)
757 _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_WRONGMODE);
758
759 // Note this impl return success even if stream is already started.
760 switch (mix->streamState)
761 {
762 case MIX_STREAM_PLAYING:
763 case MIX_STREAM_PAUSED:
764 case MIX_STREAM_PAUSED_DRAINING:
765 ret = MIX_RESULT_SUCCESS;
766 break;
767 case MIX_STREAM_STOPPED:
768 {
769 int retVal = 0;
770 #ifdef LPESTUB
771 // Not calling ioctl.
772 #else
773 g_debug("Calling SNDRV_SST_STREAM_START. fd=%d", mix->fileDescriptor);
774 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_START);
775 g_debug("_START returned %d", retVal);
776 #endif
777 if (retVal)
778 {
779 ret = MIX_RESULT_SYSTEM_ERRNO;
780 g_debug("Fail to START. Error:0x%08x. Stream state unchanged.", errno);
781 mix_audio_debug_dump(mix);
782 }
783 else
784 {
785 mix->streamState = MIX_STREAM_PLAYING;
786 ret = MIX_RESULT_SUCCESS;
787 }
788 }
789 break;
790 case MIX_STREAM_DRAINING:
791 default:
792 ret = MIX_RESULT_WRONG_STATE;
793 break;
794 }
795
796 _UNLOCK(&mix->controllock);
797
798 #ifdef LPESTUB
799 if (MIX_SUCCEEDED(ret))
800 {
801 if (mix->ts_last == 0)
802 {
803 GTimeVal tval = {0};
804 g_get_current_time(&tval);
805 mix->ts_last = 1000ll * tval.tv_sec + tval.tv_usec / 1000;
806 }
807 }
808 #endif
809 return ret;
810 }
811
mix_audio_get_version(guint * major,guint * minor)812 MIX_RESULT mix_audio_get_version(guint* major, guint *minor)
813 {
814 // simulate the way libtool generate version so the number synchronize with the filename.
815 if (major)
816 *major = MIXAUDIO_CURRENT-MIXAUDIO_AGE;
817
818 if (minor)
819 *minor = MIXAUDIO_AGE;
820
821 return MIX_RESULT_SUCCESS;
822 }
823
mix_audio_configure_default(MixAudio * mix,MixAudioConfigParams * audioconfigparams,MixDrmParams * drmparams)824 MIX_RESULT mix_audio_configure_default(MixAudio *mix, MixAudioConfigParams *audioconfigparams, MixDrmParams *drmparams)
825 {
826 MIX_RESULT ret = MIX_RESULT_SUCCESS;
827
828 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
829
830 // param checks
831 if (!MIX_IS_AUDIOCONFIGPARAMS(audioconfigparams)) return MIX_RESULT_NOT_ACP;
832 if (MIX_ACP_DECODEMODE(audioconfigparams) >= MIX_DECODE_LAST) return MIX_RESULT_INVALID_DECODE_MODE;
833 if (!mix_acp_is_streamname_valid(audioconfigparams)) return MIX_RESULT_INVALID_STREAM_NAME;
834
835 // If we cannot lock stream thread, data is flowing and we can't configure.
836 if (!g_static_rec_mutex_trylock(&mix->streamlock)) return MIX_RESULT_WRONG_STATE;
837
838 _LOCK(&mix->controllock);
839
840 // Check all unallowed conditions
841 if (mix->state == MIX_STATE_UNINITIALIZED)
842 ret = MIX_RESULT_NOT_INIT; // Will not allowed if the state is still UNINITIALIZED
843 else if ((mix->codecMode != MIX_CODING_DECODE) && (mix->codecMode != MIX_CODING_ENCODE))
844 ret = MIX_RESULT_WRONGMODE; // This configure is allowed only in DECODE mode.
845 else if ((mix->streamState != MIX_STREAM_STOPPED) && (mix->streamState != MIX_STREAM_NULL))
846 ret = MIX_RESULT_WRONG_STATE;
847
848 if (!MIX_SUCCEEDED(ret))
849 {
850 // Some check failed. Unlock and return.
851 _UNLOCK(&mix->controllock);
852 _UNLOCK(&mix->streamlock);
853 return ret;
854 }
855
856 if (audioconfigparams->audio_manager == MIX_AUDIOMANAGER_INTELAUDIOMANAGER) {
857 mix->useIAM = TRUE;
858 }
859 // now configure stream.
860
861 ret = mix_audio_am_unregister(mix, audioconfigparams);
862
863 if (MIX_SUCCEEDED(ret))
864 {
865 ret = mix_audio_SST_SET_PARAMS(mix, audioconfigparams);
866 }
867
868 if (MIX_SUCCEEDED(ret))
869 {
870 ret = mix_audio_am_register(mix, audioconfigparams);
871 }
872
873 if (MIX_SUCCEEDED(ret))
874 {
875 mix->state = MIX_STATE_CONFIGURED;
876 }
877 else
878 {
879 mix->state = MIX_STATE_INITIALIZED;
880 }
881
882 _UNLOCK(&mix->controllock);
883 _UNLOCK(&mix->streamlock);
884
885 return ret;
886 }
887
mix_audio_get_timestamp_default(MixAudio * mix,guint64 * msecs)888 MIX_RESULT mix_audio_get_timestamp_default(MixAudio *mix, guint64 *msecs)
889 {
890 MIX_RESULT ret = MIX_RESULT_SUCCESS;
891
892 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
893
894 if (!msecs) return MIX_RESULT_NULL_PTR;
895
896 _LOCK(&mix->controllock);
897
898 if (mix->state == MIX_STATE_CONFIGURED)
899 {
900 if ((mix->codecMode == MIX_CODING_DECODE) && (MIX_ACP_DECODEMODE(mix->audioconfigparams) == MIX_DECODE_DECODERETURN))
901 {
902 ret = MIX_RESULT_WRONGMODE;
903 }
904 else {
905
906 unsigned long long ts = 0;
907 int retVal = 0;
908
909 #ifdef LPESTUB
910 // For stubbing, just get system clock.
911 if (MIX_ACP_BITRATE(mix->audioconfigparams) > 0)
912 {
913 // use bytes_written and bitrate
914 // to get times in msec.
915 ts = mix->bytes_written * 8000 / MIX_ACP_BITRATE(mix->audioconfigparams);
916 }
917 else if (mix->ts_last)
918 {
919 GTimeVal tval = {0};
920 g_get_current_time(&tval);
921 ts = 1000ll * tval.tv_sec + tval.tv_usec / 1000;
922 ts -= mix->ts_last;
923 ts += mix->ts_elapsed;
924 }
925 else
926 {
927 ts = 0;
928 }
929 #else
930 g_debug("Calling SNDRV_SST_STREAM_GET_TSTAMP. fd=%d", mix->fileDescriptor);
931 ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_GET_TSTAMP, &ts);
932 #endif
933
934 if (retVal)
935 {
936 ret = MIX_RESULT_SYSTEM_ERRNO;
937 g_debug("_GET_TSTAMP failed. Error:0x%08x", errno);
938 //ret = MIX_RESULT_FAIL;
939 mix_audio_debug_dump(mix);
940 }
941 else
942 {
943 *msecs = ts;
944 g_debug("_GET_TSTAMP returned %" G_GUINT64_FORMAT, ts);
945 }
946 }
947 }
948 else
949 ret = MIX_RESULT_NOT_CONFIGURED;
950
951 _UNLOCK(&mix->controllock);
952
953 return ret;
954 }
955
mix_audio_AM_Change(MixAudioConfigParams * oldparams,MixAudioConfigParams * newparams)956 gboolean mix_audio_AM_Change(MixAudioConfigParams *oldparams, MixAudioConfigParams *newparams)
957 {
958 if (g_strcmp0(oldparams->stream_name, newparams->stream_name) == 0) {
959 return FALSE;
960 }
961
962 return TRUE;
963 }
964
mix_audio_am_unregister(MixAudio * mix,MixAudioConfigParams * audioconfigparams)965 MIX_RESULT mix_audio_am_unregister(MixAudio *mix, MixAudioConfigParams *audioconfigparams)
966 {
967 MIX_RESULT ret = MIX_RESULT_SUCCESS;
968
969 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
970
971 if (mix->am_registered && MIX_IS_AUDIOCONFIGPARAMS(mix->audioconfigparams) && MIX_IS_AUDIOCONFIGPARAMS(audioconfigparams))
972 {
973 // we have 2 params. let's check
974 if ((MIX_ACP_DECODEMODE(mix->audioconfigparams) != MIX_ACP_DECODEMODE(audioconfigparams)) ||
975 mix_audio_AM_Change(mix->audioconfigparams, audioconfigparams)) //TODO: add checking for SST change
976 {
977 // decode mode change.
978 if (mix->amStreamID > 0) {
979 if (lpe_stream_unregister(mix->amStreamID) != 0) {
980 return MIX_RESULT_FAIL;
981 }
982 mix->am_registered = FALSE;
983 }
984 }
985 }
986
987 return ret;
988 }
989
mix_audio_am_register(MixAudio * mix,MixAudioConfigParams * audioconfigparams)990 MIX_RESULT mix_audio_am_register(MixAudio *mix, MixAudioConfigParams *audioconfigparams)
991 {
992 MIX_RESULT ret = MIX_RESULT_SUCCESS;
993
994 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
995
996 gint32 codec_mode = -1;
997
998 if (mix->codecMode == MIX_CODING_DECODE)
999 codec_mode = 0;
1000 else if (mix->codecMode == MIX_CODING_ENCODE)
1001 codec_mode = 1;
1002 else
1003 return MIX_RESULT_FAIL; // TODO: what to do when fail?
1004
1005 #ifdef AUDIO_MANAGER
1006 if (audioconfigparams->stream_name == NULL)
1007 return MIX_RESULT_FAIL;
1008
1009 // if AM is enable, and not_registered, then register
1010 if (mix->useIAM && !mix->am_registered) {
1011 gint32 amStreamID = lpe_stream_register(mix->streamID, "music", audioconfigparams->stream_name, codec_mode);
1012
1013 if (amStreamID == -1){
1014 mix->amStreamID = 0;
1015 return MIX_RESULT_FAIL;
1016 }
1017 else if (amStreamID == -2) { // -2: Direct render not avail, see AM spec
1018 mix->amStreamID = 0;
1019 return MIX_RESULT_DIRECT_NOTAVAIL;
1020 }
1021 mix->am_registered = TRUE;
1022 mix->amStreamID = amStreamID;
1023 }
1024 #endif
1025
1026 return ret;
1027 }
1028
mix_audio_capture_encode_default(MixAudio * mix,MixIOVec * iovout,gint iovoutcnt)1029 MIX_RESULT mix_audio_capture_encode_default(MixAudio *mix, MixIOVec *iovout, gint iovoutcnt)
1030 {
1031 struct iovec *vec;
1032 gint bytes_read;
1033
1034 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1035
1036 // TODO: set count limit
1037 if (iovoutcnt < 1) {
1038 return MIX_RESULT_INVALID_COUNT;
1039 }
1040
1041 if (iovout == NULL)
1042 return MIX_RESULT_NULL_PTR;
1043
1044 vec = (struct iovec *) g_alloca(sizeof(struct iovec) * iovoutcnt);
1045 if (!vec) return MIX_RESULT_NO_MEMORY;
1046
1047 gint i;
1048 for (i=0; i < iovoutcnt; i++)
1049 {
1050 vec[i].iov_base = iovout[i].data;
1051 vec[i].iov_len = iovout[i].size;
1052 }
1053
1054 mix_log(MIX_AUDIO_COMP, MIX_LOG_LEVEL_INFO, "begin readv()\n");
1055 bytes_read = readv(mix->fileDescriptor, vec, iovoutcnt);
1056 mix_log(MIX_AUDIO_COMP, MIX_LOG_LEVEL_INFO, "end readv(), return: %d\n", bytes_read);
1057 if (bytes_read < 0) { // TODO: should not be 0, but driver return 0 right now
1058 mix_log(MIX_AUDIO_COMP, MIX_LOG_LEVEL_ERROR, "return: %d\n", bytes_read);
1059 return MIX_RESULT_FAIL;
1060 }
1061 /*
1062 gint bytes_count=0;
1063 for (i=0; i < iovoutcnt; i++)
1064 {
1065 bytes_count += iovout[i].size;
1066 }
1067 iovout[i].size = bytes_read - bytes_count;
1068 */
1069 return MIX_RESULT_SUCCESS;
1070 }
1071
mix_audio_get_max_vol_default(MixAudio * mix,gint * maxvol)1072 MIX_RESULT mix_audio_get_max_vol_default(MixAudio *mix, gint *maxvol)
1073 {
1074 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1075
1076 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1077
1078 if (!maxvol) return MIX_RESULT_NULL_PTR;
1079
1080 _LOCK(&mix->controllock);
1081
1082 if (!has_FW_INFO)
1083 {
1084 ret = mix_audio_FW_INFO(mix);
1085 }
1086
1087 if (MIX_SUCCEEDED(ret))
1088 {
1089 *maxvol = (gint)cur_FW_INFO.pop_info.max_vol;
1090 }
1091
1092 _UNLOCK(&mix->controllock);
1093
1094 return ret;
1095 }
1096
1097
mix_audio_get_min_vol_default(MixAudio * mix,gint * minvol)1098 MIX_RESULT mix_audio_get_min_vol_default(MixAudio *mix, gint *minvol)
1099 {
1100 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1101
1102 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1103
1104 if (!minvol) return MIX_RESULT_NULL_PTR;
1105
1106 _LOCK(&mix->controllock);
1107
1108 if (!has_FW_INFO)
1109 {
1110 ret = mix_audio_FW_INFO(mix);
1111 }
1112
1113 if (MIX_SUCCEEDED(ret))
1114 {
1115 *minvol = (gint)cur_FW_INFO.pop_info.min_vol;
1116 }
1117
1118 _UNLOCK(&mix->controllock);
1119
1120 return ret;
1121 }
1122
mix_audio_get_stream_state_default(MixAudio * mix,MixStreamState * streamState)1123 MIX_RESULT mix_audio_get_stream_state_default(MixAudio *mix, MixStreamState *streamState)
1124 {
1125 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1126
1127 if (!streamState) return MIX_RESULT_NULL_PTR;
1128
1129 _LOCK(&mix->controllock);
1130
1131 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
1132
1133 // PAUSED_DRAINING is internal state.
1134 if (mix->streamState == MIX_STREAM_PAUSED_DRAINING)
1135 *streamState = MIX_STREAM_PAUSED;
1136 else
1137 *streamState = mix->streamState;
1138
1139 _UNLOCK(&mix->controllock);
1140
1141 return MIX_RESULT_SUCCESS;
1142 }
1143
1144
mix_audio_get_volume_default(MixAudio * mix,gint * currvol,MixVolType type)1145 MIX_RESULT mix_audio_get_volume_default(MixAudio *mix, gint *currvol, MixVolType type)
1146 {
1147 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1148
1149 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1150
1151 struct snd_sst_vol vol = {0};
1152
1153 if (!currvol) return MIX_RESULT_NULL_PTR;
1154 if ((type != MIX_VOL_PERCENT) && (type != MIX_VOL_DECIBELS)) return MIX_RESULT_INVALID_PARAM;
1155
1156 _LOCK(&mix->controllock);
1157
1158 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
1159
1160 vol.stream_id = mix->streamID;
1161
1162 int retVal = 0;
1163
1164 #ifdef LPESTUB
1165 // Not calling.
1166 #else
1167 g_debug("Calling SNDRV_SST_GET_VOL. fd=%d", mix->fileDescriptor);
1168 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_GET_VOL, &vol);
1169 g_debug("SNDRV_SST_GET_VOL returned %d. vol=%d", retVal, vol.volume);
1170 #endif
1171
1172 if (retVal)
1173 {
1174 ret = MIX_RESULT_SYSTEM_ERRNO;
1175 g_debug("_GET_VOL failed. Error:0x%08x", errno);
1176 mix_audio_debug_dump(mix);
1177 }
1178 else
1179 {
1180 gint maxvol = 0;
1181 ret = mix_audio_get_max_vol(mix, &maxvol);
1182
1183 if (MIX_SUCCEEDED(ret))
1184 {
1185 if (type == MIX_VOL_PERCENT)
1186 *currvol = (maxvol!=0)?((vol.volume * 100) / maxvol):0;
1187 else
1188 *currvol = vol.volume;
1189 }
1190 }
1191
1192 _UNLOCK(&mix->controllock);
1193
1194 return ret;
1195 }
1196
mix_audio_get_mute_default(MixAudio * mix,gboolean * muted)1197 MIX_RESULT mix_audio_get_mute_default(MixAudio *mix, gboolean* muted)
1198 {
1199 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1200 return ret;
1201 }
1202
mix_audio_set_mute_default(MixAudio * mix,gboolean mute)1203 MIX_RESULT mix_audio_set_mute_default(MixAudio *mix, gboolean mute)
1204 {
1205 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1206
1207 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1208
1209 struct snd_sst_mute m = { 0 };
1210
1211 if (mute) m.mute = 1;
1212 else m.mute = 0;
1213
1214 _LOCK(&mix->controllock);
1215
1216 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
1217
1218 m.stream_id = mix->streamID;
1219
1220 int retVal = 0;
1221
1222 #ifdef LPESTUB
1223 // Not calling.
1224 #else
1225 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_MUTE, &m);
1226 #endif
1227
1228 if (retVal)
1229 {
1230 //ret = MIX_RESULT_FAIL;
1231 ret = MIX_RESULT_SYSTEM_ERRNO;
1232 g_debug("_MUTE failed. Error:0x%08x", errno);
1233 mix_audio_debug_dump(mix);
1234 }
1235
1236 _UNLOCK(&mix->controllock);
1237
1238 return ret;
1239 }
1240
mix_audio_pause_default(MixAudio * mix)1241 MIX_RESULT mix_audio_pause_default(MixAudio *mix)
1242 {
1243 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1244
1245 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1246
1247 _LOCK(&mix->controllock);
1248
1249 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
1250
1251 if (mix->streamState == MIX_STREAM_PAUSED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_SUCCESS);
1252
1253 if ((mix->streamState != MIX_STREAM_PLAYING) && (mix->streamState != MIX_STREAM_DRAINING))
1254 _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_WRONG_STATE);
1255
1256 int retVal = 0;
1257
1258 #ifdef LPESTUB
1259 // Not calling
1260 #else
1261 g_debug("Calling SNDRV_SST_STREAM_PAUSE. fd=%d", mix->fileDescriptor);
1262 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_PAUSE);
1263 g_debug("_PAUSE returned %d", retVal);
1264 #endif
1265
1266 if (retVal)
1267 {
1268 if (mix->streamState == MIX_STREAM_DRAINING)
1269 {
1270 // if stream state has been DRAINING, DRAIN could become successful during the PAUSE call, but not yet have chance to update streamState since we now hold the lock.
1271 // In this case, the mix_streamState becomes out-of-sync with the actual playback state. PAUSE failed due to stream already STOPPED but mix->streamState remains at "DRAINING"
1272 // On the other hand, we can't let DRAIN hold the lock the entire time.
1273 // We would not know if we fail PAUSE due to DRAINING, or a valid reason.
1274 // Need a better mechanism to sync DRAINING.
1275 // DRAINING is not likely problem for resume, as long as the PAUSED state is set when stream is really PAUSED.
1276 ret = MIX_RESULT_NEED_RETRY;
1277 g_warning("PAUSE failed while DRAINING. Draining could be just completed. Retry needed.");
1278 }
1279 else
1280 {
1281 ret = MIX_RESULT_SYSTEM_ERRNO;
1282 g_debug("_PAUSE failed. Error:0x%08x", errno);
1283 mix_audio_debug_dump(mix);
1284 }
1285 }
1286 else
1287 {
1288 if (mix->streamState == MIX_STREAM_DRAINING)
1289 {
1290 mix->streamState = MIX_STREAM_PAUSED_DRAINING;
1291 }
1292 else
1293 {
1294 mix->streamState = MIX_STREAM_PAUSED;
1295 }
1296 }
1297
1298 _UNLOCK(&mix->controllock);
1299
1300 #ifdef LPESTUB
1301 if (MIX_SUCCEEDED(ret))
1302 {
1303 GTimeVal tval = {0};
1304 g_get_current_time(&tval);
1305 guint64 ts = 1000ll * tval.tv_sec + tval.tv_usec / 1000;
1306 mix->ts_elapsed += ts - mix->ts_last;
1307 mix->ts_last = 0;
1308 }
1309 #endif
1310 return ret;
1311 }
1312
mix_audio_resume_default(MixAudio * mix)1313 MIX_RESULT mix_audio_resume_default(MixAudio *mix)
1314 {
1315 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1316
1317 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1318
1319 _LOCK(&mix->controllock);
1320
1321 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
1322
1323 if ((mix->streamState == MIX_STREAM_PLAYING) || (mix->streamState == MIX_STREAM_DRAINING))
1324 _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_SUCCESS);
1325
1326 if ((mix->streamState != MIX_STREAM_PAUSED_DRAINING) && (mix->streamState != MIX_STREAM_PAUSED))
1327 _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_WRONG_STATE);
1328
1329 int retVal = 0;
1330
1331 #ifdef LPESTUB
1332 // Not calling
1333 #else
1334 g_debug("Calling SNDRV_SST_STREAM_RESUME");
1335 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_RESUME);
1336 g_debug("_STREAM_RESUME returned %d", retVal);
1337 #endif
1338
1339 if (retVal)
1340 {
1341 ret = MIX_RESULT_SYSTEM_ERRNO;
1342 g_debug("_PAUSE failed. Error:0x%08x", errno);
1343 mix_audio_debug_dump(mix);
1344 }
1345 {
1346 if (mix->streamState == MIX_STREAM_PAUSED_DRAINING)
1347 mix->streamState = MIX_STREAM_DRAINING;
1348 else
1349 mix->streamState = MIX_STREAM_PLAYING;
1350 }
1351
1352 _UNLOCK(&mix->controllock);
1353
1354 #ifdef LPESTUB
1355 if (MIX_SUCCEEDED(ret))
1356 {
1357 GTimeVal tval = {0};
1358 g_get_current_time(&tval);
1359 guint64 ts = 1000ll * tval.tv_sec + tval.tv_usec / 1000;
1360 mix->ts_last = ts;
1361 }
1362 #endif
1363
1364 return ret;
1365 }
1366
mix_audio_set_volume_default(MixAudio * mix,gint currvol,MixVolType type,gulong msecs,MixVolRamp ramptype)1367 MIX_RESULT mix_audio_set_volume_default(MixAudio *mix, gint currvol, MixVolType type, gulong msecs, MixVolRamp ramptype)
1368 {
1369 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1370
1371 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1372
1373 struct snd_sst_vol vol = {0};
1374
1375 vol.ramp_duration = msecs;
1376 vol.ramp_type = ramptype; // TODO: confirm the mappings between Mix and SST.
1377
1378 if (!mix) return MIX_RESULT_NULL_PTR;
1379
1380 if ((type != MIX_VOL_PERCENT) && (type != MIX_VOL_DECIBELS)) return MIX_RESULT_INVALID_PARAM;
1381
1382 _LOCK(&mix->controllock);
1383
1384 if (mix->state != MIX_STATE_CONFIGURED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_CONFIGURED);
1385
1386 vol.stream_id = mix->streamID;
1387
1388 if (type == MIX_VOL_DECIBELS)
1389 {
1390 vol.volume = currvol;
1391 }
1392 else
1393 {
1394 gint maxvol = 0;
1395 ret = mix_audio_get_max_vol(mix, &maxvol);
1396
1397 if (!maxvol)
1398 g_critical("Max Vol is 0!");
1399
1400 if (MIX_SUCCEEDED(ret))
1401 {
1402 vol.volume = currvol * maxvol / 100;
1403 }
1404 }
1405
1406 int retVal = 0;
1407
1408 #ifdef LPESTUB
1409 // Not calling
1410 #else
1411 g_debug("calling SNDRV_SST_SET_VOL vol=%d", vol.volume);
1412 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_SET_VOL, &vol);
1413 g_debug("SNDRV_SST_SET_VOL returned %d", retVal);
1414 #endif
1415
1416 if (retVal)
1417 {
1418 ret = MIX_RESULT_SYSTEM_ERRNO;
1419 g_debug("_SET_VOL failed. Error:0x%08x", errno);
1420 mix_audio_debug_dump(mix);
1421 }
1422
1423 _UNLOCK(&mix->controllock);
1424
1425 return ret;
1426 }
1427
mix_audio_FW_INFO(MixAudio * mix)1428 MIX_RESULT mix_audio_FW_INFO(MixAudio *mix)
1429 {
1430 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1431
1432 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1433
1434 _LOCK(&mix->controllock);
1435
1436 // This call always get the fw info.
1437 int retVal = 0;
1438
1439 #ifdef LPESTUB
1440 // Not calling.
1441 #else
1442 g_debug("calling SNDRV_SST_FW_INFO fd=%d", mix->fileDescriptor);
1443 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_FW_INFO, &cur_FW_INFO);
1444 g_debug("SNDRV_SST_FW_INFO returned %d", retVal);
1445 #endif
1446
1447 if (!retVal)
1448 {
1449 has_FW_INFO = TRUE;
1450 }
1451 else
1452 {
1453 ret = MIX_RESULT_SYSTEM_ERRNO;
1454 g_debug("_FW_INFO failed. Error:0x%08x", errno);
1455 mix_audio_debug_dump(mix);
1456 }
1457
1458 _UNLOCK(&mix->controllock);
1459
1460 return ret;
1461 }
1462
1463
mix_audio_SST_writev(MixAudio * mix,const MixIOVec * iovin,gint iovincnt,guint64 * insize)1464 static MIX_RESULT mix_audio_SST_writev(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize)
1465 {
1466 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1467
1468 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1469
1470 /*
1471 definition of "struct iovec" used by writev:
1472 struct iovec {
1473 void *iov_base;
1474 size_t iov_len;
1475 };
1476 */
1477
1478 if (!mix) return MIX_RESULT_NULL_PTR;
1479
1480 size_t total_bytes = 0;
1481 // NOTE: we may want to find a way to avoid this copy.
1482 struct iovec *in = (struct iovec*)g_alloca(sizeof(struct iovec) * iovincnt);
1483 if (!in) return MIX_RESULT_NO_MEMORY;
1484
1485 int i;
1486 for (i=0;i<iovincnt;i++)
1487 {
1488 in[i].iov_base = (void*)iovin[i].data;
1489 in[i].iov_len = (size_t)iovin[i].size;
1490 total_bytes += in[i].iov_len;
1491 }
1492
1493 ssize_t written = 0;
1494
1495 #ifdef LPESTUB
1496 gulong wait_time = 0; //wait time in second.
1497 if (MIX_ACP_BITRATE(mix->audioconfigparams) > 0)
1498 {
1499 wait_time = total_bytes*8*1000*1000/MIX_ACP_BITRATE(mix->audioconfigparams);
1500 // g_debug("To wait %lu usec for writev() to simulate blocking\n", wait_time);
1501 }
1502 GTimer *timer = g_timer_new();
1503 g_timer_start(timer);
1504
1505 g_debug("calling writev(fd=%d)", mix->fileDescriptor);
1506 written = writev(mix->fileDescriptor, in, iovincnt);
1507 if (written >= 0) mix->bytes_written += written;
1508 g_debug("writev() returned %d. Total %" G_GUINT64_FORMAT, written, mix->bytes_written);
1509 /* Now since writing to file rarely block, we put timestamp there to block.*/
1510 g_timer_stop(timer);
1511 gulong elapsed = 0;
1512 g_timer_elapsed(timer, &elapsed);
1513 g_timer_destroy(timer);
1514 // g_debug("writev() returned in %lu usec\n", elapsed);
1515 if ((MIX_ACP_BITRATE(mix->audioconfigparams) > 0) && (wait_time > elapsed))
1516 {
1517 wait_time -= elapsed;
1518 g_usleep(wait_time);
1519 }
1520 #else
1521 g_debug("calling writev(fd=%d) with %d", mix->fileDescriptor, total_bytes);
1522 written = writev(mix->fileDescriptor, in, iovincnt);
1523 if (written > 0) mix->bytes_written += written;
1524 g_debug("writev() returned %d. Total %" G_GUINT64_FORMAT, written, mix->bytes_written);
1525 #endif
1526
1527 if (written < 0)
1528 {
1529 ret = MIX_RESULT_SYSTEM_ERRNO;
1530 g_debug("writev() failed. Error:0x%08x", errno);
1531 }
1532 else
1533 {
1534 // guranttee written is positive value before sign extending it.
1535 if (insize) *insize = (guint64)written;
1536 if (written != total_bytes)
1537 {
1538 g_warning("writev() wrote only %d out of %d", written, total_bytes);
1539 }
1540 }
1541
1542 return ret;
1543 }
1544
mix_audio_SST_STREAM_DECODE(MixAudio * mix,const MixIOVec * iovin,gint iovincnt,guint64 * insize,MixIOVec * iovout,gint iovoutcnt,guint64 * outsize)1545 static MIX_RESULT mix_audio_SST_STREAM_DECODE(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize)
1546 {
1547 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1548 int retVal = 0;
1549
1550 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1551
1552 if ((iovout == NULL) || (iovoutcnt <= 0))
1553 {
1554 g_critical("Wrong mode. Please report a bug...");
1555 return MIX_RESULT_NULL_PTR;
1556 }
1557
1558 g_message("Input entries=%d. Output entries=%d", iovincnt, iovoutcnt);
1559
1560 struct snd_sst_buff_entry *ientries = NULL;
1561 struct snd_sst_buff_entry *oentries = NULL;
1562
1563 ientries = (struct snd_sst_buff_entry*)g_alloca(sizeof(struct snd_sst_buff_entry) * iovincnt);
1564 oentries = (struct snd_sst_buff_entry*)g_alloca(sizeof(struct snd_sst_buff_entry) * iovoutcnt);
1565
1566 if (!ientries || !oentries) return MIX_RESULT_NO_MEMORY;
1567
1568 struct snd_sst_dbufs dbufs = {0};
1569
1570 struct snd_sst_buffs ibuf = {0};
1571 struct snd_sst_buffs obuf = {0};
1572
1573 ibuf.entries = iovincnt;
1574 ibuf.type = SST_BUF_USER;
1575 ibuf.buff_entry = ientries;
1576
1577 obuf.entries = iovoutcnt;
1578 obuf.type = SST_BUF_USER;
1579 obuf.buff_entry = oentries;
1580
1581 dbufs.ibufs = &ibuf;
1582 dbufs.obufs = &obuf;
1583
1584 int i = 0;
1585 for (i=0;i<iovincnt;i++)
1586 {
1587 ientries[i].size = (unsigned long)iovin[i].size;
1588 ientries[i].buffer = (void *)iovin[i].data;
1589 g_debug("Creating in entry#%d, size=%u", i, ientries[i].size);
1590 }
1591
1592 for (i=0;i<iovoutcnt;i++)
1593 {
1594 oentries[i].size = (unsigned long)iovout[i].size;
1595 oentries[i].buffer = (void *)iovout[i].data;
1596 g_debug("Creating out entry#%d, size=%u", i, oentries[i].size);
1597 }
1598
1599 #ifdef LPESTUB
1600 size_t total_bytes = 0;
1601 // NOTE: we may want to find a way to avoid this copy.
1602 struct iovec *in = (struct iovec*)g_alloca(sizeof(struct iovec) * iovincnt);
1603 if (iovincnt>1)
1604 {
1605 for (i=0;i<iovincnt-1;i++)
1606 {
1607 in[i].iov_base = (void*)iovin[i].data;
1608 in[i].iov_len = (size_t)iovin[i].size;
1609 total_bytes += in[i].iov_len;
1610 }
1611 in[i].iov_base = (void*)iovin[i].data;
1612 in[i].iov_len = (size_t)iovin[i].size/2;
1613 total_bytes += in[i].iov_len;
1614 }
1615 else
1616 {
1617 for (i=0;i<iovincnt;i++)
1618 {
1619 in[i].iov_base = (void*)iovin[i].data;
1620 in[i].iov_len = (size_t)iovin[i].size;
1621 total_bytes += in[i].iov_len;
1622 }
1623 }
1624 ssize_t written = 0;
1625
1626 g_debug("calling stub STREAM_DECODE (writev) (fd=%d)", mix->fileDescriptor);
1627 written = writev(mix->fileDescriptor, in, iovincnt);
1628 if (written >= 0)
1629 {
1630 mix->bytes_written += written;
1631 dbufs.output_bytes_produced = written;
1632 dbufs.input_bytes_consumed = written;
1633 }
1634 g_debug("stub STREAM_DECODE (writev) returned %d. Total %" G_GUINT64_FORMAT, written, mix->bytes_written);
1635 #else
1636 g_debug("calling SNDRV_SST_STREAM_DECODE fd=%d", mix->fileDescriptor);
1637 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_DECODE, &dbufs);
1638 g_debug("SNDRV_SST_STREAM_DECODE returned %d", retVal);
1639 #endif
1640
1641 if (retVal)
1642 {
1643 ret = MIX_RESULT_SYSTEM_ERRNO;
1644 g_debug("_STREAM_DECODE failed. Error:0x%08x", errno);
1645 mix_audio_debug_dump(mix);
1646 }
1647 else
1648 {
1649 if (insize) *insize = dbufs.input_bytes_consumed;
1650 if (outsize) *outsize = dbufs.output_bytes_produced;
1651 g_message("consumed=%" G_GUINT64_FORMAT " produced=%" G_GUINT64_FORMAT, dbufs.input_bytes_consumed, dbufs.output_bytes_produced);
1652 }
1653
1654 return ret;
1655 }
1656
1657 // Starting interface
1658 //MIX_RESULT mix_audio_get_version(guint* major, guint *minor);
1659
mix_audio_initialize(MixAudio * mix,MixCodecMode mode,MixAudioInitParams * aip,MixDrmParams * drminitparams)1660 MIX_RESULT mix_audio_initialize(MixAudio *mix, MixCodecMode mode, MixAudioInitParams *aip, MixDrmParams *drminitparams)
1661 {
1662 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1663
1664 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1665
1666 mix_log(MIX_AUDIO_COMP, MIX_LOG_LEVEL_VERBOSE, "mix_audio_initialize\n");
1667
1668 if (!klass->initialize)
1669 return MIX_RESULT_FAIL; // TODO: add more descriptive error
1670
1671 #ifdef AUDIO_MANAGER
1672 if (dbus_init() < 0) {
1673 mix_log(MIX_AUDIO_COMP, MIX_LOG_LEVEL_ERROR, "Failed to connect to dbus\n");
1674 // commented out, gracefully exit right now
1675 // return MIX_RESULT_FAIL; // TODO: add more descriptive error
1676 }
1677 #endif
1678
1679 return klass->initialize(mix, mode, aip, drminitparams);
1680 }
1681
mix_audio_configure(MixAudio * mix,MixAudioConfigParams * audioconfigparams,MixDrmParams * drmparams)1682 MIX_RESULT mix_audio_configure(MixAudio *mix, MixAudioConfigParams *audioconfigparams, MixDrmParams *drmparams)
1683 {
1684 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1685
1686 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1687
1688 if (!klass->configure)
1689 return MIX_RESULT_FAIL;
1690
1691 return klass->configure(mix, audioconfigparams, drmparams);
1692 }
1693
mix_audio_decode(MixAudio * mix,const MixIOVec * iovin,gint iovincnt,guint64 * insize,MixIOVec * iovout,gint iovoutcnt,guint64 * outsize)1694 MIX_RESULT mix_audio_decode(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize)
1695 {
1696 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1697
1698 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1699
1700 if (!klass->decode)
1701 return MIX_RESULT_FAIL;
1702
1703 return klass->decode(mix, iovin, iovincnt, insize, iovout, iovoutcnt, outsize);
1704 }
1705
mix_audio_capture_encode(MixAudio * mix,MixIOVec * iovout,gint iovoutcnt)1706 MIX_RESULT mix_audio_capture_encode(MixAudio *mix, MixIOVec *iovout, gint iovoutcnt)
1707 {
1708 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1709
1710 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1711
1712 if (!klass->capture_encode)
1713 return MIX_RESULT_FAIL;
1714
1715 return klass->capture_encode(mix, iovout, iovoutcnt);
1716 }
1717
mix_audio_start(MixAudio * mix)1718 MIX_RESULT mix_audio_start(MixAudio *mix)
1719 {
1720 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1721
1722 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1723
1724 if (!klass->start)
1725 return MIX_RESULT_FAIL;
1726
1727 return klass->start(mix);
1728 }
1729
mix_audio_stop_drop(MixAudio * mix)1730 MIX_RESULT mix_audio_stop_drop(MixAudio *mix)
1731 {
1732 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1733
1734 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1735
1736 if (!klass->stop_drop)
1737 return MIX_RESULT_FAIL;
1738
1739 return klass->stop_drop(mix);
1740 }
1741
mix_audio_stop_drain(MixAudio * mix)1742 MIX_RESULT mix_audio_stop_drain(MixAudio *mix)
1743 {
1744 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1745
1746 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1747
1748 if (!klass->stop_drain)
1749 return MIX_RESULT_FAIL;
1750
1751 return klass->stop_drain(mix);
1752 }
1753
mix_audio_pause(MixAudio * mix)1754 MIX_RESULT mix_audio_pause(MixAudio *mix)
1755 {
1756 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1757
1758 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1759
1760 if (!klass->pause)
1761 return MIX_RESULT_FAIL;
1762
1763 return klass->pause(mix);
1764 }
1765
mix_audio_resume(MixAudio * mix)1766 MIX_RESULT mix_audio_resume(MixAudio *mix)
1767 {
1768 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1769
1770 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1771
1772 if (!klass->resume)
1773 return MIX_RESULT_FAIL;
1774
1775 return klass->resume(mix);
1776 }
1777
mix_audio_get_timestamp(MixAudio * mix,guint64 * msecs)1778 MIX_RESULT mix_audio_get_timestamp(MixAudio *mix, guint64 *msecs)
1779 {
1780 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1781
1782 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1783
1784 if (!klass->get_timestamp)
1785 return MIX_RESULT_FAIL;
1786
1787 return klass->get_timestamp(mix, msecs);
1788 }
1789
mix_audio_get_mute(MixAudio * mix,gboolean * muted)1790 MIX_RESULT mix_audio_get_mute(MixAudio *mix, gboolean* muted)
1791 {
1792 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1793
1794 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1795
1796 if (!klass->get_mute)
1797 return MIX_RESULT_FAIL;
1798
1799 return klass->get_mute(mix, muted);
1800 }
1801
mix_audio_set_mute(MixAudio * mix,gboolean mute)1802 MIX_RESULT mix_audio_set_mute(MixAudio *mix, gboolean mute)
1803 {
1804 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1805
1806 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1807
1808 if (!klass->set_mute)
1809 return MIX_RESULT_FAIL;
1810
1811 return klass->set_mute(mix, mute);
1812 }
1813
mix_audio_get_max_vol(MixAudio * mix,gint * maxvol)1814 MIX_RESULT mix_audio_get_max_vol(MixAudio *mix, gint *maxvol)
1815 {
1816 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1817
1818 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1819
1820 if (!klass->get_max_vol)
1821 return MIX_RESULT_FAIL;
1822
1823 return klass->get_max_vol(mix, maxvol);
1824 }
1825
mix_audio_get_min_vol(MixAudio * mix,gint * minvol)1826 MIX_RESULT mix_audio_get_min_vol(MixAudio *mix, gint *minvol)
1827 {
1828 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1829
1830 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1831
1832 if (!klass->get_min_vol)
1833 return MIX_RESULT_FAIL;
1834
1835 return klass->get_min_vol(mix, minvol);
1836 }
1837
mix_audio_get_volume(MixAudio * mix,gint * currvol,MixVolType type)1838 MIX_RESULT mix_audio_get_volume(MixAudio *mix, gint *currvol, MixVolType type)
1839 {
1840 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1841
1842 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1843
1844 if (!klass->get_volume)
1845 return MIX_RESULT_FAIL;
1846
1847 return klass->get_volume(mix, currvol, type);
1848 }
1849
mix_audio_set_volume(MixAudio * mix,gint currvol,MixVolType type,gulong msecs,MixVolRamp ramptype)1850 MIX_RESULT mix_audio_set_volume(MixAudio *mix, gint currvol, MixVolType type, gulong msecs, MixVolRamp ramptype)
1851 {
1852 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1853
1854 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1855
1856 if (!klass->set_volume)
1857 return MIX_RESULT_FAIL;
1858
1859 return klass->set_volume(mix, currvol, type, msecs, ramptype);
1860 }
1861
mix_audio_deinitialize(MixAudio * mix)1862 MIX_RESULT mix_audio_deinitialize(MixAudio *mix)
1863 {
1864 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1865
1866 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1867
1868 if (!klass->deinitialize)
1869 return MIX_RESULT_FAIL;
1870
1871 return klass->deinitialize(mix);
1872 }
1873
mix_audio_get_stream_state(MixAudio * mix,MixStreamState * streamState)1874 MIX_RESULT mix_audio_get_stream_state(MixAudio *mix, MixStreamState *streamState)
1875 {
1876 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1877
1878 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1879
1880 if (!klass->get_stream_state)
1881 return MIX_RESULT_FAIL;
1882
1883 return klass->get_stream_state(mix, streamState);
1884 }
1885
mix_audio_get_state(MixAudio * mix,MixState * state)1886 MIX_RESULT mix_audio_get_state(MixAudio *mix, MixState *state)
1887 {
1888 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
1889
1890 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1891
1892 if (!klass->get_state)
1893 return MIX_RESULT_FAIL;
1894
1895 return klass->get_state(mix, state);
1896 }
1897
mix_audio_is_am_available_default(MixAudio * mix,MixAudioManager am,gboolean * avail)1898 MIX_RESULT mix_audio_is_am_available_default(MixAudio *mix, MixAudioManager am, gboolean *avail)
1899 {
1900 MIX_RESULT ret = MIX_RESULT_SUCCESS;
1901
1902 if (avail)
1903 *avail = FALSE;
1904 else
1905 ret = MIX_RESULT_NULL_PTR;
1906
1907 return ret;
1908 }
1909
mix_audio_is_am_available(MixAudio * mix,MixAudioManager am,gboolean * avail)1910 MIX_RESULT mix_audio_is_am_available(MixAudio *mix, MixAudioManager am, gboolean *avail)
1911 {
1912 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
1913
1914 if (!klass->is_am_available)
1915 return MIX_RESULT_FAIL;
1916
1917 return klass->is_am_available(mix, am, avail);
1918 }
1919
1920 const gchar* dbgstr_UNKNOWN="UNKNOWN";
1921
_mix_stream_state_get_name(MixStreamState s)1922 static const gchar* _mix_stream_state_get_name (MixStreamState s)
1923 {
1924 static const gchar *MixStreamStateNames[] = {
1925 "MIX_STREAM_NULL",
1926 "MIX_STREAM_STOPPED",
1927 "MIX_STREAM_PLAYING",
1928 "MIX_STREAM_PAUSED",
1929 "MIX_STREAM_DRAINING",
1930 "MIX_STREAM_PAUSED_DRAINING",
1931 "MIX_STREAM_INTERNAL_LAST"
1932 };
1933
1934 const gchar *ret = dbgstr_UNKNOWN;
1935
1936 if (s < sizeof(MixStreamStateNames)/sizeof(MixStreamStateNames[0]))
1937 {
1938 ret = MixStreamStateNames[s];
1939 }
1940
1941 return ret;
1942 }
1943
_mix_state_get_name(MixState s)1944 static const gchar* _mix_state_get_name(MixState s)
1945 {
1946 static const gchar* MixStateNames[] = {
1947 "MIX_STATE_NULL",
1948 "MIX_STATE_UNINITIALIZED",
1949 "MIX_STATE_INITIALIZED",
1950 "MIX_STATE_CONFIGURED",
1951 "MIX_STATE_LAST"
1952 };
1953
1954 const gchar *ret = dbgstr_UNKNOWN;
1955
1956 if (s < sizeof(MixStateNames)/sizeof(MixStateNames[0]))
1957 {
1958 ret = MixStateNames[s];
1959 }
1960
1961 return ret;
1962 }
1963
_mix_codec_mode_get_name(MixCodecMode s)1964 static const gchar* _mix_codec_mode_get_name(MixCodecMode s)
1965 {
1966 static const gchar* MixCodecModeNames[] = {
1967 "MIX_CODING_INVALID",
1968 "MIX_CODING_ENCODE",
1969 "MIX_CODING_DECODE",
1970 "MIX_CODING_LAST"
1971 };
1972
1973 const gchar *ret = dbgstr_UNKNOWN;
1974
1975 if (s < sizeof(MixCodecModeNames)/sizeof(MixCodecModeNames[0]))
1976 {
1977 ret = MixCodecModeNames[s];
1978 }
1979
1980 return ret;
1981 }
1982
_mix_device_state_get_name(MixDeviceState s)1983 static const gchar* _mix_device_state_get_name(MixDeviceState s)
1984 {
1985 static const gchar* MixDeviceStateNames[] = {
1986 "MIX_AUDIO_DEV_CLOSED",
1987 "MIX_AUDIO_DEV_OPENED",
1988 "MIX_AUDIO_DEV_ALLOCATED"
1989 };
1990
1991 const gchar *ret = dbgstr_UNKNOWN;
1992
1993 if (s < sizeof(MixDeviceStateNames)/sizeof(MixDeviceStateNames[0]))
1994 {
1995 ret = MixDeviceStateNames[s];
1996 }
1997
1998 return ret;
1999 }
2000
mix_audio_debug_dump(MixAudio * mix)2001 void mix_audio_debug_dump(MixAudio *mix)
2002 {
2003 const gchar* prefix="MixAudio:";
2004
2005 if (!MIX_IS_AUDIO(mix))
2006 {
2007 g_debug("%s Not a valid MixAudio object.", prefix);
2008 return;
2009 }
2010
2011 g_debug("%s streamState(%s)", prefix, _mix_stream_state_get_name(mix->streamState));
2012 g_debug("%s encoding(%s)", prefix, mix->encoding?mix->encoding:dbgstr_UNKNOWN);
2013 g_debug("%s fileDescriptor(%d)", prefix, mix->fileDescriptor);
2014 g_debug("%s state(%s)", prefix, _mix_state_get_name(mix->state));
2015 g_debug("%s codecMode(%s)", prefix, _mix_codec_mode_get_name(mix->codecMode));
2016
2017 // Private members
2018 g_debug("%s streamID(%d)", prefix, mix->streamID);
2019 //GStaticRecMutex streamlock; // lock that must be acquired to invoke stream method.
2020 //GStaticRecMutex controllock; // lock that must be acquired to call control function.
2021 if (MIX_IS_AUDIOCONFIGPARAMS(mix->audioconfigparams))
2022 {
2023 // TODO: print audioconfigparams
2024 }
2025 else
2026 {
2027 g_debug("%s audioconfigparams(NULL)", prefix);
2028 }
2029
2030 g_debug("%s deviceState(%s)", prefix, _mix_device_state_get_name(mix->deviceState));
2031
2032 g_debug("%s ts_last(%" G_GUINT64_FORMAT ")", prefix, mix->ts_last);
2033 g_debug("%s ts_elapsed(%" G_GUINT64_FORMAT ")", prefix, mix->ts_elapsed);
2034 g_debug("%s bytes_written(%" G_GUINT64_FORMAT ")", prefix, mix->bytes_written);
2035
2036 return;
2037 }
2038
mix_audio_get_output_configuration(MixAudio * mix,MixAudioConfigParams ** audioconfigparams)2039 MIX_RESULT mix_audio_get_output_configuration(MixAudio *mix, MixAudioConfigParams **audioconfigparams)
2040 {
2041 if (G_UNLIKELY(!mix)) return MIX_RESULT_NULL_PTR;
2042
2043 MixAudioClass *klass = MIX_AUDIO_GET_CLASS(mix);
2044
2045 if (!klass->get_output_configuration)
2046 return MIX_RESULT_FAIL;
2047
2048 return klass->get_output_configuration(mix, audioconfigparams);
2049 }
2050
mix_audio_get_output_configuration_default(MixAudio * mix,MixAudioConfigParams ** audioconfigparams)2051 MIX_RESULT mix_audio_get_output_configuration_default(MixAudio *mix, MixAudioConfigParams **audioconfigparams)
2052 {
2053 MIX_RESULT ret = MIX_RESULT_SUCCESS;
2054 struct snd_sst_get_stream_params stream_params = {{0}};
2055 MixAudioConfigParams *p = NULL;
2056 int retVal = 0;
2057
2058 if (G_UNLIKELY(!mix || !audioconfigparams)) return MIX_RESULT_NULL_PTR;
2059
2060 _LOCK(&mix->controllock);
2061
2062 if (mix->state <= MIX_STATE_UNINITIALIZED) _UNLOCK_RETURN(&mix->controllock, MIX_RESULT_NOT_INIT);
2063
2064 #ifdef LPESTUB
2065 #else
2066 // Check only if we are initialized.
2067 g_debug("Calling SNDRV_SST_STREAM_GET_PARAMS. fd=%d", mix->fileDescriptor);
2068 retVal = ioctl(mix->fileDescriptor, SNDRV_SST_STREAM_GET_PARAMS, &stream_params);
2069 g_debug("_GET_PARAMS returned %d", retVal);
2070 #endif
2071
2072 _UNLOCK(&mix->controllock);
2073
2074 if (retVal)
2075 {
2076 ret = MIX_RESULT_SYSTEM_ERRNO;
2077 g_debug("Failed to GET_PARAMS. errno:0x%08x. %s\n", errno, strerror(errno));
2078 }
2079 else
2080 {
2081 p = mix_sst_params_to_acp(&stream_params);
2082 *audioconfigparams = p;
2083 }
2084
2085 return ret;
2086 }
2087
mix_audio_get_stream_byte_decoded(MixAudio * mix,guint64 * byte)2088 MIX_RESULT mix_audio_get_stream_byte_decoded(MixAudio *mix, guint64 *byte)
2089 {
2090 return MIX_RESULT_NOT_SUPPORTED;
2091 }
2092
2093