Merge branch 'fz-dev' into dev

This commit is contained in:
MX
2023-02-08 12:09:57 +03:00
199 changed files with 6363 additions and 2393 deletions

View File

@@ -5,28 +5,6 @@
#include <furi_hal.h>
#include <lib/toolbox/args.h>
typedef struct {
const GpioPin* pin;
const char* name;
const bool debug;
} CliCommandGpio;
const CliCommandGpio cli_command_gpio_pins[] = {
{.pin = &gpio_ext_pc0, .name = "PC0", .debug = false},
{.pin = &gpio_ext_pc1, .name = "PC1", .debug = false},
{.pin = &gpio_ext_pc3, .name = "PC3", .debug = false},
{.pin = &gpio_ext_pb2, .name = "PB2", .debug = false},
{.pin = &gpio_ext_pb3, .name = "PB3", .debug = false},
{.pin = &gpio_ext_pa4, .name = "PA4", .debug = false},
{.pin = &gpio_ext_pa6, .name = "PA6", .debug = false},
{.pin = &gpio_ext_pa7, .name = "PA7", .debug = false},
/* Dangerous pins, may damage hardware */
{.pin = &gpio_infrared_rx, .name = "PA0", .debug = true},
{.pin = &gpio_usart_rx, .name = "PB7", .debug = true},
{.pin = &gpio_speaker, .name = "PB8", .debug = true},
{.pin = &gpio_infrared_tx, .name = "PB9", .debug = true},
};
void cli_command_gpio_print_usage() {
printf("Usage:\r\n");
printf("gpio <cmd> <args>\r\n");
@@ -38,9 +16,9 @@ void cli_command_gpio_print_usage() {
static bool pin_name_to_int(FuriString* pin_name, size_t* result) {
bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);
for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) {
if(furi_string_equal(pin_name, cli_command_gpio_pins[i].name)) {
if(!cli_command_gpio_pins[i].debug || is_debug_mode) {
for(size_t i = 0; i < gpio_pins_count; i++) {
if(furi_string_equal(pin_name, gpio_pins[i].name)) {
if(!gpio_pins[i].debug || is_debug_mode) {
*result = i;
return true;
}
@@ -53,9 +31,9 @@ static bool pin_name_to_int(FuriString* pin_name, size_t* result) {
static void gpio_print_pins(void) {
printf("Wrong pin name. Available pins: ");
bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug);
for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) {
if(!cli_command_gpio_pins[i].debug || is_debug_mode) {
printf("%s ", cli_command_gpio_pins[i].name);
for(size_t i = 0; i < gpio_pins_count; i++) {
if(!gpio_pins[i].debug || is_debug_mode) {
printf("%s ", gpio_pins[i].name);
}
}
}
@@ -113,7 +91,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
return;
}
if(cli_command_gpio_pins[num].debug) { //-V779
if(gpio_pins[num].debug) { //-V779
printf(
"Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n");
char c = cli_getc(cli);
@@ -124,12 +102,12 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) {
}
if(value == 1) { // output
furi_hal_gpio_write(cli_command_gpio_pins[num].pin, false);
furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeOutputPushPull);
printf("Pin %s is now an output (low)", cli_command_gpio_pins[num].name);
furi_hal_gpio_write(gpio_pins[num].pin, false);
furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeOutputPushPull);
printf("Pin %s is now an output (low)", gpio_pins[num].name);
} else { // input
furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeInput);
printf("Pin %s is now an input", cli_command_gpio_pins[num].name);
furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeInput);
printf("Pin %s is now an input", gpio_pins[num].name);
}
}
@@ -144,15 +122,14 @@ void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) {
}
if(LL_GPIO_MODE_INPUT != //-V779
LL_GPIO_GetPinMode(
cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) {
printf("Err: pin %s is not set as an input.", cli_command_gpio_pins[num].name);
LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) {
printf("Err: pin %s is not set as an input.", gpio_pins[num].name);
return;
}
uint8_t val = !!furi_hal_gpio_read(cli_command_gpio_pins[num].pin);
uint8_t val = !!furi_hal_gpio_read(gpio_pins[num].pin);
printf("Pin %s <= %u", cli_command_gpio_pins[num].name, val);
printf("Pin %s <= %u", gpio_pins[num].name, val);
}
void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
@@ -174,14 +151,13 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
}
if(LL_GPIO_MODE_OUTPUT != //-V779
LL_GPIO_GetPinMode(
cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) {
printf("Err: pin %s is not set as an output.", cli_command_gpio_pins[num].name);
LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) {
printf("Err: pin %s is not set as an output.", gpio_pins[num].name);
return;
}
// Extra check if debug pins used
if(cli_command_gpio_pins[num].debug) {
if(gpio_pins[num].debug) {
printf(
"Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n");
char c = cli_getc(cli);
@@ -191,8 +167,8 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) {
}
}
furi_hal_gpio_write(cli_command_gpio_pins[num].pin, !!value);
printf("Pin %s => %u", cli_command_gpio_pins[num].name, !!value);
furi_hal_gpio_write(gpio_pins[num].pin, !!value);
printf("Pin %s => %u", gpio_pins[num].name, !!value);
}
void cli_command_gpio(Cli* cli, FuriString* args, void* context) {

View File

@@ -1,4 +1,4 @@
#include "dialogs/dialogs_message.h"
#include "dialogs_message.h"
#include "dialogs_i.h"
#include <toolbox/api_lock.h>
#include <assets_icons.h>

View File

@@ -1,6 +1,7 @@
#include "dialogs_i.h"
#include <gui/modules/file_browser.h>
#include <toolbox/api_lock.h>
#include "gui/modules/file_browser.h"
typedef struct {
FuriApiLock lock;

View File

@@ -1,8 +1,9 @@
#pragma once
#include <core/pubsub.h>
#include "gui/view.h"
#include "helpers/dolphin_deed.h"
#include <gui/view.h>
#include <core/pubsub.h>
#include <stdbool.h>
#ifdef __cplusplus

View File

@@ -1,16 +1,17 @@
#include "elements.h"
#include "m-core.h"
#include <m-core.h>
#include <assets_icons.h>
#include "furi_hal_resources.h"
#include <furi_hal_resources.h>
#include <furi_hal.h>
#include "gui/canvas.h"
#include <gui/canvas.h>
#include <gui/icon_i.h>
#include <gui/icon_animation_i.h>
#include <furi.h>
#include "canvas_i.h"
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

View File

@@ -1,4 +1,3 @@
#include "gui/canvas.h"
#include "gui_i.h"
#include <assets_icons.h>

View File

@@ -1,12 +1,15 @@
#include "button_menu.h"
#include "gui/canvas.h"
#include "gui/elements.h"
#include "input/input.h"
#include <m-array.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <input/input.h>
#include <furi.h>
#include <stdint.h>
#include <assets_icons.h>
#include <stdint.h>
#include <m-array.h>
#define ITEM_FIRST_OFFSET 17
#define ITEM_NEXT_OFFSET 4
#define ITEM_HEIGHT 14

View File

@@ -1,12 +1,15 @@
#include "button_panel.h"
#include "furi_hal_resources.h"
#include "gui/canvas.h"
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <furi_hal_resources.h>
#include <stdint.h>
#include <m-array.h>
#include <m-i-list.h>
#include <m-list.h>
#include <furi.h>
#include <gui/elements.h>
#include <stdint.h>
typedef struct {
// uint16_t to support multi-screen, wide button panel

View File

@@ -1,8 +1,9 @@
#include <furi.h>
#include <gui/elements.h>
#include <assets_icons.h>
#include "byte_input.h"
#include <gui/elements.h>
#include <furi.h>
#include <assets_icons.h>
struct ByteInput {
View* view;
};

View File

@@ -1,14 +1,17 @@
#include "file_browser.h"
#include "assets_icons.h"
#include "file_browser_worker.h"
#include <gui/elements.h>
#include <assets_icons.h>
#include <toolbox/path.h>
#include <furi.h>
#include <furi_hal_resources.h>
#include <core/check.h>
#include <core/common_defines.h>
#include <core/log.h>
#include "furi_hal_resources.h"
#include <m-array.h>
#include <gui/elements.h>
#include <furi.h>
#include "toolbox/path.h"
#define LIST_ITEMS 5u
#define MAX_LEN_PX 110

View File

@@ -1,13 +1,16 @@
#include "file_browser_worker.h"
#include <storage/filesystem_api_defines.h>
#include <storage/storage.h>
#include <toolbox/path.h>
#include <core/check.h>
#include <core/common_defines.h>
#include "storage/filesystem_api_defines.h"
#include <furi.h>
#include <m-array.h>
#include <stdbool.h>
#include <storage/storage.h>
#include <furi.h>
#include <stddef.h>
#include "toolbox/path.h"
#define TAG "BrowserWorker"

View File

@@ -1,13 +1,14 @@
#include <stdint.h>
#include <furi.h>
#include <assets_icons.h>
#include "loading.h"
#include <gui/icon_animation.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <gui/view.h>
#include <input/input.h>
#include "loading.h"
#include <furi.h>
#include <assets_icons.h>
#include <stdint.h>
struct Loading {
View* view;

View File

@@ -1,9 +1,9 @@
#include "menu.h"
#include <m-array.h>
#include <gui/elements.h>
#include <assets_icons.h>
#include <furi.h>
#include <m-array.h>
struct Menu {
View* view;

View File

@@ -1,8 +1,8 @@
#include "submenu.h"
#include <m-array.h>
#include <gui/elements.h>
#include <furi.h>
#include <m-array.h>
struct Submenu {
View* view;

View File

@@ -1,7 +1,7 @@
#include "text_box.h"
#include "gui/canvas.h"
#include <furi.h>
#include <gui/canvas.h>
#include <gui/elements.h>
#include <furi.h>
#include <stdint.h>
struct TextBox {

View File

@@ -1,6 +1,6 @@
#include <furi.h>
#include "validators.h"
#include <storage/storage.h>
#include <furi.h>
struct ValidatorIsFile {
char* app_path_folder;

View File

@@ -1,6 +1,7 @@
#pragma once
#include <core/common_defines.h>
#include <core/string.h>
#ifdef __cplusplus
extern "C" {

View File

@@ -1,8 +1,8 @@
#include "variable_item_list.h"
#include "gui/canvas.h"
#include <m-array.h>
#include <furi.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <furi.h>
#include <m-array.h>
#include <stdint.h>
struct VariableItem {

View File

@@ -1,7 +1,7 @@
#include <furi.h>
#include "widget.h"
#include <m-array.h>
#include "widget_elements/widget_element_i.h"
#include <furi.h>
#include <m-array.h>
ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST);

View File

@@ -1,8 +1,9 @@
#include "gui/view.h"
#include <core/memmgr.h>
#include "view_stack.h"
#include "view_i.h"
#include <gui/view.h>
#include <core/memmgr.h>
#define MAX_VIEWS 3
typedef struct {

View File

@@ -1,4 +1,4 @@
#include "furi_hal_light.h"
#include <furi_hal_light.h>
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>

View File

@@ -1,4 +1,4 @@
#include "furi_hal_resources.h"
#include <furi_hal_resources.h>
#include "notification.h"
#include "notification_messages_notes.h"
#include <stddef.h>

View File

@@ -1,6 +1,5 @@
#include <furi.h>
#include <furi_hal.h>
#include <stm32_adafruit_sd.h>
#include <cli/cli.h>
#include <lib/toolbox/args.h>
@@ -61,28 +60,26 @@ static void storage_cli_info(Cli* cli, FuriString* path) {
}
} else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) {
SDInfo sd_info;
SD_CID sd_cid;
FS_Error error = storage_sd_info(api, &sd_info);
BSP_SD_GetCIDRegister(&sd_cid);
if(error != FSE_OK) {
storage_cli_print_error(error);
} else {
printf(
"Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n"
"%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n",
"%02x%s %s v%i.%i\r\nSN:%04lx %02i/%i\r\n",
sd_info.label,
sd_api_get_fs_type_text(sd_info.fs_type),
sd_info.kb_total,
sd_info.kb_free,
sd_cid.ManufacturerID,
sd_cid.OEM_AppliID,
sd_cid.ProdName,
sd_cid.ProdRev >> 4,
sd_cid.ProdRev & 0xf,
sd_cid.ProdSN,
sd_cid.ManufactMonth,
sd_cid.ManufactYear + 2000);
sd_info.manufacturer_id,
sd_info.oem_id,
sd_info.product_name,
sd_info.product_revision_major,
sd_info.product_revision_minor,
sd_info.product_serial_number,
sd_info.manufacturing_month,
sd_info.manufacturing_year);
}
} else {
storage_cli_print_usage();

View File

@@ -12,9 +12,7 @@
#define TAG "StorageAPI"
#define S_API_PROLOGUE \
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \
furi_check(semaphore != NULL);
#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked();
#define S_FILE_API_PROLOGUE \
Storage* storage = file->storage; \
@@ -24,13 +22,12 @@
furi_check( \
furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \
FuriStatusOk); \
furi_semaphore_acquire(semaphore, FuriWaitForever); \
furi_semaphore_free(semaphore);
api_lock_wait_unlock_and_free(lock)
#define S_API_MESSAGE(_command) \
SAReturn return_data; \
StorageMessage message = { \
.semaphore = semaphore, \
.lock = lock, \
.command = _command, \
.data = &data, \
.return_data = &return_data, \

View File

@@ -31,29 +31,13 @@ void storage_file_clear(StorageFile* obj) {
/****************** storage data ******************/
void storage_data_init(StorageData* storage) {
storage->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
furi_check(storage->mutex != NULL);
storage->data = NULL;
storage->status = StorageStatusNotReady;
StorageFileList_init(storage->files);
}
bool storage_data_lock(StorageData* storage) {
return (furi_mutex_acquire(storage->mutex, FuriWaitForever) == FuriStatusOk);
}
bool storage_data_unlock(StorageData* storage) {
return (furi_mutex_release(storage->mutex) == FuriStatusOk);
}
StorageStatus storage_data_status(StorageData* storage) {
StorageStatus status;
storage_data_lock(storage);
status = storage->status;
storage_data_unlock(storage);
return status;
return storage->status;
}
const char* storage_data_status_text(StorageData* storage) {

View File

@@ -38,8 +38,6 @@ void storage_file_set(StorageFile* obj, const StorageFile* src);
void storage_file_clear(StorageFile* obj);
void storage_data_init(StorageData* storage);
bool storage_data_lock(StorageData* storage);
bool storage_data_unlock(StorageData* storage);
StorageStatus storage_data_status(StorageData* storage);
const char* storage_data_status_text(StorageData* storage);
void storage_data_timestamp(StorageData* storage);
@@ -57,7 +55,6 @@ struct StorageData {
const FS_Api* fs_api;
StorageApi api;
void* data;
FuriMutex* mutex;
StorageStatus status;
StorageFileList_t files;
uint32_t timestamp;

View File

@@ -1,5 +1,6 @@
#pragma once
#include <furi.h>
#include <toolbox/api_lock.h>
#ifdef __cplusplus
extern "C" {
@@ -130,7 +131,7 @@ typedef enum {
} StorageCommand;
typedef struct {
FuriSemaphore* semaphore;
FuriApiLock lock;
StorageCommand command;
SAData* data;
SAReturn* return_data;

View File

@@ -2,15 +2,7 @@
#include <m-list.h>
#include <m-dict.h>
#define FS_CALL(_storage, _fn) \
storage_data_lock(_storage); \
ret = _storage->fs_api->_fn; \
storage_data_unlock(_storage);
#define ST_CALL(_storage, _fn) \
storage_data_lock(_storage); \
ret = _storage->api._fn; \
storage_data_unlock(_storage);
#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn;
static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) {
furi_check(type == ST_EXT || type == ST_INT);
@@ -44,16 +36,11 @@ static const char* remove_vfs(const char* path) {
static StorageType storage_get_type_by_path(Storage* app, const char* path) {
StorageType type = ST_ERROR;
if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) &&
memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
type = ST_EXT;
} else if(
strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) &&
memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
} else if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
type = ST_INT;
} else if(
strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) &&
memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
} else if(memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) {
type = ST_ANY;
}
@@ -68,21 +55,15 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) {
}
static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) {
if(memcmp(
furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) ==
0) {
if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) {
switch(real_storage) {
case ST_EXT:
furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]);
furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]);
furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]);
furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]);
furi_string_replace_at(
path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX);
break;
case ST_INT:
furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]);
furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]);
furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]);
furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]);
furi_string_replace_at(
path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX);
break;
default:
break;
@@ -604,7 +585,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
break;
}
furi_semaphore_release(message->semaphore);
api_lock_unlock(message->lock);
}
void storage_process_message(Storage* app, StorageMessage* message) {

View File

@@ -23,6 +23,16 @@ typedef struct {
uint16_t cluster_size;
uint16_t sector_size;
char label[SD_LABEL_LENGTH];
uint8_t manufacturer_id;
char oem_id[3];
char product_name[6];
uint8_t product_revision_major;
uint8_t product_revision_minor;
uint32_t product_serial_number;
uint8_t manufacturing_month;
uint16_t manufacturing_year;
FS_Error error;
} SDInfo;

View File

@@ -26,12 +26,10 @@ static FS_Error storage_ext_parse_error(SDError error);
static bool sd_mount_card(StorageData* storage, bool notify) {
bool result = false;
uint8_t counter = BSP_SD_MaxMountRetryCount();
uint8_t counter = sd_max_mount_retry_count();
uint8_t bsp_result;
SDData* sd_data = storage->data;
storage_data_lock(storage);
while(result == false && counter > 0 && hal_sd_detect()) {
if(notify) {
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
@@ -41,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
if((counter % 2) == 0) {
// power reset sd card
bsp_result = BSP_SD_Init(true);
bsp_result = sd_init(true);
} else {
bsp_result = BSP_SD_Init(false);
bsp_result = sd_init(false);
}
if(bsp_result) {
@@ -91,7 +89,6 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
}
storage_data_timestamp(storage);
storage_data_unlock(storage);
return result;
}
@@ -100,14 +97,12 @@ FS_Error sd_unmount_card(StorageData* storage) {
SDData* sd_data = storage->data;
SDError error;
storage_data_lock(storage);
storage->status = StorageStatusNotReady;
error = FR_DISK_ERR;
// TODO do i need to close the files?
f_mount(0, sd_data->path, 0);
storage_data_unlock(storage);
return storage_ext_parse_error(error);
}
@@ -120,8 +115,6 @@ FS_Error sd_format_card(StorageData* storage) {
SDData* sd_data = storage->data;
SDError error;
storage_data_lock(storage);
work_area = malloc(_MAX_SS);
error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS);
free(work_area);
@@ -138,8 +131,6 @@ FS_Error sd_format_card(StorageData* storage) {
storage->status = StorageStatusOK;
} while(false);
storage_data_unlock(storage);
return storage_ext_parse_error(error);
#endif
}
@@ -156,14 +147,12 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
memset(sd_info, 0, sizeof(SDInfo));
// get fs info
storage_data_lock(storage);
error = f_getlabel(sd_data->path, sd_info->label, NULL);
if(error == FR_OK) {
#ifndef FURI_RAM_EXEC
error = f_getfree(sd_data->path, &free_clusters, &fs);
#endif
}
storage_data_unlock(storage);
if(error == FR_OK) {
// calculate size
@@ -210,6 +199,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
#endif
}
SD_CID cid;
SdSpiStatus status = sd_get_cid(&cid);
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;
}
return storage_ext_parse_error(error);
}