/* $Id$
 *
 * PROJECT:         ReactOS ISA PnP Bus driver
 * FILE:            isapnp.c
 * PURPOSE:         Driver entry
 * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
 * NOTE:            Parts adapted from linux ISA PnP driver
 * UPDATE HISTORY:
 *      01-05-2001  CSH  Created
 */
#include <isapnp.h>

#ifndef NDEBUG
#define NDEBUG
#endif
#include <debug.h>


#ifdef  ALLOC_PRAGMA

// Make the initialization routines discardable, so that they
// don't waste space

#pragma  alloc_text(init, DriverEntry)


#endif  /*  ALLOC_PRAGMA  */


PUCHAR IsaPnPReadPort;


#define UCHAR2USHORT(v0, v1) \
  ((v1 << 8) | v0)

#define UCHAR2ULONG(v0, v1, v2, v3) \
  ((UCHAR2USHORT(v2, v3) << 16) | UCHAR2USHORT(v0, v1))


#ifndef NDEBUG

struct
{
  PCH Name;
} SmallTags[] = {
  {"Unknown Small Tag"},
  {"ISAPNP_SRIN_VERSION"},
  {"ISAPNP_SRIN_LDEVICE_ID"},
  {"ISAPNP_SRIN_CDEVICE_ID"},
  {"ISAPNP_SRIN_IRQ_FORMAT"},
  {"ISAPNP_SRIN_DMA_FORMAT"},
  {"ISAPNP_SRIN_START_DFUNCTION"},
  {"ISAPNP_SRIN_END_DFUNCTION"},
  {"ISAPNP_SRIN_IO_DESCRIPTOR"},
  {"ISAPNP_SRIN_FL_IO_DESCRIPOTOR"},
  {"Reserved Small Tag"},
  {"Reserved Small Tag"},
  {"Reserved Small Tag"},
  {"Reserved Small Tag"},
  {"ISAPNP_SRIN_VENDOR_DEFINED"},
  {"ISAPNP_SRIN_END_TAG"}
};

struct
{
  PCH Name;
} LargeTags[] = {
  {"Unknown Large Tag"},
  {"ISAPNP_LRIN_MEMORY_RANGE"},
  {"ISAPNP_LRIN_ID_STRING_ANSI"},
  {"ISAPNP_LRIN_ID_STRING_UNICODE"},
  {"ISAPNP_LRIN_VENDOR_DEFINED"},
  {"ISAPNP_LRIN_MEMORY_RANGE32"},
  {"ISAPNP_LRIN_FL_MEMORY_RANGE32"}
};

PCSZ TagName(ULONG Tag, BOOLEAN Small)
{
  if (Small && (Tag <= ISAPNP_SRIN_END_TAG)) {
    return SmallTags[Tag].Name;
  } else if (Tag <= ISAPNP_LRIN_FL_MEMORY_RANGE32){
    return LargeTags[Tag].Name;
  }

  return NULL;
}

#endif

static __inline VOID WriteData(UCHAR Value)
{
  WRITE_PORT_UCHAR((PUCHAR)ISAPNP_WRITE_PORT, Value);
}

static __inline VOID WriteAddress(UCHAR Value)
{
	WRITE_PORT_UCHAR((PUCHAR)ISAPNP_ADDRESS_PORT, Value);
	KeStallExecutionProcessor(20);
}

static __inline UCHAR ReadData(VOID)
{
	return READ_PORT_UCHAR(IsaPnPReadPort);
}

static UCHAR ReadUchar(UCHAR Index)
{
	WriteAddress(Index);
	return ReadData();
}

#if 0
static USHORT ReadUshort(UCHAR Index)
{
	USHORT Value;

	Value = ReadUchar(Index);
	Value = (Value << 8) + ReadUchar(Index + 1);
	return Value;
}

static ULONG ReadUlong(UCHAR Index)
{
	ULONG Value;

	Value = ReadUchar(Index);
	Value = (Value << 8) + ReadUchar(Index + 1);
	Value = (Value << 8) + ReadUchar(Index + 2);
	Value = (Value << 8) + ReadUchar(Index + 3);
	return Value;
}
#endif

static VOID WriteUchar(UCHAR Index, UCHAR Value)
{
	WriteAddress(Index);
	WriteData(Value);
}

#if 0
static VOID WriteUshort(UCHAR Index, USHORT Value)
{
	WriteUchar(Index, Value >> 8);
	WriteUchar(Index + 1, Value);
}

static VOID WriteUlong(UCHAR Index, ULONG Value)
{
	WriteUchar(Index, Value >> 24);
	WriteUchar(Index + 1, Value >> 16);
	WriteUchar(Index + 2, Value >> 8);
	WriteUchar(Index + 3, Value);
}
#endif

static __inline VOID SetReadDataPort(ULONG Port)
{
  IsaPnPReadPort = (PUCHAR)Port;
	WriteUchar(0x00, (UCHAR) (Port >> 2));
	KeStallExecutionProcessor(100);
}

static VOID SendKey(VOID)
{
  ULONG i;
  UCHAR msb;
	UCHAR code;

  /* FIXME: Is there something better? */
	KeStallExecutionProcessor(1000);
	WriteAddress(0x00);
	WriteAddress(0x00);

  code = 0x6a;
	WriteAddress(code);
	for (i = 1; i < 32; i++) {
		msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7;
		code = (code >> 1) | msb;
		WriteAddress(code);
	}
}

/* Place all PnP cards in wait-for-key state */
static VOID SendWait(VOID)
{
	WriteUchar(0x02, 0x02);
}

static VOID SendWake(UCHAR csn)
{
	WriteUchar(ISAPNP_CARD_WAKECSN, csn);
}

#if 0
static VOID SelectLogicalDevice(UCHAR LogicalDevice)
{
	WriteUchar(ISAPNP_CARD_LOG_DEVICE_NUM, LogicalDevice);
}

static VOID ActivateLogicalDevice(UCHAR LogicalDevice)
{
	SelectLogicalDevice(LogicalDevice);
	WriteUchar(ISAPNP_CONTROL_ACTIVATE, 0x1);
	KeStallExecutionProcessor(250);
}

static VOID DeactivateLogicalDevice(UCHAR LogicalDevice)
{
	SelectLogicalDevice(LogicalDevice);
	WriteUchar(ISAPNP_CONTROL_ACTIVATE, 0x0);
	KeStallExecutionProcessor(500);
}
#endif

#define READ_DATA_PORT_STEP 32  /* Minimum is 4 */

