/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * 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.
    * Neither the name of the copyright holder 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.
*/
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <assert.h>

#include "vchost.h"

#include "interface/vcos/vcos.h"
#include "interface/vchi/vchi.h"
#include "interface/vchi/common/endian.h"

#include "vc_vchi_filesys.h"
#include "interface/vmcs_host/vc_vchi_fileservice_defs.h"

/******************************************************************************
Global data.
******************************************************************************/

/******************************************************************************
Local types and defines.
******************************************************************************/
typedef enum {
   VC_SECTOR_IO_NONE,
   VC_SECTOR_IO_READING,
   VC_SECTOR_IO_WRITING
} VC_SECTOR_IO_T;

typedef struct {

   VCHI_SERVICE_HANDLE_T open_handle;

   int32_t               num_connections;

   //Host->2727
   FILESERV_MSG_T        fileserv_msg;

   //2727->Host   XXX
   FILESERV_MSG_T        vc_msg;

   VCOS_THREAD_T         filesys_thread;

   // used to signal response has arrived
   VCOS_EVENT_T          response_event;

   //we lock each vc_filesys function call
   VCOS_MUTEX_T          filesys_lock;

   //used to signal msg arrivals
   VCOS_EVENT_T          filesys_msg_avail;

   // Outstanding transaction's ID
   volatile uint32_t     cur_xid;

   // Copy of the header code from responses
   int32_t      resp_code;
   int32_t      err_no;

   char        *bulk_buffer;
   int32_t      initialised;

} FILESYS_SERVICE_T;

static FILESYS_SERVICE_T vc_filesys_client;

/******************************************************************************
Static functions.
******************************************************************************/

//Lock the host state
static __inline int32_t lock_obtain (void) {
   int ret = -1;
   if(vc_filesys_client.initialised && vcos_mutex_lock(&vc_filesys_client.filesys_lock) == VCOS_SUCCESS) {
      vchi_service_use(vc_filesys_client.open_handle);
      ret = 0;
   }
   return ret;
}

//Unlock the host state
static __inline void lock_release (void) {
   vcos_assert(vc_filesys_client.initialised);
   vchi_service_release(vc_filesys_client.open_handle);
   vcos_mutex_unlock(&vc_filesys_client.filesys_lock);
}

// File Service VCHI functions

static int vchi_msg_stub(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len );

static int vchi_msg_stub_noblock(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len);

static int vc_fs_message_handler( FILESERV_MSG_T* msg, uint32_t nbytes );

static void *filesys_task_func(void *arg);

static void filesys_callback( void *callback_param, VCHI_CALLBACK_REASON_T reason, void *msg_handle );

#ifdef PRINTF
#ifdef WIN32
#define printf tprintf
#endif
static void showmsg(VC_MSGFIFO_CMD_HEADER_T const * head,
                    struct file_service_msg_body const * body);
#endif
static int fs_host_direntbytestream_create(struct dirent *d, void *buffer);
static void fs_host_direntbytestream_interp(struct dirent *d, void *buffer);

/*---------------------------------------------------------------------------*/

/******************************************************************************
NAME
   vc_filesys_init

SYNOPSIS
   vc_filesys_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) {

FUNCTION
   Initialise the file system for use. A negative return value
   indicates failure (which may mean it has not been started on VideoCore).

RETURNS
   int
******************************************************************************/
int vc_vchi_filesys_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections )
{
   int32_t success = 0;
   SERVICE_CREATION_T filesys_parameters;
   VCOS_THREAD_ATTR_T attrs;
   VCOS_STATUS_T status;

   // record the number of connections
   memset( &vc_filesys_client, 0, sizeof(FILESYS_SERVICE_T) );
   vc_filesys_client.num_connections = num_connections;

   if(!vcos_verify(vc_filesys_client.num_connections < 2))
      return -1;

   status = vcos_mutex_create(&vc_filesys_client.filesys_lock, "HFilesys");
   vcos_assert(status == VCOS_SUCCESS);

   status = vcos_event_create(&vc_filesys_client.filesys_msg_avail, "HFilesys");
   vcos_assert(status == VCOS_SUCCESS);

   //create sema used to signal cmd response has arrived
   status = vcos_event_create(&vc_filesys_client.response_event, "HFilesys");
   vcos_assert(status == VCOS_SUCCESS);

   vc_filesys_client.bulk_buffer = vcos_malloc_aligned(FILESERV_MAX_BULK, 16, "HFilesys bulk_recv");
   vc_filesys_client.cur_xid = 0;

   memset(&filesys_parameters, 0, sizeof(filesys_parameters));
   filesys_parameters.service_id = FILESERV_4CC;   // 4cc service code
   filesys_parameters.connection = connections[0]; // passed in fn ptrs
   filesys_parameters.rx_fifo_size = 0;            // rx fifo size (unused)
   filesys_parameters.tx_fifo_size = 0;            // tx fifo size (unused)
   filesys_parameters.callback = &filesys_callback;
   filesys_parameters.callback_param = &vc_filesys_client.filesys_msg_avail;
   filesys_parameters.want_unaligned_bulk_rx = 0;
   filesys_parameters.want_unaligned_bulk_tx = 0;
   filesys_parameters.want_crc = 0;
   filesys_parameters.version.version = VC_FILESERV_VER;
   filesys_parameters.version.version_min = VC_FILESERV_VER;

   success = vchi_service_open( initialise_instance, &filesys_parameters, &vc_filesys_client.open_handle );
   vcos_assert( success == 0 );

   vcos_thread_attr_init(&attrs);
   vcos_thread_attr_setstacksize(&attrs, 4000);
   vcos_thread_attr_settimeslice(&attrs, 1);

   vc_filesys_client.initialised = 1;

   status = vcos_thread_create(&vc_filesys_client.filesys_thread, "HFilesys", &attrs, filesys_task_func, NULL);
   vcos_assert(status == VCOS_SUCCESS);

   /* Not using service immediately - release videocore */
   vchi_service_release(vc_filesys_client.open_handle);

   return (int)success;
}

static void *filesys_task_func(void *arg)
{
   int32_t success;
   uint32_t msg_len;

   (void)arg;

   vc_hostfs_init();

   while(1) {
      // wait for the semaphore to say that there is a message
      if (vcos_event_wait(&vc_filesys_client.filesys_msg_avail) != VCOS_SUCCESS || vc_filesys_client.initialised == 0)
         break;

      vchi_service_use(vc_filesys_client.open_handle);
      // read the message - should we really "peek" this
      while (1) {
         success = vchi_msg_dequeue(vc_filesys_client.open_handle, &vc_filesys_client.vc_msg,
                                    sizeof(vc_filesys_client.vc_msg), &msg_len, VCHI_FLAGS_NONE);
         if (!success)
            break;

         /* coverity[tainted_string_argument] */
         success = (int32_t) vc_fs_message_handler(&vc_filesys_client.vc_msg, msg_len);
         (void)success;
      }
      vchi_service_release(vc_filesys_client.open_handle);
   }

   return 0;
}

/******************************************************************************
NAME
   filesys_callback

SYNOPSIS
   void filesys_callback( void *callback_param,
                     const VCHI_CALLBACK_REASON_T reason,
                     const void *msg_handle )

FUNCTION
   VCHI callback

RETURNS
   int
******************************************************************************/
static void filesys_callback( void *callback_param,
                             const VCHI_CALLBACK_REASON_T reason,
                             void *msg_handle )
{
   (void)msg_handle;

   switch( reason ) {

   case VCHI_CALLBACK_MSG_AVAILABLE:
      {
         VCOS_EVENT_T *event = (VCOS_EVENT_T *) callback_param;
         if(event)
            vcos_event_signal(event);
      }
      break;

   case VCHI_CALLBACK_BULK_RECEIVED:
      break;
   case VCHI_CALLBACK_BULK_SENT:
      break;

   default:
      return;
   }
}

/******************************************************************************
NAME
   vc_filesys_stop

SYNOPSIS
   void vc_filesys_stop()

FUNCTION
   This tells us that the file system service has stopped, thereby preventing
   any of the functions from doing anything.

RETURNS
   void
******************************************************************************/

