/*
    Copyright 2000 (c) by Author,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*
	
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/ia32/Drivers/IOPci.c,v $
    Author(s):             
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 2922 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $      by: $Author: baderma $
*/
#ifdef CONFIG_PCI

#include "IOPci.h"
#include "Tools.h"
#include "Support.h"
#include "SupportAsm.h"
#include "TMHal.h"
#include "MMHal.h"
#include "MemoryLayout.h"
#include "Syscall.h"
#include "Lock.h"
#include "video.h"
#include "Video.h"
#include "TMHalAsm.h"
#include "Keyboard.h"

IODeviceDesc pciFrameworkDesc = {
                                  NULL, NULL,
                                  "pciframework",
                                  (Address)0,
                                  NULL,
                                  -1,
                                  NULL,
                                  0, /* thread id filled in after start */
                                  NULL,
                                  NULL,
                                  NULL,
                                  ioPci_init,
                                  NULL        /* private msg handler */,
                                  FALSE, NULL /* data extension */
                                };

// Global List of found (=Successfully scanned) Pci devices
static List ioPciScannedDevList;

// Global Variable for PCI Bus presence
Boolean ioPciBusPresent = FALSE;

// Global PCI Interrupt Handler Table
static InterruptHandler pciInterruptHandlerTable[MAXNBOFINTERRUPTS][MAXNBOFINTERRUPTSHARERS];
static void* pciInterruptHandlerArgTable[MAXNBOFINTERRUPTS][MAXNBOFINTERRUPTSHARERS];

// Global strings
static char strLocatedAt[] = 				"  Located at                 : bus=%i , dev=%i , func=%i\n";
static char strIDs[] = 						"  Vendor,Device,Revision IDs : 0x%04x , 0x%04x , 0x%02x\n";
static char strClassName[] = 				"  Class Name                 : %s\n";
static char strClass[] = 					"  Class,Subclass,Progr. Int. : 0x%02x , 0x%02x , 0x%02x\n";
static char strSubsysIDs[] = 				"  Subsys VendorID,DeviceID   : 0x%04x , 0x%04x\n";
static char strNoSubsysIDs[] = 				"  Subsys VendorID,DeviceID   : no subsystem\n";
static char strMasterCapableYes[] = 		"  Master Capable             : yes\n";
static char strMasterCapableNo[] = 			"  Master Capable             : no\n";
static char strFastBackToBackCapableYes[] = "  Fast Back-to-Back Capable  : yes\n";
static char strFastBackToBackCapableNo[] = 	"  Fast Back-to-Back Capable  : no\n";
static char strDevselTimingFast[] = 		"  DEVSEL Timing              : Fast\n";
static char strDevselTimingMedium[] = 		"  DEVSEL Timing              : Medium\n";
static char strDevselTimingSlow[] = 		"  DEVSEL Timing              : Slow\n";
static char strDevselTimingReserved[] = 	"  DEVSEL Timing              : Reserved\n";
static char strLatTimerMinGntMaxLat[] = 	"  Lat. Timer,Min Gnt,Max Lat.: %i , %i , %i\n";
static char strCacheLineSize[] = 			"  Cache Line Size            : %i\n";
static char strBistAvailableYes[] = 		"  BIST available             : yes\n";
static char strBistAvailableNo[] = 			"  BIST available             : no\n";
static char strCapabilityListYes[] = 		"  Capability List available  : ";
static char strCapabilityListID0[] =        "Res.,ID=0x0";
static char strCapabilityListID1[] =        "PCI PM Interface,ID=0x1";
static char strCapabilityListID2[] =        "AGP,ID=0x2";
static char strCapabilityListID3[] =        "VPD(Vital Product Data),ID=0x3";
static char strCapabilityListID4[] =        "Slot Id,ID=0x4";
static char strCapabilityListID5[] =        "MSI(Message Signaled Interrupts,ID=0x5";
static char strCapabilityListID6[] =        "CompactPCI Hot Swap,ID=0x6";
static char strCapabilityListIDRes[] =      "Res.,ID>0x6";
static char strCapabilityListNo[] = 		"  Capability List available  : no\n";
static char strIRQNum[] = 					"  IRQ                        : %i\n";
static char strIRQNo[] = 					"  IRQ                        : no interrupt\n";
static char strBaseIO[] = 					"  Base I/O  %i                : 0x%04x-0x%04x (Block size : 0x%03x=%ibyte)\n";
static char strBaseAddr[] = 				"  Base Addr %i                : 0x%08x-0x%08x\n";
static char strBlockSizeMbyte[] = 			"                              (Block size : 0x%08x=%iMbyte,Prefetch.=%i)\n";
static char strBlockSizekbyte[] = 			"                              (Block size : 0x%08x=%ikbyte,Prefetch.=%i)\n";
static char strBlockSizebyte[] = 			"                              (Block size : 0x%08x=%ibyte,Prefetch.=%i)\n";
static char strBaseAddr64[] = 				"  Base Addr64 %i              : 0x%016x-0x%016x\n";
static char strROMBase[] = 					"  ROM Base                   : 0x%08x-0x%08x\n";

static char strPCIDetected[] = "PCI : detected.\n";
static char strPCINotDetected[] = "PCI : not detected!\n";
static char strPCINamesNotAvailable[] = "<pci names not compiled into kernel>";

static char strERRORdevPciPtr[] =  	        "PCI : devPciPtr is NULL\n";
static char strVirtualMemAllocFailed[] = "PCI : Virtual Memory Allocation Error : Couldn't allocate IOPciDeviceDesc\n";
static char strVirtualMemFreeFailed[] = "PCI : Virtual Memory Free Error : Couldn't free last IOPciDeviceDesc\n";
static char strUnknownHeaderType[] = "PCI : Device has unknown Header Type\n";
static char strMaxPCISharersError[] = "PCI : Max num of PCI interrupt sharers reached\n";
static char strNoIntHandlerSpecified[] = "PCI : No Interrupt Handler specified\n";
static char strIntHandlerNotFound[] = "PCI : Can't find the Interrupt Handler to be removed\n";
static char strPCIReservedCaseError[] = "PCI : Reserved case not implemented\n";

