
/*

USBJoystick - a RISC OS USB HID Joystick driver

Based on NetBSD USB joystick driver in X-MAME by Krister Walfridsson <cato@df.lth.se> and Dieter Baron <dillo@giga.or.at>
Uses some NetBSD USB HID parsing functions (see headers in data/descr/parse C files)
Uses a FreeBSD USB HID report descriptor for XBOX360 controller (see usbrdesc header file)
Inspiration taken from EtherUSB by James Peacock (Copyright (C) 2012, James Peacock)
RISC OS specific USB code taken from Colin Granville's USBDescriptors (Copyright (c) 2015 Colin Granville. All rights reserved.)
C function to interface to Reporter is from Steve Fryatt's online series

THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.

*/

// C library
#include "swis.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "Global/NewErrors.h"
#include "Global/RISCOS.h"
#include "Global/Keyboard.h"
#include "Global/Pointer.h"

// OSLib
#include "oslib/os.h"
#include "oslib/osfind.h"
#include "oslib/osgbpb.h"
#include "oslib/osbyte.h"
#include "oslib/devicefs.h"
#include "oslib/resourcefs.h"

#include "debugrep.h"
#include "usbservice.h"
#include "adc.h"
#include "mouse.h"
#include "joyhelp.h"
#include "joyswis.h"
#include "usb.h"
#include "joydev.h"
#include "usbjoystickHdr.h"
#include "usbjoystick.h"



// resourcefs
extern void *Resources(void);



// Module state

extern void* g_module_pw = NULL;



// Module errors
// todo: This will be improved so we internationalise them

extern _kernel_oserror err_syntax = {
  ErrorNumber_Syntax,
  Module_Title ": Syntax error"
};

extern _kernel_oserror err_badnoparms = {
  ErrorNumber_BadNoParms,
  "Bad parameters"
};

extern _kernel_oserror err_toomanyparms = {
  ErrorNumber_TooManyParms,
  "Too many parameters"
};

extern _kernel_oserror err_nosuchswi = {
  ErrorNumber_NoSuchSWI,
  "No such SWI"     // ErrorString_ModuleBadSWI
};

extern _kernel_oserror err_modulebadswi = {
  ErrorNumber_ModuleBadSWI,
  "SWI value out of range for module " Module_Title
};

extern _kernel_oserror err_badjoy = {
  ErrorBase_Joystick + 1,
  "Bad joystick number"
};

extern _kernel_oserror err_nousb = {
  ErrorBase_Joystick + 2,
  Module_Title " requires USBDriver"
};

extern _kernel_oserror err_joynotactive = {
  ErrorBase_Joystick + 3,
  "Joystick not active"
};

extern _kernel_oserror err_joynotopen = {
  ErrorBase_Joystick + 4,
  "Joystick not open"
};





// Joystick data
struct joydata_struct joy_data[JOY_MAX];
struct joystick_read_dev_info_struct joy_read_dev_info[JOY_MAX];
struct joystick_read_axes_info_struct joy_read_axes_info[JOY_AXES];
int32_t joy_read_axes_values[JOY_AXES];
struct adc_data_struct adc_data;
struct adc_map_struct adc_map;

// which joystick are we currently setting up?
uint32_t joy_index = 0;








osbool emulate_acorn_on = TRUE;
osbool emulate_serial_port_on = TRUE;
osbool emulate_joy_on = TRUE;


osbool upcall_claimed = FALSE;
osbool ukswiv_claimed = FALSE;
osbool bytev_claimed = FALSE;









