use core::fmt;
use core::ops::{BitAnd, BitOr, BitOrAssign};
macro_rules! bitmask {
(
$(#[$meta:meta])*
$name:ident { $( $(#[$cmeta:meta])* $cname:ident = $value:expr; )* }
) => {
$(#[$meta])*
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(u32);
impl $name {
pub const NONE: Self = Self(0);
$( $(#[$cmeta])* pub const $cname: Self = Self($value); )*
pub const fn from_bits(bits: u32) -> Self {
Self(bits)
}
pub const fn bits(self) -> u32 {
self.0
}
pub const fn has(self, other: Self) -> bool {
self.0 & other.0 == other.0
}
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl BitOr for $name {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for $name {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl BitAnd for $name {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#010x}", self.0)
}
}
};
}
macro_rules! wire_enum {
(
$(#[$meta:meta])*
$name:ident: $repr:ty { $( $(#[$cmeta:meta])* $cname:ident = $value:expr; )* }
) => {
$(#[$meta])*
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name($repr);
impl $name {
$( $(#[$cmeta])* pub const $cname: Self = Self($value); )*
pub const fn from_wire(value: $repr) -> Self {
Self(value)
}
pub const fn to_wire(self) -> $repr {
self.0
}
}
};
}
bitmask! {
DeviceFeature {
IR_RX = 1 << 0;
IR_TX = 1 << 1;
CEC = 1 << 2;
V2IP_SOURCE = 1 << 3;
V2IP_SINK = 1 << 4;
VIDEO_ROUTING = 1 << 5;
AUDIO_ROUTING = 1 << 6;
VOLUME_CONTROL = 1 << 7;
AUDIO_RETURN = 1 << 8;
REMOTE_CONTROL = 1 << 9;
SETUP_COMPLETED = 1 << 10;
MESH_MASTER = 1 << 11;
STATUS_NOTIFY = 1 << 12;
STATUS_WARNING = 1 << 13;
STATUS_ERROR = 1 << 14;
STATUS_REBOOT = 1 << 15;
MESH_MEMBER = 1 << 16;
AUDIO_AMPLIFIER = 1 << 17;
BOOTING = 1 << 18;
MANAGER = 1 << 19;
STATUS_POWER_SAVE = 1 << 20;
MESH = 1 << 21;
MULTIVIEWER = 1 << 22;
STATUS_CRASHED = 1 << 23;
VIDEO_WALL = 1 << 24;
CONFIG_INITIALISED = 1 << 25;
BOOT_BIT = 1 << 31;
}
}
bitmask! {
BayFeatures {
HDMI_OUT = 1 << 0;
HDMI_IN = 1 << 1;
AUDIO_DIG_OUT = 1 << 2;
AUDIO_DIG_IN = 1 << 3;
AUDIO_ANA_OUT = 1 << 4;
AUDIO_ANA_IN = 1 << 5;
IR_IN = 1 << 6;
IR_OUT = 1 << 7;
AUDIO_AMP_OUT = 1 << 8;
RC_OUT = 1 << 9;
RC_IN = 1 << 10;
DOLBY = 1 << 11;
AUTO_OFF = 1 << 12;
V2IP_SOURCE_REMOTE = 1 << 13;
V2IP_SINK_REMOTE = 1 << 14;
V2IP_SOURCE_LOCAL = 1 << 15;
V2IP_SINK_LOCAL = 1 << 16;
}
}
bitmask! {
BayStatus {
FAULT = 1 << 0;
HIDDEN = 1 << 1;
POWERED = 1 << 2;
SIGNAL_DETECTED = 1 << 3;
HPD_DETECTED = 1 << 4;
SIGNAL_SCRAMBLE = 1 << 5;
HDBT_CONNECTED = 1 << 6;
CEC_DETECTED = 1 << 7;
POWERED_ON = 1 << 8;
POWERED_OFF = 1 << 9;
AUDIO_ARC_HDMI = 1 << 10;
AUDIO_ARC_OPTIC = 1 << 11;
AUDIO_ARC_ANALOG = 1 << 12;
OFFLINE = 1 << 13;
DECODER_DISABLE = 1 << 14;
ENCODER_DISABLE = 1 << 15;
CEC_DISABLED = 1 << 20;
ENCODER_ERROR = 1 << 21;
}
}
impl BayStatus {
const RC_TYPE_SHIFT: u32 = 16;
const RC_TYPE_MASK: u32 = 0xF << Self::RC_TYPE_SHIFT;
const HDCP_SHIFT: u32 = 22;
const HDCP_MASK: u32 = 0x3 << Self::HDCP_SHIFT;
pub const fn rc_type(self) -> RcType {
RcType(((self.0 & Self::RC_TYPE_MASK) >> Self::RC_TYPE_SHIFT) as u8)
}
pub const fn hdcp(self) -> u8 {
((self.0 & Self::HDCP_MASK) >> Self::HDCP_SHIFT) as u8
}
}
bitmask! {
LinkFeature {
VIDEO_HDMI = 1 << 0;
AUDIO_OPTICAL = 1 << 1;
AUDIO_ANALOG = 1 << 2;
IR = 1 << 3;
RC = 1 << 4;
}
}
wire_enum! {
RcAction: u16 {
POWER_TOGGLE = 0;
POWER_ON = 1;
POWER_OFF = 2;
VOLUME_DOWN = 3;
VOLUME_UP = 4;
VOLUME_MUTE = 5;
}
}
wire_enum! {
RcKey: u16 {
NUM0 = 0;
NUM1 = 1;
NUM2 = 2;
NUM3 = 3;
NUM4 = 4;
NUM5 = 5;
NUM6 = 6;
NUM7 = 7;
NUM8 = 8;
NUM9 = 9;
SELECT = 10;
BACK = 11;
UP = 12;
DOWN = 13;
LEFT = 14;
RIGHT = 15;
MENU = 16;
CONTENT_MENU = 17;
CHANNEL_UP = 18;
CHANNEL_DOWN = 19;
PLAY = 20;
PAUSE = 21;
STOP = 22;
RECORD = 23;
FAST_FORWARD = 24;
REWIND = 25;
RED = 26;
GREEN = 27;
YELLOW = 28;
BLUE = 29;
HELP = 30;
INFORMATION = 31;
TEXT = 32;
GUIDE = 33;
VIDEO_ON_DEMAND = 34;
PREVIOUS_CHANNEL = 80;
MODE_3D = 81;
SUBTITLE = 82;
SOUND_SELECT = 83;
INPUT_SELECT = 84;
EJECT = 85;
NEXT_CHAPTER = 86;
PREV_CHAPTER = 87;
INTERACTIVE = 128;
SEARCH = 129;
SKY = 130;
CUSTOM_CEC = 1280;
CUSTOM_SKY = 2048;
}
}
wire_enum! {
RcType: u8 {
IR = 0;
CEC = 1;
SKY_UK = 2;
TIVO = 3;
KODI = 4;
DISH = 5;
DIRECTV = 6;
MX_REMOTE = 7;
}
}
impl fmt::Display for RcType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Self::IR => "IR",
Self::CEC => "CEC",
Self::SKY_UK => "Sky",
Self::TIVO => "TiVo",
Self::KODI => "Kodi",
Self::DISH => "Dish",
Self::DIRECTV => "DirecTV",
Self::MX_REMOTE => "MX-Remote",
_ => "Unknown",
};
f.write_str(name)
}
}
wire_enum! {
EdidProfile: u16 {
STEREO_1080P = 0;
FIXED = 1;
UHD_4K = 2;
SURROUND51_1080P = 3;
HD_720P = 4;
SURROUND71_1080P = 5;
SURROUND71_4K = 6;
HDR_STEREO_4K = 7;
HDR_SURROUND71_4K = 8;
HDR_AVR_ONLY_4K = 9;
LOWEST_COMMON = 10;
LOWEST_COMMON_ALL = 11;
HDR_ATMOS_4K = 12;
SINK_1 = 101;
SINK_32 = 132;
CUSTOM_0 = 500;
UNKNOWN = 0xFFF;
}
}
impl fmt::Display for EdidProfile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Self::STEREO_1080P => "1080p stereo",
Self::FIXED => "fixed",
Self::UHD_4K => "4K",
Self::SURROUND51_1080P => "1080p 5.1",
Self::HD_720P => "720p",
Self::SURROUND71_1080P => "1080p 7.1",
Self::SURROUND71_4K => "4K 7.1",
Self::HDR_STEREO_4K => "4K HDR Stereo",
Self::HDR_SURROUND71_4K => "4K HDR 7.1",
Self::HDR_AVR_ONLY_4K => "4K HDR AVR",
Self::LOWEST_COMMON => "lowest common denominator",
Self::LOWEST_COMMON_ALL => "lowest common denominator (all sinks)",
Self::HDR_ATMOS_4K => "4K HDR Dolby Atmos",
_ => {
if self.0 >= Self::SINK_1.0 && self.0 <= Self::SINK_32.0 {
return write!(f, "copy from sink #{}", self.0 - Self::SINK_1.0 + 1);
}
return write!(f, "custom #{}", self.0);
}
};
f.write_str(name)
}
}
wire_enum! {
FirmwareType: u8 {
UNKNOWN = 0;
FPGA = 1;
LINUX = 2;
LOADING_OVERLAY = 3;
}
}
impl fmt::Display for FirmwareType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Self::FPGA => "FPGA",
Self::LINUX => "Linux",
Self::LOADING_OVERLAY => "Loading Overlay",
_ => "Unknown",
};
f.write_str(name)
}
}
wire_enum! {
UtpLinkSpeed: u8 {
UNKNOWN = 0;
SPEED_10M = 1;
SPEED_100M = 2;
SPEED_200M = 3;
SPEED_1G = 4;
}
}
impl fmt::Display for UtpLinkSpeed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Self::SPEED_10M => "10Mbit/s",
Self::SPEED_100M => "100Mbit/s",
Self::SPEED_200M => "200Mbit/s",
Self::SPEED_1G => "1Gbit/s",
_ => "Unknown",
};
f.write_str(name)
}
}
wire_enum! {
MultiviewerViewMode: u8 {
UNKNOWN = 0;
SINGLE = 1;
PIP = 2;
TWO_SCREEN_LARGE = 3;
TWO_SCREEN_SMALL = 4;
THREE_SCREEN_LARGE = 5;
THREE_SCREEN_SMALL = 6;
FOUR_SCREEN_EQUAL = 7;
FOUR_SCREEN_SMALL = 8;
}
}
wire_enum! {
MultiviewerPipPosition: u8 {
UNKNOWN = 0;
LEFT_TOP = 1;
LEFT_BOTTOM = 2;
RIGHT_TOP = 3;
RIGHT_BOTTOM = 4;
}
}
wire_enum! {
MultiviewerPipSize: u8 {
UNKNOWN = 0;
SMALL = 1;
MEDIUM = 2;
LARGE = 3;
}
}
wire_enum! {
MultiviewerOutputMode: u8 {
UNKNOWN = 0;
DCI4K_P60 = 1;
DCI4K_P50 = 2;
UHD_P60 = 3;
UHD_P50 = 4;
UHD_P30 = 5;
UHD_P25 = 6;
WUXGA_P60_RB = 7;
HD1080_P60 = 8;
HD1080_P50 = 9;
WXGA_P60 = 10;
WXGA800_P60 = 11;
HD720_P60 = 12;
HD720_P50 = 13;
XGA_P60 = 14;
}
}
wire_enum! {
MultiviewerHdcpMode: u8 {
UNKNOWN = 0;
V14 = 1;
V22 = 2;
OFF = 3;
}
}
wire_enum! {
MultiviewerItcMode: u8 {
UNKNOWN = 0;
VIDEO = 1;
PC = 2;
}
}
wire_enum! {
MultiviewerEdidTemplate: u8 {
EDID_UNKNOWN = 0;
EDID_4K2K60_444_STEREO = 1;
EDID_4K2K60_444_DOLBY_DTS_51 = 2;
EDID_4K2K60_444_HD_AUDIO_71 = 3;
EDID_4K2K30_444_STEREO = 4;
EDID_4K2K30_444_DOLBY_DTS_51 = 5;
EDID_4K2K30_444_HD_AUDIO_71 = 6;
EDID_1080P_STEREO = 7;
EDID_1080P_DOLBY_DTS_51 = 8;
EDID_1080P_HD_AUDIO_71 = 9;
EDID_1920X1200_STEREO = 10;
EDID_1680X1050_STEREO = 11;
EDID_1600X1200_STEREO = 12;
EDID_1440X900_STEREO = 13;
EDID_1360X768_STEREO = 14;
EDID_1280X1024_STEREO = 15;
EDID_1024X768_STEREO = 16;
EDID_720P_STEREO = 17;
EDID_COPY_OUTPUT = 18;
EDID_CUSTOM = 19;
}
}
wire_enum! {
MultiviewerAspectRatio: u8 {
UNKNOWN = 0;
FULL = 1;
RATIO_16_9 = 2;
}
}
wire_enum! {
MultiviewerBool: u8 {
OFF = 0;
ON = 1;
UNKNOWN = 0xFF;
}
}
wire_enum! {
MultiviewerSource: u8 {
UNKNOWN = 0;
INPUT_1 = 1;
INPUT_2 = 2;
INPUT_3 = 3;
INPUT_4 = 4;
}
}
impl MultiviewerSource {
pub(crate) const fn from_zero_based(value: u8) -> Self {
if value > 3 {
Self::UNKNOWN
} else {
Self(value + 1)
}
}
pub(crate) const fn to_zero_based(self) -> Option<u8> {
match self.0 {
1..=4 => Some(self.0 - 1),
_ => None,
}
}
}
impl MultiviewerBool {
pub(crate) const fn from_wire_tristate(value: u8) -> Self {
if value > 1 {
Self::UNKNOWN
} else {
Self(value)
}
}
}
wire_enum! {
MxrSignalType: u16 {
NONE = 0;
}
}
const SIG_BPP_UNSET: u16 = 5;
impl MxrSignalType {
pub const fn svd(self) -> u8 {
(self.0 & 0xFF) as u8
}
pub const fn colour_space(self) -> u8 {
((self.0 >> 8) & 0xF) as u8
}
pub const fn is_non_integer(self) -> bool {
self.0 & (1 << 12) != 0
}
pub const fn bpp_index(self) -> u8 {
((self.0 >> 13) & 0x7) as u8
}
pub const fn bpp(self) -> Option<u8> {
match self.bpp_index() {
1 => Some(8),
2 => Some(10),
3 => Some(12),
4 => Some(16),
_ => None,
}
}
pub const fn is_set(self) -> bool {
self.0 != 0 && self.bpp_index() as u16 != SIG_BPP_UNSET
}
}
impl fmt::Display for MxrSignalType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.is_set() {
return f.write_str("unset");
}
match self.bpp() {
Some(bpp) => write!(
f,
"svd {}, color {}, {}bpp",
self.svd(),
self.colour_space(),
bpp
),
None => write!(f, "svd {}, color {}", self.svd(), self.colour_space()),
}
}
}