/*
 * libspe - A wrapper library to adapt the JSRE SPU usage model to SPUFS
 * Copyright (C) 2005 IBM Corp.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version.
 *
 *  This library 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 Lesser General Public
 *  License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software Foundation,
 *   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


#include <default_syscall_handler.h>

#define __PRINTF(fmt, args...) { fprintf(stderr,fmt , ## args); }
#ifdef DEBUG
#define DEBUG_PRINTF(fmt, args...) __PRINTF(fmt , ## args)
#else
#define DEBUG_PRINTF(fmt, args...)
#endif

int
default_syscall_handler_write (unsigned char *base, syscall_write_t * ps)
{
	void *buf;

	buf = base + ps->ptr;

	DEBUG_PRINTF ("default_syscall_handler: write( %d, %016lx, %d) \n",
		      ps->fd, (unsigned long) buf, ps->len);
	ps->rc = write (ps->fd, buf, ps->len);

	ps->err = errno;
	return (0);
}

int
default_syscall_handler_read (unsigned char *base, syscall_read_t * ps)
{
	void *buf;

	buf = base + ps->ptr;

	DEBUG_PRINTF ("default_syscall_handler: read( %d, %016lx, %d) \n",
		      ps->fd, (unsigned long) buf, ps->len);
	ps->rc = read (ps->fd, buf, ps->len);

	ps->err = errno;
	return (0);
}

int
default_syscall_handler_open (unsigned char *base, syscall_open_t * ps)
{
	char *buf;
	int flags;

	buf = (char *) base + ps->pathname;

	DEBUG_PRINTF ("default_syscall_handler: open flags=%08x \n",
		      ps->flags);
	flags = O_RDONLY;

	/* TODO: map all internal/newlib flags to the host flags */
	if (ps->flags & 0x2)
	  {			/* newlib O_RDWR */
		  DEBUG_PRINTF ("default_syscall_handler: O_RDWR\n");
		  flags |= O_RDWR;
	  }
	if (ps->flags & 0x8)
	  {			/* newlib O_APPEND */
		  flags |= O_APPEND;
	  }
	if (ps->flags & 0x200)
	  {			/* newlib O_CREAT */
		  DEBUG_PRINTF ("default_syscall_handler: O_CREAT\n");
		  flags |= O_CREAT;
	  }
	if (ps->flags & 0x400)
	  {			/* newlib O_TRUNC */
		  DEBUG_PRINTF ("default_syscall_handler: O_TRUNC\n");
		  flags |= O_TRUNC;
	  }
	if (ps->flags & 0x800)
	  {			/* newlib O_EXCL */
		  flags |= O_EXCL;
	  }
	if (ps->flags & 0x2000)
	  {			/* newlib O_SYNC */
		  DEBUG_PRINTF ("default_syscall_handler: O_SYNC\n");
		  flags |= O_SYNC;
	  }
	if (ps->flags & 0x4000)
	  {			/* newlib O_NOBLOCK */
		  //flags |= O_NOBLOCK;
	  }
	if (ps->flags & 0x8000)
	  {			/* newlib O_NOCTTY */
		  flags |= O_NOCTTY;
	  }

	DEBUG_PRINTF ("default_syscall_handler: open( \"%s\", %x, %x) \n",
		      buf, flags, ps->mode);
	ps->rc = open (buf, flags, ps->mode);

	ps->err = errno;


#ifdef DEBUG
	if (ps->rc == -1)
	  {
		  DEBUG_PRINTF ("\trc=%d, errno=%d -> %s\n", ps->rc, ps->err,
				strerror (errno));
	  }
#endif
	return (0);
}

int
default_syscall_handler_close (unsigned char *base, syscall_close_t * ps)
{
	ps->rc = close (ps->fd);
	ps->err = errno;
	return (0);
}

int
default_syscall_handler_lseek (unsigned char *base, syscall_lseek_t * ps)
{
	DEBUG_PRINTF ("default_syscall_handler: lseek( %ld, %ld, %d) \n",
		      ps->fd, ps->offset, ps->whence);
	ps->rc = lseek (ps->fd, ps->offset, ps->whence);
	ps->err = errno;
	return (0);
}

int
default_syscall_handler_fstat (unsigned char *base, syscall_fstat_t * ps)
{
	struct stat buf;
	void *spebuf;

	spebuf = base + (unsigned long) (ps->buf);

	ps->rc = fstat (ps->fd, &buf);
	ps->err = errno;

	//memcpy (spebuf, &buf, sizeof (struct stat));
	return (0);
}

int
default_syscall_handler_unlink (unsigned char *base, syscall_unlink_t * ps)
{
	char *buf;
	buf = (char *) base + (unsigned long) (ps->pathname);
	ps->rc = unlink (buf);
	ps->err = errno;
	return (0);
}


int
default_syscall_handler (unsigned long *base, unsigned long args)
{
	syscall_t *ps;

	DEBUG_PRINTF ("default_syscall_handler: base %016lx, args %08lx\n",
		      (unsigned long) base, args);

	if (!base)
	  {
		  printf ("No MMAPed spe not supported yet.\n");
		  return (-1);
	  }
	ps = (syscall_t *) ((unsigned char *) base + args);

	switch (ps->no)
	  {
	  case SYS_WRITE:
		  default_syscall_handler_write ((unsigned char *) base,
						 (syscall_write_t *) ps);
		  break;
	  case SYS_READ:
		  default_syscall_handler_read ((unsigned char *) base,
						(syscall_read_t *) ps);
		  break;
	  case SYS_OPEN:
		  default_syscall_handler_open ((unsigned char *) base,
						(syscall_open_t *) ps);
		  break;
	  case SYS_CLOSE:
		  default_syscall_handler_close ((unsigned char *) base,
						 (syscall_close_t *) ps);
		  break;
	  case SYS_SEEK:
		  default_syscall_handler_lseek ((unsigned char *) base,
						 (syscall_lseek_t *) ps);
		  break;
	  case SYS_FSTAT:
		  default_syscall_handler_fstat ((unsigned char *) base,
						 (syscall_fstat_t *) ps);
		  break;
	  case SYS_UNLINK:
		  default_syscall_handler_unlink ((unsigned char *) base,
						  (syscall_unlink_t *) ps);
		  break;
	  default:		/* we don't know how to handle this */
		  DEBUG_PRINTF
			  ("default_syscall_handler: Unhandled type %08x\n",
			   ps->no);
		  return (1);	/* give the next handler a chance */
	  }
	DEBUG_PRINTF ("default_syscall_handler: DONE -> rc=%d\n", ps->rc);
	return (0);		/* we handled it, no further processing needed */
}
