// Copyright Notice
// ================
// BOCHS is Copyright 1994-1999 by Kevin P. Lawton.
//
// BOCHS is commercial software.
//
// For more information, read the file 'LICENSE' included in the bochs
// distribution.  If you don't have access to this file, or have questions
// regarding the licensing policy, the author may be contacted via:
//
//     US Mail:  Kevin Lawton
//               439 Marrett Rd.
//               Lexington, MA 02421-7714
//
//     EMail:    bochs@world.std.com



#include "bochs.h"



bx_g2h_c bx_g2h;



bx_g2h_c::bx_g2h_c(void)
{
  unsigned i;

  for (i=0; i<BX_MAX_G2H_CHANNELS; i++) {
    s.callback[i].f = NULL;
    s.callback[i].used = 0;
    }
}

bx_g2h_c::~bx_g2h_c(void)
{
  // nothing for now
}

  void
bx_g2h_c::init(bx_devices_c *d)
{
  // Reserve a dword port for this interface
  for (Bit32u addr=BX_G2H_PORT; addr<=(BX_G2H_PORT+3); addr++) {
    d->register_io_read_handler(&bx_g2h,
      inp_handler, addr, "g2h");
    d->register_io_write_handler(&bx_g2h,
      outp_handler, addr, "g2h");
    }
  memset(&bx_g2h.s, 0, sizeof(bx_g2h.s));
}


  unsigned
bx_g2h_c::aquire_channel(bx_g2h_callback_t f)
{
  unsigned i;

  for (i=0; i<BX_MAX_G2H_CHANNELS; i++) {
    if (bx_g2h.s.callback[i].used==0) {
      bx_g2h.s.callback[i].f = f;
      bx_g2h.s.callback[i].used = 1;
      return(i);
      }
    }

  bx_printf("g2h: attempt to aquire channel: maxed out\n");
  return(BX_G2H_ERROR); // No more free channels
}

  unsigned
bx_g2h_c::deaquire_channel(unsigned channel)
{
  if ( (channel >= BX_MAX_G2H_CHANNELS) ||
       (bx_g2h.s.callback[channel].used==0) ) {
    bx_panic("g2h: attempt to deaquire channel %u: not aquired\n",
      channel);
    }
  bx_g2h.s.callback[channel].used = 0;
  bx_g2h.s.callback[channel].f = NULL;
  return(0);
}


  // static IO port read callback handler
  // redirects to non-static class handler to avoid virtual functions

  Bit32u
bx_g2h_c::inp_handler(void *this_ptr, Bit32u addr, unsigned io_len)
{
  UNUSED(this_ptr);

  if (addr != BX_G2H_PORT)
    bx_panic("g2h: IO read not aligned on dword boundary.\n");
  if (io_len != 4)
    bx_panic("g2h: IO read not dword.\n");

  bx_panic("g2h: IO read not complete.\n");
  return(0);
}



  void
bx_g2h_c::outp_handler(void *this_ptr, Bit32u addr,
                               Bit32u val32, unsigned io_len)
{
  UNUSED(this_ptr);

  if (addr != BX_G2H_PORT)
    bx_panic("g2h: IO write not aligned on dword boundary.\n");
  if (io_len != 4)
    bx_panic("g2h: IO write not dword.\n");

  if ( (bx_g2h.s.packet_count==0) && (val32!=BX_G2H_MAGIC) ) {
    bx_printf("g2h: IO W: Not magic header.\n");
    return;
    }
  bx_g2h.s.guest_packet[bx_g2h.s.packet_count++] = val32;
  if (bx_g2h.s.packet_count >= BX_G2H_PACKET_SIZE) {
    unsigned channel;

    // Full packet received from guest.  Pass on to the host code.
    channel = bx_g2h.s.guest_packet[1];
    if (channel >= BX_MAX_G2H_CHANNELS) {
      bx_panic("g2h: channel (%u) out of bounds\n", channel);
      }
    if (bx_g2h.s.callback[channel].used==0) {
      bx_panic("g2h: channel (%u) not active\n", channel);
      }
    bx_g2h.s.callback[channel].f(&bx_g2h.s.guest_packet);
    bx_g2h.s.packet_count = 0; // Ready for next packet
    }
}