void vc_filesys_stop ()
{
   int32_t result;
   void *dummy;

   if(lock_obtain() != 0)
      return;

   result = vchi_service_close(vc_filesys_client.open_handle);
   vcos_assert(result == 0);

   vc_filesys_client.initialised = 0;

   vcos_event_signal(&vc_filesys_client.filesys_msg_avail);
   vcos_thread_join(&vc_filesys_client.filesys_thread, &dummy);

   vcos_event_delete(&vc_filesys_client.filesys_msg_avail);
   vcos_event_delete(&vc_filesys_client.response_event);
   vcos_mutex_delete(&vc_filesys_client.filesys_lock);

   if(vc_filesys_client.bulk_buffer)
      vcos_free(vc_filesys_client.bulk_buffer);
}

/******************************************************************************
NAME
   vc_filesys_single_param

SYNOPSIS
   int vc_filesys_single_param(uint32_t param, uint32_t fn)

FUNCTION
   Utility function for implementing filesys methods

RETURNS
   void
******************************************************************************/
static int vc_filesys_single_param(uint32_t param, uint32_t fn)
{
   int success = -1;

   if(lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = param;
      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, fn, 4) == FILESERV_RESP_OK)
         success = 0;

      lock_release();
   }

   return success;
}

/******************************************************************************
NAME
   vc_filesys_single_string

SYNOPSIS
   int vc_filesys_single_string(uint32_t param, const char *str, uint32_t fn, int return_param)

FUNCTION
   Utility function for implementing filesys methods

RETURNS
   void
******************************************************************************/
static int vc_filesys_single_string(uint32_t param, const char *str, uint32_t fn, int return_param)
{
   int ret = -1;
   int len = strlen(str);

   if(len < FILESERV_MAX_DATA && lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = param;
      /* coverity[buffer_size_warning] - the length of str has already been checked */
      strncpy((char*)vc_filesys_client.fileserv_msg.data, str, FILESERV_MAX_DATA);

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, fn, len+1+16) == FILESERV_RESP_OK)
      {
         if(return_param)
            ret = (int) vc_filesys_client.fileserv_msg.params[0];
         else
            ret = 0;
      }

      lock_release();
   }

   return ret;
}

/* Standard UNIX low-level library functions (declared in unistd.h) */
/******************************************************************************
NAME
   vc_filesys_close

SYNOPSIS
   int vc_filesys_close(int fildes)

FUNCTION
   Deallocates the file descriptor to a file.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_close(int fildes)
{
   return vc_filesys_single_param((uint32_t) fildes, VC_FILESYS_CLOSE);
}

/******************************************************************************
NAME
   vc_filesys_lseek

SYNOPSIS
   long vc_filesys_lseek(int fildes, long offset, int whence)

FUNCTION
   Sets the file pointer associated with the open file specified by fildes.

RETURNS
   Successful completion: offset
   Otherwise: -1
******************************************************************************/

long vc_filesys_lseek(int fildes, long offset, int whence)
{
   long set = -1;

   if(lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = (uint32_t) fildes;
      vc_filesys_client.fileserv_msg.params[1] = (uint32_t) offset;
      vc_filesys_client.fileserv_msg.params[2] = (uint32_t) whence;

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_LSEEK, 12) == FILESERV_RESP_OK)
         set = (long) vc_filesys_client.fileserv_msg.params[0];

      lock_release();
   }

   return set;
}

/******************************************************************************
NAME
   vc_filesys_lseek64

SYNOPSIS
   int64_t vc_filesys_lseek64(int fildes, int64_t offset, int whence)

FUNCTION
   Sets the file pointer associated with the open file specified by fildes.

RETURNS
   Successful completion: file pointer value
   Otherwise: -1
******************************************************************************/

int64_t vc_filesys_lseek64(int fildes, int64_t offset, int whence)
{
   int64_t set = -1;

   if(lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = (uint32_t) fildes;
      vc_filesys_client.fileserv_msg.params[1] = (uint32_t) offset;        // LSB
      vc_filesys_client.fileserv_msg.params[2] = (uint32_t)(offset >> 32); // MSB
      vc_filesys_client.fileserv_msg.params[3] = (uint32_t) whence;

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_LSEEK64, 16) == FILESERV_RESP_OK)
      {
         set = vc_filesys_client.fileserv_msg.params[0];
         set += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32;
      }

      lock_release();
   }

   return set;
}

/******************************************************************************
NAME
   vc_filesys_mount

SYNOPSIS
   int vc_filesys_mount(const char *device, const char *mountpoint, const char *options)

FUNCTION
   Mounts a filesystem at a given location

RETURNS
   Successful completion: 0
******************************************************************************/

int vc_filesys_mount(const char *device, const char *mountpoint, const char *options)
{
   int set = -1, len;
   int a = strlen(device);
   int b = strlen(mountpoint);
   int c = strlen(options);

   if(a + b + c + 3 < FILESERV_MAX_DATA && lock_obtain() == 0)
   {
      char *str = (char *) vc_filesys_client.fileserv_msg.data;

      memcpy(str, device, a);
      str[a] = 0;
      memcpy(str+a+1, mountpoint, b);
      str[a+1+b] = 0;
      memcpy(str+a+b+2, options, c);
      str[a+b+c+2] = 0;
      len = a + b + c + 3 + (int)(((FILESERV_MSG_T *)0)->data);
      len = ((len + (VCHI_BULK_GRANULARITY-1)) & ~(VCHI_BULK_GRANULARITY-1)) + VCHI_BULK_GRANULARITY;

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_MOUNT, len) == FILESERV_RESP_OK)
         set = (int) vc_filesys_client.fileserv_msg.params[0];

      lock_release();
   }

   return set;
}

/******************************************************************************
NAME
   vc_filesys_umount

SYNOPSIS
   int vc_filesys_mount(const char *mountpoint)

FUNCTION
   Un-mounts a removable device from the location that it has been mounted
   to earlier in the session

RETURNS
   Successful completion: 0
******************************************************************************/

int vc_filesys_umount(const char *mountpoint)
{
   return vc_filesys_single_string(0, mountpoint, VC_FILESYS_UMOUNT, 1);
}

/******************************************************************************
NAME
   vc_filesys_open

SYNOPSIS
   int vc_filesys_open(const char *path, int vc_oflag)

FUNCTION
   Establishes a connection between a file and a file descriptor.

RETURNS
   Successful completion: file descriptor
   Otherwise: -1
******************************************************************************/

int vc_filesys_open(const char *path, int vc_oflag)
{
   return vc_filesys_single_string((uint32_t) vc_oflag, path, VC_FILESYS_OPEN, 1);
}

/******************************************************************************
NAME
   vc_filesys_read

SYNOPSIS
   int vc_filesys_read(int fildes, void *buf, unsigned int nbyte)

FUNCTION
   Attempts to read nbyte bytes from the file associated with the file
   descriptor, fildes, into the buffer pointed to by buf.

RETURNS
   Successful completion: number of bytes read
   Otherwise: -1
******************************************************************************/

