Path: blob/master/libraries/AP_Filesystem/AP_Filesystem_FATFS.cpp
9398 views
/*1ArduPilot filesystem interface for systems using the FATFS filesystem2*/3#include "AP_Filesystem_config.h"45#if AP_FILESYSTEM_FATFS_ENABLED67#include "AP_Filesystem.h"8#include <AP_HAL/AP_HAL.h>9#include <AP_Math/AP_Math.h>10#include <stdio.h>11#include <AP_Common/time.h>1213#include <ff.h>14#include <AP_HAL_ChibiOS/sdcard.h>15#include <GCS_MAVLink/GCS.h>16#include <AP_HAL_ChibiOS/hwdef/common/stm32_util.h>1718#if 019#define debug(fmt, args ...) do {printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); } while(0)20#else21#define debug(fmt, args ...)22#endif2324extern const AP_HAL::HAL& hal;2526static bool remount_needed;2728#define FATFS_R (S_IRUSR | S_IRGRP | S_IROTH) /*< FatFs Read perms */29#define FATFS_W (S_IWUSR | S_IWGRP | S_IWOTH) /*< FatFs Write perms */30#define FATFS_X (S_IXUSR | S_IXGRP | S_IXOTH) /*< FatFs Execute perms */313233uint32_t AP_Filesystem_FATFS::io_size = AP_FATFS_MIN_IO_SIZE;343536// use a semaphore to ensure that only one filesystem operation is37// happening at a time. A recursive semaphore is used to cope with the38// mkdir() inside sdcard_retry()39static HAL_Semaphore sem;4041typedef struct {42FIL fobj; // should be first member; it's the most used43char *name;44} FAT_FILE;4546#define MAX_FILES 1647static FAT_FILE *file_table[MAX_FILES];4849/*50allocate a file descriptor51*/52static int new_file_descriptor(const char *pathname, FIL * &fh)53{54int i;55FAT_FILE *stream;5657for (i=0; i<MAX_FILES; ++i) {58if (file_table[i] == NULL) {59stream = (FAT_FILE *) calloc(1, sizeof(FAT_FILE));60if (stream == NULL) {61errno = ENOMEM;62return -1;63}64stream->name = strdup(pathname);65if (stream->name == NULL) {66free(stream);67errno = ENOMEM;68return -1;69}7071file_table[i] = stream;72fh = &stream->fobj;73return i;74}75}76errno = ENFILE;77return -1;78}7980static FAT_FILE *fileno_to_stream(int fileno)81{82FAT_FILE *stream;83if (fileno < 0 || fileno >= MAX_FILES) {84errno = EBADF;85return nullptr;86}8788stream = file_table[fileno];89if (stream == NULL) {90errno = EBADF;91return nullptr;92}93return stream;94}9596static void free_file_descriptor(int fileno)97{98FAT_FILE *stream = fileno_to_stream(fileno);99if (stream != nullptr) {100file_table[fileno] = NULL;101free(stream->name);102free(stream);103}104}105106static FIL *fileno_to_fatfs(int fileno)107{108FAT_FILE *stream;109110stream = fileno_to_stream(fileno);111if (stream == nullptr) { // unknown fileno?112return nullptr; // errno already set113}114115return &stream->fobj;116}117118static int fatfs_to_errno(FRESULT Result)119{120switch (Result) {121case FR_OK: /* FatFS (0) Succeeded */122return 0; /* POSIX OK */123case FR_DISK_ERR: /* FatFS (1) A hard error occurred in the low level disk I/O layer */124return EIO; /* POSIX Input/output error (POSIX.1) */125126case FR_INT_ERR: /* FatFS (2) Assertion failed */127return EPERM; /* POSIX Operation not permitted (POSIX.1) */128129case FR_NOT_READY: /* FatFS (3) The physical drive cannot work */130return EBUSY; /* POSIX Device or resource busy (POSIX.1) */131132case FR_NO_FILE: /* FatFS (4) Could not find the file */133return ENOENT; /* POSIX No such file or directory (POSIX.1) */134135case FR_NO_PATH: /* FatFS (5) Could not find the path */136return ENOENT; /* POSIX No such file or directory (POSIX.1) */137138case FR_INVALID_NAME: /* FatFS (6) The path name format is invalid */139return EINVAL; /* POSIX Invalid argument (POSIX.1) */140141case FR_DENIED: /* FatFS (7) Access denied due to prohibited access or directory full */142return EACCES; /* POSIX Permission denied (POSIX.1) */143144case FR_EXIST: /* file exists */145return EEXIST; /* file exists */146147case FR_INVALID_OBJECT: /* FatFS (9) The file/directory object is invalid */148return EINVAL; /* POSIX Invalid argument (POSIX.1) */149150case FR_WRITE_PROTECTED: /* FatFS (10) The physical drive is write protected */151return EROFS; /* POSIX Read-only filesystem (POSIX.1) */152153case FR_INVALID_DRIVE: /* FatFS (11) The logical drive number is invalid */154return ENXIO; /* POSIX No such device or address (POSIX.1) */155156case FR_NOT_ENABLED: /* FatFS (12) The volume has no work area */157return ENOSPC; /* POSIX No space left on device (POSIX.1) */158159case FR_NO_FILESYSTEM: /* FatFS (13) There is no valid FAT volume */160return ENXIO; /* POSIX No such device or address (POSIX.1) */161162case FR_MKFS_ABORTED: /* FatFS (14) The f_mkfs() aborted due to any parameter error */163return EINVAL; /* POSIX Invalid argument (POSIX.1) */164165case FR_TIMEOUT: /* FatFS (15) Could not get a grant to access the volume within defined period */166return EBUSY; /* POSIX Device or resource busy (POSIX.1) */167168case FR_LOCKED: /* FatFS (16) The operation is rejected according to the file sharing policy */169return EBUSY; /* POSIX Device or resource busy (POSIX.1) */170171172case FR_NOT_ENOUGH_CORE: /* FatFS (17) LFN working buffer could not be allocated */173return ENOMEM; /* POSIX Not enough space (POSIX.1) */174175case FR_TOO_MANY_OPEN_FILES:/* FatFS (18) Number of open files > _FS_SHARE */176return EMFILE; /* POSIX Too many open files (POSIX.1) */177178case FR_INVALID_PARAMETER:/* FatFS (19) Given parameter is invalid */179return EINVAL; /* POSIX Invalid argument (POSIX.1) */180181}182return EBADMSG; /* POSIX Bad message (POSIX.1) */183}184185// check for a remount and return -1 if it fails186#define CHECK_REMOUNT() do { if (remount_needed && !remount_file_system()) { errno = EIO; return -1; }} while (0)187#define CHECK_REMOUNT_NULL() do { if (remount_needed && !remount_file_system()) { errno = EIO; return NULL; }} while (0)188189// we allow for IO retries if either not armed or not in main thread190#define RETRY_ALLOWED() (!hal.scheduler->in_main_thread() || !hal.util->get_soft_armed())191192/*193try to remount the file system on disk error194*/195static bool remount_file_system(void)196{197EXPECT_DELAY_MS(3000);198if (!remount_needed) {199sdcard_stop();200}201if (!sdcard_retry()) {202remount_needed = true;203return false;204}205remount_needed = false;206for (uint16_t i=0; i<MAX_FILES; i++) {207FAT_FILE *f = file_table[i];208if (!f) {209continue;210}211FIL *fh = &f->fobj;212FSIZE_t offset = fh->fptr;213uint8_t flags = fh->flag & (FA_READ | FA_WRITE);214215memset(fh, 0, sizeof(*fh));216if (flags & FA_WRITE) {217// the file may not have been created yet on the sdcard218flags |= FA_OPEN_ALWAYS;219}220FRESULT res = f_open(fh, f->name, flags);221debug("reopen %s flags=0x%x ofs=%u -> %d\n", f->name, unsigned(flags), unsigned(offset), int(res));222if (res == FR_OK) {223f_lseek(fh, offset);224}225}226return true;227}228229int AP_Filesystem_FATFS::open(const char *pathname, int flags, bool allow_absolute_path)230{231int fileno;232int fatfs_modes;233FIL *fh;234int res;235236FS_CHECK_ALLOWED(-1);237WITH_SEMAPHORE(sem);238239CHECK_REMOUNT();240241errno = 0;242debug("Open %s 0x%x", pathname, flags);243244if ((flags & O_ACCMODE) == O_RDWR) {245fatfs_modes = FA_READ | FA_WRITE;246} else if ((flags & O_ACCMODE) == O_RDONLY) {247fatfs_modes = FA_READ;248} else {249fatfs_modes = FA_WRITE;250}251252if (flags & O_CREAT) {253if (flags & O_TRUNC) {254fatfs_modes |= FA_CREATE_ALWAYS;255} else {256fatfs_modes |= FA_OPEN_ALWAYS;257}258}259260fileno = new_file_descriptor(pathname, fh);261if (fileno < 0) { // creation failed?262return -1; // errno already set263}264265res = f_open(fh, pathname, (BYTE) (fatfs_modes & 0xff));266if (res == FR_DISK_ERR && RETRY_ALLOWED()) {267// one retry on disk error268hal.scheduler->delay(100);269if (remount_file_system()) {270res = f_open(fh, pathname, (BYTE) (fatfs_modes & 0xff));271}272}273if (res != FR_OK) {274errno = fatfs_to_errno((FRESULT)res);275free_file_descriptor(fileno);276return -1;277}278if (flags & O_APPEND) {279/// Seek to end of the file280res = f_lseek(fh, f_size(fh));281if (res != FR_OK) {282errno = fatfs_to_errno((FRESULT)res);283f_close(fh);284free_file_descriptor(fileno);285return -1;286}287}288289debug("Open %s -> %d", pathname, fileno);290291return fileno;292}293294int AP_Filesystem_FATFS::close(int fileno)295{296FIL *fh;297int res;298299FS_CHECK_ALLOWED(-1);300WITH_SEMAPHORE(sem);301302errno = 0;303304fh = fileno_to_fatfs(fileno);305if (fh == nullptr) { // unknown fileno?306return -1; // errno already set307}308res = f_close(fh);309free_file_descriptor(fileno);310if (res != FR_OK) {311errno = fatfs_to_errno((FRESULT)res);312return -1;313}314return 0;315}316317int32_t AP_Filesystem_FATFS::read(int fd, void *buf, uint32_t count)318{319UINT bytes = count;320int res;321FIL *fh;322323FS_CHECK_ALLOWED(-1);324WITH_SEMAPHORE(sem);325326CHECK_REMOUNT();327328if (count > 0) {329*(char *) buf = 0;330}331332errno = 0;333334fh = fileno_to_fatfs(fd);335if (fh == nullptr) { // unknown fd?336return -1; // errno already set337}338339UINT total = 0;340do {341UINT size = 0;342UINT n = bytes;343if (!mem_is_dma_safe(buf, count, true)) {344n = MIN(bytes, io_size);345}346res = f_read(fh, (void *)buf, n, &size);347if (res != FR_OK) {348errno = fatfs_to_errno((FRESULT)res);349return -1;350}351if (size == 0) {352break;353}354if (size > n) {355errno = EIO;356return -1;357}358total += size;359buf = (void *)(((uint8_t *)buf)+size);360bytes -= size;361if (size < n) {362break;363}364} while (bytes > 0);365return (ssize_t)total;366}367368int32_t AP_Filesystem_FATFS::write(int fd, const void *buf, uint32_t count)369{370UINT bytes = count;371FRESULT res;372FIL *fh;373errno = 0;374375FS_CHECK_ALLOWED(-1);376WITH_SEMAPHORE(sem);377378CHECK_REMOUNT();379380fh = fileno_to_fatfs(fd);381if (fh == nullptr) { // unknown fd?382return -1; // errno already set383}384385UINT total = 0;386do {387UINT n = bytes;388if (!mem_is_dma_safe(buf, count, true)) {389n = MIN(bytes, io_size);390}391UINT size = 0;392res = f_write(fh, buf, n, &size);393if (res == FR_DISK_ERR && RETRY_ALLOWED()) {394// one retry on disk error395hal.scheduler->delay(100);396if (remount_file_system()) {397res = f_write(fh, buf, n, &size);398}399}400if (size > n || size == 0) {401errno = EIO;402return -1;403}404if (res != FR_OK || size > n) {405errno = fatfs_to_errno(res);406return -1;407}408total += size;409buf = (void *)(((uint8_t *)buf)+size);410bytes -= size;411if (size < n) {412break;413}414} while (bytes > 0);415return (ssize_t)total;416}417418int AP_Filesystem_FATFS::fsync(int fileno)419{420FIL *fh;421int res;422423FS_CHECK_ALLOWED(-1);424WITH_SEMAPHORE(sem);425426errno = 0;427428fh = fileno_to_fatfs(fileno);429if (fh == nullptr) { // unknown fileno?430return -1; // errno already set431}432res = f_sync(fh);433if (res != FR_OK) {434errno = fatfs_to_errno((FRESULT)res);435return -1;436}437return 0;438}439440off_t AP_Filesystem_FATFS::lseek(int fileno, off_t position, int whence)441{442FRESULT res;443FIL *fh;444errno = 0;445446FS_CHECK_ALLOWED(-1);447WITH_SEMAPHORE(sem);448449fh = fileno_to_fatfs(fileno);450if (fh == nullptr) { // unknown fileno?451return -1; // errno already set452}453454if (whence == SEEK_END) {455position += f_size(fh);456} else if (whence==SEEK_CUR) {457position += fh->fptr;458}459460res = f_lseek(fh, position);461if (res) {462errno = fatfs_to_errno(res);463return -1;464}465return fh->fptr;466}467468static time_t fat_time_to_unix(uint16_t date, uint16_t time)469{470struct tm tp;471time_t unix;472473memset(&tp, 0, sizeof(struct tm));474475tp.tm_sec = (time << 1) & 0x3e; // 2 second resolution476tp.tm_min = ((time >> 5) & 0x3f);477tp.tm_hour = ((time >> 11) & 0x1f);478tp.tm_mday = (date & 0x1f);479tp.tm_mon = ((date >> 5) & 0x0f) - 1;480tp.tm_year = ((date >> 9) & 0x7f) + 80;481unix = ap_mktime(&tp);482return unix;483}484485int AP_Filesystem_FATFS::stat(const char *name, struct stat *buf)486{487FILINFO info;488int res;489time_t epoch;490uint16_t mode;491492FS_CHECK_ALLOWED(-1);493WITH_SEMAPHORE(sem);494495CHECK_REMOUNT();496497errno = 0;498499// f_stat does not handle / or . as root directory500if (strcmp(name,"/") == 0 || strcmp(name,".") == 0) {501buf->st_atime = 0;502buf->st_mtime = 0;503buf->st_ctime = 0;504buf->st_uid= 0;505buf->st_gid= 0;506buf->st_size = 0;507buf->st_mode = S_IFDIR;508return 0;509}510511res = f_stat(name, &info);512if (res == FR_DISK_ERR && RETRY_ALLOWED()) {513// one retry on disk error514if (remount_file_system()) {515res = f_stat(name, &info);516}517}518if (res != FR_OK) {519errno = fatfs_to_errno((FRESULT)res);520return -1;521}522523buf->st_size = info.fsize;524epoch = fat_time_to_unix(info.fdate, info.ftime);525buf->st_atime = epoch; // Access time526buf->st_mtime = epoch; // Modification time527buf->st_ctime = epoch; // Creation time528529// We only handle read only case530mode = (FATFS_R | FATFS_X);531if (!(info.fattrib & AM_RDO)) {532mode |= (FATFS_W); // enable write if NOT read only533}534535if (info.fattrib & AM_SYS) {536buf->st_uid= 0;537buf->st_gid= 0;538}539{540buf->st_uid=1000;541buf->st_gid=1000;542}543544if (info.fattrib & AM_DIR) {545mode |= S_IFDIR;546} else {547mode |= S_IFREG;548}549buf->st_mode = mode;550551return 0;552}553554int AP_Filesystem_FATFS::unlink(const char *pathname)555{556FS_CHECK_ALLOWED(-1);557WITH_SEMAPHORE(sem);558559errno = 0;560int res = f_unlink(pathname);561if (res != FR_OK) {562errno = fatfs_to_errno((FRESULT)res);563return -1;564}565return 0;566}567568int AP_Filesystem_FATFS::mkdir(const char *pathname)569{570FS_CHECK_ALLOWED(-1);571WITH_SEMAPHORE(sem);572573errno = 0;574575int res = f_mkdir(pathname);576if (res != FR_OK) {577errno = fatfs_to_errno((FRESULT)res);578return -1;579}580581return 0;582}583584int AP_Filesystem_FATFS::rename(const char *oldpath, const char *newpath)585{586FS_CHECK_ALLOWED(-1);587WITH_SEMAPHORE(sem);588589errno = 0;590591int res = f_rename(oldpath, newpath);592if (res != FR_OK) {593errno = fatfs_to_errno((FRESULT)res);594return -1;595}596597return 0;598}599600/*601wrapper structure to associate a dirent with a DIR602*/603struct DIR_Wrapper {604DIR d; // must be first structure element605struct dirent de;606};607608void *AP_Filesystem_FATFS::opendir(const char *pathdir)609{610FS_CHECK_ALLOWED(nullptr);611WITH_SEMAPHORE(sem);612613CHECK_REMOUNT_NULL();614615debug("Opendir %s", pathdir);616struct DIR_Wrapper *ret = NEW_NOTHROW DIR_Wrapper;617if (!ret) {618return nullptr;619}620int res = f_opendir(&ret->d, pathdir);621if (res == FR_DISK_ERR && RETRY_ALLOWED()) {622// one retry on disk error623if (remount_file_system()) {624res = f_opendir(&ret->d, pathdir);625}626}627if (res != FR_OK) {628errno = fatfs_to_errno((FRESULT)res);629delete ret;630return nullptr;631}632debug("Opendir %s -> %p", pathdir, ret);633return &ret->d;634}635636struct dirent *AP_Filesystem_FATFS::readdir(void *dirp_void)637{638FS_CHECK_ALLOWED(nullptr);639WITH_SEMAPHORE(sem);640DIR *dirp = (DIR *)dirp_void;641642struct DIR_Wrapper *d = (struct DIR_Wrapper *)dirp;643if (!d) {644errno = EINVAL;645return nullptr;646}647FILINFO fno;648int len;649int res;650651d->de.d_name[0] = 0;652res = f_readdir(dirp, &fno);653if (res != FR_OK || fno.fname[0] == 0) {654errno = fatfs_to_errno((FRESULT)res);655return nullptr;656}657len = strlen(fno.fname);658strncpy_noterm(d->de.d_name,fno.fname,len);659d->de.d_name[len] = 0;660if (fno.fattrib & AM_DIR) {661d->de.d_type = DT_DIR;662} else {663d->de.d_type = DT_REG;664}665return &d->de;666}667668int AP_Filesystem_FATFS::closedir(void *dirp_void)669{670DIR *dirp = (DIR *)dirp_void;671FS_CHECK_ALLOWED(-1);672WITH_SEMAPHORE(sem);673674struct DIR_Wrapper *d = (struct DIR_Wrapper *)dirp;675if (!d) {676errno = EINVAL;677return -1;678}679int res = f_closedir (dirp);680delete d;681if (res != FR_OK) {682errno = fatfs_to_errno((FRESULT)res);683return -1;684}685debug("closedir");686return 0;687}688689// return number of bytes that should be written before fsync for optimal690// streaming performance/robustness. if zero, any number can be written.691// assume similar to old logging code that max-IO-size boundaries are good.692uint32_t AP_Filesystem_FATFS::bytes_until_fsync(int fd)693{694FS_CHECK_ALLOWED(0);695WITH_SEMAPHORE(sem);696697FIL *fh = fileno_to_fatfs(fd);698if (fh == nullptr) { // unknown fd?699return 0; // return "any number", the write/fsync will fail anyway700}701702const uint32_t block_size = io_size;703704uint32_t block_pos = fh->fptr % block_size;705return block_size - block_pos;706}707708// return free disk space in bytes709int64_t AP_Filesystem_FATFS::disk_free(const char *path)710{711FS_CHECK_ALLOWED(-1);712WITH_SEMAPHORE(sem);713714FATFS *fs;715DWORD fre_clust, fre_sect;716717CHECK_REMOUNT();718719/* Get volume information and free clusters of drive 1 */720FRESULT res = f_getfree("/", &fre_clust, &fs);721if (res) {722return res;723}724725/* Get total sectors and free sectors */726fre_sect = fre_clust * fs->csize;727return (int64_t)(fre_sect)*512;728}729730// return total disk space in bytes731int64_t AP_Filesystem_FATFS::disk_space(const char *path)732{733FS_CHECK_ALLOWED(-1);734WITH_SEMAPHORE(sem);735736CHECK_REMOUNT();737738FATFS *fs;739DWORD fre_clust, tot_sect;740741/* Get volume information and free clusters of drive 1 */742FRESULT res = f_getfree("/", &fre_clust, &fs);743if (res) {744return -1;745}746747/* Get total sectors and free sectors */748tot_sect = (fs->n_fatent - 2) * fs->csize;749return (int64_t)(tot_sect)*512;750}751752/*753convert unix time_t to FATFS timestamp754*/755static void unix_time_to_fat(time_t epoch, uint16_t &date, uint16_t &time)756{757struct tm tmd {};758struct tm *t = gmtime_r((time_t *)&epoch, &tmd);759760/* Pack date and time into a uint32_t variable */761date = ((uint16_t)(t->tm_year - 80) << 9)762| (((uint16_t)t->tm_mon+1) << 5)763| (((uint16_t)t->tm_mday));764765time = ((uint16_t)t->tm_hour << 11)766| ((uint16_t)t->tm_min << 5)767| ((uint16_t)t->tm_sec >> 1);768}769770/*771set mtime on a file772*/773bool AP_Filesystem_FATFS::set_mtime(const char *filename, const uint32_t mtime_sec)774{775FILINFO fno;776uint16_t fdate, ftime;777778unix_time_to_fat(mtime_sec, fdate, ftime);779780fno.fdate = fdate;781fno.ftime = ftime;782783FS_CHECK_ALLOWED(false);784WITH_SEMAPHORE(sem);785786return f_utime(filename, (FILINFO *)&fno) == FR_OK;787}788789/*790retry mount of filesystem if needed791*/792bool AP_Filesystem_FATFS::retry_mount(void)793{794FS_CHECK_ALLOWED(false);795WITH_SEMAPHORE(sem);796return sdcard_retry();797}798799/*800unmount filesystem for reboot801*/802void AP_Filesystem_FATFS::unmount(void)803{804WITH_SEMAPHORE(sem);805return sdcard_stop();806}807808/*809format sdcard810*/811bool AP_Filesystem_FATFS::format(void)812{813#if FF_USE_MKFS814WITH_SEMAPHORE(sem);815hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_Filesystem_FATFS::format_handler, void));816// the format is handled asynchronously, we inform user of success817// via a text message. format_status can be polled for progress818format_status = FormatStatus::PENDING;819return true;820#else821return false;822#endif823}824825/*826format sdcard827*/828void AP_Filesystem_FATFS::format_handler(void)829{830#if FF_USE_MKFS831if (format_status != FormatStatus::PENDING) {832return;833}834WITH_SEMAPHORE(sem);835format_status = FormatStatus::IN_PROGRESS;836GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Formatting SDCard");837uint8_t *buf = (uint8_t *)hal.util->malloc_type(FF_MAX_SS, AP_HAL::Util::MEM_DMA_SAFE);838if (buf == nullptr) {839return;840}841// format first disk842auto ret = f_mkfs("0:", 0, buf, FF_MAX_SS);843hal.util->free_type(buf, FF_MAX_SS, AP_HAL::Util::MEM_DMA_SAFE);844if (ret == FR_OK) {845format_status = FormatStatus::SUCCESS;846GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Format: OK");847} else {848format_status = FormatStatus::FAILURE;849GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Format: Failed (%d)", int(ret));850}851sdcard_stop();852sdcard_retry();853#endif854}855856// returns true if we are currently formatting the SD card:857AP_Filesystem_Backend::FormatStatus AP_Filesystem_FATFS::get_format_status(void) const858{859// note that format_handler holds sem, so we can't take it here.860return format_status;861}862863/*864convert POSIX errno to text with user message.865*/866char *strerror(int errnum)867{868#define SWITCH_ERROR(errno) case errno: return const_cast<char *>(#errno); break869switch (errnum) {870SWITCH_ERROR(EPERM);871SWITCH_ERROR(ENOENT);872SWITCH_ERROR(ESRCH);873SWITCH_ERROR(EINTR);874SWITCH_ERROR(EIO);875SWITCH_ERROR(ENXIO);876SWITCH_ERROR(E2BIG);877SWITCH_ERROR(ENOEXEC);878SWITCH_ERROR(EBADF);879SWITCH_ERROR(ECHILD);880SWITCH_ERROR(EAGAIN);881SWITCH_ERROR(ENOMEM);882SWITCH_ERROR(EACCES);883SWITCH_ERROR(EFAULT);884#ifdef ENOTBLK885SWITCH_ERROR(ENOTBLK);886#endif // ENOTBLK887SWITCH_ERROR(EBUSY);888SWITCH_ERROR(EEXIST);889SWITCH_ERROR(EXDEV);890SWITCH_ERROR(ENODEV);891SWITCH_ERROR(ENOTDIR);892SWITCH_ERROR(EISDIR);893SWITCH_ERROR(EINVAL);894SWITCH_ERROR(ENFILE);895SWITCH_ERROR(EMFILE);896SWITCH_ERROR(ENOTTY);897SWITCH_ERROR(ETXTBSY);898SWITCH_ERROR(EFBIG);899SWITCH_ERROR(ENOSPC);900SWITCH_ERROR(ESPIPE);901SWITCH_ERROR(EROFS);902SWITCH_ERROR(EMLINK);903SWITCH_ERROR(EPIPE);904SWITCH_ERROR(EDOM);905SWITCH_ERROR(ERANGE);906SWITCH_ERROR(EBADMSG);907}908909#undef SWITCH_ERROR910911return NULL;912}913914#endif // AP_FILESYSTEM_FATFS_ENABLED915916917