From f6c26d33a4e15d966597bc9d66c1f33328a750af Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Fri, 7 Feb 2014 18:30:50 +0100 Subject: samsungypr0: Support or mounting the microsd A thread polls the appropriate GPIO pin for sd card presence and mounts using the mount system call. Change-Id: I31ab41c4120f4af64eb6998b7e7b6f9051585efb --- firmware/common/rbpaths.c | 117 ++++++++++++--- firmware/export/config/samsungypr0.h | 8 +- firmware/export/mv.h | 1 + firmware/export/rbpaths.h | 5 +- firmware/include/file.h | 2 + .../target/hosted/samsungypr/ypr0/system-ypr0.c | 165 ++++++++++++++++++++- 6 files changed, 267 insertions(+), 31 deletions(-) diff --git a/firmware/common/rbpaths.c b/firmware/common/rbpaths.c index 8efb6dd238..dba39476fc 100644 --- a/firmware/common/rbpaths.c +++ b/firmware/common/rbpaths.c @@ -48,6 +48,7 @@ #undef rmdir #undef dirent #undef DIR +#undef readlink #if (CONFIG_PLATFORM & PLATFORM_ANDROID) static const char rbhome[] = "/sdcard"; @@ -61,6 +62,9 @@ const char *rbhome; * over the ones where Rockbox is installed to. Classic example would be * $HOME/.config/rockbox.org vs /usr/share/rockbox */ #define HAVE_SPECIAL_DIRS +#define IS_HOME(p) (!strcmp(p, rbhome)) +#else +#define IS_HOME(p) (!strcmp(p, HOME_DIR)) #endif /* flags for get_user_file_path() */ @@ -70,6 +74,35 @@ const char *rbhome; /* file or directory? */ #define IS_FILE (1<<1) +#ifdef HAVE_MULTIDRIVE +/* A special link is created under e.g. HOME_DIR/, e.g. to access + * external storage in a convinient location, much similar to the mount + * point on our native targets. Here they are treated as symlink (one which + * doesn't actually exist in the filesystem and therefore we have to override + * readlink() */ +static const char *handle_special_links(const char* link, unsigned flags, + char *buf, const size_t bufsize) +{ + (void) flags; + char vol_string[VOL_ENUM_POS + 8]; + int len = sprintf(vol_string, VOL_NAMES, 1); + + /* link might be passed with or without HOME_DIR expanded. To handle + * both perform substring matching (VOL_NAMES is unique enough) */ + const char *begin = strstr(link, vol_string); + if (begin) + { + /* begin now points to the start of vol_string within link, + * we want to copy the remainder of the paths, prefixed by + * the actual mount point (the remainder might be "") */ + snprintf(buf, bufsize, MULTIDRIVE_DIR"%s", begin + len); + return buf; + } + + return link; +} +#endif + #ifdef HAVE_SPECIAL_DIRS void paths_init(void) { @@ -156,38 +189,31 @@ static const char* _get_user_file_path(const char *path, return ret; } +#elif !defined(paths_init) +void paths_init(void) { } +#endif static const char* handle_special_dirs(const char* dir, unsigned flags, char *buf, const size_t bufsize) { + (void) flags; (void) buf; (void) bufsize; +#ifdef HAVE_SPECIAL_DIRS if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN)) { const char *p = dir + HOME_DIR_LEN; while (*p == '/') p++; snprintf(buf, bufsize, "%s/%s", rbhome, p); - return buf; + dir = buf; } else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN)) - return _get_user_file_path(dir, flags, buf, bufsize); - - return dir; -} - -#else /* !HAVE_SPECIAL_DIRS */ - -#ifndef paths_init -void paths_init(void) { } + dir = _get_user_file_path(dir, flags, buf, bufsize); +#endif +#ifdef HAVE_MULTIDRIVE + dir = handle_special_links(dir, flags, buf, bufsize); #endif - -static const char* handle_special_dirs(const char* dir, unsigned flags, - char *buf, const size_t bufsize) -{ - (void) flags; (void) buf; (void) bufsize; return dir; } -#endif - int app_open(const char *name, int o, ...) { char realpath[MAX_PATH]; @@ -235,6 +261,7 @@ int app_rename(const char *old, const char *new) * get_dir_info() */ struct __dir { DIR *dir; + IF_MD(int volumes_returned); char path[]; }; @@ -246,23 +273,31 @@ struct dirinfo dir_get_info(DIR* _parent, struct dirent *dir) struct dirinfo ret; char path[MAX_PATH]; - snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); memset(&ret, 0, sizeof(ret)); +#ifdef HAVE_MULTIDRIVE + char vol_string[VOL_ENUM_POS + 8]; + sprintf(vol_string, VOL_NAMES, 1); + if (!strcmp(vol_string, dir->d_name)) + { + ret.attribute = ATTR_LINK; + strcpy(path, MULTIDRIVE_DIR); + } + else +#endif + snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); + if (!stat(path, &s)) { if (S_ISDIR(s.st_mode)) - { - ret.attribute = ATTR_DIRECTORY; - } + ret.attribute |= ATTR_DIRECTORY; + ret.size = s.st_size; tm = localtime(&(s.st_mtime)); } if (!lstat(path, &s) && S_ISLNK(s.st_mode)) - { ret.attribute |= ATTR_LINK; - } if (tm) { @@ -296,6 +331,7 @@ DIR* app_opendir(const char *_name) free(buf); return NULL; } + IF_MD(this->volumes_returned = 0); return (DIR*)this; } @@ -311,6 +347,18 @@ int app_closedir(DIR *dir) struct dirent* app_readdir(DIR* dir) { struct __dir *d = (struct __dir*)dir; +#ifdef HAVE_MULTIDRIVE + /* this is not MT-safe but OK according to man readdir */ + static struct dirent voldir; + if (d->volumes_returned < (NUM_VOLUMES-1) + && volume_present(d->volumes_returned+1) + && IS_HOME(d->path)) + { + d->volumes_returned += 1; + sprintf(voldir.d_name, VOL_NAMES, d->volumes_returned); + return &voldir; + } +#endif return readdir(d->dir); } @@ -329,3 +377,26 @@ int app_rmdir(const char* name) const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); return rmdir(fname); } + + +/* On MD we create a virtual symlink for the external drive, + * for this we need to override readlink(). */ +ssize_t app_readlink(const char *path, char *buf, size_t bufsiz) +{ + char _buf[MAX_PATH]; + (void) path; (void) buf; (void) bufsiz; + path = handle_special_dirs(path, 0, _buf, sizeof(_buf)); +#ifdef HAVE_MULTIDRIVE + /* if path == _buf then we can be sure handle_special_dir() did something + * and path is not an ordinary directory */ + if (path == _buf && !strncmp(path, MULTIDRIVE_DIR, sizeof(MULTIDRIVE_DIR)-1)) + { + /* copying NUL is not required as per readlink specification */ + ssize_t len = strlen(path); + memcpy(buf, path, len); + return len; + } +#endif + /* does not append NUL !! */ + return readlink(path, buf, bufsiz); +} diff --git a/firmware/export/config/samsungypr0.h b/firmware/export/config/samsungypr0.h index 361c9697c5..bed5a2f977 100644 --- a/firmware/export/config/samsungypr0.h +++ b/firmware/export/config/samsungypr0.h @@ -161,6 +161,10 @@ /* This folder resides in the ReadOnly CRAMFS. It is binded to /mnt/media0/.rockbox */ #define BOOTDIR "/.rockbox" -/* No special storage */ -#define CONFIG_STORAGE STORAGE_HOSTFS +/* External SD card can be mounted */ +#define CONFIG_STORAGE (STORAGE_HOSTFS|STORAGE_SD) +#define HAVE_MULTIDRIVE +#define NUM_DRIVES 2 +#define HAVE_HOTSWAP #define HAVE_STORAGE_FLUSH +#define MULTIDRIVE_DIR "/mnt/mmc" diff --git a/firmware/export/mv.h b/firmware/export/mv.h index 05c9c6349f..1d0a536663 100644 --- a/firmware/export/mv.h +++ b/firmware/export/mv.h @@ -22,6 +22,7 @@ #ifndef __MV_H__ #define __MV_H__ +#include #include "config.h" /* FixMe: These macros are a bit nasty and perhaps misplaced here. diff --git a/firmware/export/rbpaths.h b/firmware/export/rbpaths.h index 0a5b36c5ab..1f7e1a2643 100644 --- a/firmware/export/rbpaths.h +++ b/firmware/export/rbpaths.h @@ -60,7 +60,6 @@ #else /* APPLICATION */ #define HOME_DIR "" /* replaced at runtime */ -#define HOME_DIR_LEN (sizeof(HOME_DIR)-1) #define PLUGIN_DIR ROCKBOX_LIBRARY_PATH "/rockbox/rocks" #if (CONFIG_PLATFORM & PLATFORM_ANDROID) @@ -73,10 +72,13 @@ extern void paths_init(void); #endif /* !APPLICATION || SAMSUNG_YPR0 */ +#define HOME_DIR_LEN (sizeof(HOME_DIR)-1) + #ifdef APPLICATION #include #include +#include int app_open(const char *name, int o, ...); int app_creat(const char* name, mode_t mode); @@ -87,6 +89,7 @@ int app_closedir(DIR *dir); struct dirent* app_readdir(DIR* dir); int app_mkdir(const char* name); int app_rmdir(const char* name); +ssize_t app_readlink(const char *path, char *buf, size_t bufsiz); #endif diff --git a/firmware/include/file.h b/firmware/include/file.h index 9b7f123999..77930864c7 100644 --- a/firmware/include/file.h +++ b/firmware/include/file.h @@ -43,6 +43,7 @@ # define creat(x,m) app_creat(x, m) # define remove(x) app_remove(x) # define rename(x,y) app_rename(x,y) +# define readlink(x,y,z) app_readlink(x,y,z) # if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) /* SDL overrides a few more */ # define read(x,y,z) sim_read(x,y,z) @@ -59,6 +60,7 @@ # define read(x,y,z) sim_read(x,y,z) # define write(x,y,z) sim_write(x,y,z) # define close(x) sim_close(x) +/* readlink() not used in the sim yet */ extern int sim_open(const char *name, int o, ...); extern int sim_creat(const char *name, mode_t mode); #endif diff --git a/firmware/target/hosted/samsungypr/ypr0/system-ypr0.c b/firmware/target/hosted/samsungypr/ypr0/system-ypr0.c index 477b71c6a2..893c710861 100644 --- a/firmware/target/hosted/samsungypr/ypr0/system-ypr0.c +++ b/firmware/target/hosted/samsungypr/ypr0/system-ypr0.c @@ -19,16 +19,26 @@ ****************************************************************************/ #include -#include #include #include +#include +#include + #include "system.h" +#include "kernel.h" +#include "thread.h" + +#include "string-extra.h" #include "panic.h" #include "debug.h" -#include "hostfs.h" - +#include "storage.h" +#include "mv.h" #include "ascodec.h" #include "gpio-ypr.h" +#include "ascodec.h" +#include "backlight.h" +#include "rbunicode.h" +#include "logdiskf.h" void power_off(void) { @@ -61,15 +71,160 @@ void system_exception_wait(void) system_reboot(); } +/* MicroSD card removal / insertion management */ + +bool hostfs_removable(IF_MD_NONVOID(int drive)) +{ +#ifdef HAVE_MULTIDRIVE + if (drive > 0) /* Active LOW */ + return true; + else +#endif + return false; /* internal: always present */ +} + +bool hostfs_present(IF_MD_NONVOID(int drive)) +{ +#ifdef HAVE_MULTIDRIVE + if (drive > 0) /* Active LOW */ + return (!gpio_control(DEV_CTRL_GPIO_IS_HIGH, GPIO_SD_SENSE, 0, 0)); + else +#endif + return true; /* internal: always present */ +} + +#ifdef HAVE_HOTSWAP +bool volume_removable(int volume) +{ + /* don't support more than one partition yet, so volume == drive */ + return hostfs_removable(volume); +} + +bool volume_present(int volume) +{ + /* don't support more than one partition yet, so volume == drive */ + return hostfs_present(volume); +} +#endif + +static int unmount_sd(void) +{ + int ret; + do + { + ret = umount("/mnt/mmc"); + } while (ret && errno != EBUSY && errno != EINVAL); + + return ret; +} + +static int mount_sd(void) +{ + int ret; + /* kludge to make sure we get our wanted mount flags. This is needed + * when the sd was already mounted before we booted */ + unmount_sd(); + char iocharset[64] = "iocharset="; + strlcat(iocharset, get_current_codepage_name_linux(), sizeof(iocharset)); + ret = mount("/dev/mmcblk0p1", "/mnt/mmc", "vfat", + MS_MGC_VAL | MS_SYNCHRONOUS | MS_RELATIME, + iocharset); + /* failure probably means the kernel does not support the iocharset. + * retry without to load the default */ + if (ret == -1) + ret = mount("/dev/mmcblk0p1", "/mnt/mmc", "vfat", + MS_MGC_VAL | MS_SYNCHRONOUS | MS_RELATIME, NULL); + return ret; +} + +#ifdef HAVE_HOTSWAP + +static int sd_thread_stack[DEFAULT_STACK_SIZE]; + +enum { + STATE_POLL, + STATE_DEBOUNCE, + STATE_MOUNT, +}; + +static void NORETURN_ATTR sd_thread(void) +{ + int ret, state = STATE_POLL; + bool last_present, present; + int attempts = 0; + + last_present = present = storage_present(1); /* shut up gcc */ + + while (1) + { + switch (state) + { + case STATE_POLL: + sleep(HZ/3); + attempts = 0; + present = storage_present(1); + if (last_present != present) + state = STATE_DEBOUNCE; + break; + + case STATE_DEBOUNCE: + sleep(HZ/5); + present = storage_present(1); + if (last_present == present) + { + if (present) + queue_broadcast(SYS_HOTSWAP_INSERTED, 0); + else + queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); + state = STATE_MOUNT; + } + else + state = STATE_POLL; + break; + + case STATE_MOUNT: + sleep(HZ/10); + if (present) + ret = mount_sd(); + else + ret = unmount_sd(); + if (ret == 0) + { + NOTEF("Successfully %smounted SD card\n", present ? "":"un"); + queue_broadcast(SYS_FS_CHANGED, 0); + state = STATE_POLL; + } + else if (++attempts > 20) /* stop retrying after 2s */ + { + ERRORF("Failed to %smount SD card. Giving up.", present ? "":"un"); + state = STATE_POLL; + } + /* else: need to retry a few times because the kernel is + * busy setting up the SD (=> do not change state) */ + break; + } + last_present = present; + } +} + +#endif + void hostfs_init(void) { - /* stub */ + /* Setup GPIO pin for microSD sense, copied from OF */ + gpio_control(DEV_CTRL_GPIO_SET_MUX, GPIO_SD_SENSE, CONFIG_DEFAULT, 0); + gpio_control(DEV_CTRL_GPIO_SET_INPUT, GPIO_SD_SENSE, CONFIG_DEFAULT, 0); + if (storage_present(IF_MD(1))) + mount_sd(); +#ifdef HAVE_HOTSWAP + create_thread(sd_thread, sd_thread_stack, sizeof(sd_thread_stack), 0, + "sd thread" IF_PRIO(, PRIORITY_BACKGROUND) IF_COP(, CPU)); +#endif } int hostfs_flush(void) { sync(); - return 0; } -- cgit v1.2.3