From 2579bc6132f853a1bd99d46e44fd4f6c21b5a426 Mon Sep 17 00:00:00 2001 From: philmoz Date: Thu, 29 Jan 2026 12:54:14 +1100 Subject: [PATCH] chore: store more hardware settings in hwdef JSON files (#7031) --- companion/src/firmwares/boardjson.cpp | 91 +++++++++++++- companion/src/firmwares/boardjson.h | 31 ++++- companion/src/firmwares/boards.cpp | 112 ------------------ companion/src/firmwares/boards.h | 19 ++- .../src/firmwares/customfunctiondata.cpp | 2 +- radio/src/targets/taranis/board.h | 6 - radio/src/targets/taranis/hal.h | 15 +++ radio/util/hw_defs/hal_cfs.py | 26 ++++ radio/util/hw_defs/hal_json.py | 18 +++ radio/util/hw_defs/hal_lcd.py | 60 ++++++++++ radio/util/hw_defs/hal_misc.py | 44 +++++++ 11 files changed, 290 insertions(+), 134 deletions(-) create mode 100644 radio/util/hw_defs/hal_cfs.py create mode 100644 radio/util/hw_defs/hal_lcd.py create mode 100644 radio/util/hw_defs/hal_misc.py diff --git a/companion/src/firmwares/boardjson.cpp b/companion/src/firmwares/boardjson.cpp index c581003c5..da8adead4 100644 --- a/companion/src/firmwares/boardjson.cpp +++ b/companion/src/firmwares/boardjson.cpp @@ -69,10 +69,12 @@ BoardJson::BoardJson(Board::Type board, QString hwdefn) : m_switches(new SwitchesTable), m_trims(new TrimsTable), m_keys(new KeysTable), + m_display({0, 0, 0, 0, 0, 0, 0, 0}), + m_cfs({0, 0}), + m_hardware({0, 0, 0}), m_inputCnt({0, 0, 0, 0, 0, 0, 0, 0, 0}), m_switchCnt({0, 0, 0}) { - } BoardJson::~BoardJson() @@ -151,6 +153,9 @@ void BoardJson::afterLoadFixups(Board::Type board, InputsTable * inputs, Switche const int BoardJson::getCapability(const Board::Capability capability) const { switch (capability) { + case Board::Air: + return !m_hardware.surface; + case Board::FlexInputs: return (m_inputCnt.flexGyroAxes + m_inputCnt.flexJoystickAxes + @@ -161,18 +166,45 @@ const int BoardJson::getCapability(const Board::Capability capability) const case Board::FlexSwitches: return m_switchCnt.flex; + case Board::FunctionSwitchColors: + return m_cfs.rgb_led; + case Board::FunctionSwitches: return m_switchCnt.func; + case Board::FunctionSwitchGroups: + return m_cfs.groups; + case Board::GyroAxes: return m_inputCnt.flexGyroAxes; case Board::Gyros: return getCapability(Board::GyroAxes) / 2; + case Board::HasAudioMuteGPIO: + return m_hardware.has_audio_mute; + + case Board::HasBacklightColor: + return m_display.backlight_color; + + case Board::HasBlingLEDS: + return m_hardware.has_bling_leds; + + case Board::HasColorLcd: + return m_display.color; + + case Board::HasExternalModuleSupport: + return m_hardware.has_ext_module_support; + + case Board::HasInternalModuleSupport: + return m_hardware.has_int_module_support; + case Board::HasRTC: return m_inputCnt.rtcbat; + case Board::HasSDCard: + return true; + case Board::HasVBat: return m_inputCnt.vbat; @@ -182,9 +214,24 @@ const int BoardJson::getCapability(const Board::Capability capability) const case Board::InputSwitches: return m_inputCnt.switches; + case Board::JoystickAxes: + return m_inputCnt.flexJoystickAxes; + case Board::Keys: return m_keys->size(); + case Board::LcdDepth: + return m_display.depth; + + case Board::LcdHeight: + return m_display.h; + + case Board::LcdOLED: + return m_display.oled; + + case Board::LcdWidth: + return m_display.w; + case Board::MultiposPots: // assumes every input has potential to be one // index used for mapping 6 pos switches back to input @@ -208,12 +255,18 @@ const int BoardJson::getCapability(const Board::Capability capability) const case Board::Sliders: return m_inputCnt.flexSliders; + case Board::SportMaxBaudRate: + return m_hardware.sport_max_baudrate; + case Board::StandardSwitches: return m_switchCnt.std; case Board::Sticks: return m_inputCnt.sticks; + case Board::Surface: + return m_hardware.surface; + case Board::Switches: return (m_switchCnt.std + m_switchCnt.flex + @@ -920,7 +973,7 @@ bool BoardJson::loadDefinition() if (m_board == Board::BOARD_UNKNOWN) return true; - if (!loadFile(m_board, m_hwdefn, m_inputs, m_switches, m_keys, m_trims)) + if (!loadFile(m_board, m_hwdefn, m_inputs, m_switches, m_keys, m_trims, m_display, m_cfs, m_hardware)) return false; afterLoadFixups(m_board, m_inputs, m_switches, m_keys, m_trims); @@ -956,7 +1009,8 @@ bool BoardJson::loadDefinition() // static bool BoardJson::loadFile(Board::Type board, QString hwdefn, InputsTable * inputs, SwitchesTable * switches, - KeysTable * keys, TrimsTable * trims) + KeysTable * keys, TrimsTable * trims, DisplayDefn & display, CustomSwitchesDefn & cfs, + HardwareDefn & hardware) { if (board == Board::BOARD_UNKNOWN) { return false; @@ -1164,6 +1218,37 @@ bool BoardJson::loadFile(Board::Type board, QString hwdefn, InputsTable * inputs } } + if (obj.value("display").isObject()) { + const QJsonObject &o = obj.value("display").toObject(); + + display.w = o.value("w").toInt(); + display.h = o.value("h").toInt(); + display.phys_w = o.value("phys_w").toInt(); + display.phys_h = o.value("phys_h").toInt(); + display.depth = o.value("depth").toInt(); + display.color = o.value("color").toInt(); + display.oled = o.value("oled").toInt(); + display.backlight_color = o.value("backlight_color").toInt(); + } + + if (obj.value("custom_switches").isObject()) { + const QJsonObject &o = obj.value("custom_switches").toObject(); + + cfs.rgb_led = o.value("rgb_led").toInt(); + cfs.groups = o.value("groups").toInt(); + } + + if (obj.value("hardware").isObject()) { + const QJsonObject &o = obj.value("hardware").toObject(); + + hardware.has_audio_mute = o.value("has_audio_mute").toInt(); + hardware.has_bling_leds = o.value("has_bling_leds").toInt(); + hardware.has_ext_module_support = o.value("has_ext_module_support").toInt(); + hardware.has_int_module_support = o.value("has_int_module_support").toInt(); + hardware.sport_max_baudrate = o.value("sport_max_baudrate").toInt(); + hardware.surface = o.value("surface").toInt(); + } + delete json; return true; } diff --git a/companion/src/firmwares/boardjson.h b/companion/src/firmwares/boardjson.h index e2edad576..4eb269b11 100644 --- a/companion/src/firmwares/boardjson.h +++ b/companion/src/firmwares/boardjson.h @@ -100,6 +100,31 @@ class BoardJson TrimDefn() = default; }; + struct DisplayDefn { + int w = 0; + int h = 0; + int phys_w = 0; + int phys_h = 0; + int depth = 0; + int color = 0; + int oled = 0; + int backlight_color = 0; + }; + + struct CustomSwitchesDefn { + int rgb_led = 0; + int groups = 0; + }; + + struct HardwareDefn { + int has_audio_mute = 0; + int has_bling_leds = 0; + int has_ext_module_support = 0; + int has_int_module_support = 0; + int sport_max_baudrate = 0; + int surface = 0; + }; + typedef std::vector TrimsTable; explicit BoardJson(Board::Type board, QString hwdefn); @@ -170,6 +195,9 @@ private: SwitchesTable *m_switches; TrimsTable *m_trims; KeysTable *m_keys; + DisplayDefn m_display; + CustomSwitchesDefn m_cfs; + HardwareDefn m_hardware; struct InputCounts { unsigned int flexGyroAxes; @@ -194,7 +222,8 @@ private: SwitchCounts m_switchCnt; static bool loadFile(Board::Type board, QString hwdefn, InputsTable * inputs, SwitchesTable * switches, - KeysTable * keys, TrimsTable * trims); + KeysTable * keys, TrimsTable * trims, DisplayDefn & lcd, CustomSwitchesDefn & cfs, + HardwareDefn & hardware); static void afterLoadFixups(Board::Type board, InputsTable * inputs, SwitchesTable * switches, KeysTable * keys, TrimsTable * trims); diff --git a/companion/src/firmwares/boards.cpp b/companion/src/firmwares/boards.cpp index 70bf5d90d..8ba619ee0 100644 --- a/companion/src/firmwares/boards.cpp +++ b/companion/src/firmwares/boards.cpp @@ -305,66 +305,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) // TODO investigate usage of any that should be covered in BoardJson::getCapability or are no longer required // some could be used when importing pre v2.10 configurations switch (capability) { - case Air: - return !getCapability(board, Surface); - - case FactoryInstalledPots: - if (IS_TARANIS_X9(board)) - return 2; - else - return getCapability(board, Pots); - - case FactoryInstalledSwitches: - if (IS_TARANIS_X9E(board)) - return 8; - else if (IS_JUMPER_TPROV2(board)) - return 6; - else if (IS_JUMPER_TLITE(board) || IS_JUMPER_TPROV1(board) || IS_BETAFPV_LR3PRO(board) || IS_IFLIGHT_COMMANDO8(board) || IS_JUMPER_BUMBLEBEE(board)) - return 4; - else if(IS_RADIOMASTER_ZORRO(board)) - return 8; - else if (IS_RADIOMASTER_POCKET(board)) - return 5; - else if (IS_FAMILY_T12(board)) - return 6; - else if (IS_HORUS_X12S(board)) - return 8; - else - return getCapability(board, Board::Switches); - - case FunctionSwitchGroups: - if (getCapability(board, FunctionSwitches)) { - return IS_RADIOMASTER_GX12(board) ? CPN_MAX_CUSTOMSWITCH_GROUPS : 3; - } - return 0; - - case HasAudioMuteGPIO: - // All color lcd (including NV14 and EL18) except Horus X12S - // TX12, TX12MK2, ZORRO, BOXER, T8, TLITE, TPRO, LR3PRO, COMMANDO8 - return (IS_FAMILY_HORUS_OR_T16(board) && !IS_HORUS_X12S(board)) || IS_FAMILY_T12(board); - - case HasBacklightColor: - return IS_TARANIS_PLUS(board) || IS_TARANIS_X9DP_2019(board); - - case HasColorLcd: - return IS_FAMILY_HORUS_OR_T16(board); - - case HasExternalModuleSupport: - return (IS_STM32(board) && !IS_RADIOMASTER_T8(board)); - case HasIMU: return (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS(board) || IS_RADIOMASTER_TX15(board)); - case HasInternalModuleSupport: - return (IS_STM32(board) && !IS_TARANIS_X9(board)); - - case HasLedStripGPIO: - return (IS_RADIOMASTER_MT12(board) || IS_FAMILY_PL18(board) || - IS_HELLORADIOSKY_V16(board)); - - case HasSDCard: - return true; - case HasTrainerModuleCPPM: return (getCapability(board, HasTrainerModuleSBUS) || IS_FAMILY_HORUS_OR_T16(board)); @@ -376,61 +319,6 @@ int Boards::getCapability(Board::Type board, Board::Capability capability) IS_JUMPER_BUMBLEBEE(board)) || IS_FAMILY_T16(board) || IS_FAMILY_HORUS(board) || (getCapability(board, HasExternalModuleSupport) && (IS_TARANIS(board) && !IS_FAMILY_T12(board)))); - case LcdOLED: - return IS_BETAFPV_LR3PRO(board) || IS_JUMPER_TPROV2(board) || IS_JUMPER_TPROS(board) || IS_JUMPER_T20(board) || - IS_JUMPER_T14(board) || IS_JUMPER_BUMBLEBEE(board) || IS_RADIOMASTER_GX12(board); - - case LcdDepth: - if (IS_FAMILY_HORUS_OR_T16(board)) - return 16; - else if (IS_TARANIS_SMALL(board)) - return 1; - else if (IS_TARANIS(board)) - return 4; - else - return 1; - - case LcdHeight: - if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_RADIOMASTER_TX16SMK3(board)) - return 480; - else if (IS_FAMILY_PL18(board) || IS_JUMPER_T15(board) || IS_JUMPER_T15PRO(board) || IS_FLYSKY_ST16(board) || IS_RADIOMASTER_TX15(board)) - return 320; - else if (IS_FLYSKY_PA01(board)) - return 240; - else if (IS_FAMILY_HORUS_OR_T16(board)) - return 272; - else - return 64; - - case LcdWidth: - if (IS_RADIOMASTER_TX16SMK3(board)) - return 800; - else if (IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_FLYSKY_PA01(board)) - return 320; - else if (IS_FAMILY_HORUS_OR_T16(board) || IS_RADIOMASTER_TX15(board) || IS_FAMILY_PL18(board) || IS_FLYSKY_ST16(board)) - return 480; - else if (IS_TARANIS(board) && !IS_TARANIS_SMALL(board)) - return 212; - else - return 128; - - case MaxAnalogs: - return getCapability(board, Board::Sticks) + getCapability(board, Board::Pots) + getCapability(board, Board::Sliders) + - getCapability(board, Board::JoystickAxes) + getCapability(board, Board::GyroAxes); - - case SportMaxBaudRate: - if (IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board) || IS_FLYSKY_EL18(board) || IS_TARANIS_X7_ACCESS(board) || - (IS_TARANIS(board) && !IS_TARANIS_XLITE(board) && !IS_TARANIS_X7(board) && !IS_TARANIS_X9LITE(board))) - return 400000; // 400K and higher - else - return 250000; // less than 400K - - case Surface: - return IS_RADIOMASTER_MT12(board); - - case FunctionSwitchColors: - return IS_RADIOMASTER_GX12(board) || IS_FLYSKY_ST16(board) || IS_FLYSKY_PA01(board) || IS_RADIOMASTER_TX15(board) || IS_RADIOMASTER_TX16SMK3(board); - default: return getBoardJson(board)->getCapability(capability); } diff --git a/companion/src/firmwares/boards.h b/companion/src/firmwares/boards.h index 696283837..2e7502f80 100644 --- a/companion/src/firmwares/boards.h +++ b/companion/src/firmwares/boards.h @@ -212,15 +212,13 @@ namespace Board { enum Capability { Air, - FactoryInstalledPots, - FactoryInstalledSwitches, FlexInputs, FlexSwitches, - FunctionSwitches, FunctionSwitchColors, + FunctionSwitches, FunctionSwitchGroups, - Gyros, GyroAxes, + Gyros, HasAudioMuteGPIO, HasBacklightColor, HasColorLcd, @@ -228,22 +226,21 @@ namespace Board { HasIMU, HasInternalModuleSupport, HasIntModuleHeartbeatGPIO, - HasLedStripGPIO, + HasBlingLEDS, HasRTC, HasSDCard, HasTrainerModuleCPPM, HasTrainerModuleSBUS, HasVBat, - LcdOLED, - LcdDepth, - LcdHeight, - LcdWidth, - MaxAnalogs, Inputs, InputSwitches, - Joysticks, JoystickAxes, + Joysticks, Keys, + LcdDepth, + LcdHeight, + LcdOLED, + LcdWidth, MultiposPots, MultiposPotsPositions, NumFunctionSwitchesPositions, diff --git a/companion/src/firmwares/customfunctiondata.cpp b/companion/src/firmwares/customfunctiondata.cpp index e4584acd4..9a01f24e2 100644 --- a/companion/src/firmwares/customfunctiondata.cpp +++ b/companion/src/firmwares/customfunctiondata.cpp @@ -265,7 +265,7 @@ bool CustomFunctionData::isFuncAvailable(const int index, const ModelData * mode ((index >= FuncAdjustGV1 && index <= FuncAdjustGVLast) && ((index - FuncAdjustGV1) >= fw->getCapability(Gvars))) || ((index == FuncDisableTouch) && !IS_HORUS_OR_TARANIS(fw->getBoard())) || ((index == FuncDisableAudioAmp && !Boards::getCapability(fw->getBoard(), Board::HasAudioMuteGPIO))) || - ((index == FuncRGBLed && !(Boards::getCapability(fw->getBoard(), Board::HasLedStripGPIO) || Boards::getCapability(fw->getBoard(), Board::FunctionSwitchColors)))) || + ((index == FuncRGBLed && !(Boards::getCapability(fw->getBoard(), Board::HasBlingLEDS) || Boards::getCapability(fw->getBoard(), Board::FunctionSwitchColors)))) || ((index == FuncLCDtoVideo && !IS_FATFISH_F16(fw->getBoard()))) || ((index >= FuncPushCustomSwitch1 && index <= FuncPushCustomSwitchLast) && !Boards::getCapability(fw->getBoard(), Board::FunctionSwitches)) ); diff --git a/radio/src/targets/taranis/board.h b/radio/src/targets/taranis/board.h index 3c5503965..1e95703fc 100644 --- a/radio/src/targets/taranis/board.h +++ b/radio/src/targets/taranis/board.h @@ -254,16 +254,10 @@ void ledBlue(); // LCD driver #if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) -#define LCD_W 212 -#define LCD_H 64 -#define LCD_DEPTH 4 #define LCD_CONTRAST_MIN 0 #define LCD_CONTRAST_MAX 45 #define LCD_CONTRAST_DEFAULT 25 #else -#define LCD_W 128 -#define LCD_H 64 -#define LCD_DEPTH 1 #define IS_LCD_RESET_NEEDED() true #if defined(OLED_SCREEN) #define LCD_CONTRAST_MIN 2 diff --git a/radio/src/targets/taranis/hal.h b/radio/src/targets/taranis/hal.h index ce9e76a56..8c6ef5995 100644 --- a/radio/src/targets/taranis/hal.h +++ b/radio/src/targets/taranis/hal.h @@ -2961,3 +2961,18 @@ #define MIXER_SCHEDULER_TIMER_FREQ (PERI1_FREQUENCY * TIMER_MULT_APB1) #define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn #define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler + +// LCD driver +#if defined(PCBX9D) || defined(PCBX9DP) || defined(PCBX9E) +#define LCD_W 212 +#define LCD_H 64 +#define LCD_DEPTH 4 +#else +#define LCD_W 128 +#define LCD_H 64 +#define LCD_DEPTH 1 +#endif + +#if defined(PCBX9DP) || defined(PCBX9E) +#define HAS_BACKLIGHT_COLOR +#endif diff --git a/radio/util/hw_defs/hal_cfs.py b/radio/util/hw_defs/hal_cfs.py new file mode 100644 index 000000000..bb7753699 --- /dev/null +++ b/radio/util/hw_defs/hal_cfs.py @@ -0,0 +1,26 @@ + +class CFS: + + def __init__(self, cfs_rgb_led, cfs_groups): + self.rgb_led = cfs_rgb_led + self.groups = cfs_groups + +def parse_cfs(hw_defs): + + cfs_rgb_led = 0 + cfs_groups = 0 + + fcfs = f'FUNCTION_SWITCHES' + frgb = f'FUNCTION_SWITCHES_RGB_LEDS' + + if fcfs in hw_defs: + if frgb in hw_defs: + cfs_rgb_led = 1 + if f'RADIO_GX12' in hw_defs: + cfs_groups = 4 + elif f'RADIO_PA01' in hw_defs: + cfs_groups = 2 + else: + cfs_groups = 3 + + return CFS(cfs_rgb_led, cfs_groups) diff --git a/radio/util/hw_defs/hal_json.py b/radio/util/hw_defs/hal_json.py index b803ba4f8..4da2ed673 100644 --- a/radio/util/hw_defs/hal_json.py +++ b/radio/util/hw_defs/hal_json.py @@ -6,6 +6,9 @@ import json from hal_switches import Switch, parse_switches from hal_adc import ADCInput, SPI_ADCInput, ADC, ADCInputParser from hal_keys import Key, Trim, parse_trims, parse_keys +from hal_lcd import Display, parse_lcd +from hal_cfs import CFS, parse_cfs +from hal_misc import Misc, parse_misc import legacy_names @@ -62,6 +65,12 @@ class DictEncoder(json.JSONEncoder): return prune_dict(obj.__dict__) if isinstance(obj, Key): return prune_dict(obj.__dict__) + if isinstance(obj, Display): + return prune_dict(obj.__dict__) + if isinstance(obj, CFS): + return prune_dict(obj.__dict__) + if isinstance(obj, Misc): + return prune_dict(obj.__dict__) # Let the base class default method raise the TypeError return json.JSONEncoder.default(self, obj) @@ -89,5 +98,14 @@ def parse_defines(filename, target): trims = parse_trims(hw_defs) out_defs["trims"] = trims + display = parse_lcd(hw_defs) + out_defs["display"] = display + + cfs = parse_cfs(hw_defs) + out_defs["custom_switches"] = cfs + + misc = parse_misc(hw_defs) + out_defs["hardware"] = misc + print(json.dumps(out_defs, cls=DictEncoder, indent=2)) diff --git a/radio/util/hw_defs/hal_lcd.py b/radio/util/hw_defs/hal_lcd.py new file mode 100644 index 000000000..80fa3934a --- /dev/null +++ b/radio/util/hw_defs/hal_lcd.py @@ -0,0 +1,60 @@ + +class Display: + + def __init__(self, w, h, phys_w, phys_h, depth, color, oled, bl_color): + self.w = w + self.h = h + self.phys_w = phys_w + self.phys_h = phys_h + self.depth = depth + self.color = color + self.oled = oled + self.backlight_color = bl_color + +def parse_lcd(hw_defs): + + fw = f'LCD_W' + fh = f'LCD_H' + fphys_w = f'LCD_PHYS_W' + fphys_h = f'LCD_PHYS_H' + fdepth = f'LCD_DEPTH' + foled = f'OLED_SCREEN' + fbl_color = f'HAS_BACKLIGHT_COLOR' + + w = hw_defs[fw] + h = hw_defs[fh] + if fphys_w in hw_defs: + phys_w = hw_defs[fphys_w] + else: + phys_w = w + if fphys_h in hw_defs: + phys_h = hw_defs[fphys_h] + else: + phys_h = h + depth = hw_defs[fdepth] + + if phys_w == fw: + phys_w = w + if phys_w == fh: + phys_w = h + if phys_h == fh: + phys_h = h + if phys_h == fw: + phys_h = w + + if foled in hw_defs: + oled = 1 + else: + oled = 0 + + if depth < 16: + color = 0 + else: + color = 1 + + if fbl_color in hw_defs: + bl_color = 1 + else: + bl_color = 0 + + return Display(w, h, phys_w, phys_h, depth, color, oled, bl_color) diff --git a/radio/util/hw_defs/hal_misc.py b/radio/util/hw_defs/hal_misc.py new file mode 100644 index 000000000..ae8f8322e --- /dev/null +++ b/radio/util/hw_defs/hal_misc.py @@ -0,0 +1,44 @@ + +class Misc: + + def __init__(self, has_audio_mute, has_bling_leds, has_ext_module_support, has_int_module_support, sport_max_baudrate, surface): + self.has_audio_mute = has_audio_mute + self.has_bling_leds = has_bling_leds + self.has_ext_module_support = has_ext_module_support + self.has_int_module_support = has_int_module_support + self.sport_max_baudrate = sport_max_baudrate + self.surface = surface + +def parse_misc(hw_defs): + + if f'RADIO_MT12' in hw_defs: + surface = 1 + else: + surface = 0 + + if f'SPORT_MAX_BAUDRATE' in hw_defs: + sport_max_baudrate = hw_defs[f'SPORT_MAX_BAUDRATE'] + else: + sport_max_baudrate = 400000 + + if f'AUDIO_MUTE_GPIO' in hw_defs: + has_audio_mute = 1 + else: + has_audio_mute = 0 + + if f'BLING_LED_STRIP_LENGTH' in hw_defs: + has_bling_leds = hw_defs[f'BLING_LED_STRIP_LENGTH'] + else: + has_bling_leds = 0 + + if f'RADIO_T8' in hw_defs: + has_ext_module_support = 0 + else: + has_ext_module_support = 1 + + if f'PCBX9D' in hw_defs or f'PCBX9DP' in hw_defs or f'PCBX9E' in hw_defs: + has_int_module_support = 0 + else: + has_int_module_support = 1 + + return Misc(has_audio_mute, has_bling_leds, has_ext_module_support, has_int_module_support, sport_max_baudrate, surface)