/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief Audio Interface Services.
 *
 * The Audio Interface (AI) is aimed at managing audio decoding, and file system
 * navigation.
 * When using the Audio Interface, the user does not have to directly interface with
 * File System API (provided in nav_auto.h, navigation.h, file.h, playlist.h, fat.h, ...).
 * This greatly simplifies software architecture.
 *
 * The Audio Interface can be used in 2 different ways:
 *
 * - Using "asynchronous" functions, i.e. which result/effect may
 *   not be produced immediatly. Using these functions usually leads to the use of
 *   state-machines in the user firmware (since we must wait for the completion of
 *   a command before launching a new one), but has the advantage of reducing the
 *   risks of audio "blips".
 *
 * - Using "synchronous" functions, i.e. which are executed immediatly. This drastically
 *   simplifies the user firmware architecture (we do not have to use state-machines since
 *   the synchronous AI functions are immediately executed) but *may* produce audio
 *   "blips" since the execution time of these functions can lead to audio underrun.
 *
 * Each function of the Audio Interface has a "synchronous" and "asynchronous"
 * interface. For example, the command which returns the number of drives is:
 * ai_async_nav_drive_nb() in the "asynchronous" interface.
 * ai_nav_drive_nb()       in the "synchronous" interface.
 *
 * Using "asynchronous" functions shall be the preferred solution in order to avoid
 * as much as possible audio "blips".
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices can be used.
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 *****************************************************************************/

/* Copyright (c) 2009 Atmel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an Atmel
 * AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 *
 */

//_____  I N C L U D E S ___________________________________________________

#include <string.h>
#include "compiler.h"
#include "conf_audio_interface.h"

#include "audio_interface.h"

//_____ M A C R O S ________________________________________________________


//_____ D E F I N I T I O N S ______________________________________________

static   cmd_ai_id_t g_ai_cmdid;
static   U32   g_ai_u32_in;
static   U8    g_ai_status;
static   U32   g_ai_u32_out;
static   U64   g_ai_u64_out;
static   U16   g_ai_u16_arg_size;
static   char* g_ai_char_arg;

#ifdef AI_ASYNC_STACK_NB_ELEMENTS
static ai_async_context_t ai_context_bp[AI_ASYNC_STACK_NB_ELEMENTS];
static ai_async_context_t *ai_context_sp;
#endif

U8       g_retry_cnt;

volatile Bool  g_b_ai_cmd_receiv;

#define AI_CMD_RETRY_RELOAD        3

#if (!defined(AI_SYNC_TASK_CALL_BACK))
#  define AI_SYNC_TASK_CALL_BACK()
#endif

ai_async_status_t g_ai_cmd_execution_status;

//_____ D E C L A R A T I O N S ____________________________________________


Bool  ai_read_string             ( Bool (*fcnt_read_string)(char*, U8), char** string, U16 *size_string );
U16   ai_song_read_info          ( U16 (*fcnt_song_read_info)(char**), char** string );

// Handle specific commands
static Bool ai_specific_async_cmd_task(cmd_ai_id_t cmd_id, U32 u32_in, U16 *u16_arg_size, char **u8_arg_in, U32 *u32_out, U64 *u64_out);

#if (!defined USB_MS_INCLUDED) && (!defined IPOD_INCLUDED)
#error These should be defined to something!
#endif
#if (defined USB_MS_INCLUDED) && (USB_MS_INCLUDED == DISABLE) && (defined IPOD_INCLUDED) && (IPOD_INCLUDED == DISABLE)
#error Please define at least one of (USB_MS_INCLUDED, IPOD_INCLUDED)
#endif

#if (defined USB_MS_INCLUDED) && (USB_MS_INCLUDED == ENABLE)
extern const ai_fct_set ai_usb_ms_fct_set;
#endif
#if (defined IPOD_INCLUDED) && (IPOD_INCLUDED == ENABLE)
extern const ai_fct_set ai_ipod_fct_set;
#endif

#define AI_USB_MS   0
#define AI_IPOD     1

const ai_fct_set *ai_fct_set_tab[2];

U8 current_ai_fct_set = AI_USB_MS;


