#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/nodes.h>
#include <exec/resident.h>
#include <exec/errors.h>
#include <libraries/expansionbase.h>
#include <libraries/configvars.h>
#include <devices/trackdisk.h>
#include <hardware/intbits.h>

/*
 * Set SysBase to a local variable, that loads directly from 4 when it
 * has to be reloaded
 */
#define BASE_EXT_DECL
#define BASE_NAME (*(void **)4)
#include <inline/exec.h>

#include "device.h"

struct int_data {
  struct Task *sig_task;
  long sig_mask;
  long signal;
  volatile ubyte *board;
};

/*
 * interrupt handlers for the device. You can have "real" interrupts
 * or have those functions poll for the event. You should normally use
 * real interrupts. (besides, the pollin on the 2091 won't work, because
 * I don't have documentation for it:-()
 */

extern ulong read_scsi_reg (volatile ubyte *board, ulong reg_num);

/* only needed, if the code is in assembler.. (no more:-)) */
extern int int_code ();

void
install_int_handler (struct scsi_dev *dev)
{
#ifdef REAL_INT
  dev->id_sig_task = FindTask(0);
  dev->id_signal = AllocSignal(-1);
  dev->id_sig_mask = 1 << dev->id_signal;
  dev->id_board = (volatile ubyte *)dev->sc_base;
  
  /* has to be in front of hddisk.device or scsi.device
   * this can be reset to 0 when we never again will "overlay" a running
   * device :-))) */
  dev->sc_iv.is_Node.ln_Pri = 30;
  dev->sc_iv.is_Node.ln_Type = NT_INTERRUPT;
  dev->sc_iv.is_Node.ln_Name = SCSI_NAME;
  
  dev->sc_iv.is_Code = (VOID (*))int_code;
  dev->sc_iv.is_Data = (APTR) &dev->id_sig_task;
  
  AddIntServer(INTB_PORTS, &dev->sc_iv);
#endif
}

void
remove_int_handler (struct scsi_dev *dev)
{
#ifdef REAL_INT
  RemIntServer(INTB_PORTS, &dev->sc_iv);
  FreeSignal(dev->id_signal);
#endif
}

void
wait_interrupt (struct scsi_dev *dev)
{
#ifdef REAL_INT
  Wait (dev->id_sig_mask);
#else
  while (!(board[A2090_CNTL_LO] & INT_FOLLOW)) ;
  read_scsi_reg (board, WD_SCSI_STATUS);
#endif
}

void
clear_interrupt (struct scsi_dev *dev)
{
#ifdef REAL_INT
  SetSignal(0, dev->id_sig_mask);
#endif
}

#ifdef REAL_INT
#ifdef A2090
static int
int_code ()
{
  register struct int_data *id_pass asm("a1");
  register struct int_data *idi = id_pass;
  int i; 

  if (idi->board[A2090_CNTL_LO] & INT_FOLLOW)
    {
      read_scsi_reg (idi->board, WD_SCSI_STATUS);
      Signal (idi->sig_task, idi->sig_mask);
/* for (i=0; i<10; i++) *(unsigned short *)0xdff180 = 0; */
      return 1;
    }
  return 0;
}
#endif
#ifdef A2091
static int
int_code ()
{
  register struct int_data *id_pass asm("a1");
  register struct int_data *idi = id_pass;  /* id_pass;*/
  int i; 

/*  for (i=0; i<10; i++) *(unsigned short *)0xdff180 = 0; */

  if (idi->board[0x41] & (1 << 4))
    {
      if (idi->board[0x41] & (1 << 5)) *(ushort *)&idi->board[0xe4] = 0;

      if (idi->board[0x41] & (1 << 6))
	{
	  read_scsi_reg (idi->board, WD_SCSI_STATUS);

	  if (idi->board[0xa3] & (1 << 5)) idi->board[0xa7] = 0;
	  Signal(idi->sig_task, idi->sig_mask);
	}
      return 1;
    }
  return 0;
}
#endif
#endif