static char strBusMasterEnabled[] = "PCI : Bus Mastering enabled for:\n      %s\n";
static char strIncreasingLatTimer[] = "PCI : Increasing Latency Timer of:\n      %s to 64.\n";
static char strBusMasterEnableFailed[] = "PCI : Can't enable Bus Mastering for:\n      %s\n";
static char strBusMasterAlreadyEnabled[] = "PCI : Bus Mastering already enabled for:\n      %s\n";
static char strBusMasterDisabled[] = "PCI : Bus Mastering disabled for:\n      %s\n";
static char strBusMasterDisableFailed[] = "PCI : Can't disable Bus Mastering for:\n      %s\n";
static char strBusMasterAlreadyDisabled[] = "PCI : Bus Mastering already disabled for:\n      %s\n";
static char strIODecoderEnabled[] = "PCI : IO Decoder enabled for:\n      %s\n";
static char strIODecoderEnableFailed[] = "PCI : Can't enable IO Decoder for:\n      %s\n";
static char strIODecoderAlreadyEnabled[] = "PCI : IO Decoder already enabled for:\n      %s\n";
static char strIODecoderDisabled[] = "PCI : IO Decoder disabled for:\n      %s\n";
static char strIODecoderDisableFailed[] = "PCI : Can't disable IO Decoder for:\n      %s\n";
static char strIODecoderAlreadyDisabled[] = "PCI : IO Decoder already disabled for:\n      %s\n";
static char strMemDecoderEnabled[] = "PCI : Memory Mapped IO Decoder enabled for:\n      %s\n";
static char strMemDecoderEnableFailed[] = "PCI : Can't enable Memory Mapped IO Decoder for:\n      %s\n";
static char strMemDecoderAlreadyEnabled[] = "PCI : Memory Mapped IO Decoder already enabled for:\n      %s\n";
static char strMemDecoderDisabled[] = "PCI : Memory Mapped IO Decoder disabled for:\n      %s\n";
static char strMemDecoderDisableFailed[] = "PCI : Can't disable Memory Mapped IO Decoder for:\n      %s\n";
static char strMemDecoderAlreadyDisabled[] = "PCI : Memory Mapped IO Decoder already disabled for:\n      %s\n";
static char strRomDecoderEnabled[] = "PCI : ROM Decoder enabled for:\n      %s\n";
static char strRomDecoderEnableFailed[] = "PCI : Can't enable ROM Decoder for:\n      %s\n";
static char strRomDecoderAlreadyEnabled[] = "PCI : ROM IO Decoder already enabled for:\n      %s\n";
static char strRomDecoderDisabled[] = "PCI : ROM Decoder disabled for:\n      %s\n";
static char strRomDecoderDisableFailed[] = "PCI : Can't disable ROM Decoder for:\n      %s\n";
static char strRomDecoderAlreadyDisabled[] = "PCI : ROM IO Decoder already disabled for:\n      %s\n";

static char strPressSpacebar[] = "press 'spacebar' to view the PCI scan, ESC to continue";
static char strSelectLetter[] =  "select a letter for device details, ESC to quit";
static char strPressESC[] =      "press ESC to return, 'spacebar' to view next device";

#ifdef PCI_VERBOSE
void PCIprintf(char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  vprintf(fmt, ap);
  va_end(ap);
}
#endif

// Decremented in Threads/ia32/TMClock.c, function _TimerISR:
extern int _countdown; // 1Hz decrement clock (each second it decrements by 1)
extern Lock _countdownLock; // You need to lock _countdown  if you want to write a new value,
// for example to reset it.


/***************************************
 * Functions used by the Driver Writer *
 ***************************************/