_kernel_oserror *module_initialise(const char *cmd_tail, int32_t podule_base, void *pw)
{
  os_error *error;

  IGNORE(cmd_tail);
  IGNORE(podule_base);

  debug_printf("module_initialise: begin\n");
  g_module_pw = pw;

  // clean up joysticks data structure
  clean_joystick_data();

  // we must have USB
  // num_from_string will give 'swi not known', so swap this for our own error
  int32_t usbdriver_version = 0;
  error = xos_swi_number_from_string("USBDriver_Version", &usbdriver_version);
  if (error) error = (os_error*) &err_nousb;

  // claim upcall vector for acting on usb data received
  if (!error) {
    error = xos_claim(UpCallV, &upcallv_hook, pw);
    if (error)
      debug_printf("module_initialise: Cannot claim UpCallV: %u %s\n", error->errnum, error->errmess);
    else
      upcall_claimed = TRUE;
  }

  // claim ukswi vector for serial port api cmpatibility
  if (!error) {
    error = xos_claim(UKSWIV, (asm_routine)ukswiv_pre_hook, pw);
    if (error)
      debug_printf("module_initialise: Cannot claim UkSWIV: %u %s\n", error->errnum, error->errmess);
    else
     ukswiv_claimed = TRUE;
  }

  // claim byte vector for emulating Acorn I/O podule ADC
  if (!error) {
    error = xos_claim(ByteV, &bytev_hook, pw);
    if (error)
      debug_printf("module_initialise: Cannot claim ByteV: %u %s\n", error->errnum, error->errmess);
    else
      bytev_claimed = TRUE;
  }

  // claim pointer vector for emulating a mouse
  if (!error) {
    error = xos_claim(PointerV, &pointerv_hook, pw);
    if (error)
      debug_printf("module_initialise: Cannot claim PointerV: %u %s\n", error->errnum, error->errmess);
    else
      pointerv_claimed = TRUE;
  }

  // register resources
  #ifndef ROM_MODULE
  if (!error)
    error = xresourcefs_register_files((resourcefs_file_list *) Resources());
  #else
  /* In ROM builds, all resource files live in the Messages module instead */
  #endif

  // build a list of connected joysticks
  // each usb device will be queried to check for suitability
  if (!error) {
    debug_printf("module_initialise: Scanning USB devices...\n");
    usb_scan_devices(&joy_check_device, NULL);
    debug_printf("module_initialise: Scan complete\n");
  }

  /*
    TODO!!!!!!
  if (!error)
    error = free memory from scan-devices
  */

  debug_printf("module_initialise: end\n");

  return (_kernel_oserror *) error;
}





_kernel_oserror *module_swi(int32_t swi_offset, _kernel_swi_regs *r, void *pw)
{
  IGNORE(pw);

  switch(swi_offset)
  {
    case MODULE_SWI(Joystick_Read):
    {
      if (emulate_acorn_on)
        return swi_joystick_read(r);
      else
        return &err_nosuchswi;
      break;
    }

    case MODULE_SWI(Joystick_CalibrateTopRight):
    case MODULE_SWI(Joystick_CalibrateBottomLeft):
    {
      if (emulate_acorn_on)
        return NULL;
      else
        return &err_nosuchswi;
      break;
    }

    case MODULE_SWI(Joystick_Status):
    {
      if (emulate_serial_port_on)
        return swi_joystick_status(r);
      else
        return &err_nosuchswi;
      break;
    }
  }

  // SWI value out of range for module USBJoystick
  return &err_modulebadswi;
}























_kernel_oserror *module_finalise(int32_t fatal, int32_t podule, void *pw)
{
  IGNORE(fatal);
  IGNORE(podule);

  os_error *error = NULL;

  debug_printf("module_finalise: begin");

  if (upcall_claimed == TRUE) {
    error = xos_release(UpCallV, &upcallv_hook, pw);
    if (error) {
      debug_printf("module_finalise: failed to release upcallv, error number %d: %s\n", error->errnum, error->errmess);
    }
  }

  if (ukswiv_claimed == TRUE) {
    error = xos_release(UKSWIV, (asm_routine) ukswiv_pre_hook, pw);
    if (error) {
      debug_printf("module_finalise: failed to release ukswiv, error number %d: %s\n", error->errnum, error->errmess);
    }
  }

  if (bytev_claimed == TRUE) {
    error = xos_release(ByteV, &bytev_hook, pw);
    if (error) {
      debug_printf("module_finalise: failed to release bytev, error number %d: %s\n", error->errnum, error->errmess);
    }
  }

  if (pointerv_claimed == TRUE) {
    error = xos_release(PointerV, &pointerv_hook, pw);
    if (error) {
      debug_printf("module_finalise: failed to release pointerv, error number %d: %s\n", error->errnum, error->errmess);
    }
  }

  // close down any USB file handles we had active
  for (uint32_t i=0; i<JOY_MAX; i++) {
    if (joy_data[i].fp != NULL) {
      error = xosfind_closew(joy_data[i].fp);
      if (error) {
        debug_printf("module_finalise: failed to close joystick %d fp, error number %d: %s\n", i, error->errnum, error->errmess);
      }
      joy_data[i].fp = 0;
    }
  }

  #ifndef ROM_MODULE
  error = xresourcefs_deregister_files((resourcefs_file_list *) Resources());
  if (error) {
    debug_printf("module_finalise: failed to degreister resources, error number %d: %s\n", error->errnum, error->errmess);
  }
  #endif

  // allow module to die
  debug_printf("module_finalise: end (module can die)");
  return NULL;
}