//! Initialize the audio interface
//!
void  ai_init( void )
{
#if (defined USB_MS_INCLUDED) && (USB_MS_INCLUDED == ENABLE)
  ai_fct_set_tab[AI_USB_MS] = &ai_usb_ms_fct_set;
#endif
#if (defined IPOD_INCLUDED) && (IPOD_INCLUDED == ENABLE)
  ai_fct_set_tab[AI_IPOD] = &ai_ipod_fct_set;
#endif

#if (defined USB_MS_INCLUDED) && (USB_MS_INCLUDED == ENABLE)
  ai_fct_set_tab[AI_USB_MS]->init();
#endif
#if (defined IPOD_INCLUDED) && (IPOD_INCLUDED == ENABLE)
  ai_fct_set_tab[AI_IPOD]->init();
#endif

  g_b_ai_cmd_receiv = FALSE;
  g_ai_status       = TRUE;        // By default command OK

#ifdef AI_ASYNC_STACK_NB_ELEMENTS
  // Set the context stack pointer at the bottom of the stack
  ai_context_sp = ai_context_bp + AI_ASYNC_STACK_NB_ELEMENTS;
#endif
}

void  ai_task(void)
{
#if (IPOD_INCLUDED == ENABLE)
  current_ai_fct_set = (ipod_task_get_state() == IPOD_TASK_STATE_CONNECTION_READY) ? AI_IPOD : AI_USB_MS;
#endif
}

//!
//!-------- ASYNCHRONOUS routines --------
//! note: to use these routines, you must call specific routine to know the end of execution and take the result
//!

