/****************************************************************************
 * arch/xtensa/src/common/xtensa_hostfs.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <nuttx/cache.h>
#include <nuttx/fs/hostfs.h>

#include <arch/simcall.h>

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <syscall.h>
#include <unistd.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static int host_call(int nr, int param1, int param2, int param3)
{
  int ret;
  int err;

  ret = simcall(nr, param1, param2, param3, &err);
  if (ret < 0)
    {
      ret = -err;
    }

  return ret;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

int host_open(const char *pathname, int flags, int mode)
{
  int simcall_flags = 0;

  switch ((flags & O_ACCMODE))
    {
      case O_RDONLY:
        simcall_flags = SIMCALL_O_RDONLY;
        break;
      case O_WRONLY:
        simcall_flags = SIMCALL_O_WRONLY;
        break;
      case O_RDWR:
        simcall_flags = SIMCALL_O_RDWR;
        break;
    }

  if ((flags & O_APPEND) != 0)
    {
      simcall_flags |= SIMCALL_O_APPEND;
    }

  if ((flags & O_TRUNC) != 0)
    {
      simcall_flags |= SIMCALL_O_TRUNC;
    }

  if ((flags & O_CREAT) != 0)
    {
      simcall_flags |= SIMCALL_O_CREAT;
    }

  if ((flags & O_EXCL) != 0)
    {
      simcall_flags |= SIMCALL_O_EXCL;
    }

  if ((flags & O_NONBLOCK) != 0)
    {
      simcall_flags |= SIMCALL_O_NONBLOCK;
    }

#ifdef CONFIG_XTENSA_SEMIHOSTING_HOSTFS_CACHE_COHERENCE
  up_clean_dcache(pathname, pathname + strlen(pathname) + 1);
#endif
  return host_call(SIMCALL_SYS_OPEN, (int)pathname, simcall_flags, mode);
}

int host_close(int fd)
{
  return host_call(SIMCALL_SYS_CLOSE, fd, 0, 0);
}

ssize_t host_read(int fd, void *buf, size_t count)
{
#ifdef CONFIG_XTENSA_SEMIHOSTING_HOSTFS_CACHE_COHERENCE
  up_invalidate_dcache(buf, buf + count);
#endif

  return host_call(SIMCALL_SYS_READ, fd, (int)buf, count);
}

ssize_t host_write(int fd, const void *buf, size_t count)
{
#ifdef CONFIG_XTENSA_SEMIHOSTING_HOSTFS_CACHE_COHERENCE
  up_clean_dcache(buf, buf + count);
#endif

  return host_call(SIMCALL_SYS_WRITE, fd, (int)buf, count);
}

off_t host_lseek(int fd, off_t pos, off_t offset, int whence)
{
  return host_call(SIMCALL_SYS_LSEEK, fd, offset, whence);
}

int host_ioctl(int fd, int request, unsigned long arg)
{
  return -ENOSYS;
}

void host_sync(int fd)
{
}

int host_dup(int fd)
{
  return -ENOSYS;
}

int host_fstat(int fd, struct stat *buf)
{
  /* Determine the size using lseek.
   *
   * Assumptions:
   *  - host_lseek never fails
   *  - It's ok to change the file offset temporarily as
   *    hostfs_lock provides enough serialization.
   */

  off_t saved_off = host_lseek(fd, 0, 0, SEEK_CUR);
  off_t size = host_lseek(fd, 0, 0, SEEK_END);
  host_lseek(fd, 0, saved_off, SEEK_SET);

  memset(buf, 0, sizeof(*buf));
  buf->st_mode = S_IFREG | 0777;
  buf->st_size = size;
  return 0;
}

int host_fchstat(int fd, const struct stat *buf, int flags)
{
  return -ENOSYS;
}

int host_ftruncate(int fd, off_t length)
{
  return -ENOSYS;
}

void *host_opendir(const char *name)
{
  return NULL;
}

int host_readdir(void *dirp, struct dirent *entry)
{
  return -ENOSYS;
}

void host_rewinddir(void *dirp)
{
}

int host_closedir(void *dirp)
{
  return -ENOSYS;
}

int host_statfs(const char *path, struct statfs *buf)
{
  return 0;
}

int host_unlink(const char *pathname)
{
  return -ENOSYS;
}

int host_mkdir(const char *pathname, int mode)
{
  return -ENOSYS;
}

int host_rmdir(const char *pathname)
{
  return host_unlink(pathname);
}

int host_rename(const char *oldpath, const char *newpath)
{
  return -ENOSYS;
}

int host_stat(const char *path, struct stat *buf)
{
  int ret = host_open(path, O_RDONLY, 0);
  if (ret >= 0)
    {
      int fd = ret;
      ret = host_fstat(fd, buf);
      host_close(fd);
    }

  if (ret < 0)
    {
      /* Since semihosting doesn't support directory yet,
       * we have to assume it's a directory here.
       */

      ret = 0;
      memset(buf, 0, sizeof(*buf));
      buf->st_mode = S_IFDIR | 0777;
    }

  return ret;
}

int host_chstat(const char *path, const struct stat *buf, int flags)
{
  return -ENOSYS;
}

#ifdef CONFIG_FS_LINKS
int host_link(const char *path1, const char *path2)
{
  return -ENOSYS;
}

int host_symlink(const char *target, const char *linkpath)
{
  return -ENOSYS;
}

ssize_t host_readlink(const char *path, char *buf, size_t bufsize)
{
  return -ENOSYS;
}

int host_lstat(const char *path, struct stat *buf)
{
  return -ENOSYS;
}
#endif /* CONFIG_FS_LINKS */