Boolean ioPciEnableBusMaster(IOPciDeviceDesc * devPciPtr)
{
  u16 cmd;
  u8 lat;

  cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
  if (!(cmd & PCI_COMMAND_MASTER)) // If Bus Master disabled
  {
    pci_write_config_word(devPciPtr, PCI_COMMAND, (cmd | PCI_COMMAND_MASTER));
    cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
    if (cmd & PCI_COMMAND_MASTER)
    {
      PCIprintf(strBusMasterEnabled, devPciPtr->name);
      lat = pci_read_config_byte(devPciPtr, PCI_LATENCY_TIMER);
      if (lat < 16)
      {
        PCIprintf(strIncreasingLatTimer, devPciPtr->name);
        pci_write_config_byte(devPciPtr, PCI_LATENCY_TIMER, 64);
      }
      return TRUE;
    }
    else
    {
      PCIprintf(strBusMasterEnableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If Bus master already enabled -> return TRUE
  {
    PCIprintf(strBusMasterAlreadyEnabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciDisableBusMaster(IOPciDeviceDesc * devPciPtr)
{
  u16 cmd;

  cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
  if (cmd & PCI_COMMAND_MASTER) // If Bus Master enabled
  {
    pci_write_config_word(devPciPtr, PCI_COMMAND, (cmd & ~(PCI_COMMAND_MASTER)));
    cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
    if (!(cmd & PCI_COMMAND_MASTER))
    {
      PCIprintf(strBusMasterDisabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strBusMasterDisableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If Bus master already disabled -> return TRUE
  {
    PCIprintf(strBusMasterAlreadyDisabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciEnableIODecoder(IOPciDeviceDesc * devPciPtr)
{
  u16 cmd;

  cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
  if (!(cmd & PCI_COMMAND_IO)) // If IO Decoder disabled
  {
    pci_write_config_word(devPciPtr, PCI_COMMAND, (cmd | PCI_COMMAND_IO));
    cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
    if (cmd & PCI_COMMAND_IO)
    {
      PCIprintf(strIODecoderEnabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strIODecoderEnableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If IO Decoder already enabled -> return TRUE
  {
    PCIprintf(strIODecoderAlreadyEnabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciDisableIODecoder(IOPciDeviceDesc * devPciPtr)
{
  u16 cmd;

  cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
  if (cmd & PCI_COMMAND_IO) // If IO Decoder enabled
  {
    pci_write_config_word(devPciPtr, PCI_COMMAND, (cmd & ~(PCI_COMMAND_IO)));
    cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
    if (!(cmd & PCI_COMMAND_IO))
    {
      PCIprintf(strIODecoderDisabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strIODecoderDisableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If IO Decoder already disabled -> return TRUE
  {
    PCIprintf(strIODecoderAlreadyDisabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciEnableMemDecoder(IOPciDeviceDesc * devPciPtr)
{
  u16 cmd;

  cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);

  if (!(cmd & PCI_COMMAND_MEMORY)) // If Memory Mapped IO Decoder disabled
  {
    pci_write_config_word(devPciPtr, PCI_COMMAND, (cmd | PCI_COMMAND_MEMORY));
    cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
    if (cmd & PCI_COMMAND_MEMORY)
    {
      PCIprintf(strMemDecoderEnabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strMemDecoderEnableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If Memory Mapped IO Decoder already enabled -> return TRUE
  {
    PCIprintf(strMemDecoderAlreadyEnabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciDisableMemDecoder(IOPciDeviceDesc * devPciPtr)
{
  u16 cmd;

  cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
  if (cmd & PCI_COMMAND_MEMORY) // If Memory Mapped IO Decoder enabled
  {
    pci_write_config_word(devPciPtr, PCI_COMMAND, (cmd & ~(PCI_COMMAND_MEMORY)));
    cmd = pci_read_config_word(devPciPtr, PCI_COMMAND);
    if (!(cmd & PCI_COMMAND_MEMORY))
    {
      PCIprintf(strMemDecoderDisabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strMemDecoderDisableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If Memory Mapped IO Decoder already disabled -> return TRUE
  {
    PCIprintf(strMemDecoderAlreadyDisabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciEnableRomDecoder(IOPciDeviceDesc * devPciPtr)
{
  u32 rom;

  // First Enable Memory Decoder (Memory Decoder used also for ROM Decoding !)
  if (!ioPciEnableMemDecoder(devPciPtr))
    return FALSE;

  rom = pci_read_config_dword(devPciPtr, PCI_ROM_ADDRESS);
  if (!(rom & PCI_ROM_ADDRESS_ENABLE)) // If ROM Decoder disabled
  {
    pci_write_config_dword(devPciPtr, PCI_ROM_ADDRESS, (rom | PCI_ROM_ADDRESS_ENABLE));
    rom = pci_read_config_dword(devPciPtr, PCI_ROM_ADDRESS);
    if (rom & PCI_ROM_ADDRESS_ENABLE)
    {
      PCIprintf(strRomDecoderEnabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strRomDecoderEnableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If ROM Decoder already enabled -> return TRUE
  {
    PCIprintf(strRomDecoderAlreadyEnabled, devPciPtr->name);
    return TRUE;
  }
}


Boolean ioPciDisableRomDecoder(IOPciDeviceDesc * devPciPtr)
{
  u32 rom;

  rom = pci_read_config_dword(devPciPtr, PCI_ROM_ADDRESS);
  if (rom & PCI_ROM_ADDRESS_ENABLE) // If ROM Decoder enabled
  {
    pci_write_config_dword(devPciPtr, PCI_ROM_ADDRESS, (rom & ~(PCI_ROM_ADDRESS_ENABLE)));
    rom = pci_read_config_dword(devPciPtr, PCI_ROM_ADDRESS);
    if (!(rom & PCI_ROM_ADDRESS_ENABLE))
    {
      PCIprintf(strRomDecoderDisabled, devPciPtr->name);
      return TRUE;
    }
    else
    {
      PCIprintf(strRomDecoderDisableFailed, devPciPtr->name);
      return FALSE;
    }
  }
  else // If ROM Decoder already disabled -> return TRUE
  {
    PCIprintf(strRomDecoderAlreadyDisabled, devPciPtr->name);
    return TRUE;
  }
}


#define CONFIG_CMD(devPciPtr, where)   (0x80000000 | (devPciPtr->bus << 16) | (devPciPtr->devfn << 8) | (where & ~3))

u8 pci_read_config_byte(IOPciDeviceDesc *devPciPtr, int where)
{
  __outl(CONFIG_CMD(devPciPtr,where), 0xCF8);
  return(__inb(0xCFC + (where&3)));
}

u16 pci_read_config_word(IOPciDeviceDesc *devPciPtr, int where)
{
  __outl(CONFIG_CMD(devPciPtr,where), 0xCF8);
  return(__inw(0xCFC + (where&2)));
}

u32 pci_read_config_dword(IOPciDeviceDesc *devPciPtr, int where)
{
  __outl(CONFIG_CMD(devPciPtr,where), 0xCF8);
  return(__inl(0xCFC));
}

void pci_write_config_byte(IOPciDeviceDesc *devPciPtr, int where, u8 value)
{
  __outl(CONFIG_CMD(devPciPtr,where), 0xCF8);
  __outb(value, 0xCFC + (where&3));
}

void pci_write_config_word(IOPciDeviceDesc *devPciPtr, int where, u16 value)
{
  __outl(CONFIG_CMD(devPciPtr,where), 0xCF8);
  __outw(value, 0xCFC + (where&2));
}

void pci_write_config_dword(IOPciDeviceDesc *devPciPtr, int where, u32 value)
{
  __outl(CONFIG_CMD(devPciPtr,where), 0xCF8);
  __outl(value, 0xCFC);
}


Boolean ioPciDeviceAttach(IODevice drvPtr)
{
  IOPciDeviceDesc * devPciPtr;
  IOPciDeviceDesc * drvPciPtr;
  u32 mask;
  InterruptHandler interruptHandler;

  if (!ioPciBusPresent)
    return FALSE;

  asm("cli");
  drvPciPtr = (IOPciDeviceDesc*)drvPtr->extension;
  mask = drvPciPtr->mask;
  interruptHandler = drvPciPtr->interruptHandler;

  listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
  while (devPciPtr)
  {
    // Check if device is already attached to a driver:
    if (devPciPtr->devPtr)
    {
      listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
      continue; /* continue with next Device */
    }

    if (mask & PCI_MATCH_VENDORID)
    {
      if (drvPciPtr->vendorID != devPciPtr->vendorID)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_DEVICEID)
    {
      if (drvPciPtr->deviceID != devPciPtr->deviceID)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_CLASSID)
    {
      if (drvPciPtr->classID != devPciPtr->classID)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_SUBCLASSID)
    {
      if (drvPciPtr->subClassID != devPciPtr->subClassID)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_PROGRINTERFACE)
    {
      if (drvPciPtr->progrInterfaceID != devPciPtr->progrInterfaceID)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_HEADERTYPE)
    {
      if (drvPciPtr->headerType != devPciPtr->headerType)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_SUBSYSVENDORID)
    {
      if (drvPciPtr->subsysVendorID != devPciPtr->subsysVendorID)
      {
        listGetNext(ioPciScannedDevList, (void*)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_SUBSYSDEVICEID)
    {
      if (drvPciPtr->subsysDeviceID != devPciPtr->subsysDeviceID)
      {
        listGetNext(ioPciScannedDevList, (void*)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_REVISIONID)
    {
      if (drvPciPtr->revisionID != devPciPtr->revisionID)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_BUS)
    {
      if (drvPciPtr->bus != devPciPtr->bus)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    if (mask & PCI_MATCH_DEVFN)
    {
      if (drvPciPtr->devfn != devPciPtr->devfn)
      {
        listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
        continue; /* continue with next Device */
      }
    }

    devPciPtr->devPtr = drvPtr; // Attach driver<->device
    devPciPtr->mask = mask;
    devPciPtr->interruptHandler = interruptHandler;
    byteCopy(drvPciPtr, devPciPtr, sizeof(IOPciDeviceDesc));

    if (devPciPtr->irqPin > 0) // If irqPin > 0 -> interrupt implemented
      drvPtr->interrupt = devPciPtr->irqLine;
    else
      drvPtr->interrupt = 0;

    ioPciSetInterruptHandler(drvPtr);

    asm("sti");
    return TRUE; // Ok, device attached to driver
  }
  asm("sti");
  return FALSE; // No matching device found for the given driver
}

Boolean ioPciDeviceDetach(IODevice drvPtr)
{
  IOPciDeviceDesc* devPciPtr;
  IOPciDeviceDesc* drvPciPtr;

  asm("cli");
  drvPciPtr = (IOPciDeviceDesc*)drvPtr->extension;
  devPciPtr = drvPciPtr->scanRootCopy;

  if (devPciPtr->devPtr) // If attached
  {
    ioPciRemoveInterruptHandler(drvPtr);
    devPciPtr->interruptHandler = NULL;
    devPciPtr->mask = 0;
    devPciPtr->devPtr = NULL;

    drvPciPtr->scanRootCopy = NULL;

    drvPtr->interrupt = 0;
  }
  asm("sti");
  return TRUE;
}


/**********************************************************
 * Functions used internally to initialize PCI operations *
 **********************************************************/

// The init function for the Main PCI Driver.
// This Driver must be loaded before any other PCI Driver !
Error ioPci_init(IODevice devPtr)
{
  int i;
  int sharer;

  if (devPtr->isInitialised)
    return IO_INITOK;

  asm("cli");
  ioPciBusPresent = ioPciPresenceCheck();
  if (!ioPciBusPresent)
  {
    asm("sti");
    devPtr->isInitialised = FALSE;
    return IO_INITFAILED;
  }

  // Reset the PCI Interrupt Table :
  for (i=0 ; i < MAXNBOFINTERRUPTS ; i++)
  {
    for (sharer=0 ; sharer < MAXNBOFINTERRUPTSHARERS ; sharer++)
    {
      pciInterruptHandlerTable[i][sharer] = NULL;
      pciInterruptHandlerArgTable[i][sharer] = NULL;
    }
  }

  ioPciScannedDevList = listNew();

  ioPciScan();
  ioPciMapMemoryIO();
  asm("sti");

#ifdef PCI_VERBOSE

  ioPciPrintDevList();
#endif

  devPtr->isInitialised = TRUE;

  return IO_INITOK;
}


Error ioPci_read(IODevice devPtr, ThreadId threadId, char* bufPtr, long int* sizePtr)
{
  *sizePtr = 0;
  return IO_READOK;
}


Error ioPci_write(IODevice devPtr, ThreadId threadId, char* bufPtr, long int* sizePtr)
{
  *sizePtr = 0;
  return IO_WRITEOK;
}


void ioPciMainInterruptHandler(IODevice devPtr)
{
  int interrupt;
  int sharer;

  interrupt = devPtr->interrupt % MAXNBOFINTERRUPTS;

  // Call all shared Interrupt Handlers :
  for (sharer=0 ; sharer < MAXNBOFINTERRUPTSHARERS ; sharer++)
  {
    // Simple priority : first arrived, best served !
    if (pciInterruptHandlerTable[interrupt][sharer])
      (pciInterruptHandlerTable[interrupt][sharer])(pciInterruptHandlerArgTable[interrupt][sharer]);
  }
}


Boolean ioPciSetInterruptHandler(IODevice devPtr)
{
  int sharer;
  int interrupt;
  IOPciDeviceDesc* devPciPtr;

  interrupt = devPtr->interrupt % MAXNBOFINTERRUPTS;
  devPciPtr = (IOPciDeviceDesc*)devPtr->extension;

  tmSetInterruptHandler(T_IR_00 + interrupt, (InterruptHandler)ioPciMainInterruptHandler, devPtr);
  // ioLoadDriver (called later) must register the PCI Main Interrupt Handler :
  devPtr->interruptHandler = (InterruptHandler)ioPciMainInterruptHandler;

  if (devPciPtr->interruptHandler != NULL)
  {
    // Find a free place for this shared interrupt :
    for (sharer=0 ; sharer < MAXNBOFINTERRUPTSHARERS ; sharer++)
    {
      if (!pciInterruptHandlerTable[interrupt][sharer])
      {
        pciInterruptHandlerTable[interrupt][sharer] = devPciPtr->interruptHandler;
        pciInterruptHandlerArgTable[interrupt][sharer] = (void*)devPtr;
        break; // Ok found a free place -> break out from the for loop.
      }
      if (sharer == (MAXNBOFINTERRUPTSHARERS - 1))
      {
        ERROR(strMaxPCISharersError);
        return FALSE; // The maximum amount of sharability is reached -> fail !
      }
    }

    // Physically enable the IRQ :
    __endOf_IRQ(T_IR_00 + interrupt);
    __enable_IRQ(T_IR_00 + interrupt);

    return TRUE;
  }
  else
  {
    ERROR(strNoIntHandlerSpecified);
    return FALSE;
  }
}


Boolean ioPciRemoveInterruptHandler(IODevice devPtr)
{
  int sharer;
  int numSharers;
  int interrupt;
  IOPciDeviceDesc* devPciPtr;

  numSharers = 0;
  interrupt = devPtr->interrupt % MAXNBOFINTERRUPTS;
  devPciPtr = (IOPciDeviceDesc*)devPtr->extension;

  if (devPciPtr->interruptHandler != NULL)
  {
    // Find the interrupt sharer to be removed:
    for (sharer=0 ; sharer < MAXNBOFINTERRUPTSHARERS ; sharer++)
    {
      if (pciInterruptHandlerTable[interrupt][sharer] == devPciPtr->interruptHandler)
      {
        pciInterruptHandlerTable[interrupt][sharer] = NULL;
        pciInterruptHandlerArgTable[interrupt][sharer] = NULL;
        break; 	// Ok found and removed the Interrupt Handler,
      }			// -> break out from the for loop.
      if (sharer == (MAXNBOFINTERRUPTSHARERS - 1))
      {
        ERROR(strIntHandlerNotFound);
        return FALSE;
      }
    }

    // Count the number of interrupt sharers left:
    for (sharer=0 ; sharer < MAXNBOFINTERRUPTSHARERS ; sharer++)
    {
      if (pciInterruptHandlerTable[interrupt][sharer])
        ++numSharers;
    }

    // If no sharer left, disable IRQ Line:
    if (numSharers == 0)
    {
      // Physically disable the IRQ :
      __endOf_IRQ(T_IR_00 + interrupt);
      __disable_IRQ(T_IR_00 + interrupt);
    }
    return TRUE;
  }
  else
  {
    ERROR(strNoIntHandlerSpecified);
    return FALSE;
  }
}


Boolean ioPciPresenceCheck(void)
{
  u32 tmp;

  tmp = __inl(0xCF8);  // Store value for later restore
  __outl(0x80000000, 0xCF8); // Enable Config Data Register
  if (__inl(0xCF8) == 0x80000000)
  {
    PCIprintf(strPCIDetected);
    __outl(tmp, 0xCF8); // Restore old value
    return TRUE;
  }
  else
  {
    PCIprintf(strPCINotDetected);
    __outl(tmp, 0xCF8); // Restore old value
    return FALSE;
  }
}


void ioPciReadIrq(IOPciDeviceDesc * devPciPtr)
{
  devPciPtr->irqPin = pci_read_config_byte(devPciPtr, PCI_INTERRUPT_PIN);

  if (devPciPtr->irqPin > 0) /* If irqPin > 0 -> interrupt implemented */
    devPciPtr->irqLine = pci_read_config_byte(devPciPtr, PCI_INTERRUPT_LINE);
  else
    devPciPtr->irqLine = 0;
}


void ioPciReadRomBase(IOPciDeviceDesc * devPciPtr, u8 where)
{
  u32 baseAddr;
  u32 reg;
  u32 blockSize;

  baseAddr = pci_read_config_dword(devPciPtr, where);
  /* Check if this Base Address Register is implemented */
  pci_write_config_dword(devPciPtr, where, 0xfffffffe); /* Write all 1's except bit 0 */
  reg = pci_read_config_dword(devPciPtr, where); /* Read it back */
  if (!reg) /* Check if all 0's */
  {
    devPciPtr->expansionRomCount = 0;	/* Expansion ROM not implemented */
  }
  else
  {
    devPciPtr->expansionRomCount = 1;
    pci_write_config_dword(devPciPtr, where, baseAddr); /* Restore original Value */
    for(blockSize = 2048 ; blockSize <= 16777216 ; blockSize=blockSize*2) /* 16MB=16777216 */
    {
      if (reg & blockSize)
        break; /* Found first writable bit -> block size known */
    }
    devPciPtr->expansionRom.blockSize = blockSize;
    devPciPtr->expansionRom.address = (u32)(baseAddr & 0x01fff800);	/* ROM Addr= bits 24..11 */
    /* address is blockSize aligned */
    /* and ranges from 0 to 32 MB */
  }
}


void ioPciReadBases(IOPciDeviceDesc * devPciPtr, int num)
{
  int pos;
  u32 baseAddr;
  u64 baseAddr64;
  u32 reg;
  u32 regAddr;
  u32 blockSize;

  devPciPtr->ioPortCount = 0;
  devPciPtr->memMapped32Count = 0;
  devPciPtr->memMapped64Count = 0;

  for (pos=0 ; pos < num ; pos++)
  {
    regAddr = PCI_BASE_ADDRESS_0 + (pos << 2);

    baseAddr = pci_read_config_dword(devPciPtr, regAddr);
    /* Check if this Base Address Register is implemented */
    pci_write_config_dword(devPciPtr, regAddr, 0xffffffff); /* Write all 1's */
    reg = pci_read_config_dword(devPciPtr, regAddr); /* Read it back */
    if (!reg) 	  /* Check if all 0's */
      continue; /* this Base Address Register isn't implemented, check the next one */

    pci_write_config_dword(devPciPtr, regAddr, baseAddr); /* Restore original Value */

    if (baseAddr & PCI_BASE_ADDRESS_SPACE) /* First bit indicates the type of Mapping :
      												 	1=Port I/O or 0=memory mapped I/O */
    {
      /* I/O Port Address */

      /* Find first set bit, starting from bit 2 -> 2^(bit position)=Block Size */
      for(blockSize = 4 ; blockSize <= 256 ; blockSize=blockSize*2)
      {
        if (reg & blockSize)
          break; /* Found first writable bit -> block size known */
      }
      devPciPtr->ioPort[devPciPtr->ioPortCount].blockSize = blockSize;
      devPciPtr->ioPort[devPciPtr->ioPortCount].baseAddrNum = pos;
      devPciPtr->ioPort[devPciPtr->ioPortCount].address = (u32)(baseAddr & ~3); /* Filter out the first 2 bits */
      /* address is blockSize aligned */
      devPciPtr->ioPortCount++;
    }
    else
    {
      /* Memory Mapped I/O Address */

      /* Find first set bit, starting from bit 4 -> 2^(bit position)=Block Size */
      for(blockSize = 16 ; blockSize <= 2147483648u ; blockSize=blockSize*2) /* 2GB=2147483648 */
      {
        if (reg & blockSize)
          break; /* Found first writable bit -> block size known */
      }
      switch(baseAddr & PCI_BASE_ADDRESS_MEM_TYPE_MASK) /* Memory Type : baseAddr[2:1] */
      {
      case PCI_BASE_ADDRESS_MEM_TYPE_32 : /* 32 bit address located in lower 4GB */
      case PCI_BASE_ADDRESS_MEM_TYPE_1M : /* Address under 1MB (not allowed starting from PCI 2.2) */
        devPciPtr->memMapped32[devPciPtr->memMapped32Count].blockSize = blockSize;
        devPciPtr->memMapped32[devPciPtr->memMapped32Count].baseAddrNum = pos;
        /* Filter out the first 4 bits : */
        devPciPtr->memMapped32[devPciPtr->memMapped32Count].address = (u32)(baseAddr & ~15);
        if (baseAddr & PCI_BASE_ADDRESS_MEM_PREFETCH) /* If bit 3 is set */
          devPciPtr->memMapped32[devPciPtr->memMapped32Count].prefetchable = 1;
        else
          devPciPtr->memMapped32[devPciPtr->memMapped32Count].prefetchable = 0;
        devPciPtr->memMapped32Count++;
        break;

      case PCI_BASE_ADDRESS_MEM_TYPE_64 :
        /* 64 bit address located over 4GB */
        devPciPtr->memMapped64[devPciPtr->memMapped64Count].blockSize = blockSize;
        devPciPtr->memMapped64[devPciPtr->memMapped64Count].baseAddrNum = pos;
        pos++; /* Next Base Address Register is the High DWord of the 64 bit Address */
        regAddr = PCI_BASE_ADDRESS_0 + (pos << 2);
        baseAddr64 = pci_read_config_dword(devPciPtr, regAddr);
        baseAddr64 = baseAddr64 << 32;
        devPciPtr->memMapped64[devPciPtr->memMapped64Count].address = (baseAddr64 | ((u64)baseAddr & ~15));
        if (baseAddr & PCI_BASE_ADDRESS_MEM_PREFETCH) /* If bit 3 is set */
          devPciPtr->memMapped64[devPciPtr->memMapped64Count].prefetchable = 1;
        else
          devPciPtr->memMapped64[devPciPtr->memMapped64Count].prefetchable = 0;
        devPciPtr->memMapped64Count++;
        break;

      default :
        /* case 11 is reserved */
        ERROR(strPCIReservedCaseError);
        break;
      }
    }
  }
}


void ioPciMapMemoryIO(void)
{
  int i;
  u32 regAddr;
  u32 blockMask;
  u32 blockSizeAligned;
  u32 nextMemMapped32Addr;
  unsigned int numPages;
  IOPciDeviceDesc * devPciPtr;

  nextMemMapped32Addr = (u32)MaxPhysMem();

  listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
  while (devPciPtr)
  {
    for (i=0 ; i<devPciPtr->memMapped32Count ; i++)
    {
      blockMask = (devPciPtr->memMapped32[i]).blockSize - 1;
      //PCIprintf("blockSize : 0x%x , ", (devPciPtr->memMapped32[i]).blockSize);

      // Round Block Size to Page boundary:
      blockSizeAligned = (devPciPtr->memMapped32[i].blockSize + (PAGESIZE - 1)) & ~(PAGESIZE - 1);
      //PCIprintf("blockSizeAligned : 0x%x\n", blockSizeAligned);

      // Transform Block Size in bytes to Block Size in pages:
      numPages = blockSizeAligned >> PAGEBITS;
      //PCIprintf(" numPages : 0x%x , ", numPages);

      // Round nextMemMapped32Addr to blockSizeAligned boundary:
      nextMemMapped32Addr = (nextMemMapped32Addr + (blockSizeAligned - 1)) & ~(blockSizeAligned - 1);

      // Get the next free GDT Selector:
      devPciPtr->memMapped32[i].selector = Get_New_GDTE();
      //PCIprintf("GDT Sel : 0x%x , ", devPciPtr->memMapped32[i].selector);
      // Set_GDTEntry needs numPages-1 and not numPages:
      Set_GDTEntry(devPciPtr->memMapped32[i].selector, nextMemMapped32Addr, numPages, MM_DATA_B5, MM_DATA_B6);

      // Update the new Memory Mapped Address Range:
      regAddr = PCI_BASE_ADDRESS_0 + (devPciPtr->memMapped32[i].baseAddrNum << 2);
      pci_write_config_dword(devPciPtr, regAddr, nextMemMapped32Addr & ~blockMask);
      (devPciPtr->memMapped32[i]).address = nextMemMapped32Addr & ~blockMask;
      //PCIprintf("Address : 0x%x\n", pci_read_config_dword(devPciPtr, regAddr) & ~blockMask);

      // Update nextMemMapped32Addr:
      nextMemMapped32Addr = nextMemMapped32Addr + blockSizeAligned;
    }
    __reset_TLB();

    // To be implemented in future (with 64 bit CPUs).
    //for (i=0 ; i<devPciPtr->memMapped64Count ; i++)
    //{
    //}

    listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
  }
}


void ioPciPrintDeviceSmall(IOPciDeviceDesc * devPciPtr)
{
  if (devPciPtr->irqPin > 0)
    PCIprintf("%s , IRQ=%i\n", devPciPtr->name, devPciPtr->irqLine);
  else
    PCIprintf("%s , No IRQ\n", devPciPtr->name);
}


void ioPciPrintDevice(IOPciDeviceDesc * devPciPtr)
{
  int i;
  u8 nextCapability;

  PCIprintf("%s\n", devPciPtr->name);
  PCIprintf(strLocatedAt, devPciPtr->bus, ((devPciPtr->devfn) & ~7) >> 3, (devPciPtr->devfn) & 7);
  PCIprintf(strIDs, devPciPtr->vendorID, devPciPtr->deviceID, devPciPtr->revisionID);
  PCIprintf(strClassName, devPciPtr->className);
  PCIprintf(strClass, devPciPtr->classID, devPciPtr->subClassID, devPciPtr->progrInterfaceID);

  if (devPciPtr->subsysVendorID)
    PCIprintf(strSubsysIDs, devPciPtr->subsysVendorID, devPciPtr->subsysDeviceID);
  else
    PCIprintf(strNoSubsysIDs);

  if (devPciPtr->masterCapable)
    PCIprintf(strMasterCapableYes);
  else
    PCIprintf(strMasterCapableNo);

  if (devPciPtr->fastBackToBackCapable)
    PCIprintf(strFastBackToBackCapableYes);
  else
    PCIprintf(strFastBackToBackCapableNo);

  switch(devPciPtr->devSelTiming)
  {
  case 00 :
    PCIprintf(strDevselTimingFast);
    break;
  case 01 :
    PCIprintf(strDevselTimingMedium);
    break;
  case 10 :
    PCIprintf(strDevselTimingSlow);
    break;
  default :
    PCIprintf(strDevselTimingReserved);
  }

  PCIprintf(strLatTimerMinGntMaxLat, devPciPtr->latencyTimer,
            devPciPtr->minGnt, devPciPtr->maxLat);
  PCIprintf(strCacheLineSize, devPciPtr->cacheLineSize);
  if (devPciPtr->bist & PCI_BIST_CAPABLE)
    PCIprintf(strBistAvailableYes);
  else
    PCIprintf(strBistAvailableNo);

  if (devPciPtr->capability)
  {
    PCIprintf(strCapabilityListYes);
    nextCapability = devPciPtr->capability;
    while(nextCapability)
    {
      switch(pci_read_config_byte(devPciPtr, nextCapability))
      {
      case 0 :
        PCIprintf(strCapabilityListID0);
        break;
      case 1 :
        PCIprintf(strCapabilityListID1);
        break;
      case 2 :
        PCIprintf(strCapabilityListID2);
        break;
      case 3 :
        PCIprintf(strCapabilityListID3);
        break;
      case 4 :
        PCIprintf(strCapabilityListID4);
        break;
      case 5 :
        PCIprintf(strCapabilityListID5);
        break;
      case 6 :
        PCIprintf(strCapabilityListID6);
        break;
      default :
        PCIprintf(strCapabilityListIDRes);
      }
      nextCapability = pci_read_config_byte(devPciPtr, nextCapability+1);
      if (nextCapability)
        PCIprintf(" ; ");
      else
        PCIprintf("\n");
    }
  }
  else
    PCIprintf(strCapabilityListNo);

  if (devPciPtr->irqPin > 0)
    PCIprintf(strIRQNum, devPciPtr->irqLine);
  else
    PCIprintf(strIRQNo);

  for (i=0 ; i<devPciPtr->ioPortCount ; i++)
  {
    PCIprintf(strBaseIO, i,
              devPciPtr->ioPort[i].address, devPciPtr->ioPort[i].address +
              devPciPtr->ioPort[i].blockSize - 1,
              devPciPtr->ioPort[i].blockSize,
              devPciPtr->ioPort[i].blockSize);
  }
  for (i=0 ; i<devPciPtr->memMapped32Count ; i++)
  {
    PCIprintf(strBaseAddr, i,
              devPciPtr->memMapped32[i].address, devPciPtr->memMapped32[i].address +
              devPciPtr->memMapped32[i].blockSize - 1);
    if (devPciPtr->memMapped32[i].blockSize >= 1024*1024)
    {
      PCIprintf(strBlockSizeMbyte,
                devPciPtr->memMapped32[i].blockSize,
                devPciPtr->memMapped32[i].blockSize >> 20,
                devPciPtr->memMapped32[i].prefetchable);
    }
    else if (devPciPtr->memMapped32[i].blockSize >= 1024)
    {
      PCIprintf(strBlockSizekbyte,
                devPciPtr->memMapped32[i].blockSize,
                devPciPtr->memMapped32[i].blockSize >> 10,
                devPciPtr->memMapped32[i].prefetchable);
    }
    else
    {
      PCIprintf(strBlockSizebyte,
                devPciPtr->memMapped32[i].blockSize,
                devPciPtr->memMapped32[i].blockSize,
                devPciPtr->memMapped32[i].prefetchable);
    }
  }
  for (i=0 ; i<devPciPtr->memMapped64Count ; i++)
  {
    PCIprintf(strBaseAddr64, i,
              devPciPtr->memMapped64[i].address, devPciPtr->memMapped64[i].address +
              devPciPtr->memMapped64[i].blockSize - 1);
    PCIprintf(strBlockSizebyte,
              devPciPtr->memMapped64[i].blockSize,
              devPciPtr->memMapped64[i].blockSize,
              devPciPtr->memMapped64[i].prefetchable);
  }
  if (devPciPtr->expansionRomCount)
  {
    PCIprintf(strROMBase,
              devPciPtr->expansionRom.address, devPciPtr->expansionRom.address +
              devPciPtr->expansionRom.blockSize - 1);
    if (devPciPtr->expansionRom.blockSize >= 1024*1024)
    {
      PCIprintf(strBlockSizeMbyte,
                devPciPtr->expansionRom.blockSize,
                devPciPtr->expansionRom.blockSize >> 20);
    }
    else
    {
      PCIprintf(strBlockSizekbyte,
                devPciPtr->expansionRom.blockSize,
                devPciPtr->expansionRom.blockSize >> 10);
    }
  }
  PCIprintf("\n");
}


void ioPciPrintDevList(void)
{
  IOPciDeviceDesc* devPciPtr;
  int count, countdown;
  char* pCharacter;
  char selectedDevice;

  pCharacter = NULL;
  lock(_countdownLock);
  _countdown = 7;
  unlock(_countdownLock);
  __printstr((23*80+10)*2,0x1E,(unsigned char*)strPressSpacebar);
  while(1)
  {
    if (_countdown < 0)
    {
      ClearScreen();
      return;
    }
    __printchar((23*80+65)*2,0x1E,(unsigned char)('0'+_countdown));
    if (_GetChar(pCharacter))
    {
      if (*pCharacter==' ')
        break;
      else if (*pCharacter==27)
      {
        ClearScreen();
        return;
      }
      else
      {
        __beep();
        ClearScreen();
        __printstr((23*80+10)*2,0x1E,(unsigned char*)strPressSpacebar);
        __printchar((23*80+65)*2,0x1E,(unsigned char)('0'+_countdown));
      }
    }
  }

  while(1)
  {
    ClearScreen();
    count=0;
    listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
    while (devPciPtr)
    {
      PCIprintf("%c. ", count+'a');
      ioPciPrintDeviceSmall(devPciPtr);
      listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
      count++;
    }
    __printstr((23*80+10)*2,0x1E,(unsigned char*)strSelectLetter);
    while(1)
    {
      if (_GetChar(pCharacter))
      {
        if (*pCharacter==27)
        {
          ClearScreen();
          return;
        }
        else if (*pCharacter==' ')
        {
          selectedDevice = 'a';
          listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
          if (devPciPtr)
          {
            ClearScreen();
            PCIprintf("%c. ", selectedDevice);
            ioPciPrintDevice(devPciPtr);
            break;
          }
          else
            ERROR(strERRORdevPciPtr);
        }
        else if ((*pCharacter >= 'a') && (*pCharacter < ('a'+count)))
        {
          selectedDevice = *pCharacter;
          listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
          countdown = *pCharacter - 'a';
          while(countdown)
          {
            listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
            --countdown;
          }
          if (devPciPtr)
          {
            ClearScreen();
            PCIprintf("%c. ", selectedDevice);
            ioPciPrintDevice(devPciPtr);
            break;
          }
          else
            ERROR(strERRORdevPciPtr);
        }
        else
        {
          __beep();
          ClearScreen();
          __printstr((23*80+10)*2,0x1E,(unsigned char*)strSelectLetter);
          count=0;
          listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
          while (devPciPtr)
          {
            PCIprintf("%c. ", count+'a');
            ioPciPrintDeviceSmall(devPciPtr);
            listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
            count++;
          }
        }
      }
    }

    __printstr((23*80+10)*2,0x1E,(unsigned char*)strPressESC);
    while(1)
    {
      if (_GetChar(pCharacter))
      {
        if (*pCharacter==27)
          break;
        else if ((*pCharacter >= 'a') && (*pCharacter < ('a'+count)))
        {
          selectedDevice = *pCharacter;
          listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
          countdown = *pCharacter - 'a';
          while(countdown)
          {
            listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
            --countdown;
          }
          if (devPciPtr)
          {
            ClearScreen();
            PCIprintf("%c. ", selectedDevice);
            ioPciPrintDevice(devPciPtr);
            __printstr((23*80+10)*2,0x1E,(unsigned char*)strPressESC);
          }
          else
            ERROR(strERRORdevPciPtr);
        }
        else if (*pCharacter == ' ')
        {
          listGetNext(ioPciScannedDevList, (void**)&devPciPtr);
          if (!devPciPtr)
          {
            listGetFirst(ioPciScannedDevList, (void**)&devPciPtr);
            selectedDevice = 'a';
          }
          else
            selectedDevice++;
          ClearScreen();
          PCIprintf("%c. ", selectedDevice);
          ioPciPrintDevice(devPciPtr);
          __printstr((23*80+10)*2,0x1E,(unsigned char*)strPressESC);
        }
        else
        {
          __beep();
          ClearScreen();
          PCIprintf("%c. ", selectedDevice);
          ioPciPrintDevice(devPciPtr);
          __printstr((23*80+10)*2,0x1E,(unsigned char*)strPressESC);
        }
      }
    }
  }
}


void ioPciScan(void)
{
  IOPciDeviceDesc * devPciPtr;
  int bus;
  int dev;
  int func;
  int is_multi;
  u32 revIDandClassCode;
  u16 cmd;
  u16 cmdOrig;
  u16 status;

  if (vmAlloc((Address*)&devPciPtr, sizeof(IOPciDeviceDesc)) == VM_ALLOCFAILED )
  {
    ERROR(strVirtualMemAllocFailed);
    return;
  }
  // Start the PCI Bus Scan :
  for (bus = 0 ; bus < 256 ; bus++)
  {
    devPciPtr->bus = bus;
    for (dev = 0 ; dev < 32 ; dev++)
    {
      devPciPtr->devfn = dev << 3;
      is_multi = 0;
      for (func = 0 ; func < 8 ; func++)
      {
        if (func && !is_multi)
          break; // Continue with next Device = Skip all functions of the Slot

        devPciPtr->vendorID = pci_read_config_word(devPciPtr, PCI_VENDOR_ID);
        devPciPtr->deviceID = pci_read_config_word(devPciPtr, PCI_DEVICE_ID);

        if (devPciPtr->vendorID == 0xffff || // Normal device check
            (devPciPtr->vendorID == 0 && devPciPtr->deviceID == 0)) // VMware 2.0 Bug:
          // instead of 0xffff,
          // it returns 0x0000!
        {
          (devPciPtr->devfn)++;
          continue;	// No Function implemented, continue with next one
        }

        devPciPtr->headerType =  pci_read_config_byte(devPciPtr, PCI_HEADER_TYPE);

        revIDandClassCode = pci_read_config_dword(devPciPtr, PCI_CLASS_REVISION);
        devPciPtr->revisionID = revIDandClassCode & 0x000000ff; // Extract the lower 8 bits
        revIDandClassCode = revIDandClassCode >> 8; // Shift out revision ID
        devPciPtr->classID = revIDandClassCode >> 16;
        devPciPtr->subClassID = (revIDandClassCode >> 8) & 255;
        devPciPtr->progrInterfaceID = revIDandClassCode & 255;

        status = pci_read_config_word(devPciPtr, PCI_STATUS);
        if (status & PCI_STATUS_FAST_BACK) // If Device is Fast Back-To-Back Capable
          devPciPtr->fastBackToBackCapable = 1;
        else
          devPciPtr->fastBackToBackCapable = 0;

        devPciPtr->devSelTiming = (status & PCI_STATUS_DEVSEL) >> 9;

        cmdOrig = pci_read_config_word(devPciPtr, PCI_COMMAND); // Read Command reg
        cmd = cmdOrig | PCI_COMMAND_MASTER; // Set Master bit
        pci_write_config_word(devPciPtr, PCI_COMMAND, cmd); // Write modifications back
        cmd = pci_read_config_word(devPciPtr, PCI_COMMAND); // Read Command reg again
        if (cmd & PCI_COMMAND_MASTER) // If Master bit is settable -> Dev is Master Capable
          devPciPtr->masterCapable = 1;
        else
          devPciPtr->masterCapable = 0;
        pci_write_config_word(devPciPtr, PCI_COMMAND, cmdOrig); // Restore Command reg


        devPciPtr->latencyTimer = pci_read_config_byte(devPciPtr, PCI_LATENCY_TIMER);
        devPciPtr->cacheLineSize = pci_read_config_byte(devPciPtr, PCI_CACHE_LINE_SIZE);
        devPciPtr->bist = pci_read_config_byte(devPciPtr, PCI_BIST);

        switch (devPciPtr->headerType & 0x7f) // Extract the first 7 bits
        {
        case PCI_HEADER_TYPE_NORMAL :
          ioPciReadIrq(devPciPtr);
          ioPciReadRomBase(devPciPtr, PCI_ROM_ADDRESS);
          ioPciReadBases(devPciPtr, 6);
          devPciPtr->subsysVendorID = pci_read_config_word(devPciPtr, PCI_SUBSYSTEM_VENDOR_ID);
          devPciPtr->subsysDeviceID = pci_read_config_word(devPciPtr, PCI_SUBSYSTEM_ID);
          devPciPtr->minGnt = pci_read_config_byte(devPciPtr, PCI_MIN_GNT);
          devPciPtr->maxLat = pci_read_config_byte(devPciPtr, PCI_MAX_LAT);
          if (status & PCI_STATUS_CAP_LIST)
            devPciPtr->capability = pci_read_config_byte(devPciPtr, PCI_CAPABILITY_LIST);
          else
            devPciPtr->capability = 0;
          break;

        case PCI_HEADER_TYPE_BRIDGE :
          ioPciReadIrq(devPciPtr);
          ioPciReadRomBase(devPciPtr, PCI_ROM_ADDRESS1);
          ioPciReadBases(devPciPtr, 2);
          devPciPtr->subsysVendorID = 0;
          devPciPtr->subsysDeviceID = 0;
          devPciPtr->minGnt = 0; // Doesn't exist for Bridges
          devPciPtr->maxLat = 0; // Doesn't exist for Bridges
          if (status & PCI_STATUS_CAP_LIST)
            devPciPtr->capability = pci_read_config_byte(devPciPtr, PCI_CAPABILITY_LIST);
          else
            devPciPtr->capability = 0;
          break;

        case PCI_HEADER_TYPE_CARDBUS :
          ioPciReadIrq(devPciPtr);
          ioPciReadBases(devPciPtr, 1);
          devPciPtr->subsysVendorID = pci_read_config_word(devPciPtr, PCI_SUBSYSTEM_VENDOR_ID);
          devPciPtr->subsysDeviceID = pci_read_config_word(devPciPtr, PCI_SUBSYSTEM_ID);
          devPciPtr->minGnt = 0; // Doesn't exist for Bridges
          devPciPtr->maxLat = 0; // Doesn't exist for Bridges
          if (status & PCI_STATUS_CAP_LIST)
            devPciPtr->capability = pci_read_config_byte(devPciPtr, PCI_CB_CAPABILITY_LIST);
          else
            devPciPtr->capability = 0;
          break;
        default :
          ERROR(strUnknownHeaderType);
          break;
        }

        if (!func)
          is_multi = devPciPtr->headerType & 0x80;

        devPciPtr->mask = 0; // The mask has now significance at this point, see later when drivers are loaded
        devPciPtr->devPtr = NULL; // No driver is attached to this device now
        devPciPtr->scanRootCopy = devPciPtr; // This one is the Root Copy !

        // Name the Device
#ifdef CONFIG_PCI_NAMES

        ioPciNameDevice(devPciPtr);
        ioPciNameClass(devPciPtr);
#else

        str_cpy(devPciPtr->name, strPCINamesNotAvailable);
        str_cpy(devPciPtr->className, strPCINamesNotAvailable);
#endif CONFIG_PCI_NAMES

        //PCIprintf("bus:%i , dev:%i , func:%i\n", bus, dev, func);
        listAddAtEnd(ioPciScannedDevList, (void*)devPciPtr ,NULL);
        if (vmAlloc((Address*)&devPciPtr, sizeof(IOPciDeviceDesc)) == VM_ALLOCFAILED )
        {
          ERROR(strVirtualMemAllocFailed);
          return;
        }
        // Restore the bus value:
        devPciPtr->bus = bus;
        // Restore the devfn value and increment by 1:
        devPciPtr->devfn = (dev << 3) + func + 1;
      }
    }
  }
  // Free the last allocated unused PCI Structure:
  if (vmFree((Address)devPciPtr) == VM_FREEFAILED)
    ERROR(strVirtualMemFreeFailed);
}

#endif CONFIG_PCI