static ULONG FindNextReadPort(VOID)
{
	ULONG Port;



	Port = (ULONG)IsaPnPReadPort;

	while (TRUE) {

		Port += READ_DATA_PORT_STEP;



		if (Port > ISAPNP_MAX_READ_PORT)

		{

			return 0;

		}



		/*

		 * We cannot use NE2000 probe spaces for

		 * ISAPnP or we will lock up machines

		 */

		if ((Port < 0x280) || (Port > 0x380))

		{

			return Port;

		}

	}

}

static BOOLEAN IsolateReadDataPortSelect(VOID)
{
  ULONG Port;

	SendWait();
	SendKey();

	/* Control: reset CSN and conditionally everything else too */
	WriteUchar(0x02, 0x05);
	KeStallExecutionProcessor(2000);

	SendWait();
	SendKey();
	SendWake(0x00);

	Port = FindNextReadPort();
  if (Port == 0) {
		SendWait();
		return FALSE;
	}

	SetReadDataPort(Port);
	KeStallExecutionProcessor(1000);
  WriteAddress(0x01);
	KeStallExecutionProcessor(1000);
	return TRUE;
}

/*
 * Isolate (assign uniqued CSN) to all ISA PnP devices
 */
static ULONG IsolatePnPCards(VOID)
{
	UCHAR checksum = 0x6a;
	UCHAR chksum = 0x00;
	UCHAR bit = 0x00;
	ULONG data;
	ULONG csn = 0;
	ULONG i;
	ULONG iteration = 1;

  DPRINT("Called\n");

	IsaPnPReadPort = (PUCHAR)ISAPNP_MIN_READ_PORT;
  if (!IsolateReadDataPortSelect()) {
    DPRINT("Could not set read data port\n");
		return 0;
  }

	while (TRUE) {
		for (i = 1; i <= 64; i++) {
			data = ReadData() << 8;
			KeStallExecutionProcessor(250);
			data = data | ReadData();
			KeStallExecutionProcessor(250);
			if (data == 0x55aa)
				bit = 0x01;
			checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1);
			bit = 0x00;
		}
		for (i = 65; i <= 72; i++) {
			data = ReadData() << 8;
			KeStallExecutionProcessor(250);
			data = data | ReadData();
			KeStallExecutionProcessor(250);
			if (data == 0x55aa)
				chksum |= (1 << (i - 65));
		}
		if ((checksum != 0x00) && (checksum == chksum)) {
			csn++;

			WriteUchar(0x06, (UCHAR) csn);
			KeStallExecutionProcessor(250);
			iteration++;
			SendWake(0x00);
			SetReadDataPort((ULONG)IsaPnPReadPort);
			KeStallExecutionProcessor(1000);
			WriteAddress(0x01);
			KeStallExecutionProcessor(1000);
			goto next;
		}
		if (iteration == 1) {
			IsaPnPReadPort += READ_DATA_PORT_STEP;
      if (!IsolateReadDataPortSelect()) {
        DPRINT("Could not set read data port\n");
				return 0;
      }
		} else if (iteration > 1) {
			break;
		}
next:
		checksum = 0x6a;
		chksum = 0x00;
		bit = 0x00;
	}
	SendWait();
	return csn;
}


static VOID Peek(PUCHAR Data, ULONG Count)
{
	ULONG i, j;
	UCHAR d = 0;

	for (i = 1; i <= Count; i++) {
		for (j = 0; j < 20; j++) {
			d = ReadUchar(0x05);
			if (d & 0x1)
				break;
			KeStallExecutionProcessor(100);
		}
		if (!(d & 0x1)) {
			if (Data != NULL)
				*Data++ = 0xff;
			continue;
		}
		d = ReadUchar(0x04); /* PRESDI */
		if (Data != NULL)
			*Data++ = d;
	}
}


/*
 * Skip specified number of bytes from stream
 */
static VOID Skip(ULONG Count)
{
	Peek(NULL, Count);
}


/*
 * Read one tag from stream
 */
static BOOLEAN ReadTag(PUCHAR Type,
  PUSHORT Size,
  PBOOLEAN Small)
{
	UCHAR tag, tmp[2];

	Peek(&tag, 1);
  if (tag == 0) {
    /* Invalid tag */
    DPRINT("Invalid tag with value 0\n");
#ifndef NDEBUG
    for (;;);
#endif
		return FALSE;
  }

	if (tag & ISAPNP_RESOURCE_ITEM_TYPE) {
    /* Large resource item */
		*Type = (tag & 0x7f);
		Peek(tmp, 2);
		*Size = UCHAR2USHORT(tmp[0], tmp[1]);
    *Small = FALSE;
#ifndef NDEBUG
    if (*Type > ISAPNP_LRIN_FL_MEMORY_RANGE32) {
      DPRINT("Invalid large tag with value 0x%X\n", *Type);
      for (;;);
    }
#endif
	} else {
    /* Small resource item */
		*Type = (tag >> 3) & 0x0f;
		*Size = tag & 0x07;
    *Small = TRUE;
#ifndef NDEBUG
    if (*Type > ISAPNP_SRIN_END_TAG) {
      DPRINT("Invalid small tag with value 0x%X\n", *Type);
      for (;;);
    }
#endif
  }
#if 0
	DPRINT("Tag = 0x%X, Type = 0x%X, Size = %d (%s)\n",
    tag, *Type, *Size, TagName(*Type, *Small));
#endif
  /* Probably invalid data */
  if ((*Type == 0xff) && (*Size == 0xffff)) {
    DPRINT("Invalid data (Type 0x%X  Size 0x%X)\n", *Type, *Size);
    for (;;);
		return FALSE;
  }

	return TRUE;
}


/*
 * Parse ANSI name for ISA PnP logical device
 */
static NTSTATUS ParseAnsiName(PUNICODE_STRING Name, PUSHORT Size)
{
  ANSI_STRING AnsiString;
  UCHAR Buffer[256];
  USHORT size1;

  size1 = (*Size >= sizeof(Buffer)) ? (sizeof(Buffer) - 1) : *Size;

  Peek(Buffer, size1);
  Buffer[size1] = '\0';
  *Size -= size1;

  /* Clean whitespace from end of string */
  while ((size1 > 0) && (Buffer[--size1] == ' '))
    Buffer[size1] = '\0';

  DPRINT("ANSI name: %s\n", Buffer);

  RtlInitAnsiString(&AnsiString, (PCSZ)&Buffer);
  return RtlAnsiStringToUnicodeString(Name, &AnsiString, TRUE);
}


/*
 * Add a resource list to the
 * resource lists of a logical device
 */
