summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2014-02-07 18:30:50 +0100
committerThomas Martitz <kugel@rockbox.org>2014-02-23 20:23:52 +0100
commitf6c26d33a4e15d966597bc9d66c1f33328a750af (patch)
tree5e0e4d0189c856010a78ce0b79cdc3c3cb5c1354
parent46137ebd4d59d7a91d0b8d98d846a3fd2ec70f5c (diff)
downloadrockbox-f6c26d33a4e15d966597bc9d66c1f33328a750af.tar.gz
rockbox-f6c26d33a4e15d966597bc9d66c1f33328a750af.zip
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
-rw-r--r--firmware/common/rbpaths.c117
-rw-r--r--firmware/export/config/samsungypr0.h8
-rw-r--r--firmware/export/mv.h1
-rw-r--r--firmware/export/rbpaths.h5
-rw-r--r--firmware/include/file.h2
-rw-r--r--firmware/target/hosted/samsungypr/ypr0/system-ypr0.c165
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/<microSD1>, 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 <stdbool.h>
#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 "<HOME>" /* 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 <dirent.h>
#include <fcntl.h>
+#include <unistd.h>
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 <stdlib.h>
-#include <string.h>
#include <inttypes.h>
#include <unistd.h>
+#include <sys/mount.h>
+#include <errno.h>
+
#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;
}