//
// Copyright (C) 2020 The Android Open Source Project
//
// 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.

#pragma once

#include <cstdint>
#include "cuttlefish/common/libs/utils/cf_endian.h"

namespace cuttlefish {
// TODO (b/175151042): get these from the kernel headers when available

enum class AudioCommandType : uint32_t {
  /* jack control request types */
  VIRTIO_SND_R_JACK_INFO = 1,
  VIRTIO_SND_R_JACK_REMAP,

  /* PCM control request types */
  VIRTIO_SND_R_PCM_INFO = 0x0100,
  VIRTIO_SND_R_PCM_SET_PARAMS,
  VIRTIO_SND_R_PCM_PREPARE,
  VIRTIO_SND_R_PCM_RELEASE,
  VIRTIO_SND_R_PCM_START,
  VIRTIO_SND_R_PCM_STOP,

  /* channel map control request types */
  VIRTIO_SND_R_CHMAP_INFO = 0x0200,

  /* control element request types */
  VIRTIO_SND_R_CTL_INFO = 0x0300,
  VIRTIO_SND_R_CTL_ENUM_ITEMS,
  VIRTIO_SND_R_CTL_READ,
  VIRTIO_SND_R_CTL_WRITE,
  VIRTIO_SND_R_CTL_TLV_READ,
  VIRTIO_SND_R_CTL_TLV_WRITE,
  VIRTIO_SND_R_CTL_TLV_COMMAND,
};

enum class AudioStatus : uint32_t {
  /* common status codes */
  VIRTIO_SND_S_OK = 0x8000,
  VIRTIO_SND_S_BAD_MSG,
  VIRTIO_SND_S_NOT_SUPP,
  VIRTIO_SND_S_IO_ERR,
  // Not a virtio constant, but it's only used internally as an invalid value so
  // it's safe.
  NOT_SET = static_cast<uint32_t>(-1),
};

enum class AudioStreamDirection : uint8_t {
  VIRTIO_SND_D_OUTPUT = 0,
  VIRTIO_SND_D_INPUT
};

enum class AudioStreamFormat : uint8_t {
  /* analog formats (width / physical width) */
  VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0, /*  4 /  4 bits */
  VIRTIO_SND_PCM_FMT_MU_LAW,        /*  8 /  8 bits */
  VIRTIO_SND_PCM_FMT_A_LAW,         /*  8 /  8 bits */
  VIRTIO_SND_PCM_FMT_S8,            /*  8 /  8 bits */
  VIRTIO_SND_PCM_FMT_U8,            /*  8 /  8 bits */
  VIRTIO_SND_PCM_FMT_S16,           /* 16 / 16 bits */
  VIRTIO_SND_PCM_FMT_U16,           /* 16 / 16 bits */
  VIRTIO_SND_PCM_FMT_S18_3,         /* 18 / 24 bits */
  VIRTIO_SND_PCM_FMT_U18_3,         /* 18 / 24 bits */
  VIRTIO_SND_PCM_FMT_S20_3,         /* 20 / 24 bits */
  VIRTIO_SND_PCM_FMT_U20_3,         /* 20 / 24 bits */
  VIRTIO_SND_PCM_FMT_S24_3,         /* 24 / 24 bits */
  VIRTIO_SND_PCM_FMT_U24_3,         /* 24 / 24 bits */
  VIRTIO_SND_PCM_FMT_S20,           /* 20 / 32 bits */
  VIRTIO_SND_PCM_FMT_U20,           /* 20 / 32 bits */
  VIRTIO_SND_PCM_FMT_S24,           /* 24 / 32 bits */
  VIRTIO_SND_PCM_FMT_U24,           /* 24 / 32 bits */
  VIRTIO_SND_PCM_FMT_S32,           /* 32 / 32 bits */
  VIRTIO_SND_PCM_FMT_U32,           /* 32 / 32 bits */
  VIRTIO_SND_PCM_FMT_FLOAT,         /* 32 / 32 bits */
  VIRTIO_SND_PCM_FMT_FLOAT64,       /* 64 / 64 bits */
  /* digital formats (width / physical width) */
  VIRTIO_SND_PCM_FMT_DSD_U8,         /*  8 /  8 bits */
  VIRTIO_SND_PCM_FMT_DSD_U16,        /* 16 / 16 bits */
  VIRTIO_SND_PCM_FMT_DSD_U32,        /* 32 / 32 bits */
  VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */
};

/* supported PCM frame rates */
enum AudioStreamRate : uint8_t {
  VIRTIO_SND_PCM_RATE_5512 = 0,
  VIRTIO_SND_PCM_RATE_8000,
  VIRTIO_SND_PCM_RATE_11025,
  VIRTIO_SND_PCM_RATE_16000,
  VIRTIO_SND_PCM_RATE_22050,
  VIRTIO_SND_PCM_RATE_32000,
  VIRTIO_SND_PCM_RATE_44100,
  VIRTIO_SND_PCM_RATE_48000,
  VIRTIO_SND_PCM_RATE_64000,
  VIRTIO_SND_PCM_RATE_88200,
  VIRTIO_SND_PCM_RATE_96000,
  VIRTIO_SND_PCM_RATE_176400,
  VIRTIO_SND_PCM_RATE_192000,
  VIRTIO_SND_PCM_RATE_384000
};

/* standard channel position definition */
enum AudioChannelMap : uint8_t {
  VIRTIO_SND_CHMAP_NONE = 0,  /* undefined */
  VIRTIO_SND_CHMAP_NA,        /* silent */
  VIRTIO_SND_CHMAP_MONO,      /* mono stream */
  VIRTIO_SND_CHMAP_FL,        /* front left */
  VIRTIO_SND_CHMAP_FR,        /* front right */
  VIRTIO_SND_CHMAP_RL,        /* rear left */
  VIRTIO_SND_CHMAP_RR,        /* rear right */
  VIRTIO_SND_CHMAP_FC,        /* front center */
  VIRTIO_SND_CHMAP_LFE,       /* low frequency (LFE) */
  VIRTIO_SND_CHMAP_SL,        /* side left */
  VIRTIO_SND_CHMAP_SR,        /* side right */
  VIRTIO_SND_CHMAP_RC,        /* rear center */
  VIRTIO_SND_CHMAP_FLC,       /* front left center */
  VIRTIO_SND_CHMAP_FRC,       /* front right center */
  VIRTIO_SND_CHMAP_RLC,       /* rear left center */
  VIRTIO_SND_CHMAP_RRC,       /* rear right center */
  VIRTIO_SND_CHMAP_FLW,       /* front left wide */
  VIRTIO_SND_CHMAP_FRW,       /* front right wide */
  VIRTIO_SND_CHMAP_FLH,       /* front left high */
  VIRTIO_SND_CHMAP_FCH,       /* front center high */
  VIRTIO_SND_CHMAP_FRH,       /* front right high */
  VIRTIO_SND_CHMAP_TC,        /* top center */
  VIRTIO_SND_CHMAP_TFL,       /* top front left */
  VIRTIO_SND_CHMAP_TFR,       /* top front right */
  VIRTIO_SND_CHMAP_TFC,       /* top front center */
  VIRTIO_SND_CHMAP_TRL,       /* top rear left */
  VIRTIO_SND_CHMAP_TRR,       /* top rear right */
  VIRTIO_SND_CHMAP_TRC,       /* top rear center */
  VIRTIO_SND_CHMAP_TFLC,      /* top front left center */
  VIRTIO_SND_CHMAP_TFRC,      /* top front right center */
  VIRTIO_SND_CHMAP_TSL,       /* top side left */
  VIRTIO_SND_CHMAP_TSR,       /* top side right */
  VIRTIO_SND_CHMAP_LLFE,      /* left LFE */
  VIRTIO_SND_CHMAP_RLFE,      /* right LFE */
  VIRTIO_SND_CHMAP_BC,        /* bottom center */
  VIRTIO_SND_CHMAP_BLC,       /* bottom left center */
  VIRTIO_SND_CHMAP_BRC        /* bottom right center */
};

struct virtio_snd_hdr {
  Le32 code;
};

struct virtio_snd_query_info {
  struct virtio_snd_hdr hdr;
  Le32 start_id;
  Le32 count;
  Le32 size;  // unused
};

struct virtio_snd_info {
  Le32 hda_fn_nid;
};

/* supported jack features */
enum AudioJackFeatures: uint8_t {
  VIRTIO_SND_JACK_F_REMAP = 0
};

struct virtio_snd_jack_info {
  struct virtio_snd_info hdr;
  Le32 features; /* 1 << VIRTIO_SND_JACK_F_XXX */
  Le32 hda_reg_defconf;
  Le32 hda_reg_caps;
  uint8_t connected;