static NTSTATUS AddResourceList(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Priority,
  PISAPNP_CONFIGURATION_LIST *NewList)
{
  PISAPNP_CONFIGURATION_LIST List;

  DPRINT("Adding resource list for logical device %d on card %d (Priority %d)\n",
        LogicalDevice->Number,
        LogicalDevice->Card->CardId,
        Priority);

  List = (PISAPNP_CONFIGURATION_LIST)
      ExAllocatePoolWithTag(PagedPool, sizeof(ISAPNP_CONFIGURATION_LIST), TAG_ISAPNP);
  if (!List)
    return STATUS_INSUFFICIENT_RESOURCES;

  RtlZeroMemory(List, sizeof(ISAPNP_CONFIGURATION_LIST));

  List->Priority = Priority;

  InitializeListHead(&List->ListHead);

  InsertTailList(&LogicalDevice->Configuration, &List->ListEntry);

  *NewList = List;

  return STATUS_SUCCESS;
}


/*
 * Add a resource entry to the
 * resource list of a logical device
 */
static NTSTATUS AddResourceDescriptor(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Priority,
  ULONG Option,
  PISAPNP_DESCRIPTOR *Descriptor)
{
  PLIST_ENTRY CurrentEntry;
  PISAPNP_CONFIGURATION_LIST List;
  PISAPNP_DESCRIPTOR d;
  NTSTATUS Status;

  DPRINT("Adding resource descriptor for logical device %d on card %d (%d of %d)\n",
        LogicalDevice->Number,
        LogicalDevice->Card->CardId,
        LogicalDevice->CurrentDescriptorCount,
        LogicalDevice->DescriptorCount);

  d = (PISAPNP_DESCRIPTOR)
    ExAllocatePoolWithTag(PagedPool, sizeof(ISAPNP_DESCRIPTOR), TAG_ISAPNP);
  if (!d)
    return STATUS_NO_MEMORY;

  RtlZeroMemory(d, sizeof(ISAPNP_DESCRIPTOR));

  d->Descriptor.Option = (UCHAR) Option;

  *Descriptor = d;

  CurrentEntry = LogicalDevice->Configuration.Flink;
  while (CurrentEntry != &LogicalDevice->Configuration) {
    List = CONTAINING_RECORD(
      CurrentEntry, ISAPNP_CONFIGURATION_LIST, ListEntry);

    if (List->Priority == Priority) {

      LogicalDevice->ConfigurationSize += sizeof(IO_RESOURCE_DESCRIPTOR);
      InsertTailList(&List->ListHead, &d->ListEntry);
      LogicalDevice->CurrentDescriptorCount++;
      if (LogicalDevice->DescriptorCount <
        LogicalDevice->CurrentDescriptorCount) {
        LogicalDevice->DescriptorCount =
          LogicalDevice->CurrentDescriptorCount;
      }

      return STATUS_SUCCESS;
    }
    CurrentEntry = CurrentEntry->Flink;
  }

  Status = AddResourceList(LogicalDevice, Priority, &List);
  if (NT_SUCCESS(Status)) {
    LogicalDevice->ConfigurationSize += sizeof(IO_RESOURCE_LIST);
    LogicalDevice->CurrentDescriptorCount = 0;
    InsertTailList(&List->ListHead, &d->ListEntry);
  }

  return Status;
}


/*
 * Add IRQ resource to resources list
 */
static NTSTATUS AddIrqResource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[3];
  ULONG irq, i, last = 0;
  BOOLEAN found;
  NTSTATUS Status;

	Peek(tmp, Size);

	irq = UCHAR2USHORT(tmp[0], tmp[0]);

  DPRINT("IRQ bitmask: 0x%X\n", irq);

  found = FALSE;
  for (i = 0; i < 16; i++) {
    if (!found && (irq & (1 << i))) {
      last = i;
      found = TRUE;
    }

    if ((found && !(irq & (1 << i))) || (irq & (1 << i) && (i == 15))) {
      Status = AddResourceDescriptor(LogicalDevice,
        Priority, Option, &Descriptor);
      if (!NT_SUCCESS(Status))
        return Status;
      Descriptor->Descriptor.Type = CmResourceTypeInterrupt;
      Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
      Descriptor->Descriptor.u.Interrupt.MinimumVector = last;

      if ((irq & (1 << i)) && (i == 15))
        Descriptor->Descriptor.u.Interrupt.MaximumVector = i;
      else
        Descriptor->Descriptor.u.Interrupt.MaximumVector = i - 1;

      DPRINT("Found IRQ range %d - %d for logical device %d on card %d\n",
        Descriptor->Descriptor.u.Interrupt.MinimumVector,
        Descriptor->Descriptor.u.Interrupt.MaximumVector,
        LogicalDevice->Number,
        LogicalDevice->Card->CardId);

      found = FALSE;
    }
  }

  return STATUS_SUCCESS;
}

/*
 * Add DMA resource to resources list
 */
static NTSTATUS AddDmaResource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[2];
  ULONG dma, flags, i, last = 0;
  BOOLEAN found;
  NTSTATUS Status;

	Peek(tmp, Size);

	dma = tmp[0];
  flags = tmp[1];

  DPRINT("DMA bitmask: 0x%X\n", dma);

  found = FALSE;
  for (i = 0; i < 8; i++) {
    if (!found && (dma & (1 << i))) {
      last = i;
      found = TRUE;
    }

    if ((found && !(dma & (1 << i))) || (dma & (1 << i) && (i == 15))) {
      Status = AddResourceDescriptor(LogicalDevice,
        Priority, Option, &Descriptor);
      if (!NT_SUCCESS(Status))
        return Status;
      Descriptor->Descriptor.Type = CmResourceTypeDma;
      Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
      Descriptor->Descriptor.u.Dma.MinimumChannel = last;

      if ((dma & (1 << i)) && (i == 15))
        Descriptor->Descriptor.u.Dma.MaximumChannel = i;
      else
        Descriptor->Descriptor.u.Dma.MaximumChannel = i - 1;

      /* FIXME: Parse flags */

      DPRINT("Found DMA range %d - %d for logical device %d on card %d\n",
        Descriptor->Descriptor.u.Dma.MinimumChannel,
        Descriptor->Descriptor.u.Dma.MaximumChannel,
        LogicalDevice->Number,
        LogicalDevice->Card->CardId);

      found = FALSE;
    }
  }

  return STATUS_SUCCESS;
}

/*
 * Add port resource to resources list
 */
static NTSTATUS AddIOPortResource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
#if 0
  DPRINT("I/O port: size 0x%X\n", Size);
  Skip(Size);
