
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "swis.h"
#include "Global/RISCOS.h"
#include "Global/UpCall.h"   // UpCall_DeviceRxDataPresent

#include "usbjoystick.h"
#include "joydev.h"
#include "device.h"

#include "buffer.h"

/* Buffer Manager service-routine reason codes, passed in R0 when we call a
   buffer's service routine directly (see the Buffer Manager documentation). */
#define BufferService_RemoveBlock  3
#define BufferService_UsedSpace    6
#define BufferService_PurgeBuffer  8


int32_t upcallv_hook_handler(_kernel_swi_regs *r, void *pw)
{
  IGNORE(pw);

  // we only care about data received
  if (r->r[0] != UpCall_DeviceRxDataPresent) {
    return VECTOR_PASSON;
  }

  // temporary buffer for holding received data
  uint8_t buffer[JOY_MAX_DATA_LENGTH];
  int fp = (int) (r->r[1]);

  // test our active sticks to see if this is one of ours
  for (uint32_t i=0; i<JOY_MAX; i++) {
    if (joy_data[i].fp == fp) {
      // this is the stick
      joy_data[i].upcalls++;
      joy_data[i].upcalls_delta++;

      // we need to read the bytes into a buffer
      uint32_t read = 0;
      uint32_t offset = joy_data[i].device->report_data_offset;
      uint32_t to_read = joy_data[i].report_length + offset;

      // report_length comes from the connected device's HID report descriptor
      // and isn't bounded against our fixed-size scratch buffer - refuse to
      // read more than it can hold rather than overflow it.
      // no debug_printf here - this runs inside an UpCallV vector handler,
      // and every other vector handler in this codebase deliberately avoids
      // calling it from the hot path (see the commented-out calls in
      // bytev_hook_handler)
      if (to_read > sizeof(buffer)) {
        joy_data[i].bad_reads++;
        joy_data[i].bad_reads_delta++;
        return VECTOR_PASSON;
      }

      buffer_read(joy_data[i].buffer_internal_id, to_read, buffer, &read, joy_data[i].buffer_service_routine, joy_data[i].buffer_workspace);

      // if we have read the correct number of bytes, so copy to stick 'last known good data'
      if (read == to_read) {
        joy_data[i].good_reads++;
        joy_data[i].good_reads_delta++;
        memcpy(joy_data[i].data_buf, buffer, read);
        joystick_decode(i);
      }
      else {
        joy_data[i].bad_reads++;
        joy_data[i].bad_reads_delta++;
      }
      // can stop here, since this rx event cannot also be for another stick!
      // todo: is pass-on right?  should we return claim?
      return VECTOR_PASSON;
    }
  }

  // upcall was not for one of our sticks
  return VECTOR_PASSON;
}





_kernel_oserror* buffer_read(int id, uint32_t to_read, uint8_t *data, uint32_t *read, void *code, void *ws)
{
  if (id == 0)
  {
    *read = 0;
    return NULL;
  }

  // the service routine is BLX'd to, so give the assembler a function-pointer
  // target rather than a plain data pointer (Buffer_InternalInfo handed it back
  // as an address in what our struct holds as a void *). The direct cast
  // draws a "function pointer / non-function object" warning from Norcroft
  // (happens in plenty of other areas of the source tree).
  void (*service)(void) = (void (*)(void)) code;

  // find out the used space (buffer manager 6)
  uint32_t bufused = 0;
  __asm
  {
    MOV      R0,#BufferService_UsedSpace
    MOV      R1,id
    MOV      R12,ws
    BLX      service,{R0,R1,R12},{R2},{LR,PSR}
    MOV      bufused,R2
  }

  // max we can read is the size of the buffer
  if (to_read > bufused)
    to_read = bufused;

  // if the buffer is empty, bail
  if (to_read == 0)
  {
    *read = 0;
    return NULL;
  }

  // perform the read, then flush the buffer
  __asm
  {
    MOV      R0,#BufferService_RemoveBlock
    MOV      R1,id
    MOV      R2,data
    MOV      R3,to_read
    MOV      R12,ws
    BLX      service,{R0-R3,R12},{},{R2,R3,LR,PSR}
    MOV      R0,#BufferService_PurgeBuffer
    BLX      service,{R0,R1,R12},{},{LR,PSR}
  }

  // note how many bytes read from buffer
  *read = to_read;

  return NULL;
}
