OVMS3/OVMS.V3/components/sdcard/src/sdcard.cpp

381 lines
9.9 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 14th March 2017
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "ovms_log.h"
static const char *TAG = "sdcard";
#include <string>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_intr_alloc.h"
#include "driver/gpio.h"
#include "sdcard.h"
#include "ovms_config.h"
#include "ovms_command.h"
#include "ovms_peripherals.h"
#include "ovms_events.h"
#include "ovms_boot.h"
static int insertcount = 0;
static int mountcount = 0;
void sdcard::Ticker1(std::string event, void* data)
{
if (insertcount > 0)
{
insertcount--;
if (insertcount == 0)
{
CheckCardState();
}
}
if (mountcount > 0)
{
mountcount--;
if (mountcount == 0)
{
if (m_mounted)
MyEvents.SignalEvent("sd.mounted", NULL);
else
{
MyEvents.SignalEvent("sd.unmounted", NULL);
if (MyBoot.IsShuttingDown())
MyBoot.ShutdownReady(TAG);
}
}
}
}
void sdcard::EventSystemShutDown(std::string event, void* data)
{
if (m_mounted && event == "system.shuttingdown")
{
MyBoot.ShutdownPending(TAG);
ESP_LOGI(TAG,"Unmounting SDCARD for reset");
unmount();
}
else if (m_mounted && event == "system.shutdown")
{
ESP_LOGI(TAG,"Hard SD unmount for reset");
unmount(true);
}
}
static void IRAM_ATTR sdcard_isr_handler(void* arg)
{
insertcount = 2;
}
sdcard::sdcard(const char* name, bool mode1bit, bool autoformat, int cdpin)
: pcp(name)
{
m_host = sdmmc_host_t SDMMC_HOST_DEFAULT();
if (mode1bit)
{
m_host.flags = SDMMC_HOST_FLAG_1BIT;
}
m_slot = sdmmc_slot_config_t SDMMC_SLOT_CONFIG_DEFAULT();
// Disable driver-level CD pin, as we do this ourselves
// if (cdpin)
// {
// m_slot.gpio_cd = (gpio_num_t)cdpin;
// }
m_slot.width = 1;
memset(&m_mount,0,sizeof(esp_vfs_fat_sdmmc_mount_config_t));
m_mount.format_if_mount_failed = autoformat;
m_mount.max_files = 5;
m_mounted = false;
m_unmounting = false;
m_cd = false;
m_cdpin = cdpin;
insertcount = 5;
// Register our events
#undef bind // Kludgy, but works
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(TAG,"ticker.1", std::bind(&sdcard::Ticker1, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.shuttingdown", std::bind(&sdcard::EventSystemShutDown, this, _1, _2));
MyEvents.RegisterEvent(TAG,"system.shutdown", std::bind(&sdcard::EventSystemShutDown, this, _1, _2));
gpio_pullup_en((gpio_num_t)cdpin);
gpio_set_intr_type((gpio_num_t)cdpin, GPIO_INTR_ANYEDGE);
gpio_isr_handler_add((gpio_num_t)cdpin, sdcard_isr_handler, (void*) this);
}
sdcard::~sdcard()
{
if (m_mounted)
{
unmount(true);
}
}
esp_err_t sdcard::mount()
{
if (m_unmounting)
{
ESP_LOGE(TAG, "mount failed: unmount in progress");
return ESP_FAIL;
}
else if (m_mounted)
{
ESP_LOGE(TAG, "mount failed: already mounted");
return ESP_FAIL;
}
m_host.max_freq_khz = MyConfig.GetParamValueInt("sdcard", "maxfreq.khz", 16000);
esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sd", &m_host, &m_slot, &m_mount, &m_card);
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "mount done");
m_mounted = true;
m_unmounting = false;
mountcount = 3;
}
else
{
ESP_LOGE(TAG, "mount failed: %s", esp_err_to_name(ret));
}
return ret;
}
static void sdcard_unmounting_done(const char* event, void* data)
{
ESP_LOGD(TAG, "unmount: preparation done");
MyPeripherals->m_sdcard->unmount(true);
}
esp_err_t sdcard::unmount(bool hard /*=false*/)
{
if (!m_mounted)
return ESP_OK;
if (!hard)
{
if (!m_unmounting)
{
m_unmounting = true;
ESP_LOGD(TAG, "unmount: preparing");
MyEvents.SignalEvent("sd.unmounting", NULL, sdcard_unmounting_done);
}
return ESP_FAIL;
}
else
{
esp_err_t ret = esp_vfs_fat_sdmmc_unmount();
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "unmount done");
m_mounted = false;
m_unmounting = false;
mountcount = 3;
}
else
{
ESP_LOGE(TAG, "unmount failed: %s", esp_err_to_name(ret));
}
return ret;
}
}
bool sdcard::isavailable()
{
return m_mounted && !m_unmounting;
}
bool sdcard::isunmounting()
{
return m_unmounting;
}
bool sdcard::ismounted()
{
return m_mounted;
}
bool sdcard::isinserted()
{
return m_cd;
}
void sdcard::CheckCardState()
{
bool cd = (gpio_get_level((gpio_num_t)m_cdpin)==0)?true:false;
if (cd != m_cd)
{
m_cd = cd;
if (m_cd)
{
// SD CARD has been inserted. Let's auto-mount
ESP_LOGI(TAG, "SD CARD has been inserted");
MyEvents.SignalEvent("sd.insert", NULL);
if (MyConfig.GetParamValueBool("sdcard", "automount", true))
{
mount();
}
}
else
{
// SD CARD has been removed. A bit late, but let's dismount
ESP_LOGI(TAG, "SD CARD has been removed");
if (m_mounted) unmount(true);
MyEvents.SignalEvent("sd.remove", NULL);
}
}
}
void sdcard_printinfo(int verbosity, OvmsWriter* writer)
{
sdmmc_card_t* card = MyPeripherals->m_sdcard->m_card;
FATFS *fs;
DWORD fre_clust, fre_sect, tot_sect;
writer->printf("Name: %s\n", card->cid.name);
// Get volume information and free clusters
// Note: assuming drive "1:" = /sd ("0:" = /store)
if (f_getfree("1:", &fre_clust, &fs) == FR_OK)
{
tot_sect = (fs->n_fatent - 2) * fs->csize;
fre_sect = fre_clust * fs->csize;
writer->printf("Size: %6llu MB\nFree: %6llu MB\n",
((uint64_t) tot_sect) * card->csd.sector_size / (1024 * 1024),
((uint64_t) fre_sect) * card->csd.sector_size / (1024 * 1024));
}
if (verbosity > COMMAND_RESULT_MINIMAL)
{
writer->printf("\nCard type: %s\n", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC");
writer->printf("Max speed: %d kHz\n", card->csd.tr_speed/1000);
writer->printf("Capacity: %llu MB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
writer->printf("CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
card->csd.csd_ver,
card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
writer->printf("SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
}
}
void sdcard_mount(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (MyPeripherals->m_sdcard->isunmounting())
{
writer->puts("Error: SD CARD is unmounting");
return;
}
if (MyPeripherals->m_sdcard->ismounted())
{
writer->puts("SD CARD is already mounted");
return;
}
esp_err_t res = MyPeripherals->m_sdcard->mount();
if (res == ESP_OK)
writer->puts("Mounted SD CARD");
else
writer->printf("Error: SD mount failed: %s\n", esp_err_to_name(res));
}
void sdcard_unmount(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyPeripherals->m_sdcard->ismounted())
{
writer->puts("SD CARD not mounted");
return;
}
esp_err_t res = ESP_OK;
int maxwait_seconds = 5;
if (argc >= 1)
maxwait_seconds = atoi(argv[0]);
if (maxwait_seconds == 0)
{
MyPeripherals->m_sdcard->unmount();
writer->puts("Unmounting SD CARD");
return;
}
for (int i=maxwait_seconds; i; i--)
{
res = MyPeripherals->m_sdcard->unmount();
if (res == ESP_OK) break;
if (i > 1)
vTaskDelay(pdMS_TO_TICKS(1000));
}
if (res == ESP_OK)
writer->puts("Unmounted SD CARD");
else
writer->printf("Error: SD unmount failed: %s\n", esp_err_to_name(res));
}
void sdcard_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (MyPeripherals->m_sdcard->isinserted())
{
writer->puts("SD CARD is inserted");
}
else
{
writer->puts("SD CARD is not inserted");
}
if (MyPeripherals->m_sdcard->ismounted())
{
if (MyPeripherals->m_sdcard->isavailable())
writer->puts("Status: available");
else
writer->puts("Status: unmounting");
sdcard_printinfo(verbosity, writer);
}
}
class SDCardInit
{
public: SDCardInit();
} MySDCardInit __attribute__ ((init_priority (4400)));
SDCardInit::SDCardInit()
{
ESP_LOGI(TAG, "Initialising SD CARD (4400)");
MyConfig.RegisterParam("sdcard", "SD CARD configuration", true, true);
OvmsCommand* cmd_sd = MyCommandApp.RegisterCommand("sd","SD CARD framework", sdcard_status, "", 0, 0, false);
cmd_sd->RegisterCommand("mount","Mount SD CARD",sdcard_mount);
cmd_sd->RegisterCommand("unmount","Unmount SD CARD",sdcard_unmount,"[<maxwait_seconds>]",0,1);
cmd_sd->RegisterCommand("status","Show SD CARD status",sdcard_status);
}