#else
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[7];
  NTSTATUS Status;

	Peek(tmp, Size);

  Status = AddResourceDescriptor(LogicalDevice,
    Priority, Option, &Descriptor);
  if (!NT_SUCCESS(Status))
    return Status;
  Descriptor->Descriptor.Type = CmResourceTypePort;
  Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
  Descriptor->Descriptor.u.Port.Length = tmp[6];
  /* FIXME: Parse flags */
  Descriptor->Descriptor.u.Port.Alignment = 0;
  Descriptor->Descriptor.u.Port.MinimumAddress.QuadPart = UCHAR2USHORT(tmp[1], tmp[2]);
  Descriptor->Descriptor.u.Port.MaximumAddress.QuadPart = UCHAR2USHORT(tmp[4], tmp[4]);

  DPRINT("Found I/O port range 0x%X - 0x%X for logical device %d on card %d\n",
    Descriptor->Descriptor.u.Port.MinimumAddress,
    Descriptor->Descriptor.u.Port.MaximumAddress,
    LogicalDevice->Number,
    LogicalDevice->Card->CardId);
#endif
  return STATUS_SUCCESS;
}

/*
 * Add fixed port resource to resources list
 */
static NTSTATUS AddFixedIOPortResource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
#if 0
  DPRINT("Fixed I/O port: size 0x%X\n", Size);
  Skip(Size);
#else
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[3];
  NTSTATUS Status;

	Peek(tmp, Size);

  Status = AddResourceDescriptor(LogicalDevice,
    Priority, Option, &Descriptor);
  if (!NT_SUCCESS(Status))
    return Status;
  Descriptor->Descriptor.Type = CmResourceTypePort;
  Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
  Descriptor->Descriptor.u.Port.Length = tmp[2];
  Descriptor->Descriptor.u.Port.Alignment = 0;
  Descriptor->Descriptor.u.Port.MinimumAddress.QuadPart = UCHAR2USHORT(tmp[0], tmp[1]);
  Descriptor->Descriptor.u.Port.MaximumAddress.QuadPart = UCHAR2USHORT(tmp[0], tmp[1]);

  DPRINT("Found fixed I/O port range 0x%X - 0x%X for logical device %d on card %d\n",
    Descriptor->Descriptor.u.Port.MinimumAddress,
    Descriptor->Descriptor.u.Port.MaximumAddress,
    LogicalDevice->Number,
    LogicalDevice->Card->CardId);
#endif
  return STATUS_SUCCESS;
}

/*
 * Add memory resource to resources list
 */
static NTSTATUS AddMemoryResource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
#if 0
  DPRINT("Memory range: size 0x%X\n", Size);
  Skip(Size);
#else
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[9];
  NTSTATUS Status;

	Peek(tmp, Size);

  Status = AddResourceDescriptor(LogicalDevice,
    Priority, Option, &Descriptor);
  if (!NT_SUCCESS(Status))
    return Status;
  Descriptor->Descriptor.Type = CmResourceTypeMemory;
  Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
  Descriptor->Descriptor.u.Memory.Length = UCHAR2USHORT(tmp[7], tmp[8]) << 8;
  Descriptor->Descriptor.u.Memory.Alignment = UCHAR2USHORT(tmp[5], tmp[6]);
  Descriptor->Descriptor.u.Memory.MinimumAddress.QuadPart = UCHAR2USHORT(tmp[1], tmp[2]) << 8;
  Descriptor->Descriptor.u.Memory.MaximumAddress.QuadPart = UCHAR2USHORT(tmp[3], tmp[4]) << 8;

  DPRINT("Found memory range 0x%X - 0x%X for logical device %d on card %d\n",
    Descriptor->Descriptor.u.Memory.MinimumAddress,
    Descriptor->Descriptor.u.Memory.MaximumAddress,
    LogicalDevice->Number,
    LogicalDevice->Card->CardId);
#endif
  return STATUS_SUCCESS;
}

/*
 * Add 32-bit memory resource to resources list
 */
static NTSTATUS AddMemory32Resource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
#if 0
  DPRINT("Memory32 range: size 0x%X\n", Size);
  Skip(Size);
#else
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[17];
  NTSTATUS Status;

	Peek(tmp, Size);

  Status = AddResourceDescriptor(LogicalDevice,
    Priority, Option, &Descriptor);
  if (!NT_SUCCESS(Status))
    return Status;
  Descriptor->Descriptor.Type = CmResourceTypeMemory;
  Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
  Descriptor->Descriptor.u.Memory.Length =
    UCHAR2ULONG(tmp[13], tmp[14], tmp[15], tmp[16]);
  Descriptor->Descriptor.u.Memory.Alignment =
    UCHAR2ULONG(tmp[9], tmp[10], tmp[11], tmp[12]);
  Descriptor->Descriptor.u.Memory.MinimumAddress.QuadPart =
    UCHAR2ULONG(tmp[1], tmp[2], tmp[3], tmp[4]);
  Descriptor->Descriptor.u.Memory.MaximumAddress.QuadPart =
    UCHAR2ULONG(tmp[5], tmp[6], tmp[7], tmp[8]);

  DPRINT("Found memory32 range 0x%X - 0x%X for logical device %d on card %d\n",
    Descriptor->Descriptor.u.Memory.MinimumAddress,
    Descriptor->Descriptor.u.Memory.MaximumAddress,
    LogicalDevice->Number,
    LogicalDevice->Card->CardId);
#endif
  return STATUS_SUCCESS;
}

/*
 * Add 32-bit fixed memory resource to resources list
 */
static NTSTATUS AddFixedMemory32Resource(
  PISAPNP_LOGICAL_DEVICE LogicalDevice,
  ULONG Size,
  ULONG Priority,
  ULONG Option)
{
#if 0
  DPRINT("Memory32 range: size 0x%X\n", Size);
  Skip(Size);
#else
  PISAPNP_DESCRIPTOR Descriptor;
	UCHAR tmp[17];
  NTSTATUS Status;

	Peek(tmp, Size);

  Status = AddResourceDescriptor(LogicalDevice,
    Priority, Option, &Descriptor);
  if (!NT_SUCCESS(Status))
    return Status;
  Descriptor->Descriptor.Type = CmResourceTypeMemory;
  Descriptor->Descriptor.ShareDisposition = CmResourceShareDeviceExclusive;
  Descriptor->Descriptor.u.Memory.Length =
    UCHAR2ULONG(tmp[9], tmp[10], tmp[11], tmp[12]);
  Descriptor->Descriptor.u.Memory.Alignment =
    UCHAR2ULONG(tmp[5], tmp[6], tmp[7], tmp[8]);
  Descriptor->Descriptor.u.Memory.MinimumAddress.QuadPart =
    UCHAR2ULONG(tmp[1], tmp[2], tmp[3], tmp[4]);
  Descriptor->Descriptor.u.Memory.MaximumAddress.QuadPart =
    UCHAR2ULONG(tmp[1], tmp[2], tmp[3], tmp[4]);

  DPRINT("Found fixed memory32 range 0x%X - 0x%X for logical device %d on card %d\n",
    Descriptor->Descriptor.u.Memory.MinimumAddress,
    Descriptor->Descriptor.u.Memory.MaximumAddress,
    LogicalDevice->Number,
    LogicalDevice->Card->CardId);
#endif
  return STATUS_SUCCESS;
}