/******************************************************************************

FUNCTION

RETURNS
   Successful completion: the number of bytes received
   Otherwise  negative error code
******************************************************************************/
static int vc_vchi_msg_bulk_read(FILESERV_MSG_T* msg, uint16_t cmd_id, uint32_t transfer_len, uint8_t* recv_addr )
{
   uint32_t i;
   int msg_len;
   uint32_t host_align_bytes;
   uint32_t num_bytes_read;
   int32_t success;

   //this is current file_io_buffer size so assuming never more than this
   //otherwise we will split the read into chunks
   if(!vcos_verify(transfer_len <= FILESERV_MAX_BULK))
      return -1;

   //number of bytes required to align recv_addr
   host_align_bytes = VCHI_BULK_ALIGN_NBYTES(recv_addr);

   i = vc_filesys_client.cur_xid + 1;
   i &= 0x7fffffffUL;
   vc_filesys_client.cur_xid = i;

   msg->xid = vc_filesys_client.cur_xid;

   //fill in cmd id: VC_FILESYS_READ etc
   msg->cmd_code = cmd_id;

   msg->params[2] = transfer_len;

   msg->params[3] = host_align_bytes;

   //24 comes from the static size of FILESERV_MSG_T
   msg_len = 24;

   if(vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ) != 0)
      return -1;

   //wait to receive response
   if(vcos_event_wait(&vc_filesys_client.response_event) != VCOS_SUCCESS || msg->cmd_code == FILESERV_RESP_ERROR)
      return -1;

   //total bytes read
   num_bytes_read = msg->params[0];

   if(!vcos_verify(num_bytes_read <= FILESERV_MAX_BULK))
      return -1;

   //everything is in msg->data
   if(msg->cmd_code == FILESERV_RESP_OK) {
      if(!vcos_verify(num_bytes_read <= FILESERV_MAX_DATA))
         return -1;

      memcpy(recv_addr, msg->data, num_bytes_read);
      return (int) num_bytes_read;
   }

// Make this code conditional to stop Coverity complaining about dead code
#if VCHI_BULK_ALIGN > 1
   //copy host_align_bytes bytes to recv_addr, now ready for bulk
   if(host_align_bytes) {
      memcpy(recv_addr, msg->data, host_align_bytes);
      recv_addr += host_align_bytes;
      transfer_len -= host_align_bytes;
   }
#endif

   //receive bulk from host
   if(msg->cmd_code == FILESERV_BULK_WRITE){
      //number of end bytes
      uint32_t end_bytes = msg->params[1];
      //calculate what portion of read transfer by bulk
      uint32_t bulk_bytes = (num_bytes_read-host_align_bytes-end_bytes);

      success = vchi_bulk_queue_receive(vc_filesys_client.open_handle,
                              recv_addr,
                              bulk_bytes,
                              VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE,
                              NULL );
      if(!vcos_verify(success == 0))
         return -1;

      recv_addr+=bulk_bytes;

      //copy any left over end bytes from original msg
      if(end_bytes)
         memcpy(recv_addr, &msg->data[host_align_bytes], end_bytes);
   }
   else {
      return -1;
   }

   return (int) num_bytes_read;
}

int vc_filesys_read(int fildes, void *buf, unsigned int nbyte)
{
   int bulk_bytes = 0;
   int actual_read = 0;
   int total_byte = 0;
   uint8_t* ptr = (uint8_t*)buf;

   if (nbyte == 0) {
      return 0;
   }

   if(lock_obtain() == 0)
   {
      do {
         bulk_bytes = nbyte > FILESERV_MAX_BULK ? FILESERV_MAX_BULK : nbyte;

         //we overwrite the response here so fill in data again
         vc_filesys_client.fileserv_msg.params[0] = (uint32_t)fildes;
         vc_filesys_client.fileserv_msg.params[1] = 0xffffffffU;        // offset: use -1 to indicate no offset

         actual_read = vc_vchi_msg_bulk_read(&vc_filesys_client.fileserv_msg , VC_FILESYS_READ, (uint32_t)bulk_bytes, (uint8_t*)ptr);

         if(bulk_bytes != actual_read) {
            if(actual_read < 0)
               total_byte = -1;
            else
               total_byte += actual_read;
            //exit loop
            break;
         }

         ptr+=bulk_bytes;
         nbyte -= actual_read;
         total_byte += actual_read;
      }while(nbyte > 0);

      lock_release();
   }

   return total_byte;
}

/******************************************************************************
NAME
   vc_filesys_write

SYNOPSIS
   int vc_filesys_write(int fildes, const void *buf, unsigned int nbyte)

FUNCTION
   Attempts to write nbyte bytes from the buffer pointed to by buf to file
   associated with the file descriptor, fildes.

RETURNS
   Successful completion: number of bytes written
   Otherwise: -1
******************************************************************************/

/******************************************************************************

FUNCTION

RETURNS
   Successful completion: the number of bytes received
   Otherwise  negative error code
******************************************************************************/
static int vc_vchi_msg_bulk_write(FILESERV_MSG_T* msg, uint16_t cmd_id, uint32_t transfer_len, uint8_t* send_addr )
{
   uint32_t i;
   int msg_len;
   uint32_t align_bytes = 0;
   uint32_t bulk_end_bytes = 0;
   uint32_t bulk_bytes = 0;
   int num_bytes_written = -1;

   //this is current file_io_buffer size so assuming never more than this
   //otherwise we will split the read into chunks
   if(!vcos_verify(transfer_len <= FILESERV_MAX_BULK))
      return -1;

   i = vc_filesys_client.cur_xid + 1;
   i &= 0x7fffffffUL;
   vc_filesys_client.cur_xid = i;

   msg->xid = vc_filesys_client.cur_xid;

   //fill in cmd id VC_FILESYS_OPEN etc
   msg->cmd_code = cmd_id;

   msg->params[2] = transfer_len;

   //24 comes from the static size of FILESERV_MSG_T
   msg_len = 24;

   //put it all in one msg
   if(transfer_len <= FILESERV_MAX_DATA) {
      memcpy(msg->data, send_addr, transfer_len);
      msg->params[3] = 0;
      msg_len += transfer_len;
      //send request to write to host
      if(vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ) != 0)
         return -1;

      // wait to receive response
      if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS &&
         msg->cmd_code != FILESERV_RESP_ERROR && msg->params[0] == transfer_len)
      {
         //vc_fs_message_handler copies resp into outgoing msg struct
         num_bytes_written = (int)msg->params[0];
      }
   }
   else {
      //required to make send_addr for bulk
      align_bytes = VCHI_BULK_ALIGN_NBYTES(send_addr);

// Make this code conditional to stop Coverity complaining about dead code
#if VCHI_BULK_ALIGN > 1
      //copy vc_align bytes to msg->data, send_addr ready for bulk
      if(align_bytes) {
         msg->params[3] = align_bytes;
         memcpy(msg->data, send_addr, align_bytes);
         transfer_len -= align_bytes;
         send_addr += align_bytes;
         msg_len += align_bytes;
      }
      else
#endif
         msg->params[3] = 0;

      // need to ensure we have the appropriate alignment
      bulk_bytes = (transfer_len)&(~(VCHI_BULK_GRANULARITY-1));

      bulk_end_bytes = transfer_len-bulk_bytes;

      if(bulk_end_bytes) {
         memcpy(msg->data+align_bytes, send_addr+bulk_bytes, bulk_end_bytes);
         msg_len += bulk_end_bytes;
      }

      //send request to write to host
      if(vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ) != 0)
         return -1;

      //send bulk VCHI_FLAGS_BLOCK_UNTIL_QUEUED is ok because we wait for response msg with actual length written
      if(vchi_bulk_queue_transmit( vc_filesys_client.open_handle,
                                   send_addr,
                                   bulk_bytes,
                                   VCHI_FLAGS_BLOCK_UNTIL_QUEUED,
                                   NULL ) != 0)
         return -1;

      // wait to receive response sent by filsys_task_func
      if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && msg->cmd_code == FILESERV_BULK_READ)
         num_bytes_written = (int)msg->params[0];
   }

   return num_bytes_written;
}

int vc_filesys_write(int fildes, const void *buf, unsigned int nbyte)
{
   int num_wrt = 0;
   int bulk_bytes = 0;
   int actual_written = 0;
   uint8_t *ptr = (uint8_t*) buf;

   if (nbyte == 0) {
      return 0;
   }

   if(lock_obtain() == 0)
   {
      //will split the read into 4K chunks based on vc fwrite buffer size array size
      //we will make this more dynamic later
      do {
         bulk_bytes = nbyte > FILESERV_MAX_BULK ? FILESERV_MAX_BULK : nbyte;

         //we overwrite the response here so fill in data again
         vc_filesys_client.fileserv_msg.params[0] = (uint32_t)fildes;
         vc_filesys_client.fileserv_msg.params[1] = 0xffffffffU;        // offset: use -1 to indicate no offset

         actual_written = vc_vchi_msg_bulk_write(&vc_filesys_client.fileserv_msg , VC_FILESYS_WRITE, (uint32_t)bulk_bytes, (uint8_t*)ptr);

         if(bulk_bytes != actual_written) {
            if(actual_written < 0)
               num_wrt = -1;
            else
               num_wrt += actual_written;
            break;
         }

         ptr+=bulk_bytes;
         nbyte -= actual_written;
         num_wrt += actual_written;
      }while(nbyte > 0);

      lock_release();
   }

   return num_wrt;
}

