Login
1 branch 0 tags
Ben (Desktop/Arch) Added clang-format 2f88780 1 month ago 66 Commits
moon / esp32 / main / storage_esp32.c
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "pinout.h"
#include "sdmmc_cmd.h"
#include "storage.h"
#include "tusb_msc_storage.h"
#include "usb_msc.h"

#define MOUNT_POINT "/sdcard"

static const char* TAG = "storage";
static bool mounted = false;
static sdmmc_card_t* card = NULL;

static void msc_mount_changed_cb(tinyusb_msc_event_t* event) {
	mounted = event->mount_changed_data.is_mounted;
	printf("%s: MSC mount changed: %s\n", TAG,
	       mounted ? "mounted" : "unmounted");
}

bool storage_init(void) {
	esp_err_t ret;

	// SPI bus configuration
	spi_bus_config_t bus_cfg = {
	    .mosi_io_num = SD_PIN_MOSI,
	    .miso_io_num = SD_PIN_MISO,
	    .sclk_io_num = SD_PIN_CLK,
	    .quadwp_io_num = -1,
	    .quadhd_io_num = -1,
	    .max_transfer_sz = 4096,
	};

	// Initialize SPI bus (SPI3_HOST to avoid conflict with display on SPI2)
	ret = spi_bus_initialize(SPI3_HOST, &bus_cfg, SDSPI_DEFAULT_DMA);
	if (ret != ESP_OK) {
		printf("%s: Failed to initialize SPI bus: %s\n", TAG,
		       esp_err_to_name(ret));
		return false;
	}

	// Initialize the SPI SD slot
	sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
	slot_config.gpio_cs = SD_PIN_CS;
	slot_config.host_id = SPI3_HOST;

	sdmmc_host_t host = SDSPI_HOST_DEFAULT();
	host.slot = SPI3_HOST;

	// Initialize SD card at the lower level
	sdspi_dev_handle_t sdspi_handle;
	ret = sdspi_host_init();
	if (ret != ESP_OK) {
		printf("%s: Failed to init sdspi host: %s\n", TAG,
		       esp_err_to_name(ret));
		spi_bus_free(SPI3_HOST);
		return false;
	}

	ret = sdspi_host_init_device(&slot_config, &sdspi_handle);
	if (ret != ESP_OK) {
		printf("%s: Failed to init sdspi device: %s\n", TAG,
		       esp_err_to_name(ret));
		sdspi_host_deinit();
		spi_bus_free(SPI3_HOST);
		return false;
	}

	// Update host slot to the device handle (must be done before card init)
	host.slot = sdspi_handle;

	// Allocate and initialize the card
	card = calloc(1, sizeof(sdmmc_card_t));
	if (!card) {
		printf("%s: Failed to allocate card\n", TAG);
		sdspi_host_deinit();
		spi_bus_free(SPI3_HOST);
		return false;
	}

	ret = sdmmc_card_init(&host, card);
	if (ret != ESP_OK) {
		printf("%s: Failed to initialize SD card: %s\n", TAG,
		       esp_err_to_name(ret));
		free(card);
		card = NULL;
		sdspi_host_deinit();
		spi_bus_free(SPI3_HOST);
		return false;
	}

	sdmmc_card_print_info(stdout, card);

	// Initialize TinyUSB MSC with this card
	const tinyusb_msc_sdmmc_config_t msc_config = {
	    .card = card,
	    .callback_mount_changed = msc_mount_changed_cb,
	    .mount_config =
	        {
	            .format_if_mount_failed = false,
	            .max_files = 5,
	            .allocation_unit_size = 16 * 1024,
	        },
	};
	ret = tinyusb_msc_storage_init_sdmmc(&msc_config);
	if (ret != ESP_OK) {
		printf("%s: Failed to init MSC storage: %s\n", TAG,
		       esp_err_to_name(ret));
		free(card);
		card = NULL;
		sdspi_host_deinit();
		spi_bus_free(SPI3_HOST);
		return false;
	}

	// Mount via TinyUSB MSC storage (handles VFS+FATFS registration)
	ret = tinyusb_msc_storage_mount(MOUNT_POINT);
	if (ret != ESP_OK) {
		printf("%s: Failed to mount filesystem: %s\n", TAG,
		       esp_err_to_name(ret));
		tinyusb_msc_storage_deinit();
		free(card);
		card = NULL;
		sdspi_host_deinit();
		spi_bus_free(SPI3_HOST);
		return false;
	}

	// Initialize TinyUSB driver for USB MSC
	usb_msc_init();

	mounted = true;
	printf("%s: SD card mounted at %s\n", TAG, MOUNT_POINT);
	return true;
}