  uint8_t padding[7];
};

constexpr uint8_t VIRTIO_SND_CHMAP_MAX_SIZE = 18;
struct virtio_snd_chmap_info {
  struct virtio_snd_info hdr;
  uint8_t direction;
  uint8_t channels;
  uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
};

struct virtio_snd_pcm_info {
  struct virtio_snd_info hdr;
  Le32 features; /* 1 << VIRTIO_SND_PCM_F_XXX */
  Le64 formats;  /* 1 << VIRTIO_SND_PCM_FMT_XXX */
  Le64 rates;    /* 1 << VIRTIO_SND_PCM_RATE_XXX */
  uint8_t direction;
  uint8_t channels_min;
  uint8_t channels_max;

  uint8_t padding[5];
};

struct virtio_snd_pcm_hdr {
  struct virtio_snd_hdr hdr;
  Le32 stream_id;
};

struct virtio_snd_pcm_set_params {
  struct virtio_snd_pcm_hdr hdr;
  Le32 buffer_bytes;
  Le32 period_bytes;
  Le32 features; /* 1 << VIRTIO_SND_PCM_F_XXX */
  uint8_t channels;
  uint8_t format;
  uint8_t rate;
  uint8_t padding;
};

struct virtio_snd_pcm_xfer {
  Le32 stream_id;
};

struct virtio_snd_pcm_status {
  Le32 status;
  Le32 latency_bytes;
};

enum class AudioControlRole : uint8_t {
  VIRTIO_SND_CTL_ROLE_UNDEFINED = 0,
  VIRTIO_SND_CTL_ROLE_VOLUME,
  VIRTIO_SND_CTL_ROLE_MUTE,
  VIRTIO_SND_CTL_ROLE_GAIN,
};

enum class AudioControlType : uint8_t {
  VIRTIO_SND_CTL_TYPE_BOOLEAN = 0,
  VIRTIO_SND_CTL_TYPE_INTEGER,
  VIRTIO_SND_CTL_TYPE_INTEGER64,
  VIRTIO_SND_CTL_TYPE_ENUMERATED,
  VIRTIO_SND_CTL_TYPE_BYTES,
  VIRTIO_SND_CTL_TYPE_IEC958,
};

enum AudioControlAccess : uint8_t {
  VIRTIO_SND_CTL_ACCESS_READ = 0,
  VIRTIO_SND_CTL_ACCESS_WRITE,
  VIRTIO_SND_CTL_ACCESS_VOLATILE,
  VIRTIO_SND_CTL_ACCESS_INACTIVE,
  VIRTIO_SND_CTL_ACCESS_TLV_READ,
  VIRTIO_SND_CTL_ACCESS_TLV_WRITE,
  VIRTIO_SND_CTL_ACCESS_TLV_COMMAND
};

struct virtio_snd_ctl_info {
  struct virtio_snd_info hdr;
  Le32 role;
  Le32 type;
  Le32 access;  // 1 << VIRTIO_SND_CTL_ACCESS_XXX
  Le32 count;
  Le32 index;
  uint8_t name[44];
  union {
    struct {
      Le32 min;
      Le32 max;
      Le32 step;
    } integer;
    struct {
      Le64 min;
      Le64 max;
      Le64 step;
    } integer64;
    struct {
      Le32 items;
    } enumerated;
  } value;
};

struct virtio_snd_ctl_hdr {
  struct virtio_snd_hdr hdr;
  Le32 control_id;
};

struct virtio_snd_ctl_value {
  union {
    Le32 integer[128];
    Le64 integer64[64];
    Le32 enumerated[128];
    uint8_t bytes[512];
    struct virtio_snd_ctl_iec958 {
      uint8_t status[24];      /* AES/IEC958 channel status bits */
      uint8_t subcode[147];    /* AES/IEC958 subcode bits */
      uint8_t pad;             /* nothing */
      uint8_t dig_subframe[4]; /* AES/IEC958 subframe bits */
    } iec958;
  } value;
};

struct virtio_snd_ctl_event {
  struct virtio_snd_hdr hdr;
  Le16 control_id;
  Le16 mask;
};

inline constexpr uint32_t VIOS_VERSION = 3;

struct VioSConfig {
  uint32_t version;
  uint32_t jacks;
  uint32_t streams;
  uint32_t chmaps;
  uint32_t controls;
};

struct IoTransferMsg {
  virtio_snd_pcm_xfer io_xfer;
  uint32_t buffer_offset;
  uint32_t buffer_len;
};

struct IoStatusMsg {
  virtio_snd_pcm_status status;
  uint32_t buffer_offset;
  uint32_t consumed_length;
};

// Ensure all message structs have predictable sizes
#define ASSERT_VALID_MSG_TYPE(T, size) \
  static_assert(sizeof(T) == (size), #T " has the wrong size")
ASSERT_VALID_MSG_TYPE(virtio_snd_query_info, 16);
ASSERT_VALID_MSG_TYPE(virtio_snd_jack_info, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_chmap_info, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_info, 32);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_set_params, 24);
ASSERT_VALID_MSG_TYPE(virtio_snd_pcm_hdr, 8);
ASSERT_VALID_MSG_TYPE(virtio_snd_ctl_info, 96);
ASSERT_VALID_MSG_TYPE(virtio_snd_ctl_hdr, 8);
ASSERT_VALID_MSG_TYPE(virtio_snd_ctl_value, 512);
ASSERT_VALID_MSG_TYPE(IoTransferMsg, 12);
ASSERT_VALID_MSG_TYPE(IoStatusMsg, 16);
#undef ASSERT_VALID_MSG_TYPE

}  // namespace cuttlefish