/*
 * Parse logical device tag
 */
static PISAPNP_LOGICAL_DEVICE ParseLogicalDevice(
  PISAPNP_DEVICE_EXTENSION DeviceExtension,
  PISAPNP_CARD Card,
  ULONG Size,
  USHORT Number)
{
	UCHAR tmp[6];
	PISAPNP_LOGICAL_DEVICE LogicalDevice;

  DPRINT("Card %d  Number %d\n", Card->CardId, Number);

  Peek(tmp, Size);

  LogicalDevice = (PISAPNP_LOGICAL_DEVICE)ExAllocatePoolWithTag(
    PagedPool, sizeof(ISAPNP_LOGICAL_DEVICE), TAG_ISAPNP);
	if (!LogicalDevice)
		return NULL;

  RtlZeroMemory(LogicalDevice, sizeof(ISAPNP_LOGICAL_DEVICE));

  LogicalDevice->Number = Number;
	LogicalDevice->VendorId = UCHAR2USHORT(tmp[0], tmp[1]);
	LogicalDevice->DeviceId = UCHAR2USHORT(tmp[2], tmp[3]);
	LogicalDevice->Regs = tmp[4];
	LogicalDevice->Card = Card;
	if (Size > 5)
		LogicalDevice->Regs |= tmp[5] << 8;

  InitializeListHead(&LogicalDevice->Configuration);

  ExInterlockedInsertTailList(&Card->LogicalDevices,
    &LogicalDevice->CardListEntry,
    &Card->LogicalDevicesLock);

  ExInterlockedInsertTailList(&DeviceExtension->DeviceListHead,
    &LogicalDevice->DeviceListEntry,
    &DeviceExtension->GlobalListLock);

  DeviceExtension->DeviceListCount++;

	return LogicalDevice;
}


/*
 * Parse resource map for logical device
 */
static BOOLEAN CreateLogicalDevice(PISAPNP_DEVICE_EXTENSION DeviceExtension,
  PISAPNP_CARD Card, USHORT Size)
{
	ULONG number = 0, skip = 0, compat = 0;
	UCHAR type, tmp[17];
  PISAPNP_LOGICAL_DEVICE LogicalDevice;
  BOOLEAN Small;
  ULONG Priority = 0;
  ULONG Option = IO_RESOURCE_REQUIRED;

  DPRINT("Card %d  Size %d\n", Card->CardId, Size);

  LogicalDevice = ParseLogicalDevice(DeviceExtension, Card, Size, (USHORT) number++);
	if (!LogicalDevice)
		return FALSE;

  while (TRUE) {
		if (!ReadTag(&type, &Size, &Small))
			return FALSE;

                if (skip && !(Small && ((type == ISAPNP_SRIN_LDEVICE_ID)
      || (type == ISAPNP_SRIN_END_TAG))))
			goto skip;

    if (Small) {
  		switch (type) {
	  	case ISAPNP_SRIN_LDEVICE_ID:
        if ((Size >= 5) && (Size <= 6)) {
          LogicalDevice = ParseLogicalDevice(
            DeviceExtension, Card, Size, (USHORT)number++);
	        if (!LogicalDevice)
  	        return FALSE;
  				Size = 0;
	  			skip = 0;
		  	} else {
			  	skip = 1;
			  }
        Priority = 0;
        Option = IO_RESOURCE_REQUIRED;
			  compat = 0;
			  break;

		  case ISAPNP_SRIN_CDEVICE_ID:
			  if ((Size == 4) && (compat < MAX_COMPATIBLE_ID)) {
				  Peek(tmp, 4);
				  LogicalDevice->CVendorId[compat] = UCHAR2USHORT(tmp[0], tmp[1]);
				  LogicalDevice->CDeviceId[compat] = UCHAR2USHORT(tmp[2], tmp[3]);
				  compat++;
				  Size = 0;
			  }
			  break;

		  case ISAPNP_SRIN_IRQ_FORMAT:
  			if ((Size < 2) || (Size > 3))
				  goto skip;
			  AddIrqResource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  case ISAPNP_SRIN_DMA_FORMAT:
  			if (Size != 2)
				  goto skip;
        AddDmaResource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  case ISAPNP_SRIN_START_DFUNCTION:
  			if (Size > 1)
				  goto skip;

        if (Size > 0) {
  				Peek(tmp, Size);
          Priority = tmp[0];
				  Size = 0;
          /* FIXME: Maybe use IO_RESOURCE_PREFERRED for some */
          Option = IO_RESOURCE_ALTERNATIVE;
        } else {
          Priority = 0;
          Option = IO_RESOURCE_ALTERNATIVE;
        }

        DPRINT("   Start priority %d   \n", Priority);

        LogicalDevice->CurrentDescriptorCount = 0;

			  break;

  		case ISAPNP_SRIN_END_DFUNCTION:

        DPRINT("   End priority %d   \n", Priority);

	  		if (Size != 0)
		  		goto skip;
        Priority = 0;
        Option = IO_RESOURCE_REQUIRED;
        LogicalDevice->CurrentDescriptorCount = 0;
			  break;

  		case ISAPNP_SRIN_IO_DESCRIPTOR:
	  		if (Size != 7)
		  		goto skip;
			  AddIOPortResource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  case ISAPNP_SRIN_FL_IO_DESCRIPOTOR:
			  if (Size != 3)
  				goto skip;
			  AddFixedIOPortResource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  case ISAPNP_SRIN_VENDOR_DEFINED:
  			break;

		  case ISAPNP_SRIN_END_TAG:
			  if (Size > 0)
  				Skip(Size);
        return FALSE;

		  default:
  			DPRINT("Ignoring small tag of type 0x%X for logical device %d on card %d\n",
          type, LogicalDevice->Number, Card->CardId);
      }
    } else {
      switch (type) {
		  case ISAPNP_LRIN_MEMORY_RANGE:
			  if (Size != 9)
  				goto skip;
			  AddMemoryResource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  case ISAPNP_LRIN_ID_STRING_ANSI:
        ParseAnsiName(&LogicalDevice->Name, &Size);
			  break;

		  case ISAPNP_LRIN_ID_STRING_UNICODE:
  			break;

	  	case ISAPNP_LRIN_VENDOR_DEFINED:
			  break;

		  case ISAPNP_LRIN_MEMORY_RANGE32:
  			if (Size != 17)
				  goto skip;
			  AddMemory32Resource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  case ISAPNP_LRIN_FL_MEMORY_RANGE32:
  			if (Size != 17)
				  goto skip;
			  AddFixedMemory32Resource(LogicalDevice, Size, Priority, Option);
			  Size = 0;
			  break;

		  default:
  			DPRINT("Ignoring large tag of type 0x%X for logical device %d on card %d\n",
          type, LogicalDevice->Number, Card->CardId);
		  }
    }
skip:
  if (Size > 0)
	  Skip(Size);
	}

	return TRUE;
}