void storage_unmount(void) {
	if (!mounted) {
		return;
	}
	tinyusb_msc_storage_unmount();
	mounted = false;
}

bool storage_remount(void) {
	if (mounted) {
		return true;
	}
	esp_err_t ret = tinyusb_msc_storage_mount(MOUNT_POINT);
	if (ret != ESP_OK) {
		printf("%s: Failed to remount: %s\n", TAG, esp_err_to_name(ret));
		return false;
	}
	mounted = true;
	return true;
}

bool storage_mkdir(const char* path) {
	if (!mounted || !path) {
		return false;
	}

	char full_path[STORAGE_MAX_PATH];
	snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);

	struct stat st;
	if (stat(full_path, &st) == 0) {
		return S_ISDIR(st.st_mode);
	}
	return mkdir(full_path, 0755) == 0;
}

bool storage_remove(const char* path) {
	if (!mounted || !path) {
		return false;
	}

	const char* rel = (path[0] == '/') ? path + 1 : path;
	char full_path[STORAGE_MAX_PATH];
	snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, rel);

	if (remove(full_path) != 0) {
		struct stat st;
		if (stat(full_path, &st) == 0) {
			printf("%s: Failed to remove %s\n", TAG, full_path);
			return false;
		}
	}
	return true;
}

bool storage_is_mounted(void) {
	return mounted;
}

int storage_list_dir(const char* path, storage_entry_t* entries, int max) {
	if (!mounted || !entries || max <= 0) {
		return -1;
	}

	char full_path[STORAGE_MAX_PATH];
	if (path == NULL || path[0] == '\0' || strcmp(path, "/") == 0) {
		snprintf(full_path, sizeof(full_path), "%s", MOUNT_POINT);
	} else {
		snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);
	}

	DIR* dir = opendir(full_path);
	if (!dir) {
		printf("%s: Failed to open directory %s\n", TAG, full_path);
		return -1;
	}

	int count = 0;
	struct dirent* ent;
	while ((ent = readdir(dir)) != NULL && count < max) {
		// Skip . and ..
		if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
			continue;
		}

		strncpy(entries[count].name, ent->d_name, STORAGE_MAX_NAME - 1);
		entries[count].name[STORAGE_MAX_NAME - 1] = '\0';

		// Get file info
		char file_path[STORAGE_MAX_PATH * 2];
		snprintf(file_path, sizeof(file_path), "%s/%s", full_path, ent->d_name);

		struct stat st;
		if (stat(file_path, &st) == 0) {
			entries[count].type =
			    S_ISDIR(st.st_mode) ? STORAGE_TYPE_DIR : STORAGE_TYPE_FILE;
			entries[count].size = (size_t)st.st_size;
		} else {
			entries[count].type = STORAGE_TYPE_FILE;
			entries[count].size = 0;
		}

		count++;
	}

	closedir(dir);
	return count;
}

storage_file_t storage_open(const char* path, const char* mode) {
	if (!mounted || !path || !mode) {
		return NULL;
	}

	char full_path[STORAGE_MAX_PATH];
	snprintf(full_path, sizeof(full_path), "%s/%s", MOUNT_POINT, path);

	FILE* f = fopen(full_path, mode);
	if (!f) {
		printf("%s: Failed to open %s\n", TAG, full_path);
	}
	return (storage_file_t)f;
}

size_t storage_read(storage_file_t file, void* buf, size_t size) {
	if (!file || !buf) {
		return 0;
	}
	return fread(buf, 1, size, (FILE*)file);
}

size_t storage_write(storage_file_t file, const void* buf, size_t size) {
	if (!file || !buf) {
		return 0;
	}
	return fwrite(buf, 1, size, (FILE*)file);
}

int storage_close(storage_file_t file) {
	if (!file) {
		return -1;
	}
	return fclose((FILE*)file);
}