New kernel drivers and device migrations (#563)
This commit is contained in:
committed by
GitHub
parent
955416dac8
commit
8af6204ba1
@@ -0,0 +1,72 @@
|
||||
#include "BatteryManager.h"
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/backlight.h>
|
||||
#include <tactility/drivers/gpio.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <drivers/bq24295.h>
|
||||
|
||||
constexpr auto* TAG = "unPhoneFeatures";
|
||||
|
||||
namespace expanderpin {
|
||||
static constexpr gpio_pin_t EXPANDER_POWER = 0; // enable exp brd if high
|
||||
}
|
||||
|
||||
bool BatteryManager::initGpioExpander() {
|
||||
if (device_get_by_name("tca9535", &expander) != ERROR_NONE) {
|
||||
LOG_E(TAG, "IO expander device not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
expanderPowerPin = gpio_descriptor_acquire(expander, expanderpin::EXPANDER_POWER, GPIO_OWNER_GPIO);
|
||||
check(expanderPowerPin != nullptr);
|
||||
gpio_descriptor_set_flags(expanderPowerPin, GPIO_FLAG_DIRECTION_OUTPUT);
|
||||
|
||||
device_put(expander);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BatteryManager::init() {
|
||||
LOG_I(TAG, "init");
|
||||
|
||||
if (!initGpioExpander()) {
|
||||
LOG_E(TAG, "GPIO expander init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BatteryManager::setExpanderPower(bool on) const {
|
||||
return gpio_descriptor_set_level(expanderPowerPin, on) == ERROR_NONE;
|
||||
}
|
||||
|
||||
void BatteryManager::turnPeripheralsOff() const {
|
||||
setExpanderPower(false);
|
||||
|
||||
Device* backlight = nullptr;
|
||||
check(device_get_by_name("display_backlight", &backlight) == ERROR_NONE);
|
||||
backlight_set_brightness(backlight, 0); // Allowed to fail, we don't care about the result
|
||||
device_put(backlight);
|
||||
}
|
||||
|
||||
void BatteryManager::setShipping(bool on) const {
|
||||
if (on) {
|
||||
LOG_W(TAG, "setShipping: on");
|
||||
bq24295_set_watchdog_timer(batteryManagement, BQ24295_WATCHDOG_DISABLED);
|
||||
bq24295_set_bat_fet_on(batteryManagement, false);
|
||||
} else {
|
||||
LOG_W(TAG, "setShipping: off");
|
||||
bq24295_set_watchdog_timer(batteryManagement, BQ24295_WATCHDOG_ENABLED_40S);
|
||||
bq24295_set_bat_fet_on(batteryManagement, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool BatteryManager::isUsbPowerConnected() const {
|
||||
bool connected = false;
|
||||
return bq24295_is_usb_power_connected(batteryManagement, &connected) == ERROR_NONE && connected;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
struct Device;
|
||||
struct GpioDescriptor;
|
||||
|
||||
/**
|
||||
* Easy access to GPIO pins
|
||||
*/
|
||||
class BatteryManager final {
|
||||
|
||||
::Device* expander = nullptr;
|
||||
::Device* batteryManagement = nullptr;
|
||||
::GpioDescriptor* expanderPowerPin = nullptr;
|
||||
|
||||
bool initGpioExpander();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
explicit BatteryManager() {
|
||||
check(device_get_by_name("bq24295", &batteryManagement) == ERROR_NONE);
|
||||
}
|
||||
|
||||
~BatteryManager() {
|
||||
device_put(batteryManagement);
|
||||
}
|
||||
|
||||
bool init();
|
||||
|
||||
bool setExpanderPower(bool on) const;
|
||||
|
||||
void turnPeripheralsOff() const;
|
||||
|
||||
/** Battery management (BQ24295) will stop supplying power until USB connects */
|
||||
void setShipping(bool on) const;
|
||||
|
||||
bool isUsbPowerConnected() const;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/unphone_nav_buttons.h>
|
||||
|
||||
// The devicetree compiler derives the expected config typedef name from the compatible
|
||||
// string's suffix (e.g. "unphone,nav-buttons" -> nav_buttons_config_dt), not from the
|
||||
// node name or driver name, so the tag here must match that exactly.
|
||||
DEFINE_DEVICETREE(nav_buttons, struct UnphoneNavButtonsConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/unphone_power_switch.h>
|
||||
|
||||
// The devicetree compiler derives the expected config typedef name from the compatible
|
||||
// string's suffix (e.g. "unphone,power-switch" -> power_switch_config_dt), not from the
|
||||
// node name or driver name, so the tag here must match that exactly.
|
||||
DEFINE_DEVICETREE(power_switch, struct UnphonePowerSwitchConfig)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include "unphone_nav_buttons.h"
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/drivers/gpio_descriptor.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <Tactility/Thread.h>
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#define TAG "UnphoneNavButtons"
|
||||
#define GET_CONFIG(device) (static_cast<const UnphoneNavButtonsConfig*>((device)->config))
|
||||
#define GET_INTERNAL(device) (static_cast<UnphoneNavButtonsInternal*>(device_get_driver_data(device)))
|
||||
|
||||
namespace {
|
||||
// Only button 1 currently drives any behavior (stopping the running app); 2 and 3 are
|
||||
// read and queued like button 1 but nothing consumes them yet (mirrors the original
|
||||
// unPhone nav button behavior).
|
||||
constexpr int BUTTON1_INDEX = 1;
|
||||
constexpr int BUTTON2_INDEX = 2;
|
||||
constexpr int BUTTON3_INDEX = 3;
|
||||
}
|
||||
|
||||
struct UnphoneNavButtonsInternal {
|
||||
GpioDescriptor* pin_button1 = nullptr;
|
||||
GpioDescriptor* pin_button2 = nullptr;
|
||||
GpioDescriptor* pin_button3 = nullptr;
|
||||
|
||||
QueueHandle_t event_queue = nullptr;
|
||||
tt::Thread thread;
|
||||
bool thread_interrupt_requested = false;
|
||||
};
|
||||
|
||||
// region ISR callbacks
|
||||
|
||||
static void IRAM_ATTR on_button1(void* arg) {
|
||||
auto* internal = static_cast<UnphoneNavButtonsInternal*>(arg);
|
||||
int index = BUTTON1_INDEX;
|
||||
xQueueSendFromISR(internal->event_queue, &index, nullptr);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR on_button2(void* arg) {
|
||||
auto* internal = static_cast<UnphoneNavButtonsInternal*>(arg);
|
||||
int index = BUTTON2_INDEX;
|
||||
xQueueSendFromISR(internal->event_queue, &index, nullptr);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR on_button3(void* arg) {
|
||||
auto* internal = static_cast<UnphoneNavButtonsInternal*>(arg);
|
||||
int index = BUTTON3_INDEX;
|
||||
xQueueSendFromISR(internal->event_queue, &index, nullptr);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Consumer thread
|
||||
|
||||
static int32_t nav_buttons_thread_main(UnphoneNavButtonsInternal* internal) {
|
||||
int button_index;
|
||||
while (!internal->thread_interrupt_requested) {
|
||||
if (xQueueReceive(internal->event_queue, &button_index, portMAX_DELAY)) {
|
||||
// The buttons might generate more than 1 click because of how they are built
|
||||
LOG_I(TAG, "Pressed button %d", button_index);
|
||||
if (button_index == BUTTON1_INDEX) {
|
||||
tt::app::stop();
|
||||
}
|
||||
|
||||
// Debounce all events for a short period of time
|
||||
// This is easier than keeping track when each button was last pressed
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(internal->event_queue);
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(internal->event_queue);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Pin acquisition
|
||||
|
||||
static error_t acquire_button(const GpioPinSpec& pin, void (*callback)(void*), void* arg, GpioDescriptor** out_descriptor) {
|
||||
auto* descriptor = gpio_descriptor_acquire(pin.gpio_controller, pin.pin, GPIO_OWNER_GPIO);
|
||||
if (descriptor == nullptr) {
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
// Digital pull-up; the buttons pull the pin low while pressed. Listen for the release
|
||||
// (positive edge) rather than the press - if we listen to the press, these buttons can
|
||||
// generate more than one signal when held down (mirrors the original unPhone init).
|
||||
gpio_flags_t flags = GPIO_FLAG_DIRECTION_INPUT | GPIO_FLAG_PULL_UP;
|
||||
flags = GPIO_FLAG_INTERRUPT_TO_OPTIONS(flags, GPIO_INTERRUPT_POS_EDGE);
|
||||
|
||||
error_t error = gpio_descriptor_set_flags(descriptor, flags);
|
||||
if (error == ERROR_NONE) {
|
||||
error = gpio_descriptor_add_callback(descriptor, callback, arg);
|
||||
}
|
||||
if (error == ERROR_NONE) {
|
||||
error = gpio_descriptor_enable_interrupt(descriptor);
|
||||
}
|
||||
|
||||
if (error != ERROR_NONE) {
|
||||
gpio_descriptor_remove_callback(descriptor);
|
||||
gpio_descriptor_release(descriptor);
|
||||
return error;
|
||||
}
|
||||
|
||||
*out_descriptor = descriptor;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static void release_button(GpioDescriptor*& descriptor) {
|
||||
if (descriptor == nullptr) {
|
||||
return;
|
||||
}
|
||||
gpio_descriptor_disable_interrupt(descriptor);
|
||||
gpio_descriptor_remove_callback(descriptor);
|
||||
gpio_descriptor_release(descriptor);
|
||||
descriptor = nullptr;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t start(Device* device) {
|
||||
const auto* config = GET_CONFIG(device);
|
||||
|
||||
auto* internal = new(std::nothrow) UnphoneNavButtonsInternal();
|
||||
if (internal == nullptr) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
internal->event_queue = xQueueCreate(4, sizeof(int));
|
||||
if (internal->event_queue == nullptr) {
|
||||
delete internal;
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
error_t error = acquire_button(config->pin_button1, on_button1, internal, &internal->pin_button1);
|
||||
if (error == ERROR_NONE) {
|
||||
error = acquire_button(config->pin_button2, on_button2, internal, &internal->pin_button2);
|
||||
}
|
||||
if (error == ERROR_NONE) {
|
||||
error = acquire_button(config->pin_button3, on_button3, internal, &internal->pin_button3);
|
||||
}
|
||||
|
||||
if (error != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to acquire nav button pins");
|
||||
release_button(internal->pin_button1);
|
||||
release_button(internal->pin_button2);
|
||||
release_button(internal->pin_button3);
|
||||
vQueueDelete(internal->event_queue);
|
||||
delete internal;
|
||||
return error;
|
||||
}
|
||||
|
||||
internal->thread.setName("unphone_nav_buttons");
|
||||
internal->thread.setPriority(tt::Thread::Priority::High);
|
||||
internal->thread.setStackSize(3072);
|
||||
internal->thread.setMainFunction([internal] { return nav_buttons_thread_main(internal); });
|
||||
internal->thread.start();
|
||||
|
||||
device_set_driver_data(device, internal);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* internal = GET_INTERNAL(device);
|
||||
|
||||
if (internal->thread.getState() != tt::Thread::State::Stopped) {
|
||||
internal->thread_interrupt_requested = true;
|
||||
internal->thread.join();
|
||||
}
|
||||
|
||||
release_button(internal->pin_button1);
|
||||
release_button(internal->pin_button2);
|
||||
release_button(internal->pin_button3);
|
||||
vQueueDelete(internal->event_queue);
|
||||
|
||||
device_set_driver_data(device, nullptr);
|
||||
delete internal;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
extern Module unphone_module;
|
||||
|
||||
Driver unphone_nav_buttons_driver = {
|
||||
.name = "unphone_nav_buttons",
|
||||
.compatible = (const char*[]) { "unphone,nav-buttons", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &unphone_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <tactility/drivers/gpio.h>
|
||||
|
||||
struct UnphoneNavButtonsConfig {
|
||||
struct GpioPinSpec pin_button1;
|
||||
struct GpioPinSpec pin_button2;
|
||||
struct GpioPinSpec pin_button3;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include "unphone_power_switch.h"
|
||||
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/drivers/gpio_controller.h>
|
||||
#include <tactility/drivers/gpio_descriptor.h>
|
||||
#include <tactility/error_esp32.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
#define TAG "UnphonePowerSwitch"
|
||||
#define GET_CONFIG(device) (static_cast<const UnphonePowerSwitchConfig*>((device)->config))
|
||||
|
||||
struct UnphonePowerSwitchInternal {
|
||||
GpioDescriptor* descriptor;
|
||||
gpio_num_t native_pin;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Module unphone_module;
|
||||
|
||||
static error_t start(Device* device) {
|
||||
const auto* config = GET_CONFIG(device);
|
||||
|
||||
auto* descriptor = gpio_descriptor_acquire(config->pin.gpio_controller, config->pin.pin, GPIO_OWNER_GPIO);
|
||||
if (descriptor == nullptr) {
|
||||
LOG_E(TAG, "Failed to acquire GPIO descriptor");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
if (gpio_descriptor_set_flags(descriptor, config->pin.flags | GPIO_FLAG_DIRECTION_INPUT) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to configure power switch pin as input");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
gpio_num_t native_pin;
|
||||
if (gpio_descriptor_get_native_pin_number(descriptor, &native_pin) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Power switch pin has no native pin number");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Digital pull resistors are powered down during deep sleep; the RTC domain's own pull
|
||||
// resistors are what actually hold the pin state while asleep, so both must be armed
|
||||
// here (mirrors the original unPhone power switch init).
|
||||
if (rtc_gpio_pullup_en(native_pin) != ESP_OK || rtc_gpio_pulldown_en(native_pin) != ESP_OK) {
|
||||
LOG_E(TAG, "Failed to configure RTC pull resistors for power switch");
|
||||
gpio_descriptor_release(descriptor);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
auto* internal = new UnphonePowerSwitchInternal { .descriptor = descriptor, .native_pin = native_pin };
|
||||
device_set_driver_data(device, internal);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* internal = static_cast<UnphonePowerSwitchInternal*>(device_get_driver_data(device));
|
||||
gpio_descriptor_release(internal->descriptor);
|
||||
delete internal;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t unphone_power_switch_is_on(Device* device, bool* on) {
|
||||
auto* internal = static_cast<UnphonePowerSwitchInternal*>(device_get_driver_data(device));
|
||||
return gpio_descriptor_get_level(internal->descriptor, on);
|
||||
}
|
||||
|
||||
error_t unphone_power_switch_enable_wake(Device* device) {
|
||||
auto* internal = static_cast<UnphonePowerSwitchInternal*>(device_get_driver_data(device));
|
||||
auto esp_error = esp_sleep_enable_ext0_wakeup(internal->native_pin, 1);
|
||||
return esp_err_to_error(esp_error);
|
||||
}
|
||||
|
||||
Driver unphone_power_switch_driver = {
|
||||
.name = "unphone_power_switch",
|
||||
.compatible = (const char*[]) { "unphone,power-switch", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &unphone_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/gpio.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct UnphonePowerSwitchConfig {
|
||||
struct GpioPinSpec pin;
|
||||
};
|
||||
|
||||
/** @brief Reads whether the physical power switch is currently in the "on" position. */
|
||||
error_t unphone_power_switch_is_on(struct Device* device, bool* on);
|
||||
|
||||
/**
|
||||
* @brief Arms deep-sleep wakeup so the device wakes when the switch moves to "on".
|
||||
* Only applies to the deep sleep entered right after this call.
|
||||
*/
|
||||
error_t unphone_power_switch_enable_wake(struct Device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "BatteryManager.h"
|
||||
#include "drivers/unphone_power_switch.h"
|
||||
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#include <esp_sleep.h>
|
||||
#include <memory>
|
||||
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/Thread.h>
|
||||
|
||||
constexpr auto* TAG = "unPhone";
|
||||
|
||||
std::shared_ptr<BatteryManager> batteryManagement;
|
||||
static std::unique_ptr<tt::Thread> powerThread;
|
||||
|
||||
enum class PowerState {
|
||||
Initial,
|
||||
On,
|
||||
Off
|
||||
};
|
||||
|
||||
static void update_power_switch(Device* power_switch) {
|
||||
static PowerState last_state = PowerState::Initial;
|
||||
|
||||
bool power_switch_on = false;
|
||||
check(unphone_power_switch_is_on(power_switch, &power_switch_on) == ERROR_NONE);
|
||||
|
||||
if (!power_switch_on) {
|
||||
if (last_state != PowerState::Off) {
|
||||
last_state = PowerState::Off;
|
||||
LOG_W(TAG, "Power off");
|
||||
}
|
||||
|
||||
if (!batteryManagement->isUsbPowerConnected()) { // and usb unplugged we go into shipping mode
|
||||
LOG_W(TAG, "Shipping mode until USB connects");
|
||||
batteryManagement->turnPeripheralsOff();
|
||||
batteryManagement->setShipping(true); // tell BM to stop supplying power until USB connects
|
||||
} else { // When power switch is off, but USB is plugged in, we wait (deep sleep) until USB is unplugged.
|
||||
LOG_W(TAG, "Waiting for USB disconnect to power off");
|
||||
batteryManagement->turnPeripheralsOff();
|
||||
// Deep sleep for 1 minute, then awaken to check power state again
|
||||
// GPIO trigger from power switch also awakens the device
|
||||
unphone_power_switch_enable_wake(power_switch);
|
||||
esp_sleep_enable_timer_wakeup(60000000);
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
} else {
|
||||
if (last_state != PowerState::On) {
|
||||
last_state = PowerState::On;
|
||||
LOG_I(TAG, "Power on");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t power_switch_thread_main() { // check power switch every 10th of sec
|
||||
Device* power_switch = nullptr;
|
||||
check(device_get_by_name("power_switch", &power_switch) == ERROR_NONE);
|
||||
|
||||
while (true) {
|
||||
update_power_switch(power_switch);
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
static void power_switch_thread_start() {
|
||||
powerThread = std::make_unique<tt::Thread>(
|
||||
"unphone_power_switch",
|
||||
4096,
|
||||
[]() -> int32_t { return power_switch_thread_main(); }
|
||||
);
|
||||
powerThread->start();
|
||||
}
|
||||
|
||||
|
||||
static bool power_on() {
|
||||
batteryManagement = std::make_shared<BatteryManager>();
|
||||
if (!batteryManagement->init()) {
|
||||
LOG_E(TAG, "UnPhoneFeatures init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
batteryManagement->setExpanderPower(false);
|
||||
|
||||
// Turn off the device if power switch is on off state,
|
||||
// instead of waiting for the Thread to start and continue booting
|
||||
Device* power_switch = nullptr;
|
||||
check(device_get_by_name("power_switch", &power_switch) == ERROR_NONE);
|
||||
update_power_switch(power_switch);
|
||||
device_put(power_switch);
|
||||
|
||||
power_switch_thread_start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init_boot() {
|
||||
LOG_I(TAG, LOG_MESSAGE_POWER_ON_START);
|
||||
|
||||
if (!power_on()) {
|
||||
LOG_E(TAG, LOG_MESSAGE_POWER_ON_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/device_listener.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/error.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
bool init_boot();
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver unphone_power_switch_driver;
|
||||
extern Driver unphone_nav_buttons_driver;
|
||||
|
||||
// The root device (the only device with no parent) reaching DEVICE_EVENT_STARTED means the
|
||||
// whole devicetree has finished constructing/starting, so it's now safe to power on the
|
||||
// unPhone-specific peripherals that initBoot() depends on (e.g. the bq24295 fuel gauge).
|
||||
static void on_device_event(Device* device, DeviceEvent event, void* context) {
|
||||
(void)context;
|
||||
|
||||
static bool has_power_switch = false;
|
||||
static bool has_bq24295 = false;
|
||||
static bool has_backlight = false;
|
||||
static bool did_init = false;
|
||||
|
||||
if (event == DEVICE_EVENT_STARTED) {
|
||||
if (strcmp(device->name, "power_switch") == 0) { has_power_switch = true; }
|
||||
if (strcmp(device->name, "bq24295") == 0) { has_bq24295 = true; }
|
||||
if (strcmp(device->name, "display_backlight") == 0) { has_backlight = true; }
|
||||
}
|
||||
|
||||
if (!did_init && has_power_switch && has_bq24295 && has_backlight) {
|
||||
check(init_boot(), "unPhone initBoot failed");
|
||||
did_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
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(&unphone_power_switch_driver) == ERROR_NONE);
|
||||
check(driver_construct_add(&unphone_nav_buttons_driver) == ERROR_NONE);
|
||||
device_listener_add(on_device_event, nullptr);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
device_listener_remove(&on_device_event);
|
||||
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||
check(driver_remove_destruct(&unphone_nav_buttons_driver) == ERROR_NONE);
|
||||
check(driver_remove_destruct(&unphone_power_switch_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
struct Module unphone_module = {
|
||||
.name = "unphone",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user