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;
|
||||
}
|
||||
|
||||
std::FILE *fdopen(FileHandle *fh, const char *mode)
|
||||
{
|
||||
return mbed_fdopen(fh, mode);
|
||||
}
|
||||
|
||||
} // 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 <string.h>
|
||||
#include <limits.h>
|
||||
#if DEVICE_STDIO_MESSAGES
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include "platform/mbed_retarget.h"
|
||||
|
||||
|
@ -95,6 +93,8 @@ static FileHandle *filehandles[OPEN_MAX];
|
|||
static SingletonPtr<PlatformMutex> 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*/
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#if __cplusplus
|
||||
#include <cstdio>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif //__cplusplus
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
@ -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
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
|
Loading…
Reference in New Issue