mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-26 22:40:25 +00:00
Merge branch 'dev' into feat/subghz-save-hopping-state
This commit is contained in:
commit
78446bab07
25 changed files with 1364 additions and 1264 deletions
|
@ -290,8 +290,7 @@ void elements_multiline_text_aligned(
|
|||
} else if((y + font_height) > canvas_height(canvas)) {
|
||||
line = furi_string_alloc_printf("%.*s...\n", chars_fit, start);
|
||||
} else {
|
||||
// Account for the added "-" in length
|
||||
line = furi_string_alloc_printf("%.*s-\n", chars_fit - 1, start);
|
||||
line = furi_string_alloc_printf("%.*s-\n", chars_fit, start);
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line));
|
||||
furi_string_free(line);
|
||||
|
|
|
@ -174,9 +174,10 @@ void view_port_input_callback_set(
|
|||
|
||||
void view_port_update(ViewPort* view_port) {
|
||||
furi_assert(view_port);
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
// TODO: Uncomment when all apps are verified to be fixed !!!!!!!!!!!!!!!!!!!!!!!
|
||||
//furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
|
||||
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
//furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
void view_port_gui_set(ViewPort* view_port, Gui* gui) {
|
||||
|
|
|
@ -303,7 +303,8 @@ static LoaderStatus loader_start_external_app(
|
|||
FlipperApplicationPreloadStatus preload_res =
|
||||
flipper_application_preload(loader->app.fap, path);
|
||||
if(preload_res != FlipperApplicationPreloadStatusSuccess) {
|
||||
if(preload_res == FlipperApplicationPreloadStatusApiMismatch) {
|
||||
if((preload_res == FlipperApplicationPreloadStatusApiTooOld) ||
|
||||
(preload_res == FlipperApplicationPreloadStatusApiTooNew)) {
|
||||
if(!ignore_mismatch) {
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
|
|
|
@ -32,8 +32,6 @@ typedef struct {
|
|||
uint32_t product_serial_number;
|
||||
uint8_t manufacturing_month;
|
||||
uint16_t manufacturing_year;
|
||||
|
||||
FS_Error error;
|
||||
} SDInfo;
|
||||
|
||||
const char* sd_api_get_fs_type_text(SDFsType fs_type);
|
||||
|
|
|
@ -26,11 +26,11 @@ static FS_Error storage_ext_parse_error(SDError error);
|
|||
|
||||
static bool sd_mount_card_internal(StorageData* storage, bool notify) {
|
||||
bool result = false;
|
||||
uint8_t counter = sd_max_mount_retry_count();
|
||||
uint8_t counter = furi_hal_sd_max_mount_retry_count();
|
||||
uint8_t bsp_result;
|
||||
SDData* sd_data = storage->data;
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
while(result == false && counter > 0 && furi_hal_sd_is_present()) {
|
||||
if(notify) {
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
sd_notify_wait(notification);
|
||||
|
@ -39,9 +39,9 @@ static bool sd_mount_card_internal(StorageData* storage, bool notify) {
|
|||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
bsp_result = sd_init(true);
|
||||
bsp_result = furi_hal_sd_init(true);
|
||||
} else {
|
||||
bsp_result = sd_init(false);
|
||||
bsp_result = furi_hal_sd_init(false);
|
||||
}
|
||||
|
||||
if(bsp_result) {
|
||||
|
@ -225,18 +225,18 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
|
|||
#endif
|
||||
}
|
||||
|
||||
SD_CID cid;
|
||||
SdSpiStatus status = sd_get_cid(&cid);
|
||||
FuriHalSdInfo info;
|
||||
FuriStatus status = furi_hal_sd_info(&info);
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
sd_info->manufacturer_id = cid.ManufacturerID;
|
||||
memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID));
|
||||
memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName));
|
||||
sd_info->product_revision_major = cid.ProdRev >> 4;
|
||||
sd_info->product_revision_minor = cid.ProdRev & 0x0F;
|
||||
sd_info->product_serial_number = cid.ProdSN;
|
||||
sd_info->manufacturing_year = 2000 + cid.ManufactYear;
|
||||
sd_info->manufacturing_month = cid.ManufactMonth;
|
||||
if(status == FuriStatusOk) {
|
||||
sd_info->manufacturer_id = info.manufacturer_id;
|
||||
memcpy(sd_info->oem_id, info.oem_id, sizeof(info.oem_id));
|
||||
memcpy(sd_info->product_name, info.product_name, sizeof(info.product_name));
|
||||
sd_info->product_revision_major = info.product_revision_major;
|
||||
sd_info->product_revision_minor = info.product_revision_minor;
|
||||
sd_info->product_serial_number = info.product_serial_number;
|
||||
sd_info->manufacturing_year = info.manufacturing_year;
|
||||
sd_info->manufacturing_month = info.manufacturing_month;
|
||||
}
|
||||
|
||||
return storage_ext_parse_error(error);
|
||||
|
@ -246,19 +246,19 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) {
|
|||
SDData* sd_data = storage->data;
|
||||
|
||||
if(sd_data->sd_was_present) {
|
||||
if(hal_sd_detect()) {
|
||||
if(furi_hal_sd_is_present()) {
|
||||
FURI_LOG_I(TAG, "card detected");
|
||||
sd_data->sd_was_present = false;
|
||||
sd_mount_card(storage, notify);
|
||||
|
||||
if(!hal_sd_detect()) {
|
||||
if(!furi_hal_sd_is_present()) {
|
||||
FURI_LOG_I(TAG, "card removed while mounting");
|
||||
sd_unmount_card(storage);
|
||||
sd_data->sd_was_present = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(!hal_sd_detect()) {
|
||||
if(!furi_hal_sd_is_present()) {
|
||||
FURI_LOG_I(TAG, "card removed");
|
||||
sd_data->sd_was_present = true;
|
||||
|
||||
|
@ -639,7 +639,7 @@ void storage_ext_init(StorageData* storage) {
|
|||
storage->api.tick = storage_ext_tick;
|
||||
storage->fs_api = &fs_api;
|
||||
|
||||
hal_sd_detect_init();
|
||||
furi_hal_sd_presence_init();
|
||||
|
||||
// do not notify on first launch, notifications app is waiting for our thread to read settings
|
||||
storage_ext_tick_internal(storage, false);
|
||||
|
|
|
@ -24,3 +24,7 @@ There are 2 signals that will be exposed to external GPIO pins:
|
|||
|
||||
- `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode.
|
||||
- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible.
|
||||
|
||||
## FuriHalSD
|
||||
|
||||
`--extra-define=FURI_HAL_SD_SPI_DEBUG` enables SD card SPI bus logging.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,36.1,,
|
||||
Version,+,38.0,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
|
@ -826,8 +826,9 @@ Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,Fl
|
|||
Function,+,flipper_application_is_plugin,_Bool,FlipperApplication*
|
||||
Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*"
|
||||
Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus
|
||||
Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_manifest_is_too_new,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_too_old,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
|
||||
Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication*
|
||||
|
@ -1222,6 +1223,14 @@ Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
|
|||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
|
||||
Function,+,furi_hal_rtc_sync_shadow,void,
|
||||
Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
|
||||
Function,+,furi_hal_sd_get_card_state,FuriStatus,
|
||||
Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo*
|
||||
Function,+,furi_hal_sd_init,FuriStatus,_Bool
|
||||
Function,+,furi_hal_sd_is_present,_Bool,
|
||||
Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
|
||||
Function,+,furi_hal_sd_presence_init,void,
|
||||
Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
|
||||
Function,-,furi_hal_speaker_deinit,void,
|
||||
Function,-,furi_hal_speaker_init,void,
|
||||
|
@ -1477,9 +1486,6 @@ Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
|
|||
Function,+,gui_set_lockdown,void,"Gui*, _Bool"
|
||||
Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
|
||||
Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
|
||||
Function,+,hal_sd_detect,_Bool,
|
||||
Function,+,hal_sd_detect_init,void,
|
||||
Function,+,hal_sd_detect_set_low,void,
|
||||
Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
|
||||
Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
|
||||
Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
|
||||
|
@ -2423,7 +2429,6 @@ Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
|
|||
Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
|
||||
Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*,
|
||||
Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
|
||||
Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
|
||||
Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
|
||||
|
|
|
|
@ -136,6 +136,14 @@ static void furi_hal_resources_init_input_pins(GpioMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
static void furi_hal_resources_init_gpio_pins(GpioMode mode) {
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(!gpio_pins[i].debug) {
|
||||
furi_hal_gpio_init(gpio_pins[i].pin, mode, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_resources_init_early() {
|
||||
furi_hal_bus_enable(FuriHalBusGPIOA);
|
||||
furi_hal_bus_enable(FuriHalBusGPIOB);
|
||||
|
@ -179,14 +187,7 @@ void furi_hal_resources_init_early() {
|
|||
furi_hal_gpio_write(&gpio_usb_dp, 0);
|
||||
|
||||
// External header pins
|
||||
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_resources_init_gpio_pins(GpioModeAnalog);
|
||||
}
|
||||
|
||||
void furi_hal_resources_deinit_early() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,36.1,,
|
||||
Version,+,38.0,,
|
||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
|
@ -913,8 +913,9 @@ Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,Fl
|
|||
Function,+,flipper_application_is_plugin,_Bool,FlipperApplication*
|
||||
Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*"
|
||||
Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus
|
||||
Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_manifest_is_too_new,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_too_old,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
|
||||
Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest*
|
||||
Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
|
||||
Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication*
|
||||
|
@ -1393,6 +1394,14 @@ Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t
|
|||
Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t"
|
||||
Function,+,furi_hal_rtc_sync_shadow,void,
|
||||
Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime*
|
||||
Function,+,furi_hal_sd_get_card_state,FuriStatus,
|
||||
Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo*
|
||||
Function,+,furi_hal_sd_init,FuriStatus,_Bool
|
||||
Function,+,furi_hal_sd_is_present,_Bool,
|
||||
Function,+,furi_hal_sd_max_mount_retry_count,uint8_t,
|
||||
Function,+,furi_hal_sd_presence_init,void,
|
||||
Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t"
|
||||
Function,+,furi_hal_speaker_acquire,_Bool,uint32_t
|
||||
Function,-,furi_hal_speaker_deinit,void,
|
||||
Function,-,furi_hal_speaker_init,void,
|
||||
|
@ -1688,9 +1697,6 @@ Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
|
|||
Function,+,gui_set_lockdown,void,"Gui*, _Bool"
|
||||
Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*"
|
||||
Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*"
|
||||
Function,+,hal_sd_detect,_Bool,
|
||||
Function,+,hal_sd_detect_init,void,
|
||||
Function,+,hal_sd_detect_set_low,void,
|
||||
Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*"
|
||||
Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*"
|
||||
Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*"
|
||||
|
@ -3266,7 +3272,6 @@ Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
|
|||
Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,
|
||||
Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle,
|
||||
Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*,
|
||||
Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus,
|
||||
Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle,
|
||||
Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle,
|
||||
|
|
|
|
@ -1,843 +0,0 @@
|
|||
#include "sd_spi_io.h"
|
||||
#include "sector_cache.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi/core/core_defines.h>
|
||||
|
||||
// #define SD_SPI_DEBUG 1
|
||||
#define TAG "SdSpi"
|
||||
|
||||
#ifdef SD_SPI_DEBUG
|
||||
#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define sd_spi_debug(...)
|
||||
#endif
|
||||
|
||||
#define SD_CMD_LENGTH 6
|
||||
#define SD_DUMMY_BYTE 0xFF
|
||||
#define SD_ANSWER_RETRY_COUNT 8
|
||||
#define SD_IDLE_RETRY_COUNT 100
|
||||
|
||||
#define FLAG_SET(x, y) (((x) & (y)) == (y))
|
||||
|
||||
static bool sd_high_capacity = false;
|
||||
|
||||
typedef enum {
|
||||
SdSpiDataResponceOK = 0x05,
|
||||
SdSpiDataResponceCRCError = 0x0B,
|
||||
SdSpiDataResponceWriteError = 0x0D,
|
||||
SdSpiDataResponceOtherError = 0xFF,
|
||||
} SdSpiDataResponce;
|
||||
|
||||
typedef struct {
|
||||
uint8_t r1;
|
||||
uint8_t r2;
|
||||
uint8_t r3;
|
||||
uint8_t r4;
|
||||
uint8_t r5;
|
||||
} SdSpiCmdAnswer;
|
||||
|
||||
typedef enum {
|
||||
SdSpiCmdAnswerTypeR1,
|
||||
SdSpiCmdAnswerTypeR1B,
|
||||
SdSpiCmdAnswerTypeR2,
|
||||
SdSpiCmdAnswerTypeR3,
|
||||
SdSpiCmdAnswerTypeR4R5,
|
||||
SdSpiCmdAnswerTypeR7,
|
||||
} SdSpiCmdAnswerType;
|
||||
|
||||
/*
|
||||
SdSpiCmd and SdSpiToken use non-standard enum value names convention,
|
||||
because it is more convenient to look for documentation on a specific command.
|
||||
For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for
|
||||
SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification".
|
||||
|
||||
Do not use that naming convention in other places.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
SD_CMD0_GO_IDLE_STATE = 0,
|
||||
SD_CMD1_SEND_OP_COND = 1,
|
||||
SD_CMD8_SEND_IF_COND = 8,
|
||||
SD_CMD9_SEND_CSD = 9,
|
||||
SD_CMD10_SEND_CID = 10,
|
||||
SD_CMD12_STOP_TRANSMISSION = 12,
|
||||
SD_CMD13_SEND_STATUS = 13,
|
||||
SD_CMD16_SET_BLOCKLEN = 16,
|
||||
SD_CMD17_READ_SINGLE_BLOCK = 17,
|
||||
SD_CMD18_READ_MULT_BLOCK = 18,
|
||||
SD_CMD23_SET_BLOCK_COUNT = 23,
|
||||
SD_CMD24_WRITE_SINGLE_BLOCK = 24,
|
||||
SD_CMD25_WRITE_MULT_BLOCK = 25,
|
||||
SD_CMD27_PROG_CSD = 27,
|
||||
SD_CMD28_SET_WRITE_PROT = 28,
|
||||
SD_CMD29_CLR_WRITE_PROT = 29,
|
||||
SD_CMD30_SEND_WRITE_PROT = 30,
|
||||
SD_CMD32_SD_ERASE_GRP_START = 32,
|
||||
SD_CMD33_SD_ERASE_GRP_END = 33,
|
||||
SD_CMD34_UNTAG_SECTOR = 34,
|
||||
SD_CMD35_ERASE_GRP_START = 35,
|
||||
SD_CMD36_ERASE_GRP_END = 36,
|
||||
SD_CMD37_UNTAG_ERASE_GROUP = 37,
|
||||
SD_CMD38_ERASE = 38,
|
||||
SD_CMD41_SD_APP_OP_COND = 41,
|
||||
SD_CMD55_APP_CMD = 55,
|
||||
SD_CMD58_READ_OCR = 58,
|
||||
} SdSpiCmd;
|
||||
|
||||
/** Data tokens */
|
||||
typedef enum {
|
||||
SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE,
|
||||
SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE,
|
||||
SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE,
|
||||
SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC,
|
||||
SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD,
|
||||
} SdSpiToken;
|
||||
|
||||
/** R1 answer value */
|
||||
typedef enum {
|
||||
SdSpi_R1_NO_ERROR = 0x00,
|
||||
SdSpi_R1_IN_IDLE_STATE = 0x01,
|
||||
SdSpi_R1_ERASE_RESET = 0x02,
|
||||
SdSpi_R1_ILLEGAL_COMMAND = 0x04,
|
||||
SdSpi_R1_COM_CRC_ERROR = 0x08,
|
||||
SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10,
|
||||
SdSpi_R1_ADDRESS_ERROR = 0x20,
|
||||
SdSpi_R1_PARAMETER_ERROR = 0x40,
|
||||
} SdSpiR1;
|
||||
|
||||
/** R2 answer value */
|
||||
typedef enum {
|
||||
/* R2 answer value */
|
||||
SdSpi_R2_NO_ERROR = 0x00,
|
||||
SdSpi_R2_CARD_LOCKED = 0x01,
|
||||
SdSpi_R2_LOCKUNLOCK_ERROR = 0x02,
|
||||
SdSpi_R2_ERROR = 0x04,
|
||||
SdSpi_R2_CC_ERROR = 0x08,
|
||||
SdSpi_R2_CARD_ECC_FAILED = 0x10,
|
||||
SdSpi_R2_WP_VIOLATION = 0x20,
|
||||
SdSpi_R2_ERASE_PARAM = 0x40,
|
||||
SdSpi_R2_OUTOFRANGE = 0x80,
|
||||
} SdSpiR2;
|
||||
|
||||
static inline void sd_spi_select_card() {
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false);
|
||||
furi_delay_us(10); // Entry guard time for some SD cards
|
||||
}
|
||||
|
||||
static inline void sd_spi_deselect_card() {
|
||||
furi_delay_us(10); // Exit guard time for some SD cards
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true);
|
||||
}
|
||||
|
||||
static void sd_spi_bus_to_ground() {
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->miso,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFnUnused);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->mosi,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFnUnused);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->sck,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFnUnused);
|
||||
|
||||
sd_spi_select_card();
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false);
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false);
|
||||
furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false);
|
||||
}
|
||||
|
||||
static void sd_spi_bus_rise_up() {
|
||||
sd_spi_deselect_card();
|
||||
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->miso,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->mosi,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
furi_hal_gpio_init_ex(
|
||||
furi_hal_sd_spi_handle->sck,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioPullUp,
|
||||
GpioSpeedVeryHigh,
|
||||
GpioAltFn5SPI2);
|
||||
}
|
||||
|
||||
static inline uint8_t sd_spi_read_byte(void) {
|
||||
uint8_t responce;
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS));
|
||||
return responce;
|
||||
}
|
||||
|
||||
static inline void sd_spi_write_byte(uint8_t data) {
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) {
|
||||
uint8_t responce;
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS));
|
||||
return responce;
|
||||
}
|
||||
|
||||
static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) {
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) {
|
||||
furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) {
|
||||
uint32_t timeout_mul = (size / 512) + 1;
|
||||
furi_check(furi_hal_spi_bus_trx_dma(
|
||||
furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul));
|
||||
}
|
||||
|
||||
static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) {
|
||||
uint32_t timeout_mul = (size / 512) + 1;
|
||||
furi_check(furi_hal_spi_bus_trx_dma(
|
||||
furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul));
|
||||
}
|
||||
|
||||
static uint8_t sd_spi_wait_for_data_and_read(void) {
|
||||
uint8_t retry_count = SD_ANSWER_RETRY_COUNT;
|
||||
uint8_t responce;
|
||||
|
||||
// Wait until we get a valid data
|
||||
do {
|
||||
responce = sd_spi_read_byte();
|
||||
retry_count--;
|
||||
|
||||
} while((responce == SD_DUMMY_BYTE) && retry_count);
|
||||
|
||||
return responce;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000);
|
||||
uint8_t byte;
|
||||
|
||||
do {
|
||||
byte = sd_spi_read_byte();
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
return SdSpiStatusTimeout;
|
||||
}
|
||||
} while((byte != data));
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static inline void sd_spi_deselect_card_and_purge() {
|
||||
sd_spi_deselect_card();
|
||||
sd_spi_read_byte();
|
||||
}
|
||||
|
||||
static inline void sd_spi_purge_crc() {
|
||||
sd_spi_read_byte();
|
||||
sd_spi_read_byte();
|
||||
}
|
||||
|
||||
static SdSpiCmdAnswer
|
||||
sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) {
|
||||
uint8_t frame[SD_CMD_LENGTH];
|
||||
SdSpiCmdAnswer cmd_answer = {
|
||||
.r1 = SD_DUMMY_BYTE,
|
||||
.r2 = SD_DUMMY_BYTE,
|
||||
.r3 = SD_DUMMY_BYTE,
|
||||
.r4 = SD_DUMMY_BYTE,
|
||||
.r5 = SD_DUMMY_BYTE,
|
||||
};
|
||||
|
||||
// R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes
|
||||
// R1b identical to R1 + Busy information
|
||||
// R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes
|
||||
|
||||
frame[0] = ((uint8_t)cmd | 0x40);
|
||||
frame[1] = (uint8_t)(arg >> 24);
|
||||
frame[2] = (uint8_t)(arg >> 16);
|
||||
frame[3] = (uint8_t)(arg >> 8);
|
||||
frame[4] = (uint8_t)(arg);
|
||||
frame[5] = (crc | 0x01);
|
||||
|
||||
sd_spi_select_card();
|
||||
sd_spi_write_bytes(frame, sizeof(frame));
|
||||
|
||||
switch(answer_type) {
|
||||
case SdSpiCmdAnswerTypeR1:
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
break;
|
||||
case SdSpiCmdAnswerTypeR1B:
|
||||
// TODO FL-3507: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
|
||||
// In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B
|
||||
// reassert card
|
||||
sd_spi_deselect_card();
|
||||
furi_delay_us(1000);
|
||||
sd_spi_deselect_card();
|
||||
|
||||
// and wait for it to be ready
|
||||
while(sd_spi_read_byte() != 0xFF) {
|
||||
};
|
||||
|
||||
break;
|
||||
case SdSpiCmdAnswerTypeR2:
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
cmd_answer.r2 = sd_spi_read_byte();
|
||||
break;
|
||||
case SdSpiCmdAnswerTypeR3:
|
||||
case SdSpiCmdAnswerTypeR7:
|
||||
cmd_answer.r1 = sd_spi_wait_for_data_and_read();
|
||||
cmd_answer.r2 = sd_spi_read_byte();
|
||||
cmd_answer.r3 = sd_spi_read_byte();
|
||||
cmd_answer.r4 = sd_spi_read_byte();
|
||||
cmd_answer.r5 = sd_spi_read_byte();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cmd_answer;
|
||||
}
|
||||
|
||||
static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) {
|
||||
SdSpiDataResponce responce = sd_spi_read_byte();
|
||||
// read busy response byte
|
||||
sd_spi_read_byte();
|
||||
|
||||
switch(responce & 0x1F) {
|
||||
case SdSpiDataResponceOK:
|
||||
// TODO FL-3508: check timings
|
||||
sd_spi_deselect_card();
|
||||
sd_spi_select_card();
|
||||
|
||||
// wait for 0xFF
|
||||
if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) {
|
||||
return SdSpiDataResponceOK;
|
||||
} else {
|
||||
return SdSpiDataResponceOtherError;
|
||||
}
|
||||
case SdSpiDataResponceCRCError:
|
||||
return SdSpiDataResponceCRCError;
|
||||
case SdSpiDataResponceWriteError:
|
||||
return SdSpiDataResponceWriteError;
|
||||
default:
|
||||
return SdSpiDataResponceOtherError;
|
||||
}
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_init_spi_mode_v1(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
uint8_t retry_count = 0;
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v1");
|
||||
|
||||
do {
|
||||
retry_count++;
|
||||
|
||||
// CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
|
||||
sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
// ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v1 done");
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_init_spi_mode_v2(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
uint8_t retry_count = 0;
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v2");
|
||||
|
||||
do {
|
||||
retry_count++;
|
||||
// CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
|
||||
sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
// ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
|
||||
response =
|
||||
sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
sd_spi_debug("ACMD41 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
|
||||
|
||||
if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) {
|
||||
sd_spi_debug("ACMD41 is illegal command");
|
||||
retry_count = 0;
|
||||
do {
|
||||
retry_count++;
|
||||
// CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_IN_IDLE_STATE) {
|
||||
sd_spi_debug("CMD55 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
// ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
sd_spi_debug("ACMD41 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 == SdSpi_R1_IN_IDLE_STATE);
|
||||
}
|
||||
|
||||
sd_spi_debug("Init SD card in SPI mode v2 done");
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_init_spi_mode(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
uint8_t retry_count;
|
||||
|
||||
// CMD0 (GO_IDLE_STATE) to put SD in SPI mode and
|
||||
// wait for In Idle State Response (R1 Format) equal to 0x01
|
||||
retry_count = 0;
|
||||
do {
|
||||
retry_count++;
|
||||
response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(retry_count >= SD_IDLE_RETRY_COUNT) {
|
||||
sd_spi_debug("CMD0 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
} while(response.r1 != SdSpi_R1_IN_IDLE_STATE);
|
||||
|
||||
// CMD8 (SEND_IF_COND) to check the power supply status
|
||||
// and wait until response (R7 Format) equal to 0xAA and
|
||||
response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) {
|
||||
if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) {
|
||||
sd_spi_debug("Init mode v1 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
sd_high_capacity = 0;
|
||||
} else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) {
|
||||
if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) {
|
||||
sd_spi_debug("Init mode v2 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response
|
||||
response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
sd_spi_debug("CMD58 failed");
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
sd_high_capacity = (response.r2 & 0x40) >> 6;
|
||||
} else {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC");
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) {
|
||||
uint16_t counter = 0;
|
||||
uint8_t csd_data[16];
|
||||
SdSpiStatus ret = SdSpiStatusError;
|
||||
SdSpiCmdAnswer response;
|
||||
|
||||
// CMD9 (SEND_CSD): R1 format (0x00 is no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
|
||||
if(response.r1 == SdSpi_R1_NO_ERROR) {
|
||||
if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) ==
|
||||
SdSpiStatusOK) {
|
||||
// read CSD data
|
||||
for(counter = 0; counter < 16; counter++) {
|
||||
csd_data[counter] = sd_spi_read_byte();
|
||||
}
|
||||
|
||||
sd_spi_purge_crc();
|
||||
|
||||
/*************************************************************************
|
||||
CSD header decoding
|
||||
*************************************************************************/
|
||||
|
||||
csd->CSDStruct = (csd_data[0] & 0xC0) >> 6;
|
||||
csd->Reserved1 = csd_data[0] & 0x3F;
|
||||
csd->TAAC = csd_data[1];
|
||||
csd->NSAC = csd_data[2];
|
||||
csd->MaxBusClkFrec = csd_data[3];
|
||||
csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4);
|
||||
csd->RdBlockLen = csd_data[5] & 0x0F;
|
||||
csd->PartBlockRead = (csd_data[6] & 0x80) >> 7;
|
||||
csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6;
|
||||
csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5;
|
||||
csd->DSRImpl = (csd_data[6] & 0x10) >> 4;
|
||||
|
||||
/*************************************************************************
|
||||
CSD v1/v2 decoding
|
||||
*************************************************************************/
|
||||
|
||||
if(sd_high_capacity == 0) {
|
||||
csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2);
|
||||
csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) |
|
||||
((csd_data[8] & 0xC0) >> 6);
|
||||
csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3;
|
||||
csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07);
|
||||
csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5;
|
||||
csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2;
|
||||
csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) |
|
||||
((csd_data[10] & 0x80) >> 7);
|
||||
} else {
|
||||
csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) |
|
||||
((csd_data[7] & 0xC0) >> 6);
|
||||
csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) |
|
||||
csd_data[9];
|
||||
csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8);
|
||||
}
|
||||
|
||||
csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6;
|
||||
csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7);
|
||||
csd->WrProtectGrSize = (csd_data[11] & 0x7F);
|
||||
csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7;
|
||||
csd->Reserved2 = (csd_data[12] & 0x60) >> 5;
|
||||
csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2;
|
||||
csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6);
|
||||
csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5;
|
||||
csd->Reserved3 = (csd_data[13] & 0x1F);
|
||||
csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7;
|
||||
csd->CopyFlag = (csd_data[14] & 0x40) >> 6;
|
||||
csd->PermWrProtect = (csd_data[14] & 0x20) >> 5;
|
||||
csd->TempWrProtect = (csd_data[14] & 0x10) >> 4;
|
||||
csd->FileFormat = (csd_data[14] & 0x0C) >> 2;
|
||||
csd->Reserved4 = (csd_data[14] & 0x03);
|
||||
csd->crc = (csd_data[15] & 0xFE) >> 1;
|
||||
csd->Reserved5 = (csd_data[15] & 0x01);
|
||||
|
||||
ret = SdSpiStatusOK;
|
||||
}
|
||||
}
|
||||
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) {
|
||||
uint16_t counter = 0;
|
||||
uint8_t cid_data[16];
|
||||
SdSpiStatus ret = SdSpiStatusError;
|
||||
SdSpiCmdAnswer response;
|
||||
|
||||
// CMD10 (SEND_CID): R1 format (0x00 is no errors)
|
||||
response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
|
||||
if(response.r1 == SdSpi_R1_NO_ERROR) {
|
||||
if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) ==
|
||||
SdSpiStatusOK) {
|
||||
// read CID data
|
||||
for(counter = 0; counter < 16; counter++) {
|
||||
cid_data[counter] = sd_spi_read_byte();
|
||||
}
|
||||
|
||||
sd_spi_purge_crc();
|
||||
|
||||
Cid->ManufacturerID = cid_data[0];
|
||||
memcpy(Cid->OEM_AppliID, cid_data + 1, 2);
|
||||
memcpy(Cid->ProdName, cid_data + 3, 5);
|
||||
Cid->ProdRev = cid_data[8];
|
||||
Cid->ProdSN = cid_data[9] << 24;
|
||||
Cid->ProdSN |= cid_data[10] << 16;
|
||||
Cid->ProdSN |= cid_data[11] << 8;
|
||||
Cid->ProdSN |= cid_data[12];
|
||||
Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4;
|
||||
Cid->ManufactYear = (cid_data[13] & 0x0F) << 4;
|
||||
Cid->ManufactYear |= (cid_data[14] & 0xF0) >> 4;
|
||||
Cid->ManufactMonth = (cid_data[14] & 0x0F);
|
||||
Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1;
|
||||
Cid->Reserved2 = 1;
|
||||
|
||||
ret = SdSpiStatusOK;
|
||||
}
|
||||
}
|
||||
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SdSpiStatus
|
||||
sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
|
||||
uint32_t block_address = address;
|
||||
uint32_t offset = 0;
|
||||
|
||||
// CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
|
||||
SdSpiCmdAnswer response =
|
||||
sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
if(!sd_high_capacity) {
|
||||
block_address = address * SD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
while(blocks--) {
|
||||
// CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors)
|
||||
response =
|
||||
sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
sd_spi_deselect_card_and_purge();
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// Wait for the data start token
|
||||
if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) ==
|
||||
SdSpiStatusOK) {
|
||||
// Read the data block
|
||||
sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE);
|
||||
sd_spi_purge_crc();
|
||||
|
||||
// increase offset
|
||||
offset += SD_BLOCK_SIZE;
|
||||
|
||||
// increase block address
|
||||
if(sd_high_capacity) {
|
||||
block_address += 1;
|
||||
} else {
|
||||
block_address += SD_BLOCK_SIZE;
|
||||
}
|
||||
} else {
|
||||
sd_spi_deselect_card_and_purge();
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
sd_spi_deselect_card_and_purge();
|
||||
}
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
static SdSpiStatus sd_spi_cmd_write_blocks(
|
||||
uint32_t* data,
|
||||
uint32_t address,
|
||||
uint32_t blocks,
|
||||
uint32_t timeout_ms) {
|
||||
uint32_t block_address = address;
|
||||
uint32_t offset = 0;
|
||||
|
||||
// CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors)
|
||||
SdSpiCmdAnswer response =
|
||||
sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
if(!sd_high_capacity) {
|
||||
block_address = address * SD_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
while(blocks--) {
|
||||
// CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors)
|
||||
response = sd_spi_send_cmd(
|
||||
SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1);
|
||||
if(response.r1 != SdSpi_R1_NO_ERROR) {
|
||||
sd_spi_deselect_card_and_purge();
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN
|
||||
// TODO FL-3509: check bytes count
|
||||
sd_spi_write_byte(SD_DUMMY_BYTE);
|
||||
sd_spi_write_byte(SD_DUMMY_BYTE);
|
||||
|
||||
// Send the data start token
|
||||
sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE);
|
||||
sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE);
|
||||
sd_spi_purge_crc();
|
||||
|
||||
// Read data response
|
||||
SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
if(data_responce != SdSpiDataResponceOK) {
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
// increase offset
|
||||
offset += SD_BLOCK_SIZE;
|
||||
|
||||
// increase block address
|
||||
if(sd_high_capacity) {
|
||||
block_address += 1;
|
||||
} else {
|
||||
block_address += SD_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
uint8_t sd_max_mount_retry_count() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_init(bool power_reset) {
|
||||
// Slow speed init
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow;
|
||||
|
||||
// We reset card in spi_lock context, so it is safe to disturb spi bus
|
||||
if(power_reset) {
|
||||
sd_spi_debug("Power reset");
|
||||
|
||||
// disable power and set low on all bus pins
|
||||
furi_hal_power_disable_external_3_3v();
|
||||
sd_spi_bus_to_ground();
|
||||
hal_sd_detect_set_low();
|
||||
furi_delay_ms(250);
|
||||
|
||||
// reinit bus and enable power
|
||||
sd_spi_bus_rise_up();
|
||||
hal_sd_detect_init();
|
||||
furi_hal_power_enable_external_3_3v();
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
SdSpiStatus status = SdSpiStatusError;
|
||||
|
||||
// Send 80 dummy clocks with CS high
|
||||
sd_spi_deselect_card();
|
||||
for(uint8_t i = 0; i < 80; i++) {
|
||||
sd_spi_write_byte(SD_DUMMY_BYTE);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 128; i++) {
|
||||
status = sd_spi_init_spi_mode();
|
||||
if(status == SdSpiStatusOK) {
|
||||
// SD initialized and init to SPI mode properly
|
||||
sd_spi_debug("SD init OK after %d retries", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
|
||||
|
||||
// Init sector cache
|
||||
sector_cache_init();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_get_card_state(void) {
|
||||
SdSpiCmdAnswer response;
|
||||
|
||||
// Send CMD13 (SEND_STATUS) to get SD status
|
||||
response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2);
|
||||
sd_spi_deselect_card_and_purge();
|
||||
|
||||
// Return status OK if response is valid
|
||||
if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) {
|
||||
return SdSpiStatusOK;
|
||||
}
|
||||
|
||||
return SdSpiStatusError;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) {
|
||||
SdSpiStatus status;
|
||||
|
||||
status = sd_spi_get_csd(&(card_info->Csd));
|
||||
|
||||
if(status != SdSpiStatusOK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = sd_spi_get_cid(&(card_info->Cid));
|
||||
|
||||
if(status != SdSpiStatusOK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if(sd_high_capacity == 1) {
|
||||
card_info->LogBlockSize = 512;
|
||||
card_info->CardBlockSize = 512;
|
||||
card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL *
|
||||
(uint64_t)card_info->LogBlockSize;
|
||||
card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize);
|
||||
} else {
|
||||
card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1);
|
||||
card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2));
|
||||
card_info->LogBlockSize = 512;
|
||||
card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen);
|
||||
card_info->CardCapacity *= card_info->CardBlockSize;
|
||||
card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus
|
||||
sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
|
||||
SdSpiStatus status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms);
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus
|
||||
sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) {
|
||||
SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms);
|
||||
return status;
|
||||
}
|
||||
|
||||
SdSpiStatus sd_get_cid(SD_CID* cid) {
|
||||
SdSpiStatus status;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
memset(cid, 0, sizeof(SD_CID));
|
||||
status = sd_spi_get_cid(cid);
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return status;
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define __IO volatile
|
||||
|
||||
#define SD_TIMEOUT_MS (1000)
|
||||
#define SD_BLOCK_SIZE 512
|
||||
|
||||
typedef enum {
|
||||
SdSpiStatusOK,
|
||||
SdSpiStatusError,
|
||||
SdSpiStatusTimeout,
|
||||
} SdSpiStatus;
|
||||
|
||||
/**
|
||||
* @brief Card Specific Data: CSD Register
|
||||
*/
|
||||
typedef struct {
|
||||
/* Header part */
|
||||
uint8_t CSDStruct : 2; /* CSD structure */
|
||||
uint8_t Reserved1 : 6; /* Reserved */
|
||||
uint8_t TAAC : 8; /* Data read access-time 1 */
|
||||
uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */
|
||||
uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */
|
||||
uint16_t CardComdClasses : 12; /* Card command classes */
|
||||
uint8_t RdBlockLen : 4; /* Max. read data block length */
|
||||
uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */
|
||||
uint8_t WrBlockMisalign : 1; /* Write block misalignment */
|
||||
uint8_t RdBlockMisalign : 1; /* Read block misalignment */
|
||||
uint8_t DSRImpl : 1; /* DSR implemented */
|
||||
|
||||
/* v1 or v2 struct */
|
||||
union csd_version {
|
||||
struct {
|
||||
uint8_t Reserved1 : 2; /* Reserved */
|
||||
uint16_t DeviceSize : 12; /* Device Size */
|
||||
uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */
|
||||
uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */
|
||||
uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */
|
||||
uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */
|
||||
uint8_t DeviceSizeMul : 3; /* Device size multiplier */
|
||||
} v1;
|
||||
struct {
|
||||
uint8_t Reserved1 : 6; /* Reserved */
|
||||
uint32_t DeviceSize : 22; /* Device Size */
|
||||
uint8_t Reserved2 : 1; /* Reserved */
|
||||
} v2;
|
||||
} version;
|
||||
|
||||
uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */
|
||||
uint8_t EraseSectorSize : 7; /* Erase group size multiplier */
|
||||
uint8_t WrProtectGrSize : 7; /* Write protect group size */
|
||||
uint8_t WrProtectGrEnable : 1; /* Write protect group enable */
|
||||
uint8_t Reserved2 : 2; /* Reserved */
|
||||
uint8_t WrSpeedFact : 3; /* Write speed factor */
|
||||
uint8_t MaxWrBlockLen : 4; /* Max. write data block length */
|
||||
uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */
|
||||
uint8_t Reserved3 : 5; /* Reserved */
|
||||
uint8_t FileFormatGrouop : 1; /* File format group */
|
||||
uint8_t CopyFlag : 1; /* Copy flag (OTP) */
|
||||
uint8_t PermWrProtect : 1; /* Permanent write protection */
|
||||
uint8_t TempWrProtect : 1; /* Temporary write protection */
|
||||
uint8_t FileFormat : 2; /* File Format */
|
||||
uint8_t Reserved4 : 2; /* Reserved */
|
||||
uint8_t crc : 7; /* Reserved */
|
||||
uint8_t Reserved5 : 1; /* always 1*/
|
||||
|
||||
} SD_CSD;
|
||||
|
||||
/**
|
||||
* @brief Card Identification Data: CID Register
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t ManufacturerID; /* ManufacturerID */
|
||||
char OEM_AppliID[2]; /* OEM/Application ID */
|
||||
char ProdName[5]; /* Product Name */
|
||||
uint8_t ProdRev; /* Product Revision */
|
||||
uint32_t ProdSN; /* Product Serial Number */
|
||||
uint8_t Reserved1; /* Reserved1 */
|
||||
uint8_t ManufactYear; /* Manufacturing Year */
|
||||
uint8_t ManufactMonth; /* Manufacturing Month */
|
||||
uint8_t CID_CRC; /* CID CRC */
|
||||
uint8_t Reserved2; /* always 1 */
|
||||
} SD_CID;
|
||||
|
||||
/**
|
||||
* @brief SD Card information structure
|
||||
*/
|
||||
typedef struct {
|
||||
SD_CSD Csd;
|
||||
SD_CID Cid;
|
||||
uint64_t CardCapacity; /*!< Card Capacity */
|
||||
uint32_t CardBlockSize; /*!< Card Block Size */
|
||||
uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */
|
||||
uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */
|
||||
} SD_CardInfo;
|
||||
|
||||
/**
|
||||
* @brief SD card max mount retry count
|
||||
*
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t sd_max_mount_retry_count();
|
||||
|
||||
/**
|
||||
* @brief Init sd card
|
||||
*
|
||||
* @param power_reset reset card power
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_init(bool power_reset);
|
||||
|
||||
/**
|
||||
* @brief Get card state
|
||||
*
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_get_card_state(void);
|
||||
|
||||
/**
|
||||
* @brief Get card info
|
||||
*
|
||||
* @param card_info
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_get_card_info(SD_CardInfo* card_info);
|
||||
|
||||
/**
|
||||
* @brief Read blocks
|
||||
*
|
||||
* @param data
|
||||
* @param address
|
||||
* @param blocks
|
||||
* @param timeout_ms
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Write blocks
|
||||
*
|
||||
* @param data
|
||||
* @param address
|
||||
* @param blocks
|
||||
* @param timeout_ms
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus
|
||||
sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Get card CSD register
|
||||
*
|
||||
* @param Cid
|
||||
* @return SdSpiStatus
|
||||
*/
|
||||
SdSpiStatus sd_get_cid(SD_CID* cid);
|
|
@ -1,17 +1,8 @@
|
|||
#include "user_diskio.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "user_diskio.h"
|
||||
#include "sector_cache.h"
|
||||
|
||||
static DSTATUS driver_check_status(BYTE lun) {
|
||||
UNUSED(lun);
|
||||
DSTATUS status = 0;
|
||||
if(sd_get_card_state() != SdSpiStatusOK) {
|
||||
status = STA_NOINIT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static DSTATUS driver_initialize(BYTE pdrv);
|
||||
static DSTATUS driver_status(BYTE pdrv);
|
||||
static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
|
@ -26,79 +17,6 @@ Diskio_drvTypeDef sd_fatfs_driver = {
|
|||
driver_ioctl,
|
||||
};
|
||||
|
||||
static inline bool sd_cache_get(uint32_t address, uint32_t* data) {
|
||||
uint8_t* cached_data = sector_cache_get(address);
|
||||
if(cached_data) {
|
||||
memcpy(data, cached_data, SD_BLOCK_SIZE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void sd_cache_put(uint32_t address, uint32_t* data) {
|
||||
sector_cache_put(address, (uint8_t*)data);
|
||||
}
|
||||
|
||||
static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) {
|
||||
sector_cache_invalidate_range(start_sector, end_sector);
|
||||
}
|
||||
|
||||
static inline void sd_cache_invalidate_all() {
|
||||
sector_cache_init();
|
||||
}
|
||||
|
||||
static bool sd_device_read(uint32_t* buff, uint32_t sector, uint32_t count) {
|
||||
bool result = false;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
if(sd_read_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
|
||||
|
||||
/* wait until the read operation is finished */
|
||||
result = true;
|
||||
while(sd_get_card_state() != SdSpiStatusOK) {
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) {
|
||||
bool result = false;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
if(sd_write_blocks(buff, sector, count, SD_TIMEOUT_MS) == SdSpiStatusOK) {
|
||||
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000);
|
||||
|
||||
/* wait until the Write operation is finished */
|
||||
result = true;
|
||||
while(sd_get_card_state() != SdSpiStatusOK) {
|
||||
if(furi_hal_cortex_timer_is_expired(timer)) {
|
||||
sd_cache_invalidate_all();
|
||||
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes a Drive
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
|
@ -115,13 +33,11 @@ static DSTATUS driver_initialize(BYTE pdrv) {
|
|||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS driver_status(BYTE pdrv) {
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
DSTATUS status = driver_check_status(pdrv);
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
UNUSED(pdrv);
|
||||
DSTATUS status = 0;
|
||||
if(furi_hal_sd_get_card_state() != FuriStatusOk) {
|
||||
status = STA_NOINIT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -136,43 +52,8 @@ static DSTATUS driver_status(BYTE pdrv) {
|
|||
*/
|
||||
static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
|
||||
UNUSED(pdrv);
|
||||
|
||||
bool result;
|
||||
bool single_sector = count == 1;
|
||||
|
||||
if(single_sector) {
|
||||
if(sd_cache_get(sector, (uint32_t*)buff)) {
|
||||
return RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
|
||||
if(!result) {
|
||||
uint8_t counter = sd_max_mount_retry_count();
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
SdSpiStatus status;
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
status = sd_init(true);
|
||||
} else {
|
||||
status = sd_init(false);
|
||||
}
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
result = sd_device_read((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
}
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
|
||||
if(single_sector && result == true) {
|
||||
sd_cache_put(sector, (uint32_t*)buff);
|
||||
}
|
||||
|
||||
return result ? RES_OK : RES_ERROR;
|
||||
FuriStatus status = furi_hal_sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
return status == FuriStatusOk ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,33 +66,8 @@ static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) {
|
|||
*/
|
||||
static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) {
|
||||
UNUSED(pdrv);
|
||||
bool result;
|
||||
|
||||
sd_cache_invalidate_range(sector, sector + count);
|
||||
|
||||
result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
|
||||
if(!result) {
|
||||
uint8_t counter = sd_max_mount_retry_count();
|
||||
|
||||
while(result == false && counter > 0 && hal_sd_detect()) {
|
||||
SdSpiStatus status;
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
status = sd_init(true);
|
||||
} else {
|
||||
status = sd_init(false);
|
||||
}
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
result = sd_device_write((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
}
|
||||
counter--;
|
||||
}
|
||||
}
|
||||
|
||||
return result ? RES_OK : RES_ERROR;
|
||||
FuriStatus status = furi_hal_sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count);
|
||||
return status == FuriStatusOk ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,12 +79,9 @@ static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT coun
|
|||
*/
|
||||
static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||
DRESULT res = RES_ERROR;
|
||||
SD_CardInfo CardInfo;
|
||||
FuriHalSdInfo sd_info;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
DSTATUS status = driver_check_status(pdrv);
|
||||
DSTATUS status = driver_status(pdrv);
|
||||
if(status & STA_NOINIT) return RES_NOTRDY;
|
||||
|
||||
switch(cmd) {
|
||||
|
@ -239,22 +92,22 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
|||
|
||||
/* Get number of sectors on the disk (DWORD) */
|
||||
case GET_SECTOR_COUNT:
|
||||
sd_get_card_info(&CardInfo);
|
||||
*(DWORD*)buff = CardInfo.LogBlockNbr;
|
||||
furi_hal_sd_info(&sd_info);
|
||||
*(DWORD*)buff = sd_info.logical_block_count;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get R/W sector size (WORD) */
|
||||
case GET_SECTOR_SIZE:
|
||||
sd_get_card_info(&CardInfo);
|
||||
*(WORD*)buff = CardInfo.LogBlockSize;
|
||||
furi_hal_sd_info(&sd_info);
|
||||
*(WORD*)buff = sd_info.logical_block_size;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* Get erase block size in unit of sector (DWORD) */
|
||||
case GET_BLOCK_SIZE:
|
||||
sd_get_card_info(&CardInfo);
|
||||
*(DWORD*)buff = CardInfo.LogBlockSize;
|
||||
furi_hal_sd_info(&sd_info);
|
||||
*(DWORD*)buff = sd_info.logical_block_size;
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
|
@ -262,8 +115,5 @@ static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
|||
res = RES_PARERR;
|
||||
}
|
||||
|
||||
furi_hal_sd_spi_handle = NULL;
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sd_spi_io.h"
|
||||
#include "fatfs/ff_gen_drv.h"
|
||||
|
||||
extern Diskio_drvTypeDef sd_fatfs_driver;
|
||||
|
|
|
@ -117,6 +117,14 @@ static void furi_hal_resources_init_input_pins(GpioMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
static void furi_hal_resources_init_gpio_pins(GpioMode mode) {
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(!gpio_pins[i].debug) {
|
||||
furi_hal_gpio_init(gpio_pins[i].pin, mode, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_resources_init_early() {
|
||||
furi_hal_bus_enable(FuriHalBusGPIOA);
|
||||
furi_hal_bus_enable(FuriHalBusGPIOB);
|
||||
|
@ -161,14 +169,7 @@ void furi_hal_resources_init_early() {
|
|||
furi_hal_gpio_write(&gpio_usb_dp, 0);
|
||||
|
||||
// External header pins
|
||||
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_resources_init_gpio_pins(GpioModeAnalog);
|
||||
}
|
||||
|
||||
void furi_hal_resources_deinit_early() {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,8 +23,8 @@ static FATFS* pfs = NULL;
|
|||
}
|
||||
|
||||
static bool flipper_update_mount_sd() {
|
||||
for(int i = 0; i < sd_max_mount_retry_count(); ++i) {
|
||||
if(sd_init((i % 2) == 0) != SdSpiStatusOK) {
|
||||
for(int i = 0; i < furi_hal_sd_max_mount_retry_count(); ++i) {
|
||||
if(furi_hal_sd_init((i % 2) == 0) != FuriStatusOk) {
|
||||
/* Next attempt will be without card reset, let it settle */
|
||||
furi_delay_ms(1000);
|
||||
continue;
|
||||
|
@ -51,7 +51,7 @@ static bool flipper_update_init() {
|
|||
furi_hal_spi_config_init();
|
||||
|
||||
fatfs_init();
|
||||
if(!hal_sd_detect()) {
|
||||
if(!furi_hal_sd_is_present()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,30 +4,82 @@
|
|||
* SD Card HAL API
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <furi_hal_spi_types.h>
|
||||
#include <furi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Init SD card detect
|
||||
*/
|
||||
void hal_sd_detect_init(void);
|
||||
typedef struct {
|
||||
uint64_t capacity; /*!< total capacity in bytes */
|
||||
uint32_t block_size; /*!< block size */
|
||||
uint32_t logical_block_count; /*!< logical capacity in blocks */
|
||||
uint32_t logical_block_size; /*!< logical block size in bytes */
|
||||
|
||||
/** Set SD card detect pin to low
|
||||
*/
|
||||
void hal_sd_detect_set_low(void);
|
||||
uint8_t manufacturer_id; /*!< manufacturer ID */
|
||||
char oem_id[3]; /*!< OEM ID, 2 characters + null terminator */
|
||||
char product_name[6]; /*!< product name, 5 characters + null terminator */
|
||||
uint8_t product_revision_major; /*!< product revision major */
|
||||
uint8_t product_revision_minor; /*!< product revision minor */
|
||||
uint32_t product_serial_number; /*!< product serial number */
|
||||
uint8_t manufacturing_month; /*!< manufacturing month */
|
||||
uint16_t manufacturing_year; /*!< manufacturing year */
|
||||
} FuriHalSdInfo;
|
||||
|
||||
/** Get SD card status
|
||||
*
|
||||
* @return true if SD card present, false if SD card not present
|
||||
/**
|
||||
* @brief Init SD card presence detection
|
||||
*/
|
||||
bool hal_sd_detect(void);
|
||||
void furi_hal_sd_presence_init();
|
||||
|
||||
/** Pointer to currently used SPI Handle */
|
||||
extern FuriHalSpiBusHandle* furi_hal_sd_spi_handle;
|
||||
/**
|
||||
* @brief Get SD card status
|
||||
* @return true if SD card is present
|
||||
*/
|
||||
bool furi_hal_sd_is_present();
|
||||
|
||||
/**
|
||||
* @brief SD card max mount retry count
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t furi_hal_sd_max_mount_retry_count();
|
||||
|
||||
/**
|
||||
* @brief Init SD card
|
||||
* @param power_reset reset card power
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_init(bool power_reset);
|
||||
|
||||
/**
|
||||
* @brief Read blocks from SD card
|
||||
* @param buff
|
||||
* @param sector
|
||||
* @param count
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_read_blocks(uint32_t* buff, uint32_t sector, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Write blocks to SD card
|
||||
* @param buff
|
||||
* @param sector
|
||||
* @param count
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_write_blocks(const uint32_t* buff, uint32_t sector, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Get SD card info
|
||||
* @param info
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_info(FuriHalSdInfo* info);
|
||||
|
||||
/**
|
||||
* @brief Get SD card state
|
||||
* @return FuriStatus
|
||||
*/
|
||||
FuriStatus furi_hal_sd_get_card_state();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -11,10 +11,21 @@ bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* man
|
|||
return true;
|
||||
}
|
||||
|
||||
bool flipper_application_manifest_is_compatible(
|
||||
bool flipper_application_manifest_is_too_old(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface) {
|
||||
if(manifest->base.api_version.major != api_interface->api_version_major /* ||
|
||||
if(manifest->base.api_version.major < api_interface->api_version_major /* ||
|
||||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flipper_application_manifest_is_too_new(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface) {
|
||||
if(manifest->base.api_version.major > api_interface->api_version_major /* ||
|
||||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -54,14 +54,25 @@ typedef FlipperApplicationManifestV1 FlipperApplicationManifest;
|
|||
*/
|
||||
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest);
|
||||
|
||||
/**
|
||||
* @brief Check if manifest is compatible with current ELF API interface
|
||||
/** Check if API Version declared in manifest is older than firmware ELF API interface
|
||||
*
|
||||
* @param manifest The manifest
|
||||
* @param api_interface The api interface
|
||||
*
|
||||
* @param manifest
|
||||
* @param api_interface
|
||||
* @return bool
|
||||
*/
|
||||
bool flipper_application_manifest_is_compatible(
|
||||
bool flipper_application_manifest_is_too_old(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface);
|
||||
|
||||
/** Check if API Version declared in manifest is newer than firmware ELF API interface
|
||||
*
|
||||
* @param manifest The manifest
|
||||
* @param api_interface The api interface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool flipper_application_manifest_is_too_new(
|
||||
const FlipperApplicationManifest* manifest,
|
||||
const ElfApiInterface* api_interface);
|
||||
|
||||
|
|
|
@ -101,9 +101,14 @@ static FlipperApplicationPreloadStatus
|
|||
return FlipperApplicationPreloadStatusTargetMismatch;
|
||||
}
|
||||
|
||||
if(!flipper_application_manifest_is_compatible(
|
||||
if(!flipper_application_manifest_is_too_old(
|
||||
&app->manifest, elf_file_get_api_interface(app->elf))) {
|
||||
return FlipperApplicationPreloadStatusApiMismatch;
|
||||
return FlipperApplicationPreloadStatusApiTooOld;
|
||||
}
|
||||
|
||||
if(!flipper_application_manifest_is_too_new(
|
||||
&app->manifest, elf_file_get_api_interface(app->elf))) {
|
||||
return FlipperApplicationPreloadStatusApiTooNew;
|
||||
}
|
||||
|
||||
return FlipperApplicationPreloadStatusSuccess;
|
||||
|
@ -257,7 +262,8 @@ static const char* preload_status_strings[] = {
|
|||
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
|
||||
[FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
|
||||
[FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
|
||||
[FlipperApplicationPreloadStatusApiMismatch] = "API version mismatch",
|
||||
[FlipperApplicationPreloadStatusApiTooOld] = "Update Application to use with this Firmware (ApiTooOld)",
|
||||
[FlipperApplicationPreloadStatusApiTooNew] = "Update Firmware to use with this Application (ApiTooNew)",
|
||||
[FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
|
||||
};
|
||||
|
||||
|
@ -265,7 +271,7 @@ static const char* load_status_strings[] = {
|
|||
[FlipperApplicationLoadStatusSuccess] = "Success",
|
||||
[FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
|
||||
[FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
|
||||
[FlipperApplicationLoadStatusMissingImports] = "Found unsatisfied imports",
|
||||
[FlipperApplicationLoadStatusMissingImports] = "Update Firmware to use with this Application (MissingImports)",
|
||||
};
|
||||
|
||||
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
|
||||
|
|
|
@ -21,7 +21,8 @@ typedef enum {
|
|||
FlipperApplicationPreloadStatusUnspecifiedError,
|
||||
FlipperApplicationPreloadStatusInvalidFile,
|
||||
FlipperApplicationPreloadStatusInvalidManifest,
|
||||
FlipperApplicationPreloadStatusApiMismatch,
|
||||
FlipperApplicationPreloadStatusApiTooOld,
|
||||
FlipperApplicationPreloadStatusApiTooNew,
|
||||
FlipperApplicationPreloadStatusTargetMismatch,
|
||||
} FlipperApplicationPreloadStatus;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <furi_hal.h>
|
||||
|
||||
#define CONTRAST_ERC 31
|
||||
#define CONTRAST_MGG 31
|
||||
#define CONTRAST_MGG 27
|
||||
|
||||
uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
|
||||
UNUSED(u8x8);
|
||||
|
|
|
@ -67,7 +67,10 @@ class FlipperFormatFile:
|
|||
self.writeLine("")
|
||||
|
||||
def writeComment(self, text: str):
|
||||
if text:
|
||||
self.writeLine(f"# {text}")
|
||||
else:
|
||||
self.writeLine("#")
|
||||
|
||||
def getHeader(self):
|
||||
if self.cursor != 0 and len(self.lines) == 0:
|
||||
|
|
79
scripts/infrared.py
Executable file
79
scripts/infrared.py
Executable file
|
@ -0,0 +1,79 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from os import path
|
||||
|
||||
from flipper.app import App
|
||||
from flipper.utils.fff import *
|
||||
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
# Subparsers
|
||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||
|
||||
self.parser_cleanup = self.subparsers.add_parser(
|
||||
"cleanup", help="Cleanup duplicate remotes"
|
||||
)
|
||||
self.parser_cleanup.add_argument("filename", type=str)
|
||||
self.parser_cleanup.set_defaults(func=self.cleanup)
|
||||
|
||||
def cleanup(self):
|
||||
f = FlipperFormatFile()
|
||||
f.load(self.args.filename)
|
||||
|
||||
filetype, version = f.getHeader()
|
||||
if filetype != "IR library file" or version != 1:
|
||||
self.logger.error(f"Incorrect file type({filetype}) or version({version})")
|
||||
return 1
|
||||
|
||||
data = []
|
||||
unique = {}
|
||||
while True:
|
||||
try:
|
||||
d = {}
|
||||
d["name"] = f.readKey("name")
|
||||
d["type"] = f.readKey("type")
|
||||
key = None
|
||||
if d["type"] == "parsed":
|
||||
d["protocol"] = f.readKey("protocol")
|
||||
d["address"] = f.readKey("address")
|
||||
d["command"] = f.readKey("command")
|
||||
key = f'{d["protocol"]}{d["address"]}{d["command"]}'
|
||||
elif d["type"] == "raw":
|
||||
d["frequency"] = f.readKey("frequency")
|
||||
d["duty_cycle"] = f.readKey("duty_cycle")
|
||||
d["data"] = f.readKey("data")
|
||||
key = f'{d["frequency"]}{d["duty_cycle"]}{d["data"]}'
|
||||
else:
|
||||
raise Exception(f'Unknown type: {d["type"]}')
|
||||
if not key in unique:
|
||||
unique[key] = d
|
||||
data.append(d)
|
||||
else:
|
||||
self.logger.warn(f"Duplicate key: {key}")
|
||||
except EOFError:
|
||||
break
|
||||
# Form new file
|
||||
f = FlipperFormatFile()
|
||||
f.setHeader(filetype, version)
|
||||
for i in data:
|
||||
f.writeComment(None)
|
||||
f.writeKey("name", i["name"])
|
||||
f.writeKey("type", i["type"])
|
||||
if i["type"] == "parsed":
|
||||
f.writeKey("protocol", i["protocol"])
|
||||
f.writeKey("address", i["address"])
|
||||
f.writeKey("command", i["command"])
|
||||
elif i["type"] == "raw":
|
||||
f.writeKey("frequency", i["frequency"])
|
||||
f.writeKey("duty_cycle", i["duty_cycle"])
|
||||
f.writeKey("data", i["data"])
|
||||
else:
|
||||
raise Exception(f'Unknown type: {i["type"]}')
|
||||
f.save(self.args.filename)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()()
|
|
@ -22,7 +22,8 @@ class Main(App):
|
|||
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
||||
self._add_args(self.parser_set)
|
||||
self.parser_set.set_defaults(func=self.set)
|
||||
# Set command
|
||||
|
||||
# Recover command
|
||||
self.parser_recover = self.subparsers.add_parser(
|
||||
"recover", help="Recover Option Bytes"
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue