Merge remote-tracking branch 'ofw/dev' into feat/nfc-type-4

This commit is contained in:
Willy-JL
2025-04-10 03:40:10 +01:00
388 changed files with 8000 additions and 3466 deletions
+1
View File
@@ -43,6 +43,7 @@ libs = env.BuildModules(
"ble_profile",
"bit_lib",
"datetime",
"ieee754_parse_wrap",
],
)
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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*)&reg, 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*)&reg, 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
View File
@@ -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,
+4 -4
View File
@@ -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) {
+4 -4
View File
@@ -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
View File
@@ -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, &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
View File
@@ -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[];
+1 -1
View File
@@ -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,
};
+1 -1
View File
@@ -8,4 +8,4 @@ typedef enum {
iButtonProtocolGroupMax
} iButtonProtocolGroup;
extern const iButtonProtocolGroupBase* ibutton_protocol_groups[];
extern const iButtonProtocolGroupBase* const ibutton_protocol_groups[];
+31
View File
@@ -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")
+14
View File
@@ -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);
}
+14
View File
@@ -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
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -37,7 +37,7 @@ typedef enum {
LFRFIDProtocolMax,
} LFRFIDProtocol;
extern const ProtocolBase* lfrfid_protocols[];
extern const ProtocolBase* const lfrfid_protocols[];
typedef enum {
LFRFIDWriteTypeT5577,
+1 -1
View File
@@ -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}};
+6 -6
View File
@@ -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
View File
@@ -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);
+7 -29
View File
@@ -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(
+36 -32
View File
@@ -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;
+70 -12
View File
@@ -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);
}
}
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -6,7 +6,7 @@
extern "C" {
#endif
extern const NfcDeviceBase* nfc_devices[];
extern const NfcDeviceBase* const nfc_devices[];
#ifdef __cplusplus
}
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -7,7 +7,7 @@
extern "C" {
#endif
extern const NfcListenerBase* nfc_listeners_api[NfcProtocolNum];
extern const NfcListenerBase* const nfc_listeners_api[NfcProtocolNum];
#ifdef __cplusplus
}
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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,
+1 -1
View File
@@ -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;
};
+4
View File
@@ -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"),
+123
View File
@@ -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;
}
+153
View File
@@ -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
+17
View File
@@ -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);
}
+103
View File
@@ -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
+178
View File
@@ -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 &registry->commands;
}
+92
View File
@@ -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
+55
View File
@@ -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
+478
View File
@@ -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;
}
+75
View File
@@ -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
+34
View File
@@ -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
+378
View File
@@ -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},
},
};
+42
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+7 -1
View File
@@ -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 -2
View File
@@ -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));
+1 -1
View File
@@ -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