mirror of https://github.com/ARMmbed/mbed-os.git
Rework retarget opening
Rationalise layers a little more to add the POSIX standard fdopen(int) and a local open(FileHandle) to map a FileHandle to a POSIX file descriptor. fdopen(FileHandle) is now a composite of those two, rather than being a primitive.pull/5571/head
parent
b5ac071b66
commit
96c709fb35
|
@ -33,9 +33,4 @@ off_t FileHandle::size()
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::FILE *fdopen(FileHandle *fh, const char *mode)
|
|
||||||
{
|
|
||||||
return mbed_fdopen(fh, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mbed
|
} // namespace mbed
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
|
@ -33,9 +33,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#if DEVICE_STDIO_MESSAGES
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#endif
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "platform/mbed_retarget.h"
|
#include "platform/mbed_retarget.h"
|
||||||
|
|
||||||
|
@ -95,6 +93,8 @@ static FileHandle *filehandles[OPEN_MAX];
|
||||||
static SingletonPtr<PlatformMutex> filehandle_mutex;
|
static SingletonPtr<PlatformMutex> filehandle_mutex;
|
||||||
|
|
||||||
namespace mbed {
|
namespace mbed {
|
||||||
|
void mbed_set_unbuffered_stream(std::FILE *_file);
|
||||||
|
|
||||||
void remove_filehandle(FileHandle *file) {
|
void remove_filehandle(FileHandle *file) {
|
||||||
filehandle_mutex->lock();
|
filehandle_mutex->lock();
|
||||||
/* Remove all open filehandles for this */
|
/* Remove all open filehandles for this */
|
||||||
|
@ -175,6 +175,86 @@ static inline int openflags_to_posix(int openflags) {
|
||||||
return posix;
|
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.
|
/* @brief standard c library fopen() retargeting function.
|
||||||
*
|
*
|
||||||
* This function is invoked by the standard c library retargeting to handle fopen()
|
* 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) {
|
extern "C" FILEHANDLE PREFIX(_open)(const char *name, int openflags) {
|
||||||
return open(name, openflags_to_posix(openflags));
|
#if defined(__MICROLIB) && (__ARMCC_VERSION>5030000)
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int open(const char *name, int oflag, ...) {
|
|
||||||
#if defined(__MICROLIB) && (__ARMCC_VERSION>5030000)
|
|
||||||
#if !defined(MBED_CONF_RTOS_PRESENT)
|
#if !defined(MBED_CONF_RTOS_PRESENT)
|
||||||
// valid only for mbed 2
|
// valid only for mbed 2
|
||||||
// for ulib, this is invoked after RAM init, prior c++
|
// 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
|
// This is the workaround that the microlib author suggested us
|
||||||
static int n = 0;
|
static int n = 0;
|
||||||
if (!std::strcmp(name, ":tt")) return n++;
|
if (!std::strcmp(name, ":tt")) return n++;
|
||||||
#else
|
#else
|
||||||
/* Use the posix convention that stdin,out,err are filehandles 0,1,2.
|
/* Use the posix convention that stdin,out,err are filehandles 0,1,2.
|
||||||
*/
|
*/
|
||||||
if (std::strcmp(name, __stdin_name) == 0) {
|
if (std::strcmp(name, __stdin_name) == 0) {
|
||||||
|
@ -223,54 +299,44 @@ extern "C" int open(const char *name, int oflag, ...) {
|
||||||
init_serial();
|
init_serial();
|
||||||
return 2;
|
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
|
extern "C" int open(const char *name, int oflag, ...) {
|
||||||
filehandle_mutex->lock();
|
int fh_i = reserve_filehandle();
|
||||||
unsigned int fh_i;
|
if (fh_i < 0) {
|
||||||
for (fh_i = 3; fh_i < sizeof(filehandles)/sizeof(*filehandles); fh_i++) {
|
return fh_i;
|
||||||
/* Take a next free filehandle slot available. */
|
|
||||||
if (filehandles[fh_i] == NULL) break;
|
|
||||||
}
|
}
|
||||||
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;
|
FileHandle *res = NULL;
|
||||||
|
FilePath path(name);
|
||||||
|
|
||||||
/* FILENAME: ":(pointer)" describes a FileHandle* */
|
if (!path.exists()) {
|
||||||
if (name[0] == ':') {
|
/* The first part of the filename (between first 2 '/') is not a
|
||||||
void *p;
|
* registered mount point in the namespace.
|
||||||
memcpy(&p, name + 1, sizeof(p));
|
*/
|
||||||
res = (FileHandle*)p;
|
return handle_open_errors(-ENODEV, fh_i);
|
||||||
|
}
|
||||||
|
|
||||||
/* FILENAME: "/file_system/file_name" */
|
if (path.isFile()) {
|
||||||
|
res = path.file();
|
||||||
} else {
|
} else {
|
||||||
FilePath path(name);
|
FileSystemHandle *fs = path.fileSystem();
|
||||||
|
if (fs == NULL) {
|
||||||
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);
|
return handle_open_errors(-ENODEV, fh_i);
|
||||||
}
|
}
|
||||||
|
int err = fs->open(&res, path.fileName(), oflag);
|
||||||
if (path.isFile()) {
|
if (err) {
|
||||||
res = path.file();
|
return handle_open_errors(err, fh_i);
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -960,28 +1026,6 @@ void mbed_set_unbuffered_stream(std::FILE *_file) {
|
||||||
#endif
|
#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){
|
int mbed_getc(std::FILE *_file){
|
||||||
#if defined(__IAR_SYSTEMS_ICC__ ) && (__VER__ < 8000000)
|
#if defined(__IAR_SYSTEMS_ICC__ ) && (__VER__ < 8000000)
|
||||||
/*This is only valid for unbuffered streams*/
|
/*This is only valid for unbuffered streams*/
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#if __cplusplus
|
#if __cplusplus
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#else
|
||||||
|
#include <stdio.h>
|
||||||
#endif //__cplusplus
|
#endif //__cplusplus
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -67,9 +69,9 @@ namespace mbed {
|
||||||
|
|
||||||
class FileHandle;
|
class FileHandle;
|
||||||
class DirHandle;
|
class DirHandle;
|
||||||
std::FILE *mbed_fdopen(FileHandle *fh, const char *mode);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef mbed::DirHandle DIR;
|
typedef mbed::DirHandle DIR;
|
||||||
#else
|
#else
|
||||||
typedef struct Dir DIR;
|
typedef struct Dir DIR;
|
||||||
|
@ -440,10 +442,18 @@ struct pollfd {
|
||||||
short revents;
|
short revents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* POSIX-compatible I/O functions */
|
||||||
#if __cplusplus
|
#if __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
int open(const char *path, int oflag, ...);
|
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 write(int fildes, const void *buf, size_t nbyte);
|
||||||
ssize_t read(int fildes, 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);
|
off_t lseek(int fildes, off_t offset, int whence);
|
||||||
|
@ -462,8 +472,41 @@ extern "C" {
|
||||||
void seekdir(DIR*, long);
|
void seekdir(DIR*, long);
|
||||||
int mkdir(const char *name, mode_t n);
|
int mkdir(const char *name, mode_t n);
|
||||||
#if __cplusplus
|
#if __cplusplus
|
||||||
};
|
}; // extern "C"
|
||||||
#endif
|
|
||||||
|
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
|
||||||
|
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue