OpenRTX/lib/miosix-kernel/miosix/stdlib_integration/libc_integration.cpp

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