/*
 * DANG_BEGIN_MODULE
 *
 *
 *
 * DANG_END_MODULE
 *
 * DANG_BEGIN_CHANGELOG
 *
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 *
 * $Log: $
 *
 * DANG_END_CHANGELOG
 */

#include "dosemu.h"
#include "base/memory.h"
#include "base/termio.h"
#include "base/keyb.h"
#include "base/mouse.h"
#include "base/int.h"
#include "base/pic.h"
#include "base/ioctlq.h"
#include "arch/signals.h"

#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/ioctl.h>

#ifdef CONFIG_EMUMODULE
#include "arch/emusys.h"
#endif

#if defined(CONFIG_SIG) && defined(CONFIG_EMUMODULE)
static int SillyG_pendind_irq_bits=0;
int SillyG_do_irq(void)
{
  int irq=10, ret;
  ret = do_irq();
  SillyG_pendind_irq_bits &= ~(1 << irq);
  get_and_reset_irq(irq);
  return ret;
}
#endif

#if defined(CONFIG_SIG)
extern SillyG_t *SillyG;

static inline void process_interrupt(SillyG_t *sg)
{
  int irq;

  if ((irq = sg->irq) != 0) {
    h_printf("INTERRUPT: 0x%02x\n", irq);
    pic_request(pic_irq_list[irq]);
  }
}
#endif

void io_select(fd_set fds)
{
  static int selrtn;
  static struct timeval tvptr;

#if defined(CONFIG_SIG) && defined(CONFIG_EMUMODULE)
  if (SillyG) {
    int irq_bits = get_irq_bits() & ~SillyG_pendind_irq_bits;
    if (irq_bits) {
      SillyG_t *sg=SillyG;
      while (sg->fd) {
        if (irq_bits & (1 << sg->irq)) {
          SillyG_pendind_irq_bits |= 1 << sg->irq;
          h_printf("SIG: We have an interrupt\n");
          process_interrupt(sg);
        }
        sg++;
      }
    }
  }
#endif

  tvptr.tv_sec=0L;
  tvptr.tv_usec=0L;

  while (((selrtn = select(25, &fds, NULL, NULL, &tvptr)) == -1) &&
	 (errno == EINTR)) {
    tvptr.tv_sec=0L;
    tvptr.tv_usec=0L;
    g_printf("WARNING: interrupted io_select: %s\n", strerror(errno));
  }

  switch (selrtn) {
    case 0:			/* none ready, nothing to do :-) */
      return;

    case -1:			/* error (not EINTR) */
      error("ERROR: bad io_select: %s\n", strerror(errno));
      break;

    default:			/* has at least 1 descriptor ready */
#if defined(CONFIG_SIG) && !defined(CONFIG_EMUMODULE)
      if (SillyG) {
        SillyG_t *sg=SillyG;
        while (sg->fd) {
	  if (FD_ISSET(sg->fd, &fds)) {
	    h_printf("SIG: We have an interrupt\n");
	    process_interrupt(sg);
	  }
	  sg++;
	}
      }
#endif
      if (mice->intdrv || mice->type == MOUSE_PS2)
	if (FD_ISSET(mice->fd, &fds)) {
          m_printf("MOUSE: We have data\n");
          pic_request(PIC_IMOUSE);
	}
      if (FD_ISSET(kbd_fd, &fds)) {
	keyb_get_keys();
      }
      break;
    }
#if 0
#ifdef CONFIG_IPX
    pic_request(16);
#endif
#endif
}

/*
 * DANG_BEGIN_FUNCTION io_select_init
 * 
 * description:
 * Initialize fd_sets to NULL for both SIGIO and NON-SIGIO.
 * 
 * DANG_END_FUNCTION
 */
void io_select_init(void)
{
  FD_ZERO(&fds_sigio);	/* initialize both fd_sets to 0 */
  FD_ZERO(&fds_no_sigio);
}

/*
 * DANG_BEGIN_FUNCTION io_select_add
 * 
 * arguments: fd - File handle to add to select statment. want_sigio -
 * Specifiy whether you want SIGIO (1) if it's available, or not (0).
 * 
 * description: Add file handle to one of 2 select FDS_SET's depending on
 * whether the kernel can handle SIGIO.
 * 
 * DANG_END_FUNCTION
 */
void io_select_add(int new_fd, u_char want_sigio)
{
  if (use_sigio && want_sigio) {
    int flags = fcntl(new_fd, F_GETFL);
    fcntl(new_fd, F_SETOWN, getpid());
    fcntl(new_fd, F_SETFL, flags | use_sigio);
    FD_SET(new_fd, &fds_sigio);
    g_printf("GEN: fd=%d gets SIGIO\n", new_fd);
  } else {
    FD_SET(new_fd, &fds_no_sigio);
    g_printf("GEN: fd=%d does not get SIGIO\n", new_fd);
    not_use_sigio++;
  }
}

/*
 * DANG_BEGIN_FUNCTION io_select_remove
 * 
 * arguments: fd - File handle to remove from select statment. used_sigio -
 * Specifiy whether you used SIGIO (1) if it's available, or not (0).
 * 
 * description: Remove a file handle from one of 2 select FDS_SET's depending
 * on whether the kernel can handle SIGIO.
 * 
 * DANG_END_FUNCTION
 */
void io_select_remove(int new_fd, u_char used_sigio)
{
  if (use_sigio && used_sigio) {
    int flags = fcntl(new_fd, F_GETFL);
    fcntl(new_fd, F_SETOWN, NULL);
    fcntl(new_fd, F_SETFL, flags & ~(use_sigio));
    FD_CLR(new_fd, &fds_sigio);
    g_printf("GEN: fd=%d removed from select SIGIO\n", new_fd);
  } else {
    FD_CLR(new_fd, &fds_no_sigio);
    g_printf("GEN: fd=%d removed from select\n", new_fd);
    not_use_sigio++;
  }
}

int ioctl_execute(int fd, int req, int param3)
{
  int tmp;

  if (in_sighandler && in_ioctl) {
    k_printf("KBD: ioctl_execute(): in ioctl %d 0x%04x 0x%04x.\nqueuing: %d \
0x%04x 0x%04x\n", curi.fd, curi.req, curi.param3, fd, req, param3);
    ioctl_queue(fd, req, param3);
    errno = EDEADLOCK;
#ifdef CONFIG_SYNC_ALOT
    fflush(stdout);
    sync();			/* for safety */
#endif
    return -1;
  } else {
    in_ioctl = 1;
    curi.fd = fd;
    curi.req = req;
    curi.param3 = param3;
    if (iq.queued) {
      k_printf("KBD: detected queued ioctl in ioctl_execute(): %d 0x%04x \
0x%04x\n", iq.fd, iq.req, iq.param3);
    }
    k_printf("KBD: IOCTL fd=0x%x, req=0x%x, param3=0x%x\n", fd, req, param3);
    tmp = ioctl(fd, req, param3);
    in_ioctl = 0;
    return tmp;
  }
}

int ioctl_queue(int fd, int req, int param3)
{
  if (iq.queued) {
    error("ioctl queue full: %d 0x%04x 0x%04x\n", iq.fd, iq.req, iq.param3);
    return 1;
  }
  iq.fd = fd;
  iq.req = req;
  iq.param3 = param3;
  iq.queued = 1;
  
  return 0;			/* success */
}

void ioctl_queue_run(void)
{
  if (iq.queued) {
    iq.queued = 0;
    ioctl_execute(iq.fd, iq.req, iq.param3);
  }
}