/* Directory management functions */

/******************************************************************************
NAME
   vc_filesys_closedir

SYNOPSIS
   int vc_filesys_closedir(void *dhandle)

FUNCTION
   Ends a directory list iteration.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_closedir(void *dhandle)
{
   return vc_filesys_single_param((uint32_t)dhandle, VC_FILESYS_CLOSEDIR);
}

/******************************************************************************
NAME
   vc_filesys_format

SYNOPSIS
   int vc_filesys_format(const char *path)

FUNCTION
   Formats the physical file system that contains path.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_format(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_FORMAT, 0);
}

/******************************************************************************
NAME
   vc_filesys_freespace

SYNOPSIS
   int vc_filesys_freespace(const char *path)

FUNCTION
   Returns the amount of free space on the physical file system that contains
   path.

RETURNS
   Successful completion: free space
   Otherwise: -1
******************************************************************************/

int vc_filesys_freespace(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_FREESPACE, 1);
}

/******************************************************************************
NAME
   vc_filesys_freespace64

SYNOPSIS
   int64_t vc_filesys_freespace64(const char *path)

FUNCTION
   Returns the amount of free space on the physical file system that contains
   path.

RETURNS
   Successful completion: free space
   Otherwise: -1
******************************************************************************/

int64_t vc_filesys_freespace64(const char *path)
{
   int64_t freespace = -1LL;

   if(lock_obtain() == 0)
   {
      strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH);

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_FREESPACE64,
                        (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK)
      {
         freespace = vc_filesys_client.fileserv_msg.params[0];
         freespace += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32;
      }

      lock_release();
   }

   return freespace;
}

/******************************************************************************
NAME
   vc_filesys_get_attr

SYNOPSIS
   int vc_filesys_get_attr(const char *path, fattributes_t *attr)

FUNCTION
   Gets the file/directory attributes.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_get_attr(const char *path, fattributes_t *attr)
{
   int success = -1;

   if(lock_obtain() == 0)
   {
      strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH);

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_GET_ATTR,
                        (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK)
      {
         success = 0;
         *attr = (fattributes_t) vc_filesys_client.fileserv_msg.params[0];
      }

      lock_release();
   }

   return success;
}

/******************************************************************************
NAME
   vc_filesys_fstat

SYNOPSIS
   int64_t vc_filesys_fstat(int fildes, FSTAT_T *buf)

FUNCTION
   Returns the file stat info structure for the specified file.
   This structure includes date and size info.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_fstat(int fildes, FSTAT_T *buf)
{
   int success = -1;

   if (buf != NULL && lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = (uint32_t) fildes;
      if ( vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_FSTAT, 4) == FILESERV_RESP_OK )
      {
         buf->st_size = (int64_t)vc_filesys_client.fileserv_msg.params[0];          // LSB
         buf->st_size |= (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32;    // MSB
         buf->st_modtime = (uint32_t)vc_filesys_client.fileserv_msg.params[2];
         // there's room for expansion here to pass across more elements of the structure if required...
         success = 0;
      }

      lock_release();
   }

   return success;
}

/******************************************************************************
NAME
   vc_filesys_mkdir

SYNOPSIS
   int vc_filesys_mkdir(const char *path)

FUNCTION
   Creates a new directory named by the pathname pointed to by path.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_mkdir(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_MKDIR, 0);
}

/******************************************************************************
NAME
   vc_filesys_opendir

SYNOPSIS
   void *vc_filesys_opendir(const char *dirname)

FUNCTION
   Starts a directory list iteration.

RETURNS
   Successful completion: dhandle (pointer)
   Otherwise: NULL
******************************************************************************/

void *vc_filesys_opendir(const char *dirname)
{
   return (void *) vc_filesys_single_string(0, dirname, VC_FILESYS_OPENDIR, 1);
}

/******************************************************************************
NAME
   vc_filesys_dirsize

SYNOPSIS
   int vc_filesys_dirsize(const char *path)

FUNCTION
   Look through the specified directory tree and sum the file sizes.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int64_t vc_filesys_dirsize(const char *path, uint32_t *num_files, uint32_t *num_dirs)
{
   int64_t ret = -1;

   if(lock_obtain() == 0)
   {
      strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH);

      // FIXME: Should probably use a non-blocking call here since it may take a
      // long time to do the operation...
      if ( vchi_msg_stub(&vc_filesys_client.fileserv_msg,
                         VC_FILESYS_DIRSIZE,
                         (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK )
      {
         ret = vc_filesys_client.fileserv_msg.params[0];
         ret += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32;
         if (num_files)
            *num_files = vc_filesys_client.fileserv_msg.params[2];
         if (num_dirs)
            *num_dirs = vc_filesys_client.fileserv_msg.params[3];
      }

      lock_release();
   }

   return ret;
}

/******************************************************************************
NAME
   vc_filesys_readdir_r

SYNOPSIS
   struct dirent *vc_filesys_readdir_r(void *dhandle, struct dirent *result)

FUNCTION
   Fills in the passed result structure with details of the directory entry
   at the current psition in the directory stream specified by the argument
   dhandle, and positions the directory stream at the next entry.

RETURNS
   Successful completion: result
   End of directory stream: NULL
******************************************************************************/

struct dirent *vc_filesys_readdir_r(void *dhandle, struct dirent *result)
{
   struct dirent *ret = NULL;

   if(lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = (uint32_t)dhandle;
      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_READDIR, 4) == FILESERV_RESP_OK)
      {
         fs_host_direntbytestream_interp(result, (void *)vc_filesys_client.fileserv_msg.data);
         ret = result;
      }

      lock_release();
   }

   return ret;
}

/******************************************************************************
NAME
   vc_filesys_remove

SYNOPSIS
   int vc_filesys_remove(const char *path)

FUNCTION
   Removes a file or a directory. A directory must be empty before it can be
   deleted.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_remove(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_REMOVE, 0);
}

/******************************************************************************
NAME
   vc_filesys_rename

SYNOPSIS
   int vc_filesys_rename(const char *oldfile, const char *newfile)

FUNCTION
   Changes the name of a file. The old and new pathnames must be on the same
   physical file system.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_rename(const char *oldfile, const char *newfile)
{
   int a, b, success = -1;

   // Ensure the pathnames aren't too long
   if ((a = strlen(oldfile)) < FS_MAX_PATH && (b = strlen(newfile)) < FS_MAX_PATH && lock_obtain() == 0)
   {
      strncpy((char *)vc_filesys_client.fileserv_msg.data, oldfile, FS_MAX_PATH);
      strncpy((char *)&vc_filesys_client.fileserv_msg.data[a+1], newfile, FS_MAX_PATH);

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_RENAME, 16+a+1+b+1) == FILESERV_RESP_OK)
         success = 0;

      lock_release();
   }

   return success;
}

/******************************************************************************
NAME
   vc_filesys_reset

SYNOPSIS
   int vc_filesys_reset()

FUNCTION
   Send a vc_FILESYS_RESET command. This will return immediately.

RETURNS
   Successful completion: FILESERV_RESP_OK
   Otherwise: -
******************************************************************************/

int vc_filesys_reset()
{
   return vc_filesys_single_param(0, VC_FILESYS_RESET);
}

