blob: 3cc55e29ecd54f5df31df695211fdee245c0267d [file] [log] [blame]
/* ------------------------------------------
* Copyright (c) 2017, Synopsys, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1) Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* 3) Neither the name of the Synopsys, Inc., nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* \version 2017.03
* \date 2016-01-18
* \author Huaqi Fang(Huaqi.Fang@synopsys.com)
--------------------------------------------- */
#include "embARC_syscalls.h"
#include "device/device_hal/inc/dev_uart.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
#ifdef __GNU__
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/times.h>
#include <sys/stat.h>
#endif
/**
* \todo Consider hostlink and nsim support
*/
#ifndef _HOSTLINK_ /* Not using hostlink library */
#ifndef _ENVIRON_LEN
#define _ENVIRON_LEN 32
#endif
#ifndef BOARD_CONSOLE_UART_ID
#define BOARD_CONSOLE_UART_ID 1
#endif
#ifndef BOARD_CONSOLE_UART_BAUD
#define BOARD_CONSOLE_UART_BAUD UART_BAUDRATE_115200
#endif
#ifndef EMBARC_CFG_STDIO_CRLF
#define EMBARC_CFG_STDIO_CRLF 1
#endif
#ifndef EMBARC_CFG_USE_RTCTIME
#define EMBARC_CFG_USE_RTCTIME 0
#endif
#define STDIO_FID_OFS 3
int stdio_uart_inited = 0;
DEV_UART *stdio_uart_obj;
static void init_stdio_serial(void)
{
if (stdio_uart_inited) { return; }
stdio_uart_inited = 1;
stdio_uart_obj = uart_get_dev(BOARD_CONSOLE_UART_ID);
if (stdio_uart_obj)
{
stdio_uart_obj->uart_open(BOARD_CONSOLE_UART_BAUD);
stdio_uart_obj->uart_control(UART_CMD_SET_BAUD, CONV2VOID(BOARD_CONSOLE_UART_BAUD));
}
else
{
stdio_uart_inited = -1;
}
}
static int stdio_read(char *buffer, unsigned int length)
{
if (!stdio_uart_obj) { return -1; }
unsigned int avail_len;
stdio_uart_obj->uart_control(UART_CMD_GET_RXAVAIL, (void *)(&avail_len));
length = (length < avail_len) ? length : avail_len;
if (length == 0)
{
length = 1;
}
return (int)stdio_uart_obj->uart_read((void *)buffer, (uint32_t)length);
}
static void stdio_write_char(char data)
{
stdio_uart_obj->uart_write((const void *)(&data), 1);
}
static int stdio_write(const char *buffer, unsigned int length)
{
if (!stdio_uart_obj) { return -1; }
#if EMBARC_CFG_STDIO_CRLF
unsigned int i = 0;
while (i < length)
{
if (_arc_rarely(buffer[i] == '\n'))
{
stdio_write_char('\r');
}
stdio_write_char(buffer[i]);
i++;
}
return (int)i;
#else
return (int)stdio_uart_obj->uart_write((const void *)buffer, (uint32_t)length);
#endif
}
/////////////////////
// File Processing //
/////////////////////
#ifdef MID_FATFS
#include "ff.h"
#ifndef EMBARC_CFG_MAX_FILEHANDLES
#define EMBARC_CFG_MAX_FILEHANDLES 32
#endif
static FIL *file_handles[EMBARC_CFG_MAX_FILEHANDLES] = { NULL };
#endif
#if defined(MID_FATFS) && !_FS_READONLY
/** Posix mode to FatFS mode */
Inline BYTE conv_openmode(int flags)
{
BYTE openmode = 0;
if (flags == O_RDONLY)
{
openmode = FA_READ;
}
if (flags & O_WRONLY)
{
openmode |= FA_WRITE;
}
if (flags & O_RDWR)
{
openmode |= FA_READ | FA_WRITE;
}
if (flags & O_CREAT)
{
if (flags & O_TRUNC)
{
openmode |= FA_CREATE_ALWAYS;
}
else
{
openmode |= FA_OPEN_ALWAYS;
}
}
return openmode;
}
#endif
/** Opening Files */
#ifdef __MW__
int SYSCALL_PREFIX(_open)(const char *path, int flags, /* int mode */ ...)
#else
int SYSCALL_PREFIX(_open)(const char *path, int flags, int mode)
#endif
{
#ifdef MID_FATFS
int fid; /** file handle id */
FIL *tmp_filep; /** temp file handle pointer */
BYTE openmode;
/* Find the first available file pointer in this handles */
for (fid = 0; fid < EMBARC_CFG_MAX_FILEHANDLES; fid ++)
{
if (file_handles[fid] == NULL)
{
break;
}
}
if (fid >= EMBARC_CFG_MAX_FILEHANDLES)
{
return -1; /* No more available file handler */
}
/* Malloc memory for FIL object */
tmp_filep = (FIL *)malloc(sizeof(FIL));
if (!tmp_filep) /* No more memory */
{
return -1;
}
file_handles[fid] = tmp_filep;
#if _FS_READONLY
if (flags == O_RDONLY)
{
openmode = FA_READ;
}
else
{
return -1;
}
#else
openmode = conv_openmode(flags);
#endif
FRESULT res = f_open(tmp_filep, path, openmode);
if (res) /* open error */
{
free(tmp_filep);
file_handles[fid] = NULL;
return -1;
}
/** Append if O_APPEND flags is set */
if (flags & O_APPEND)
{
f_lseek(tmp_filep, f_size(tmp_filep));
}
return fid + STDIO_FID_OFS; /* 0-2 are stdin/stdout/stderr, so plus 3 */
#else /* No File System */
return -1;
#endif
}
/** Closing Files */
int SYSCALL_PREFIX(_close)(int handle)
{
if (handle < STDIO_FID_OFS) /* for stdin/stdout/stderr */
{
return 0;
}
#ifdef MID_FATFS
if ((handle - STDIO_FID_OFS) >= EMBARC_CFG_MAX_FILEHANDLES)
{
return -1; /* No more available file handler */
}
FIL *tmp_filep; /** temp file handle pointer */
tmp_filep = file_handles[handle - STDIO_FID_OFS];
if (tmp_filep)
{
file_handles[handle - STDIO_FID_OFS] = NULL;
FRESULT res = f_close(tmp_filep);
free(tmp_filep);
if (res)
{
return -1;
}
return 0;
}
#endif
return -1;
}
/** Reading Files */
int SYSCALL_PREFIX(_read)(int handle, char *buffer, unsigned int length)
{
if (handle < STDIO_FID_OFS)
{
init_stdio_serial();
return stdio_read(buffer, length);
}
#ifdef MID_FATFS
if ((handle - STDIO_FID_OFS) >= EMBARC_CFG_MAX_FILEHANDLES)
{
return -1; /* No more available file handler */
}
FIL *tmp_filep; /** temp file handle pointer */
tmp_filep = file_handles[handle - STDIO_FID_OFS];
if (tmp_filep)
{
UINT br;
FRESULT res = f_read(tmp_filep, (void *)buffer, (UINT)length, &br);
if (res)
{
return -1;
}
return (int)br;
}
#endif
return -1;
}
/** Writing Files */
int SYSCALL_PREFIX(_write)(int handle, const char *buffer, unsigned int length)
{
if (handle < STDIO_FID_OFS)
{
init_stdio_serial();
return stdio_write(buffer, length);
}
#ifdef MID_FATFS
if ((handle - STDIO_FID_OFS) >= EMBARC_CFG_MAX_FILEHANDLES)
{
return -1; /* No more available file handler */
}
FIL *tmp_filep; /** temp file handle pointer */
tmp_filep = file_handles[handle - STDIO_FID_OFS];
if (tmp_filep)
{
UINT bw;
FRESULT res = f_write(tmp_filep, (void *)buffer, (UINT)length, &bw);
if (res)
{
return -1;
}
return (int)bw;
}
#endif
return -1;
}
/** Seeking to a Location in a File */
long SYSCALL_PREFIX(_lseek)(int handle, long offset, int method)
{
if (handle < STDIO_FID_OFS)
{
return 0;
}
#ifdef MID_FATFS
if ((handle - STDIO_FID_OFS) >= EMBARC_CFG_MAX_FILEHANDLES)
{
return -1; /* No more available file handler */
}
FIL *tmp_filep; /** temp file handle pointer */
tmp_filep = file_handles[handle - STDIO_FID_OFS];
if (tmp_filep)
{
FRESULT res;
long ofs = offset;
if (method == SEEK_CUR)
{
ofs = (long)f_tell(tmp_filep) + offset;
}
else if (method == SEEK_END)
{
ofs = (long)f_size(tmp_filep) + offset;
}
if (ofs >= 0)
{
res = f_lseek(tmp_filep, (DWORD)ofs);
if (res) { return -1; }
return f_tell(tmp_filep);
}
}
#endif
return -1;
}
/** Testing File Access */
int SYSCALL_PREFIX(_access)(const char *name, int mode)
{
#ifdef MID_FATFS
FRESULT res = f_stat((const TCHAR *)name, NULL);
if (res) { return -1; }
return 0;
#endif
return -1;
}
/** Testing for an Interactive Display Device */
int SYSCALL_PREFIX(_isatty)(int handle)
{
if (handle < STDIO_FID_OFS)
{
return 1;
}
#ifdef MID_FATFS
if ((handle - STDIO_FID_OFS) >= EMBARC_CFG_MAX_FILEHANDLES)
{
return -1; /* No more available file handler */
}
FIL *tmp_filep; /** temp file handle pointer */
tmp_filep = file_handles[handle - STDIO_FID_OFS];
if (tmp_filep)
{
return 0;
}
#endif
return -1;
}
/** Unlink Files */
int SYSCALL_PREFIX(_unlink)(const char *path)
{
#ifdef MID_FATFS
if (f_unlink((const TCHAR *)path) == FR_OK)
{
return 0;
}
#endif
return -1;
}
/** Renaming Files */
int SYSCALL_PREFIX(rename)(const char *old, const char *new)
{
#ifdef MID_FATFS
if (f_rename((const TCHAR *)old, (const TCHAR *)new) == FR_OK)
{
return 0;
}
#endif
return -1;
}
/** Remove Directory */
int SYSCALL_PREFIX(_rmdir)(const char *pathname)
{
return SYSCALL_PREFIX(_unlink)(pathname);
}
/** Status of a file (by name). */
int SYSCALL_PREFIX(_stat)(const char *path, struct stat *buf)
{
if (!buf) { return -1; }
#ifdef MID_FATFS
FILINFO fno;
#if _USE_LFN
fno.lfname = NULL;
fno.lfsize = 0;
#endif
if (f_stat((const TCHAR *)path, &fno) == FR_OK)
{
if (fno.fattrib & AM_DIR)
{
buf->st_mode = S_IFDIR;
}
else
{
buf->st_mode = S_IFREG;
}
buf->st_size = fno.fsize;
buf->st_mtime = fno.ftime;
return 0;
}
#endif
return -1;
}
/** Status of a file (by name). */
int SYSCALL_PREFIX(_lstat)(const char *path, struct stat *buf)
{
return SYSCALL_PREFIX(_stat)(path, buf);
}
/** Status of an open file */
/** \todo fstat need more work to provide correct file stat */
int SYSCALL_PREFIX(_fstat)(int handle, struct stat *buf)
{
if (!buf) { return -1; }
if (handle < STDIO_FID_OFS)
{
buf->st_mode = S_IFCHR;
buf->st_blksize = 1;
return 0;
}
/** Can't provide unix file stat using fatfs API */
return -1;
}
//////////////////////////////
// Directory Identification //
//////////////////////////////
/** Determining the Current Directory */
char *SYSCALL_PREFIX(_getcwd)(char *buf, int len)
{
#ifdef MID_FATFS
if (f_getcwd((TCHAR *)buf, (UINT)len) == FR_OK)
{
return buf;
}
#endif
return NULL;
}
#ifdef __GNU__
char *SYSCALL_PREFIX(getcwd)(char *buf, int len)
{
return SYSCALL_PREFIX(_getcwd)(buf, len);
}
#endif
#if __MW__
#pragma weak open = _open
#pragma weak close = _close
#pragma weak read = _read
#pragma weak write = _write
#pragma weak lseek = _lseek
#pragma weak unlink = _unlink
#pragma weak getcwd = _getcwd
#pragma weak isatty = _isatty
#pragma weak getpid = _getpid
#pragma weak access = _access
#pragma weak remove = _unlink
#pragma weak fstat = _fstat
#pragma weak stat = _stat
#pragma weak lstat = _lstat
#endif
////////////////////////////////////////
// Process Management //
// \todo Integrate with FreeRTOS task //
////////////////////////////////////////
/** Determining a Process ID */
int SYSCALL_PREFIX(_getpid)(void)
{
return 0;
}
extern void __attribute__((noreturn)) _exit_loop();
/** Terminating a Process */
void __attribute__((noreturn)) SYSCALL_PREFIX(_exit)(int status)
{
_exit_loop(status);
}
/** Kill a process */
int SYSCALL_PREFIX(_kill)(int pid, int sig)
{
SYSCALL_PREFIX(_exit)(sig);
return -1;
}
////////////////////////////////
// Environment Identification //
// No environment provided //
////////////////////////////////
static char *settings[_ENVIRON_LEN] =
{
/*
* Add your default environment here:
*/
"PLATFORM=EMBARC",
"POSIXLY_CORRECT",
0
};
char **_environ = settings;
/** Getting Environment Variables */
char *SYSCALL_PREFIX(_getenv)(const char *var)
{
int len;
char **ep;
if (!(ep = _environ)) { return NULL; }
len = strlen(var);
while (*ep)
{
if (memcmp(var, *ep, len) == 0 && (*ep)[len] == '=')
{
return *ep + len + 1;
}
ep++;
}
return NULL;
}
/** Putting Environment Variable */
int SYSCALL_PREFIX(_putenv)(char *string)
{
int i, len;
for (len = 0; string[len] && string[len] != '='; len++);
for (i = 0; i < _ENVIRON_LEN; i++)
{
if (_environ[i] != 0 && strncmp(string, _environ[i], len) == 0)
{
_environ[i] = string;
return 0;
}
}
for (i = 0; i < _ENVIRON_LEN; i++)
{
if (_environ[i] == NULL)
{
_environ[i] = string;
if ((i + 1) < _ENVIRON_LEN)
{
_environ[i + 1] = NULL;
}
return 0;
}
}
return -1;
}
#if __MW__
char *_mwgetenv2(const char *var)
{
return SYSCALL_PREFIX(_getenv)(var);
}
#pragma weak getenv = _getenv
#pragma weak putenv = _putenv
#endif
//////////////////////
// Time Calculation //
//////////////////////
/** Determining the Clock Value */
clock_t SYSCALL_PREFIX(clock)(void)
{
uint64_t total_ticks;
total_ticks = (uint64_t)OSP_GET_CUR_HWTICKS();
return (clock_t)((uint64_t)(total_ticks * CLOCKS_PER_SEC) / BOARD_CPU_CLOCK);
}
#if !EMBARC_CFG_USE_RTCTIME
static int buildtime_ready = 0;
static struct tm build_tm;
static time_t build_time;
#endif
/** Determining the Time Value */
time_t SYSCALL_PREFIX(_time)(time_t *timer)
{
if (!buildtime_ready)
{
build_time = get_build_timedate(&build_tm);
buildtime_ready = 1;
}
time_t cur_time;
cur_time = build_time + (OSP_GET_CUR_MS() / 1000);
if (timer)
{
*timer = cur_time;
}
return cur_time;
}
#ifdef __GNU__
int SYSCALL_PREFIX(_times)(struct tms *buf)
{
if (!buf) { return -1; }
buf->tms_utime = OSP_GET_CUR_MS() / 1000;
buf->tms_stime = 0;
buf->tms_cutime = 0;
buf->tms_cstime = 0;
return 0;
}
#endif
int SYSCALL_PREFIX(_gettimeofday)(struct timeval *tv, void *tz)
{
if (tv == NULL) { return -1; }
if (!buildtime_ready)
{
build_time = get_build_timedate(&build_tm);
buildtime_ready = 1;
}
tv->tv_sec = build_time + (OSP_GET_CUR_US() / 1000000);
tv->tv_usec = OSP_GET_CUR_US() % 1000000;
return 0;
}
#if __MW__
#pragma weak time = _time
#pragma weak gettimeofday = _gettimeofday
#endif
/////////////////////////
// Argument Processing //
/////////////////////////
/** Counting Arguments */
int SYSCALL_PREFIX(__argc)(void)
{
return 0;
}
/** Finding the Value of an Argument */
char *SYSCALL_PREFIX(__argv)(int num)
{
return NULL;
}
#endif /* !defined(_HOSTLINK_) */
#if defined(__GNU__) && !defined(_HAVE_LIBGLOSS_) && !defined(_HOSTLINK_)
/* Support newlibc for newlibc(<= ARC GNU 2015.06) */
typedef struct exc_frame
{
uint32_t erbta;
uint32_t eret;
uint32_t erstatus;
uint32_t lpcount;
uint32_t lpend;
uint32_t lpstart;
uint32_t blink;
uint32_t r30;
uint32_t ilink;
uint32_t fp;
uint32_t gp;
uint32_t r12;
uint32_t r11;
uint32_t r10;
uint32_t r9;
uint32_t r8;
uint32_t r7;
uint32_t r6;
uint32_t r5;
uint32_t r4;
uint32_t r3;
uint32_t r2;
uint32_t r1;
uint32_t r0;
} EXC_FRAME;
/**
* \brief syscall functions
* partly implemented for gnu in swi exception handler
* \param ptr exception handle stack pointer
*/
void syscall_swi(void *ptr)
{
EXC_FRAME *swi_frame = (EXC_FRAME *)ptr;
switch (swi_frame->r8)
{
case SYS_read:
swi_frame->r0 = SYSCALL_PREFIX(_read)((int)(swi_frame->r0), (char *)(swi_frame->r1), (int)(swi_frame->r2));
break;
case SYS_write:
swi_frame->r0 = SYSCALL_PREFIX(_write)((int)(swi_frame->r0), (char *)(swi_frame->r1), (int)(swi_frame->r2));
break;
case SYS_exit:
SYSCALL_PREFIX(_exit)(1);
break;
case SYS_open:
swi_frame->r0 = SYSCALL_PREFIX(_open)((const char *)(swi_frame->r0), (int)(swi_frame->r1), (int)(swi_frame->r2));
break;
case SYS_close:
swi_frame->r0 = SYSCALL_PREFIX(_close)((int)(swi_frame->r0));
break;
case SYS_lseek:
swi_frame->r0 = SYSCALL_PREFIX(_lseek)((int)(swi_frame->r0), (long)(swi_frame->r1), (int)(swi_frame->r2));
break;
case SYS_fstat:
swi_frame->r0 = SYSCALL_PREFIX(_fstat)((int)(swi_frame->r0), (struct stat *)(swi_frame->r1));
break;
case SYS_unlink:
swi_frame->r0 = SYSCALL_PREFIX(_unlink)((const char *)(swi_frame->r0));
break;
case SYS_time:
swi_frame->r0 = SYSCALL_PREFIX(_time)((time_t *)(swi_frame->r0));
break;
case SYS_gettimeofday:
swi_frame->r0 = SYSCALL_PREFIX(_gettimeofday)((struct timeval *)(swi_frame->r0), (void *)(swi_frame->r1));
break;
case SYS_access:
swi_frame->r0 = SYSCALL_PREFIX(_access)((const char *)(swi_frame->r0), (int)(swi_frame->r1));
break;
case SYS_kill:
swi_frame->r0 = SYSCALL_PREFIX(_kill)((int)(swi_frame->r0), (int)(swi_frame->r1));
break;
case SYS_rename:
swi_frame->r0 = SYSCALL_PREFIX(rename)((const char *)(swi_frame->r0), (const char *)(swi_frame->r1));
break;
case SYS_times:
swi_frame->r0 = SYSCALL_PREFIX(_times)((struct tms *)(swi_frame->r0));
break;
case SYS_getcwd:
swi_frame->r0 = (uint32_t)SYSCALL_PREFIX(_getcwd)((char *)(swi_frame->r0), (int)(swi_frame->r1));
break;
default:
swi_frame->r0 = -1;
break;
}
swi_frame->eret = swi_frame->eret + 4;
}
#endif /* __GNU__ && !_HAVE_LIBGLOSS_ && _HOSTLINK_ */