/*
 * Copyright (C) 2016 "IoT.bzh"
 * Author Fulup Ar Foll <fulup@iot.bzh>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * To find out which control your sound card uses
 *  aplay -l  # Check sndcard name name in between []
 *  amixer -D hw:xx controls # get supported controls
 *  amixer -D "hw:3" cget numid=xx  # get control settings
 *
 */
#define _GNU_SOURCE
#include "hal-interface.h"

// Define few private tag for not standard functions
#define PCM_Volume_Multimedia 1000
#define PCM_Volume_Navigation 1001
#define PCM_Volume_Emergency      1002

// Default Values for MasterVolume Ramping
STATIC halVolRampT volRampMaster= {
    .mode    = RAMP_VOL_NORMAL,
    .slave   = Master_Playback_Volume,
    .delay   = 100*1000, // ramping delay in us
    .stepDown=1,
    .stepUp  =1,
};

//Solvol Ramping Value can be customize by Audio Role and Hardwar card
STATIC halVolRampT volRampEmergency= {
    .slave   = Emergency_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 10,
};

STATIC halVolRampT volRampWarning= {
    .slave   = Warning_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 10,
};

STATIC halVolRampT volRampCustomHigh= {
    .slave   = CustomHigh_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampPhone= {
    .slave   = Phone_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampNavigation= {
    .slave   = Navigation_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampCustomMedium= {
    .slave   = CustomMedium_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampVideo= {
    .slave   = Video_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampStreaming= {
    .slave   = Streaming_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampMultimedia= {
    .slave   = Multimedia_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampRadio= {
    .slave   = Radio_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampCustomLow= {
    .slave   = CustomLow_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

STATIC halVolRampT volRampFallback= {
    .slave   = Fallback_Playback_Volume,
    .delay   = 50*1000, // ramping delay in us
    .stepDown= 2,
    .stepUp  = 4,
};

// Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities.
STATIC alsaHalMapT  alsaHalMap[]= {

  // Sound card does not have hardware volume ramping
  { .tag=Master_Playback_Ramp   , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="Master Playback Volume Ramp",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .name="Master_Ramp"}
  },

  // Implement Rampup Volume for Virtual Channels (0-100)
  { .tag=Emergency_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampEmergency}, .info="RampUp Emergency Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Emergency_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Warning_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampWarning}, .info="RampUp Warning Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Warning_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=CustomHigh_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampCustomHigh}, .info="RampUp CustomHigh Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="CustomHigh_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Phone_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampPhone}, .info="RampUp Phone Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Phone_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Navigation_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampNavigation}, .info="RampUp Navigation Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Navigation_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=CustomMedium_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampCustomMedium}, .info="RampUp CustomMedium Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="CustomMedium_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Video_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampVideo}, .info="RampUp Video Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Video_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Streaming_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampStreaming}, .info="RampUp Streaming Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Streaming_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Multimedia_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampMultimedia}, .info="RampUp Multimedia Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Multimedia_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Radio_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampRadio}, .info="RampUp Radio Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Radio_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=CustomLow_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampCustomLow}, .info="RampUp CustomLow Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="CustomLow_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },
  { .tag=Fallback_Playback_Ramp, .cb={.callback=volumeRamp, .handle=&volRampFallback}, .info="RampUp Fallback Volume",
    .ctl={.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.name="Fallback_Ramp", .minval=0, .maxval=100, .step=1, .value=80 }
  },

  // Bind with existing ones created by ALSA configuration (and linked to softvol) [0-255]
  { .tag=Emergency_Playback_Volume      ,
     .ctl={.name="Emergency_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Warning_Playback_Volume      ,
     .ctl={.name="Warning_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=CustomHigh_Playback_Volume      ,
     .ctl={.name="CustomHigh_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Phone_Playback_Volume      ,
     .ctl={.name="Phone_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Navigation_Playback_Volume      ,
     .ctl={.name="Navigation_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=CustomMedium_Playback_Volume      ,
     .ctl={.name="CustomMedium_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Video_Playback_Volume      ,
     .ctl={.name="Video_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Streaming_Playback_Volume      ,
     .ctl={.name="Streaming_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Multimedia_Playback_Volume      ,
     .ctl={.name="Multimedia_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Radio_Playback_Volume      ,
     .ctl={.name="Radio_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=CustomLow_Playback_Volume      ,
     .ctl={.name="CustomLow_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=Fallback_Playback_Volume      ,
     .ctl={.name="Fallback_Volume",.numid=CTL_AUTO, .type=SND_CTL_ELEM_TYPE_INTEGER,.count=2, .maxval=255, .value=204 }
  },
  { .tag=EndHalCrlTag}  /* marker for end of the array */
} ;

// HAL sound card mapping info
STATIC alsaHalSndCardT alsaHalSndCard = {
    .name = "USB-Audio", //  Match any USB audio sound card
    .info = "Hardware Abstraction Layer for Generic USB Audio sound card",
    .ctls = alsaHalMap,
};

STATIC int sndServiceInit() {
    int err;
    AFB_DEBUG("USB-Audio HAL Binding Init");
    err = halServiceInit(afbBindingV2.api, &alsaHalSndCard);
    return err;
}

// API prefix should be unique for each snd card
PUBLIC const struct afb_binding_v2 afbBindingV2 = {
    .api = "usb-audio",
    .init = sndServiceInit,
    .verbs = halServiceApi,
    .onevent = halServiceEvent,
};