/******************************************************************************
NAME
   vc_filesys_set_attr

SYNOPSIS
   int vc_filesys_set_attr(const char *path, fattributes_t attr)

FUNCTION
   Sets file/directory attributes.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_set_attr(const char *path, fattributes_t attr)
{
   return vc_filesys_single_string((uint32_t) attr, path, VC_FILESYS_SET_ATTR, 0);
}

/******************************************************************************
NAME
   vc_filesys_setend

SYNOPSIS
   int vc_filesys_setend(int fildes)

FUNCTION
   Truncates file at current position.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_setend(int fildes)
{
   return vc_filesys_single_param((uint32_t) fildes, VC_FILESYS_SETEND);
}

/******************************************************************************
NAME
   vc_filesys_scandisk

SYNOPSIS
   void vc_filesys_scandisk(const char *path)

FUNCTION
   Truncates file at current position.

RETURNS
   -
******************************************************************************/

void vc_filesys_scandisk(const char *path)
{
   vc_filesys_single_string(0, path, VC_FILESYS_SCANDISK, 0);
   return;
}

/******************************************************************************
NAME
   vc_filesys_chkdsk

SYNOPSIS
   int vc_filesys_chkdsk(const char *path, int fix_errors)

FUNCTION
   Truncates file at current position.

RETURNS
   -
******************************************************************************/

int vc_filesys_chkdsk(const char *path, int fix_errors)
{
   return vc_filesys_single_string(fix_errors, path, VC_FILESYS_CHKDSK, 1);
}

/******************************************************************************
NAME
   vc_filesys_size

SYNOPSIS
   int vc_filesys_size(const char *path)

FUNCTION
   return size of file

RETURNS
   -
******************************************************************************/

int vc_filesys_size(const char *path)
{
   int fd;
   int end_pos = 0;
   int success = -1;
   if((fd = vc_filesys_open(path, VC_O_RDONLY)) == 0)
   {
      end_pos = vc_filesys_lseek(fd, 0, SEEK_END);
      success = vc_filesys_close(fd);
      (void)success;
   }

   return end_pos;
}
/******************************************************************************
NAME
   vc_filesys_totalspace

SYNOPSIS
   int vc_filesys_totalspace(const char *path)

FUNCTION
   Returns the total amount of space on the physical file system that contains
   path.

RETURNS
   Successful completion: total space
   Otherwise: -1
******************************************************************************/

int vc_filesys_totalspace(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_TOTALSPACE, 1);
}

/******************************************************************************
NAME
   vc_filesys_totalspace64

SYNOPSIS
   int64_t vc_filesys_totalspace64(const char *path)

FUNCTION
   Returns the total amount of space on the physical file system that contains
   path.

RETURNS
   Successful completion: total space
   Otherwise: -1
******************************************************************************/

int64_t vc_filesys_totalspace64(const char *path)
{
   int64_t totalspace = -1LL;

   if(lock_obtain() == 0)
   {
      strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH);

      if (vchi_msg_stub(&vc_filesys_client.fileserv_msg, VC_FILESYS_TOTALSPACE64,
                        (int)(16+strlen((char *)vc_filesys_client.fileserv_msg.data)+1)) == FILESERV_RESP_OK)
      {
         totalspace = vc_filesys_client.fileserv_msg.params[0];
         totalspace += (int64_t)vc_filesys_client.fileserv_msg.params[1] << 32;
      }

      lock_release();
   }

   return totalspace;
}

/******************************************************************************
NAME
   vc_filesys_diskwritable

SYNOPSIS
   int vc_filesys_diskwritable(const char *path)

FUNCTION
   Return whether the named disk is writable.

RETURNS
   Successful completion: 1 (disk writable) or 0 (disk not writable)
   Otherwise: -1
******************************************************************************/

int vc_filesys_diskwritable(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_DISKWRITABLE, 1);
}

/******************************************************************************
NAME
   vc_filesys_fstype

SYNOPSIS
   int vc_filesys_fstype(const char *path)

FUNCTION
   Return the filesystem type of the named disk.

RETURNS
   Successful completion: disk type (see vc_fileservice_defs.h)
   Otherwise: -1
******************************************************************************/

int vc_filesys_fstype(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_FSTYPE, 1);
}

/******************************************************************************
NAME
   vc_filesys_open_disk_raw

SYNOPSIS
   int vc_filesys_open_disk_raw(const char *path)

FUNCTION
   Open disk for access in raw mode.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_open_disk_raw(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_OPEN_DISK_RAW, 1);
}

/******************************************************************************
NAME
   vc_filesys_close_disk_raw

SYNOPSIS
   int vc_filesys_close_disk_raw(const char *path)

FUNCTION
   Close disk from access in raw mode.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_close_disk_raw(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_CLOSE_DISK_RAW, 1);
}

/******************************************************************************
NAME
   vc_filesys_open_disk

SYNOPSIS
   int vc_filesys_open_disk(const char *path)

FUNCTION
   Open disk for normal access.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_open_disk(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_OPEN_DISK, 1);
}

/******************************************************************************
NAME
   vc_filesys_close_disk

SYNOPSIS
   int vc_filesys_close_disk(const char *path)

FUNCTION
   Close disk from normal access.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_close_disk(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_CLOSE_DISK, 1);
}

/******************************************************************************
NAME
   vc_filesys_numsectors

SYNOPSIS
   int vc_filesys_numsectors(const char *path)

FUNCTION
   Return number of sectors on disk

RETURNS
   Successful completion: greater than 0
   Otherwise: -1
******************************************************************************/

int vc_filesys_numsectors(const char *path)
{
   return vc_filesys_single_string(0, path, VC_FILESYS_NUMSECTORS, 1);
}

/******************************************************************************
NAME
   vc_filesys_read_sectors

SYNOPSIS
   int vc_filesys_read_sectors(const char *path, uint32_t sector_num, char *buffer, uint32_t num_sectors)

FUNCTION
   Start streaming sectors from 2727

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/
int vc_filesys_read_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_read)
{
#if VCHI_BULK_ALIGN > 1
   uint32_t align_bytes = 0;
#endif
   char* bulk_addr = sectors;
   int len;
   int ret = -1;

   if((len = (int)strlen(path)) < FS_MAX_PATH && lock_obtain() == 0)
   {
      strncpy((char *)vc_filesys_client.fileserv_msg.data, path, FS_MAX_PATH);
      vc_filesys_client.fileserv_msg.params[0] = sector_num;
      vc_filesys_client.fileserv_msg.params[1] = num_sectors;

#if VCHI_BULK_ALIGN > 1
      //required to make buffer aligned for bulk
      align_bytes = VCHI_BULK_ALIGN_NBYTES(sectors);
#endif
      //note for read we are currently doing memcpy on host to tell 2727 align is 0
      vc_filesys_client.fileserv_msg.params[2] = 0;

      //we send read request
      if (vchi_msg_stub_noblock(&vc_filesys_client.fileserv_msg, VC_FILESYS_READ_SECTORS, 16+len+1) == 0)
      {
         while(num_sectors)
         {
            uint32_t bulk_sectors = (num_sectors > FILESERV_MAX_BULK_SECTOR) ? (uint32_t)FILESERV_MAX_BULK_SECTOR : num_sectors;
            if(vchi_bulk_queue_receive( vc_filesys_client.open_handle,
#if VCHI_BULK_ALIGN > 1
                                        align_bytes ? vc_filesys_client.bulk_buffer : bulk_addr,
#else
                                        bulk_addr,
#endif
                                        (bulk_sectors*FILESERV_SECTOR_LENGTH), VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, NULL) != 0)
               break;

#if VCHI_BULK_ALIGN > 1
            if(align_bytes) {
               //this is bad but will do for now..
               memcpy( bulk_addr, vc_filesys_client.bulk_buffer, (bulk_sectors*FILESERV_SECTOR_LENGTH));
            }
#endif
            bulk_addr += (bulk_sectors*FILESERV_SECTOR_LENGTH);
            num_sectors -= bulk_sectors;
         }

         // now wait to receive resp from original msg...
         if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && vc_filesys_client.fileserv_msg.cmd_code == FILESERV_RESP_OK)
         {
            *sectors_read = vc_filesys_client.fileserv_msg.params[0];
            ret = 0;
         }
         else
         {
            //error code in [0]
            *sectors_read = vc_filesys_client.fileserv_msg.params[1];
            ret = vc_filesys_client.fileserv_msg.params[0];
         }
      }

      lock_release();
   }

   return ret;
}

/******************************************************************************
NAME
   vc_filesys_write_sectors

SYNOPSIS
   int vc_filesys_write_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_written)

FUNCTION
   Start streaming sectors to 2727

RETURNS
   Successful completion: 0
   Otherwise: -error code
******************************************************************************/
int vc_filesys_write_sectors(const char *path, uint32_t sector_num, char *sectors, uint32_t num_sectors, uint32_t *sectors_written)
{
   uint32_t align_bytes = 0;
   char* bulk_addr = sectors;
   int len;
   int ret = -1;

   len = (int) strlen(path);
   if((len = (int) strlen(path)) < FS_MAX_PATH && lock_obtain() == 0)
   {
      vc_filesys_client.fileserv_msg.params[0] = sector_num;
      vc_filesys_client.fileserv_msg.params[1] = num_sectors;

      //required to make buffer aligned for bulk
      align_bytes = ((unsigned long)sectors & (VCHI_BULK_ALIGN-1));
      vc_filesys_client.fileserv_msg.params[2] = align_bytes;

      //copy path at end of any alignbytes
      strncpy(((char *)vc_filesys_client.fileserv_msg.data), path, FS_MAX_PATH);

      //we send write request
      if (vchi_msg_stub_noblock(&vc_filesys_client.fileserv_msg, VC_FILESYS_WRITE_SECTORS, 16+len+1) == 0)
      {
         if(align_bytes) {
            //note we are cheating and going backward to make addr aligned and sending more data...
            bulk_addr -= align_bytes;
         }

         while(num_sectors) {
            uint32_t bulk_sectors = (num_sectors > FILESERV_MAX_BULK_SECTOR) ? (uint32_t)FILESERV_MAX_BULK_SECTOR : num_sectors;
            //we send some extra data at the start
            if(vchi_bulk_queue_transmit( vc_filesys_client.open_handle, bulk_addr,
                                         VCHI_BULK_ROUND_UP((bulk_sectors*FILESERV_SECTOR_LENGTH)+align_bytes),
                                         VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, NULL) != 0)
               break;

            //go to next ALIGNED address
            bulk_addr += FILESERV_MAX_BULK;
            num_sectors -= bulk_sectors;
         }

         // now wait to receive resp from original msg...
         if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS && vc_filesys_client.fileserv_msg.cmd_code == FILESERV_RESP_OK)
         {
            *sectors_written = vc_filesys_client.fileserv_msg.params[0];
            ret = 0;
         }
         else
         {
            //error code in [0]
            *sectors_written = vc_filesys_client.fileserv_msg.params[1];
            ret = vc_filesys_client.fileserv_msg.params[0];
         }
      }

      lock_release();
   }

   return ret;
}

