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 */
92static 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
100typedef enum {
101  MIX_STREAM_PAUSED_DRAINING = MIX_STREAM_LAST,
102  MIX_STREAM_INTERNAL_LAST
103} MixStreamStateInternal;
104
105
106MIX_RESULT mix_audio_initialize_default(MixAudio *mix, MixCodecMode mode, MixAudioInitParams *aip, MixDrmParams *drminitparams);
107MIX_RESULT mix_audio_configure_default(MixAudio *mix, MixAudioConfigParams *audioconfigparams, MixDrmParams *drmparams);
108MIX_RESULT mix_audio_decode_default(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize);
109MIX_RESULT mix_audio_capture_encode_default(MixAudio *mix, MixIOVec *iovout, gint iovoutcnt);
110MIX_RESULT mix_audio_start_default(MixAudio *mix);
111MIX_RESULT mix_audio_stop_drop_default(MixAudio *mix);
112MIX_RESULT mix_audio_stop_drain_default(MixAudio *mix);
113MIX_RESULT mix_audio_pause_default(MixAudio *mix);
114MIX_RESULT mix_audio_resume_default(MixAudio *mix);
115MIX_RESULT mix_audio_get_timestamp_default(MixAudio *mix, guint64 *msecs);
116MIX_RESULT mix_audio_set_mute_default(MixAudio *mix, gboolean mute);
117MIX_RESULT mix_audio_get_mute_default(MixAudio *mix, gboolean* muted);
118MIX_RESULT mix_audio_get_max_vol_default(MixAudio *mix, gint *maxvol);
119MIX_RESULT mix_audio_get_min_vol_default(MixAudio *mix, gint *minvol);
120MIX_RESULT mix_audio_get_volume_default(MixAudio *mix, gint *currvol, MixVolType type);
121MIX_RESULT mix_audio_set_volume_default(MixAudio *mix, gint currvol, MixVolType type, gulong msecs, MixVolRamp ramptype);
122MIX_RESULT mix_audio_deinitialize_default(MixAudio *mix);
123MIX_RESULT mix_audio_get_stream_state_default(MixAudio *mix, MixStreamState *streamState);
124MIX_RESULT mix_audio_get_state_default(MixAudio *mix, MixState *state);
125MIX_RESULT mix_audio_is_am_available_default(MixAudio *mix, MixAudioManager am, gboolean *avail);
126MIX_RESULT mix_audio_get_output_configuration_default(MixAudio *mix, MixAudioConfigParams **audioconfigparams);
127
128static gboolean g_IAM_available = FALSE;
129MIX_RESULT mix_audio_am_unregister(MixAudio *mix, MixAudioConfigParams *audioconfigparams);
130MIX_RESULT mix_audio_am_register(MixAudio *mix, MixAudioConfigParams *audioconfigparams);
131MIX_RESULT mix_audio_AM_Change(MixAudioConfigParams *oldparams, MixAudioConfigParams *newparams);
132
133static void mix_audio_finalize(GObject *obj);
134G_DEFINE_TYPE (MixAudio, mix_audio, G_TYPE_OBJECT);
135
136static gboolean has_FW_INFO = FALSE;
137static struct snd_sst_fw_info cur_FW_INFO = {{0}};
138
139static MIX_RESULT mix_audio_FW_INFO(MixAudio *mix);
140static MIX_RESULT mix_audio_SST_SET_PARAMS(MixAudio *mix, MixAudioConfigParams *params);
141static MIX_RESULT mix_audio_SST_writev(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize);
142static MIX_RESULT mix_audio_SST_STREAM_DECODE(MixAudio *mix, const MixIOVec *iovin, gint iovincnt, guint64 *insize, MixIOVec *iovout, gint iovoutcnt, guint64 *outsize);
143static void mix_audio_debug_dump(MixAudio *mix);
144
145static guint g_log_handler=0;
146static 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*/
154void mix_acp_print_params(MixAudioConfigParams *obj);
155
156static 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
185void _mix_aip_initialize (void);
186
187static 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
255static 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
282MixAudio *mix_audio_new(void)
283{
284  MixAudio *ret = g_object_new(MIX_TYPE_AUDIO, NULL);
285
286  return ret;
287}
288
289void 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
317MixAudio *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
324MIX_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
410gboolean 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
417gboolean 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 */
433MIX_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
539MIX_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
552MIX_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
572MIX_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
613MIX_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
655MIX_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
745MIX_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
812MIX_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
824MIX_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
888MIX_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
956gboolean 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
965MIX_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
990MIX_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
1029MIX_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
1072MIX_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
1098MIX_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
1123MIX_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
1145MIX_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
1197MIX_RESULT mix_audio_get_mute_default(MixAudio *mix, gboolean* muted)
1198{
1199  MIX_RESULT ret = MIX_RESULT_SUCCESS;
1200  return ret;
1201}
1202
1203MIX_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
1241MIX_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
1313MIX_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
1367MIX_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
1428MIX_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
1464static 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
1545static 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
1660MIX_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
1682MIX_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
1694MIX_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
1706MIX_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
1718MIX_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
1730MIX_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
1742MIX_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
1754MIX_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
1766MIX_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
1778MIX_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
1790MIX_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
1802MIX_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
1814MIX_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
1826MIX_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
1838MIX_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
1850MIX_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
1862MIX_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
1874MIX_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
1886MIX_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
1898MIX_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
1910MIX_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
1920const gchar* dbgstr_UNKNOWN="UNKNOWN";
1921
1922static 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
1944static 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
1964static 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
1983static 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
2001void 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
2039MIX_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
2051MIX_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
2088MIX_RESULT mix_audio_get_stream_byte_decoded(MixAudio *mix, guint64 *byte)
2089{
2090    return MIX_RESULT_NOT_SUPPORTED;
2091}
2092
2093