diff --git a/Devices/lilygo-tdeck/CMakeLists.txt b/Devices/lilygo-tdeck/CMakeLists.txt index fc5c6b676..eda65fcd2 100644 --- a/Devices/lilygo-tdeck/CMakeLists.txt +++ b/Devices/lilygo-tdeck/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility EstimatedPower driver + REQUIRES Tactility driver ) diff --git a/Devices/lilygo-tdeck/Source/Configuration.cpp b/Devices/lilygo-tdeck/Source/Configuration.cpp deleted file mode 100644 index 61a274f41..000000000 --- a/Devices/lilygo-tdeck/Source/Configuration.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "devices/Power.h" -#include "devices/TrackballDevice.h" - -#include - -bool initBoot(); - -using namespace tt::hal; - -static std::vector> createDevices() { - return { - createPower() - // std::make_shared(), - }; -} - -extern const Configuration hardwareConfiguration = { - .createDevices = createDevices -}; diff --git a/Devices/lilygo-tdeck/Source/devices/Power.cpp b/Devices/lilygo-tdeck/Source/devices/Power.cpp deleted file mode 100644 index 115a11c69..000000000 --- a/Devices/lilygo-tdeck/Source/devices/Power.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -std::shared_ptr createPower() { - ChargeFromAdcVoltage::Configuration configuration; - // 2.0 ratio, but +.11 added as display voltage sag compensation. - configuration.adcMultiplier = 2.11; - - return std::make_shared(configuration); -} diff --git a/Devices/lilygo-tdeck/Source/devices/Power.h b/Devices/lilygo-tdeck/Source/devices/Power.h deleted file mode 100644 index e2f20d5f6..000000000 --- a/Devices/lilygo-tdeck/Source/devices/Power.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -std::shared_ptr createPower(); diff --git a/Devices/lilygo-tdeck/Source/module.cpp b/Devices/lilygo-tdeck/Source/module.cpp index 5f0341f6a..5c239157c 100644 --- a/Devices/lilygo-tdeck/Source/module.cpp +++ b/Devices/lilygo-tdeck/Source/module.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,9 @@ constexpr auto* TAG = "T-Deck"; constexpr auto TDECK_POWERON_GPIO = GPIO_NUM_10; +// Legacy placeholder (required until legacy HAL is cleaned up everywhere) +extern const tt::hal::Configuration hardwareConfiguration = {}; + extern "C" { extern Driver tdeck_keyboard_driver; diff --git a/Devices/lilygo-tdeck/Source/tdeck_keyboard.cpp b/Devices/lilygo-tdeck/Source/tdeck_keyboard.cpp index a40c3b397..a41acc0b1 100644 --- a/Devices/lilygo-tdeck/Source/tdeck_keyboard.cpp +++ b/Devices/lilygo-tdeck/Source/tdeck_keyboard.cpp @@ -87,7 +87,7 @@ static const KeyboardApi tdeck_keyboard_api = { .read_key = tdeck_keyboard_read_key, }; -extern struct Module lilygo_tdeck_module; +extern Module lilygo_tdeck_module; Driver tdeck_keyboard_driver = { .name = "tdeck_keyboard", diff --git a/Devices/lilygo-tdeck/lilygo,tdeck.dts b/Devices/lilygo-tdeck/lilygo,tdeck.dts index b8f21e92a..e6abdf07c 100644 --- a/Devices/lilygo-tdeck/lilygo,tdeck.dts +++ b/Devices/lilygo-tdeck/lilygo,tdeck.dts @@ -1,26 +1,43 @@ /dts-v1/; +#include #include +#include #include -#include #include #include #include #include #include +#include +#include +#include +#include + #include +#include #include #include -#include -#include -#include -#include // Reference: https://wiki.lilygo.cc/get_started/en/Wearable/T-Deck-Plus/T-Deck-Plus.html#Pin-Overview / { compatible = "root"; model = "LilyGO T-Deck"; + adc0 { + compatible = "espressif,esp32-adc-oneshot"; + unit-id = ; + clk-src = ; + channels = ; + }; + + battery-sense { + compatible = "battery-sense"; + io-channels = <&adc0 0>; + reference-voltage-mv = <3300>; + multiplier = <2110>; + }; + wifi0 { compatible = "espressif,esp32-wifi-pinned"; status = "disabled"; diff --git a/Platforms/platform-esp32/CMakeLists.txt b/Platforms/platform-esp32/CMakeLists.txt index 548e94fc3..6d419cb1c 100644 --- a/Platforms/platform-esp32/CMakeLists.txt +++ b/Platforms/platform-esp32/CMakeLists.txt @@ -6,7 +6,7 @@ idf_component_register( SRCS ${SOURCES} INCLUDE_DIRS "include/" PRIV_INCLUDE_DIRS "private/" - REQUIRES TactilityKernel driver esp_driver_i2c vfs fatfs esp_wifi esp_netif esp_event + REQUIRES TactilityKernel driver esp_adc esp_driver_i2c vfs fatfs esp_wifi esp_netif esp_event ) idf_component_optional_requires(PRIVATE bt usb espressif__usb_host_hid espressif__usb_host_msc) diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-adc-oneshot.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-adc-oneshot.yaml new file mode 100644 index 000000000..cc35fedd0 --- /dev/null +++ b/Platforms/platform-esp32/bindings/espressif,esp32-adc-oneshot.yaml @@ -0,0 +1,30 @@ +description: ESP32 ADC Controller (oneshot mode) + +include: ["adc-controller.yaml"] + +compatible: "espressif,esp32-adc-oneshot" + +properties: + unit-id: + type: int + required: true + description: | + The ADC unit, defined by adc_unit_t (e.g. ADC_UNIT_1, ADC_UNIT_2). + clk-src: + type: int + default: 0 + description: | + Clock source, defined by adc_oneshot_clk_src_t. 0 selects the default clock source. + ulp-mode: + type: int + default: 0 + description: | + ULP control mode, defined by adc_ulp_mode_t. 0 means ADC_ULP_MODE_DISABLE. + channels: + type: phandle-array + element-type: "struct Esp32AdcOneshotChannelConfig" + default: "{ }" + description: | + Per-channel configuration. Each entry is written as , + e.g. . + A consumer device refers to a channel with a phandle and channel number, e.g. `<&adc0 3>`. diff --git a/Platforms/platform-esp32/include/tactility/bindings/esp32_adc_oneshot.h b/Platforms/platform-esp32/include/tactility/bindings/esp32_adc_oneshot.h new file mode 100644 index 000000000..3e2d13efe --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/bindings/esp32_adc_oneshot.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_DEVICETREE(esp32_adc_oneshot, struct Esp32AdcOneshotConfig) + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_adc_oneshot.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_adc_oneshot.h new file mode 100644 index 000000000..826a490a1 --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_adc_oneshot.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct Esp32AdcOneshotChannelConfig { + adc_channel_t channel; + adc_atten_t atten; + adc_bitwidth_t bitwidth; +}; + +struct Esp32AdcOneshotConfig { + adc_unit_t unit_id; + adc_oneshot_clk_src_t clk_src; + adc_ulp_mode_t ulp_mode; + /** Per-channel configuration */ + const struct Esp32AdcOneshotChannelConfig* channels; + /** The item count of channels */ + size_t channel_count; +}; + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/source/drivers/esp32_adc_oneshot.cpp b/Platforms/platform-esp32/source/drivers/esp32_adc_oneshot.cpp new file mode 100644 index 000000000..1957142ca --- /dev/null +++ b/Platforms/platform-esp32/source/drivers/esp32_adc_oneshot.cpp @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +#include + +#include +#include +#include +#include +#include +#include + +#define TAG "esp32_adc_oneshot" + +#define GET_CONFIG(device) ((Esp32AdcOneshotConfig*)device->config) +#define GET_HANDLE(device) ((adc_oneshot_unit_handle_t)device_get_driver_data(device)) + +static const Esp32AdcOneshotChannelConfig* find_channel_config(const Esp32AdcOneshotConfig* dts_config, uint8_t channel_index) { + if (channel_index >= dts_config->channel_count) { + return nullptr; + } + return &dts_config->channels[channel_index]; +} + +extern "C" { + +static error_t read_raw(Device* device, uint8_t channel, int* out_raw, TickType_t timeout) { + if (xPortInIsrContext()) return ERROR_ISR_STATUS; + auto* dts_config = GET_CONFIG(device); + auto* channel_config = find_channel_config(dts_config, channel); + if (channel_config == nullptr) { + return ERROR_OUT_OF_RANGE; + } + + esp_err_t esp_error = adc_oneshot_read(GET_HANDLE(device), channel_config->channel, out_raw); + if (esp_error != ESP_OK) { + LOG_E(TAG, "read(channel=%u) failed: %s", channel, esp_err_to_name(esp_error)); + } + return esp_err_to_error(esp_error); +} + +static error_t start(Device* device) { + LOG_I(TAG, "start %s", device->name); + auto* dts_config = GET_CONFIG(device); + + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = dts_config->unit_id, + .clk_src = dts_config->clk_src, + .ulp_mode = dts_config->ulp_mode, + }; + + adc_oneshot_unit_handle_t handle; + esp_err_t esp_error = adc_oneshot_new_unit(&init_config, &handle); + if (esp_error != ESP_OK) { + LOG_E(TAG, "Failed to create ADC unit %d: %s", (int)dts_config->unit_id, esp_err_to_name(esp_error)); + return ERROR_RESOURCE; + } + + for (size_t i = 0; i < dts_config->channel_count; i++) { + const auto& channel_config = dts_config->channels[i]; + adc_oneshot_chan_cfg_t chan_cfg = { + .atten = channel_config.atten, + .bitwidth = channel_config.bitwidth, + }; + esp_error = adc_oneshot_config_channel(handle, channel_config.channel, &chan_cfg); + if (esp_error != ESP_OK) { + LOG_E(TAG, "Failed to configure channel %d: %s", (int)channel_config.channel, esp_err_to_name(esp_error)); + adc_oneshot_del_unit(handle); + return ERROR_RESOURCE; + } + } + + device_set_driver_data(device, handle); + return ERROR_NONE; +} + +static error_t stop(Device* device) { + LOG_I(TAG, "stop %s", device->name); + adc_oneshot_del_unit(GET_HANDLE(device)); + device_set_driver_data(device, nullptr); + return ERROR_NONE; +} + +static constexpr AdcControllerApi ESP32_ADC_ONESHOT_API = { + .read_raw = read_raw +}; + +extern Module platform_esp32_module; + +Driver esp32_adc_oneshot_driver = { + .name = "esp32_adc_oneshot", + .compatible = (const char*[]) { "espressif,esp32-adc-oneshot", nullptr }, + .start_device = start, + .stop_device = stop, + .api = &ESP32_ADC_ONESHOT_API, + .device_type = &ADC_CONTROLLER_TYPE, + .owner = &platform_esp32_module, + .internal = nullptr +}; + +} // extern "C" diff --git a/Platforms/platform-esp32/source/module.cpp b/Platforms/platform-esp32/source/module.cpp index 6809ad484..f4c3545ec 100644 --- a/Platforms/platform-esp32/source/module.cpp +++ b/Platforms/platform-esp32/source/module.cpp @@ -11,6 +11,7 @@ extern "C" { +extern Driver esp32_adc_oneshot_driver; extern Driver esp32_gpio_driver; extern Driver esp32_i2c_driver; extern Driver esp32_i2c_master_driver; @@ -43,6 +44,7 @@ extern Driver esp32_usbhost_msc_driver; static error_t start() { /* We crash when construct fails, because if a single driver fails to construct, * there is no guarantee that the previously constructed drivers can be destroyed */ + check(driver_construct_add(&esp32_adc_oneshot_driver) == ERROR_NONE); check(driver_construct_add(&esp32_gpio_driver) == ERROR_NONE); check(driver_construct_add(&esp32_i2c_driver) == ERROR_NONE); check(driver_construct_add(&esp32_i2c_master_driver) == ERROR_NONE); @@ -93,6 +95,7 @@ static error_t stop() { check(driver_remove_destruct(&esp32_ble_serial_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_bluetooth_driver) == ERROR_NONE); #endif + check(driver_remove_destruct(&esp32_adc_oneshot_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_gpio_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_i2c_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_i2c_master_driver) == ERROR_NONE); diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index cc0b1eb1e..66af4d9e9 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -231,7 +231,7 @@ static void registerInternalApps() { addAppManifest(app::gpssettings::manifest); } - if (hal::hasDevice(hal::Device::Type::Power)) { + if (device_exists_of_type(&POWER_SUPPLY_TYPE)) { addAppManifest(app::power::manifest); } diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index 5269e2906..4ac68543f 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -90,10 +90,10 @@ static const char* getSdCardStatusIcon(bool mounted) { static const char* getPowerStatusIcon() { // TODO: Support multiple power devices? - std::shared_ptr power; - hal::findDevices(hal::Device::Type::Power, [&power](const auto& device) { - if (device->supportsMetric(hal::power::PowerDevice::MetricType::ChargeLevel)) { - power = device; + Device* power = nullptr; + device_for_each_of_type(&POWER_SUPPLY_TYPE, &power, [](Device* device, void* context) { + if (device_is_ready(device) && power_supply_supports_property(device, POWER_SUPPLY_PROP_CAPACITY)) { + *static_cast(context) = device; return false; } return true; @@ -103,12 +103,12 @@ static const char* getPowerStatusIcon() { return nullptr; } - hal::power::PowerDevice::MetricData charge_level; - if (!power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) { + PowerSupplyPropertyValue charge_level; + if (power_supply_get_property(power, POWER_SUPPLY_PROP_CAPACITY, &charge_level) != ERROR_NONE) { return nullptr; } - uint8_t charge = charge_level.valueAsUint8; + int charge = charge_level.int_value; if (charge >= 95) { return LVGL_ICON_STATUSBAR_BATTERY_ANDROID_FRAME_FULL; diff --git a/TactilityKernel/bindings/adc-controller.yaml b/TactilityKernel/bindings/adc-controller.yaml new file mode 100644 index 000000000..5c055e135 --- /dev/null +++ b/TactilityKernel/bindings/adc-controller.yaml @@ -0,0 +1 @@ +description: ADC controller diff --git a/TactilityKernel/bindings/battery-sense.yaml b/TactilityKernel/bindings/battery-sense.yaml new file mode 100644 index 000000000..0776e5885 --- /dev/null +++ b/TactilityKernel/bindings/battery-sense.yaml @@ -0,0 +1,19 @@ +description: Battery voltage sense via an ADC channel behind a voltage divider + +compatible: "battery-sense" + +properties: + io-channels: + type: phandles + required: true + description: The ADC channel connected to the battery sense circuit, e.g. `<&adc1 3>` + reference-voltage-mv: + type: int + required: true + description: The ADC's effective full-scale reference voltage at the channel's configured attenuation, in mV. + multiplier: + type: int + default: 1000 + description: | + Voltage divider correction factor, fixed-point with 3 decimals (1000 = 1.000, i.e. no division). + battery_mv = adc_mv * multiplier / 1000. diff --git a/TactilityKernel/include/tactility/bindings/battery_sense.h b/TactilityKernel/include/tactility/bindings/battery_sense.h new file mode 100644 index 000000000..438d20615 --- /dev/null +++ b/TactilityKernel/include/tactility/bindings/battery_sense.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_DEVICETREE(battery_sense, struct BatterySenseConfig) + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/include/tactility/drivers/adc.h b/TactilityKernel/include/tactility/drivers/adc.h new file mode 100644 index 000000000..f92b8ced4 --- /dev/null +++ b/TactilityKernel/include/tactility/drivers/adc.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct Device; + +#define ADC_CHANNEL_SPEC_NONE ((struct AdcChannelSpec) { NULL, 0 }) + +/** The index of a channel on an ADC controller */ +typedef uint8_t adc_channel_index_t; + +/** + * Specifies a channel on a specific ADC controller. + * Used by the devicetree, drivers and application code to refer to ADC channels. + */ +struct AdcChannelSpec { + /** ADC device controlling the channel */ + struct Device* adc_controller; + /** The channel's index on the device */ + adc_channel_index_t channel; +}; + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/include/tactility/drivers/adc_controller.h b/TactilityKernel/include/tactility/drivers/adc_controller.h new file mode 100644 index 000000000..875ec2e47 --- /dev/null +++ b/TactilityKernel/include/tactility/drivers/adc_controller.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "adc.h" + +#include +#include + +struct Device; + +/** + * @brief API for ADC controller drivers. + */ +struct AdcControllerApi { + /** + * @brief Reads the raw conversion result of an ADC channel. + * @param[in] device the ADC controller device + * @param[in] channel the channel index to read + * @param[out] out_raw the raw conversion result + * @param[in] timeout the maximum time to wait for the operation to complete + * @retval ERROR_NONE when the read operation was successful + * @retval ERROR_OUT_OF_RANGE when the channel index is not configured + * @retval ERROR_TIMEOUT when the operation timed out + */ + error_t (*read_raw)(struct Device* device, uint8_t channel, int* out_raw, TickType_t timeout); +}; + +/** + * @brief Reads the raw conversion result of an ADC channel using the specified controller. + * @param[in] device the ADC controller device + * @param[in] channel the channel index to read + * @param[out] out_raw the raw conversion result + * @param[in] timeout the maximum time to wait for the operation to complete + * @retval ERROR_NONE when the read operation was successful + */ +error_t adc_controller_read_raw(struct Device* device, uint8_t channel, int* out_raw, TickType_t timeout); + +/** + * @brief Reads the raw conversion result of the ADC channel described by the given spec. + * @param[in] spec the channel spec, as acquired from a devicetree phandle reference (e.g. `<&adc0 3>`) + * @param[out] out_raw the raw conversion result + * @param[in] timeout the maximum time to wait for the operation to complete + * @retval ERROR_NONE when the read operation was successful + */ +error_t adc_channel_read_raw(const struct AdcChannelSpec* spec, int* out_raw, TickType_t timeout); + +extern const struct DeviceType ADC_CONTROLLER_TYPE; + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/include/tactility/drivers/battery_sense.h b/TactilityKernel/include/tactility/drivers/battery_sense.h new file mode 100644 index 000000000..811fe9be7 --- /dev/null +++ b/TactilityKernel/include/tactility/drivers/battery_sense.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct BatterySenseConfig { + /** The ADC channel connected to the battery sense circuit */ + struct AdcChannelSpec io_channel; + /** The ADC's effective full-scale reference voltage at the channel's configured attenuation, in mV */ + uint32_t reference_voltage_mv; + /** Voltage divider correction factor, fixed-point with 3 decimals (1000 = 1.000, i.e. no division) */ + uint32_t multiplier; +}; + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/source/drivers/adc_controller.cpp b/TactilityKernel/source/drivers/adc_controller.cpp new file mode 100644 index 000000000..e944abfa7 --- /dev/null +++ b/TactilityKernel/source/drivers/adc_controller.cpp @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +#define ADC_CONTROLLER_DRIVER_API(driver) ((struct AdcControllerApi*)driver->api) + +extern "C" { + +error_t adc_controller_read_raw(Device* device, uint8_t channel, int* out_raw, TickType_t timeout) { + const auto* driver = device_get_driver(device); + return ADC_CONTROLLER_DRIVER_API(driver)->read_raw(device, channel, out_raw, timeout); +} + +error_t adc_channel_read_raw(const struct AdcChannelSpec* spec, int* out_raw, TickType_t timeout) { + return adc_controller_read_raw(spec->adc_controller, spec->channel, out_raw, timeout); +} + +const struct DeviceType ADC_CONTROLLER_TYPE { + .name = "adc-controller" +}; + +} diff --git a/TactilityKernel/source/drivers/battery_sense.cpp b/TactilityKernel/source/drivers/battery_sense.cpp new file mode 100644 index 000000000..21b858e1d --- /dev/null +++ b/TactilityKernel/source/drivers/battery_sense.cpp @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include +#include +#include +#include + +#include + +// Raw-to-millivolt conversion assumes a 12-bit ADC, since the generic ADC API doesn't expose resolution. +#define ADC_MAX_RAW 4095 + +// The power-supply child's config pointer isn't set; it reads its settings from its parent's config instead. +#define GET_PARENT_CONFIG(device) ((const BatterySenseConfig*)device_get_parent(device)->config) + +extern "C" { + +static bool supports_property(Device*, PowerSupplyProperty property) { + return property == POWER_SUPPLY_PROP_VOLTAGE; +} + +static error_t get_property(Device* device, PowerSupplyProperty property, PowerSupplyPropertyValue* out_value) { + if (property != POWER_SUPPLY_PROP_VOLTAGE) { + return ERROR_NOT_SUPPORTED; + } + + const auto* config = GET_PARENT_CONFIG(device); + int raw; + error_t error = adc_channel_read_raw(&config->io_channel, &raw, portMAX_DELAY); + if (error != ERROR_NONE) { + return error; + } + + int64_t adc_mv = ((int64_t)raw * config->reference_voltage_mv) / ADC_MAX_RAW; + out_value->int_value = (int)((adc_mv * config->multiplier) / 1000); + return ERROR_NONE; +} + +static bool supports_charge_control(Device*) { return false; } +static bool is_allowed_to_charge(Device*) { return false; } +static error_t set_allowed_to_charge(Device*, bool) { return ERROR_NOT_SUPPORTED; } +static bool supports_quick_charge(Device*) { return false; } +static bool is_quick_charge_enabled(Device*) { return false; } +static error_t set_quick_charge_enabled(Device*, bool) { return ERROR_NOT_SUPPORTED; } +static bool supports_power_off(Device*) { return false; } +static error_t power_off(Device*) { return ERROR_NOT_SUPPORTED; } + +static constexpr PowerSupplyApi BATTERY_SENSE_POWER_SUPPLY_API = { + .supports_property = supports_property, + .get_property = get_property, + .supports_charge_control = supports_charge_control, + .is_allowed_to_charge = is_allowed_to_charge, + .set_allowed_to_charge = set_allowed_to_charge, + .supports_quick_charge = supports_quick_charge, + .is_quick_charge_enabled = is_quick_charge_enabled, + .set_quick_charge_enabled = set_quick_charge_enabled, + .supports_power_off = supports_power_off, + .power_off = power_off, +}; + +// Registered (driver_construct_add() in kernel_init.cpp) so driver_bind() has a valid ->internal, +// but never matched against a devicetree node: battery_sense_driver wires it up directly by pointer. +Driver battery_sense_power_supply_driver = { + .name = "battery-sense-power-supply", + .compatible = (const char*[]) { "battery-sense-power-supply", nullptr }, + .start_device = nullptr, + .stop_device = nullptr, + .api = &BATTERY_SENSE_POWER_SUPPLY_API, + .device_type = &POWER_SUPPLY_TYPE, + .owner = nullptr, + .internal = nullptr +}; + +struct BatterySenseInternal { + Device* power_supply_device = nullptr; +}; + +static error_t create_power_supply_child(Device* parent, Device*& out_child) { + auto* child = new(std::nothrow) Device { .address = 0, .name = "battery-sense-power-supply", .config = nullptr, .parent = nullptr, .internal = nullptr }; + if (child == nullptr) { + return ERROR_OUT_OF_MEMORY; + } + + error_t error = device_construct(child); + if (error != ERROR_NONE) { + delete child; + return error; + } + + device_set_parent(child, parent); + device_set_driver(child, &battery_sense_power_supply_driver); + + error = device_add(child); + if (error != ERROR_NONE) { + device_destruct(child); + delete child; + return error; + } + + error = device_start(child); + if (error != ERROR_NONE) { + device_remove(child); + device_destruct(child); + delete child; + return error; + } + + out_child = child; + return ERROR_NONE; +} + +static void destroy_power_supply_child(Device* child) { + check(device_stop(child) == ERROR_NONE); + check(device_remove(child) == ERROR_NONE); + check(device_destruct(child) == ERROR_NONE); + delete child; +} + +static error_t start(Device* device) { + auto* internal = new(std::nothrow) BatterySenseInternal(); + if (internal == nullptr) { + return ERROR_OUT_OF_MEMORY; + } + + error_t error = create_power_supply_child(device, internal->power_supply_device); + if (error != ERROR_NONE) { + delete internal; + return error; + } + + device_set_driver_data(device, internal); + return ERROR_NONE; +} + +static error_t stop(Device* device) { + auto* internal = (BatterySenseInternal*)device_get_driver_data(device); + destroy_power_supply_child(internal->power_supply_device); + device_set_driver_data(device, nullptr); + delete internal; + return ERROR_NONE; +} + +Driver battery_sense_driver = { + .name = "battery-sense", + .compatible = (const char*[]) { "battery-sense", nullptr }, + .start_device = start, + .stop_device = stop, + .api = nullptr, + .device_type = nullptr, + .owner = nullptr, + .internal = nullptr +}; + +} diff --git a/TactilityKernel/source/kernel_init.cpp b/TactilityKernel/source/kernel_init.cpp index bd1e10619..f6fc5148e 100644 --- a/TactilityKernel/source/kernel_init.cpp +++ b/TactilityKernel/source/kernel_init.cpp @@ -20,6 +20,10 @@ static error_t start() { if (driver_construct_add(&pointer_placeholder_driver) != ERROR_NONE) return ERROR_RESOURCE; extern Driver spi_peripheral_driver; if (driver_construct_add(&spi_peripheral_driver) != ERROR_NONE) return ERROR_RESOURCE; + extern Driver battery_sense_driver; + if (driver_construct_add(&battery_sense_driver) != ERROR_NONE) return ERROR_RESOURCE; + extern Driver battery_sense_power_supply_driver; + if (driver_construct_add(&battery_sense_power_supply_driver) != ERROR_NONE) return ERROR_RESOURCE; return ERROR_NONE; }