/******************************************************************************
NAME
   vc_filesys_errno

SYNOPSIS
   int vc_filesys_errno(void)

FUNCTION
   Returns the error code of the last file system error that occurred.

RETURNS
   Error code
******************************************************************************/

int vc_filesys_errno(void)
{
   return (int) vc_filesys_client.err_no;
}

/* File Service Message FIFO functions */

/******************************************************************************
NAME
   vchi_msg_stub

SYNOPSIS
   static int vc_fs_stub(int verb, int ext_len)

FUNCTION
   Generates a request and sends it to the co-processor. It then suspends
   until it receives a reply from the host. The calling task must hold
   the filesys_lock.

RETURNS
   Successful completion: Response code of reply
   Otherwise: -
******************************************************************************/
static int vchi_msg_stub(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len )
{
   int ret = -1;
   vchi_msg_stub_noblock(msg, cmd_id, msg_len);

   // wait to receive response
   if(vcos_event_wait(&vc_filesys_client.response_event) == VCOS_SUCCESS)
      ret = (int) msg->cmd_code;

   return ret;
}

static int vchi_msg_stub_noblock(FILESERV_MSG_T* msg, uint16_t cmd_id, int msg_len)
{
   uint32_t i;

   if(!vcos_verify(msg_len <= VCHI_MAX_MSG_SIZE))
      return -1;

   //will get changed by response from command
   vc_filesys_client.resp_code = FILESERV_RESP_ERROR;

   //the top bit is used for host/vc
   i = vc_filesys_client.cur_xid + 1;
   i &= 0x7fffffffUL;
   vc_filesys_client.cur_xid = i;

   //fill in transaction id, used for response identification
   vchi_writebuf_uint32( &(msg->xid), vc_filesys_client.cur_xid  );

   //fill in cmd id VC_FILESYS_OPEN etc
   vchi_writebuf_uint32( &(msg->cmd_code), cmd_id );

   //we always have cmd_id, xid
   msg_len += 8;

   //return response code
   return (int) vchi_msg_queue( vc_filesys_client.open_handle, msg, (uint32_t)msg_len, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
}

static int vc_send_response( FILESERV_MSG_T* msg, uint32_t retval, uint32_t nbytes )
{
   int success = -1;
   //convert all to over the wire values
   vchi_writebuf_uint32(&msg->cmd_code, retval);
   vchi_writebuf_uint32(&msg->xid, msg->xid);
   vchi_writebuf_uint32(&msg->params[0], msg->params[0]);
   vchi_writebuf_uint32(&msg->params[1], msg->params[1]);
   vchi_writebuf_uint32(&msg->params[2], msg->params[2]);
   vchi_writebuf_uint32(&msg->params[3], msg->params[3]);

   //start with 8 because always xid and retval
   nbytes += 8;

   if(vcos_verify(nbytes <= VCHI_MAX_MSG_SIZE))
      success = (int) vchi_msg_queue( vc_filesys_client.open_handle, msg, nbytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );

   return success;
}

/******************************************************************************
NAME
   vc_fs_message_handler

SYNOPSIS
   static int vc_fs_message_handler()

FUNCTION
   Handle messages from the co-processor.

RETURNS
   0 - No message found.
   1 - Request received and actioned.
   2 - Reply received.
******************************************************************************/

static int vc_fs_message_handler( FILESERV_MSG_T* msg, uint32_t nbytes )
{
   int rr = 0;
   uint32_t xid = vchi_readbuf_uint32(&msg->xid);

   if (xid == vc_filesys_client.cur_xid) {

   	  //memcpy reply to msg should really peek before
      vc_filesys_client.fileserv_msg.xid = xid;
      vc_filesys_client.fileserv_msg.cmd_code  = vchi_readbuf_uint32(&msg->cmd_code);
      vc_filesys_client.fileserv_msg.params[0] = vchi_readbuf_uint32(&msg->params[0]);
      vc_filesys_client.fileserv_msg.params[1] = vchi_readbuf_uint32(&msg->params[1]);
      vc_filesys_client.fileserv_msg.params[2] = vchi_readbuf_uint32(&msg->params[2]);
      vc_filesys_client.fileserv_msg.params[3] = vchi_readbuf_uint32(&msg->params[3]);

      //copy any data, 24 is size of header
      if(nbytes >24)
         memcpy(&vc_filesys_client.fileserv_msg.data, msg->data, (nbytes-24));

      vc_filesys_client.resp_code = (int32_t)vc_filesys_client.fileserv_msg.cmd_code;

      if (vc_filesys_client.resp_code == FILESERV_RESP_ERROR) {
         vc_filesys_client.err_no = (int32_t)vc_filesys_client.fileserv_msg.params[0];
      }

      // signal vchi_msg_stub which will be waiting for response
      vcos_event_signal(&vc_filesys_client.response_event);

      rr = 2;
   }
   else if ((xid & 0x80000000UL) == 0x80000000UL) {
      /* Process new requests from the co-processor */

      uint32_t retval = FILESERV_RESP_OK;

      //this is the number of uint32_t param[] + data that we send back to VC in bytes

      uint32_t rlen = 0;
      int i;

      switch (msg->cmd_code) {

      case VC_FILESYS_CLOSE:

         i = vc_hostfs_close((int)msg->params[0]);
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_CLOSEDIR:

         i = vc_hostfs_closedir((void *)msg->params[0]);
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_FORMAT:

         i = vc_hostfs_format((const char *)msg->data);
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_FREESPACE:

         i = vc_hostfs_freespace((const char *)msg->data);
         if (i < 0) {
            retval = FILESERV_RESP_ERROR;
            rlen = 0;
         } else {
            msg->params[0] = (uint32_t)i;
            rlen = 4;
         }
         break;

      case VC_FILESYS_FREESPACE64:
         {
            int64_t freespace;
            freespace = vc_hostfs_freespace64((const char *)msg->data);
            if (freespace < (int64_t)0) {
               retval = FILESERV_RESP_ERROR;
               rlen = 0;
            } else {
               msg->params[0] = (uint32_t)freespace;
               msg->params[1] = (uint32_t)(freespace>>32);
               rlen = 8;
            }
         }
         break;

      case VC_FILESYS_GET_ATTR:
         {
            fattributes_t attr;

            i = vc_hostfs_get_attr((const char *)msg->data,
                                   &attr);
            if (i != 0) {
               retval = FILESERV_RESP_ERROR;
               rlen = 0;
            } else {
               msg->params[0] = (uint32_t) attr;
               rlen = 4;
            }
         }
         break;
      case VC_FILESYS_LSEEK:

         i = vc_hostfs_lseek( (int)msg->params[0],
                              (int)msg->params[1],
                              (int)msg->params[2]);
         if (i < 0) {
            retval = FILESERV_RESP_ERROR;
            rlen = 0;
         } else {
            msg->params[0] = (uint32_t) i;
            rlen = 4;
         }
         break;

      case VC_FILESYS_LSEEK64:
         {
            int64_t offset;
            offset = (((int64_t) msg->params[2]) << 32) + msg->params[1];

            offset = vc_hostfs_lseek64( (int)msg->params[0], offset, (int)msg->params[3]);
            if (offset < (int64_t)0) {
               retval = FILESERV_RESP_ERROR;
               rlen = 0;
            } else {
               msg->params[0] = (uint32_t)offset;
               msg->params[1] = (uint32_t)(offset>>32);
               rlen = 8;
            }
         }
         break;

      case VC_FILESYS_MKDIR:

         i = vc_hostfs_mkdir((const char *)msg->data);
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_OPEN:

         i = vc_hostfs_open((const char *)msg->data,
                            (int) msg->params[0]);
         if (i < 0) {
            retval = FILESERV_RESP_ERROR;
         } else {
            msg->params[0] = (uint32_t) i;
         }
         rlen = 4;
         break;

      case VC_FILESYS_OPENDIR:

         msg->params[0] = (uint32_t)vc_hostfs_opendir(
                                                      (const char *)msg->data);
         if ((void *)msg->params[0] == NULL) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 4;
         break;

      case VC_FILESYS_READ:
         {
            uint32_t fd = msg->params[0];
            uint32_t offset = msg->params[1];
            int total_bytes = (int)msg->params[2];
            uint32_t nalign_bytes = msg->params[3];

            i = 0;

            if(!vcos_verify(((int)vc_filesys_client.bulk_buffer & (VCHI_BULK_ALIGN-1)) == 0 &&
                            total_bytes <= FILESERV_MAX_BULK))
            {
               retval = FILESERV_RESP_ERROR;
               rlen = 4;
               break;
            }

            rlen = 0;

            //perform any seeking
            if ( (uint32_t)0xffffffffUL != offset)
            {
               i = vc_hostfs_lseek( (int)fd,
                                    (long int) offset,
                                    VC_FILESYS_SEEK_SET);
               if ( 0 > i)
               {
                  retval = FILESERV_RESP_ERROR;
                  rlen = 4;
                  break;
               }
            }

            //put it all in one msg
            if(total_bytes <= FILESERV_MAX_DATA) {
               i = vc_hostfs_read( (int)fd,
                                   msg->data,
                                   (unsigned int) total_bytes);

               if(i < 0) {
                  retval = FILESERV_RESP_ERROR;
                  msg->params[0] = 0;
               }
               else {
                  retval = FILESERV_RESP_OK;
                  //send back length of read
                  msg->params[0] = (uint32_t) i;
               }

               msg->params[1] = 0;
               rlen = 16 + (uint32_t) i;
            }
            //bulk transfer required
            else {
               uint32_t end_bytes = 0;
               retval = FILESERV_BULK_WRITE;

               //we send the bytes required for HOST buffer align
               if(nalign_bytes) {
                  i = vc_hostfs_read( (int)fd,
                                      msg->data,
                                      (unsigned int)nalign_bytes);
                  if(i < 0) {
                     retval = FILESERV_RESP_ERROR;
                     rlen = 16;
                     break;
                  }
                  else if(i != (int)nalign_bytes) {
                     //all data will be in one msg
                     retval = FILESERV_RESP_OK;
                     msg->params[0] = (uint32_t) i;
                     msg->params[1] = 0;
                     rlen = 16 + (uint32_t) i;
                     //send response
                     break;
                  }

                  total_bytes -= i;
                  rlen += (uint32_t) i;
               }

               //bulk bytes
               i = vc_hostfs_read((int)fd, vc_filesys_client.bulk_buffer, (unsigned int)total_bytes);

               if(i < 0) {
                  retval = FILESERV_RESP_ERROR;
                  rlen = 16;
                  break;
               }
               else if((i+nalign_bytes) <= FILESERV_MAX_DATA) {
                  retval = FILESERV_RESP_OK;
                  memcpy(&msg->data[nalign_bytes], &vc_filesys_client.bulk_buffer[0], (size_t) i);
                  //read size
                  msg->params[0] = (i + nalign_bytes);
                  msg->params[1] = 0;
                  rlen = i + nalign_bytes + 16;
                  break;
               }

               //copy end unaligned length bytes into msg->data
               end_bytes  = (uint32_t) (i & (VCHI_BULK_GRANULARITY-1));
               if(end_bytes) {
                  int end_index = i - (int) end_bytes;
                  memcpy(&msg->data[nalign_bytes], &vc_filesys_client.bulk_buffer[end_index], end_bytes);
                  rlen += end_bytes;
               }

               //send back total bytes
               msg->params[0] = (uint32_t)(i + nalign_bytes);
               //number of end bytes
               msg->params[1] = end_bytes;
               //number of bulk bytes
               msg->params[2] = (uint32_t)(i - end_bytes);
               //16 for param len
               rlen += 16;

               //queue bulk to be sent
               if(vchi_bulk_queue_transmit( vc_filesys_client.open_handle,
                                            vc_filesys_client.bulk_buffer,
                                            msg->params[2],
                                            VCHI_FLAGS_BLOCK_UNTIL_QUEUED,
                                            NULL ) != 0)
               {
                  retval = FILESERV_RESP_ERROR;
                  rlen = 4;
                  break;
               }
            }
         }
         //send response
         break;

      case VC_FILESYS_READDIR:
         {
            struct dirent result;
            if (vc_hostfs_readdir_r((void *)msg->params[0],
                                    &result) == NULL) {
               retval = FILESERV_RESP_ERROR;
               rlen = 4;
            } else {
               rlen = (uint32_t) (16+fs_host_direntbytestream_create(&result,
                                                                     (void *)msg->data));
            }
         }
         break;

      case VC_FILESYS_REMOVE:

         i = vc_hostfs_remove((const char *)msg->data);
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_RENAME:

         i = (int) strlen((char *)msg->data);
         if (vc_hostfs_rename((const char *)msg->data,
                              (const char *)&msg->data[i+1])
             != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_SETEND:

         i = vc_hostfs_setend( (int)msg->params[0] );
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_SET_ATTR:

         i = vc_hostfs_set_attr((const char *)msg->data,
                                (fattributes_t)msg->params[0]);
         if (i != 0) {
            retval = FILESERV_RESP_ERROR;
         }
         rlen = 0;
         break;

      case VC_FILESYS_TOTALSPACE:

         i = vc_hostfs_totalspace((const char *)msg->data);
         if (i < 0) {
            retval = FILESERV_RESP_ERROR;
            rlen = 0;
         } else {
            msg->params[0] = (uint32_t) i;
            rlen = 4;
         }
         break;

      case VC_FILESYS_TOTALSPACE64:
         {
            int64_t totalspace;
            totalspace = vc_hostfs_totalspace64((const char *)msg->data);
            if (totalspace < (int64_t)0) {
               retval = FILESERV_RESP_ERROR;
               rlen = 0;
            } else {
               msg->params[0] = (uint32_t)totalspace;
               msg->params[1] = (uint32_t)(totalspace>>32);
               rlen = 8;
            }
         }
         break;
#if 0  // I don't think host systems are ready for these yet
      case VC_FILESYS_SCANDISK:

         vc_hostfs_scandisk((const char *)msg->data);
         rlen = 0;
         break;

      case VC_FILESYS_CHKDSK:

         i = vc_hostfs_chkdsk((const char *)msg->data, msg->params[0]);
         if (i < 0) {
            retval = FILESERV_RESP_ERROR;
            rlen = 0;
         } else {
            msg->params[0] = (uint32_t)i;
            rlen = 4;
         }
         break;
#endif
      case VC_FILESYS_WRITE:
         {
            uint32_t fd = msg->params[0];
            //            uint32_t offset = msg->params[1];
            int total_bytes = (int)msg->params[2];
            uint32_t nalign_bytes = msg->params[3];
            retval = FILESERV_RESP_OK;
            i = 0;

            //everything in one msg
            if(total_bytes <= FILESERV_MAX_DATA)
            {
               i = vc_hostfs_write( (int)fd,
                                    msg->data,
                                    (unsigned int) total_bytes);
               if (i < 0) {
                  retval = FILESERV_RESP_ERROR;
               } else {
                  msg->params[0] = (uint32_t) i;
               }
               rlen = 4;
               //send response
               break;
            }
            else
            {
               uint32_t end_bytes;
               uint32_t bulk_bytes;
               uint32_t total_bytes_written = 0;
               i = 0;
               //one return param
               rlen = 4;
               retval = FILESERV_BULK_READ;

               //write bytes required for VC buffer align
               if(nalign_bytes) {
                  i = vc_hostfs_write( (int)fd,
                                       msg->data,
                                       (unsigned int)nalign_bytes);
                  if(i < 0) {
                     retval = FILESERV_RESP_ERROR;
                     msg->params[0] = 0;
                     //break from switch and send reply
                     break;
                  }
                  total_bytes_written += i;
                  total_bytes -= nalign_bytes;
               }

               //calculate bytes that wil lbe sent by bulk
               bulk_bytes = (uint32_t)((total_bytes)&(~(VCHI_BULK_ALIGN-1)));

               end_bytes = total_bytes-bulk_bytes;

               if(vchi_bulk_queue_receive(vc_filesys_client.open_handle,
                                          vc_filesys_client.bulk_buffer,
                                          bulk_bytes,
                                          VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE,
                                          NULL) != 0 ||
                  (i = vc_hostfs_write( (int)fd,
                                        vc_filesys_client.bulk_buffer,
                                        (unsigned int) bulk_bytes)) < 0)
               {
                  retval = FILESERV_RESP_ERROR;
                  msg->params[0] = 0;
                  break;
               }

               total_bytes_written += i;

               if(end_bytes) {
                  i = vc_hostfs_write( (int)fd,
                                       msg->data+nalign_bytes,
                                       (unsigned int)end_bytes);

                  total_bytes_written += i;
               }

               if(i < 0) {
                  retval = FILESERV_RESP_ERROR;
                  msg->params[0] = 0;
                  break;
               }

               msg->params[0] = total_bytes_written;

            }
         }
         break;

      default:
         rlen = 4;
         retval = FILESERV_RESP_ERROR;
         break;
      }

      //convert all to over the wire values and send
      vc_send_response( msg, retval, rlen );

      rr = 1;

   } else {
      /* A message has been left in the fifo and the host side has been reset.
         The message needs to be flushed. It would be better to do this by resetting
         the fifos. */
   }

   return rr;
}

/******************************************************************************
NAME
   showmsg

SYNOPSIS
   static void showmsg(VC_MSGFIFO_CMD_HEADER_T const * head,
                       struct file_service_msg_body const * body)

FUNCTION
   De-bug tool: prints out fifo message.

RETURNS
   void
******************************************************************************/

#ifdef PRINTF
static void showmsg(VC_MSGFIFO_CMD_HEADER_T const * head,
                    struct file_service_msg_body const * body)
{
   unsigned int ael = (head->ext_length + 15) & ~15;
   printf("Sync=%08x XID=%08x Code=%08x Extlen=%08x (%d bytes follow)\n",
          head->sync, head->xid, head->cmd_code, head->ext_length, ael);
   if (ael) {
      unsigned int i;
      printf("Content:");
      for (i = 0; i < 4 && i*4 < head->ext_length; ++i) printf(" %08x", body->params[i]);
      //for(i = 0; i+16 < head->ext_length; ++i) printf(" %02x", body->data[i]);
      if (head->ext_length > 16) printf(" plus %d bytes\n", head->ext_length);
   }
   printf("\n");
}
#endif

/******************************************************************************
NAME
   fs_host_direntbytestream_create

SYNOPSIS
   static int fs_host_direntbytestream_create(struct dirent *d, void *buffer)

FUNCTION
   Turns a variable of type struct dirent into a compiler independent byte
   stream, which is stored in buffer.

RETURNS
   Successful completion: The length of the byte stream
   Otherwise: -1
******************************************************************************/
static void write_bytestream(void *a, char *b, int n)
{
   int i;
   for (i=0;i<n;i++) {
      b[i] = ((char *)a)[i];
   }
   for (;i<4;i++) {
      b[i] = 0;
   }
}

static int fs_host_direntbytestream_create(struct dirent *d, void *buffer)
{
   char *buf = (char*)buffer;

   // Write d_name (D_NAME_MAX_SIZE chars)
   memcpy(buf, &d->d_name, D_NAME_MAX_SIZE);
   buf += D_NAME_MAX_SIZE;

   // Write d_size (unsigned int)
   write_bytestream((void *)&d->d_size, buf, (int)sizeof(d->d_size));
   buf += 4;

   // Write d_attrib (int)
   write_bytestream((void *)&d->d_attrib, buf, (int)sizeof(d->d_attrib));
   buf += 4;

   // Write d_modtime (time_t)
   write_bytestream((void *)&d->d_modtime, buf, (int)sizeof(d->d_modtime));
   buf += 4;

   return (int)(buf-(char *)buffer);
}

/******************************************************************************
NAME
   fs_host_direntbytestream_interp

SYNOPSIS
   static void fs_host_direntbytestream_interp(struct dirent *d, void *buffer)

FUNCTION
   Turns a compiler independent byte stream back into a struct of type dirent.

RETURNS
   Successful completion: 0
   Otherwise: -1
******************************************************************************/
static void read_bytestream(void *a, char *b, int n)
{
   int i;
   for (i=0;i<n;i++) {
      ((char *)a)[i] = b[i];
   }
}

static void fs_host_direntbytestream_interp(struct dirent *d, void *buffer)
{
   char *buf = (char*)buffer;

   // Read d_name (D_NAME_MAX_SIZE chars)
   memcpy(&d->d_name, buf, D_NAME_MAX_SIZE);
   buf += D_NAME_MAX_SIZE;

   // Read d_size (unsigned int)
   read_bytestream((void *)&d->d_size, buf, (int)sizeof(d->d_size));
   d->d_size = VC_VTOH32(d->d_size);
   buf += 4;

   // Read d_attrib (int)
   read_bytestream((void *)&d->d_attrib, buf, (int)sizeof(d->d_attrib));
   d->d_attrib = VC_VTOH32(d->d_attrib);
   buf += 4;

   // Read d_modtime (time_t)
   read_bytestream((void *)&d->d_modtime, buf, (int)sizeof(d->d_modtime));
   d->d_modtime = VC_VTOH32(d->d_modtime);

   return;
}

void vc_filesys_sendreset(void)
{
   //TODO
}
