mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-12 23:48:10 -07:00
Merge remote-tracking branch 'ofw/dev' into feat/nfc-type-4
This commit is contained in:
@@ -43,6 +43,7 @@ libs = env.BuildModules(
|
||||
"ble_profile",
|
||||
"bit_lib",
|
||||
"datetime",
|
||||
"ieee754_parse_wrap",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -380,7 +380,10 @@ bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta)
|
||||
#define CONNECTION_INTERVAL_MAX (0x24)
|
||||
|
||||
static GapConfig template_config = {
|
||||
.adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||
.adv_service = {
|
||||
.UUID_Type = UUID_TYPE_16,
|
||||
.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID,
|
||||
},
|
||||
.appearance_char = GAP_APPEARANCE_KEYBOARD,
|
||||
.bonding_mode = true,
|
||||
.pairing_method = GapPairingPinCodeVerifyYesNo,
|
||||
|
||||
+19
-19
@@ -35,7 +35,7 @@ typedef struct {
|
||||
|
||||
static bq25896_regs_t bq25896_regs;
|
||||
|
||||
bool bq25896_init(FuriHalI2cBusHandle* handle) {
|
||||
bool bq25896_init(const FuriHalI2cBusHandle* handle) {
|
||||
bool result = true;
|
||||
|
||||
bq25896_regs.r14.REG_RST = 1;
|
||||
@@ -78,19 +78,19 @@ bool bq25896_init(FuriHalI2cBusHandle* handle) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim) {
|
||||
void bq25896_set_boost_lim(const FuriHalI2cBusHandle* handle, BoostLim boost_lim) {
|
||||
bq25896_regs.r0A.BOOST_LIM = boost_lim;
|
||||
furi_hal_i2c_write_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void bq25896_poweroff(FuriHalI2cBusHandle* handle) {
|
||||
void bq25896_poweroff(const FuriHalI2cBusHandle* handle) {
|
||||
bq25896_regs.r09.BATFET_DIS = 1;
|
||||
furi_hal_i2c_write_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x09, *(uint8_t*)&bq25896_regs.r09, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle) {
|
||||
ChrgStat bq25896_get_charge_status(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_mem(
|
||||
handle,
|
||||
BQ25896_ADDRESS,
|
||||
@@ -103,52 +103,52 @@ ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle) {
|
||||
return bq25896_regs.r0B.CHRG_STAT;
|
||||
}
|
||||
|
||||
bool bq25896_is_charging(FuriHalI2cBusHandle* handle) {
|
||||
bool bq25896_is_charging(const FuriHalI2cBusHandle* handle) {
|
||||
// Include precharge, fast charging, and charging termination done as "charging"
|
||||
return bq25896_get_charge_status(handle) != ChrgStatNo;
|
||||
}
|
||||
|
||||
bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle) {
|
||||
bool bq25896_is_charging_done(const FuriHalI2cBusHandle* handle) {
|
||||
return bq25896_get_charge_status(handle) == ChrgStatDone;
|
||||
}
|
||||
|
||||
void bq25896_enable_charging(FuriHalI2cBusHandle* handle) {
|
||||
void bq25896_enable_charging(const FuriHalI2cBusHandle* handle) {
|
||||
bq25896_regs.r03.CHG_CONFIG = 1;
|
||||
furi_hal_i2c_write_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void bq25896_disable_charging(FuriHalI2cBusHandle* handle) {
|
||||
void bq25896_disable_charging(const FuriHalI2cBusHandle* handle) {
|
||||
bq25896_regs.r03.CHG_CONFIG = 0;
|
||||
furi_hal_i2c_write_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void bq25896_enable_otg(FuriHalI2cBusHandle* handle) {
|
||||
void bq25896_enable_otg(const FuriHalI2cBusHandle* handle) {
|
||||
bq25896_regs.r03.OTG_CONFIG = 1;
|
||||
furi_hal_i2c_write_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void bq25896_disable_otg(FuriHalI2cBusHandle* handle) {
|
||||
void bq25896_disable_otg(const FuriHalI2cBusHandle* handle) {
|
||||
bq25896_regs.r03.OTG_CONFIG = 0;
|
||||
furi_hal_i2c_write_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x03, *(uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) {
|
||||
bool bq25896_is_otg_enabled(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x03, (uint8_t*)&bq25896_regs.r03, BQ25896_I2C_TIMEOUT);
|
||||
return bq25896_regs.r03.OTG_CONFIG;
|
||||
}
|
||||
|
||||
uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq25896_get_vreg_voltage(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x06, (uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);
|
||||
return (uint16_t)bq25896_regs.r06.VREG * 16 + 3840;
|
||||
}
|
||||
|
||||
void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) {
|
||||
void bq25896_set_vreg_voltage(const FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) {
|
||||
if(vreg_voltage < 3840) {
|
||||
// Minimum valid value is 3840 mV
|
||||
vreg_voltage = 3840;
|
||||
@@ -166,13 +166,13 @@ void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage
|
||||
handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle) {
|
||||
bool bq25896_check_otg_fault(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT);
|
||||
return bq25896_regs.r0C.BOOST_FAULT;
|
||||
}
|
||||
|
||||
uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq25896_get_vbus_voltage(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x11, (uint8_t*)&bq25896_regs.r11, BQ25896_I2C_TIMEOUT);
|
||||
if(bq25896_regs.r11.VBUS_GD) {
|
||||
@@ -182,25 +182,25 @@ uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle) {
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq25896_get_vsys_voltage(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x0F, (uint8_t*)&bq25896_regs.r0F, BQ25896_I2C_TIMEOUT);
|
||||
return (uint16_t)bq25896_regs.r0F.SYSV * 20 + 2304;
|
||||
}
|
||||
|
||||
uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq25896_get_vbat_voltage(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x0E, (uint8_t*)&bq25896_regs.r0E, BQ25896_I2C_TIMEOUT);
|
||||
return (uint16_t)bq25896_regs.r0E.BATV * 20 + 2304;
|
||||
}
|
||||
|
||||
uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq25896_get_vbat_current(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x12, (uint8_t*)&bq25896_regs.r12, BQ25896_I2C_TIMEOUT);
|
||||
return (uint16_t)bq25896_regs.r12.ICHGR * 50;
|
||||
}
|
||||
|
||||
uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle) {
|
||||
uint32_t bq25896_get_ntc_mpct(const FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_read_reg_8(
|
||||
handle, BQ25896_ADDRESS, 0x10, (uint8_t*)&bq25896_regs.r10, BQ25896_I2C_TIMEOUT);
|
||||
return (uint32_t)bq25896_regs.r10.TSPCT * 465 + 21000;
|
||||
|
||||
+19
-19
@@ -7,61 +7,61 @@
|
||||
#include <furi_hal_i2c.h>
|
||||
|
||||
/** Initialize Driver */
|
||||
bool bq25896_init(FuriHalI2cBusHandle* handle);
|
||||
bool bq25896_init(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Set boost lim*/
|
||||
void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim);
|
||||
void bq25896_set_boost_lim(const FuriHalI2cBusHandle* handle, BoostLim boost_lim);
|
||||
|
||||
/** Send device into shipping mode */
|
||||
void bq25896_poweroff(FuriHalI2cBusHandle* handle);
|
||||
void bq25896_poweroff(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get charging status */
|
||||
ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle);
|
||||
ChrgStat bq25896_get_charge_status(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Is currently charging */
|
||||
bool bq25896_is_charging(FuriHalI2cBusHandle* handle);
|
||||
bool bq25896_is_charging(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Is charging completed while connected to charger */
|
||||
bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle);
|
||||
bool bq25896_is_charging_done(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Enable charging */
|
||||
void bq25896_enable_charging(FuriHalI2cBusHandle* handle);
|
||||
void bq25896_enable_charging(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Disable charging */
|
||||
void bq25896_disable_charging(FuriHalI2cBusHandle* handle);
|
||||
void bq25896_disable_charging(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Enable otg */
|
||||
void bq25896_enable_otg(FuriHalI2cBusHandle* handle);
|
||||
void bq25896_enable_otg(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Disable otg */
|
||||
void bq25896_disable_otg(FuriHalI2cBusHandle* handle);
|
||||
void bq25896_disable_otg(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Is otg enabled */
|
||||
bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle);
|
||||
bool bq25896_is_otg_enabled(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get VREG (charging limit) voltage in mV */
|
||||
uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq25896_get_vreg_voltage(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Set VREG (charging limit) voltage in mV
|
||||
*
|
||||
* Valid range: 3840mV - 4208mV, in steps of 16mV
|
||||
*/
|
||||
void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage);
|
||||
void bq25896_set_vreg_voltage(const FuriHalI2cBusHandle* handle, uint16_t vreg_voltage);
|
||||
|
||||
/** Check OTG BOOST Fault status */
|
||||
bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle);
|
||||
bool bq25896_check_otg_fault(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get VBUS Voltage in mV */
|
||||
uint16_t bq25896_get_vbus_voltage(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq25896_get_vbus_voltage(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get VSYS Voltage in mV */
|
||||
uint16_t bq25896_get_vsys_voltage(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq25896_get_vsys_voltage(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get VBAT Voltage in mV */
|
||||
uint16_t bq25896_get_vbat_voltage(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq25896_get_vbat_voltage(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get VBAT current in mA */
|
||||
uint16_t bq25896_get_vbat_current(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq25896_get_vbat_current(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get NTC voltage in mpct of REGN */
|
||||
uint32_t bq25896_get_ntc_mpct(FuriHalI2cBusHandle* handle);
|
||||
uint32_t bq25896_get_ntc_mpct(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
+29
-23
@@ -43,7 +43,7 @@
|
||||
#endif
|
||||
|
||||
static inline bool bq27220_read_reg(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
uint8_t address,
|
||||
uint8_t* buffer,
|
||||
size_t buffer_size) {
|
||||
@@ -52,7 +52,7 @@ static inline bool bq27220_read_reg(
|
||||
}
|
||||
|
||||
static inline bool bq27220_write(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
uint8_t address,
|
||||
const uint8_t* buffer,
|
||||
size_t buffer_size) {
|
||||
@@ -60,11 +60,11 @@ static inline bool bq27220_write(
|
||||
handle, BQ27220_ADDRESS, address, buffer, buffer_size, BQ27220_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) {
|
||||
static inline bool bq27220_control(const FuriHalI2cBusHandle* handle, uint16_t control) {
|
||||
return bq27220_write(handle, CommandControl, (uint8_t*)&control, 2);
|
||||
}
|
||||
|
||||
static uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) {
|
||||
static uint16_t bq27220_read_word(const FuriHalI2cBusHandle* handle, uint8_t address) {
|
||||
uint16_t buf = BQ27220_ERROR;
|
||||
|
||||
if(!bq27220_read_reg(handle, address, (uint8_t*)&buf, 2)) {
|
||||
@@ -83,7 +83,7 @@ static uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) {
|
||||
}
|
||||
|
||||
static bool bq27220_parameter_check(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
uint16_t address,
|
||||
uint32_t value,
|
||||
size_t size,
|
||||
@@ -163,7 +163,7 @@ static bool bq27220_parameter_check(
|
||||
}
|
||||
|
||||
static bool bq27220_data_memory_check(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
const BQ27220DMData* data_memory,
|
||||
bool update) {
|
||||
if(update) {
|
||||
@@ -268,7 +268,7 @@ static bool bq27220_data_memory_check(
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) {
|
||||
bool bq27220_init(const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) {
|
||||
bool result = false;
|
||||
bool reset_and_provisioning_required = false;
|
||||
|
||||
@@ -365,7 +365,7 @@ bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bq27220_reset(FuriHalI2cBusHandle* handle) {
|
||||
bool bq27220_reset(const FuriHalI2cBusHandle* handle) {
|
||||
bool result = false;
|
||||
do {
|
||||
if(!bq27220_control(handle, Control_RESET)) {
|
||||
@@ -396,7 +396,7 @@ bool bq27220_reset(FuriHalI2cBusHandle* handle) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bq27220_seal(FuriHalI2cBusHandle* handle) {
|
||||
bool bq27220_seal(const FuriHalI2cBusHandle* handle) {
|
||||
Bq27220OperationStatus operation_status = {0};
|
||||
bool result = false;
|
||||
do {
|
||||
@@ -431,7 +431,7 @@ bool bq27220_seal(FuriHalI2cBusHandle* handle) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bq27220_unseal(FuriHalI2cBusHandle* handle) {
|
||||
bool bq27220_unseal(const FuriHalI2cBusHandle* handle) {
|
||||
Bq27220OperationStatus operation_status = {0};
|
||||
bool result = false;
|
||||
do {
|
||||
@@ -465,7 +465,7 @@ bool bq27220_unseal(FuriHalI2cBusHandle* handle) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bq27220_full_access(FuriHalI2cBusHandle* handle) {
|
||||
bool bq27220_full_access(const FuriHalI2cBusHandle* handle) {
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
@@ -518,29 +518,35 @@ bool bq27220_full_access(FuriHalI2cBusHandle* handle) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_voltage(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandVoltage);
|
||||
}
|
||||
|
||||
int16_t bq27220_get_current(FuriHalI2cBusHandle* handle) {
|
||||
int16_t bq27220_get_current(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandCurrent);
|
||||
}
|
||||
|
||||
bool bq27220_get_control_status(FuriHalI2cBusHandle* handle, Bq27220ControlStatus* control_status) {
|
||||
bool bq27220_get_control_status(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220ControlStatus* control_status) {
|
||||
return bq27220_read_reg(handle, CommandControl, (uint8_t*)control_status, 2);
|
||||
}
|
||||
|
||||
bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, Bq27220BatteryStatus* battery_status) {
|
||||
bool bq27220_get_battery_status(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220BatteryStatus* battery_status) {
|
||||
return bq27220_read_reg(handle, CommandBatteryStatus, (uint8_t*)battery_status, 2);
|
||||
}
|
||||
|
||||
bool bq27220_get_operation_status(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220OperationStatus* operation_status) {
|
||||
return bq27220_read_reg(handle, CommandOperationStatus, (uint8_t*)operation_status, 2);
|
||||
}
|
||||
|
||||
bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatus* gauging_status) {
|
||||
bool bq27220_get_gauging_status(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220GaugingStatus* gauging_status) {
|
||||
// Request gauging data to be loaded to MAC
|
||||
if(!bq27220_control(handle, Control_GAUGING_STATUS)) {
|
||||
FURI_LOG_E(TAG, "DM SelectSubclass for read failed");
|
||||
@@ -552,26 +558,26 @@ bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatu
|
||||
return bq27220_read_reg(handle, CommandMACData, (uint8_t*)gauging_status, 2);
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_temperature(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandTemperature);
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_full_charge_capacity(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandFullChargeCapacity);
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_design_capacity(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandDesignCapacity);
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_remaining_capacity(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandRemainingCapacity);
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_state_of_charge(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandStateOfCharge);
|
||||
}
|
||||
|
||||
uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle) {
|
||||
uint16_t bq27220_get_state_of_health(const FuriHalI2cBusHandle* handle) {
|
||||
return bq27220_read_word(handle, CommandStateOfHealth);
|
||||
}
|
||||
|
||||
+23
-17
@@ -136,7 +136,7 @@ typedef struct BQ27220DMData BQ27220DMData;
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory);
|
||||
bool bq27220_init(const FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory);
|
||||
|
||||
/** Reset gauge
|
||||
*
|
||||
@@ -144,7 +144,7 @@ bool bq27220_init(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory)
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_reset(FuriHalI2cBusHandle* handle);
|
||||
bool bq27220_reset(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Seal gauge access
|
||||
*
|
||||
@@ -152,7 +152,7 @@ bool bq27220_reset(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_seal(FuriHalI2cBusHandle* handle);
|
||||
bool bq27220_seal(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Unseal gauge access
|
||||
*
|
||||
@@ -160,7 +160,7 @@ bool bq27220_seal(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_unseal(FuriHalI2cBusHandle* handle);
|
||||
bool bq27220_unseal(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get full access
|
||||
*
|
||||
@@ -170,7 +170,7 @@ bool bq27220_unseal(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_full_access(FuriHalI2cBusHandle* handle);
|
||||
bool bq27220_full_access(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get battery voltage
|
||||
*
|
||||
@@ -178,7 +178,7 @@ bool bq27220_full_access(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return voltage in mV or BQ27220_ERROR
|
||||
*/
|
||||
uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_voltage(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get current
|
||||
*
|
||||
@@ -186,7 +186,7 @@ uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return current in mA or BQ27220_ERROR
|
||||
*/
|
||||
int16_t bq27220_get_current(FuriHalI2cBusHandle* handle);
|
||||
int16_t bq27220_get_current(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get control status
|
||||
*
|
||||
@@ -195,7 +195,9 @@ int16_t bq27220_get_current(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_get_control_status(FuriHalI2cBusHandle* handle, Bq27220ControlStatus* control_status);
|
||||
bool bq27220_get_control_status(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220ControlStatus* control_status);
|
||||
|
||||
/** Get battery status
|
||||
*
|
||||
@@ -204,7 +206,9 @@ bool bq27220_get_control_status(FuriHalI2cBusHandle* handle, Bq27220ControlStatu
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, Bq27220BatteryStatus* battery_status);
|
||||
bool bq27220_get_battery_status(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220BatteryStatus* battery_status);
|
||||
|
||||
/** Get operation status
|
||||
*
|
||||
@@ -214,7 +218,7 @@ bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, Bq27220BatteryStatu
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_get_operation_status(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220OperationStatus* operation_status);
|
||||
|
||||
/** Get gauging status
|
||||
@@ -224,7 +228,9 @@ bool bq27220_get_operation_status(
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatus* gauging_status);
|
||||
bool bq27220_get_gauging_status(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
Bq27220GaugingStatus* gauging_status);
|
||||
|
||||
/** Get temperature
|
||||
*
|
||||
@@ -232,7 +238,7 @@ bool bq27220_get_gauging_status(FuriHalI2cBusHandle* handle, Bq27220GaugingStatu
|
||||
*
|
||||
* @return temperature in units of 0.1°K
|
||||
*/
|
||||
uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_temperature(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get compensated full charge capacity
|
||||
*
|
||||
@@ -240,7 +246,7 @@ uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return full charge capacity in mAh or BQ27220_ERROR
|
||||
*/
|
||||
uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_full_charge_capacity(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get design capacity
|
||||
*
|
||||
@@ -248,7 +254,7 @@ uint16_t bq27220_get_full_charge_capacity(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return design capacity in mAh or BQ27220_ERROR
|
||||
*/
|
||||
uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_design_capacity(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get remaining capacity
|
||||
*
|
||||
@@ -256,7 +262,7 @@ uint16_t bq27220_get_design_capacity(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return remaining capacity in mAh or BQ27220_ERROR
|
||||
*/
|
||||
uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_remaining_capacity(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get predicted remaining battery capacity
|
||||
*
|
||||
@@ -264,7 +270,7 @@ uint16_t bq27220_get_remaining_capacity(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return state of charge in percents or BQ27220_ERROR
|
||||
*/
|
||||
uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_state_of_charge(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Get ratio of full charge capacity over design capacity
|
||||
*
|
||||
@@ -272,4 +278,4 @@ uint16_t bq27220_get_state_of_charge(FuriHalI2cBusHandle* handle);
|
||||
*
|
||||
* @return state of health in percents or BQ27220_ERROR
|
||||
*/
|
||||
uint16_t bq27220_get_state_of_health(FuriHalI2cBusHandle* handle);
|
||||
uint16_t bq27220_get_state_of_health(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
+26
-22
@@ -3,7 +3,8 @@
|
||||
#include <string.h>
|
||||
#include <furi_hal_cortex.h>
|
||||
|
||||
static bool cc1101_spi_trx(FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) {
|
||||
static bool
|
||||
cc1101_spi_trx(const FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CC1101_TIMEOUT * 1000);
|
||||
|
||||
while(furi_hal_gpio_read(handle->miso)) {
|
||||
@@ -16,7 +17,7 @@ static bool cc1101_spi_trx(FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx
|
||||
return true;
|
||||
}
|
||||
|
||||
CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) {
|
||||
CC1101Status cc1101_strobe(const FuriHalSpiBusHandle* handle, uint8_t strobe) {
|
||||
uint8_t tx[1] = {strobe};
|
||||
CC1101Status rx[1] = {0};
|
||||
rx[0].CHIP_RDYn = 1;
|
||||
@@ -27,7 +28,7 @@ CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) {
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) {
|
||||
CC1101Status cc1101_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) {
|
||||
uint8_t tx[2] = {reg, data};
|
||||
CC1101Status rx[2] = {0};
|
||||
rx[0].CHIP_RDYn = 1;
|
||||
@@ -39,7 +40,7 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
return rx[1];
|
||||
}
|
||||
|
||||
CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) {
|
||||
CC1101Status cc1101_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data) {
|
||||
assert(sizeof(CC1101Status) == 1);
|
||||
uint8_t tx[2] = {reg | CC1101_READ, 0};
|
||||
CC1101Status rx[2] = {0};
|
||||
@@ -52,33 +53,36 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t*
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
uint8_t cc1101_get_partnumber(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t cc1101_get_partnumber(const FuriHalSpiBusHandle* handle) {
|
||||
uint8_t partnumber = 0;
|
||||
cc1101_read_reg(handle, CC1101_STATUS_PARTNUM | CC1101_BURST, &partnumber);
|
||||
return partnumber;
|
||||
}
|
||||
|
||||
uint8_t cc1101_get_version(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t cc1101_get_version(const FuriHalSpiBusHandle* handle) {
|
||||
uint8_t version = 0;
|
||||
cc1101_read_reg(handle, CC1101_STATUS_VERSION | CC1101_BURST, &version);
|
||||
return version;
|
||||
}
|
||||
|
||||
uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t cc1101_get_rssi(const FuriHalSpiBusHandle* handle) {
|
||||
uint8_t rssi = 0;
|
||||
cc1101_read_reg(handle, CC1101_STATUS_RSSI | CC1101_BURST, &rssi);
|
||||
return rssi;
|
||||
}
|
||||
|
||||
CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_reset(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SRES);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_get_status(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SNOP);
|
||||
}
|
||||
|
||||
bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) {
|
||||
bool cc1101_wait_status_state(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
CC1101State state,
|
||||
uint32_t timeout_us) {
|
||||
bool result = false;
|
||||
CC1101Status status = {0};
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us);
|
||||
@@ -92,35 +96,35 @@ bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, ui
|
||||
return result;
|
||||
}
|
||||
|
||||
CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_shutdown(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SPWD);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_calibrate(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SCAL);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_switch_to_idle(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SIDLE);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_switch_to_rx(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SRX);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_switch_to_tx(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_STX);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_flush_rx(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SFRX);
|
||||
}
|
||||
|
||||
CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle) {
|
||||
CC1101Status cc1101_flush_tx(const FuriHalSpiBusHandle* handle) {
|
||||
return cc1101_strobe(handle, CC1101_STROBE_SFTX);
|
||||
}
|
||||
|
||||
uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value) {
|
||||
uint32_t cc1101_set_frequency(const FuriHalSpiBusHandle* handle, uint32_t value) {
|
||||
uint64_t real_value = (uint64_t)value * CC1101_FDIV / CC1101_QUARTZ;
|
||||
|
||||
// Sanity check
|
||||
@@ -135,7 +139,7 @@ uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value) {
|
||||
return (uint32_t)real_frequency;
|
||||
}
|
||||
|
||||
uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t value) {
|
||||
uint32_t cc1101_set_intermediate_frequency(const FuriHalSpiBusHandle* handle, uint32_t value) {
|
||||
uint64_t real_value = value * CC1101_IFDIV / CC1101_QUARTZ;
|
||||
assert((real_value & 0xFF) == real_value);
|
||||
|
||||
@@ -146,7 +150,7 @@ uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t
|
||||
return (uint32_t)real_frequency;
|
||||
}
|
||||
|
||||
void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) {
|
||||
void cc1101_set_pa_table(const FuriHalSpiBusHandle* handle, const uint8_t value[8]) {
|
||||
uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; //-V1009
|
||||
CC1101Status rx[9] = {0};
|
||||
rx[0].CHIP_RDYn = 1;
|
||||
@@ -159,7 +163,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) {
|
||||
assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0);
|
||||
}
|
||||
|
||||
uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) {
|
||||
uint8_t cc1101_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size) {
|
||||
uint8_t buff_tx[64];
|
||||
uint8_t buff_rx[64];
|
||||
buff_tx[0] = CC1101_FIFO | CC1101_BURST;
|
||||
@@ -170,7 +174,7 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint
|
||||
return size;
|
||||
}
|
||||
|
||||
uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) {
|
||||
uint8_t cc1101_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) {
|
||||
uint8_t buff_trx[2];
|
||||
buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST;
|
||||
|
||||
|
||||
+24
-21
@@ -19,7 +19,7 @@ extern "C" {
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe);
|
||||
CC1101Status cc1101_strobe(const FuriHalSpiBusHandle* handle, uint8_t strobe);
|
||||
|
||||
/** Write device register
|
||||
*
|
||||
@@ -29,7 +29,7 @@ CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe);
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data);
|
||||
CC1101Status cc1101_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data);
|
||||
|
||||
/** Read device register
|
||||
*
|
||||
@@ -39,7 +39,7 @@ CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data);
|
||||
CC1101Status cc1101_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data);
|
||||
|
||||
/* High level API */
|
||||
|
||||
@@ -49,7 +49,7 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t*
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_reset(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Get status
|
||||
*
|
||||
@@ -57,7 +57,7 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_get_status(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Wait specific chip state
|
||||
*
|
||||
@@ -67,7 +67,10 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return true on success, false otherwise
|
||||
*/
|
||||
bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us);
|
||||
bool cc1101_wait_status_state(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
CC1101State state,
|
||||
uint32_t timeout_us);
|
||||
|
||||
/** Enable shutdown mode
|
||||
*
|
||||
@@ -75,7 +78,7 @@ bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, ui
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_shutdown(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Get Partnumber
|
||||
*
|
||||
@@ -83,7 +86,7 @@ CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return part number id
|
||||
*/
|
||||
uint8_t cc1101_get_partnumber(FuriHalSpiBusHandle* handle);
|
||||
uint8_t cc1101_get_partnumber(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Get Version
|
||||
*
|
||||
@@ -91,7 +94,7 @@ uint8_t cc1101_get_partnumber(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return version
|
||||
*/
|
||||
uint8_t cc1101_get_version(FuriHalSpiBusHandle* handle);
|
||||
uint8_t cc1101_get_version(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Get raw RSSI value
|
||||
*
|
||||
@@ -99,7 +102,7 @@ uint8_t cc1101_get_version(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return rssi value
|
||||
*/
|
||||
uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle);
|
||||
uint8_t cc1101_get_rssi(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Calibrate oscillator
|
||||
*
|
||||
@@ -107,13 +110,13 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_calibrate(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Switch to idle
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*/
|
||||
CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_switch_to_idle(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Switch to RX
|
||||
*
|
||||
@@ -121,7 +124,7 @@ CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_switch_to_rx(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Switch to TX
|
||||
*
|
||||
@@ -129,7 +132,7 @@ CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_switch_to_tx(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Flush RX FIFO
|
||||
*
|
||||
@@ -137,13 +140,13 @@ CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return CC1101Status structure
|
||||
*/
|
||||
CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_flush_rx(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Flush TX FIFO
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*/
|
||||
CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle);
|
||||
CC1101Status cc1101_flush_tx(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Set Frequency
|
||||
*
|
||||
@@ -152,7 +155,7 @@ CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle);
|
||||
*
|
||||
* @return real frequency that were synthesized
|
||||
*/
|
||||
uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value);
|
||||
uint32_t cc1101_set_frequency(const FuriHalSpiBusHandle* handle, uint32_t value);
|
||||
|
||||
/** Set Intermediate Frequency
|
||||
*
|
||||
@@ -161,14 +164,14 @@ uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value);
|
||||
*
|
||||
* @return real inermediate frequency that were synthesized
|
||||
*/
|
||||
uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t value);
|
||||
uint32_t cc1101_set_intermediate_frequency(const FuriHalSpiBusHandle* handle, uint32_t value);
|
||||
|
||||
/** Set Power Amplifier level table, ramp
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param value - array of power level values
|
||||
*/
|
||||
void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]);
|
||||
void cc1101_set_pa_table(const FuriHalSpiBusHandle* handle, const uint8_t value[8]);
|
||||
|
||||
/** Write FIFO
|
||||
*
|
||||
@@ -178,7 +181,7 @@ void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]);
|
||||
*
|
||||
* @return size, written bytes count
|
||||
*/
|
||||
uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size);
|
||||
uint8_t cc1101_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* data, uint8_t size);
|
||||
|
||||
/** Read FIFO
|
||||
*
|
||||
@@ -188,7 +191,7 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint
|
||||
*
|
||||
* @return size, read bytes count
|
||||
*/
|
||||
uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size);
|
||||
uint8_t cc1101_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+20
-11
@@ -3,12 +3,12 @@
|
||||
#include "lp5562_reg.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
void lp5562_reset(FuriHalI2cBusHandle* handle) {
|
||||
void lp5562_reset(const FuriHalI2cBusHandle* handle) {
|
||||
Reg0D_Reset reg = {.value = 0xFF};
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x0D, *(uint8_t*)®, LP5562_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void lp5562_configure(FuriHalI2cBusHandle* handle) {
|
||||
void lp5562_configure(const FuriHalI2cBusHandle* handle) {
|
||||
Reg08_Config config = {.INT_CLK_EN = true, .PS_EN = true, .PWM_HF = true};
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x08, *(uint8_t*)&config, LP5562_I2C_TIMEOUT);
|
||||
|
||||
@@ -21,14 +21,17 @@ void lp5562_configure(FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, *(uint8_t*)&map, LP5562_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void lp5562_enable(FuriHalI2cBusHandle* handle) {
|
||||
void lp5562_enable(const FuriHalI2cBusHandle* handle) {
|
||||
Reg00_Enable reg = {.CHIP_EN = true, .LOG_EN = true};
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, *(uint8_t*)®, LP5562_I2C_TIMEOUT);
|
||||
//>488μs delay is required after writing to 0x00 register, otherwise program engine will not work
|
||||
furi_delay_us(500);
|
||||
}
|
||||
|
||||
void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
|
||||
void lp5562_set_channel_current(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Channel channel,
|
||||
uint8_t value) {
|
||||
uint8_t reg_no;
|
||||
if(channel == LP5562ChannelRed) {
|
||||
reg_no = LP5562_CHANNEL_RED_CURRENT_REGISTER;
|
||||
@@ -44,7 +47,10 @@ void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel chann
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value) {
|
||||
void lp5562_set_channel_value(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Channel channel,
|
||||
uint8_t value) {
|
||||
uint8_t reg_no;
|
||||
if(channel == LP5562ChannelRed) {
|
||||
reg_no = LP5562_CHANNEL_RED_VALUE_REGISTER;
|
||||
@@ -60,7 +66,7 @@ void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, reg_no, value, LP5562_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel) {
|
||||
uint8_t lp5562_get_channel_value(const FuriHalI2cBusHandle* handle, LP5562Channel channel) {
|
||||
uint8_t reg_no;
|
||||
uint8_t value;
|
||||
if(channel == LP5562ChannelRed) {
|
||||
@@ -78,7 +84,10 @@ uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel chan
|
||||
return value;
|
||||
}
|
||||
|
||||
void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src) {
|
||||
void lp5562_set_channel_src(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Channel channel,
|
||||
LP5562Engine src) {
|
||||
uint8_t reg_val = 0;
|
||||
uint8_t bit_offset = 0;
|
||||
|
||||
@@ -107,7 +116,7 @@ void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel,
|
||||
}
|
||||
|
||||
void lp5562_execute_program(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Engine eng,
|
||||
LP5562Channel ch,
|
||||
uint16_t* program) {
|
||||
@@ -155,7 +164,7 @@ void lp5562_execute_program(
|
||||
furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x00, enable_reg, LP5562_I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) {
|
||||
void lp5562_stop_program(const FuriHalI2cBusHandle* handle, LP5562Engine eng) {
|
||||
if((eng < LP5562Engine1) || (eng > LP5562Engine3)) return;
|
||||
uint8_t reg_val = 0;
|
||||
uint8_t bit_offset = 0;
|
||||
@@ -169,7 +178,7 @@ void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) {
|
||||
}
|
||||
|
||||
void lp5562_execute_ramp(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Engine eng,
|
||||
LP5562Channel ch,
|
||||
uint8_t val_start,
|
||||
@@ -213,7 +222,7 @@ void lp5562_execute_ramp(
|
||||
}
|
||||
|
||||
void lp5562_execute_blink(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Engine eng,
|
||||
LP5562Channel ch,
|
||||
uint16_t on_time,
|
||||
|
||||
+20
-11
@@ -20,39 +20,48 @@ typedef enum {
|
||||
} LP5562Engine;
|
||||
|
||||
/** Initialize Driver */
|
||||
void lp5562_reset(FuriHalI2cBusHandle* handle);
|
||||
void lp5562_reset(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Configure Driver */
|
||||
void lp5562_configure(FuriHalI2cBusHandle* handle);
|
||||
void lp5562_configure(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Enable Driver */
|
||||
void lp5562_enable(FuriHalI2cBusHandle* handle);
|
||||
void lp5562_enable(const FuriHalI2cBusHandle* handle);
|
||||
|
||||
/** Set channel current */
|
||||
void lp5562_set_channel_current(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value);
|
||||
void lp5562_set_channel_current(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Channel channel,
|
||||
uint8_t value);
|
||||
|
||||
/** Set channel PWM value */
|
||||
void lp5562_set_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel, uint8_t value);
|
||||
void lp5562_set_channel_value(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Channel channel,
|
||||
uint8_t value);
|
||||
|
||||
/** Get channel PWM value */
|
||||
uint8_t lp5562_get_channel_value(FuriHalI2cBusHandle* handle, LP5562Channel channel);
|
||||
uint8_t lp5562_get_channel_value(const FuriHalI2cBusHandle* handle, LP5562Channel channel);
|
||||
|
||||
/** Set channel source */
|
||||
void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, LP5562Engine src);
|
||||
void lp5562_set_channel_src(
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Channel channel,
|
||||
LP5562Engine src);
|
||||
|
||||
/** Execute program sequence */
|
||||
void lp5562_execute_program(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Engine eng,
|
||||
LP5562Channel ch,
|
||||
uint16_t* program);
|
||||
|
||||
/** Stop program sequence */
|
||||
void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng);
|
||||
void lp5562_stop_program(const FuriHalI2cBusHandle* handle, LP5562Engine eng);
|
||||
|
||||
/** Execute ramp program sequence */
|
||||
void lp5562_execute_ramp(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Engine eng,
|
||||
LP5562Channel ch,
|
||||
uint8_t val_start,
|
||||
@@ -61,7 +70,7 @@ void lp5562_execute_ramp(
|
||||
|
||||
/** Start blink program sequence */
|
||||
void lp5562_execute_blink(
|
||||
FuriHalI2cBusHandle* handle,
|
||||
const FuriHalI2cBusHandle* handle,
|
||||
LP5562Engine eng,
|
||||
LP5562Channel ch,
|
||||
uint16_t on_time,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask) {
|
||||
void st25r3916_mask_irq(const FuriHalSpiBusHandle* handle, uint32_t mask) {
|
||||
furi_assert(handle);
|
||||
|
||||
uint8_t irq_mask_regs[4] = {
|
||||
@@ -14,7 +14,7 @@ void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask) {
|
||||
st25r3916_write_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_mask_regs, 4);
|
||||
}
|
||||
|
||||
uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle) {
|
||||
uint32_t st25r3916_get_irq(const FuriHalSpiBusHandle* handle) {
|
||||
furi_assert(handle);
|
||||
|
||||
uint8_t irq_regs[4] = {};
|
||||
@@ -32,7 +32,7 @@ uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle) {
|
||||
return irq;
|
||||
}
|
||||
|
||||
void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) {
|
||||
void st25r3916_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) {
|
||||
furi_assert(handle);
|
||||
furi_assert(buff);
|
||||
|
||||
@@ -45,7 +45,7 @@ void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size
|
||||
}
|
||||
|
||||
bool st25r3916_read_fifo(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t* buff,
|
||||
size_t buff_size,
|
||||
size_t* buff_bits) {
|
||||
|
||||
@@ -75,7 +75,7 @@ extern "C" {
|
||||
* @param handle - pointer to FuriHalSpiBusHandle instance
|
||||
* @param mask - mask of interrupts to be disabled
|
||||
*/
|
||||
void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask);
|
||||
void st25r3916_mask_irq(const FuriHalSpiBusHandle* handle, uint32_t mask);
|
||||
|
||||
/** Get st25r3916 interrupts
|
||||
*
|
||||
@@ -83,7 +83,7 @@ void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask);
|
||||
*
|
||||
* @return received interrupts
|
||||
*/
|
||||
uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle);
|
||||
uint32_t st25r3916_get_irq(const FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Write FIFO
|
||||
*
|
||||
@@ -91,7 +91,7 @@ uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle);
|
||||
* @param buff - buffer to write to FIFO
|
||||
* @param bits - number of bits to write
|
||||
*/
|
||||
void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits);
|
||||
void st25r3916_write_fifo(const FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits);
|
||||
|
||||
/** Read FIFO
|
||||
*
|
||||
@@ -103,7 +103,7 @@ void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size
|
||||
* @return true if read success, false otherwise
|
||||
*/
|
||||
bool st25r3916_read_fifo(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t* buff,
|
||||
size_t buff_size,
|
||||
size_t* buff_bits);
|
||||
|
||||
+29
-20
@@ -28,18 +28,18 @@
|
||||
(ST25R3916_CMD_LEN + \
|
||||
ST25R3916_FIFO_DEPTH) /*!< ST25R3916 communication buffer: CMD + FIFO length */
|
||||
|
||||
static void st25r3916_reg_tx_byte(FuriHalSpiBusHandle* handle, uint8_t byte) {
|
||||
static void st25r3916_reg_tx_byte(const FuriHalSpiBusHandle* handle, uint8_t byte) {
|
||||
uint8_t val = byte;
|
||||
furi_hal_spi_bus_tx(handle, &val, 1, 5);
|
||||
}
|
||||
|
||||
void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) {
|
||||
void st25r3916_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) {
|
||||
furi_check(handle);
|
||||
st25r3916_read_burst_regs(handle, reg, val, 1);
|
||||
}
|
||||
|
||||
void st25r3916_read_burst_regs(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg_start,
|
||||
uint8_t* values,
|
||||
uint8_t length) {
|
||||
@@ -59,14 +59,14 @@ void st25r3916_read_burst_regs(
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) {
|
||||
void st25r3916_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) {
|
||||
furi_check(handle);
|
||||
uint8_t reg_val = val;
|
||||
st25r3916_write_burst_regs(handle, reg, ®_val, 1);
|
||||
}
|
||||
|
||||
void st25r3916_write_burst_regs(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg_start,
|
||||
const uint8_t* values,
|
||||
uint8_t length) {
|
||||
@@ -86,7 +86,10 @@ void st25r3916_write_burst_regs(
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length) {
|
||||
void st25r3916_reg_write_fifo(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* buff,
|
||||
size_t length) {
|
||||
furi_check(handle);
|
||||
furi_check(buff);
|
||||
furi_check(length);
|
||||
@@ -98,7 +101,7 @@ void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff,
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {
|
||||
void st25r3916_reg_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {
|
||||
furi_check(handle);
|
||||
furi_check(buff);
|
||||
furi_check(length);
|
||||
@@ -110,7 +113,10 @@ void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) {
|
||||
void st25r3916_write_pta_mem(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* values,
|
||||
size_t length) {
|
||||
furi_check(handle);
|
||||
furi_check(values);
|
||||
furi_check(length);
|
||||
@@ -122,7 +128,7 @@ void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values,
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {
|
||||
void st25r3916_read_pta_mem(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {
|
||||
furi_check(handle);
|
||||
furi_check(buff);
|
||||
furi_check(length);
|
||||
@@ -136,7 +142,10 @@ void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t l
|
||||
memcpy(buff, tmp_buff + 1, length);
|
||||
}
|
||||
|
||||
void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) {
|
||||
void st25r3916_write_ptf_mem(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* values,
|
||||
size_t length) {
|
||||
furi_check(handle);
|
||||
furi_check(values);
|
||||
|
||||
@@ -146,7 +155,7 @@ void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values,
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {
|
||||
void st25r3916_write_pttsn_mem(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) {
|
||||
furi_check(handle);
|
||||
furi_check(buff);
|
||||
|
||||
@@ -156,7 +165,7 @@ void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd) {
|
||||
void st25r3916_direct_cmd(const FuriHalSpiBusHandle* handle, uint8_t cmd) {
|
||||
furi_check(handle);
|
||||
|
||||
furi_hal_gpio_write(handle->cs, false);
|
||||
@@ -164,7 +173,7 @@ void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd) {
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) {
|
||||
void st25r3916_read_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) {
|
||||
furi_check(handle);
|
||||
|
||||
furi_hal_gpio_write(handle->cs, false);
|
||||
@@ -174,7 +183,7 @@ void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t*
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) {
|
||||
void st25r3916_write_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) {
|
||||
furi_check(handle);
|
||||
|
||||
furi_hal_gpio_write(handle->cs, false);
|
||||
@@ -184,7 +193,7 @@ void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) {
|
||||
void st25r3916_clear_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) {
|
||||
furi_check(handle);
|
||||
|
||||
uint8_t reg_val = 0;
|
||||
@@ -195,7 +204,7 @@ void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) {
|
||||
void st25r3916_set_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) {
|
||||
furi_check(handle);
|
||||
|
||||
uint8_t reg_val = 0;
|
||||
@@ -207,7 +216,7 @@ void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t se
|
||||
}
|
||||
|
||||
void st25r3916_change_reg_bits(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t value) {
|
||||
@@ -217,7 +226,7 @@ void st25r3916_change_reg_bits(
|
||||
}
|
||||
|
||||
void st25r3916_modify_reg(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg,
|
||||
uint8_t clr_mask,
|
||||
uint8_t set_mask) {
|
||||
@@ -233,7 +242,7 @@ void st25r3916_modify_reg(
|
||||
}
|
||||
|
||||
void st25r3916_change_test_reg_bits(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t value) {
|
||||
@@ -248,7 +257,7 @@ void st25r3916_change_test_reg_bits(
|
||||
}
|
||||
}
|
||||
|
||||
bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) {
|
||||
bool st25r3916_check_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) {
|
||||
furi_check(handle);
|
||||
|
||||
uint8_t reg_val = 0;
|
||||
|
||||
+28
-19
@@ -967,7 +967,7 @@ extern "C" {
|
||||
* @param reg - register address
|
||||
* @param val - pointer to the variable to store the read value
|
||||
*/
|
||||
void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);
|
||||
void st25r3916_read_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);
|
||||
|
||||
/** Read multiple registers
|
||||
*
|
||||
@@ -977,7 +977,7 @@ void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);
|
||||
* @param length - number of registers to read
|
||||
*/
|
||||
void st25r3916_read_burst_regs(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg_start,
|
||||
uint8_t* values,
|
||||
uint8_t length);
|
||||
@@ -988,7 +988,7 @@ void st25r3916_read_burst_regs(
|
||||
* @param reg - register address
|
||||
* @param val - value to write
|
||||
*/
|
||||
void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);
|
||||
void st25r3916_write_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);
|
||||
|
||||
/** Write multiple registers
|
||||
*
|
||||
@@ -998,7 +998,7 @@ void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);
|
||||
* @param length - number of registers to write
|
||||
*/
|
||||
void st25r3916_write_burst_regs(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg_start,
|
||||
const uint8_t* values,
|
||||
uint8_t length);
|
||||
@@ -1009,7 +1009,10 @@ void st25r3916_write_burst_regs(
|
||||
* @param buff - buffer to write to FIFO
|
||||
* @param length - number of bytes to write
|
||||
*/
|
||||
void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length);
|
||||
void st25r3916_reg_write_fifo(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* buff,
|
||||
size_t length);
|
||||
|
||||
/** Read fifo register
|
||||
*
|
||||
@@ -1017,7 +1020,7 @@ void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff,
|
||||
* @param buff - buffer to store the read values
|
||||
* @param length - number of bytes to read
|
||||
*/
|
||||
void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length);
|
||||
void st25r3916_reg_read_fifo(const FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length);
|
||||
|
||||
/** Write PTA memory register
|
||||
*
|
||||
@@ -1025,7 +1028,10 @@ void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t
|
||||
* @param values - pointer to buffer to write
|
||||
* @param length - number of bytes to write
|
||||
*/
|
||||
void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length);
|
||||
void st25r3916_write_pta_mem(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* values,
|
||||
size_t length);
|
||||
|
||||
/** Read PTA memory register
|
||||
*
|
||||
@@ -1033,7 +1039,7 @@ void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values,
|
||||
* @param values - buffer to store the read values
|
||||
* @param length - number of bytes to read
|
||||
*/
|
||||
void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length);
|
||||
void st25r3916_read_pta_mem(const FuriHalSpiBusHandle* handle, uint8_t* values, size_t length);
|
||||
|
||||
/** Write PTF memory register
|
||||
*
|
||||
@@ -1041,7 +1047,10 @@ void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t
|
||||
* @param values - pointer to buffer to write
|
||||
* @param length - number of bytes to write
|
||||
*/
|
||||
void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length);
|
||||
void st25r3916_write_ptf_mem(
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
const uint8_t* values,
|
||||
size_t length);
|
||||
|
||||
/** Read PTTSN memory register
|
||||
*
|
||||
@@ -1049,21 +1058,21 @@ void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values,
|
||||
* @param values - pointer to buffer to write
|
||||
* @param length - number of bytes to write
|
||||
*/
|
||||
void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length);
|
||||
void st25r3916_write_pttsn_mem(const FuriHalSpiBusHandle* handle, uint8_t* values, size_t length);
|
||||
|
||||
/** Send Direct command
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiBusHandle instance
|
||||
* @param cmd - direct command
|
||||
*/
|
||||
void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd);
|
||||
void st25r3916_direct_cmd(const FuriHalSpiBusHandle* handle, uint8_t cmd);
|
||||
|
||||
/** Read test register
|
||||
* @param handle - pointer to FuriHalSpiBusHandle instance
|
||||
* @param reg - register address
|
||||
* @param val - pointer to the variable to store the read value
|
||||
*/
|
||||
void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);
|
||||
void st25r3916_read_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val);
|
||||
|
||||
/** Write test register
|
||||
*
|
||||
@@ -1071,7 +1080,7 @@ void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t*
|
||||
* @param reg - register address
|
||||
* @param val - value to write
|
||||
*/
|
||||
void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);
|
||||
void st25r3916_write_test_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val);
|
||||
|
||||
/** Clear register bits
|
||||
*
|
||||
@@ -1079,7 +1088,7 @@ void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
* @param reg - register address
|
||||
* @param clr_mask - bit mask to clear
|
||||
*/
|
||||
void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask);
|
||||
void st25r3916_clear_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask);
|
||||
|
||||
/** Set register bits
|
||||
*
|
||||
@@ -1087,7 +1096,7 @@ void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t
|
||||
* @param reg - register address
|
||||
* @param set_mask - bit mask to set
|
||||
*/
|
||||
void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask);
|
||||
void st25r3916_set_reg_bits(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask);
|
||||
|
||||
/** Change register bits
|
||||
*
|
||||
@@ -1097,7 +1106,7 @@ void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t se
|
||||
* @param value - new register value to write
|
||||
*/
|
||||
void st25r3916_change_reg_bits(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t value);
|
||||
@@ -1110,7 +1119,7 @@ void st25r3916_change_reg_bits(
|
||||
* @param set_mask - bit mask to set
|
||||
*/
|
||||
void st25r3916_modify_reg(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg,
|
||||
uint8_t clr_mask,
|
||||
uint8_t set_mask);
|
||||
@@ -1123,7 +1132,7 @@ void st25r3916_modify_reg(
|
||||
* @param value - new register value to write
|
||||
*/
|
||||
void st25r3916_change_test_reg_bits(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
const FuriHalSpiBusHandle* handle,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t value);
|
||||
@@ -1137,7 +1146,7 @@ void st25r3916_change_test_reg_bits(
|
||||
*
|
||||
* @return true if register value matches the expected value, false otherwise
|
||||
*/
|
||||
bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val);
|
||||
bool st25r3916_check_reg(const FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "protocol_ds1971.h"
|
||||
#include "protocol_ds_generic.h"
|
||||
|
||||
const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = {
|
||||
const iButtonProtocolDallasBase* const ibutton_protocols_dallas[] = {
|
||||
[iButtonProtocolDS1990] = &ibutton_protocol_ds1990,
|
||||
[iButtonProtocolDS1992] = &ibutton_protocol_ds1992,
|
||||
[iButtonProtocolDS1996] = &ibutton_protocol_ds1996,
|
||||
|
||||
@@ -14,4 +14,4 @@ typedef enum {
|
||||
iButtonProtocolDSMax,
|
||||
} iButtonProtocolDallas;
|
||||
|
||||
extern const iButtonProtocolDallasBase* ibutton_protocols_dallas[];
|
||||
extern const iButtonProtocolDallasBase* const ibutton_protocols_dallas[];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "protocol_cyfral.h"
|
||||
#include "protocol_metakom.h"
|
||||
|
||||
const ProtocolBase* ibutton_protocols_misc[] = {
|
||||
const ProtocolBase* const ibutton_protocols_misc[] = {
|
||||
[iButtonProtocolMiscCyfral] = &ibutton_protocol_misc_cyfral,
|
||||
[iButtonProtocolMiscMetakom] = &ibutton_protocol_misc_metakom,
|
||||
/* Add new misc protocols here */
|
||||
|
||||
@@ -8,4 +8,4 @@ typedef enum {
|
||||
iButtonProtocolMiscMax,
|
||||
} iButtonProtocolMisc;
|
||||
|
||||
extern const ProtocolBase* ibutton_protocols_misc[];
|
||||
extern const ProtocolBase* const ibutton_protocols_misc[];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "dallas/protocol_group_dallas.h"
|
||||
#include "misc/protocol_group_misc.h"
|
||||
|
||||
const iButtonProtocolGroupBase* ibutton_protocol_groups[] = {
|
||||
const iButtonProtocolGroupBase* const ibutton_protocol_groups[] = {
|
||||
[iButtonProtocolGroupDallas] = &ibutton_protocol_group_dallas,
|
||||
[iButtonProtocolGroupMisc] = &ibutton_protocol_group_misc,
|
||||
};
|
||||
|
||||
@@ -8,4 +8,4 @@ typedef enum {
|
||||
iButtonProtocolGroupMax
|
||||
} iButtonProtocolGroup;
|
||||
|
||||
extern const iButtonProtocolGroupBase* ibutton_protocol_groups[];
|
||||
extern const iButtonProtocolGroupBase* const ibutton_protocol_groups[];
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
Import("env")
|
||||
|
||||
wrapped_fn_list = [
|
||||
"strtof",
|
||||
"strtod",
|
||||
]
|
||||
|
||||
for wrapped_fn in wrapped_fn_list:
|
||||
env.Append(
|
||||
LINKFLAGS=[
|
||||
"-Wl,--wrap," + wrapped_fn,
|
||||
]
|
||||
)
|
||||
|
||||
env.Append(
|
||||
SDK_HEADERS=[
|
||||
File("wrappers.h"),
|
||||
],
|
||||
LINT_SOURCES=[
|
||||
Dir("."),
|
||||
],
|
||||
)
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="ieee754_parse_wrap")
|
||||
libenv.ApplyLibFlags()
|
||||
|
||||
sources = libenv.GlobRecursive("*.c*", ".")
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "wrappers.h"
|
||||
|
||||
// Based on the disassembly, providing NULL as `locale` is fine.
|
||||
// The default `strtof` and `strtod` provided in the same libc_nano also just
|
||||
// call these functions, but with an actual locale structure which was taking up
|
||||
// lots of .data space (364 bytes).
|
||||
|
||||
float __wrap_strtof(const char* in, char** tail) {
|
||||
return strtof_l(in, tail, NULL);
|
||||
}
|
||||
|
||||
double __wrap_strtod(const char* in, char** tail) {
|
||||
return strtod_l(in, tail, NULL);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
float __wrap_strtof(const char* in, char** tail);
|
||||
double __wrap_strtod(const char* in, char** tail);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "protocol_gproxii.h"
|
||||
#include "protocol_noralsy.h"
|
||||
|
||||
const ProtocolBase* lfrfid_protocols[] = {
|
||||
const ProtocolBase* const lfrfid_protocols[] = {
|
||||
[LFRFIDProtocolEM4100] = &protocol_em4100,
|
||||
[LFRFIDProtocolEM410032] = &protocol_em4100_32,
|
||||
[LFRFIDProtocolEM410016] = &protocol_em4100_16,
|
||||
|
||||
@@ -37,7 +37,7 @@ typedef enum {
|
||||
LFRFIDProtocolMax,
|
||||
} LFRFIDProtocol;
|
||||
|
||||
extern const ProtocolBase* lfrfid_protocols[];
|
||||
extern const ProtocolBase* const lfrfid_protocols[];
|
||||
|
||||
typedef enum {
|
||||
LFRFIDWriteTypeT5577,
|
||||
|
||||
@@ -21,7 +21,7 @@ typedef struct {
|
||||
uint8_t chk;
|
||||
} ProtocolNexwatchMagic;
|
||||
|
||||
ProtocolNexwatchMagic magic_items[] = {
|
||||
static ProtocolNexwatchMagic magic_items[] = {
|
||||
{0xBE, "Quadrakey", 0},
|
||||
{0x88, "Nexkey", 0},
|
||||
{0x86, "Honeywell", 0}};
|
||||
|
||||
@@ -47,7 +47,7 @@ static int ptest(struct pstate* p) {
|
||||
return tok;
|
||||
}
|
||||
|
||||
static int s_unary_ops[] = {
|
||||
static const int s_unary_ops[] = {
|
||||
TOK_NOT,
|
||||
TOK_TILDA,
|
||||
TOK_PLUS_PLUS,
|
||||
@@ -56,10 +56,10 @@ static int s_unary_ops[] = {
|
||||
TOK_MINUS,
|
||||
TOK_PLUS,
|
||||
TOK_EOF};
|
||||
static int s_comparison_ops[] = {TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_EOF};
|
||||
static int s_postfix_ops[] = {TOK_PLUS_PLUS, TOK_MINUS_MINUS, TOK_EOF};
|
||||
static int s_equality_ops[] = {TOK_EQ, TOK_NE, TOK_EQ_EQ, TOK_NE_NE, TOK_EOF};
|
||||
static int s_assign_ops[] = {
|
||||
static const int s_comparison_ops[] = {TOK_LT, TOK_LE, TOK_GT, TOK_GE, TOK_EOF};
|
||||
static const int s_postfix_ops[] = {TOK_PLUS_PLUS, TOK_MINUS_MINUS, TOK_EOF};
|
||||
static const int s_equality_ops[] = {TOK_EQ, TOK_NE, TOK_EQ_EQ, TOK_NE_NE, TOK_EOF};
|
||||
static const int s_assign_ops[] = {
|
||||
TOK_ASSIGN,
|
||||
TOK_PLUS_ASSIGN,
|
||||
TOK_MINUS_ASSIGN,
|
||||
@@ -74,7 +74,7 @@ static int s_assign_ops[] = {
|
||||
TOK_OR_ASSIGN,
|
||||
TOK_EOF};
|
||||
|
||||
static int findtok(int* toks, int tok) {
|
||||
static int findtok(int const* toks, int tok) {
|
||||
int i = 0;
|
||||
while(tok != toks[i] && toks[i] != TOK_EOF)
|
||||
i++;
|
||||
|
||||
+19
-6
@@ -9,6 +9,8 @@
|
||||
#include "mjs_string_public.h"
|
||||
#include "mjs_util.h"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
mjs_val_t mjs_mk_null(void) {
|
||||
return MJS_NULL;
|
||||
}
|
||||
@@ -94,7 +96,7 @@ int mjs_is_boolean(mjs_val_t v) {
|
||||
}
|
||||
|
||||
#define MJS_IS_POINTER_LEGIT(n) \
|
||||
(((n)&MJS_TAG_MASK) == 0 || ((n)&MJS_TAG_MASK) == (~0 & MJS_TAG_MASK))
|
||||
(((n) & MJS_TAG_MASK) == 0 || ((n) & MJS_TAG_MASK) == (~0 & MJS_TAG_MASK))
|
||||
|
||||
MJS_PRIVATE mjs_val_t mjs_pointer_to_value(struct mjs* mjs, void* p) {
|
||||
uint64_t n = ((uint64_t)(uintptr_t)p);
|
||||
@@ -165,13 +167,13 @@ MJS_PRIVATE void mjs_number_to_string(struct mjs* mjs) {
|
||||
mjs_val_t ret = MJS_UNDEFINED;
|
||||
mjs_val_t base_v = MJS_UNDEFINED;
|
||||
int32_t base = 10;
|
||||
int32_t num;
|
||||
double num;
|
||||
|
||||
/* get number from `this` */
|
||||
if(!mjs_check_arg(mjs, -1 /*this*/, "this", MJS_TYPE_NUMBER, NULL)) {
|
||||
goto clean;
|
||||
}
|
||||
num = mjs_get_int32(mjs, mjs->vals.this_obj);
|
||||
num = mjs_get_double(mjs, mjs->vals.this_obj);
|
||||
|
||||
if(mjs_nargs(mjs) >= 1) {
|
||||
/* get base from arg 0 */
|
||||
@@ -181,9 +183,20 @@ MJS_PRIVATE void mjs_number_to_string(struct mjs* mjs) {
|
||||
base = mjs_get_int(mjs, base_v);
|
||||
}
|
||||
|
||||
char tmp_str[] = "-2147483648";
|
||||
itoa(num, tmp_str, base);
|
||||
ret = mjs_mk_string(mjs, tmp_str, ~0, true);
|
||||
if(base != 10 || floor(num) == num) {
|
||||
char tmp_str[] = "-2147483648";
|
||||
itoa((int32_t)num, tmp_str, base);
|
||||
ret = mjs_mk_string(mjs, tmp_str, ~0, true);
|
||||
} else {
|
||||
char tmp_str[] = "2.22514337200000e-308";
|
||||
snprintf(tmp_str, sizeof(tmp_str), "%.*g", DBL_DIG, num);
|
||||
size_t len = strlen(tmp_str);
|
||||
while(len && tmp_str[len - 1] == '0') {
|
||||
len--;
|
||||
}
|
||||
tmp_str[len] = '\0';
|
||||
ret = mjs_mk_string(mjs, tmp_str, ~0, true);
|
||||
}
|
||||
|
||||
clean:
|
||||
mjs_return(mjs, ret);
|
||||
|
||||
@@ -392,37 +392,15 @@ static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfCl
|
||||
|
||||
mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]);
|
||||
|
||||
// Set every block to 0x00
|
||||
uint16_t block_num = mf_classic_get_total_block_num(type);
|
||||
if(type == MfClassicType4k) {
|
||||
// Set every block to 0x00
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0x00, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
}
|
||||
} else if(type == MfClassicType1k) {
|
||||
// Set every block to 0x00
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0x00, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
}
|
||||
} else if(type == MfClassicTypeMini) {
|
||||
// Set every block to 0x00
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0x00, 16);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
for(uint16_t i = 1; i < block_num; i++) {
|
||||
if(mf_classic_is_sector_trailer(i)) {
|
||||
nfc_generate_mf_classic_sector_trailer(mfc_data, i);
|
||||
} else {
|
||||
memset(&mfc_data->block[i].data, 0x00, MF_CLASSIC_BLOCK_SIZE);
|
||||
}
|
||||
mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]);
|
||||
}
|
||||
|
||||
nfc_generate_mf_classic_block_0(
|
||||
|
||||
@@ -173,33 +173,35 @@ bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version)
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) &&
|
||||
flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) {
|
||||
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY;
|
||||
|
||||
uint32_t block_count;
|
||||
if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break;
|
||||
|
||||
data->system_info.block_count = block_count;
|
||||
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY;
|
||||
|
||||
if(!flipper_format_read_hex(
|
||||
ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1))
|
||||
break;
|
||||
|
||||
simple_array_init(
|
||||
data->block_data, data->system_info.block_size * data->system_info.block_count);
|
||||
|
||||
if(!flipper_format_read_hex(
|
||||
ff,
|
||||
ISO15693_3_DATA_CONTENT_KEY,
|
||||
simple_array_get_data(data->block_data),
|
||||
simple_array_get_count(data->block_data)))
|
||||
break;
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) {
|
||||
if(data->system_info.block_count > 0 && data->system_info.block_size > 0) {
|
||||
simple_array_init(
|
||||
data->block_data,
|
||||
data->system_info.block_size * data->system_info.block_count);
|
||||
simple_array_init(data->block_security, data->system_info.block_count);
|
||||
|
||||
const bool security_loaded = has_lock_bits ?
|
||||
iso15693_3_load_security(data, ff) :
|
||||
iso15693_3_load_security_legacy(data, ff);
|
||||
if(!security_loaded) break;
|
||||
if(!flipper_format_read_hex(
|
||||
ff,
|
||||
ISO15693_3_DATA_CONTENT_KEY,
|
||||
simple_array_get_data(data->block_data),
|
||||
simple_array_get_count(data->block_data)))
|
||||
break;
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) {
|
||||
const bool security_loaded = has_lock_bits ?
|
||||
iso15693_3_load_security(data, ff) :
|
||||
iso15693_3_load_security_legacy(data, ff);
|
||||
if(!security_loaded) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,22 +262,24 @@ bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {
|
||||
ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
ISO15693_3_DATA_CONTENT_KEY,
|
||||
simple_array_cget_data(data->block_data),
|
||||
simple_array_get_count(data->block_data)))
|
||||
break;
|
||||
if(data->system_info.block_count > 0 && data->system_info.block_size > 0) {
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
ISO15693_3_DATA_CONTENT_KEY,
|
||||
simple_array_cget_data(data->block_data),
|
||||
simple_array_get_count(data->block_data)))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
ff, "Block Security Status: 01 = locked, 00 = not locked"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
ISO15693_3_SECURITY_STATUS_KEY,
|
||||
simple_array_cget_data(data->block_security),
|
||||
simple_array_get_count(data->block_security)))
|
||||
break;
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
ff, "Block Security Status: 01 = locked, 00 = not locked"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
ISO15693_3_SECURITY_STATUS_KEY,
|
||||
simple_array_cget_data(data->block_security),
|
||||
simple_array_get_count(data->block_security)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
@@ -100,10 +100,12 @@ Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_
|
||||
break;
|
||||
}
|
||||
|
||||
if(system_info->block_count > 0) {
|
||||
// Read blocks: Optional command
|
||||
if(system_info->block_count > 0 && system_info->block_size > 0) {
|
||||
simple_array_init(
|
||||
data->block_data, system_info->block_count * system_info->block_size);
|
||||
simple_array_init(data->block_security, system_info->block_count);
|
||||
|
||||
// Read blocks: Optional command
|
||||
ret = iso15693_3_poller_read_blocks(
|
||||
instance,
|
||||
simple_array_get_data(data->block_data),
|
||||
@@ -115,8 +117,6 @@ Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_
|
||||
}
|
||||
|
||||
// Get block security status: Optional command
|
||||
simple_array_init(data->block_security, system_info->block_count);
|
||||
|
||||
ret = iso15693_3_poller_get_blocks_security(
|
||||
instance, simple_array_get_data(data->block_security), system_info->block_count);
|
||||
if(ret != Iso15693_3ErrorNone) {
|
||||
|
||||
@@ -19,7 +19,7 @@ typedef struct {
|
||||
uint8_t cmd_start_byte;
|
||||
size_t cmd_len_bits;
|
||||
size_t command_num;
|
||||
MfClassicListenerCommandHandler* handler;
|
||||
const MfClassicListenerCommandHandler* handler;
|
||||
} MfClassicListenerCmd;
|
||||
|
||||
static void mf_classic_listener_prepare_emulation(MfClassicListener* instance) {
|
||||
@@ -441,42 +441,42 @@ static MfClassicListenerCommand
|
||||
return command;
|
||||
}
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = {
|
||||
mf_classic_listener_halt_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = {
|
||||
mf_classic_listener_auth_key_a_handler,
|
||||
mf_classic_listener_auth_second_part_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = {
|
||||
mf_classic_listener_auth_key_b_handler,
|
||||
mf_classic_listener_auth_second_part_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = {
|
||||
mf_classic_listener_read_block_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = {
|
||||
mf_classic_listener_write_block_first_part_handler,
|
||||
mf_classic_listener_write_block_second_part_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = {
|
||||
mf_classic_listener_value_dec_handler,
|
||||
mf_classic_listener_value_data_receive_handler,
|
||||
mf_classic_listener_value_transfer_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = {
|
||||
mf_classic_listener_value_inc_handler,
|
||||
mf_classic_listener_value_data_receive_handler,
|
||||
mf_classic_listener_value_transfer_handler,
|
||||
};
|
||||
|
||||
static MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = {
|
||||
static const MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = {
|
||||
mf_classic_listener_value_restore_handler,
|
||||
mf_classic_listener_value_data_receive_handler,
|
||||
mf_classic_listener_value_transfer_handler,
|
||||
|
||||
@@ -104,6 +104,7 @@ typedef enum {
|
||||
MfDesfireFileTypeValue = 2,
|
||||
MfDesfireFileTypeLinearRecord = 3,
|
||||
MfDesfireFileTypeCyclicRecord = 4,
|
||||
MfDesfireFileTypeTransactionMac = 5,
|
||||
} MfDesfireFileType;
|
||||
|
||||
typedef enum {
|
||||
@@ -135,6 +136,11 @@ typedef struct {
|
||||
uint32_t max;
|
||||
uint32_t cur;
|
||||
} record;
|
||||
struct {
|
||||
uint8_t key_option;
|
||||
uint8_t key_version;
|
||||
uint32_t counter_limit;
|
||||
} transaction_mac;
|
||||
};
|
||||
} MfDesfireFileSettings;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "mf_desfire_i.h"
|
||||
|
||||
#include <bit_lib/bit_lib.h>
|
||||
|
||||
#define TAG "MfDesfire"
|
||||
|
||||
#define BITS_IN_BYTE (8U)
|
||||
@@ -47,6 +49,10 @@
|
||||
#define MF_DESFIRE_FFF_FILE_MAX_KEY "Max"
|
||||
#define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur"
|
||||
|
||||
#define MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY "Key Option"
|
||||
#define MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY "Key Version"
|
||||
#define MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY "Counter Limit"
|
||||
|
||||
bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) {
|
||||
const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion);
|
||||
|
||||
@@ -183,12 +189,19 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
|
||||
uint32_t cur : 3 * BITS_IN_BYTE;
|
||||
} MfDesfireFileSettingsRecord;
|
||||
|
||||
typedef struct FURI_PACKED {
|
||||
uint8_t key_option;
|
||||
uint8_t key_version;
|
||||
uint8_t counter_limit[];
|
||||
} MfDesfireFileSettingsTransactionMac;
|
||||
|
||||
typedef struct FURI_PACKED {
|
||||
MfDesfireFileSettingsHeader header;
|
||||
union {
|
||||
MfDesfireFileSettingsData data;
|
||||
MfDesfireFileSettingsValue value;
|
||||
MfDesfireFileSettingsRecord record;
|
||||
MfDesfireFileSettingsTransactionMac transaction_mac;
|
||||
};
|
||||
} MfDesfireFileSettingsLayout;
|
||||
|
||||
@@ -197,7 +210,7 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
|
||||
const size_t data_size = bit_buffer_get_size_bytes(buf);
|
||||
const uint8_t* data_ptr = bit_buffer_get_data(buf);
|
||||
const size_t min_data_size =
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData);
|
||||
sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsTransactionMac);
|
||||
|
||||
if(data_size < min_data_size) {
|
||||
FURI_LOG_E(
|
||||
@@ -217,17 +230,11 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
|
||||
|
||||
if(file_settings_temp.type == MfDesfireFileTypeStandard ||
|
||||
file_settings_temp.type == MfDesfireFileTypeBackup) {
|
||||
memcpy(
|
||||
&layout.data,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsData));
|
||||
memcpy(&layout.data, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsData));
|
||||
file_settings_temp.data.size = layout.data.size;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsData);
|
||||
} else if(file_settings_temp.type == MfDesfireFileTypeValue) {
|
||||
memcpy(
|
||||
&layout.value,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsValue));
|
||||
memcpy(&layout.value, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsValue));
|
||||
file_settings_temp.value.lo_limit = layout.value.lo_limit;
|
||||
file_settings_temp.value.hi_limit = layout.value.hi_limit;
|
||||
file_settings_temp.value.limited_credit_value = layout.value.limited_credit_value;
|
||||
@@ -237,13 +244,34 @@ bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer
|
||||
file_settings_temp.type == MfDesfireFileTypeLinearRecord ||
|
||||
file_settings_temp.type == MfDesfireFileTypeCyclicRecord) {
|
||||
memcpy(
|
||||
&layout.record,
|
||||
&data_ptr[sizeof(MfDesfireFileSettingsHeader)],
|
||||
sizeof(MfDesfireFileSettingsRecord));
|
||||
&layout.record, &data_ptr[bytes_processed], sizeof(MfDesfireFileSettingsRecord));
|
||||
file_settings_temp.record.size = layout.record.size;
|
||||
file_settings_temp.record.max = layout.record.max;
|
||||
file_settings_temp.record.cur = layout.record.cur;
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsRecord);
|
||||
} else if(file_settings_temp.type == MfDesfireFileTypeTransactionMac) {
|
||||
const bool has_counter_limit = (layout.header.comm & 0x20) != 0;
|
||||
memcpy(
|
||||
&layout.transaction_mac,
|
||||
&data_ptr[bytes_processed],
|
||||
sizeof(MfDesfireFileSettingsTransactionMac));
|
||||
file_settings_temp.transaction_mac.key_option = layout.transaction_mac.key_option;
|
||||
file_settings_temp.transaction_mac.key_version = layout.transaction_mac.key_version;
|
||||
if(!has_counter_limit) {
|
||||
file_settings_temp.transaction_mac.counter_limit = 0;
|
||||
} else {
|
||||
// AES (4b) or LRP (2b)
|
||||
const size_t counter_limit_size = (layout.transaction_mac.key_option & 0x02) ? 4 :
|
||||
2;
|
||||
memcpy(
|
||||
&layout.transaction_mac,
|
||||
&data_ptr[bytes_processed],
|
||||
sizeof(MfDesfireFileSettingsTransactionMac) + counter_limit_size);
|
||||
file_settings_temp.transaction_mac.counter_limit = bit_lib_bytes_to_num_be(
|
||||
layout.transaction_mac.counter_limit, counter_limit_size);
|
||||
bytes_processed += counter_limit_size;
|
||||
}
|
||||
bytes_processed += sizeof(MfDesfireFileSettingsTransactionMac);
|
||||
} else {
|
||||
FURI_LOG_W(TAG, "Unknown file type: %02x", file_settings_temp.type);
|
||||
break;
|
||||
@@ -483,6 +511,21 @@ bool mf_desfire_file_settings_load(
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);
|
||||
if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))
|
||||
break;
|
||||
} else if(data->type == MfDesfireFileTypeTransactionMac) {
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY);
|
||||
if(!flipper_format_read_hex(
|
||||
ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1))
|
||||
break;
|
||||
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY);
|
||||
if(!flipper_format_read_hex(
|
||||
ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1))
|
||||
break;
|
||||
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY);
|
||||
if(!flipper_format_read_uint32(
|
||||
ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1))
|
||||
break;
|
||||
}
|
||||
|
||||
success = true;
|
||||
@@ -731,6 +774,21 @@ bool mf_desfire_file_settings_save(
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY);
|
||||
if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1))
|
||||
break;
|
||||
} else if(data->type == MfDesfireFileTypeTransactionMac) {
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_OPTION_KEY);
|
||||
if(!flipper_format_write_hex(
|
||||
ff, furi_string_get_cstr(key), &data->transaction_mac.key_option, 1))
|
||||
break;
|
||||
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_KEY_VERSION_KEY);
|
||||
if(!flipper_format_write_hex(
|
||||
ff, furi_string_get_cstr(key), &data->transaction_mac.key_version, 1))
|
||||
break;
|
||||
|
||||
furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COUNTER_LIMIT_KEY);
|
||||
if(!flipper_format_write_uint32(
|
||||
ff, furi_string_get_cstr(key), &data->transaction_mac.counter_limit, 1))
|
||||
break;
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
@@ -551,7 +551,7 @@ MfDesfireError mf_desfire_poller_read_file_data_multi(
|
||||
file_type == MfDesfireFileTypeLinearRecord ||
|
||||
file_type == MfDesfireFileTypeCyclicRecord) {
|
||||
error = mf_desfire_poller_read_file_records(
|
||||
instance, file_id, 0, file_settings_cur->data.size, file_data);
|
||||
instance, file_id, 0, file_settings_cur->record.size, file_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* When implementing a new protocol, add its implementation
|
||||
* here under its own index defined in nfc_protocol.h.
|
||||
*/
|
||||
const NfcDeviceBase* nfc_devices[NfcProtocolNum] = {
|
||||
const NfcDeviceBase* const nfc_devices[NfcProtocolNum] = {
|
||||
[NfcProtocolIso14443_3a] = &nfc_device_iso14443_3a,
|
||||
[NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b,
|
||||
[NfcProtocolIso14443_4a] = &nfc_device_iso14443_4a,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const NfcDeviceBase* nfc_devices[];
|
||||
extern const NfcDeviceBase* const nfc_devices[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <nfc/protocols/slix/slix_listener_defs.h>
|
||||
#include <nfc/protocols/type_4_tag/type_4_tag_listener_defs.h>
|
||||
|
||||
const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = {
|
||||
const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum] = {
|
||||
[NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a,
|
||||
[NfcProtocolIso14443_3b] = NULL,
|
||||
[NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const NfcListenerBase* nfc_listeners_api[NfcProtocolNum];
|
||||
extern const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <nfc/protocols/ntag4xx/ntag4xx_poller_defs.h>
|
||||
#include <nfc/protocols/type_4_tag/type_4_tag_poller_defs.h>
|
||||
|
||||
const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = {
|
||||
const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum] = {
|
||||
[NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a,
|
||||
[NfcProtocolIso14443_3b] = &nfc_poller_iso14443_3b,
|
||||
[NfcProtocolIso14443_4a] = &nfc_poller_iso14443_4a,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const NfcPollerBase* nfc_pollers_api[NfcProtocolNum];
|
||||
extern const NfcPollerBase* const nfc_pollers_api[NfcProtocolNum];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+27
-17
@@ -14,12 +14,13 @@
|
||||
|
||||
#define TAG "SubGhzProtocolCame"
|
||||
|
||||
#define CAME_12_COUNT_BIT 12
|
||||
#define CAME_24_COUNT_BIT 24
|
||||
#define PRASTEL_COUNT_BIT 25
|
||||
#define PRASTEL_NAME "Prastel"
|
||||
#define AIRFORCE_COUNT_BIT 18
|
||||
#define AIRFORCE_NAME "Airforce"
|
||||
#define CAME_12_COUNT_BIT 12
|
||||
#define CAME_24_COUNT_BIT 24
|
||||
#define PRASTEL_25_COUNT_BIT 25
|
||||
#define PRASTEL_42_COUNT_BIT 42
|
||||
#define PRASTEL_NAME "Prastel"
|
||||
#define AIRFORCE_COUNT_BIT 18
|
||||
#define AIRFORCE_NAME "Airforce"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_came_const = {
|
||||
.te_short = 320,
|
||||
@@ -123,6 +124,7 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i
|
||||
|
||||
switch(instance->generic.data_count_bit) {
|
||||
case CAME_24_COUNT_BIT:
|
||||
case PRASTEL_42_COUNT_BIT:
|
||||
// CAME 24 Bit = 24320 us
|
||||
header_te = 76;
|
||||
break;
|
||||
@@ -131,7 +133,7 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i
|
||||
// CAME 12 Bit Original only! and Airforce protocol = 15040 us
|
||||
header_te = 47;
|
||||
break;
|
||||
case PRASTEL_COUNT_BIT:
|
||||
case PRASTEL_25_COUNT_BIT:
|
||||
// PRASTEL = 11520 us
|
||||
header_te = 36;
|
||||
break;
|
||||
@@ -174,7 +176,7 @@ SubGhzProtocolStatus
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit > PRASTEL_COUNT_BIT) {
|
||||
if(instance->generic.data_count_bit > PRASTEL_42_COUNT_BIT) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
ret = SubGhzProtocolStatusErrorValueBitCount;
|
||||
break;
|
||||
@@ -268,7 +270,8 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat
|
||||
if((instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_came_const.min_count_bit_for_found) ||
|
||||
(instance->decoder.decode_count_bit == AIRFORCE_COUNT_BIT) ||
|
||||
(instance->decoder.decode_count_bit == PRASTEL_COUNT_BIT) ||
|
||||
(instance->decoder.decode_count_bit == PRASTEL_25_COUNT_BIT) ||
|
||||
(instance->decoder.decode_count_bit == PRASTEL_42_COUNT_BIT) ||
|
||||
(instance->decoder.decode_count_bit == CAME_24_COUNT_BIT)) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
@@ -337,7 +340,7 @@ SubGhzProtocolStatus
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit > PRASTEL_COUNT_BIT) {
|
||||
if(instance->generic.data_count_bit > PRASTEL_42_COUNT_BIT) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
ret = SubGhzProtocolStatusErrorValueBitCount;
|
||||
break;
|
||||
@@ -350,23 +353,30 @@ void subghz_protocol_decoder_came_get_string(void* context, FuriString* output)
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x000003ffffffffff;
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x000003ffffffffff;
|
||||
|
||||
const char* name = instance->generic.protocol_name;
|
||||
switch(instance->generic.data_count_bit) {
|
||||
case PRASTEL_25_COUNT_BIT:
|
||||
case PRASTEL_42_COUNT_BIT:
|
||||
name = PRASTEL_NAME;
|
||||
break;
|
||||
case AIRFORCE_COUNT_BIT:
|
||||
name = AIRFORCE_NAME;
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%08lX\r\n"
|
||||
"Yek:0x%08lX\r\n",
|
||||
(instance->generic.data_count_bit == PRASTEL_COUNT_BIT ?
|
||||
PRASTEL_NAME :
|
||||
(instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ?
|
||||
AIRFORCE_NAME :
|
||||
instance->generic.protocol_name)),
|
||||
name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_lo,
|
||||
code_found_reverse_lo);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "protocol_items.h" // IWYU pragma: keep
|
||||
|
||||
const SubGhzProtocol* subghz_protocol_registry_items[] = {
|
||||
const SubGhzProtocol* const subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_gate_tx,
|
||||
&subghz_protocol_keeloq,
|
||||
&subghz_protocol_star_line,
|
||||
|
||||
@@ -12,7 +12,7 @@ typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;
|
||||
typedef struct SubGhzProtocol SubGhzProtocol;
|
||||
|
||||
struct SubGhzProtocolRegistry {
|
||||
const SubGhzProtocol** items;
|
||||
const SubGhzProtocol* const* items;
|
||||
const size_t size;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ env.Append(
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("api_lock.h"),
|
||||
File("cli/cli_ansi.h"),
|
||||
File("cli/cli_command.h"),
|
||||
File("cli/cli_registry.h"),
|
||||
File("cli/shell/cli_shell.h"),
|
||||
File("compress.h"),
|
||||
File("manchester_decoder.h"),
|
||||
File("manchester_encoder.h"),
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "cli_ansi.h"
|
||||
|
||||
typedef enum {
|
||||
CliAnsiParserStateInitial,
|
||||
CliAnsiParserStateEscape,
|
||||
CliAnsiParserStateEscapeBrace,
|
||||
CliAnsiParserStateEscapeBraceOne,
|
||||
CliAnsiParserStateEscapeBraceOneSemicolon,
|
||||
CliAnsiParserStateEscapeBraceOneSemicolonModifiers,
|
||||
} CliAnsiParserState;
|
||||
|
||||
struct CliAnsiParser {
|
||||
CliAnsiParserState state;
|
||||
CliModKey modifiers;
|
||||
};
|
||||
|
||||
CliAnsiParser* cli_ansi_parser_alloc(void) {
|
||||
CliAnsiParser* parser = malloc(sizeof(CliAnsiParser));
|
||||
return parser;
|
||||
}
|
||||
|
||||
void cli_ansi_parser_free(CliAnsiParser* parser) {
|
||||
free(parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a single character representing a special key into the enum
|
||||
* representation
|
||||
*/
|
||||
static CliKey cli_ansi_key_from_mnemonic(char c) {
|
||||
switch(c) {
|
||||
case 'A':
|
||||
return CliKeyUp;
|
||||
case 'B':
|
||||
return CliKeyDown;
|
||||
case 'C':
|
||||
return CliKeyRight;
|
||||
case 'D':
|
||||
return CliKeyLeft;
|
||||
case 'F':
|
||||
return CliKeyEnd;
|
||||
case 'H':
|
||||
return CliKeyHome;
|
||||
default:
|
||||
return CliKeyUnrecognized;
|
||||
}
|
||||
}
|
||||
|
||||
#define PARSER_RESET_AND_RETURN(parser, modifiers_val, key_val) \
|
||||
do { \
|
||||
parser->state = CliAnsiParserStateInitial; \
|
||||
return (CliAnsiParserResult){ \
|
||||
.is_done = true, \
|
||||
.result = (CliKeyCombo){ \
|
||||
.modifiers = modifiers_val, \
|
||||
.key = key_val, \
|
||||
}}; \
|
||||
} while(0);
|
||||
|
||||
CliAnsiParserResult cli_ansi_parser_feed(CliAnsiParser* parser, char c) {
|
||||
switch(parser->state) {
|
||||
case CliAnsiParserStateInitial:
|
||||
// <key> -> <key>
|
||||
if(c != CliKeyEsc) PARSER_RESET_AND_RETURN(parser, CliModKeyNo, c); // -V1048
|
||||
|
||||
// <ESC> ...
|
||||
parser->state = CliAnsiParserStateEscape;
|
||||
break;
|
||||
|
||||
case CliAnsiParserStateEscape:
|
||||
// <ESC> <ESC> -> <ESC>
|
||||
if(c == CliKeyEsc) PARSER_RESET_AND_RETURN(parser, CliModKeyNo, c);
|
||||
|
||||
// <ESC> <key> -> Alt + <key>
|
||||
if(c != '[') PARSER_RESET_AND_RETURN(parser, CliModKeyAlt, c);
|
||||
|
||||
// <ESC> [ ...
|
||||
parser->state = CliAnsiParserStateEscapeBrace;
|
||||
break;
|
||||
|
||||
case CliAnsiParserStateEscapeBrace:
|
||||
// <ESC> [ <key mnemonic> -> <key>
|
||||
if(c != '1') PARSER_RESET_AND_RETURN(parser, CliModKeyNo, cli_ansi_key_from_mnemonic(c));
|
||||
|
||||
// <ESC> [ 1 ...
|
||||
parser->state = CliAnsiParserStateEscapeBraceOne;
|
||||
break;
|
||||
|
||||
case CliAnsiParserStateEscapeBraceOne:
|
||||
// <ESC> [ 1 <non-;> -> error
|
||||
if(c != ';') PARSER_RESET_AND_RETURN(parser, CliModKeyNo, CliKeyUnrecognized);
|
||||
|
||||
// <ESC> [ 1 ; ...
|
||||
parser->state = CliAnsiParserStateEscapeBraceOneSemicolon;
|
||||
break;
|
||||
|
||||
case CliAnsiParserStateEscapeBraceOneSemicolon:
|
||||
// <ESC> [ 1 ; <modifiers> ...
|
||||
parser->modifiers = (c - '0');
|
||||
parser->modifiers &= ~1;
|
||||
parser->state = CliAnsiParserStateEscapeBraceOneSemicolonModifiers;
|
||||
break;
|
||||
|
||||
case CliAnsiParserStateEscapeBraceOneSemicolonModifiers:
|
||||
// <ESC> [ 1 ; <modifiers> <key mnemonic> -> <modifiers> + <key>
|
||||
PARSER_RESET_AND_RETURN(parser, parser->modifiers, cli_ansi_key_from_mnemonic(c));
|
||||
}
|
||||
|
||||
return (CliAnsiParserResult){.is_done = false};
|
||||
}
|
||||
|
||||
CliAnsiParserResult cli_ansi_parser_feed_timeout(CliAnsiParser* parser) {
|
||||
CliAnsiParserResult result = {.is_done = false};
|
||||
|
||||
if(parser->state == CliAnsiParserStateEscape) {
|
||||
result.is_done = true;
|
||||
result.result.key = CliKeyEsc;
|
||||
result.result.modifiers = CliModKeyNo;
|
||||
}
|
||||
|
||||
parser->state = CliAnsiParserStateInitial;
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// text styling
|
||||
|
||||
#define ANSI_RESET "\e[0m"
|
||||
#define ANSI_BOLD "\e[1m"
|
||||
#define ANSI_FAINT "\e[2m"
|
||||
#define ANSI_INVERT "\e[7m"
|
||||
|
||||
#define ANSI_FG_BLACK "\e[30m"
|
||||
#define ANSI_FG_RED "\e[31m"
|
||||
#define ANSI_FG_GREEN "\e[32m"
|
||||
#define ANSI_FG_YELLOW "\e[33m"
|
||||
#define ANSI_FG_BLUE "\e[34m"
|
||||
#define ANSI_FG_MAGENTA "\e[35m"
|
||||
#define ANSI_FG_CYAN "\e[36m"
|
||||
#define ANSI_FG_WHITE "\e[37m"
|
||||
#define ANSI_FG_BR_BLACK "\e[90m"
|
||||
#define ANSI_FG_BR_RED "\e[91m"
|
||||
#define ANSI_FG_BR_GREEN "\e[92m"
|
||||
#define ANSI_FG_BR_YELLOW "\e[93m"
|
||||
#define ANSI_FG_BR_BLUE "\e[94m"
|
||||
#define ANSI_FG_BR_MAGENTA "\e[95m"
|
||||
#define ANSI_FG_BR_CYAN "\e[96m"
|
||||
#define ANSI_FG_BR_WHITE "\e[97m"
|
||||
|
||||
#define ANSI_BG_BLACK "\e[40m"
|
||||
#define ANSI_BG_RED "\e[41m"
|
||||
#define ANSI_BG_GREEN "\e[42m"
|
||||
#define ANSI_BG_YELLOW "\e[43m"
|
||||
#define ANSI_BG_BLUE "\e[44m"
|
||||
#define ANSI_BG_MAGENTA "\e[45m"
|
||||
#define ANSI_BG_CYAN "\e[46m"
|
||||
#define ANSI_BG_WHITE "\e[47m"
|
||||
#define ANSI_BG_BR_BLACK "\e[100m"
|
||||
#define ANSI_BG_BR_RED "\e[101m"
|
||||
#define ANSI_BG_BR_GREEN "\e[102m"
|
||||
#define ANSI_BG_BR_YELLOW "\e[103m"
|
||||
#define ANSI_BG_BR_BLUE "\e[104m"
|
||||
#define ANSI_BG_BR_MAGENTA "\e[105m"
|
||||
#define ANSI_BG_BR_CYAN "\e[106m"
|
||||
#define ANSI_BG_BR_WHITE "\e[107m"
|
||||
|
||||
#define ANSI_FLIPPER_BRAND_ORANGE "\e[38;2;255;130;0m"
|
||||
|
||||
// cursor positioning
|
||||
|
||||
#define ANSI_CURSOR_UP_BY(rows) "\e[" rows "A"
|
||||
#define ANSI_CURSOR_DOWN_BY(rows) "\e[" rows "B"
|
||||
#define ANSI_CURSOR_RIGHT_BY(cols) "\e[" cols "C"
|
||||
#define ANSI_CURSOR_LEFT_BY(cols) "\e[" cols "D"
|
||||
#define ANSI_CURSOR_DOWN_BY_AND_FIRST_COLUMN(rows) "\e[" rows "E"
|
||||
#define ANSI_CURSOR_UP_BY_AND_FIRST_COLUMN(rows) "\e[" rows "F"
|
||||
#define ANSI_CURSOR_HOR_POS(pos) "\e[" pos "G"
|
||||
#define ANSI_CURSOR_POS(row, col) "\e[" row ";" col "H"
|
||||
|
||||
// erasing
|
||||
|
||||
#define ANSI_ERASE_FROM_CURSOR_TO_END "0"
|
||||
#define ANSI_ERASE_FROM_START_TO_CURSOR "1"
|
||||
#define ANSI_ERASE_ENTIRE "2"
|
||||
|
||||
#define ANSI_ERASE_DISPLAY(portion) "\e[" portion "J"
|
||||
#define ANSI_ERASE_LINE(portion) "\e[" portion "K"
|
||||
#define ANSI_ERASE_SCROLLBACK_BUFFER ANSI_ERASE_DISPLAY("3")
|
||||
|
||||
// misc
|
||||
|
||||
#define ANSI_INSERT_MODE_ENABLE "\e[4h"
|
||||
#define ANSI_INSERT_MODE_DISABLE "\e[4l"
|
||||
|
||||
typedef enum {
|
||||
CliKeyUnrecognized = 0,
|
||||
|
||||
CliKeySOH = 0x01,
|
||||
CliKeyETX = 0x03,
|
||||
CliKeyEOT = 0x04,
|
||||
CliKeyBell = 0x07,
|
||||
CliKeyBackspace = 0x08,
|
||||
CliKeyTab = 0x09,
|
||||
CliKeyLF = 0x0A,
|
||||
CliKeyFF = 0x0C,
|
||||
CliKeyCR = 0x0D,
|
||||
CliKeyETB = 0x17,
|
||||
CliKeyEsc = 0x1B,
|
||||
CliKeyUS = 0x1F,
|
||||
CliKeySpace = 0x20,
|
||||
CliKeyDEL = 0x7F,
|
||||
|
||||
CliKeySpecial = 0x80,
|
||||
CliKeyLeft,
|
||||
CliKeyRight,
|
||||
CliKeyUp,
|
||||
CliKeyDown,
|
||||
CliKeyHome,
|
||||
CliKeyEnd,
|
||||
} CliKey;
|
||||
|
||||
typedef enum {
|
||||
CliModKeyNo = 0,
|
||||
CliModKeyAlt = 2,
|
||||
CliModKeyCtrl = 4,
|
||||
CliModKeyMeta = 8,
|
||||
} CliModKey;
|
||||
|
||||
typedef struct {
|
||||
CliModKey modifiers;
|
||||
CliKey key;
|
||||
} CliKeyCombo;
|
||||
|
||||
typedef struct CliAnsiParser CliAnsiParser;
|
||||
|
||||
typedef struct {
|
||||
bool is_done;
|
||||
CliKeyCombo result;
|
||||
} CliAnsiParserResult;
|
||||
|
||||
/**
|
||||
* @brief Allocates an ANSI parser
|
||||
*/
|
||||
CliAnsiParser* cli_ansi_parser_alloc(void);
|
||||
|
||||
/**
|
||||
* @brief Frees an ANSI parser
|
||||
*/
|
||||
void cli_ansi_parser_free(CliAnsiParser* parser);
|
||||
|
||||
/**
|
||||
* @brief Feeds an ANSI parser a character
|
||||
*/
|
||||
CliAnsiParserResult cli_ansi_parser_feed(CliAnsiParser* parser, char c);
|
||||
|
||||
/**
|
||||
* @brief Feeds an ANSI parser a timeout event
|
||||
*
|
||||
* As a user of the ANSI parser API, you are responsible for calling this
|
||||
* function some time after the last character was fed into the parser. The
|
||||
* recommended timeout is about 10 ms. The exact value does not matter as long
|
||||
* as it is small enough for the user not notice a delay, but big enough that
|
||||
* when a terminal is sending an escape sequence, this function does not get
|
||||
* called in between the characters of the sequence.
|
||||
*/
|
||||
CliAnsiParserResult cli_ansi_parser_feed_timeout(CliAnsiParser* parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "cli_command.h"
|
||||
#include "cli_ansi.h"
|
||||
|
||||
bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side) {
|
||||
if(pipe_state(side) == PipeStateBroken) return true;
|
||||
if(!pipe_bytes_available(side)) return false;
|
||||
char c = getchar();
|
||||
return c == CliKeyETX;
|
||||
}
|
||||
|
||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
||||
furi_check(cmd);
|
||||
furi_check(arg);
|
||||
furi_check(usage);
|
||||
|
||||
printf("%s: illegal option -- %s\r\nusage: %s %s", cmd, arg, cmd, usage);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @file cli_command.h
|
||||
* Command metadata and helpers
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <lib/flipper_application/flipper_application.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CLI_PLUGIN_API_VERSION 1
|
||||
|
||||
typedef enum {
|
||||
CliCommandFlagDefault = 0, /**< Default */
|
||||
CliCommandFlagParallelSafe = (1 << 0), /**< Safe to run in parallel with other apps */
|
||||
CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */
|
||||
CliCommandFlagDontAttachStdio = (1 << 2), /**< Do no attach I/O pipe to thread stdio */
|
||||
CliCommandFlagUseShellThread =
|
||||
(1
|
||||
<< 3), /**< Don't start a separate thread to run the command in. Incompatible with DontAttachStdio */
|
||||
|
||||
// internal flags (do not set them yourselves!)
|
||||
|
||||
CliCommandFlagExternal = (1 << 4), /**< The command comes from a .fal file */
|
||||
} CliCommandFlag;
|
||||
|
||||
/**
|
||||
* @brief CLI command execution callback pointer
|
||||
*
|
||||
* This callback will be called from a separate thread spawned just for your
|
||||
* command. The pipe will be installed as the thread's stdio, so you can use
|
||||
* `printf`, `getchar` and other standard functions to communicate with the
|
||||
* user.
|
||||
*
|
||||
* @param [in] pipe Pipe that can be used to send and receive data. If
|
||||
* `CliCommandFlagDontAttachStdio` was not set, you can
|
||||
* also use standard C functions (printf, getc, etc.) to
|
||||
* access this pipe.
|
||||
* @param [in] args String with what was passed after the command
|
||||
* @param [in] context Whatever you provided to `cli_add_command`
|
||||
*/
|
||||
typedef void (*CliCommandExecuteCallback)(PipeSide* pipe, FuriString* args, void* context);
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
CliCommandExecuteCallback execute_callback;
|
||||
CliCommandFlag flags;
|
||||
size_t stack_depth;
|
||||
} CliCommandDescriptor;
|
||||
|
||||
/**
|
||||
* @brief Configuration for locating external commands
|
||||
*/
|
||||
typedef struct {
|
||||
const char* search_directory; //<! The directory to look in
|
||||
const char* fal_prefix; //<! File name prefix that commands should have
|
||||
const char* appid; //<! Expected plugin-reported appid
|
||||
} CliCommandExternalConfig;
|
||||
|
||||
/**
|
||||
* @brief Detects if Ctrl+C has been pressed or session has been terminated
|
||||
*
|
||||
* @param [in] side Pointer to pipe side given to the command thread
|
||||
* @warning This function also assumes that the pipe is installed as the
|
||||
* thread's stdio
|
||||
* @warning This function will consume 0 or 1 bytes from the pipe
|
||||
*/
|
||||
bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side);
|
||||
|
||||
/** Print unified cmd usage tip
|
||||
*
|
||||
* @param cmd cmd name
|
||||
* @param usage usage tip
|
||||
* @param arg arg passed by user
|
||||
*/
|
||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg);
|
||||
|
||||
#define CLI_COMMAND_INTERFACE(name, execute_callback, flags, stack_depth, app_id) \
|
||||
static const CliCommandDescriptor cli_##name##_desc = { \
|
||||
#name, \
|
||||
&execute_callback, \
|
||||
flags, \
|
||||
stack_depth, \
|
||||
}; \
|
||||
\
|
||||
static const FlipperAppPluginDescriptor plugin_descriptor = { \
|
||||
.appid = app_id, \
|
||||
.ep_api_version = CLI_PLUGIN_API_VERSION, \
|
||||
.entry_point = &cli_##name##_desc, \
|
||||
}; \
|
||||
\
|
||||
const FlipperAppPluginDescriptor* cli_##name##_ep(void) { \
|
||||
return &plugin_descriptor; \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "cli_registry.h"
|
||||
#include "cli_registry_i.h"
|
||||
#include <toolbox/pipe.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "cli"
|
||||
|
||||
struct CliRegistry {
|
||||
CliCommandTree_t commands;
|
||||
FuriMutex* mutex;
|
||||
};
|
||||
|
||||
CliRegistry* cli_registry_alloc(void) {
|
||||
CliRegistry* registry = malloc(sizeof(CliRegistry));
|
||||
CliCommandTree_init(registry->commands);
|
||||
registry->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||
return registry;
|
||||
}
|
||||
|
||||
void cli_registry_free(CliRegistry* registry) {
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
furi_mutex_free(registry->mutex);
|
||||
CliCommandTree_clear(registry->commands);
|
||||
free(registry);
|
||||
}
|
||||
|
||||
void cli_registry_add_command(
|
||||
CliRegistry* registry,
|
||||
const char* name,
|
||||
CliCommandFlag flags,
|
||||
CliCommandExecuteCallback callback,
|
||||
void* context) {
|
||||
cli_registry_add_command_ex(
|
||||
registry, name, flags, callback, context, CLI_BUILTIN_COMMAND_STACK_SIZE);
|
||||
}
|
||||
|
||||
void cli_registry_add_command_ex(
|
||||
CliRegistry* registry,
|
||||
const char* name,
|
||||
CliCommandFlag flags,
|
||||
CliCommandExecuteCallback callback,
|
||||
void* context,
|
||||
size_t stack_size) {
|
||||
furi_check(registry);
|
||||
furi_check(name);
|
||||
furi_check(callback);
|
||||
|
||||
// the shell always attaches the pipe to the stdio, thus both flags can't be used at once
|
||||
if(flags & CliCommandFlagUseShellThread) furi_check(!(flags & CliCommandFlagDontAttachStdio));
|
||||
|
||||
FuriString* name_str;
|
||||
name_str = furi_string_alloc_set(name);
|
||||
// command cannot contain spaces
|
||||
furi_check(furi_string_search_char(name_str, ' ') == FURI_STRING_FAILURE);
|
||||
|
||||
CliRegistryCommand command = {
|
||||
.context = context,
|
||||
.execute_callback = callback,
|
||||
.flags = flags,
|
||||
.stack_depth = stack_size,
|
||||
};
|
||||
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
CliCommandTree_set_at(registry->commands, name_str, command);
|
||||
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||
|
||||
furi_string_free(name_str);
|
||||
}
|
||||
|
||||
void cli_registry_delete_command(CliRegistry* registry, const char* name) {
|
||||
furi_check(registry);
|
||||
FuriString* name_str;
|
||||
name_str = furi_string_alloc_set(name);
|
||||
furi_string_trim(name_str);
|
||||
|
||||
size_t name_replace;
|
||||
do {
|
||||
name_replace = furi_string_replace(name_str, " ", "_");
|
||||
} while(name_replace != FURI_STRING_FAILURE);
|
||||
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
CliCommandTree_erase(registry->commands, name_str);
|
||||
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||
|
||||
furi_string_free(name_str);
|
||||
}
|
||||
|
||||
bool cli_registry_get_command(
|
||||
CliRegistry* registry,
|
||||
FuriString* command,
|
||||
CliRegistryCommand* result) {
|
||||
furi_assert(registry);
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
CliRegistryCommand* data = CliCommandTree_get(registry->commands, command);
|
||||
if(data) *result = *data;
|
||||
|
||||
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||
|
||||
return !!data;
|
||||
}
|
||||
|
||||
void cli_registry_remove_external_commands(CliRegistry* registry) {
|
||||
furi_check(registry);
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
|
||||
// FIXME FL-3977: memory leak somewhere within this function
|
||||
|
||||
CliCommandTree_t internal_cmds;
|
||||
CliCommandTree_init(internal_cmds);
|
||||
for
|
||||
M_EACH(item, registry->commands, CliCommandTree_t) {
|
||||
if(!(item->value_ptr->flags & CliCommandFlagExternal))
|
||||
CliCommandTree_set_at(internal_cmds, *item->key_ptr, *item->value_ptr);
|
||||
}
|
||||
CliCommandTree_move(registry->commands, internal_cmds);
|
||||
|
||||
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void cli_registry_reload_external_commands(
|
||||
CliRegistry* registry,
|
||||
const CliCommandExternalConfig* config) {
|
||||
furi_check(registry);
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
FURI_LOG_D(TAG, "Reloading ext commands");
|
||||
|
||||
cli_registry_remove_external_commands(registry);
|
||||
|
||||
// iterate over files in plugin directory
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* plugin_dir = storage_file_alloc(storage);
|
||||
|
||||
if(storage_dir_open(plugin_dir, config->search_directory)) {
|
||||
char plugin_filename[64];
|
||||
FuriString* plugin_name = furi_string_alloc();
|
||||
|
||||
while(storage_dir_read(plugin_dir, NULL, plugin_filename, sizeof(plugin_filename))) {
|
||||
FURI_LOG_T(TAG, "Plugin: %s", plugin_filename);
|
||||
furi_string_set_str(plugin_name, plugin_filename);
|
||||
|
||||
furi_check(furi_string_end_with_str(plugin_name, ".fal"));
|
||||
furi_string_replace_all_str(plugin_name, ".fal", "");
|
||||
furi_check(furi_string_start_with_str(plugin_name, config->fal_prefix));
|
||||
furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), "");
|
||||
|
||||
CliRegistryCommand command = {
|
||||
.context = NULL,
|
||||
.execute_callback = NULL,
|
||||
.flags = CliCommandFlagExternal,
|
||||
};
|
||||
CliCommandTree_set_at(registry->commands, plugin_name, command);
|
||||
}
|
||||
|
||||
furi_string_free(plugin_name);
|
||||
}
|
||||
|
||||
storage_dir_close(plugin_dir);
|
||||
storage_file_free(plugin_dir);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
FURI_LOG_D(TAG, "Done reloading ext commands");
|
||||
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void cli_registry_lock(CliRegistry* registry) {
|
||||
furi_assert(registry);
|
||||
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void cli_registry_unlock(CliRegistry* registry) {
|
||||
furi_assert(registry);
|
||||
furi_mutex_release(registry->mutex);
|
||||
}
|
||||
|
||||
CliCommandTree_t* cli_registry_get_commands(CliRegistry* registry) {
|
||||
furi_assert(registry);
|
||||
return ®istry->commands;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @file cli_registry.h
|
||||
* API for registering commands with a CLI shell
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <m-array.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include "cli_command.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct CliRegistry CliRegistry;
|
||||
|
||||
/**
|
||||
* @brief Allocates a `CliRegistry`.
|
||||
*/
|
||||
CliRegistry* cli_registry_alloc(void);
|
||||
|
||||
/**
|
||||
* @brief Frees a `CliRegistry`.
|
||||
*/
|
||||
void cli_registry_free(CliRegistry* registry);
|
||||
|
||||
/**
|
||||
* @brief Registers a command with the registry. Provides less options than the
|
||||
* `_ex` counterpart.
|
||||
*
|
||||
* @param [in] registry Pointer to registry instance
|
||||
* @param [in] name Command name
|
||||
* @param [in] flags see CliCommandFlag
|
||||
* @param [in] callback Callback function
|
||||
* @param [in] context Custom context
|
||||
*/
|
||||
void cli_registry_add_command(
|
||||
CliRegistry* registry,
|
||||
const char* name,
|
||||
CliCommandFlag flags,
|
||||
CliCommandExecuteCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Registers a command with the registry. Provides more options than the
|
||||
* non-`_ex` counterpart.
|
||||
*
|
||||
* @param [in] registry Pointer to registry instance
|
||||
* @param [in] name Command name
|
||||
* @param [in] flags see CliCommandFlag
|
||||
* @param [in] callback Callback function
|
||||
* @param [in] context Custom context
|
||||
* @param [in] stack_size Thread stack size
|
||||
*/
|
||||
void cli_registry_add_command_ex(
|
||||
CliRegistry* registry,
|
||||
const char* name,
|
||||
CliCommandFlag flags,
|
||||
CliCommandExecuteCallback callback,
|
||||
void* context,
|
||||
size_t stack_size);
|
||||
|
||||
/**
|
||||
* @brief Deletes a cli command
|
||||
*
|
||||
* @param [in] registry Pointer to registry instance
|
||||
* @param [in] name Command name
|
||||
*/
|
||||
void cli_registry_delete_command(CliRegistry* registry, const char* name);
|
||||
|
||||
/**
|
||||
* @brief Unregisters all external commands
|
||||
*
|
||||
* @param [in] registry Pointer to registry instance
|
||||
*/
|
||||
void cli_registry_remove_external_commands(CliRegistry* registry);
|
||||
|
||||
/**
|
||||
* @brief Reloads the list of externally available commands
|
||||
*
|
||||
* @param [in] registry Pointer to registry instance
|
||||
* @param [in] config See `CliCommandExternalConfig`
|
||||
*/
|
||||
void cli_registry_reload_external_commands(
|
||||
CliRegistry* registry,
|
||||
const CliCommandExternalConfig* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @file cli_registry_i.h
|
||||
* Internal API for getting commands registered with the CLI
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <m-bptree.h>
|
||||
#include "cli_registry.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CLI_BUILTIN_COMMAND_STACK_SIZE (4 * 1024U)
|
||||
|
||||
typedef struct {
|
||||
void* context; //<! Context passed to callbacks
|
||||
CliCommandExecuteCallback execute_callback; //<! Callback for command execution
|
||||
CliCommandFlag flags;
|
||||
size_t stack_depth;
|
||||
} CliRegistryCommand;
|
||||
|
||||
#define CLI_COMMANDS_TREE_RANK 4
|
||||
|
||||
// -V:BPTREE_DEF2:1103
|
||||
// -V:BPTREE_DEF2:524
|
||||
BPTREE_DEF2(
|
||||
CliCommandTree,
|
||||
CLI_COMMANDS_TREE_RANK,
|
||||
FuriString*,
|
||||
FURI_STRING_OPLIST,
|
||||
CliRegistryCommand,
|
||||
M_POD_OPLIST);
|
||||
|
||||
#define M_OPL_CliCommandTree_t() BPTREE_OPLIST2(CliCommandTree, FURI_STRING_OPLIST, M_POD_OPLIST)
|
||||
|
||||
bool cli_registry_get_command(
|
||||
CliRegistry* registry,
|
||||
FuriString* command,
|
||||
CliRegistryCommand* result);
|
||||
|
||||
void cli_registry_lock(CliRegistry* registry);
|
||||
|
||||
void cli_registry_unlock(CliRegistry* registry);
|
||||
|
||||
/**
|
||||
* @warning Surround calls to this function with `cli_registry_[un]lock`
|
||||
*/
|
||||
CliCommandTree_t* cli_registry_get_commands(CliRegistry* registry);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,478 @@
|
||||
#include "cli_shell.h"
|
||||
#include "cli_shell_i.h"
|
||||
#include "../cli_ansi.h"
|
||||
#include "../cli_registry_i.h"
|
||||
#include "../cli_command.h"
|
||||
#include "cli_shell_line.h"
|
||||
#include "cli_shell_completions.h"
|
||||
#include <stdio.h>
|
||||
#include <furi_hal_version.h>
|
||||
#include <m-array.h>
|
||||
#include <loader/loader.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include <flipper_application/plugins/plugin_manager.h>
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "CliShell"
|
||||
|
||||
#define ANSI_TIMEOUT_MS 10
|
||||
|
||||
typedef enum {
|
||||
CliShellComponentCompletions,
|
||||
CliShellComponentLine,
|
||||
CliShellComponentMAX, //<! do not use
|
||||
} CliShellComponent;
|
||||
|
||||
CliShellKeyComboSet* component_key_combo_sets[] = {
|
||||
[CliShellComponentCompletions] = &cli_shell_completions_key_combo_set,
|
||||
[CliShellComponentLine] = &cli_shell_line_key_combo_set,
|
||||
};
|
||||
static_assert(CliShellComponentMAX == COUNT_OF(component_key_combo_sets));
|
||||
|
||||
typedef enum {
|
||||
CliShellStorageEventMount,
|
||||
CliShellStorageEventUnmount,
|
||||
} CliShellStorageEvent;
|
||||
|
||||
struct CliShell {
|
||||
// Set and freed by external thread
|
||||
CliShellMotd motd;
|
||||
void* callback_context;
|
||||
PipeSide* pipe;
|
||||
CliRegistry* registry;
|
||||
const CliCommandExternalConfig* ext_config;
|
||||
FuriThread* thread;
|
||||
const char* prompt;
|
||||
|
||||
// Set and freed by shell thread
|
||||
FuriEventLoop* event_loop;
|
||||
CliAnsiParser* ansi_parser;
|
||||
FuriEventLoopTimer* ansi_parsing_timer;
|
||||
|
||||
Storage* storage;
|
||||
FuriPubSubSubscription* storage_subscription;
|
||||
FuriMessageQueue* storage_event_queue;
|
||||
|
||||
void* components[CliShellComponentMAX];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
CliRegistryCommand* command;
|
||||
PipeSide* pipe;
|
||||
FuriString* args;
|
||||
} CliCommandThreadData;
|
||||
|
||||
static void cli_shell_data_available(PipeSide* pipe, void* context);
|
||||
static void cli_shell_pipe_broken(PipeSide* pipe, void* context);
|
||||
|
||||
static void cli_shell_install_pipe(CliShell* cli_shell) {
|
||||
pipe_install_as_stdio(cli_shell->pipe);
|
||||
pipe_attach_to_event_loop(cli_shell->pipe, cli_shell->event_loop);
|
||||
pipe_set_callback_context(cli_shell->pipe, cli_shell);
|
||||
pipe_set_data_arrived_callback(cli_shell->pipe, cli_shell_data_available, 0);
|
||||
pipe_set_broken_callback(cli_shell->pipe, cli_shell_pipe_broken, 0);
|
||||
}
|
||||
|
||||
static void cli_shell_detach_pipe(CliShell* cli_shell) {
|
||||
pipe_detach_from_event_loop(cli_shell->pipe);
|
||||
furi_thread_set_stdin_callback(NULL, NULL);
|
||||
furi_thread_set_stdout_callback(NULL, NULL);
|
||||
}
|
||||
|
||||
// =================
|
||||
// Built-in commands
|
||||
// =================
|
||||
|
||||
void cli_command_reload_external(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
CliShell* shell = context;
|
||||
furi_check(shell->ext_config);
|
||||
cli_registry_reload_external_commands(shell->registry, shell->ext_config);
|
||||
printf("OK!");
|
||||
}
|
||||
|
||||
void cli_command_help(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
CliShell* shell = context;
|
||||
CliRegistry* registry = shell->registry;
|
||||
|
||||
const size_t columns = 3;
|
||||
|
||||
printf("Available commands:\r\n" ANSI_FG_GREEN);
|
||||
cli_registry_lock(registry);
|
||||
CliCommandTree_t* commands = cli_registry_get_commands(registry);
|
||||
size_t commands_count = CliCommandTree_size(*commands);
|
||||
|
||||
CliCommandTree_it_t iterator;
|
||||
CliCommandTree_it(iterator, *commands);
|
||||
for(size_t i = 0; i < commands_count; i++) {
|
||||
const CliCommandTree_itref_t* item = CliCommandTree_cref(iterator);
|
||||
printf("%-30s", furi_string_get_cstr(*item->key_ptr));
|
||||
CliCommandTree_next(iterator);
|
||||
|
||||
if(i % columns == columns - 1) printf("\r\n");
|
||||
}
|
||||
|
||||
if(shell->ext_config)
|
||||
printf(
|
||||
ANSI_RESET
|
||||
"\r\nIf you added a new external command and can't see it above, run `reload_ext_cmds`");
|
||||
printf(ANSI_RESET "\r\nFind out more: https://docs.flipper.net/development/cli");
|
||||
|
||||
cli_registry_unlock(registry);
|
||||
}
|
||||
|
||||
void cli_command_exit(PipeSide* pipe, FuriString* args, void* context) {
|
||||
UNUSED(pipe);
|
||||
UNUSED(args);
|
||||
CliShell* shell = context;
|
||||
cli_shell_line_set_about_to_exit(shell->components[CliShellComponentLine]);
|
||||
furi_event_loop_stop(shell->event_loop);
|
||||
}
|
||||
|
||||
// ==================
|
||||
// Internal functions
|
||||
// ==================
|
||||
|
||||
static int32_t cli_command_thread(void* context) {
|
||||
CliCommandThreadData* thread_data = context;
|
||||
if(!(thread_data->command->flags & CliCommandFlagDontAttachStdio))
|
||||
pipe_install_as_stdio(thread_data->pipe);
|
||||
|
||||
thread_data->command->execute_callback(
|
||||
thread_data->pipe, thread_data->args, thread_data->command->context);
|
||||
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) {
|
||||
// split command into command and args
|
||||
size_t space = furi_string_search_char(command, ' ');
|
||||
if(space == FURI_STRING_FAILURE) space = furi_string_size(command);
|
||||
FuriString* command_name = furi_string_alloc_set(command);
|
||||
furi_string_left(command_name, space);
|
||||
FuriString* args = furi_string_alloc_set(command);
|
||||
furi_string_right(args, space + 1);
|
||||
|
||||
PluginManager* plugin_manager = NULL;
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
bool loader_locked = false;
|
||||
CliRegistryCommand command_data;
|
||||
|
||||
do {
|
||||
// find handler
|
||||
if(!cli_registry_get_command(cli_shell->registry, command_name, &command_data)) {
|
||||
printf(
|
||||
ANSI_FG_RED "could not find command `%s`, try `help`" ANSI_RESET,
|
||||
furi_string_get_cstr(command_name));
|
||||
break;
|
||||
}
|
||||
|
||||
// load external command
|
||||
if(command_data.flags & CliCommandFlagExternal) {
|
||||
const CliCommandExternalConfig* ext_config = cli_shell->ext_config;
|
||||
plugin_manager = plugin_manager_alloc(
|
||||
ext_config->appid, CLI_PLUGIN_API_VERSION, firmware_api_interface);
|
||||
FuriString* path = furi_string_alloc_printf(
|
||||
"%s/%s%s.fal",
|
||||
ext_config->search_directory,
|
||||
ext_config->fal_prefix,
|
||||
furi_string_get_cstr(command_name));
|
||||
uint32_t plugin_cnt_last = plugin_manager_get_count(plugin_manager);
|
||||
PluginManagerError error =
|
||||
plugin_manager_load_single(plugin_manager, furi_string_get_cstr(path));
|
||||
furi_string_free(path);
|
||||
|
||||
if(error != PluginManagerErrorNone) {
|
||||
printf(ANSI_FG_RED "failed to load external command" ANSI_RESET);
|
||||
break;
|
||||
}
|
||||
|
||||
const CliCommandDescriptor* plugin =
|
||||
plugin_manager_get_ep(plugin_manager, plugin_cnt_last);
|
||||
furi_assert(plugin);
|
||||
furi_check(furi_string_cmp_str(command_name, plugin->name) == 0);
|
||||
command_data.execute_callback = plugin->execute_callback;
|
||||
command_data.flags = plugin->flags | CliCommandFlagExternal;
|
||||
command_data.stack_depth = plugin->stack_depth;
|
||||
|
||||
// external commands have to run in an external thread
|
||||
furi_check(!(command_data.flags & CliCommandFlagUseShellThread));
|
||||
}
|
||||
|
||||
// lock loader
|
||||
if(!(command_data.flags & CliCommandFlagParallelSafe)) {
|
||||
loader_locked = loader_lock(loader);
|
||||
if(!loader_locked) {
|
||||
printf(ANSI_FG_RED
|
||||
"this command cannot be run while an application is open" ANSI_RESET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(command_data.flags & CliCommandFlagUseShellThread) {
|
||||
// run command in this thread
|
||||
command_data.execute_callback(cli_shell->pipe, args, command_data.context);
|
||||
} else {
|
||||
// run command in separate thread
|
||||
cli_shell_detach_pipe(cli_shell);
|
||||
CliCommandThreadData thread_data = {
|
||||
.command = &command_data,
|
||||
.pipe = cli_shell->pipe,
|
||||
.args = args,
|
||||
};
|
||||
FuriThread* thread = furi_thread_alloc_ex(
|
||||
furi_string_get_cstr(command_name),
|
||||
command_data.stack_depth,
|
||||
cli_command_thread,
|
||||
&thread_data);
|
||||
furi_thread_start(thread);
|
||||
furi_thread_join(thread);
|
||||
furi_thread_free(thread);
|
||||
cli_shell_install_pipe(cli_shell);
|
||||
}
|
||||
} while(0);
|
||||
|
||||
furi_string_free(command_name);
|
||||
furi_string_free(args);
|
||||
|
||||
// unlock loader
|
||||
if(loader_locked) loader_unlock(loader);
|
||||
furi_record_close(RECORD_LOADER);
|
||||
|
||||
// unload external command
|
||||
if(plugin_manager) plugin_manager_free(plugin_manager);
|
||||
}
|
||||
|
||||
const char* cli_shell_get_prompt(CliShell* cli_shell) {
|
||||
return cli_shell->prompt;
|
||||
}
|
||||
|
||||
// ==============
|
||||
// Event handlers
|
||||
// ==============
|
||||
|
||||
static void cli_shell_storage_event(const void* message, void* context) {
|
||||
CliShell* cli_shell = context;
|
||||
const StorageEvent* event = message;
|
||||
|
||||
if(event->type == StorageEventTypeCardMount) {
|
||||
CliShellStorageEvent cli_event = CliShellStorageEventMount;
|
||||
furi_check(
|
||||
furi_message_queue_put(cli_shell->storage_event_queue, &cli_event, 0) == FuriStatusOk);
|
||||
} else if(event->type == StorageEventTypeCardUnmount) {
|
||||
CliShellStorageEvent cli_event = CliShellStorageEventUnmount;
|
||||
furi_check(
|
||||
furi_message_queue_put(cli_shell->storage_event_queue, &cli_event, 0) == FuriStatusOk);
|
||||
}
|
||||
}
|
||||
|
||||
static void cli_shell_storage_internal_event(FuriEventLoopObject* object, void* context) {
|
||||
CliShell* cli_shell = context;
|
||||
FuriMessageQueue* queue = object;
|
||||
CliShellStorageEvent event;
|
||||
furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);
|
||||
|
||||
if(event == CliShellStorageEventMount) {
|
||||
cli_registry_reload_external_commands(cli_shell->registry, cli_shell->ext_config);
|
||||
} else if(event == CliShellStorageEventUnmount) {
|
||||
cli_registry_remove_external_commands(cli_shell->registry);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cli_shell_process_parser_result(CliShell* cli_shell, CliAnsiParserResult parse_result) {
|
||||
if(!parse_result.is_done) return;
|
||||
CliKeyCombo key_combo = parse_result.result;
|
||||
if(key_combo.key == CliKeyUnrecognized) return;
|
||||
|
||||
for(size_t i = 0; i < CliShellComponentMAX; i++) { // -V1008
|
||||
CliShellKeyComboSet* set = component_key_combo_sets[i];
|
||||
void* component_context = cli_shell->components[i];
|
||||
|
||||
for(size_t j = 0; j < set->count; j++) {
|
||||
if(set->records[j].combo.modifiers == key_combo.modifiers &&
|
||||
set->records[j].combo.key == key_combo.key)
|
||||
if(set->records[j].action(key_combo, component_context)) return;
|
||||
}
|
||||
|
||||
if(set->fallback)
|
||||
if(set->fallback(key_combo, component_context)) return;
|
||||
}
|
||||
}
|
||||
|
||||
static void cli_shell_pipe_broken(PipeSide* pipe, void* context) {
|
||||
// allow commands to be processed before we stop the shell
|
||||
if(pipe_bytes_available(pipe)) return;
|
||||
|
||||
CliShell* cli_shell = context;
|
||||
furi_event_loop_stop(cli_shell->event_loop);
|
||||
}
|
||||
|
||||
static void cli_shell_data_available(PipeSide* pipe, void* context) {
|
||||
UNUSED(pipe);
|
||||
CliShell* cli_shell = context;
|
||||
|
||||
furi_event_loop_timer_start(cli_shell->ansi_parsing_timer, furi_ms_to_ticks(ANSI_TIMEOUT_MS));
|
||||
|
||||
// process ANSI escape sequences
|
||||
int c = getchar();
|
||||
furi_assert(c >= 0);
|
||||
cli_shell_process_parser_result(cli_shell, cli_ansi_parser_feed(cli_shell->ansi_parser, c));
|
||||
}
|
||||
|
||||
static void cli_shell_timer_expired(void* context) {
|
||||
CliShell* cli_shell = context;
|
||||
cli_shell_process_parser_result(
|
||||
cli_shell, cli_ansi_parser_feed_timeout(cli_shell->ansi_parser));
|
||||
}
|
||||
|
||||
// ===========
|
||||
// Thread code
|
||||
// ===========
|
||||
|
||||
static void cli_shell_init(CliShell* shell) {
|
||||
cli_registry_add_command(
|
||||
shell->registry,
|
||||
"help",
|
||||
CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,
|
||||
cli_command_help,
|
||||
shell);
|
||||
cli_registry_add_command(
|
||||
shell->registry,
|
||||
"?",
|
||||
CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,
|
||||
cli_command_help,
|
||||
shell);
|
||||
cli_registry_add_command(
|
||||
shell->registry,
|
||||
"exit",
|
||||
CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,
|
||||
cli_command_exit,
|
||||
shell);
|
||||
|
||||
if(shell->ext_config) {
|
||||
cli_registry_add_command(
|
||||
shell->registry,
|
||||
"reload_ext_cmds",
|
||||
CliCommandFlagUseShellThread,
|
||||
cli_command_reload_external,
|
||||
shell);
|
||||
cli_registry_reload_external_commands(shell->registry, shell->ext_config);
|
||||
}
|
||||
|
||||
shell->components[CliShellComponentLine] = cli_shell_line_alloc(shell);
|
||||
shell->components[CliShellComponentCompletions] = cli_shell_completions_alloc(
|
||||
shell->registry, shell, shell->components[CliShellComponentLine]);
|
||||
|
||||
shell->ansi_parser = cli_ansi_parser_alloc();
|
||||
|
||||
shell->event_loop = furi_event_loop_alloc();
|
||||
shell->ansi_parsing_timer = furi_event_loop_timer_alloc(
|
||||
shell->event_loop, cli_shell_timer_expired, FuriEventLoopTimerTypeOnce, shell);
|
||||
|
||||
shell->storage_event_queue = furi_message_queue_alloc(1, sizeof(CliShellStorageEvent));
|
||||
furi_event_loop_subscribe_message_queue(
|
||||
shell->event_loop,
|
||||
shell->storage_event_queue,
|
||||
FuriEventLoopEventIn,
|
||||
cli_shell_storage_internal_event,
|
||||
shell);
|
||||
shell->storage = furi_record_open(RECORD_STORAGE);
|
||||
shell->storage_subscription =
|
||||
furi_pubsub_subscribe(storage_get_pubsub(shell->storage), cli_shell_storage_event, shell);
|
||||
|
||||
cli_shell_install_pipe(shell);
|
||||
}
|
||||
|
||||
static void cli_shell_deinit(CliShell* shell) {
|
||||
furi_pubsub_unsubscribe(storage_get_pubsub(shell->storage), shell->storage_subscription);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
furi_event_loop_unsubscribe(shell->event_loop, shell->storage_event_queue);
|
||||
furi_message_queue_free(shell->storage_event_queue);
|
||||
|
||||
cli_shell_completions_free(shell->components[CliShellComponentCompletions]);
|
||||
cli_shell_line_free(shell->components[CliShellComponentLine]);
|
||||
|
||||
cli_shell_detach_pipe(shell);
|
||||
furi_event_loop_timer_free(shell->ansi_parsing_timer);
|
||||
furi_event_loop_free(shell->event_loop);
|
||||
cli_ansi_parser_free(shell->ansi_parser);
|
||||
}
|
||||
|
||||
static int32_t cli_shell_thread(void* context) {
|
||||
CliShell* shell = context;
|
||||
|
||||
// Sometimes, the other side closes the pipe even before our thread is started. Although the
|
||||
// rest of the code will eventually find this out if this check is removed, there's no point in
|
||||
// wasting time.
|
||||
if(pipe_state(shell->pipe) == PipeStateBroken) return 0;
|
||||
|
||||
cli_shell_init(shell);
|
||||
FURI_LOG_D(TAG, "Started");
|
||||
|
||||
shell->motd(shell->callback_context);
|
||||
cli_shell_line_prompt(shell->components[CliShellComponentLine]);
|
||||
|
||||
furi_event_loop_run(shell->event_loop);
|
||||
|
||||
FURI_LOG_D(TAG, "Stopped");
|
||||
cli_shell_deinit(shell);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ==========
|
||||
// Public API
|
||||
// ==========
|
||||
|
||||
CliShell* cli_shell_alloc(
|
||||
CliShellMotd motd,
|
||||
void* context,
|
||||
PipeSide* pipe,
|
||||
CliRegistry* registry,
|
||||
const CliCommandExternalConfig* ext_config) {
|
||||
furi_check(motd);
|
||||
furi_check(pipe);
|
||||
furi_check(registry);
|
||||
|
||||
CliShell* shell = malloc(sizeof(CliShell));
|
||||
*shell = (CliShell){
|
||||
.motd = motd,
|
||||
.callback_context = context,
|
||||
.pipe = pipe,
|
||||
.registry = registry,
|
||||
.ext_config = ext_config,
|
||||
};
|
||||
|
||||
shell->thread =
|
||||
furi_thread_alloc_ex("CliShell", CLI_SHELL_STACK_SIZE, cli_shell_thread, shell);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
void cli_shell_free(CliShell* shell) {
|
||||
furi_check(shell);
|
||||
furi_thread_free(shell->thread);
|
||||
free(shell);
|
||||
}
|
||||
|
||||
void cli_shell_start(CliShell* shell) {
|
||||
furi_check(shell);
|
||||
furi_thread_start(shell->thread);
|
||||
}
|
||||
|
||||
void cli_shell_join(CliShell* shell) {
|
||||
furi_check(shell);
|
||||
furi_thread_join(shell->thread);
|
||||
}
|
||||
|
||||
void cli_shell_set_prompt(CliShell* shell, const char* prompt) {
|
||||
furi_check(shell);
|
||||
shell->prompt = prompt;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <toolbox/pipe.h>
|
||||
#include "../cli_registry.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CLI_SHELL_STACK_SIZE (4 * 1024U)
|
||||
|
||||
typedef struct CliShell CliShell;
|
||||
|
||||
/**
|
||||
* Called from the shell thread to print the Message of the Day when the shell
|
||||
* is started.
|
||||
*/
|
||||
typedef void (*CliShellMotd)(void* context);
|
||||
|
||||
/**
|
||||
* @brief Allocates a shell
|
||||
*
|
||||
* @param [in] motd Message of the Day callback
|
||||
* @param [in] context Callback context
|
||||
* @param [in] pipe Pipe side to be used by the shell
|
||||
* @param [in] registry Command registry
|
||||
* @param [in] ext_config External command configuration. See
|
||||
* `CliCommandExternalConfig`. May be NULL if support for
|
||||
* external commands is not required.
|
||||
*
|
||||
* @return Shell instance
|
||||
*/
|
||||
CliShell* cli_shell_alloc(
|
||||
CliShellMotd motd,
|
||||
void* context,
|
||||
PipeSide* pipe,
|
||||
CliRegistry* registry,
|
||||
const CliCommandExternalConfig* ext_config);
|
||||
|
||||
/**
|
||||
* @brief Frees a shell
|
||||
*
|
||||
* @param [in] shell Shell instance
|
||||
*/
|
||||
void cli_shell_free(CliShell* shell);
|
||||
|
||||
/**
|
||||
* @brief Starts a shell
|
||||
*
|
||||
* The shell runs in a separate thread. This call is non-blocking.
|
||||
*
|
||||
* @param [in] shell Shell instance
|
||||
*/
|
||||
void cli_shell_start(CliShell* shell);
|
||||
|
||||
/**
|
||||
* @brief Joins the shell thread
|
||||
*
|
||||
* @warning This call is blocking.
|
||||
*
|
||||
* @param [in] shell Shell instance
|
||||
*/
|
||||
void cli_shell_join(CliShell* shell);
|
||||
|
||||
/**
|
||||
* @brief Sets optional text before prompt (`>:`)
|
||||
*
|
||||
* @param [in] shell Shell instance
|
||||
*/
|
||||
void cli_shell_set_prompt(CliShell* shell, const char* prompt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,366 @@
|
||||
#include "cli_shell_completions.h"
|
||||
|
||||
ARRAY_DEF(CommandCompletions, FuriString*, FURI_STRING_OPLIST); // -V524
|
||||
#define M_OPL_CommandCompletions_t() ARRAY_OPLIST(CommandCompletions)
|
||||
|
||||
struct CliShellCompletions {
|
||||
CliRegistry* registry;
|
||||
CliShell* shell;
|
||||
CliShellLine* line;
|
||||
CommandCompletions_t variants;
|
||||
size_t selected;
|
||||
bool is_displaying;
|
||||
};
|
||||
|
||||
#define COMPLETION_COLUMNS 3
|
||||
#define COMPLETION_COLUMN_WIDTH "30"
|
||||
#define COMPLETION_COLUMN_WIDTH_I 30
|
||||
|
||||
/**
|
||||
* @brief Update for the completions menu
|
||||
*/
|
||||
typedef enum {
|
||||
CliShellCompletionsActionOpen,
|
||||
CliShellCompletionsActionClose,
|
||||
CliShellCompletionsActionUp,
|
||||
CliShellCompletionsActionDown,
|
||||
CliShellCompletionsActionLeft,
|
||||
CliShellCompletionsActionRight,
|
||||
CliShellCompletionsActionSelect,
|
||||
CliShellCompletionsActionSelectNoClose,
|
||||
} CliShellCompletionsAction;
|
||||
|
||||
typedef enum {
|
||||
CliShellCompletionSegmentTypeCommand,
|
||||
CliShellCompletionSegmentTypeArguments,
|
||||
} CliShellCompletionSegmentType;
|
||||
|
||||
typedef struct {
|
||||
CliShellCompletionSegmentType type;
|
||||
size_t start;
|
||||
size_t length;
|
||||
} CliShellCompletionSegment;
|
||||
|
||||
// ==========
|
||||
// Public API
|
||||
// ==========
|
||||
|
||||
CliShellCompletions*
|
||||
cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line) {
|
||||
CliShellCompletions* completions = malloc(sizeof(CliShellCompletions));
|
||||
|
||||
completions->registry = registry;
|
||||
completions->shell = shell;
|
||||
completions->line = line;
|
||||
CommandCompletions_init(completions->variants);
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
void cli_shell_completions_free(CliShellCompletions* completions) {
|
||||
CommandCompletions_clear(completions->variants);
|
||||
free(completions);
|
||||
}
|
||||
|
||||
// =======
|
||||
// Helpers
|
||||
// =======
|
||||
|
||||
CliShellCompletionSegment cli_shell_completions_segment(CliShellCompletions* completions) {
|
||||
furi_assert(completions);
|
||||
CliShellCompletionSegment segment;
|
||||
|
||||
FuriString* input = furi_string_alloc_set(cli_shell_line_get_editing(completions->line));
|
||||
furi_string_left(input, cli_shell_line_get_line_position(completions->line));
|
||||
|
||||
// find index of first non-space character
|
||||
size_t first_non_space = 0;
|
||||
while(1) {
|
||||
size_t ret = furi_string_search_char(input, ' ', first_non_space);
|
||||
if(ret == FURI_STRING_FAILURE) break;
|
||||
if(ret - first_non_space > 1) break;
|
||||
first_non_space++;
|
||||
}
|
||||
|
||||
size_t first_space_in_command = furi_string_search_char(input, ' ', first_non_space);
|
||||
|
||||
if(first_space_in_command == FURI_STRING_FAILURE) {
|
||||
segment.type = CliShellCompletionSegmentTypeCommand;
|
||||
segment.start = first_non_space;
|
||||
segment.length = furi_string_size(input) - first_non_space;
|
||||
} else {
|
||||
segment.type = CliShellCompletionSegmentTypeArguments;
|
||||
segment.start = 0;
|
||||
segment.length = 0;
|
||||
// support removed, might reimplement in the future
|
||||
}
|
||||
|
||||
furi_string_free(input);
|
||||
return segment;
|
||||
}
|
||||
|
||||
void cli_shell_completions_fill_variants(CliShellCompletions* completions) {
|
||||
furi_assert(completions);
|
||||
CommandCompletions_reset(completions->variants);
|
||||
|
||||
CliShellCompletionSegment segment = cli_shell_completions_segment(completions);
|
||||
FuriString* input = furi_string_alloc_set(cli_shell_line_get_editing(completions->line));
|
||||
furi_string_right(input, segment.start);
|
||||
furi_string_left(input, segment.length);
|
||||
|
||||
if(segment.type == CliShellCompletionSegmentTypeCommand) {
|
||||
CliRegistry* registry = completions->registry;
|
||||
cli_registry_lock(registry);
|
||||
CliCommandTree_t* commands = cli_registry_get_commands(registry);
|
||||
for
|
||||
M_EACH(registered_command, *commands, CliCommandTree_t) {
|
||||
FuriString* command_name = *registered_command->key_ptr;
|
||||
if(furi_string_start_with(command_name, input)) {
|
||||
CommandCompletions_push_back(completions->variants, command_name);
|
||||
}
|
||||
}
|
||||
cli_registry_unlock(registry);
|
||||
|
||||
} else {
|
||||
// support removed, might reimplement in the future
|
||||
}
|
||||
|
||||
furi_string_free(input);
|
||||
}
|
||||
|
||||
static size_t cli_shell_completions_rows_at_column(CliShellCompletions* completions, size_t x) {
|
||||
size_t completions_size = CommandCompletions_size(completions->variants);
|
||||
size_t n_full_rows = completions_size / COMPLETION_COLUMNS;
|
||||
size_t n_cols_in_last_row = completions_size % COMPLETION_COLUMNS;
|
||||
size_t n_rows_at_x = n_full_rows + ((x >= n_cols_in_last_row) ? 0 : 1);
|
||||
return n_rows_at_x;
|
||||
}
|
||||
|
||||
void cli_shell_completions_render(
|
||||
CliShellCompletions* completions,
|
||||
CliShellCompletionsAction action) {
|
||||
furi_assert(completions);
|
||||
if(action == CliShellCompletionsActionOpen) furi_check(!completions->is_displaying);
|
||||
if(action == CliShellCompletionsActionClose) furi_check(completions->is_displaying);
|
||||
|
||||
char prompt[64];
|
||||
cli_shell_line_format_prompt(completions->line, prompt, sizeof(prompt));
|
||||
|
||||
if(action == CliShellCompletionsActionOpen) {
|
||||
cli_shell_completions_fill_variants(completions);
|
||||
completions->selected = 0;
|
||||
|
||||
if(CommandCompletions_size(completions->variants) == 1) {
|
||||
cli_shell_completions_render(completions, CliShellCompletionsActionSelectNoClose);
|
||||
return;
|
||||
}
|
||||
|
||||
// show completions menu (full re-render)
|
||||
printf("\n\r");
|
||||
size_t position = 0;
|
||||
for
|
||||
M_EACH(completion, completions->variants, CommandCompletions_t) {
|
||||
if(position == completions->selected) printf(ANSI_INVERT);
|
||||
printf("%-" COMPLETION_COLUMN_WIDTH "s", furi_string_get_cstr(*completion));
|
||||
if(position == completions->selected) printf(ANSI_RESET);
|
||||
if((position % COMPLETION_COLUMNS == COMPLETION_COLUMNS - 1) &&
|
||||
position != CommandCompletions_size(completions->variants)) {
|
||||
printf("\r\n");
|
||||
}
|
||||
position++;
|
||||
}
|
||||
|
||||
if(!position) {
|
||||
printf(ANSI_FG_RED "no completions" ANSI_RESET);
|
||||
}
|
||||
|
||||
size_t total_rows = (position / COMPLETION_COLUMNS) + 1;
|
||||
printf(
|
||||
ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END) ANSI_CURSOR_UP_BY("%zu")
|
||||
ANSI_CURSOR_HOR_POS("%zu"),
|
||||
total_rows,
|
||||
strlen(prompt) + cli_shell_line_get_line_position(completions->line) + 1);
|
||||
|
||||
completions->is_displaying = true;
|
||||
|
||||
} else if(action == CliShellCompletionsActionClose) {
|
||||
// clear completions menu
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("%zu") ANSI_ERASE_DISPLAY(ANSI_ERASE_FROM_CURSOR_TO_END)
|
||||
ANSI_CURSOR_HOR_POS("%zu"),
|
||||
strlen(prompt) + furi_string_size(cli_shell_line_get_selected(completions->line)) + 1,
|
||||
strlen(prompt) + cli_shell_line_get_line_position(completions->line) + 1);
|
||||
completions->is_displaying = false;
|
||||
|
||||
} else if(
|
||||
action == CliShellCompletionsActionUp || action == CliShellCompletionsActionDown ||
|
||||
action == CliShellCompletionsActionLeft || action == CliShellCompletionsActionRight) {
|
||||
if(CommandCompletions_empty_p(completions->variants)) return;
|
||||
|
||||
// move selection
|
||||
size_t completions_size = CommandCompletions_size(completions->variants);
|
||||
size_t old_selection = completions->selected;
|
||||
int n_columns = (completions_size >= COMPLETION_COLUMNS) ? COMPLETION_COLUMNS :
|
||||
completions_size;
|
||||
int selection_unclamped = old_selection;
|
||||
if(action == CliShellCompletionsActionLeft) {
|
||||
selection_unclamped--;
|
||||
} else if(action == CliShellCompletionsActionRight) {
|
||||
selection_unclamped++;
|
||||
} else {
|
||||
int selection_x = old_selection % COMPLETION_COLUMNS;
|
||||
int selection_y_unclamped = old_selection / COMPLETION_COLUMNS;
|
||||
if(action == CliShellCompletionsActionUp) selection_y_unclamped--;
|
||||
if(action == CliShellCompletionsActionDown) selection_y_unclamped++;
|
||||
size_t selection_y = 0;
|
||||
if(selection_y_unclamped < 0) {
|
||||
selection_x = CLAMP_WRAPAROUND(selection_x - 1, n_columns - 1, 0);
|
||||
selection_y =
|
||||
cli_shell_completions_rows_at_column(completions, selection_x) - 1; // -V537
|
||||
} else if(
|
||||
(size_t)selection_y_unclamped >
|
||||
cli_shell_completions_rows_at_column(completions, selection_x) - 1) {
|
||||
selection_x = CLAMP_WRAPAROUND(selection_x + 1, n_columns - 1, 0);
|
||||
selection_y = 0;
|
||||
} else {
|
||||
selection_y = selection_y_unclamped;
|
||||
}
|
||||
selection_unclamped = (selection_y * COMPLETION_COLUMNS) + selection_x;
|
||||
}
|
||||
size_t new_selection = CLAMP_WRAPAROUND(selection_unclamped, (int)completions_size - 1, 0);
|
||||
completions->selected = new_selection;
|
||||
|
||||
if(new_selection != old_selection) {
|
||||
// determine selection coordinates relative to top-left of suggestion menu
|
||||
size_t old_x = (old_selection % COMPLETION_COLUMNS) * COMPLETION_COLUMN_WIDTH_I;
|
||||
size_t old_y = old_selection / COMPLETION_COLUMNS;
|
||||
size_t new_x = (new_selection % COMPLETION_COLUMNS) * COMPLETION_COLUMN_WIDTH_I;
|
||||
size_t new_y = new_selection / COMPLETION_COLUMNS;
|
||||
printf("\n\r");
|
||||
|
||||
// print old selection in normal colors
|
||||
if(old_y) printf(ANSI_CURSOR_DOWN_BY("%zu"), old_y);
|
||||
printf(ANSI_CURSOR_HOR_POS("%zu"), old_x + 1);
|
||||
printf(
|
||||
"%-" COMPLETION_COLUMN_WIDTH "s",
|
||||
furi_string_get_cstr(
|
||||
*CommandCompletions_cget(completions->variants, old_selection)));
|
||||
if(old_y) printf(ANSI_CURSOR_UP_BY("%zu"), old_y);
|
||||
printf(ANSI_CURSOR_HOR_POS("1"));
|
||||
|
||||
// print new selection in inverted colors
|
||||
if(new_y) printf(ANSI_CURSOR_DOWN_BY("%zu"), new_y);
|
||||
printf(ANSI_CURSOR_HOR_POS("%zu"), new_x + 1);
|
||||
printf(
|
||||
ANSI_INVERT "%-" COMPLETION_COLUMN_WIDTH "s" ANSI_RESET,
|
||||
furi_string_get_cstr(
|
||||
*CommandCompletions_cget(completions->variants, new_selection)));
|
||||
|
||||
// return cursor
|
||||
printf(ANSI_CURSOR_UP_BY("%zu"), new_y + 1);
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("%zu"),
|
||||
strlen(prompt) + furi_string_size(cli_shell_line_get_selected(completions->line)) +
|
||||
1);
|
||||
}
|
||||
|
||||
} else if(action == CliShellCompletionsActionSelectNoClose) {
|
||||
// insert selection into prompt
|
||||
CliShellCompletionSegment segment = cli_shell_completions_segment(completions);
|
||||
FuriString* input = cli_shell_line_get_selected(completions->line);
|
||||
FuriString* completion =
|
||||
*CommandCompletions_cget(completions->variants, completions->selected);
|
||||
furi_string_replace_at(
|
||||
input, segment.start, segment.length, furi_string_get_cstr(completion));
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("%zu") "%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),
|
||||
strlen(prompt) + 1,
|
||||
furi_string_get_cstr(input));
|
||||
|
||||
int position_change = (int)furi_string_size(completion) - (int)segment.length;
|
||||
cli_shell_line_set_line_position(
|
||||
completions->line,
|
||||
MAX(0, (int)cli_shell_line_get_line_position(completions->line) + position_change));
|
||||
|
||||
} else if(action == CliShellCompletionsActionSelect) {
|
||||
cli_shell_completions_render(completions, CliShellCompletionsActionSelectNoClose);
|
||||
cli_shell_completions_render(completions, CliShellCompletionsActionClose);
|
||||
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
// ==============
|
||||
// Input handlers
|
||||
// ==============
|
||||
|
||||
static bool hide_if_open_and_continue_handling(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellCompletions* completions = context;
|
||||
if(completions->is_displaying)
|
||||
cli_shell_completions_render(completions, CliShellCompletionsActionClose);
|
||||
return false; // process other home events
|
||||
}
|
||||
|
||||
static bool key_combo_cr(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellCompletions* completions = context;
|
||||
if(!completions->is_displaying) return false;
|
||||
cli_shell_completions_render(completions, CliShellCompletionsActionSelect);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool key_combo_up_down(CliKeyCombo combo, void* context) {
|
||||
CliShellCompletions* completions = context;
|
||||
if(!completions->is_displaying) return false;
|
||||
cli_shell_completions_render(
|
||||
completions,
|
||||
(combo.key == CliKeyUp) ? CliShellCompletionsActionUp : CliShellCompletionsActionDown);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool key_combo_left_right(CliKeyCombo combo, void* context) {
|
||||
CliShellCompletions* completions = context;
|
||||
if(!completions->is_displaying) return false;
|
||||
cli_shell_completions_render(
|
||||
completions,
|
||||
(combo.key == CliKeyLeft) ? CliShellCompletionsActionLeft :
|
||||
CliShellCompletionsActionRight);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool key_combo_tab(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellCompletions* completions = context;
|
||||
cli_shell_completions_render(
|
||||
completions,
|
||||
completions->is_displaying ? CliShellCompletionsActionRight :
|
||||
CliShellCompletionsActionOpen);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool key_combo_esc(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellCompletions* completions = context;
|
||||
if(!completions->is_displaying) return false;
|
||||
cli_shell_completions_render(completions, CliShellCompletionsActionClose);
|
||||
return true;
|
||||
}
|
||||
|
||||
CliShellKeyComboSet cli_shell_completions_key_combo_set = {
|
||||
.fallback = hide_if_open_and_continue_handling,
|
||||
.count = 7,
|
||||
.records =
|
||||
{
|
||||
{{CliModKeyNo, CliKeyCR}, key_combo_cr},
|
||||
{{CliModKeyNo, CliKeyUp}, key_combo_up_down},
|
||||
{{CliModKeyNo, CliKeyDown}, key_combo_up_down},
|
||||
{{CliModKeyNo, CliKeyLeft}, key_combo_left_right},
|
||||
{{CliModKeyNo, CliKeyRight}, key_combo_left_right},
|
||||
{{CliModKeyNo, CliKeyTab}, key_combo_tab},
|
||||
{{CliModKeyNo, CliKeyEsc}, key_combo_esc},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <m-array.h>
|
||||
#include "cli_shell_i.h"
|
||||
#include "cli_shell_line.h"
|
||||
#include "../cli_registry.h"
|
||||
#include "../cli_registry_i.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct CliShellCompletions CliShellCompletions;
|
||||
|
||||
CliShellCompletions*
|
||||
cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line);
|
||||
|
||||
void cli_shell_completions_free(CliShellCompletions* completions);
|
||||
|
||||
extern CliShellKeyComboSet cli_shell_completions_key_combo_set;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "../cli_ansi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct CliShell CliShell;
|
||||
|
||||
/**
|
||||
* @brief Key combo handler
|
||||
* @return true if the event was handled, false otherwise
|
||||
*/
|
||||
typedef bool (*CliShellKeyComboAction)(CliKeyCombo combo, void* context);
|
||||
|
||||
typedef struct {
|
||||
CliKeyCombo combo;
|
||||
CliShellKeyComboAction action;
|
||||
} CliShellKeyComboRecord;
|
||||
|
||||
typedef struct {
|
||||
CliShellKeyComboAction fallback;
|
||||
size_t count;
|
||||
CliShellKeyComboRecord records[];
|
||||
} CliShellKeyComboSet;
|
||||
|
||||
void cli_shell_execute_command(CliShell* cli_shell, FuriString* command);
|
||||
|
||||
const char* cli_shell_get_prompt(CliShell* cli_shell);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,378 @@
|
||||
#include "cli_shell_line.h"
|
||||
|
||||
#define HISTORY_DEPTH 10
|
||||
|
||||
struct CliShellLine {
|
||||
size_t history_position;
|
||||
size_t line_position;
|
||||
FuriString* history[HISTORY_DEPTH];
|
||||
size_t history_entries;
|
||||
CliShell* shell;
|
||||
bool about_to_exit;
|
||||
};
|
||||
|
||||
// ==========
|
||||
// Public API
|
||||
// ==========
|
||||
|
||||
CliShellLine* cli_shell_line_alloc(CliShell* shell) {
|
||||
CliShellLine* line = malloc(sizeof(CliShellLine));
|
||||
line->shell = shell;
|
||||
|
||||
line->history[0] = furi_string_alloc();
|
||||
line->history_entries = 1;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void cli_shell_line_free(CliShellLine* line) {
|
||||
for(size_t i = 0; i < line->history_entries; i++)
|
||||
furi_string_free(line->history[i]);
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
FuriString* cli_shell_line_get_selected(CliShellLine* line) {
|
||||
return line->history[line->history_position];
|
||||
}
|
||||
|
||||
FuriString* cli_shell_line_get_editing(CliShellLine* line) {
|
||||
return line->history[0];
|
||||
}
|
||||
|
||||
void cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length) {
|
||||
UNUSED(line);
|
||||
const char* prompt = cli_shell_get_prompt(line->shell);
|
||||
snprintf(buf, length - 1, "%s>: ", prompt ? prompt : "");
|
||||
}
|
||||
|
||||
size_t cli_shell_line_prompt_length(CliShellLine* line) {
|
||||
char buffer[128];
|
||||
cli_shell_line_format_prompt(line, buffer, sizeof(buffer));
|
||||
return strlen(buffer);
|
||||
}
|
||||
|
||||
void cli_shell_line_prompt(CliShellLine* line) {
|
||||
char buffer[32];
|
||||
cli_shell_line_format_prompt(line, buffer, sizeof(buffer));
|
||||
printf("\r\n%s", buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line) {
|
||||
if(line->history_position > 0) {
|
||||
FuriString* source = cli_shell_line_get_selected(line);
|
||||
FuriString* destination = cli_shell_line_get_editing(line);
|
||||
furi_string_set(destination, source);
|
||||
line->history_position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cli_shell_line_set_about_to_exit(CliShellLine* line) {
|
||||
line->about_to_exit = true;
|
||||
}
|
||||
|
||||
size_t cli_shell_line_get_line_position(CliShellLine* line) {
|
||||
return line->line_position;
|
||||
}
|
||||
|
||||
void cli_shell_line_set_line_position(CliShellLine* line, size_t position) {
|
||||
line->line_position = position;
|
||||
}
|
||||
|
||||
// =======
|
||||
// Helpers
|
||||
// =======
|
||||
|
||||
typedef enum {
|
||||
CliCharClassWord,
|
||||
CliCharClassSpace,
|
||||
CliCharClassOther,
|
||||
} CliCharClass;
|
||||
|
||||
typedef enum {
|
||||
CliSkipDirectionLeft,
|
||||
CliSkipDirectionRight,
|
||||
} CliSkipDirection;
|
||||
|
||||
CliCharClass cli_shell_line_char_class(char c) {
|
||||
if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') {
|
||||
return CliCharClassWord;
|
||||
} else if(c == ' ') {
|
||||
return CliCharClassSpace;
|
||||
} else {
|
||||
return CliCharClassOther;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
cli_shell_line_skip_run(FuriString* string, size_t original_pos, CliSkipDirection direction) {
|
||||
if(furi_string_size(string) == 0) return original_pos;
|
||||
if(direction == CliSkipDirectionLeft && original_pos == 0) return original_pos;
|
||||
if(direction == CliSkipDirectionRight && original_pos == furi_string_size(string))
|
||||
return original_pos;
|
||||
|
||||
int8_t look_offset = (direction == CliSkipDirectionLeft) ? -1 : 0;
|
||||
int8_t increment = (direction == CliSkipDirectionLeft) ? -1 : 1;
|
||||
int32_t position = original_pos;
|
||||
CliCharClass start_class =
|
||||
cli_shell_line_char_class(furi_string_get_char(string, position + look_offset));
|
||||
|
||||
while(true) {
|
||||
position += increment;
|
||||
if(position < 0) break;
|
||||
if(position >= (int32_t)furi_string_size(string)) break;
|
||||
if(cli_shell_line_char_class(furi_string_get_char(string, position + look_offset)) !=
|
||||
start_class)
|
||||
break;
|
||||
}
|
||||
|
||||
return MAX(0, position);
|
||||
}
|
||||
|
||||
// ==============
|
||||
// Input handlers
|
||||
// ==============
|
||||
|
||||
static bool cli_shell_line_input_ctrl_c(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
// reset input
|
||||
furi_string_reset(cli_shell_line_get_editing(line));
|
||||
line->line_position = 0;
|
||||
line->history_position = 0;
|
||||
printf("^C");
|
||||
cli_shell_line_prompt(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_cr(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
|
||||
FuriString* command = cli_shell_line_get_selected(line);
|
||||
furi_string_trim(command);
|
||||
if(furi_string_empty(command)) {
|
||||
cli_shell_line_prompt(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
FuriString* command_copy = furi_string_alloc_set(command);
|
||||
|
||||
if(line->history_position == 0) {
|
||||
for(size_t i = 1; i < line->history_entries; i++) {
|
||||
if(furi_string_cmp(line->history[i], command) == 0) {
|
||||
line->history_position = i;
|
||||
command = cli_shell_line_get_selected(line);
|
||||
furi_string_trim(command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move selected command to the front
|
||||
if(line->history_position > 0) {
|
||||
size_t pos = line->history_position;
|
||||
size_t len = line->history_entries;
|
||||
memmove(
|
||||
&line->history[pos], &line->history[pos + 1], (len - pos - 1) * sizeof(FuriString*));
|
||||
furi_string_move(line->history[0], command);
|
||||
line->history_entries--;
|
||||
}
|
||||
|
||||
// insert empty command
|
||||
if(line->history_entries == HISTORY_DEPTH) {
|
||||
furi_string_free(line->history[HISTORY_DEPTH - 1]);
|
||||
line->history_entries--;
|
||||
}
|
||||
memmove(&line->history[1], &line->history[0], line->history_entries * sizeof(FuriString*));
|
||||
line->history[0] = furi_string_alloc();
|
||||
line->history_entries++;
|
||||
line->line_position = 0;
|
||||
line->history_position = 0;
|
||||
|
||||
// execute command
|
||||
printf("\r\n");
|
||||
cli_shell_execute_command(line->shell, command_copy);
|
||||
furi_string_free(command_copy);
|
||||
if(!line->about_to_exit) cli_shell_line_prompt(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_up_down(CliKeyCombo combo, void* context) {
|
||||
CliShellLine* line = context;
|
||||
// go up and down in history
|
||||
int increment = (combo.key == CliKeyUp) ? 1 : -1;
|
||||
size_t new_pos =
|
||||
CLAMP((int)line->history_position + increment, (int)line->history_entries - 1, 0);
|
||||
|
||||
// print prompt with selected command
|
||||
if(new_pos != line->history_position) {
|
||||
char prompt[64];
|
||||
cli_shell_line_format_prompt(line, prompt, sizeof(prompt));
|
||||
line->history_position = new_pos;
|
||||
FuriString* command = cli_shell_line_get_selected(line);
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("1") "%s%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),
|
||||
prompt,
|
||||
furi_string_get_cstr(command));
|
||||
fflush(stdout);
|
||||
line->line_position = furi_string_size(command);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_left_right(CliKeyCombo combo, void* context) {
|
||||
CliShellLine* line = context;
|
||||
// go left and right in the current line
|
||||
FuriString* command = cli_shell_line_get_selected(line);
|
||||
int increment = (combo.key == CliKeyRight) ? 1 : -1;
|
||||
size_t new_pos =
|
||||
CLAMP((int)line->line_position + increment, (int)furi_string_size(command), 0);
|
||||
|
||||
// move cursor
|
||||
if(new_pos != line->line_position) {
|
||||
line->line_position = new_pos;
|
||||
printf("%s", (increment == 1) ? ANSI_CURSOR_RIGHT_BY("1") : ANSI_CURSOR_LEFT_BY("1"));
|
||||
fflush(stdout);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_home(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
// go to the start
|
||||
line->line_position = 0;
|
||||
printf(ANSI_CURSOR_HOR_POS("%zu"), cli_shell_line_prompt_length(line) + 1);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_end(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
// go to the end
|
||||
line->line_position = furi_string_size(cli_shell_line_get_selected(line));
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("%zu"), cli_shell_line_prompt_length(line) + line->line_position + 1);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_bksp(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
// erase one character
|
||||
cli_shell_line_ensure_not_overwriting_history(line);
|
||||
FuriString* editing_line = cli_shell_line_get_editing(line);
|
||||
if(line->line_position == 0) {
|
||||
putc(CliKeyBell, stdout);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
line->line_position--;
|
||||
furi_string_replace_at(editing_line, line->line_position, 1, "");
|
||||
|
||||
// move cursor, print the rest of the line, restore cursor
|
||||
printf(
|
||||
ANSI_CURSOR_LEFT_BY("1") "%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),
|
||||
furi_string_get_cstr(editing_line) + line->line_position);
|
||||
size_t left_by = furi_string_size(editing_line) - line->line_position;
|
||||
if(left_by) // apparently LEFT_BY("0") still shifts left by one ._ .
|
||||
printf(ANSI_CURSOR_LEFT_BY("%zu"), left_by);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_ctrl_l(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
// clear screen
|
||||
FuriString* command = cli_shell_line_get_selected(line);
|
||||
char prompt[64];
|
||||
cli_shell_line_format_prompt(line, prompt, sizeof(prompt));
|
||||
printf(
|
||||
ANSI_ERASE_DISPLAY(ANSI_ERASE_ENTIRE) ANSI_ERASE_SCROLLBACK_BUFFER ANSI_CURSOR_POS(
|
||||
"1", "1") "%s%s" ANSI_CURSOR_HOR_POS("%zu"),
|
||||
prompt,
|
||||
furi_string_get_cstr(command),
|
||||
strlen(prompt) + line->line_position + 1 /* 1-based column indexing */);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_ctrl_left_right(CliKeyCombo combo, void* context) {
|
||||
CliShellLine* line = context;
|
||||
// skip run of similar chars to the left or right
|
||||
FuriString* selected_line = cli_shell_line_get_selected(line);
|
||||
CliSkipDirection direction = (combo.key == CliKeyLeft) ? CliSkipDirectionLeft :
|
||||
CliSkipDirectionRight;
|
||||
line->line_position = cli_shell_line_skip_run(selected_line, line->line_position, direction);
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("%zu"), cli_shell_line_prompt_length(line) + line->line_position + 1);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_ctrl_bksp(CliKeyCombo combo, void* context) {
|
||||
UNUSED(combo);
|
||||
CliShellLine* line = context;
|
||||
// delete run of similar chars to the left
|
||||
cli_shell_line_ensure_not_overwriting_history(line);
|
||||
FuriString* selected_line = cli_shell_line_get_selected(line);
|
||||
size_t run_start =
|
||||
cli_shell_line_skip_run(selected_line, line->line_position, CliSkipDirectionLeft);
|
||||
furi_string_replace_at(selected_line, run_start, line->line_position - run_start, "");
|
||||
line->line_position = run_start;
|
||||
printf(
|
||||
ANSI_CURSOR_HOR_POS("%zu") "%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END)
|
||||
ANSI_CURSOR_HOR_POS("%zu"),
|
||||
cli_shell_line_prompt_length(line) + line->line_position + 1,
|
||||
furi_string_get_cstr(selected_line) + run_start,
|
||||
cli_shell_line_prompt_length(line) + run_start + 1);
|
||||
fflush(stdout);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cli_shell_line_input_normal(CliKeyCombo combo, void* context) {
|
||||
CliShellLine* line = context;
|
||||
if(combo.modifiers != CliModKeyNo) return false;
|
||||
if(combo.key < CliKeySpace || combo.key >= CliKeyDEL) return false;
|
||||
// insert character
|
||||
cli_shell_line_ensure_not_overwriting_history(line);
|
||||
FuriString* editing_line = cli_shell_line_get_editing(line);
|
||||
if(line->line_position == furi_string_size(editing_line)) {
|
||||
furi_string_push_back(editing_line, combo.key);
|
||||
printf("%c", combo.key);
|
||||
} else {
|
||||
const char in_str[2] = {combo.key, 0};
|
||||
furi_string_replace_at(editing_line, line->line_position, 0, in_str);
|
||||
printf(ANSI_INSERT_MODE_ENABLE "%c" ANSI_INSERT_MODE_DISABLE, combo.key);
|
||||
}
|
||||
fflush(stdout);
|
||||
line->line_position++;
|
||||
return true;
|
||||
}
|
||||
|
||||
CliShellKeyComboSet cli_shell_line_key_combo_set = {
|
||||
.fallback = cli_shell_line_input_normal,
|
||||
.count = 14,
|
||||
.records =
|
||||
{
|
||||
{{CliModKeyNo, CliKeyETX}, cli_shell_line_input_ctrl_c},
|
||||
{{CliModKeyNo, CliKeyCR}, cli_shell_line_input_cr},
|
||||
{{CliModKeyNo, CliKeyUp}, cli_shell_line_input_up_down},
|
||||
{{CliModKeyNo, CliKeyDown}, cli_shell_line_input_up_down},
|
||||
{{CliModKeyNo, CliKeyLeft}, cli_shell_line_input_left_right},
|
||||
{{CliModKeyNo, CliKeyRight}, cli_shell_line_input_left_right},
|
||||
{{CliModKeyNo, CliKeyHome}, cli_shell_line_input_home},
|
||||
{{CliModKeyNo, CliKeyEnd}, cli_shell_line_input_end},
|
||||
{{CliModKeyNo, CliKeyBackspace}, cli_shell_line_input_bksp},
|
||||
{{CliModKeyNo, CliKeyDEL}, cli_shell_line_input_bksp},
|
||||
{{CliModKeyNo, CliKeyFF}, cli_shell_line_input_ctrl_l},
|
||||
{{CliModKeyCtrl, CliKeyLeft}, cli_shell_line_input_ctrl_left_right},
|
||||
{{CliModKeyCtrl, CliKeyRight}, cli_shell_line_input_ctrl_left_right},
|
||||
{{CliModKeyNo, CliKeyETB}, cli_shell_line_input_ctrl_bksp},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "cli_shell_i.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct CliShellLine CliShellLine;
|
||||
|
||||
CliShellLine* cli_shell_line_alloc(CliShell* shell);
|
||||
|
||||
void cli_shell_line_free(CliShellLine* line);
|
||||
|
||||
FuriString* cli_shell_line_get_selected(CliShellLine* line);
|
||||
|
||||
FuriString* cli_shell_line_get_editing(CliShellLine* line);
|
||||
|
||||
size_t cli_shell_line_prompt_length(CliShellLine* line);
|
||||
|
||||
void cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length);
|
||||
|
||||
void cli_shell_line_prompt(CliShellLine* line);
|
||||
|
||||
size_t cli_shell_line_get_line_position(CliShellLine* line);
|
||||
|
||||
void cli_shell_line_set_line_position(CliShellLine* line, size_t position);
|
||||
|
||||
/**
|
||||
* @brief If a line from history has been selected, moves it into the active line
|
||||
*/
|
||||
void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line);
|
||||
|
||||
void cli_shell_line_set_about_to_exit(CliShellLine* line);
|
||||
|
||||
extern CliShellKeyComboSet cli_shell_line_key_combo_set;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+44
-22
@@ -1,6 +1,8 @@
|
||||
#include "pipe.h"
|
||||
#include <furi.h>
|
||||
|
||||
#define PIPE_DEFAULT_STATE_CHECK_PERIOD furi_ms_to_ticks(100)
|
||||
|
||||
/**
|
||||
* Data shared between both sides.
|
||||
*/
|
||||
@@ -23,7 +25,7 @@ struct PipeSide {
|
||||
PipeSideDataArrivedCallback on_data_arrived;
|
||||
PipeSideSpaceFreedCallback on_space_freed;
|
||||
PipeSideBrokenCallback on_pipe_broken;
|
||||
FuriWait stdout_timeout;
|
||||
FuriWait state_check_period;
|
||||
};
|
||||
|
||||
PipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level) {
|
||||
@@ -53,14 +55,14 @@ PipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSetti
|
||||
.shared = shared,
|
||||
.sending = alice_to_bob,
|
||||
.receiving = bob_to_alice,
|
||||
.stdout_timeout = FuriWaitForever,
|
||||
.state_check_period = PIPE_DEFAULT_STATE_CHECK_PERIOD,
|
||||
};
|
||||
*bobs_side = (PipeSide){
|
||||
.role = PipeRoleBob,
|
||||
.shared = shared,
|
||||
.sending = bob_to_alice,
|
||||
.receiving = alice_to_bob,
|
||||
.stdout_timeout = FuriWaitForever,
|
||||
.state_check_period = PIPE_DEFAULT_STATE_CHECK_PERIOD,
|
||||
};
|
||||
|
||||
return (PipeSideBundle){.alices_side = alices_side, .bobs_side = bobs_side};
|
||||
@@ -99,42 +101,62 @@ void pipe_free(PipeSide* pipe) {
|
||||
}
|
||||
}
|
||||
|
||||
static void _pipe_stdout_cb(const char* data, size_t size, void* context) {
|
||||
static void pipe_stdout_cb(const char* data, size_t size, void* context) {
|
||||
furi_assert(context);
|
||||
PipeSide* pipe = context;
|
||||
while(size) {
|
||||
size_t sent = pipe_send(pipe, data, size, pipe->stdout_timeout);
|
||||
if(!sent) break;
|
||||
data += sent;
|
||||
size -= sent;
|
||||
}
|
||||
pipe_send(pipe, data, size);
|
||||
}
|
||||
|
||||
static size_t _pipe_stdin_cb(char* data, size_t size, FuriWait timeout, void* context) {
|
||||
static size_t pipe_stdin_cb(char* data, size_t size, FuriWait timeout, void* context) {
|
||||
UNUSED(timeout);
|
||||
furi_assert(context);
|
||||
PipeSide* pipe = context;
|
||||
return pipe_receive(pipe, data, size, timeout);
|
||||
return pipe_receive(pipe, data, size);
|
||||
}
|
||||
|
||||
void pipe_install_as_stdio(PipeSide* pipe) {
|
||||
furi_check(pipe);
|
||||
furi_thread_set_stdout_callback(_pipe_stdout_cb, pipe);
|
||||
furi_thread_set_stdin_callback(_pipe_stdin_cb, pipe);
|
||||
furi_thread_set_stdout_callback(pipe_stdout_cb, pipe);
|
||||
furi_thread_set_stdin_callback(pipe_stdin_cb, pipe);
|
||||
}
|
||||
|
||||
void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout) {
|
||||
void pipe_set_state_check_period(PipeSide* pipe, FuriWait check_period) {
|
||||
furi_check(pipe);
|
||||
pipe->stdout_timeout = timeout;
|
||||
pipe->state_check_period = check_period;
|
||||
}
|
||||
|
||||
size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout) {
|
||||
size_t pipe_receive(PipeSide* pipe, void* data, size_t length) {
|
||||
furi_check(pipe);
|
||||
return furi_stream_buffer_receive(pipe->receiving, data, length, timeout);
|
||||
|
||||
size_t received = 0;
|
||||
while(length) {
|
||||
size_t received_this_time =
|
||||
furi_stream_buffer_receive(pipe->receiving, data, length, pipe->state_check_period);
|
||||
if(!received_this_time && pipe_state(pipe) == PipeStateBroken) break;
|
||||
|
||||
received += received_this_time;
|
||||
length -= received_this_time;
|
||||
data += received_this_time;
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
size_t pipe_send(PipeSide* pipe, const void* data, size_t length, FuriWait timeout) {
|
||||
size_t pipe_send(PipeSide* pipe, const void* data, size_t length) {
|
||||
furi_check(pipe);
|
||||
return furi_stream_buffer_send(pipe->sending, data, length, timeout);
|
||||
|
||||
size_t sent = 0;
|
||||
while(length) {
|
||||
size_t sent_this_time =
|
||||
furi_stream_buffer_send(pipe->sending, data, length, pipe->state_check_period);
|
||||
if(!sent_this_time && pipe_state(pipe) == PipeStateBroken) break;
|
||||
|
||||
sent += sent_this_time;
|
||||
length -= sent_this_time;
|
||||
data += sent_this_time;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
size_t pipe_bytes_available(PipeSide* pipe) {
|
||||
@@ -151,14 +173,14 @@ static void pipe_receiving_buffer_callback(FuriEventLoopObject* buffer, void* co
|
||||
UNUSED(buffer);
|
||||
PipeSide* pipe = context;
|
||||
furi_assert(pipe);
|
||||
if(pipe->on_space_freed) pipe->on_data_arrived(pipe, pipe->callback_context);
|
||||
if(pipe->on_data_arrived) pipe->on_data_arrived(pipe, pipe->callback_context);
|
||||
}
|
||||
|
||||
static void pipe_sending_buffer_callback(FuriEventLoopObject* buffer, void* context) {
|
||||
UNUSED(buffer);
|
||||
PipeSide* pipe = context;
|
||||
furi_assert(pipe);
|
||||
if(pipe->on_data_arrived) pipe->on_space_freed(pipe, pipe->callback_context);
|
||||
if(pipe->on_space_freed) pipe->on_space_freed(pipe, pipe->callback_context);
|
||||
}
|
||||
|
||||
static void pipe_semaphore_callback(FuriEventLoopObject* semaphore, void* context) {
|
||||
|
||||
+21
-11
@@ -148,38 +148,48 @@ void pipe_free(PipeSide* pipe);
|
||||
void pipe_install_as_stdio(PipeSide* pipe);
|
||||
|
||||
/**
|
||||
* @brief Sets the timeout for `stdout` write operations
|
||||
* @brief Sets the state check period for `send` and `receive` operations
|
||||
*
|
||||
* @note This value is set to `FuriWaitForever` when the pipe is created
|
||||
* @note This value is set to 100 ms when the pipe is created
|
||||
*
|
||||
* @param [in] pipe Pipe side to set the timeout of
|
||||
* @param [in] timeout Timeout value in ticks
|
||||
* `send` and `receive` will check the state of the pipe if exactly 0 bytes were
|
||||
* sent or received during any given `check_period`. Read the documentation for
|
||||
* `pipe_send` and `pipe_receive` for more info.
|
||||
*
|
||||
* @param [in] pipe Pipe side to set the check period of
|
||||
* @param [in] check_period Period in ticks
|
||||
*/
|
||||
void pipe_set_stdout_timeout(PipeSide* pipe, FuriWait timeout);
|
||||
void pipe_set_state_check_period(PipeSide* pipe, FuriWait check_period);
|
||||
|
||||
/**
|
||||
* @brief Receives data from the pipe.
|
||||
*
|
||||
* This function will try to receive all of the requested bytes from the pipe.
|
||||
* If at some point during the operation the pipe becomes broken, this function
|
||||
* will return prematurely, in which case the return value will be less than the
|
||||
* requested `length`.
|
||||
*
|
||||
* @param [in] pipe The pipe side to read data out of
|
||||
* @param [out] data The buffer to fill with data
|
||||
* @param length Maximum length of data to read
|
||||
* @param timeout The timeout (in ticks) after which the read operation is
|
||||
* interrupted
|
||||
* @returns The number of bytes actually written into the provided buffer
|
||||
*/
|
||||
size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout);
|
||||
size_t pipe_receive(PipeSide* pipe, void* data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Sends data into the pipe.
|
||||
*
|
||||
* This function will try to send all of the requested bytes to the pipe.
|
||||
* If at some point during the operation the pipe becomes broken, this function
|
||||
* will return prematurely, in which case the return value will be less than the
|
||||
* requested `length`.
|
||||
*
|
||||
* @param [in] pipe The pipe side to send data into
|
||||
* @param [out] data The buffer to get data from
|
||||
* @param length Maximum length of data to send
|
||||
* @param timeout The timeout (in ticks) after which the write operation is
|
||||
* interrupted
|
||||
* @returns The number of bytes actually read from the provided buffer
|
||||
*/
|
||||
size_t pipe_send(PipeSide* pipe, const void* data, size_t length, FuriWait timeout);
|
||||
size_t pipe_send(PipeSide* pipe, const void* data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Determines how many bytes there are in the pipe available to be read.
|
||||
|
||||
@@ -36,11 +36,17 @@ void pretty_format_bytes_hex_canonical(
|
||||
}
|
||||
|
||||
const size_t begin_idx = i;
|
||||
const size_t end_idx = MIN(i + num_places, data_size);
|
||||
const size_t wrap_idx = i + num_places;
|
||||
const size_t end_idx = MIN(wrap_idx, data_size);
|
||||
|
||||
for(size_t j = begin_idx; j < end_idx; j++) {
|
||||
furi_string_cat_printf(result, "%02X ", data[j]);
|
||||
}
|
||||
if(end_idx < wrap_idx) {
|
||||
for(size_t j = end_idx; j < wrap_idx; j++) {
|
||||
furi_string_cat_printf(result, " ");
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_push_back(result, '|');
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
#include "protocol_dict.h"
|
||||
|
||||
struct ProtocolDict {
|
||||
const ProtocolBase** base;
|
||||
const ProtocolBase* const* base;
|
||||
size_t count;
|
||||
void* data[];
|
||||
};
|
||||
|
||||
ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t count) {
|
||||
ProtocolDict* protocol_dict_alloc(const ProtocolBase* const* protocols, size_t count) {
|
||||
furi_check(protocols);
|
||||
|
||||
ProtocolDict* dict = malloc(sizeof(ProtocolDict) + (sizeof(void*) * count));
|
||||
|
||||
@@ -12,7 +12,7 @@ typedef int32_t ProtocolId;
|
||||
#define PROTOCOL_NO (-1)
|
||||
#define PROTOCOL_ALL_FEATURES (0xFFFFFFFF)
|
||||
|
||||
ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t protocol_count);
|
||||
ProtocolDict* protocol_dict_alloc(const ProtocolBase* const* protocols, size_t protocol_count);
|
||||
|
||||
void protocol_dict_free(ProtocolDict* dict);
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "submenu_based.h"
|
||||
#include <archive/helpers/archive_favorites.h>
|
||||
|
||||
struct SubmenuSettingsHelper {
|
||||
const SubmenuSettingsHelperDescriptor* descriptor;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
Submenu* submenu;
|
||||
uint32_t submenu_view_id;
|
||||
uint32_t main_scene_id;
|
||||
};
|
||||
|
||||
SubmenuSettingsHelper*
|
||||
submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor) {
|
||||
furi_check(descriptor);
|
||||
SubmenuSettingsHelper* helper = malloc(sizeof(SubmenuSettingsHelper));
|
||||
helper->descriptor = descriptor;
|
||||
return helper;
|
||||
}
|
||||
|
||||
void submenu_settings_helpers_assign_objects(
|
||||
SubmenuSettingsHelper* helper,
|
||||
ViewDispatcher* view_dispatcher,
|
||||
SceneManager* scene_manager,
|
||||
Submenu* submenu,
|
||||
uint32_t submenu_view_id,
|
||||
uint32_t main_scene_id) {
|
||||
furi_check(helper);
|
||||
furi_check(view_dispatcher);
|
||||
furi_check(scene_manager);
|
||||
furi_check(submenu);
|
||||
helper->view_dispatcher = view_dispatcher;
|
||||
helper->scene_manager = scene_manager;
|
||||
helper->submenu = submenu;
|
||||
helper->submenu_view_id = submenu_view_id;
|
||||
helper->main_scene_id = main_scene_id;
|
||||
}
|
||||
|
||||
void submenu_settings_helpers_free(SubmenuSettingsHelper* helper) {
|
||||
free(helper);
|
||||
}
|
||||
|
||||
bool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg) {
|
||||
furi_check(helper);
|
||||
if(!arg) return false;
|
||||
|
||||
const char* option = arg;
|
||||
for(size_t i = 0; i < helper->descriptor->options_cnt; i++) {
|
||||
if(strcmp(helper->descriptor->options[i].name, option) == 0) {
|
||||
scene_manager_next_scene(
|
||||
helper->scene_manager, helper->descriptor->options[i].scene_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
submenu_settings_helpers_callback(void* context, InputType input_type, uint32_t index) {
|
||||
SubmenuSettingsHelper* helper = context;
|
||||
if(input_type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(helper->view_dispatcher, index);
|
||||
} else if(input_type == InputTypeLong) {
|
||||
archive_favorites_handle_setting_pin_unpin(
|
||||
helper->descriptor->app_name, helper->descriptor->options[index].name);
|
||||
}
|
||||
}
|
||||
|
||||
void submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper) {
|
||||
furi_check(helper);
|
||||
for(size_t i = 0; i < helper->descriptor->options_cnt; i++) {
|
||||
submenu_add_item_ex(
|
||||
helper->submenu,
|
||||
helper->descriptor->options[i].name,
|
||||
i,
|
||||
submenu_settings_helpers_callback,
|
||||
helper);
|
||||
}
|
||||
|
||||
submenu_set_selected_item(
|
||||
helper->submenu,
|
||||
scene_manager_get_scene_state(helper->scene_manager, helper->main_scene_id));
|
||||
view_dispatcher_switch_to_view(helper->view_dispatcher, helper->submenu_view_id);
|
||||
}
|
||||
|
||||
bool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event) {
|
||||
furi_check(helper);
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_next_scene(
|
||||
helper->scene_manager, helper->descriptor->options[event.event].scene_id);
|
||||
scene_manager_set_scene_state(helper->scene_manager, helper->main_scene_id, event.event);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper) {
|
||||
furi_check(helper);
|
||||
submenu_reset(helper->submenu);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*SubmenuSettingsHelpherCallback)(void* context, uint32_t index);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
uint32_t scene_id;
|
||||
} SubmenuSettingsHelperOption;
|
||||
|
||||
typedef struct {
|
||||
const char* app_name;
|
||||
size_t options_cnt;
|
||||
SubmenuSettingsHelperOption options[];
|
||||
} SubmenuSettingsHelperDescriptor;
|
||||
|
||||
typedef struct SubmenuSettingsHelper SubmenuSettingsHelper;
|
||||
|
||||
/**
|
||||
* @brief Allocates a submenu-based settings helper
|
||||
* @param descriptor settings descriptor
|
||||
*/
|
||||
SubmenuSettingsHelper*
|
||||
submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor);
|
||||
|
||||
/**
|
||||
* @brief Assigns dynamic objects to the submenu-based settings helper
|
||||
* @param helper helper object
|
||||
* @param view_dispatcher ViewDispatcher
|
||||
* @param scene_manager SceneManager
|
||||
* @param submenu Submenu
|
||||
* @param submenu_view_id Submenu view id in the ViewDispatcher
|
||||
* @param main_scene_id Main scene id in the SceneManager
|
||||
*/
|
||||
void submenu_settings_helpers_assign_objects(
|
||||
SubmenuSettingsHelper* helper,
|
||||
ViewDispatcher* view_dispatcher,
|
||||
SceneManager* scene_manager,
|
||||
Submenu* submenu,
|
||||
uint32_t submenu_view_id,
|
||||
uint32_t main_scene_id);
|
||||
|
||||
/**
|
||||
* @brief Frees a submenu-based settings helper
|
||||
* @param helper helper object
|
||||
*/
|
||||
void submenu_settings_helpers_free(SubmenuSettingsHelper* helper);
|
||||
|
||||
/**
|
||||
* @brief App start callback for the submenu-based settings helper
|
||||
*
|
||||
* If an argument containing one of the options was provided, launches the
|
||||
* corresponding scene.
|
||||
*
|
||||
* @param helper helper object
|
||||
* @param arg app argument, may be NULL
|
||||
* @returns true if a setting name was provided in the argument, false if normal
|
||||
* app operation shall commence
|
||||
*/
|
||||
bool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Main scene enter callback for the submenu-based settings helper
|
||||
* @param helper helper object
|
||||
*/
|
||||
void submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper);
|
||||
|
||||
/**
|
||||
* @brief Main scene event callback for the submenu-based settings helper
|
||||
* @param helper helper object
|
||||
* @param event event data
|
||||
* @returns true if the event was consumed, false otherwise
|
||||
*/
|
||||
bool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event);
|
||||
|
||||
/**
|
||||
* @brief Main scene exit callback for the submenu-based settings helper
|
||||
* @param helper helper object
|
||||
*/
|
||||
void submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user