diff --git a/platform/FileHandle.cpp b/platform/FileHandle.cpp index 026b11d9ce..c8cd9032a7 100644 --- a/platform/FileHandle.cpp +++ b/platform/FileHandle.cpp @@ -33,9 +33,4 @@ off_t FileHandle::size() return size; } -std::FILE *fdopen(FileHandle *fh, const char *mode) -{ - return mbed_fdopen(fh, mode); -} - } // namespace mbed diff --git a/platform/FileHandle.h b/platform/FileHandle.h index ecec462f7a..5ce64566ab 100644 --- a/platform/FileHandle.h +++ b/platform/FileHandle.h @@ -252,18 +252,6 @@ public: } }; -/** Not a member function - * This call is equivalent to posix fdopen(). - * It associates a Stream to an already opened file descriptor (FileHandle) - * - * @param fh a pointer to an opened file descriptor - * @param mode operation upon the file descriptor, e.g., 'wb+' - * - * @returns a pointer to std::FILE -*/ - -std::FILE *fdopen(FileHandle *fh, const char *mode); - /**@}*/ /**@}*/ diff --git a/platform/mbed_retarget.cpp b/platform/mbed_retarget.cpp index 0e588c252e..1247efacf4 100644 --- a/platform/mbed_retarget.cpp +++ b/platform/mbed_retarget.cpp @@ -33,9 +33,7 @@ #include #include #include -#if DEVICE_STDIO_MESSAGES #include -#endif #include #include "platform/mbed_retarget.h" @@ -95,6 +93,8 @@ static FileHandle *filehandles[OPEN_MAX]; static SingletonPtr filehandle_mutex; namespace mbed { +void mbed_set_unbuffered_stream(std::FILE *_file); + void remove_filehandle(FileHandle *file) { filehandle_mutex->lock(); /* Remove all open filehandles for this */ @@ -175,6 +175,86 @@ static inline int openflags_to_posix(int openflags) { return posix; } +static int reserve_filehandle() { + // find the first empty slot in filehandles, after the slots reserved for stdin/stdout/stderr + filehandle_mutex->lock(); + int fh_i; + for (fh_i = 3; fh_i < OPEN_MAX; fh_i++) { + /* Take a next free filehandle slot available. */ + if (filehandles[fh_i] == NULL) break; + } + if (fh_i >= OPEN_MAX) { + /* Too many file handles have been opened */ + errno = EMFILE; + filehandle_mutex->unlock(); + return -1; + } + filehandles[fh_i] = (FileHandle*)FILE_HANDLE_RESERVED; + filehandle_mutex->unlock(); + + return fh_i; +} + +int mbed::bind_to_fd(FileHandle *fh) { + int fh_i = reserve_filehandle(); + if (fh_i < 0) { + return fh_i; + } + + filehandles[fh_i] = fh; + + return fh_i; +} + +static int unbind_from_fd(int fd, FileHandle *fh) { + if (filehandles[fd] == fh) { + filehandles[fd] = NULL; + return 0; + } else { + errno = EBADF; + return -1; + } +} + +#ifndef __IAR_SYSTEMS_ICC__ +/* IAR provides fdopen itself */ +extern "C" std::FILE* fdopen(int fildes, const char *mode) +{ + // This is to avoid scanf and the bloat it brings. + char buf[1 + sizeof fildes]; /* @(integer) */ + MBED_STATIC_ASSERT(sizeof buf == 5, "Integers should be 4 bytes."); + buf[0] = '@'; + memcpy(buf + 1, &fildes, sizeof fildes); + + std::FILE *stream = std::fopen(buf, mode); + /* newlib-nano doesn't appear to ever call _isatty itself, so + * happily fully buffers an interactive stream. Deal with that here. + */ + if (stream && isatty(fildes)) { + mbed_set_unbuffered_stream(stream); + } + return stream; +} +#endif + +namespace mbed { +std::FILE *fdopen(FileHandle *fh, const char *mode) +{ + // First reserve the integer file descriptor + int fd = bind_to_fd(fh); + if (!fd) { + return NULL; + } + // Then bind that to the C stream. If successful, C library + // takes ownership and responsibility to close. + std::FILE *stream = ::fdopen(fd, mode); + if (!stream) { + unbind_from_fd(fd, fh); + } + return stream; +} +} + /* @brief standard c library fopen() retargeting function. * * This function is invoked by the standard c library retargeting to handle fopen() @@ -187,11 +267,7 @@ static inline int openflags_to_posix(int openflags) { * * */ extern "C" FILEHANDLE PREFIX(_open)(const char *name, int openflags) { - return open(name, openflags_to_posix(openflags)); -} - -extern "C" int open(const char *name, int oflag, ...) { - #if defined(__MICROLIB) && (__ARMCC_VERSION>5030000) +#if defined(__MICROLIB) && (__ARMCC_VERSION>5030000) #if !defined(MBED_CONF_RTOS_PRESENT) // valid only for mbed 2 // for ulib, this is invoked after RAM init, prior c++ @@ -210,7 +286,7 @@ extern "C" int open(const char *name, int oflag, ...) { // This is the workaround that the microlib author suggested us static int n = 0; if (!std::strcmp(name, ":tt")) return n++; - #else +#else /* Use the posix convention that stdin,out,err are filehandles 0,1,2. */ if (std::strcmp(name, __stdin_name) == 0) { @@ -223,54 +299,44 @@ extern "C" int open(const char *name, int oflag, ...) { init_serial(); return 2; } - #endif +#endif +#ifndef __IAR_SYSTEMS_ICC__ + /* FILENAME: "@(integer)" gives an already-allocated descriptor */ + if (name[0] == '@') { + int fd; + memcpy(&fd, name + 1, sizeof fd); + return fd; + } +#endif + return open(name, openflags_to_posix(openflags)); +} - // find the first empty slot in filehandles, after the slots reserved for stdin/stdout/stderr - filehandle_mutex->lock(); - unsigned int fh_i; - for (fh_i = 3; fh_i < sizeof(filehandles)/sizeof(*filehandles); fh_i++) { - /* Take a next free filehandle slot available. */ - if (filehandles[fh_i] == NULL) break; +extern "C" int open(const char *name, int oflag, ...) { + int fh_i = reserve_filehandle(); + if (fh_i < 0) { + return fh_i; } - if (fh_i >= sizeof(filehandles)/sizeof(*filehandles)) { - /* Too many file handles have been opened */ - errno = EMFILE; - filehandle_mutex->unlock(); - return -1; - } - filehandles[fh_i] = (FileHandle*)FILE_HANDLE_RESERVED; - filehandle_mutex->unlock(); FileHandle *res = NULL; + FilePath path(name); - /* FILENAME: ":(pointer)" describes a FileHandle* */ - if (name[0] == ':') { - void *p; - memcpy(&p, name + 1, sizeof(p)); - res = (FileHandle*)p; + if (!path.exists()) { + /* The first part of the filename (between first 2 '/') is not a + * registered mount point in the namespace. + */ + return handle_open_errors(-ENODEV, fh_i); + } - /* FILENAME: "/file_system/file_name" */ + if (path.isFile()) { + res = path.file(); } else { - FilePath path(name); - - if (!path.exists()) { - /* The first part of the filename (between first 2 '/') is not a - * registered mount point in the namespace. - */ + FileSystemHandle *fs = path.fileSystem(); + if (fs == NULL) { return handle_open_errors(-ENODEV, fh_i); } - - if (path.isFile()) { - res = path.file(); - } else { - FileSystemHandle *fs = path.fileSystem(); - if (fs == NULL) { - return handle_open_errors(-ENODEV, fh_i); - } - int err = fs->open(&res, path.fileName(), oflag); - if (err) { - return handle_open_errors(err, fh_i); - } + int err = fs->open(&res, path.fileName(), oflag); + if (err) { + return handle_open_errors(err, fh_i); } } @@ -960,28 +1026,6 @@ void mbed_set_unbuffered_stream(std::FILE *_file) { #endif } -/* Applications are expected to use fdopen() - * not this function directly. This code had to live here because FILE and FileHandle - * processes are all linked together here. - */ -std::FILE *mbed_fdopen(FileHandle *fh, const char *mode) -{ - // This is to avoid scanf(buf, ":%.4s", fh) and the bloat it brings. - char buf[1 + sizeof(fh)]; /* :(pointer) */ - MBED_STATIC_ASSERT(sizeof(buf) == 5, "Pointers should be 4 bytes."); - buf[0] = ':'; - memcpy(buf + 1, &fh, sizeof(fh)); - - std::FILE *stream = std::fopen(buf, mode); - /* newlib-nano doesn't appear to ever call _isatty itself, so - * happily fully buffers an interactive stream. Deal with that here. - */ - if (stream && fh->isatty()) { - mbed_set_unbuffered_stream(stream); - } - return stream; -} - int mbed_getc(std::FILE *_file){ #if defined(__IAR_SYSTEMS_ICC__ ) && (__VER__ < 8000000) /*This is only valid for unbuffered streams*/ diff --git a/platform/mbed_retarget.h b/platform/mbed_retarget.h index 19bafc71f0..f089ef37bb 100644 --- a/platform/mbed_retarget.h +++ b/platform/mbed_retarget.h @@ -21,6 +21,8 @@ #if __cplusplus #include +#else +#include #endif //__cplusplus #include #include @@ -67,9 +69,9 @@ namespace mbed { class FileHandle; class DirHandle; -std::FILE *mbed_fdopen(FileHandle *fh, const char *mode); } + typedef mbed::DirHandle DIR; #else typedef struct Dir DIR; @@ -440,10 +442,18 @@ struct pollfd { short revents; }; +/* POSIX-compatible I/O functions */ #if __cplusplus extern "C" { #endif int open(const char *path, int oflag, ...); +#ifndef __IAR_SYSTEMS_ICC__ /* IAR provides fdopen itself */ +#if __cplusplus + std::FILE* fdopen(int fildes, const char *mode); +#else + FILE* fdopen(int fildes, const char *mode); +#endif +#endif ssize_t write(int fildes, const void *buf, size_t nbyte); ssize_t read(int fildes, void *buf, size_t nbyte); off_t lseek(int fildes, off_t offset, int whence); @@ -462,8 +472,41 @@ extern "C" { void seekdir(DIR*, long); int mkdir(const char *name, mode_t n); #if __cplusplus -}; -#endif +}; // extern "C" + +namespace mbed { + +/** This call is an analogue to POSIX fdopen(). + * + * It associates a C stream to an already-opened FileHandle, to allow you to + * use C printf/scanf/fwrite etc. The provided FileHandle must remain open - + * it will be closed by the C library when fclose(FILE) is called. + * + * The net effect is fdopen(bind_to_fd(fh), mode), with error handling. + * + * @param fh a pointer to an opened FileHandle + * @param mode operation upon the file descriptor, e.g., "w+" + * + * @returns a pointer to FILE + */ +std::FILE* fdopen(mbed::FileHandle *fh, const char *mode); + +/** Bind an mbed FileHandle to a POSIX file descriptor + * + * This is similar to fdopen, but only operating at the POSIX layer - it + * associates a POSIX integer file descriptor with a FileHandle, to allow you + * to use POSIX read/write calls etc. The provided FileHandle must remain open - + * it will be closed when close(int) is called. + * + * @param fh a pointer to an opened FileHandle + * + * @return an integer file descriptor, or negative if no descriptors available + */ +int bind_to_fd(mbed::FileHandle *fh); + +} // namespace mbed + +#endif // __cplusplus /**@}*/