/*
 * Parse resource map for ISA PnP card
 */
static BOOLEAN ParseResourceMap(PISAPNP_DEVICE_EXTENSION DeviceExtension,
  PISAPNP_CARD Card)
{
	UCHAR type, tmp[17];
	USHORT size;
  BOOLEAN Small;

  DPRINT("Card %d\n", Card->CardId);

	while (TRUE) {
		if (!ReadTag(&type, &size, &Small))
			return FALSE;

    if (Small) {
		  switch (type) {
		  case ISAPNP_SRIN_VERSION:
  			if (size != 2)
	  			goto skip;
		  	Peek(tmp, 2);
			  Card->PNPVersion = tmp[0];
			  Card->ProductVersion = tmp[1];
			  size = 0;
			  break;

      case ISAPNP_SRIN_LDEVICE_ID:
  			if ((size >= 5) && (size <= 6)) {
				  if (!CreateLogicalDevice(DeviceExtension, Card, size))
  					return FALSE;
				  size = 0;
			  }
			  break;

      case ISAPNP_SRIN_CDEVICE_ID:
        /* FIXME: Parse compatible IDs */
        break;

		  case ISAPNP_SRIN_END_TAG:
			  if (size > 0)
  				Skip(size);
			  return TRUE;

		  default:
  			DPRINT("Ignoring small tag Type 0x%X for Card %d\n", type, Card->CardId);
		  }
    } else {
		  switch (type) {
		  case ISAPNP_LRIN_ID_STRING_ANSI:
  			ParseAnsiName(&Card->Name, &size);
			  break;

      default:
  			DPRINT("Ignoring large tag Type 0x%X for Card %d\n",
          type, Card->CardId);
		  }
    }
skip:
  if (size > 0)
    Skip(size);
  }

  return TRUE;
}


/*
 * Compute ISA PnP checksum for first eight bytes
 */
static UCHAR Checksum(PUCHAR data)
{
	ULONG i, j;
	UCHAR checksum = 0x6a, bit, b;

	for (i = 0; i < 8; i++) {
		b = data[i];
		for (j = 0; j < 8; j++) {
			bit = 0;
			if (b & (1 << j))
				bit = 1;
			checksum = ((((checksum ^ (checksum >> 1)) &
        0x01) ^ bit) << 7) | (checksum >> 1);
		}
	}
	return checksum;
}


/*
 * Build a resource list for a logical ISA PnP device
 */
static NTSTATUS BuildResourceList(PISAPNP_LOGICAL_DEVICE LogicalDevice,
  PIO_RESOURCE_LIST DestinationList,
  ULONG Priority)
{
  PLIST_ENTRY CurrentEntry, Entry;
  PISAPNP_CONFIGURATION_LIST List;
  PISAPNP_DESCRIPTOR Descriptor;
  ULONG i;

  if (IsListEmpty(&LogicalDevice->Configuration))
    return STATUS_NOT_FOUND;

  CurrentEntry = LogicalDevice->Configuration.Flink;
  while (CurrentEntry != &LogicalDevice->Configuration) {
    List = CONTAINING_RECORD(
      CurrentEntry, ISAPNP_CONFIGURATION_LIST, ListEntry);

    if (List->Priority == Priority) {

      DPRINT("Logical device %d  DestinationList 0x%X\n",
        LogicalDevice->Number,
        DestinationList);

      DestinationList->Version = 1;
      DestinationList->Revision = 1;
      DestinationList->Count = LogicalDevice->DescriptorCount;

      i = 0;
      Entry = List->ListHead.Flink;
      while (Entry != &List->ListHead) {
        Descriptor = CONTAINING_RECORD(
          Entry, ISAPNP_DESCRIPTOR, ListEntry);

        DPRINT("Logical device %d  Destination 0x%X(%d)\n",
          LogicalDevice->Number,
          &DestinationList->Descriptors[i],
          i);

        RtlCopyMemory(&DestinationList->Descriptors[i],
          &Descriptor->Descriptor,
          sizeof(IO_RESOURCE_DESCRIPTOR));

        i++;

        Entry = Entry->Flink;
      }

      RemoveEntryList(&List->ListEntry);

      ExFreePool(List);

      return STATUS_SUCCESS;
    }

    CurrentEntry = CurrentEntry->Flink;
  }

  return STATUS_UNSUCCESSFUL;
}


/*
 * Build resource lists for a logical ISA PnP device
 */
static NTSTATUS BuildResourceLists(PISAPNP_LOGICAL_DEVICE LogicalDevice)
{
  ULONG ListSize;
  ULONG Priority;
  ULONG SingleListSize;
  PIO_RESOURCE_LIST p;
  NTSTATUS Status;

  ListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST)
    - sizeof(IO_RESOURCE_LIST)
    + LogicalDevice->ConfigurationSize;

  DPRINT("Logical device %d  ListSize 0x%X  ConfigurationSize 0x%X  DescriptorCount %d\n",
    LogicalDevice->Number, ListSize,
    LogicalDevice->ConfigurationSize,
    LogicalDevice->DescriptorCount);

  LogicalDevice->ResourceLists =
    (PIO_RESOURCE_REQUIREMENTS_LIST)ExAllocatePoolWithTag(
      PagedPool, ListSize, TAG_ISAPNP);
	if (!LogicalDevice->ResourceLists)
		return STATUS_INSUFFICIENT_RESOURCES;

  RtlZeroMemory(LogicalDevice->ResourceLists, ListSize);

  SingleListSize = sizeof(IO_RESOURCE_LIST) +
    (LogicalDevice->DescriptorCount - 1) *
    sizeof(IO_RESOURCE_DESCRIPTOR);

  DPRINT("SingleListSize %d\n", SingleListSize);

  Priority = 0;
  p = &LogicalDevice->ResourceLists->List[0];
  do {
    Status = BuildResourceList(LogicalDevice, p, Priority);
    if (NT_SUCCESS(Status)) {
      p = (PIO_RESOURCE_LIST)((ULONG)p + SingleListSize);
      Priority++;
    }
  } while (Status != STATUS_NOT_FOUND);

  LogicalDevice->ResourceLists->ListSize = ListSize;
  LogicalDevice->ResourceLists->AlternativeLists = Priority + 1;

  return STATUS_SUCCESS;
}