void ai_async_cmd_task( void )
{
  St_ai_info_version ver;

  if ( !g_b_ai_cmd_receiv )
    return;

  g_ai_u32_out = 0;           // By default resultat U32 is NULL
  g_ai_u64_out = 0;           // By default resultat U64 is NULL

  if( ai_fct_set_tab[current_ai_fct_set]==NULL )
  {
    g_ai_cmd_execution_status = CMD_ERROR;
    g_ai_status               = FALSE;
    g_b_ai_cmd_receiv         = FALSE;
    return;
  }
  else
  {
    g_ai_cmd_execution_status = CMD_EXECUTING;
    g_ai_status               = TRUE;        // By default command OK
  }

  switch ( g_ai_cmdid )
  {
  case CMD_AI_IS_DEVICE_CONNECTED    :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->is_device_connected(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_GET_PRODUCT_ID         :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->get_product_id(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_GET_VENDOR_ID          :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->get_vendor_id(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_GET_SERIAL_NUMBER      :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->get_serial_number(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DRIVE_NB           :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_drive_nb(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DRIVE_SET          :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_drive_set(g_ai_u32_in, &g_ai_cmd_execution_status );
    break;
  case CMD_AI_NAV_DRIVE_GET          :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_drive_get(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DRIVE_MOUNT        :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_drive_mount(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DRIVE_TOTAL_SPACE  :
    g_ai_u64_out = ai_fct_set_tab[current_ai_fct_set]->nav_drive_total_space(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DRIVE_FREE_SPACE   :
    g_ai_u64_out = ai_fct_set_tab[current_ai_fct_set]->nav_drive_free_space(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DIR_ROOT           :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_dir_root(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DIR_CD             :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_dir_cd(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DIR_GOTOPARENT     :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_dir_gotoparent(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DIR_NAME           :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->nav_dir_name( &g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_FILE_NAME          :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->nav_file_name( &g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_FILE_ISDIR         :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_isdir(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_FILE_NEXT          :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_file_next(&g_ai_cmd_execution_status );
    break;
  case CMD_AI_NAV_FILE_PREVIOUS      :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_file_previous(&g_ai_cmd_execution_status );
    break;
  case CMD_AI_NAV_FILE_GOTO          :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_file_goto(g_ai_u32_in, &g_ai_cmd_execution_status );
    break;
  case CMD_AI_NAV_FILE_POS           :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_pos(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_FILE_NB            :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_nb(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_DIR_NB             :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_dir_nb(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_PLAYLIST_NB        :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_playlist_nb(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_GETPLAYFILE        :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->nav_getplayfile(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_TYPE          :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_type(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_VERSION       :
    ver = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_version(&g_ai_cmd_execution_status);
    g_ai_u32_out = ver.digit.major << 16 |
                   ver.digit.minor << 8 |
                   ver.digit.revision;
    break;
  case CMD_AI_NAV_INFO_TITLE         :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_title(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_ARTIST        :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_artist(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_ALBUM         :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_album(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_YEAR          :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_year(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_TRACK         :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_track(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_GENRE         :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_genre(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_DURATION      :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->nav_file_info_duration(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_NAV_INFO_IMAGE         :
    g_ai_u32_out = (U32) ai_fct_set_tab[current_ai_fct_set]->nav_file_info_image((ai_image_size_t *) g_ai_u32_in, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CONTEXT_SAVE     :
    g_ai_char_arg = (char*) ai_fct_set_tab[current_ai_fct_set]->audio_context_save(&g_ai_status, &g_ai_u16_arg_size, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CONTEXT_RESTORE  :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_context_restore((void*) g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_PLAYFILE     :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_nav_playfile(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_NEXT         :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_nav_next(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_PREV         :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_nav_previous(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_EOF_OCCUR    :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_nav_eof_occur(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_NB           :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_nb(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_GETPOS       :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_getpos(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_SETPOS       :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_setpos(g_ai_u32_in, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_GETNAME      :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->audio_nav_getname( &g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_TYPE    :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_type(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_VERSION :
    ver = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_version(&g_ai_cmd_execution_status);
    g_ai_u32_out = ver.digit.major << 16 |
                   ver.digit.minor << 8 |
                   ver.digit.revision;
    break;
  case CMD_AI_AUDIO_NAV_INFO_TITLE   :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_title(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_ARTIST  :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_artist(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_ALBUM   :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_album(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_YEAR    :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_year(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_TRACK   :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_track(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_GENRE   :
    g_ai_u16_arg_size = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_genre(&g_ai_char_arg, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_DURATION:
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_duration(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_INFO_IMAGE   :
    g_ai_u32_out = (U32) ai_fct_set_tab[current_ai_fct_set]->audio_nav_file_info_image((ai_image_size_t *) g_ai_u32_in, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_REPEAT_GET   :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_repeat_get(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_REPEAT_SET   :
    ai_fct_set_tab[current_ai_fct_set]->audio_nav_repeat_set(g_ai_u32_in, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_EXPMODE_GET  :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_expmode_get(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_EXPMODE_SET  :
    ai_fct_set_tab[current_ai_fct_set]->audio_nav_expmode_set( g_ai_u32_in, &g_ai_cmd_execution_status );
    break;
  case CMD_AI_AUDIO_NAV_SHUFFLE_GET  :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_nav_shuffle_get(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_NAV_SHUFFLE_SET  :
    ai_fct_set_tab[current_ai_fct_set]->audio_nav_shuffle_set( g_ai_u32_in, &g_ai_cmd_execution_status );
    break;
  case CMD_AI_AUDIO_CTRL_STOP        :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_stop(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_RESUME      :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_resume(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_PAUSE       :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_pause(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_TIME        :
    g_ai_u32_out = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_time(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_STATUS      :
    g_ai_u32_out = (U32)(ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_status(&g_ai_cmd_execution_status)).all;
    break;
  case CMD_AI_AUDIO_CTRL_FFW         :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_ffw(g_ai_u32_in, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_FRW         :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_frw(g_ai_u32_in, &g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_START_FFW   :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_start_ffw(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_START_FRW   :
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_start_frw(&g_ai_cmd_execution_status);
    break;
  case CMD_AI_AUDIO_CTRL_STOP_FFW_FRW:
    g_ai_status = ai_fct_set_tab[current_ai_fct_set]->audio_ctrl_stop_ffw_frw(&g_ai_cmd_execution_status);
    break;
  default:
    // Specific commands
    g_ai_status = ai_specific_async_cmd_task(g_ai_cmdid,
                                             g_ai_u32_in,
                                             &g_ai_u16_arg_size,
                                             &g_ai_char_arg,
                                             &g_ai_u32_out,
                                             &g_ai_u64_out);
  }

  if (g_ai_cmd_execution_status == CMD_EXECUTING) return;

#ifdef AI_ASYNC_STACK_NB_ELEMENTS
  // Set the context stack pointer at the bottom of the stack
  ai_context_sp = ai_context_bp + AI_ASYNC_STACK_NB_ELEMENTS;
#endif

  // Make response to send
  g_b_ai_cmd_receiv = FALSE;
}

Bool ai_async_context_push(ai_async_context_t *ctx)
{
#ifdef AI_ASYNC_STACK_NB_ELEMENTS
  if (ai_context_sp == ai_context_bp)
    return FALSE;

  ai_context_sp--;
  memcpy(ai_context_sp, ctx, sizeof(ai_async_context_t));
  return TRUE;
#else
  return FALSE;
#endif
}

Bool ai_async_context_pop(ai_async_context_t *ctx)
{
#ifdef AI_ASYNC_STACK_NB_ELEMENTS
  if (ai_context_sp == ai_context_bp + AI_ASYNC_STACK_NB_ELEMENTS)
    return FALSE;

  memcpy(ctx, ai_context_sp, sizeof(ai_async_context_t));
  ai_context_sp++;
  return TRUE;
#else
  return FALSE;
#endif
}

void ai_async_update_cmd_status(ai_async_status_t *cmd_ai_status)
{
#ifdef AI_ASYNC_STACK_NB_ELEMENTS
  ai_async_context_t ctx;

  // If no elements on the stack, then don't do anything
  if (ai_context_sp == ai_context_bp + AI_ASYNC_STACK_NB_ELEMENTS)
    return;
  // If an error has been previously raised, then keep it
  if (*cmd_ai_status == CMD_ERROR)
    return;

  // Else read the current context
  memcpy(&ctx, ai_context_sp, sizeof(ai_async_context_t));

  // Update the status
  if (ctx.status == CMD_ERROR)
    *cmd_ai_status = CMD_ERROR;
  else if (*cmd_ai_status == CMD_DONE)
    *cmd_ai_status = ctx.status;
#endif
}

Bool  is_ai_async_cmd_finished( void )
{
  return (!g_b_ai_cmd_receiv);
}

U8    ai_async_cmd_out_status( void )
{
  if (g_ai_cmd_execution_status == CMD_ERROR)
    return (U8) FALSE;
  return g_ai_status;
}

U32   ai_async_cmd_out_u32( void )
{
  return g_ai_u32_out;
}

U64   ai_async_cmd_out_u64( void )
{
  return g_ai_u64_out;
}

void  ai_async_cmd_out_free_ArrayU8( void )
{
  if (g_ai_u16_arg_size != 0)
  {
    free( g_ai_char_arg );
    g_ai_char_arg = NULL;
    g_ai_u16_arg_size = 0;
  }
}

U16   ai_async_cmd_out_SizeArrayU8( void )
{
  return g_ai_u16_arg_size;
}

char*   ai_async_cmd_out_PtrArrayU8( void )
{
  return g_ai_char_arg;
}

//! This routine may be used to send directly a command values from a communication port to audio interface
//! @note you can use the ai_async_cmd_put() routine or ai_async_X() routines to send an asynchronous command
Bool ai_async_cmd_put(cmd_ai_id_t cmd_id, U32 u32_in, U16 u16_arg_size, char* u8_arg_in, Bool need_extra_buffer)
{
  if ( g_b_ai_cmd_receiv )
    return FALSE;
  g_ai_cmdid         = cmd_id;
  g_ai_u32_in        = u32_in;
  if (need_extra_buffer)
  {
    ai_async_cmd_out_free_ArrayU8();
    g_ai_u16_arg_size  = u16_arg_size;
    g_ai_char_arg      = u8_arg_in;
  }
  g_b_ai_cmd_receiv  = TRUE;
  g_retry_cnt        = AI_CMD_RETRY_RELOAD;
  return TRUE;
}

static Bool ai_specific_async_cmd_task(cmd_ai_id_t cmd_id, U32 u32_in, U16 *u16_arg_size, char **u8_arg_in, U32 *u32_out, U64 *u64_out)
{
  return ai_fct_set_tab[current_ai_fct_set]->specific_async_cmd_task(cmd_id, u32_in, u16_arg_size, u8_arg_in, u32_out, u64_out, &g_ai_cmd_execution_status);
}



//!
//!-------- SYNCHRONOUS routines --------
//!

#define AI_SYNC_FUNC_RET_STRUCT( ret_type, func_name, arg_type, arg_val, err_ret )   \
ret_type ai_##func_name ( arg_type arg_val )   \
{                                              \
  ret_type var;                                \
  var.all=(err_ret);                           \
  if( !ai_async_##func_name(arg_val) )         \
    return var;                                \
                                               \
  do                                           \
  {                                            \
    AI_SYNC_TASK_CALL_BACK();                  \
    ai_async_cmd_task();                       \
  }while( !is_ai_async_cmd_finished() );       \
                                               \
  var.all=ai_async_cmd_out_u32();              \
  return var;                                  \
}

#define AI_SYNC_FUNC_RET_VALUE( ret_type, func_name, arg_type, arg_val, err_ret )   \
ret_type ai_##func_name ( arg_type arg_val )   \
{                                              \
  if( !ai_async_##func_name(arg_val) )         \
    return (err_ret);                          \
                                               \
  do                                           \
  {                                            \
    AI_SYNC_TASK_CALL_BACK();                  \
    ai_async_cmd_task();                       \
  }while( !is_ai_async_cmd_finished() );       \
                                               \
  return (ret_type)ai_async_cmd_out_u32();     \
}

#define AI_SYNC_FUNC_RET_VOID( func_name, arg_type, arg_val  ) \
void ai_##func_name ( arg_type arg_val )       \
{                                              \
  if( !ai_async_##func_name(arg_val) )         \
    return ;                                   \
                                               \
  do                                           \
  {                                            \
    AI_SYNC_TASK_CALL_BACK();                  \
    ai_async_cmd_task();                       \
  }while( !is_ai_async_cmd_finished() );       \
}

#define AI_SYNC_FUNC_MALLOC( ret_type, func_name, pp_arg, err_ret )   \
ret_type ai_##func_name ( char** pp_arg )          \
{                                                  \
  if( !ai_async_##func_name() )                    \
    return (err_ret);                              \
                                                   \
  do                                               \
  {                                                \
    AI_SYNC_TASK_CALL_BACK();                      \
    ai_async_cmd_task();                           \
  }while( !is_ai_async_cmd_finished() );           \
                                                   \
  *pp_arg= ai_async_cmd_out_PtrArrayU8();          \
  return (ret_type)ai_async_cmd_out_SizeArrayU8(); \
}

//                      Return type            Func name              arg type  arg     error value
AI_SYNC_FUNC_RET_VALUE( Bool,                  is_device_connected,   void,     ,           0);

//                      Return type            Func name              arg type  arg     error value
AI_SYNC_FUNC_RET_VALUE( U16,                   get_product_id,        void,             , 0);
AI_SYNC_FUNC_RET_VALUE( U16,                   get_vendor_id,         void,, 0);

//                      Return type            Func name              string name       error value
AI_SYNC_FUNC_MALLOC(    U16,                   get_serial_number,     pp_sz_sn, 0 );


// File navigation
// ===============
//                      Return type            Func name              arg type  arg       error value
AI_SYNC_FUNC_RET_VALUE( U8,                    nav_drive_nb,          void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_drive_set,         U8,       u8_number , 0);
AI_SYNC_FUNC_RET_VALUE( U8,                    nav_drive_get,         void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_drive_mount,       void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U64,                   nav_drive_total_space, void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U64,                   nav_drive_free_space,  void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_dir_root,          void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_dir_cd,            void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_dir_gotoparent,    void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_file_isdir,        void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_file_next,         void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_file_previous,     void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_file_goto,         U32,      u32_newpos, 0);
AI_SYNC_FUNC_RET_VALUE( U16,                   nav_file_pos,          void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U16,                   nav_file_nb,           void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U16,                   nav_dir_nb,            void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U16,                   nav_playlist_nb,       void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U32,                   nav_file_info_type,    void,     ,           0);
AI_SYNC_FUNC_RET_STRUCT(St_ai_info_version,    nav_file_info_version, void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U16,                   nav_file_info_year,    void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U32,                   nav_file_info_track,   void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( U32,                   nav_file_info_duration,void,     ,           0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  nav_getplayfile,       void,     ,           0);

//                      Return type            Func name              string name       error value
AI_SYNC_FUNC_MALLOC(    U16,                   nav_dir_name,          ppstring,         0);
AI_SYNC_FUNC_MALLOC(    U16,                   nav_file_name,         ppstring,         0);
AI_SYNC_FUNC_MALLOC(    U16,                   nav_file_info_title,   ppsz_title,       0);
AI_SYNC_FUNC_MALLOC(    U16,                   nav_file_info_artist,  ppsz_artist,      0);
AI_SYNC_FUNC_MALLOC(    U16,                   nav_file_info_album,   ppsz_album,       0);
AI_SYNC_FUNC_MALLOC(    U16,                   nav_file_info_genre,   ppsz_genre,       0);



// Audio navigation
// ================
//                      Return type            Func name              arg type  arg       error value
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_nav_playfile,    void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_context_restore, void*,    ctx,      0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_nav_next,        void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_nav_previous,    void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_nav_eof_occur,   void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( U16,                   audio_nav_nb,          void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( U16,                   audio_nav_getpos,      void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_nav_setpos,      U16,      u16_pos,  0);
AI_SYNC_FUNC_RET_VALUE( Ai_repeat_mode,        audio_nav_repeat_get,  void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( Ai_shuffle_mode,       audio_nav_shuffle_get, void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( Ai_explorer_mode,      audio_nav_expmode_get, void,     ,         0);

//                      Func name              arg type  arg       error value
AI_SYNC_FUNC_RET_VOID(  audio_nav_repeat_set,  Ai_repeat_mode,     repeat_mode);
AI_SYNC_FUNC_RET_VOID(  audio_nav_shuffle_set, Ai_shuffle_mode,    shuffle );
AI_SYNC_FUNC_RET_VOID(  audio_nav_expmode_set, Ai_explorer_mode,   exp_mode );



//                      Return type            Func name                     arg type  arg       error value
AI_SYNC_FUNC_RET_VALUE( U32,                   audio_nav_file_info_type,     void,     ,         0);
AI_SYNC_FUNC_RET_STRUCT(St_ai_info_version,    audio_nav_file_info_version,  void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( U16,                   audio_nav_file_info_year,     void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( U32,                   audio_nav_file_info_track,    void,     ,         0);
AI_SYNC_FUNC_RET_VALUE( U32,                   audio_nav_file_info_duration, void,     ,         0);

//                      Return type            Func name                    string name       error value
AI_SYNC_FUNC_MALLOC(    U16,                   audio_nav_getname,           ppstring,         0);
AI_SYNC_FUNC_MALLOC(    U16,                   audio_nav_file_info_title,   ppsz_title,       0);
AI_SYNC_FUNC_MALLOC(    U16,                   audio_nav_file_info_artist,  ppsz_artist,      0);
AI_SYNC_FUNC_MALLOC(    U16,                   audio_nav_file_info_album,   ppsz_album,       0);
AI_SYNC_FUNC_MALLOC(    U16,                   audio_nav_file_info_genre,   ppsz_genre,       0);



// Audio control
// =============
//                      Return type            Func name                arg type  arg             error value
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_stop,         void,     ,               0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_resume,       void,     ,               0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_pause,        void,     ,               0);
AI_SYNC_FUNC_RET_VALUE( U32,                   audio_ctrl_time,         void,     ,               0);
AI_SYNC_FUNC_RET_STRUCT(Ai_player_flag_t,      audio_ctrl_status,       void,     ,               0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_ffw,          U32,      u32_duration,   0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_frw,          U32,      u32_duration,   0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_start_ffw,    void,     ,               0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_start_frw,    void,     ,               0);
AI_SYNC_FUNC_RET_VALUE( Bool,                  audio_ctrl_stop_ffw_frw, void,     ,               0);

void *ai_audio_context_save( U8 *status, U16 *size )
{
  if( !ai_async_audio_context_save() )
  {
    *status = FALSE;
    return NULL;
  }

  do
  {
    AI_SYNC_TASK_CALL_BACK();
    ai_async_cmd_task();
  }while( !is_ai_async_cmd_finished() );

  *status = ai_async_cmd_out_status();
  *size   = ai_async_cmd_out_SizeArrayU8();
  return (void *)ai_async_cmd_out_PtrArrayU8();
}

void *ai_nav_file_info_image(ai_image_size_t *size)
{
  if (!ai_async_nav_file_info_image(size))
    return NULL;

  do
  {
    AI_SYNC_TASK_CALL_BACK();
    ai_async_cmd_task();
  } while( !is_ai_async_cmd_finished() );

  return (void *) ai_async_cmd_out_u32();
}

void *ai_audio_nav_file_info_image(ai_image_size_t *size)
{
  if (!ai_async_audio_nav_file_info_image(size))
    return NULL;

  do
  {
    AI_SYNC_TASK_CALL_BACK();
    ai_async_cmd_task();
  } while( !is_ai_async_cmd_finished() );

  return (void *) ai_async_cmd_out_u32();
}

