/main/sd.c
#include <ctype.h>
#include <dirent.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include "diskio_impl.h"
#include "diskio_sdmmc.h"
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"

#include "gfx.h"
#include "keyboard.h"
#include "sd.h"

#define TAG "sd"

sdmmc_host_t host = SDSPI_HOST_DEFAULT();
const char mount_point[] = "/sd";
FATFS* fs;

bool sd_card_detect() {
	return !(bool)gpio_get_level(GPIO_NUM_13);
}

int sd_init() {
	esp_err_t ret;

	ESP_LOGI(TAG, "Initializing SPI bus");

	spi_bus_config_t bus_cfg = {
		.mosi_io_num = 23,
		.miso_io_num = 19,
		.sclk_io_num = 18,
		.quadwp_io_num = -1,
		.quadhd_io_num = -1,
		.max_transfer_sz = 4000,
	};
	ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CH_AUTO);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to initialize bus: %s", esp_err_to_name(ret));
		return 0;
	}

	sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
	slot_config.gpio_cs = 5;
	slot_config.host_id = host.slot;

	ESP_LOGI(TAG, "Initializing SDSPI device");
	sdspi_dev_handle_t sdspi;
	ret = sdspi_host_init_device(&slot_config, &sdspi);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to init SDSPI device: %d", ret);
		return 0;
	}

	ret = sdspi_host_set_card_clk(sdspi, 20000);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to set SPI card speed: %d", ret);
		return 0;
	}

	return 1;
}

FATFS * sd_card_mount() {
	esp_err_t ret;

	ESP_LOGI(TAG, "Initializing SD card");

	sdmmc_card_t *card = (sdmmc_card_t *) malloc(sizeof(sdmmc_card_t));
	if (card == NULL) {
		ESP_LOGE(TAG, "Could not allocate card struct");
	}

	host.max_freq_khz = 1600;
	ret = sdmmc_card_init(&host, card);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to init SD card: %s", esp_err_to_name(ret));
		return NULL;
	}

	sdmmc_card_print_info(stdout, card);

	ESP_LOGI(TAG, "Mounting filesystem");
	BYTE pdrv = 0;
	ff_diskio_register_sdmmc(pdrv, card);
	FATFS *fs = NULL;
	ret = esp_vfs_fat_register(mount_point, "0:", 5, &fs);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "Failed to register VFS: %d", ret);
		return NULL;
	}

	FRESULT fret = f_mount(fs, "0:", 1);
	if (fret != FR_OK) {
		ESP_LOGE(TAG, "Failed to mount FATFS: %d", fret);
	}

	ESP_LOGI(TAG, "Filesystem mounted on %s", mount_point);

	return fs;
}

void sd_card_unmount() {
	esp_err_t ret;

	f_unmount("0:");
	ret = esp_vfs_fat_unregister_path(mount_point);
	if (ret != ESP_OK) {
		ESP_LOGE(TAG, "can't unmount: %s not mounted", mount_point);
		return;
	}
	ESP_LOGI(TAG, "%s unmounted", mount_point);

	ff_diskio_register(0, NULL);
	ESP_LOGI(TAG, "disk driver freed");
}

FATFS* sd_get() {
	if (!sd_card_detect()) {
		sd_card_unmount();
		fs = NULL;
		return NULL;
	}

	if (fs == NULL) {
		fs = sd_card_mount();
	}
	return fs;
}

bool sd_ready() {
	return sd_card_detect() && fs != NULL;
}

void test_sd() {
	gfx_set_enabled(false);

	char buf[40];
	key_event_t ke;
	int cs_state = 1;
	FATFS *fs = NULL;

	while (ke = key_get_event(0), !(ke.type == KEY_PRESSED && ke.code == KEY_BAND)) {
		int new_cs_state = gpio_get_level(GPIO_NUM_13);
		if (!new_cs_state && cs_state) {
			// card inserted
			fs = sd_card_mount();
			if (fs == NULL) {
				ESP_LOGE(TAG, "Could not initialize SD");
				cs_state = new_cs_state;
				continue;
			}

			DIR *d = opendir("/sd");
			struct dirent *e;
			while ((e = readdir(d)) != NULL) {
				printf("%02x %s\n", e->d_type, e->d_name);
			}
			closedir(d);
		} else if (new_cs_state && !cs_state) {
			sd_card_unmount();
			fs = NULL;
			// card removed
		}
		cs_state = new_cs_state;

		u8g2_ClearBuffer(&u8g2);

		sprintf(buf, "CS: %c", cs_state ? 'H' : 'L');
		u8g2_DrawStr(&u8g2, 0, 6, buf);
		sprintf(buf, "FS: %p", fs);
		for (int i = 0; i < strlen(buf); i++) {
			buf[i] = toupper(buf[i]);
		}
		u8g2_DrawStr(&u8g2, 0, 12, buf);

		u8g2_SendBuffer(&u8g2);

		vTaskDelay(100 / portTICK_PERIOD_MS);
	}

	gfx_set_enabled(true);
}