/*
 * Build resource lists for a ISA PnP card
 */
static NTSTATUS BuildResourceListsForCard(PISAPNP_CARD Card)
{
  PISAPNP_LOGICAL_DEVICE LogicalDevice;
  PLIST_ENTRY CurrentEntry;
  NTSTATUS Status;

  CurrentEntry = Card->LogicalDevices.Flink;
  while (CurrentEntry != &Card->LogicalDevices) {
    LogicalDevice = CONTAINING_RECORD(
      CurrentEntry, ISAPNP_LOGICAL_DEVICE, CardListEntry);
    Status = BuildResourceLists(LogicalDevice);
    if (!NT_SUCCESS(Status))
      return Status;
    CurrentEntry = CurrentEntry->Flink;
  }

  return STATUS_SUCCESS;
}


/*
 * Build resource lists for all present ISA PnP cards
 */
static NTSTATUS BuildResourceListsForAll(
  PISAPNP_DEVICE_EXTENSION DeviceExtension)
{
  PLIST_ENTRY CurrentEntry;
  PISAPNP_CARD Card;
  NTSTATUS Status;

  CurrentEntry = DeviceExtension->CardListHead.Flink;
  while (CurrentEntry != &DeviceExtension->CardListHead) {
    Card = CONTAINING_RECORD(
      CurrentEntry, ISAPNP_CARD, ListEntry);
    Status = BuildResourceListsForCard(Card);
    if (!NT_SUCCESS(Status))
      return Status;
    CurrentEntry = CurrentEntry->Flink;
  }

  return STATUS_SUCCESS;
}


/*
 * Build device list for all present ISA PnP cards
 */
