1142 lines
28 KiB
C++
1142 lines
28 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 *
|
|
* by Terraneo Federico *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* As a special exception, if other files instantiate templates or use *
|
|
* macros or inline functions from this file, or you compile this file *
|
|
* and link it with other works to produce a work based on this file, *
|
|
* this file does not by itself cause the resulting work to be covered *
|
|
* by the GNU General Public License. However the source code for this *
|
|
* file must still be made available in accordance with the GNU General *
|
|
* Public License. This exception does not invalidate any other reasons *
|
|
* why a work based on this file might be covered by the GNU General *
|
|
* Public License. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
|
***************************************************************************/
|
|
|
|
#include "libc_integration.h"
|
|
#include <stdexcept>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <reent.h>
|
|
#include <sys/time.h>
|
|
#include <sys/times.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/fcntl.h>
|
|
//// Settings
|
|
#include "config/miosix_settings.h"
|
|
//// Filesystem
|
|
#include "filesystem/file_access.h"
|
|
//// Console
|
|
#include "kernel/logging.h"
|
|
//// kernel interface
|
|
#include "kernel/kernel.h"
|
|
#include "interfaces/bsp.h"
|
|
#include "interfaces/delays.h"
|
|
#include "board_settings.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace miosix {
|
|
|
|
// This holds the max heap usage since the program started.
|
|
// It is written by _sbrk_r and read by getMaxHeap()
|
|
static unsigned int maxHeapEnd=0;
|
|
|
|
unsigned int getMaxHeap()
|
|
{
|
|
//If getMaxHeap() is called before the first _sbrk_r() maxHeapEnd is zero.
|
|
extern char _end asm("_end"); //defined in the linker script
|
|
if(maxHeapEnd==0) return reinterpret_cast<unsigned int>(&_end);
|
|
return maxHeapEnd;
|
|
}
|
|
|
|
/**
|
|
* \return the global C reentrancy structure
|
|
*/
|
|
static struct _reent *kernelNotStartedGetReent() { return _GLOBAL_REENT; }
|
|
|
|
/**
|
|
* Pointer to a function that retrieves the correct reentrancy structure.
|
|
* When the C reentrancy structure is requested before the kernel is started,
|
|
* the default reentrancy structure shall be returned, while after the kernel
|
|
* is started, the per-thread reentrancy structure needs to be returned to
|
|
* avoid race conditions between threads.
|
|
* The function pointer is needed to switch between the two behaviors as the
|
|
* per-thread code would cause a circular dependency if called before the
|
|
* kernel is started (getCurrentThread needs to allocate a thread with malloc
|
|
* if called before the kernel istarted, and malloc needs the reentrancy
|
|
* structure).
|
|
*/
|
|
static struct _reent *(*getReent)()=kernelNotStartedGetReent;
|
|
|
|
void setCReentrancyCallback(struct _reent *(*callback)()) { getReent=callback; }
|
|
|
|
} //namespace miosix
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
//
|
|
// C atexit support, for thread safety and code size optimizations
|
|
// ===============================================================
|
|
|
|
// Prior to Miosix 1.58 atexit was effectively unimplemented, but its partial
|
|
// support in newlib used ~384bytes of RAM. Within the kernel it will always
|
|
// be unimplemented, so newlib has been patched not to waste RAM.
|
|
// The support library for Miosix processes will instead implement those stubs
|
|
// so as to support atexit in processes, as in that case it makes sense.
|
|
|
|
/**
|
|
* Function called by atexit(), on_exit() and __cxa_atexit() to register
|
|
* C functions/C++ destructors to be run at program termintion.
|
|
* It is called in this way:
|
|
* atexit(): __register_exitproc(__et_atexit, fn, 0, 0)
|
|
* on_exit(): __register_exitproc(__et_onexit, fn, arg, 0)
|
|
* __cxa_atexit(): __register_exitproc(__et_cxa, fn, arg, d)
|
|
* \param type to understand if the function was called by atexit, on_exit, ...
|
|
* \param fn pointer to function to be called
|
|
* \param arg 0 in case of atexit, function argument in case of on_exit,
|
|
* "this" parameter for C++ destructors registered with __cxa_atexit
|
|
* \param d __dso_handle used to selectively call C++ destructors of a shared
|
|
* library loaded dynamically, unused since Miosix does not support shared libs
|
|
* \return 0 on success
|
|
*/
|
|
int __register_exitproc(int type, void (*fn)(void), void *arg, void *d)
|
|
{
|
|
(void) type;
|
|
(void) fn;
|
|
(void) arg;
|
|
(void) d;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Called by exit() to call functions registered through atexit()
|
|
* \param code the exit code, for example with exit(1), code==1
|
|
* \param d __dso_handle, see __register_exitproc
|
|
*/
|
|
void __call_exitprocs(int code, void *d)
|
|
{
|
|
(void) code;
|
|
(void) d;
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* Required by C++ standard library.
|
|
* See http://lists.debian.org/debian-gcc/2003/07/msg00057.html
|
|
*/
|
|
void *__dso_handle=(void*) &__dso_handle;
|
|
|
|
|
|
|
|
|
|
//
|
|
// C/C++ system calls, to support malloc, printf, fopen, etc.
|
|
// ==========================================================
|
|
|
|
/**
|
|
* \internal
|
|
* _exit, restarts the system
|
|
*/
|
|
void _exit(int n)
|
|
{
|
|
(void) n;
|
|
|
|
miosix::reboot();
|
|
//Never reach here
|
|
for(;;) ; //Required to avoid a warning about noreturn functions
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _sbrk_r, allocates memory dynamically
|
|
*/
|
|
void *_sbrk_r(struct _reent *ptr, ptrdiff_t incr)
|
|
{
|
|
(void) ptr;
|
|
|
|
//This is the absolute start of the heap
|
|
extern char _end asm("_end"); //defined in the linker script
|
|
//This is the absolute end of the heap
|
|
extern char _heap_end asm("_heap_end"); //defined in the linker script
|
|
//This holds the current end of the heap (static)
|
|
static char *curHeapEnd=NULL;
|
|
//This holds the previous end of the heap
|
|
char *prevHeapEnd;
|
|
|
|
//Check if it's first time called
|
|
if(curHeapEnd==NULL) curHeapEnd=&_end;
|
|
|
|
prevHeapEnd=curHeapEnd;
|
|
if((curHeapEnd+incr)>&_heap_end)
|
|
{
|
|
//bad, heap overflow
|
|
#ifdef __NO_EXCEPTIONS
|
|
// When exceptions are disabled operator new would return 0, which would
|
|
// cause undefined behaviour. So when exceptions are disabled, a heap
|
|
// overflow causes a reboot.
|
|
errorLog("\n***Heap overflow\n");
|
|
_exit(1);
|
|
#else //__NO_EXCEPTIONS
|
|
return reinterpret_cast<void*>(-1);
|
|
#endif //__NO_EXCEPTIONS
|
|
}
|
|
curHeapEnd+=incr;
|
|
|
|
if(reinterpret_cast<unsigned int>(curHeapEnd) > miosix::maxHeapEnd)
|
|
miosix::maxHeapEnd=reinterpret_cast<unsigned int>(curHeapEnd);
|
|
|
|
return reinterpret_cast<void*>(prevHeapEnd);
|
|
}
|
|
|
|
void *sbrk(ptrdiff_t incr)
|
|
{
|
|
return _sbrk_r(miosix::getReent(),incr);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* __malloc_lock, called by malloc to ensure no context switch happens during
|
|
* memory allocation (the heap is global and shared between the threads, so
|
|
* memory allocation should not be interrupted by a context switch)
|
|
*
|
|
* WARNING:
|
|
* pauseKernel() does not stop interrupts, so interrupts may occur
|
|
* during memory allocation. So NEVER use malloc inside an interrupt!
|
|
* Also beware that some newlib functions, like printf, iprintf...
|
|
* do call malloc, so you must not use them inside an interrupt.
|
|
*/
|
|
void __malloc_lock()
|
|
{
|
|
miosix::pauseKernel();
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* __malloc_unlock, called by malloc after performing operations on the heap
|
|
*/
|
|
void __malloc_unlock()
|
|
{
|
|
miosix::restartKernel();
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* __getreent(), return the reentrancy structure of the current thread.
|
|
* Used by newlib to make the C standard library thread safe
|
|
*/
|
|
struct _reent *__getreent()
|
|
{
|
|
return miosix::getReent();
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* \internal
|
|
* _open_r, open a file
|
|
*/
|
|
int _open_r(struct _reent *ptr, const char *name, int flags, int mode)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().open(name,flags,mode);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) name;
|
|
(void) flags;
|
|
(void) mode;
|
|
|
|
ptr->_errno=ENFILE;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int open(const char *name, int flags, ...)
|
|
{
|
|
int mode=0;
|
|
if(flags & O_CREAT)
|
|
{
|
|
va_list arg;
|
|
va_start(arg,flags);
|
|
mode=va_arg(arg,int);
|
|
va_end(arg);
|
|
}
|
|
return _open_r(miosix::getReent(),name,flags,mode);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _close_r, close a file
|
|
*/
|
|
int _close_r(struct _reent *ptr, int fd)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().close(fd);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) fd;
|
|
ptr->_errno=EBADF;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int close(int fd)
|
|
{
|
|
return _close_r(miosix::getReent(),fd);
|
|
}
|
|
|
|
int write(int fd, const void *buf, size_t cnt)
|
|
{
|
|
return _write_r(miosix::getReent(),fd,buf,cnt);
|
|
}
|
|
|
|
|
|
int read(int fd, void *buf, size_t cnt)
|
|
{
|
|
return _read_r(miosix::getReent(),fd,buf,cnt);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _lseek_r, move file pointer
|
|
*/
|
|
off_t _lseek_r(struct _reent *ptr, int fd, off_t pos, int whence)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
off_t result=miosix::getFileDescriptorTable().lseek(fd,pos,whence);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) fd;
|
|
(void) pos;
|
|
(void) whence;
|
|
ptr->_errno=EBADF;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
off_t lseek(int fd, off_t pos, int whence)
|
|
{
|
|
return _lseek_r(miosix::getReent(),fd,pos,whence);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _fstat_r, return file info
|
|
*/
|
|
int _fstat_r(struct _reent *ptr, int fd, struct stat *pstat)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().fstat(fd,pstat);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
switch(fd)
|
|
{
|
|
case STDIN_FILENO:
|
|
case STDOUT_FILENO:
|
|
case STDERR_FILENO:
|
|
memset(pstat,0,sizeof(struct stat));
|
|
pstat->st_mode=S_IFCHR;//Character device
|
|
pstat->st_blksize=0; //Defualt file buffer equals to BUFSIZ
|
|
return 0;
|
|
default:
|
|
ptr->_errno=EBADF;
|
|
return -1;
|
|
}
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int fstat(int fd, struct stat *pstat)
|
|
{
|
|
return _fstat_r(miosix::getReent(),fd,pstat);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _stat_r, collect data about a file
|
|
*/
|
|
int _stat_r(struct _reent *ptr, const char *file, struct stat *pstat)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().stat(file,pstat);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) file;
|
|
(void) pstat;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int stat(const char *file, struct stat *pstat)
|
|
{
|
|
return _stat_r(miosix::getReent(),file,pstat);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* isatty, returns 1 if fd is associated with a terminal
|
|
*/
|
|
int _isatty_r(struct _reent *ptr, int fd)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().isatty(fd);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) ptr;
|
|
switch(fd)
|
|
{
|
|
case STDIN_FILENO:
|
|
case STDOUT_FILENO:
|
|
case STDERR_FILENO:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int isatty(int fd)
|
|
{
|
|
return _isatty_r(miosix::getReent(),fd);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _fntl_r, perform operations on a file descriptor
|
|
*/
|
|
int _fcntl_r(struct _reent *ptr, int fd, int cmd, int opt)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().fcntl(fd,cmd,opt);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) fd;
|
|
(void) cmd;
|
|
(void) opt;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int fcntl(int fd, int cmd, ...)
|
|
{
|
|
va_list arg;
|
|
int result;
|
|
struct _reent *r=miosix::getReent();
|
|
switch(cmd)
|
|
{
|
|
case F_DUPFD:
|
|
case F_SETFD:
|
|
case F_SETFL:
|
|
va_start(arg,cmd);
|
|
result=_fcntl_r(r,fd,cmd,va_arg(arg,int));
|
|
va_end(arg);
|
|
default:
|
|
result=_fcntl_r(r,fd,cmd,0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _ioctl_r, perform operations on a file descriptor
|
|
*/
|
|
int _ioctl_r(struct _reent *ptr, int fd, int cmd, void *arg)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().ioctl(fd,cmd,arg);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
if(fd==STDIN_FILENO || fd==STDOUT_FILENO || fd==STDERR_FILENO)
|
|
{
|
|
int result=miosix::DefaultConsole::instance().getTerminal()->ioctl(cmd,arg);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
} else {
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
}
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int ioctl(int fd, int cmd, void *arg)
|
|
{
|
|
return _ioctl_r(miosix::getReent(),fd,cmd,arg);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _getcwd_r, return current directory
|
|
*/
|
|
char *_getcwd_r(struct _reent *ptr, char *buf, size_t size)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().getcwd(buf,size);
|
|
if(result>=0) return buf;
|
|
ptr->_errno=-result;
|
|
return NULL;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return NULL;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) buf;
|
|
(void) size;
|
|
ptr->_errno=ENOENT;
|
|
return NULL;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
char *getcwd(char *buf, size_t size)
|
|
{
|
|
return _getcwd_r(miosix::getReent(),buf,size);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _chdir_r, change current directory
|
|
*/
|
|
int _chdir_r(struct _reent *ptr, const char *path)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().chdir(path);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) path;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int chdir(const char *path)
|
|
{
|
|
return _chdir_r(miosix::getReent(),path);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _mkdir_r, create a directory
|
|
*/
|
|
int _mkdir_r(struct _reent *ptr, const char *path, int mode)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().mkdir(path,mode);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) path;
|
|
(void) mode;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int mkdir(const char *path, mode_t mode)
|
|
{
|
|
return _mkdir_r(miosix::getReent(),path,mode);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _rmdir_r, remove a directory if empty
|
|
*/
|
|
int _rmdir_r(struct _reent *ptr, const char *path)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().rmdir(path);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) path;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int rmdir(const char *path)
|
|
{
|
|
return _rmdir_r(miosix::getReent(),path);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _link_r: create hardlinks
|
|
*/
|
|
int _link_r(struct _reent *ptr, const char *f_old, const char *f_new)
|
|
{
|
|
(void) f_old;
|
|
(void) f_new;
|
|
ptr->_errno=ENOENT; //Unimplemented at the moment
|
|
return -1;
|
|
}
|
|
|
|
int link(const char *f_old, const char *f_new)
|
|
{
|
|
return _link_r(miosix::getReent(),f_old,f_new);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _unlink_r, remove a file
|
|
*/
|
|
int _unlink_r(struct _reent *ptr, const char *file)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().unlink(file);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) file;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int unlink(const char *file)
|
|
{
|
|
return _unlink_r(miosix::getReent(),file);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _rename_r, rename a file or directory
|
|
*/
|
|
int _rename_r(struct _reent *ptr, const char *f_old, const char *f_new)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().rename(f_old,f_new);
|
|
if(result>=0) return result;
|
|
ptr->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
ptr->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) f_old;
|
|
(void) f_new;
|
|
ptr->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
int rename(const char *f_old, const char *f_new)
|
|
{
|
|
return _rename_r(miosix::getReent(),f_old,f_new);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* getdents, allows to list the content of a directory
|
|
*/
|
|
int getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
|
|
{
|
|
#ifdef WITH_FILESYSTEM
|
|
|
|
#ifndef __NO_EXCEPTIONS
|
|
try {
|
|
#endif //__NO_EXCEPTIONS
|
|
int result=miosix::getFileDescriptorTable().getdents(fd,dirp,count);
|
|
if(result>=0) return result;
|
|
miosix::getReent()->_errno=-result;
|
|
return -1;
|
|
#ifndef __NO_EXCEPTIONS
|
|
} catch(exception& e) {
|
|
miosix::getReent()->_errno=ENOMEM;
|
|
return -1;
|
|
}
|
|
#endif //__NO_EXCEPTIONS
|
|
|
|
#else //WITH_FILESYSTEM
|
|
(void) fd;
|
|
(void) dirp;
|
|
(void) count;
|
|
miosix::getReent()->_errno=ENOENT;
|
|
return -1;
|
|
#endif //WITH_FILESYSTEM
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Time API in Miosix
|
|
* ==================
|
|
*
|
|
* CORE
|
|
* - clock_gettime
|
|
* - clock_nanosleep
|
|
* - clock_settime
|
|
* - clock_getres
|
|
*
|
|
* DERIVED, preferred
|
|
* - C++11 chrono system|steady_clock -> clock_gettime
|
|
* - C++11 sleep_for|until -> clock_nanosleep
|
|
*
|
|
* DERIVED, for compatibility (replace with core functions when possible)
|
|
* - sleep|usleep -> nanosleep -> clock_nanosleep
|
|
* - clock -> times -> clock_gettime
|
|
* - time -> gettimeofday -> clock_gettime
|
|
*
|
|
* UNSUPPORTED
|
|
* - timer_create -> ?
|
|
*/
|
|
|
|
#ifndef _MIOSIX_GCC_PATCH_MAJOR //Before GCC 9.2.0
|
|
#define CLOCK_MONOTONIC 4
|
|
#endif
|
|
|
|
/// Conversion factor from ticks to nanoseconds
|
|
/// TICK_FREQ in Miosix is either 1000 or (on older chips) 200, so a simple
|
|
/// multiplication/division factor does not cause rounding errors
|
|
static constexpr long tickNsFactor=1000000000/miosix::TICK_FREQ;
|
|
|
|
/**
|
|
* Convert from timespec to the Miosix representation of time
|
|
* \param tp input timespec, must not be nullptr and be a valid pointer
|
|
* \return Miosix ticks
|
|
*/
|
|
inline long long timespec2ll(const struct timespec *tp)
|
|
{
|
|
//NOTE: the cast is required to prevent overflow with older versions
|
|
//of the Miosix compiler where tv_sec is int and not long long
|
|
return static_cast<long long>(tp->tv_sec)*miosix::TICK_FREQ
|
|
+ tp->tv_nsec/tickNsFactor;
|
|
}
|
|
|
|
/**
|
|
* Convert from he Miosix representation of time to a timespec
|
|
* \param tick input Miosix ticks
|
|
* \param tp output timespec, must not be nullptr and be a valid pointer
|
|
*/
|
|
inline void ll2timespec(long long tick, struct timespec *tp)
|
|
{
|
|
#ifdef __ARM_EABI__
|
|
// Despite there being a single intrinsic, __aeabi_ldivmod, that computes
|
|
// both the result of the / and % operator, GCC 9.2.0 isn't smart enough and
|
|
// calls the intrinsic twice. This asm implementation saves ~115 cycles
|
|
// by calling it once. Sadly, I had to use asm as the calling conventions
|
|
// of the intrinsic appear to be nonstandard.
|
|
// NOTE: actually a and b, by being 64 bit numbers, occupy register pairs
|
|
register long long a asm("r0") = tick;
|
|
register long long b asm("r2") = miosix::TICK_FREQ;
|
|
// NOTE: clobbering lr to mark function not leaf due to the bl
|
|
asm volatile("bl __aeabi_ldivmod" : "+r"(a), "+r"(b) :: "lr");
|
|
tp->tv_sec = a;
|
|
tp->tv_nsec = static_cast<long>(b) * tickNsFactor;
|
|
#else //__ARM_EABI__
|
|
tp->tv_sec = tick / miosix::TICK_FREQ;
|
|
tp->tv_nsec = static_cast<long>(tick % miosix::TICK_FREQ) * tickNsFactor;
|
|
#endif //__ARM_EABI__
|
|
}
|
|
|
|
int clock_gettime(clockid_t clock_id, struct timespec *tp)
|
|
{
|
|
(void) clock_id;
|
|
(void) tp;
|
|
|
|
if(tp==nullptr) return -1;
|
|
//TODO: support CLOCK_REALTIME
|
|
ll2timespec(miosix::getTick(),tp);
|
|
return 0;
|
|
}
|
|
|
|
int clock_settime(clockid_t clock_id, const struct timespec *tp)
|
|
{
|
|
(void) clock_id;
|
|
(void) tp;
|
|
|
|
//TODO: support CLOCK_REALTIME
|
|
return -1;
|
|
}
|
|
|
|
int clock_getres(clockid_t clock_id, struct timespec *res)
|
|
{
|
|
(void) clock_id;
|
|
|
|
if(res==nullptr) return -1;
|
|
res->tv_sec=0;
|
|
res->tv_nsec=tickNsFactor;
|
|
return 0;
|
|
}
|
|
|
|
int clock_nanosleep(clockid_t clock_id, int flags,
|
|
const struct timespec *req, struct timespec *rem)
|
|
{
|
|
(void) clock_id;
|
|
(void) rem;
|
|
|
|
if(req==nullptr) return -1;
|
|
//TODO: support CLOCK_REALTIME
|
|
long long timeTick=timespec2ll(req);
|
|
if(flags!=TIMER_ABSTIME) timeTick+=miosix::getTick();
|
|
miosix::Thread::sleepUntil(timeTick);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _times_r, return elapsed time
|
|
*/
|
|
clock_t _times_r(struct _reent *ptr, struct tms *tim)
|
|
{
|
|
(void) ptr;
|
|
|
|
struct timespec tp;
|
|
//No CLOCK_PROCESS_CPUTIME_ID support, use CLOCK_MONOTONIC
|
|
if(clock_gettime(CLOCK_MONOTONIC,&tp)) return static_cast<clock_t>(-1);
|
|
constexpr int divFactor=1000000000/CLOCKS_PER_SEC;
|
|
clock_t utime=tp.tv_sec*CLOCKS_PER_SEC + tp.tv_nsec/divFactor;
|
|
|
|
//Actually, we should return tim.utime or -1 on failure, but clock_t is
|
|
//unsigned, so if we return tim.utime and someone calls _times_r in an
|
|
//unlucky moment where tim.utime is 0xffffffff it would be interpreted as -1
|
|
//IMHO, the specifications are wrong since returning an unsigned leaves
|
|
//no value left to return in case of errors. Thus 0 is returned if a valid
|
|
//pointer is passed, and tim.utime if the pointer is null
|
|
if(tim==nullptr) return utime;
|
|
tim->tms_utime=utime;
|
|
tim->tms_stime=0;
|
|
tim->tms_cutime=0;
|
|
tim->tms_cstime=0;
|
|
return 0;
|
|
}
|
|
|
|
clock_t times(struct tms *tim)
|
|
{
|
|
return _times_r(miosix::getReent(),tim);
|
|
}
|
|
|
|
int _gettimeofday_r(struct _reent *ptr, struct timeval *tv, void *tz)
|
|
{
|
|
(void) ptr;
|
|
|
|
if(tv==nullptr || tz!=nullptr) return -1;
|
|
struct timespec tp;
|
|
if(clock_gettime(CLOCK_REALTIME,&tp)) return -1;
|
|
tv->tv_sec=tp.tv_sec;
|
|
tv->tv_usec=tp.tv_nsec/1000;
|
|
return 0;
|
|
}
|
|
|
|
int gettimeofday(struct timeval *tv, void *tz)
|
|
{
|
|
return _gettimeofday_r(miosix::getReent(),tv,tz);
|
|
}
|
|
|
|
|
|
int nanosleep(const struct timespec *req, struct timespec *rem)
|
|
{
|
|
return clock_nanosleep(CLOCK_MONOTONIC,0,req,rem);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* \internal
|
|
* it looks like abort() calls _kill instead of exit, this implementation
|
|
* calls _exit() so that calling abort() really terminates the program
|
|
*/
|
|
int _kill_r(struct _reent* ptr, int pid, int sig)
|
|
{
|
|
(void) ptr;
|
|
(void) sig;
|
|
|
|
if(pid==0) _exit(1); //pid=1 means the only running process
|
|
else return -1;
|
|
}
|
|
|
|
int kill(int pid, int sig)
|
|
{
|
|
return _kill_r(miosix::getReent(),pid,sig);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _getpid_r, there is only one process in Miosix (but multiple threads)
|
|
*/
|
|
int _getpid_r(struct _reent* ptr)
|
|
{
|
|
(void) ptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* getpid, there is only one process in Miosix (but multiple threads)
|
|
*/
|
|
int getpid()
|
|
{
|
|
return _getpid_r(miosix::getReent());
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _wait_r, unimpemented because processes are not supported in Miosix
|
|
*/
|
|
int _wait_r(struct _reent *ptr, int *status)
|
|
{
|
|
(void) ptr;
|
|
(void) status;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int wait(int *status)
|
|
{
|
|
return _wait_r(miosix::getReent(),status);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _execve_r, unimpemented because processes are not supported in Miosix
|
|
*/
|
|
int _execve_r(struct _reent *ptr, const char *path, char *const argv[],
|
|
char *const env[])
|
|
{
|
|
(void) ptr;
|
|
(void) path;
|
|
(void) argv;
|
|
(void) env;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int execve(const char *path, char *const argv[], char *const env[])
|
|
{
|
|
return _execve_r(miosix::getReent(),path,argv,env);
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* _forkexecve_r, reserved for future use
|
|
*/
|
|
pid_t _forkexecve_r(struct _reent *ptr, const char *path, char *const argv[],
|
|
char *const env[])
|
|
{
|
|
(void) ptr;
|
|
(void) path;
|
|
(void) argv;
|
|
(void) env;
|
|
|
|
return -1;
|
|
}
|
|
|
|
pid_t forkexecve(const char *path, char *const argv[], char *const env[])
|
|
{
|
|
return _forkexecve_r(miosix::getReent(),path,argv,env);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//
|
|
// Check that newlib has been configured correctly
|
|
// ===============================================
|
|
|
|
#ifndef _REENT_SMALL
|
|
#error "_REENT_SMALL not defined"
|
|
#endif //_REENT_SMALL
|
|
|
|
#ifndef _POSIX_THREADS
|
|
#error "_POSIX_THREADS not defined"
|
|
#endif //_POSIX_THREADS
|
|
|
|
#ifndef __DYNAMIC_REENT__
|
|
#error "__DYNAMIC_REENT__ not defined"
|
|
#endif
|