static NTSTATUS BuildDeviceList(PISAPNP_DEVICE_EXTENSION DeviceExtension)
{
	ULONG csn;
	UCHAR header[9], checksum;
  PISAPNP_CARD Card;

  DPRINT("Called\n");

	SendWait();
	SendKey();
	for (csn = 1; csn <= 10; csn++) {
		SendWake((UCHAR)csn);
		Peek(header, 9);
		checksum = Checksum(header);

    if (checksum == 0x00 || checksum != header[8])  /* Invalid CSN */
			continue;

		DPRINT("VENDOR: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
			header[0], header[1], header[2], header[3],
			header[4], header[5], header[6], header[7], header[8]);

    Card = (PISAPNP_CARD)ExAllocatePoolWithTag(
      PagedPool, sizeof(ISAPNP_CARD), TAG_ISAPNP);
    if (!Card)
      return STATUS_INSUFFICIENT_RESOURCES;

    RtlZeroMemory(Card, sizeof(ISAPNP_CARD));

		Card->CardId = (USHORT) csn;
		Card->VendorId = (header[1] << 8) | header[0];
		Card->DeviceId = (header[3] << 8) | header[2];
		Card->Serial = (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | header[4];

    InitializeListHead(&Card->LogicalDevices);
    KeInitializeSpinLock(&Card->LogicalDevicesLock);

    ParseResourceMap(DeviceExtension, Card);

    ExInterlockedInsertTailList(&DeviceExtension->CardListHead,
      &Card->ListEntry,
      &DeviceExtension->GlobalListLock);
	}

  return STATUS_SUCCESS;
}


static NTSTATUS
ISAPNPQueryBusRelations(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp,
  PIO_STACK_LOCATION IrpSp)
{
  PISAPNP_DEVICE_EXTENSION DeviceExtension;
  PISAPNP_LOGICAL_DEVICE LogicalDevice;
  PDEVICE_RELATIONS Relations;
  PLIST_ENTRY CurrentEntry;
  NTSTATUS Status = STATUS_SUCCESS;
  ULONG Size;
  ULONG i;

  DPRINT("Called\n");

  DeviceExtension = (PISAPNP_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

  if (Irp->IoStatus.Information) {
    /* FIXME: Another bus driver has already created a DEVICE_RELATIONS
              structure so we must merge this structure with our own */
  }

  Size = sizeof(DEVICE_RELATIONS) + sizeof(Relations->Objects) *
    (DeviceExtension->DeviceListCount - 1);
  Relations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, Size, TAG_ISAPNP);
  if (!Relations)
    return STATUS_INSUFFICIENT_RESOURCES;

  Relations->Count = DeviceExtension->DeviceListCount;

  i = 0;
  CurrentEntry = DeviceExtension->DeviceListHead.Flink;
  while (CurrentEntry != &DeviceExtension->DeviceListHead) {
    LogicalDevice = CONTAINING_RECORD(
      CurrentEntry, ISAPNP_LOGICAL_DEVICE, DeviceListEntry);

    if (!LogicalDevice->Pdo) {
      /* Create a physical device object for the
         device as it does not already have one */
      Status = IoCreateDevice(DeviceObject->DriverObject, 0,
        NULL, FILE_DEVICE_CONTROLLER, 0, FALSE, &LogicalDevice->Pdo);
      if (!NT_SUCCESS(Status)) {
        DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
        ExFreePool(Relations);
        return Status;
      }

      LogicalDevice->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
    }

    /* Reference the physical device object. The PnP manager
       will dereference it again when it is no longer needed */
    ObReferenceObject(LogicalDevice->Pdo);

    Relations->Objects[i] = LogicalDevice->Pdo;

    i++;

    CurrentEntry = CurrentEntry->Flink;
  }

  Irp->IoStatus.Information = (ULONG)Relations;

  return Status;
}


static NTSTATUS
ISAPNPQueryDeviceRelations(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp,
  PIO_STACK_LOCATION IrpSp)
{
  PISAPNP_DEVICE_EXTENSION DeviceExtension;
  NTSTATUS Status;

  DPRINT("Called\n");

  DeviceExtension = (PISAPNP_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

  if (DeviceExtension->State == dsStopped)
    return STATUS_UNSUCCESSFUL;

  switch (IrpSp->Parameters.QueryDeviceRelations.Type) {
    case BusRelations:
      Status = ISAPNPQueryBusRelations(DeviceObject, Irp, IrpSp);
      break;

    default:
      Status = STATUS_NOT_IMPLEMENTED;
  }

  return Status;
}


static NTSTATUS
ISAPNPStartDevice(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp,
  PIO_STACK_LOCATION IrpSp)
{
  PISAPNP_DEVICE_EXTENSION DeviceExtension;
  NTSTATUS Status;
  ULONG NumCards;

  DPRINT("Called\n");

  DeviceExtension = (PISAPNP_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

  if (DeviceExtension->State == dsStarted)
    return STATUS_SUCCESS;

  NumCards = IsolatePnPCards();

  DPRINT("Number of ISA PnP cards found: %d\n", NumCards);

  Status = BuildDeviceList(DeviceExtension);
  if (!NT_SUCCESS(Status)) {
    DPRINT("BuildDeviceList() failed with status 0x%X\n", Status);
    return Status;
  }

  Status = BuildResourceListsForAll(DeviceExtension);
  if (!NT_SUCCESS(Status)) {
    DPRINT("BuildResourceListsForAll() failed with status 0x%X\n", Status);
    return Status;
  }

  DeviceExtension->State = dsStarted;

  return STATUS_SUCCESS;
}


static NTSTATUS
ISAPNPStopDevice(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp,
  PIO_STACK_LOCATION IrpSp)
{
  PISAPNP_DEVICE_EXTENSION DeviceExtension;

  DPRINT("Called\n");

  DeviceExtension = (PISAPNP_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

  if (DeviceExtension->State != dsStopped) {
    /* FIXME: Stop device */
    DeviceExtension->State = dsStopped;
  }

  return STATUS_SUCCESS;
}


static NTSTATUS
STDCALL
ISAPNPDispatchOpenClose(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp)
{
  DPRINT("Called\n");

  Irp->IoStatus.Status = STATUS_SUCCESS;
  Irp->IoStatus.Information = FILE_OPENED;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  return STATUS_SUCCESS;
}


static NTSTATUS
STDCALL
ISAPNPDispatchReadWrite(
  IN PDEVICE_OBJECT PhysicalDeviceObject,
  IN PIRP Irp)
{
  DPRINT("Called\n");

  Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
  Irp->IoStatus.Information = 0;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);

  return STATUS_UNSUCCESSFUL;
}


static NTSTATUS
STDCALL
ISAPNPDispatchDeviceControl(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp)
{
  PIO_STACK_LOCATION IrpSp;
  NTSTATUS Status;

  DPRINT("Called\n");

  Irp->IoStatus.Information = 0;

  IrpSp = IoGetCurrentIrpStackLocation(Irp);
  switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
  default:
    DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
    Status = STATUS_NOT_IMPLEMENTED;
    break;
  }

  if (Status != STATUS_PENDING) {
    Irp->IoStatus.Status = Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
  }

  DPRINT("Leaving. Status 0x%X\n", Status);

  return Status;
}


static NTSTATUS
STDCALL
ISAPNPControl(
  IN PDEVICE_OBJECT DeviceObject,
  IN PIRP Irp)
{
  PIO_STACK_LOCATION IrpSp;
  NTSTATUS Status;

  DPRINT("Called\n");

  IrpSp = IoGetCurrentIrpStackLocation(Irp);
  switch (IrpSp->MinorFunction) {
  case IRP_MN_QUERY_DEVICE_RELATIONS:
    Status = ISAPNPQueryDeviceRelations(DeviceObject, Irp, IrpSp);
    break;

  case IRP_MN_START_DEVICE:
    Status = ISAPNPStartDevice(DeviceObject, Irp, IrpSp);
    break;

  case IRP_MN_STOP_DEVICE:
    Status = ISAPNPStopDevice(DeviceObject, Irp, IrpSp);
    break;

  default:
    DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
    Status = STATUS_NOT_IMPLEMENTED;
    break;
  }

  if (Status != STATUS_PENDING) {
    Irp->IoStatus.Status = Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
  }

  DPRINT("Leaving. Status 0x%X\n", Status);

  return Status;
}


static NTSTATUS
STDCALL
ISAPNPAddDevice(
  IN PDRIVER_OBJECT DriverObject,
  IN PDEVICE_OBJECT PhysicalDeviceObject)
{
  PISAPNP_DEVICE_EXTENSION DeviceExtension;
  PDEVICE_OBJECT Fdo;
  NTSTATUS Status;

  DPRINT("Called\n");

  Status = IoCreateDevice(DriverObject, sizeof(ISAPNP_DEVICE_EXTENSION),
    NULL, FILE_DEVICE_BUS_EXTENDER, FILE_DEVICE_SECURE_OPEN, TRUE, &Fdo);
  if (!NT_SUCCESS(Status)) {
    DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
    return Status;
  }

  DeviceExtension = (PISAPNP_DEVICE_EXTENSION)Fdo->DeviceExtension;

  DeviceExtension->Pdo = PhysicalDeviceObject;

  DeviceExtension->Ldo =
    IoAttachDeviceToDeviceStack(Fdo, PhysicalDeviceObject);

  InitializeListHead(&DeviceExtension->CardListHead);
  InitializeListHead(&DeviceExtension->DeviceListHead);
  DeviceExtension->DeviceListCount = 0;
  KeInitializeSpinLock(&DeviceExtension->GlobalListLock);

  DeviceExtension->State = dsStopped;

  Fdo->Flags &= ~DO_DEVICE_INITIALIZING;

  DPRINT("Done AddDevice\n");

  return STATUS_SUCCESS;
}


NTSTATUS
STDCALL
DriverEntry(
  IN PDRIVER_OBJECT DriverObject,
  IN PUNICODE_STRING RegistryPath)
{
  DbgPrint("ISA Plug and Play Bus Driver\n");

  DriverObject->MajorFunction[IRP_MJ_CREATE] = ISAPNPDispatchOpenClose;
  DriverObject->MajorFunction[IRP_MJ_CLOSE] = ISAPNPDispatchOpenClose;
  DriverObject->MajorFunction[IRP_MJ_READ] = ISAPNPDispatchReadWrite;
  DriverObject->MajorFunction[IRP_MJ_WRITE] = ISAPNPDispatchReadWrite;
  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ISAPNPDispatchDeviceControl;
  DriverObject->MajorFunction[IRP_MJ_PNP] = ISAPNPControl;
  DriverObject->DriverExtension->AddDevice = ISAPNPAddDevice;

  return STATUS_SUCCESS;
}

/* EOF */
