Index: trunk/src/Makefile
===================================================================
--- trunk/src/Makefile	(revision 12)
+++ 	(revision )
@@ -1,107 +1,0 @@
-###############################################################################
-# Makefile - makefile for os2ahci driver
-#
-# Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
-# Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-
-###############################################################################
-# Tool Chain
-
-AS         = ml
-CC         = cl
-LD         = link
-
-AFLAGS     = -c -nologo
-CFLAGS     = -c -nologo -Asnw -W2 -G2ms -Zlpd -Fc -Zi
-LFLAGS     = /noe /nod /packd /a:16 /batch /map /line 
-
-###############################################################################
-# Environment
-
-# main path to OS/2 DDK; this needs to be set before this makefile will work
-DDK          = i:\ddk
-
-# main path to 16 bit C compiler
-CC16         = i:\c600
-
-CC_INCLUDE   = -I$(DDK)\base\h \
-               -I$(DDK)\base\ibmh \
-	       -I$(DDK)\base\src\dev\dasd\diskh \
-               -I$(CC16)\include
-
-AS_INCLUDE   = -I$(DDK)\base\inc \
-               -I$(DDK)\base\src\dev\dasd\diskinc
-
-LIB_DIRS     = $(DDK)\base\lib\ \
-	       $(DDK)\base\src\dev\dasd\devhlp\ \
-               $(CC16)\lib\ \
-
-
-###############################################################################
-# Main dependencies
-
-TARGET   = os2ahci.add
-
-LIBS     = addcalls dhcalls doscalls slibcep rmcalls
-
-SRCS     = init.asm libc.c os2ahci.c pci.c ahci.c ata.c atapi.c ctxhook.c
-
-OBJS     = init.obj libc.obj os2ahci.obj pci.obj ahci.obj ata.obj atapi.obj \
-           ctxhook.obj
-
-INCS     = os2ahci.h ahci.h
-
-
-all: $(TARGET)
-
-clean:
-	rm -f $(OBJS) $(TARGET)
-
-###############################################################################
-# Object/source dependencies
-
-init.obj:     init.asm   Makefile
-
-libc.obj:     libc.c     Makefile $(INCS)
-
-os2ahci.obj:  os2ahci.c  Makefile $(INCS)
-
-pci.obj:      pci.c      Makefile $(INCS)
-
-ahci.obj:     ahci.c     Makefile $(INCS) ata.h atapi.h
-
-ata.obj:      ata.c      Makefile $(INCS) ata.h
-
-atapi.obj:    atapi.c    Makefile $(INCS) ata.h atapi.h
-
-ctxhook.obj:  ctxhook.c  Makefile $(INCS) ata.h atapi.h
-
-###############################################################################
-# Action definitions (compile/link commands)
-
-.asm.obj:
-	$(AS) $(AFLAGS) $(AS_INCLUDE) $*.asm
-
-.c.obj:
-	$(CC) $(CFLAGS) $(CC_INCLUDE) $*.c
-
-$(TARGET): $(OBJS) os2ahci.def Makefile
-	$(LD) $(LFLAGS) $(OBJS),$(TARGET),$*.map,$(LIB_DIRS) $(LIBS),$*.def
-	mapsym os2ahci
-
-
Index: trunk/src/ahci.c
===================================================================
--- trunk/src/ahci.c	(revision 12)
+++ 	(revision )
@@ -1,1482 +1,0 @@
-/******************************************************************************
- * ahci.c - ahci hardware access functions
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-#include "ata.h"
-#include "atapi.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* produce ata/atapi function pointer with the given func name */
-#define cmd_func(iorb, func)  ad_infos[iorb_unit_adapter(iorb)].     \
-                                 ports[iorb_unit_port(iorb)].        \
-                                  devs[iorb_unit_device(iorb)].atapi \
-                              ? atapi_##func : ata_##func
-
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* -------------------------- function prototypes -------------------------- */
-
-static void ahci_setup_device     (AD_INFO *ai, int p, int d, u16 *id_buf);
-static void _far timeout_callback (ULONG timer_handle, ULONG p1, ULONG p2);
-
-/* ------------------------ global/static variables ------------------------ */
-
-/* Initial driver status flags indexed by the board_* constants in os2ahci.h
- *
- * NOTE: The Linux AHCI driver uses a combination of board-specific quirk
- *       flags and overriding certain libata service functions to handle
- *       adapter flaws. However, there were only three overrides at the time
- *       os2ahci was written, one for hard adapter resets and two for port
- *       resets, and we can easily implement those within the corresponding
- *       reset handlers. If this becomes more complex, this array of flags
- *       should be converted into a structure array which contains function
- *       pointers to all handler functions which may need to be overridden.
- */
-u16 initial_flags[] = {
-  0,                                        /* board_ahci */
-  AHCI_HFLAG_NO_NCQ |                       /* board_ahci_vt8251 */
-    AHCI_HFLAG_NO_PMP,
-  AHCI_HFLAG_IGN_IRQ_IF_ERR,                /* board_ahci_ign_iferr */
-  AHCI_HFLAG_IGN_SERR_INTERNAL |            /* board_ahci_sb600 */
-    AHCI_HFLAG_NO_MSI |
-    AHCI_HFLAG_SECT255 |
-    AHCI_HFLAG_32BIT_ONLY,
-  AHCI_HFLAG_NO_NCQ |                       /* board_ahci_mv */
-    AHCI_HFLAG_NO_MSI |
-    AHCI_HFLAG_MV_PATA |
-    AHCI_HFLAG_NO_PMP,
-  AHCI_HFLAG_IGN_SERR_INTERNAL,             /* board_ahci_sb700 */
-  AHCI_HFLAG_YES_NCQ,                       /* board_ahci_mcp65 */
-  AHCI_HFLAG_NO_PMP,                        /* board_ahci_nopmp */
-  AHCI_HFLAG_YES_NCQ,                       /* board_ahci_yesncq */
-  AHCI_HFLAG_NO_SNTF,                       /* board_ahci_nosntf */
-};
-
-/* IRQ levels for stub interrupt handlers. OS/2 calls interrupt handlers
- * without passing the IRQ level, yet it expects the interrupt handler to
- * know the IRQ level for EOI processing. Thus we need multiple interrupt
- * handlers, one for each IRQ, and some mapping from the interrupt handler
- * index to the corresponding IRQ.
- */
-static u16      irq_map[MAX_AD];   /* IRQ level for each stub IRQ func */
-static int      irq_map_cnt;       /* number of IRQ stub funcs used */
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * Interrupt handlers. Those are stubs which call the real interrupt handler
- * with the IRQ level as parameter. This mapping is required because OS/2
- * calls interrupt handlers without any parameters, yet expects them to know
- * which IRQ level to complete when calling DevHelp_EOI().
- *
- * This array of functions needs to be extended when increasing MAX_AD.
- */
-#if MAX_AD > 8
-#error must extend irq_handler_xx and irq_handlers[] when increasing MAX_AD
-#endif
-
-/* Macro to call AHCI interrupt handler and set/clear carry flag accordingly.
- * We need to set the carry flag if the interrupt was not handled. This is
- * done by shifting the return value of ahci_intr() to the right, implying
- * bit 0 will be set when the interrupt was not handled.
- */
-#define call_ahci_intr(i)   return(ahci_intr(irq_map[i]) >> 1)
-
-static USHORT _far irq_handler_00(void)  { call_ahci_intr(0); }
-static USHORT _far irq_handler_01(void)  { call_ahci_intr(1); }
-static USHORT _far irq_handler_02(void)  { call_ahci_intr(2); }
-static USHORT _far irq_handler_03(void)  { call_ahci_intr(3); }
-static USHORT _far irq_handler_04(void)  { call_ahci_intr(4); }
-static USHORT _far irq_handler_05(void)  { call_ahci_intr(5); }
-static USHORT _far irq_handler_06(void)  { call_ahci_intr(6); }
-static USHORT _far irq_handler_07(void)  { call_ahci_intr(7); }
-
-PFN irq_handlers[] = {
-  (PFN) irq_handler_00, (PFN) irq_handler_01, (PFN) irq_handler_02,
-  (PFN) irq_handler_03, (PFN) irq_handler_04, (PFN) irq_handler_05,
-  (PFN) irq_handler_06, (PFN) irq_handler_07
-};
-
-/******************************************************************************
- * Save BIOS configuration of AHCI adapter. As a side effect, this also saves
- * generic configuration information which we may have to restore after an
- * adapter reset.
- *
- * NOTE: This function also saves working copies of the CAP and CAP2 registers
- *       as well as the initial port map in the AD_INFO structure after
- *       removing features which are known to cause trouble on this specific
- *       piece of hardware.
- */
-int ahci_save_bios_config(AD_INFO *ai)
-{
-  int ports;
-  int i;
-
-  /* save BIOS configuration */
-  for (i = 0; i < HOST_CAP2; i += sizeof(u32)) {
-    ai->bios_config[i / sizeof(u32)] = readl(ai->mmio + i);
-  }
-
-  /* HOST_CAP2 only exists for AHCI V1.2 and later */
-  if (ai->bios_config[HOST_VERSION / sizeof(u32)] >= 0x00010200L) {
-    ai->bios_config[HOST_CAP2 / sizeof(u32)] = readl(ai->mmio + HOST_CAP2);
-  } else {
-    ai->bios_config[HOST_CAP2 / sizeof(u32)] = 0;
-  }
-
-  /* print AHCI register debug information */
-  if (debug) {
-    printf("AHCI global controller registers:\n");
-    for (i = 0; i <= HOST_CAP2 / sizeof(u32); i++) {
-      u32 val = ai->bios_config[i];
-      printf(" %02x: %08lx", i, val);
-
-      if (i == HOST_CAP) {
-        printf(" -");
-        if (val & HOST_CAP_64)         printf(" 64bit");
-        if (val & HOST_CAP_NCQ)        printf(" ncq");
-        if (val & HOST_CAP_SNTF)       printf(" sntf");
-        if (val & HOST_CAP_MPS)        printf(" mps");
-        if (val & HOST_CAP_SSS)        printf(" sss");
-        if (val & HOST_CAP_ALPM)       printf(" alpm");
-        if (val & HOST_CAP_LED)        printf(" led");
-        if (val & HOST_CAP_CLO)        printf(" clo");
-        if (val & HOST_CAP_ONLY)       printf(" ahci_only");
-        if (val & HOST_CAP_PMP)        printf(" pmp");
-        if (val & HOST_CAP_FBS)        printf(" fbs");
-        if (val & HOST_CAP_PIO_MULTI)  printf(" pio_multi");
-        if (val & HOST_CAP_SSC)        printf(" ssc");
-        if (val & HOST_CAP_PART)       printf(" part");
-        if (val & HOST_CAP_CCC)        printf(" ccc");
-        if (val & HOST_CAP_EMS)        printf(" ems");
-        if (val & HOST_CAP_SXS)        printf(" sxs");
-        printf(" ports:%d", (val & 0x1f) + 1);
-
-      } else if (i == HOST_CTL) {
-        printf(" -");
-        if (val & HOST_AHCI_EN)        printf(" ahci_enabled");
-        if (val & HOST_IRQ_EN)         printf(" irq_enabled");
-        if (val & HOST_RESET)          printf(" resetting");
-
-      } else if (i == HOST_CAP2) {
-        printf(" -");
-        if (val & HOST_CAP2_BOH)       printf(" boh");
-        if (val & HOST_CAP2_NVMHCI)    printf(" nvmhci");
-        if (val & HOST_CAP2_APST)      printf(" apst");
-      
-      }
-      printf("\n");
-    }
-  }
-
-  /* Save working copies of CAP, CAP2 and port_map and remove broken feature
-   * bits. This is largely copied from the Linux AHCI driver -- the wisdom
-   * around quirks and faulty hardware is hard to come by...
-   */
-  ai->cap      = ai->bios_config[HOST_CAP        / sizeof(u32)];
-  ai->cap2     = ai->bios_config[HOST_CAP2       / sizeof(u32)];
-  ai->port_map = ai->bios_config[HOST_PORTS_IMPL / sizeof(u32)];
-
-  if (ai->pci->board >= sizeof(initial_flags) / sizeof(*initial_flags)) {
-    dprintf("error: invalid board index in PCI info\n");
-    return(-1);
-  }
-  ai->flags = initial_flags[ai->pci->board];
-
-  if ((ai->cap & HOST_CAP_64) && (ai->flags & AHCI_HFLAG_32BIT_ONLY)) {
-    /* disable 64-bit support for faulty controllers; OS/2 can't do 64 bits at
-     * this point, of course, but who knows where all this will be in a few
-     * years...
-     */
-    ai->cap &= ~HOST_CAP_64;
-  }
-
-  if ((ai->cap & HOST_CAP_NCQ) && (ai->flags & AHCI_HFLAG_NO_NCQ)) {
-    dprintf("controller can't do NCQ, turning off CAP_NCQ\n");
-    ai->cap &= ~HOST_CAP_NCQ;
-  }
-
-  if (!(ai->cap & HOST_CAP_NCQ) && (ai->flags & AHCI_HFLAG_YES_NCQ)) {
-    dprintf("controller can do NCQ, turning on CAP_NCQ\n");
-    ai->cap |= HOST_CAP_NCQ;
-  }
-
-  if ((ai->cap & HOST_CAP_PMP) && (ai->flags & AHCI_HFLAG_NO_PMP)) {
-    dprintf("controller can't do PMP, turning off CAP_PMP\n");
-    ai->cap |= HOST_CAP_PMP;
-  }
-
-  if ((ai->cap & HOST_CAP_SNTF) && (ai->flags & AHCI_HFLAG_NO_SNTF)) {
-    dprintf("controller can't do SNTF, turning off CAP_SNTF\n");
-    ai->cap &= ~HOST_CAP_SNTF;
-  }
-
-  if (ai->pci->vendor == PCI_VENDOR_ID_JMICRON &&
-      ai->pci->device == 0x2361 && ai->port_map != 1) {
-    dprintf("JMB361 has only one port, port_map 0x%x -> 0x%x\n", ai->port_map, 1);
-    ai->port_map = 1;
-  }
-
-  /* Correlate port map to number of ports reported in HOST_CAP
-   *
-   * NOTE: Port map and number of ports handling differs a bit from the
-   *       Linux AHCI driver because we're storing both in AI_INFO. As in the
-   *       Linux driver, the port map is the main driver for port scanning but
-   *       we're also saving a maximum port number in AI_INFO to reduce the
-   *       number of IORB queues to look at in trigger_engine(). This is done
-   *       in ahci_scan_ports().
-   */
-  ports = (ai->cap & 0x1f) + 1;
-  for (i = 0; i < AHCI_MAX_PORTS; i++) {
-    if (ai->port_map & (1UL << i)) {
-      ports--;
-    }
-  }
-  if (ports < 0) {
-    /* more ports in port_map than in HOST_CAP & 0x1f */
-    ports = (ai->cap & 0x1f) + 1;
-    dprintf("implemented port map (0x%lx) contains more "
-            "ports than nr_ports (%d), using nr_ports\n",
-            ai->port_map, ports);
-    ai->port_map = (1UL << ports) - 1UL;
-  }
-
-  /* set maximum command slot number */
-  ai->cmd_max = (u16) ((ai->cap >> 8) & 0x1f);
-
-  return(0);
-}
-
-/******************************************************************************
- * Restore BIOS configuration of AHCI adapter. This is needed after scanning
- * for devices because we still need the BIOS until the initial boot sequence
- * has completed.
- */
-int ahci_restore_bios_config(AD_INFO *ai)
-{
-  ddprintf("restoring AHCI BIOS configuration\n");
-
-  /* restore saved BIOS configuration */
-  writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
-  writel(ai->mmio + HOST_CCC,       ai->bios_config[HOST_CCC / sizeof(u32)]);
-  writel(ai->mmio + HOST_CCC_PORTS, ai->bios_config[HOST_CCC_PORTS / sizeof(u32)]);
-  writel(ai->mmio + HOST_EM_CTL,    ai->bios_config[HOST_EM_CTL / sizeof(u32)]);
-
-  /* flush PCI MMIO delayed write buffers */
-  readl(ai->mmio + HOST_EM_CTL);
-
-  return(0);
-}
-
-/******************************************************************************
- * Restore initial configuration (e.g. after an adapter reset). This relies
- * on information saved by 'ahci_save_bios_config()'.
- */
-int ahci_restore_initial_config(AD_INFO *ai)
-{
-  ddprintf("restoring initial configuration\n");
-
-  /* restore saved BIOS configuration */
-  writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
-  writel(ai->mmio + HOST_CCC,       ai->bios_config[HOST_CCC / sizeof(u32)]);
-  writel(ai->mmio + HOST_CCC_PORTS, ai->bios_config[HOST_CCC_PORTS / sizeof(u32)]);
-  writel(ai->mmio + HOST_EM_CTL,    ai->bios_config[HOST_EM_CTL / sizeof(u32)]);
-
-  /* flush PCI MMIO delayed write buffers */
-  readl(ai->mmio + HOST_EM_CTL);
-
-  /* (re-)enable AHCI mode */
-  ahci_enable_ahci(ai);
-
-  return(0);
-}
-
-/******************************************************************************
- * Save port configuration. This is primarily used to save the BIOS port
- * configuration (command list and FIS buffers and the IRQ mask).
- *
- * The port configuration returned by this function is dynamically allocated
- * and automatically freed when calling ahci_restore_port_config().
- */
-AHCI_PORT_CFG *ahci_save_port_config(AD_INFO *ai, int p)
-{
-  AHCI_PORT_CFG *pc;
-  u8 _far *port_mmio = port_base(ai, p);
-
-  if ((pc = malloc(sizeof(*pc))) == NULL) {
-    return(NULL);
-  }
-
-  pc->cmd_list   = readl(port_mmio + PORT_LST_ADDR);
-  pc->cmd_list_h = readl(port_mmio + PORT_LST_ADDR_HI);
-  pc->fis_rx     = readl(port_mmio + PORT_FIS_ADDR);
-  pc->fis_rx_h   = readl(port_mmio + PORT_FIS_ADDR_HI);
-  pc->irq_mask   = readl(port_mmio + PORT_IRQ_MASK);
-
-  return(pc);
-}
-
-/******************************************************************************
- * Restore port configuration. This is primarily used to restore the BIOS port
- * configuration (command list and FIS buffers and the IRQ mask).
- *
- * The port configuration automatically freed.
- */
-void ahci_restore_port_config(AD_INFO *ai, int p, AHCI_PORT_CFG *pc)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-
-  writel(port_mmio + PORT_LST_ADDR,    pc->cmd_list);
-  writel(port_mmio + PORT_LST_ADDR_HI, pc->cmd_list_h);
-  writel(port_mmio + PORT_FIS_ADDR,    pc->fis_rx);
-  writel(port_mmio + PORT_FIS_ADDR_HI, pc->fis_rx_h);
-  writel(port_mmio + PORT_IRQ_MASK,    pc->irq_mask);
-
-  readl(port_base(ai, p) + PORT_IRQ_MASK); /* flush */
-
-  free(pc);
-}
-
-/******************************************************************************
- * Enable AHCI mode on this controller.
- */
-int ahci_enable_ahci(AD_INFO *ai)
-{
-  u32 ctl = readl(ai->mmio + HOST_CTL);
-  int i;
-
-  if (ctl & HOST_AHCI_EN) {
-    /* AHCI mode already enbled */
-    return(0);
-  }
-
-  /* some controllers need AHCI_EN to be written multiple times */
-  for (i = 0; i < 5; i++) {
-    ctl |= HOST_AHCI_EN;
-    writel(ai->mmio + HOST_CTL, ctl);
-    ctl = readl(ai->mmio + HOST_CTL);   /* flush && sanity check */
-    if (ctl & HOST_AHCI_EN) {
-      return(0);
-    }
-    mdelay(10);
-  }
-
-  /* couldn't enable AHCI mode */
-  dprintf("failed to enable AHCI mode on adapter #%d\n", ad_no(ai));
-  return(1);
-}
-
-/******************************************************************************
- * Scan all ports for connected devices and fill in the corresponding device
- * information.
- *
- * NOTES:
- *
- *  - The adapter is temporarily configured for os2ahci but the original BIOS
- *    configuration will be restored when done. This happens only until we
- *    have received the IOCC_COMPLETE_INIT command.
- *
- *  - Subsequent calls are currently not planned but may be required for
- *    suspend/resume handling, hot swap functionality, etc.
- *
- *  - This function is expected to be called with the spinlock released but
- *    the corresponding adapter's busy flag set. It will aquire the spinlock
- *    temporarily to allocate/free memory for the ATA identify buffer.
- */
-int ahci_scan_ports(AD_INFO *ai)
-{
-  AHCI_PORT_CFG *pc = NULL;
-  u16 *id_buf;
-  int rc;
-  int p;
-
-  spin_lock(drv_lock);
-  id_buf = malloc(ATA_ID_WORDS * sizeof(u16));
-  spin_unlock(drv_lock);
-  if (id_buf == NULL) {
-    return(-1);
-  }
-
-  if (ai->bios_config[0] == 0) {
-    /* first call */
-    ahci_save_bios_config(ai);
-  }
-
-  if (ahci_enable_ahci(ai)) {
-    goto exit_port_scan;
-  }
-  
-  /* perform port scan */
-  dprintf("scanning ports on adapter #%d\n", ad_no(ai));
-  for (p = 0; p < AHCI_MAX_PORTS; p++) {
-    if (ai->port_map & (1UL << p)) {
-
-      if (!init_complete) {
-        if ((pc = ahci_save_port_config(ai, p)) == NULL) {
-          goto exit_port_scan;
-        }
-      }
-
-      /* start/reset port; if no device is attached, this is expected to fail */
-      if (init_reset) {
-        ddprintf("resetting port #%d\n", p);
-        rc = ahci_reset_port(ai, p, 0);
-      } else {
-        ddprintf("(re)starting port #%d\n", p);
-        ahci_stop_port(ai, p);
-        rc = ahci_start_port(ai, p, 0);
-      }
-      if (rc) {
-        /* no device attached to this port */
-        ai->port_map &= ~(1UL << p);
-        goto restore_port_config;
-      }
-
-      /* this port has a device attached and is ready to accept commands */
-      ddprintf("port #%d seems to be attached to a device; probing...\n", p);
-      rc = ahci_exec_polled_cmd(ai, p, 0, 500, ATA_CMD_ID_ATA, 
-                                AP_VADDR, (void _far *) id_buf, 512,
-                                AP_END);
-
-      if (rc != 0 || id_buf[ATA_ID_CONFIG] & (1U << 15)) {
-        /* this might be an ATAPI device; run IDENTIFY_PACKET_DEVICE */
-        rc = ahci_exec_polled_cmd(ai, p, 0, 500, ATA_CMD_ID_ATAPI,
-                                  AP_VADDR, (void _far *) id_buf, 512,
-                                  AP_END);
-      }
-
-      if (rc == 0) {
-        /* we have a valid IDENTIFY or IDENTIFY_PACKET response */
-        ddphex(id_buf, 512, "ATA_IDENTIFY(_PACKET) results:\n");
-        ahci_setup_device(ai, p, 0, id_buf);
-      } else {
-        /* no device attached to this port */
-        ai->port_map &= ~(1UL << p);
-      }
-
-    restore_port_config:
-      if (pc != NULL) {
-        ahci_restore_port_config(ai, p, pc);
-      }
-    }
-  }
-
-exit_port_scan:
-  if (!init_complete) {
-    ahci_restore_bios_config(ai);
-  }
-  spin_lock(drv_lock);
-  free(id_buf);
-  spin_unlock(drv_lock);
-  return(0);
-}
-
-/******************************************************************************
- * Complete initialization of adapter. This includes restarting all active
- * ports and initializing interrupt processing. This is called when receiving
- * the IOCM_COMPLETE_INIT request.
- */
-int ahci_complete_init(AD_INFO *ai)
-{
-  int rc;
-  int p;
-  int i;
-
-  dprintf("completing initialization of adapter #%d\n", ad_no(ai));
-
-  /* register IRQ handlers; each IRQ level is registered only once */
-  for (i = 0; i < irq_map_cnt; i++) {
-    if (irq_map[i] == ai->irq) {
-      /* we already have this IRQ registered */
-      break;
-    }
-  }
-
-  if (i >= irq_map_cnt) {
-    dprintf("registering interrupt #%d\n", ai->irq);
-
-    if (DevHelp_SetIRQ(mk_NPFN(irq_handlers[irq_map_cnt]), ai->irq, 1) != 0) {
-      dprintf("failed to register shared interrupt\n");
-
-      if (DevHelp_SetIRQ(mk_NPFN(irq_handlers[irq_map_cnt]), ai->irq, 0) != 0) {
-        dprintf("failed to register exclusive interrupt\n");
-        return(-1);
-      }
-    }
-    irq_map[irq_map_cnt++] = ai->irq;
-  }
-
-  /* enable AHCI mode */
-  if ((rc = ahci_enable_ahci(ai)) != 0) {
-    return(rc);
-  }
-
-  /* Start all ports. The main purpose is to set the command list and FIS
-   * receive area addresses properly and to enable port-level interrupts; we
-   * don't really care about the return status because we'll find out soon
-   * enough if a previously detected device has problems.
-   */
-  for (p = 0; p < AHCI_MAX_PORTS; p++) {
-    if (ai->port_map & (1UL << p)) {
-      dprintf("restarting port #%d\n", p);
-      ahci_stop_port(ai, p);
-      ahci_start_port(ai, p, 1);
-    }
-  }
-
-  /* enable adapter-level interrupts */
-  writel(ai->mmio + HOST_CTL, HOST_IRQ_EN);
-  readl(ai->mmio + HOST_CTL); /* flush */
-
-  /* enable interrupts on PCI-level (PCI 2.3 added a feature to disable ints) */
-  pci_enable_int(ai->bus, ai->dev_func);
-
-  return(0);
-}
-
-/******************************************************************************
- * Reset specified port. This function is typically called during adapter
- * initialization and first gets the port into a defined status, then resets
- * the port by sending a COMRESET signal.
- *
- * This function is also the location of the link speed initialization (link
- * needs to be restablished after changing link speed, anyway).
- *
- * NOTE: This function uses a busy loop to wait for DMA engines to stop and
- *       the COMRESET to complete. It should only be called at task time
- *       during initialization or in a context hook.
- */
-int ahci_reset_port(AD_INFO *ai, int p, int ei)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  u32 tmp;
-  int timeout = 500;
-
-  dprintf("resetting port %d.%d\n", ad_no(ai), p);
-
-  /* stop port engines (we don't care whether there is an error doing so) */
-  ahci_stop_port(ai, p);
-
-  /* clear SError */
-  tmp = readl(port_mmio + PORT_SCR_ERR);
-  ddprintf(" PORT_SCR_ERR  = 0x%lx\n", tmp);
-  writel(port_mmio + PORT_SCR_ERR, tmp);
-
-  /* clear pending port IRQs */
-  tmp = readl(port_mmio + PORT_IRQ_STAT);
-  if (tmp) {
-    writel(port_mmio + PORT_IRQ_STAT, tmp);
-  }
-  ddprintf(" PORT_IRQ_STAT = 0x%lx\n", tmp);
-  ddprintf(" PORT_IRQ_MASK = 0x%lx\n", readl(port_mmio + PORT_IRQ_MASK));
-  ddprintf(" HOST_IRQ_STAT = 0x%lx\n", readl(ai->mmio + HOST_IRQ_STAT));
-  writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
-
-  /* set link speed */
-  tmp = readl(port_mmio + PORT_SCR_CTL) & ~0x000000f0UL;
-  writel(port_mmio + PORT_SCR_CTL, tmp | (link_speed[ad_no(ai)][p] << 4));
-
-  /* issue COMRESET on the port */
-  tmp = readl(port_mmio + PORT_SCR_CTL) & ~0x0000000fUL;
-  writel(port_mmio + PORT_SCR_CTL, tmp | 1);
-  readl(port_mmio + PORT_SCR_CTL);  /* flush */
-
-  /* spec says "leave reset bit on for at least 1ms"; make it 2ms */
-  mdelay(2);
-
-  writel(port_mmio + PORT_SCR_CTL, tmp);
-  readl(port_mmio + PORT_SCR_CTL);  /* flush */
-
-  /* wait for communication to be re-established after port reset */
-  while (((tmp = readl(port_mmio + PORT_SCR_STAT) & 3)) != 3) {
-    mdelay(10);
-    timeout -= 10;
-    if (timeout <= 0) {
-      dprintf("no device present after resetting port #%d "
-              "(PORT_SCR_STAT = 0x%lx)\n", p, tmp);
-      return(-1);
-    }
-  }
-
-  /* clear SError again (recommended by AHCI spec) */
-  tmp = readl(port_mmio + PORT_SCR_ERR);
-  writel(port_mmio + PORT_SCR_ERR, tmp);
-
-  /* start port so we can receive the COMRESET FIS */
-  ahci_start_port(ai, p, ei);
-
-  /* wait for device to be ready ((PxTFD & (BSY | DRQ | ERR)) == 0) */
-  while (((tmp = readl(port_mmio + PORT_TFDATA)) & 0x89) != 0) {
-    mdelay(10);
-    timeout -= 10;
-    if (timeout <= 0) {
-      dprintf("device not ready on port #%d "
-              "(PORT_TFDATA = 0x%lx)\n", p, tmp);
-      ahci_stop_port(ai, p);
-      return(-1);
-    }
-  }
-  ddprintf(" PORT_TFDATA   = 0x%lx\n", readl(port_mmio + PORT_TFDATA));
-
-  return(0);
-}
-
-/******************************************************************************
- * Start specified port.
- */
-int ahci_start_port(AD_INFO *ai, int p, int ei)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  u32 status;
-
-  /* check whether device presence is detected and link established */
-  status = readl(port_mmio + PORT_SCR_STAT);
-  ddprintf(" PORT_SCR_STAT = 0x%lx\n", status);
-  if ((status & 0xf) != 3) {
-    return(-1);
-  }
-
-  /* enable FIS reception */
-  ahci_start_fis_rx(ai, p);
-
-  /* enable DMA */
-  ahci_start_engine(ai, p);
-
-  if (ei) {
-    /* enable port interrupts */
-    writel(port_mmio + PORT_IRQ_MASK, PORT_IRQ_TF_ERR |
-                                      PORT_IRQ_HBUS_ERR |
-                                      PORT_IRQ_HBUS_DATA_ERR |
-                                      PORT_IRQ_IF_ERR |
-                                      PORT_IRQ_OVERFLOW |
-                                      PORT_IRQ_BAD_PMP |
-                                      PORT_IRQ_UNK_FIS |
-                                      PORT_IRQ_SDB_FIS |
-                                      PORT_IRQ_D2H_REG_FIS);
-  } else {
-    writel(port_mmio + PORT_IRQ_MASK, 0);
-  }
-  readl(port_mmio + PORT_IRQ_MASK);  /* flush */
-
-  return(0);
-}
-
-/******************************************************************************
- * Start port FIS reception. Copied from Linux AHCI driver and adopted to
- * OS2AHCI.
- */
-void ahci_start_fis_rx(AD_INFO *ai, int p)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  u32 port_dma = port_dma_base_phys(ai, p);
-  u32 tmp;
-
-  /* set comand header and FIS address registers */
-  writel(port_mmio + PORT_LST_ADDR, port_dma + offsetof(AHCI_PORT_DMA, cmd_hdr));
-  writel(port_mmio + PORT_LST_ADDR_HI, 0);
-  writel(port_mmio + PORT_FIS_ADDR, port_dma + offsetof(AHCI_PORT_DMA, rx_fis));
-  writel(port_mmio + PORT_FIS_ADDR_HI, 0);
-
-  /* enable FIS reception */
-  tmp = readl(port_mmio + PORT_CMD);
-  tmp |= PORT_CMD_FIS_RX;
-  writel(port_mmio + PORT_CMD, tmp);
-
-  /* flush */
-  readl(port_mmio + PORT_CMD);
-}
-
-/******************************************************************************
- * Start port HW engine. Copied from Linux AHCI driver and adopted to OS2AHCI.
- */
-void ahci_start_engine(AD_INFO *ai, int p)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  u32 tmp;
-
-  /* start DMA */
-  tmp = readl(port_mmio + PORT_CMD);
-  tmp |= PORT_CMD_START;
-  writel(port_mmio + PORT_CMD, tmp);
-  readl(port_mmio + PORT_CMD); /* flush */
-}
-
-/******************************************************************************
- * Stop specified port
- */
-int ahci_stop_port(AD_INFO *ai, int p)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  int rc;
-
-  /* disable FIS reception */
-  if ((rc = ahci_stop_fis_rx(ai, p)) != 0) {
-    dprintf("error: failed to stop FIS receive (%d)\n", rc);
-    return(rc);
-  }
-
-  /* disable DMA */
-  if ((rc = ahci_stop_engine(ai, p)) != 0) {
-    dprintf("error: failed to stop port HW engine (%d)\n", rc);
-    return(rc);
-  }
-
-  /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
-  writel(port_mmio + PORT_SCR_ACT, 0);
-  readl(port_mmio + PORT_SCR_ACT);  /* flush */
-
-  return(0);
-}
-
-/******************************************************************************
- * Stop port FIS reception. Copied from Linux AHCI driver and adopted to
- * OS2AHCI.
- *
- * NOTE: This function uses a busy loop to wait for the DMA engine to stop. It
- *       should only be called at task time during initialization or in a
- *       context hook (e.g. when resetting a port).
- */
-int ahci_stop_fis_rx(AD_INFO *ai, int p)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  int timeout = 1000;
-  u32 tmp;
-
-  /* disable FIS reception */
-  tmp = readl(port_mmio + PORT_CMD);
-  tmp &= ~PORT_CMD_FIS_RX;
-  writel(port_mmio + PORT_CMD, tmp);
-
-  /* wait for completion, spec says 500ms, give it 1000 */
-  while (timeout > 0 && (readl(port_mmio + PORT_CMD) & PORT_CMD_FIS_ON)) {
-    mdelay(10);
-    timeout -= 10;
-  }
-
-  return((timeout <= 0) ? -1 : 0);
-}
-
-/******************************************************************************
- * Stop port HW engine. Copied from Linux AHCI driver and adopted to OS2AHCI.
- *
- * NOTE: This function uses a busy loop to wait for the DMA engine to stop. It
- *       should only be called at task time during initialization or in a
- *       context hook (e.g. when resetting a port).
- */
-int ahci_stop_engine(AD_INFO *ai, int p)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  int timeout = 500;
-  u32 tmp;
-
-  tmp = readl(port_mmio + PORT_CMD);
-
-  /* check if the port is already stopped */
-  if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) {
-    return 0;
-  }
-
-  /* set port to idle */
-  tmp &= ~PORT_CMD_START;
-  writel(port_mmio + PORT_CMD, tmp);
-
-  /* wait for engine to stop. This could be as long as 500 msec */
-  while (timeout > 0 && (readl(port_mmio + PORT_CMD) & PORT_CMD_LIST_ON)) {
-    mdelay(10);
-    timeout -= 10;
-  }
-
-  return((timeout <= 0) ? -1 : 0);
-}
-
-/******************************************************************************
- * Execute AHCI command for given IORB. This includes all steps typically
- * required by any of the ahci_*() IORB processing functions.
- *
- * NOTE: In order to prevent race conditions with port restart and reset
- *       handlers, we either need to keep the spinlock during the whole
- *       operation or set the adapter's busy flag. Since the expectation
- *       is that command preparation will be quick (it certainly doesn't
- *       involve delays), we're going with the spinlock for the time being.
- */
-void ahci_exec_iorb(IORBH _far *iorb, int ncq_capable,
-                    int (*func)(IORBH _far *, int))
-{
-  volatile u32 *cmds;
-  ADD_WORKSPACE _far *aws = add_workspace(iorb);
-  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
-  P_INFO *port = ai->ports + iorb_unit_port(iorb);
-  ULONG timeout = (iorb->Timeout > 0) ? iorb->Timeout : DEFAULT_TIMEOUT;
-  u8 _far *port_mmio = port_base(ai, iorb_unit_port(iorb));
-  int i;
-
-  /* determine whether this will be an NCQ request */
-  aws->is_ncq = ((ai->cap & HOST_CAP_NCQ) && ncq_capable && !aws->no_ncq);
-
-  /* NCQ disabled temporarily until non-NCQ commands are fully working */
-  aws->is_ncq = 0;
-
-  /* check whether adapter is available */
-  spin_lock(drv_lock);
-  if (!ai->busy) {
-
-    if (!init_complete) {
-      ai->busy = 1;
-      spin_unlock(drv_lock);
-      ahci_exec_polled_iorb(iorb, func, timeout);
-      ai->busy = 0;
-      return;
-    }
-
-    /* prevent NCQ/regular command mix */
-    if (aws->is_ncq && port->reg_cmds == 0 ||
-        !aws->is_ncq && port->ncq_cmds == 0) {
-
-      /* Find next available command slot. We use a simple round-robin 
-       * algorithm for this to prevent commands with higher slot indexes
-       * from stalling when new commands are coming in frequently.
-       */
-      cmds = (aws->is_ncq) ? &port->ncq_cmds : &port->reg_cmds;
-      for (i = 0; i <= ai->cmd_max; i++) {
-        if ((*cmds & (1UL << port->cmd_slot)) == 0) {
-          break;
-        }
-        if (++(port->cmd_slot) > ai->cmd_max) {
-          port->cmd_slot = 0;
-        }
-      }
-
-      if ((*cmds & (1UL << port->cmd_slot)) == 0) {
-        /* prepare command */
-        if (func(iorb, port->cmd_slot)) {
-          /* Command preparation failed, or no HW command required; IORB
-           * will already have the error code if there was an error.
-           */
-          spin_unlock(drv_lock);
-          iorb_done(iorb);
-          return;
-        }
-
-        /* start timer for this IORB */
-        ADD_StartTimerMS(&aws->timer, timeout, (PFN) timeout_callback, iorb, 0);
-
-        /* update IORB */
-        aws->queued_hw = 1;
-        aws->cmd_slot = port->cmd_slot;
-
-        /* issue command to hardware */
-        ddprintf("issuing command on slot %d\n", port->cmd_slot);
-        *cmds |= (1UL << port->cmd_slot);
-        if (aws->is_ncq) {
-          writel(port_mmio + PORT_SCR_ACT, (1UL << port->cmd_slot));
-          readl(port_mmio + PORT_SCR_ACT); /* flush */
-        }
-        writel(port_mmio + PORT_CMD_ISSUE, (1UL << port->cmd_slot));
-        readl(port_mmio + PORT_CMD_ISSUE); /* flush */
-
-        /* make sure next cmd won't use the same slot to prevent starvation */
-        if (++(port->cmd_slot) > ai->cmd_max) {
-          port->cmd_slot = 0;
-        }
-        spin_unlock(drv_lock);
-        return;
-      }
-    }
-  }
-
-  /* requeue this IORB; it will be picked up again in trigger_engine() */
-  aws->processing = 0;
-  spin_unlock(drv_lock);
-}
-
-/******************************************************************************
- * Execute polled IORB command. This function is called by ahci_exec_iorb()
- * when the initialization has not yet completed. The reasons for polling until
- * initialization has completed are:
- *
- *  - We need to restore the BIOS configuration after we're done with this
- *    command because someone might still call int 13 routines; sending
- *    asynchronous commands and waiting for interrupts to indicate completion
- *    won't work in such a scenario.
- *  - Our context hooks won't work while the device managers are initializing
- *    (they can't yield at init time).
- *  - The device managers typically poll for command completion during
- *    initialization so it won't make much of a difference, anyway.
- *
- * NOTE: This function must be called with the adapter-level busy flag set but
- *       without the driver-level spinlock held.
- */
-void ahci_exec_polled_iorb(IORBH _far *iorb, int (*func)(IORBH _far *, int),
-                           ULONG timeout)
-{
-  AHCI_PORT_CFG *pc = NULL;
-  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-  u8 _far *port_mmio = port_base(ai, p);
-
-  /* enable AHCI mode */
-  if (ahci_enable_ahci(ai) != 0) {
-    iorb_seterr(iorb, IOERR_ADAPTER_NONSPECIFIC);
-    goto restore_bios_config;
-  }
-
-  /* check whether command slot 0 is available */
-  if ((readl(port_mmio + PORT_CMD_ISSUE) & 1) != 0) {
-    iorb_seterr(iorb, IOERR_DEVICE_BUSY);
-    goto restore_bios_config;
-  }
-  
-  /* save port configuration */
-  if ((pc = ahci_save_port_config(ai, p)) == NULL) {
-    iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
-    goto restore_bios_config;
-  }
-
-  /* restart port (includes the necessary port configuration) */
-  if (ahci_stop_port(ai, p) || ahci_start_port(ai, p, 0)) {
-    iorb_seterr(iorb, IOERR_ADAPTER_NONSPECIFIC);
-    goto restore_bios_config;
-  }
-
-  /* prepare command */
-  if (func(iorb, 0) == 0) {
-    /* successfully prepared cmd; issue cmd and wait for completion */
-    ddprintf("executing polled cmd...");
-    writel(port_mmio + PORT_CMD_ISSUE, 1);
-    timeout /= 10;
-    while (timeout > 0 && (readl(port_mmio + PORT_CMD_ISSUE) & 1)) {
-      mdelay(10);
-      timeout--;
-    }
-    ddprintf(" done (time left = %ld)\n", timeout * 10);
-
-    if (timeout == 0) {
-      dprintf("timeout for IORB %Fp\n", iorb);
-      iorb_seterr(iorb, IOERR_ADAPTER_TIMEOUT);
-
-    } else if (readl(port_mmio + PORT_SCR_ERR) != 0 ||
-               readl(port_mmio + PORT_TFDATA) & 0x89) {
-      dprintf("polled cmd error for IORB %Fp\n", iorb);
-      iorb_seterr(iorb, IOERR_DEVICE_NONSPECIFIC);
-      ahci_reset_port(ai, iorb_unit_port(iorb), 0);
-
-    } else {
-      /* successfully executed command */
-      if (add_workspace(iorb)->ppfunc != NULL) {
-        add_workspace(iorb)->ppfunc(iorb);
-      } else {
-        add_workspace(iorb)->complete = 1;
-      }
-    }
-  }
-
-restore_bios_config:
-  /* restore BIOS configuration */
-  if (pc != NULL) {
-    ahci_restore_port_config(ai, p, pc);
-  }
-  ahci_restore_bios_config(ai);
-
-  if (add_workspace(iorb)->complete | (iorb->Status | IORB_ERROR)) {
-    aws_free(add_workspace(iorb));
-    iorb_done(iorb);
-  }
-  return;
-}
-
-/******************************************************************************
- * Execute polled ATA/ATAPI command. This function will block until the command
- * has completed or the timeout has expired, thus it should only be used during
- * initialization. Furthermore, it will always use command slot zero.
- *
- * The difference to ahci_exec_polled_iorb() is that this function executes
- * arbitrary ATA/ATAPI commands outside the context of an IORB. It's typically
- * used when scanning for devices during initialization.
- */
-int ahci_exec_polled_cmd(AD_INFO *ai, int p, int d, int timeout, int cmd, ...)
-{
-  va_list va;
-  u8 _far *port_mmio = port_base(ai, p);
-  u32 tmp;
-  int rc;
-
-  /* verify that command slot 0 is idle */
-  if (readl(port_mmio + PORT_CMD_ISSUE) & 1) {
-    ddprintf("port %d slot 0 is not idle; not executing polled cmd\n", p);
-    return(-1);
-  }
-
-  /* fill in command slot 0 */
-  va_start(va, cmd);
-  if ((rc = v_ata_cmd(ai, p, d, 0, cmd, va)) != 0) {
-    return(rc);
-  }
-
-  /* start command execution for slot 0 */
-  ddprintf("executing polled cmd...");
-  writel(port_mmio + PORT_CMD_ISSUE, 1);
-
-  /* wait until command has completed */
-  while (timeout > 0 && (readl(port_mmio + PORT_CMD_ISSUE) & 1)) {
-    mdelay(10);
-    timeout -= 10;
-  }
-  ddprintf(" done (time left = %d)\n", timeout);
-
-  /* check error condition */
-  if ((tmp = readl(port_mmio + PORT_SCR_ERR)) != 0) {
-    dprintf("SERR = 0x%08lx\n", tmp);
-    return(-1);
-  }
-  if (((tmp = readl(port_mmio + PORT_TFDATA)) & 0x89) != 0) {
-    dprintf("TFDATA = 0x%08lx\n", tmp);
-    return(-1);
-  }
-
-  return((timeout <= 0) ? -1 : 0);
-}
-
-/******************************************************************************
- * AHCI top-level hardware interrupt handler. This handler finds the adapters
- * and ports which have issued the interrupt and calls the corresponding
- * port interrupt handler.
- *
- * On entry, OS/2 will have processor interrupts enabled because we're using
- * shared IRQs but we won't be preempted by another interrupt on the same IRQ
- * IRQ level until we indicated EOI. We'll keep it this way, only requesting
- * the driver-level spinlock when actually changing the driver state (IORB
- * queues, ...)
- */
-int ahci_intr(u16 irq)
-{
-  u32 irq_stat;
-  int handled = 0;
-  int a;
-  int p;
-
-  /* find adapter(s) with pending interrupts */
-  for (a = 0; a < ad_info_cnt; a++) {
-    AD_INFO *ai = ad_infos + a;
-
-    if (ai->irq == irq && (irq_stat = readl(ai->mmio + HOST_IRQ_STAT)) != 0) {
-      /* this adapter has interrupts pending */
-      u32 irq_masked = irq_stat & ai->port_map;
-
-      for (p = 0; p <= ai->port_max; p++) {
-        if (irq_masked & (1UL << p)) {
-          ahci_port_intr(ai, p);
-        }
-      }
-
-      /* clear interrupt condition on the adapter */
-      writel(ai->mmio + HOST_IRQ_STAT, irq_stat);
-      readl(ai->mmio + HOST_IRQ_STAT); /* flush */
-      handled = 1;
-    }
-  }
-
-  if (handled) {
-    /* trigger state machine to process next IORBs, if any */
-    spin_lock(drv_lock);
-    trigger_engine();
-    spin_unlock(drv_lock);
-
-    /* complete the interrupt */
-    DevHelp_EOI(irq);
-    return(0);
-  } else {
-    return(1);
-  }
-}
-
-/******************************************************************************
- * AHCI port-level interrupt handler. As described above, processor interrupts
- * are enabled on entry thus we have to protect shared resources with a
- * spinlock.
- */
-void ahci_port_intr(AD_INFO *ai, int p)
-{
-  IORB_QUEUE done_queue;
-  IORBH _far *iorb;
-  IORBH _far *next = NULL;
-  u8 _far *port_mmio = port_base(ai, p);
-  u32 irq_stat;
-  u32 active_cmds;
-  u32 done_mask;
-  
-  ddprintf("port interrupt for adapter #%d, port #%d\n", ad_no(ai), p);
-  memset(&done_queue, 0x00, sizeof(done_queue));
-
-  /* get interrupt status and clear it right away */
-  irq_stat = readl(port_mmio + PORT_IRQ_STAT);
-  writel(port_mmio + PORT_IRQ_STAT, irq_stat);
-  readl(port_mmio + PORT_IRQ_STAT); /* flush */
-  
-  if (irq_stat & PORT_IRQ_ERROR) {
-    /* this is an error interrupt */
-    ahci_error_intr(ai, p, irq_stat);
-    return;
-  }
-
-  spin_lock(drv_lock);
-
-  /* Find out which command slots have completed. Since error recovery for
-   * NCQ commands interfers with non-NCQ commands, the upper layers will
-   * make sure there's never a mixture of NCQ and non-NCQ commands active
-   * on any port at any given time. This makes it easier to find out which
-   * commands have completed, too.
-   */
-  if (ai->ports[p].ncq_cmds != 0) {
-    active_cmds = readl(port_mmio + PORT_SCR_ACT);
-    done_mask = ai->ports[p].ncq_cmds ^ active_cmds;
-    ddprintf("[ncq_cmds]: active_cmds = 0x%08lx, done_mask = 0x%08lx\n",
-             active_cmds, done_mask);
-  } else {
-    active_cmds = readl(port_mmio + PORT_CMD_ISSUE);
-    done_mask = ai->ports[p].reg_cmds ^ active_cmds;
-    ddprintf("[reg_cmds]: active_cmds = 0x%08lx, done_mask = 0x%08lx\n",
-             active_cmds, done_mask);
-  }
-
-  /* Find the IORBs related to the completed commands and complete them.
-   *
-   * NOTES: The spinlock must not be released while in this loop to prevent
-   *        race conditions with timeout handlers or other threads in SMP
-   *        systems.
-   *
-   *        Since we hold the spinlock when IORBs complete, we can't call the
-   *        IORB notification routine right away because this routine might
-   *        schedule another IORB which could cause a deadlock. Thus, we'll
-   *        add all IORBs to be completed to a temporary queue which will be
-   *        processed after releasing the spinlock.
-   */
-  for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
-    ADD_WORKSPACE _far *aws = (ADD_WORKSPACE _far *) &iorb->ADDWorkSpace;
-    next = iorb->pNxtIORB;
-    if (aws->queued_hw && (done_mask & (1UL << aws->cmd_slot))) {
-      /* this command has completed */
-      if (aws->ppfunc != NULL) {
-        aws->ppfunc(iorb);
-      } else {
-        aws->complete = 1;
-      }
-
-      if (aws->complete) {
-        /* this IORB is complete */
-        aws_free(aws);
-
-        /* move IORB to our temporary done queue */
-        iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
-        iorb_queue_add(&done_queue, iorb);
-      }
-
-      /* clear corresponding bit in issued command bitmaps */
-      ai->ports[p].ncq_cmds &= ~(1UL << aws->cmd_slot);
-      ai->ports[p].reg_cmds &= ~(1UL << aws->cmd_slot);
-    }
-  }
-
-  spin_unlock(drv_lock);
-
-  /* call notification routines for all IORBs in the done queue */
-  for (iorb = done_queue.root; iorb != NULL; iorb = next) {
-    next = iorb->pNxtIORB;
-    iorb->Status = IORB_DONE;
-    if (iorb->RequestControl & IORB_ASYNC_POST) {
-      iorb->NotifyAddress(iorb);
-    }
-  }
-}
-
-/******************************************************************************
- * AHCI error interrupt handler. Errors include interface errors and device
- * errors (usually triggered by the error bit in the AHCI task file register).
- *
- * Since this involves long-running operations such as restarting or even
- * resetting a port, this function is invoked at task time via a context
- * hook.
- *
- * NOTE: AHCI controllers stop all processing when encountering an error
- *       condition in order to give the driver time to find out what exactly
- *       went wrong. This means no new commands will be processed until we
- *       clear the error register and restore the "commands issued" register.
- */
-void ahci_error_intr(AD_INFO *ai, int p, u32 irq_stat)
-{
-  u8 _far *port_mmio = port_base(ai, p);
-  int reset_port = 0;
-
-  /* Handle adapter and interface errors. Those typically require a port
-   * reset, or worse.
-   */
-  if (irq_stat & PORT_IRQ_UNK_FIS) {
-    u32 _far *unk = (u32 _far *) (port_dma_base(ai, p)->rx_fis + RX_FIS_UNK);
-    dprintf("warning: unknown FIS %08lx %08lx %08lx %08lx\n",
-            unk[0], unk[1], unk[2], unk[3]);
-    reset_port = 1;
-  }
-  if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
-    dprintf("warning: host bus [data] error for port #%d\n", p);
-    reset_port = 1;
-  }
-  if (irq_stat & PORT_IRQ_IF_ERR && !(ai->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)) {
-    dprintf("warning: interface fatal error for port #%d\n", p);
-    reset_port = 1;
-  }
-  if (reset_port) {
-    /* need to reset the port; leave this to the reset context hook */
-    ports_to_reset[ad_no(ai)] |= 1UL << p;
-    DevHelp_ArmCtxHook(0, reset_ctxhook_h);
-
-    /* no point analyzing device errors after a reset... */
-    return;
-  } 
-
-  /* Handle device-specific errors. Those errors typically involve restarting
-   * the corresponding port to resume operations which can take some time,
-   * thus we need to offload this functionality to the restart context hook.
-   */
-  if (irq_stat & PORT_IRQ_TF_ERR) {
-    ports_to_restart[ad_no(ai)] |= 1UL << p;
-    DevHelp_ArmCtxHook(0, restart_ctxhook_h);
-  }
-}
-   
-/******************************************************************************
- * Get device or media geometry. Device and media geometry are expected to be
- * the same for non-removable devices.
- */
-void ahci_get_geometry(IORBH _far *iorb)
-{
-  dprintf("ahci_get_geometry(%d.%d.%d)\n", (int) iorb_unit_adapter(iorb),
-          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb));
-
-  ahci_exec_iorb(iorb, 0, cmd_func(iorb, get_geometry));
-}
-
-/******************************************************************************
- * Test whether unit is ready.
- */
-void ahci_unit_ready(IORBH _far *iorb)
-{
-  dprintf("ahci_unit_ready(%d.%d.%d)\n", (int) iorb_unit_adapter(iorb),
-          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb));
-
-  ahci_exec_iorb(iorb, 0, cmd_func(iorb, unit_ready));
-}
-
-/******************************************************************************
- * Read sectors from AHCI device.
- */
-void ahci_read(IORBH _far *iorb)
-{
-  dprintf("ahci_read(%d.%d.%d, %ld, %ld)\n", (int) iorb_unit_adapter(iorb),
-          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb),
-          (long) ((IORB_EXECUTEIO _far *) iorb)->RBA,
-          (long) ((IORB_EXECUTEIO _far *) iorb)->BlockCount);
-
-  ahci_exec_iorb(iorb, 1, cmd_func(iorb, read));
-}  
-
-/******************************************************************************
- * Verify readability of sectors on AHCI device.
- */
-void ahci_verify(IORBH _far *iorb)
-{
-  dprintf("ahci_verify(%d.%d.%d, %ld, %ld)\n", (int) iorb_unit_adapter(iorb),
-          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb),
-          (long) ((IORB_EXECUTEIO _far *) iorb)->RBA,
-          (long) ((IORB_EXECUTEIO _far *) iorb)->BlockCount);
-
-  ahci_exec_iorb(iorb, 0, cmd_func(iorb, verify));
-}
-
-/******************************************************************************
- * Write sectors to AHCI device.
- */
-void ahci_write(IORBH _far *iorb)
-{
-  dprintf("ahci_write(%d.%d.%d, %ld, %ld)\n", (int) iorb_unit_adapter(iorb),
-          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb),
-          (long) ((IORB_EXECUTEIO _far *) iorb)->RBA,
-          (long) ((IORB_EXECUTEIO _far *) iorb)->BlockCount);
-
-  ahci_exec_iorb(iorb, 1, cmd_func(iorb, write));
-}
-
-/******************************************************************************
- * Execute SCSI (ATAPI) command.
- */
-void ahci_execute_cdb(IORBH _far *iorb)
-{
-  int a = iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-
-  dphex(((IORB_ADAPTER_PASSTHRU _far *) iorb)->pControllerCmd,
-        ((IORB_ADAPTER_PASSTHRU _far *) iorb)->ControllerCmdLen,
-        "ahci_execute_cdb(%d.%d.%d)", (int) iorb_unit_adapter(iorb),
-        (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb));
-  
-  if (ad_infos[a].ports[p].devs[d].atapi) {
-    ahci_exec_iorb(iorb, 0, atapi_execute_cdb);
-  } else {
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-  }
-}
-
-/******************************************************************************
- * Execute ATA command.
- */
-void ahci_execute_ata(IORBH _far *iorb)
-{
-  int a = iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-  
-  dphex(((IORB_ADAPTER_PASSTHRU _far *) iorb)->pControllerCmd,
-        ((IORB_ADAPTER_PASSTHRU _far *) iorb)->ControllerCmdLen,
-        "ahci_execute_cdb(%d.%d.%d)", (int) iorb_unit_adapter(iorb),
-        (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb));
-  
-  if (ad_infos[a].ports[p].devs[d].atapi) {
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-  } else {
-    ahci_exec_iorb(iorb, 0, ata_execute_ata);
-  }
-}
-
-/******************************************************************************
- * Set up device attached to the specified port based on ATA_IDENTFY_DEVICE or
- * ATA_IDENTFY_PACKET_DEVICE data.
- *
- * NOTE: Port multipliers are not supported, yet, thus the device number is
- *       expected to be 0 for the time being.
- */
-static void ahci_setup_device(AD_INFO *ai, int p, int d, u16 *id_buf)
-{
-  DEVICESTRUCT ds;
-  ADJUNCT adj;
-  HDEVICE dh;
-
-  if (ai->port_max < p) {
-    ai->port_max = p;
-  }
-  if (ai->ports[p].dev_max < d) {
-    ai->ports[p].dev_max = d;
-  }
-  memset(ai->ports[p].devs + d, 0x00, sizeof(*ai->ports[p].devs));
-
-  /* set generic device information (assuming an ATA disk device for now) */
-  ai->ports[p].devs[d].present   = 1;
-  ai->ports[p].devs[d].removable = (id_buf[ATA_ID_CONFIG] & 0x0080U) != 0;
-  ai->ports[p].devs[d].dev_type  = UIB_TYPE_DISK;
-
-  if (id_buf[ATA_ID_CONFIG] & 0x8000U) {
-    /* this is an ATAPI device; augment device information */
-    ai->ports[p].devs[d].atapi     = 1;
-    ai->ports[p].devs[d].atapi_16  = (id_buf[ATA_ID_CONFIG] & 0x0001U) != 0;
-    ai->ports[p].devs[d].dev_type  = (id_buf[ATA_ID_CONFIG] & 0x1f00U) >> 8;
-
-  } else {
-    /* complete ATA-specific device information */
-    ai->ports[p].devs[d].ncq_max   = id_buf[ATA_ID_QUEUE_DEPTH] & 0x001fU;
-    if (id_buf[ATA_ID_CFS_ENABLE_2] & 0x40) {
-      ai->ports[p].devs[d].lba48   = 1;
-    }
-  }
-
-  dprintf("found device %d.%d.%d: removable = %d, dev_type = %d, atapi = %d\n",
-          ad_no(ai), p, d,
-          ai->ports[p].devs[d].removable,
-          ai->ports[p].devs[d].dev_type,
-          ai->ports[p].devs[d].atapi);
-
-  /* add device to resource manager; we don't really care about errors here */
-  memset(&ds, 0x00, sizeof(ds));
-  memset(&adj, 0x00, sizeof(adj));
-
-  adj.pNextAdj           = NULL;
-  adj.AdjLength          = sizeof(adj);
-  adj.AdjType            = ADJ_ADD_UNIT;
-  adj.Add_Unit.ADDHandle = rm_drvh;
-
-  ds.DevDescriptName = ata_dev_name(id_buf);
-  ds.DevFlags        = (ai->ports[p].devs[d].removable) ? DS_REMOVEABLE_MEDIA
-                                                        : DS_FIXED_LOGICALNAME;
-  ds.DevType         = ai->ports[p].devs[d].dev_type;
-
-  RMCreateDevice(rm_drvh, &dh, &ds, ai->rm_adh, NULL);
-
-  /* try to detect virtualbox environment to enable a hack for IRQ routing */
-  if (ai == ad_infos && p == 7 &&
-      ai->pci->vendor == 0x8086 && ai->pci->device == 0x2829 &&
-      !memcmp(ds.DevDescriptName, "VBOX HARDDISK", 13)) {
-    /* running inside virtualbox */
-    pci_hack_virtualbox();
-  }
-}
-
-/******************************************************************************
- * Timeout handler for I/O commands. Since timeout handling can involve
- * lengthy operations like port resets, the main code is located in a
- * separate function which is invoked via a context hook.
- */
-static void _far timeout_callback(ULONG timer_handle, ULONG p1, ULONG p2)
-{
-  IORBH _far *iorb = (IORBH _far *) p1;
-  int a = iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-
-  ADD_CancelTimer(timer_handle);
-  dprintf("timeout for IORB %Fp\n", iorb);
-
-  /* Move the timed-out IORB to the abort queue. Since it's possible that the
-   * IORB has completed after the timeout has expired but before we got to
-   * this line of code, we'll check the return code of iorb_queue_del(): If it
-   * returns an error, the IORB must have completed a few microseconds ago and
-   * there is no timeout.
-   */
-  spin_lock(drv_lock);
-  if (iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb) == 0) {
-    iorb_queue_add(&abort_queue, iorb);
-    iorb->ErrorCode = IOERR_ADAPTER_TIMEOUT;
-  }
-  spin_unlock(drv_lock);
-
-  /* Trigger abort processing function. We don't really care whether this
-   * succeeds because the only reason why it would fail should be multiple
-   * calls to DevHelp_ArmCtxHook() before the context hook had a chance to
-   * start executing, which leaves two scenarios:
-   *
-   *  - We succeded in arming the context hook. Fine.
-   *
-   *  - We armed the context hook a second time before it had a chance to
-   *    start executing. In this case, the already scheduled context hook
-   *    will process our IORB as well.
-   */
-  DevHelp_ArmCtxHook(0, reset_ctxhook_h);
-}
-
Index: trunk/src/ahci.h
===================================================================
--- trunk/src/ahci.h	(revision 12)
+++ 	(revision )
@@ -1,260 +1,0 @@
-/******************************************************************************
- * ahci.h - AHCI-specific constants for os2ahci.h
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* ----------------------------- include files ----------------------------- */
-
-/* -------------------------- macros and constants ------------------------- */
-
-/******************************************************************************
- * AHCI flags and constants; those were initially copied from the Linux AHCI
- * driver but converted to macros because enums are 16 bits for OS/2 drivers
- * (unless we use KEE and a 32-bit compiler, which we don't)
- *
- * Changes from the Linux source:
- *
- *  - reduced AHCI_MAX_SG from 168 to 48 because the port-specific DMA scratch
- *    buffer needs to be less than 64K to allow mapping the whole DMA area to a
- *    16-bit memory segment
- *
- *  - added AHCI_MAX_SG_ELEMENT_LEN constant
- *
- *  - replaced much of the top-level size/offset math with real structs and
- *    corresponding sizeof() directives.
- */
-#define AHCI_PCI_BAR            5
-#define AHCI_MAX_PORTS          32
-#define AHCI_MAX_SG             48 /* hardware max is 64K */
-#define AHCI_MAX_SG_ELEMENT_LEN (1UL << 22)
-#define AHCI_MAX_CMDS           32
-#define AHCI_RX_FIS_SZ          256
-
-/* port-specific DMA scratch buffer aligned to 1024 bytes */
-#define AHCI_PORT_PRIV_DMA_SZ   (((sizeof(AHCI_PORT_DMA) + 1023U) / 1024U) * 1024U)
-
-#define AHCI_IRQ_ON_SG          (1UL << 31)
-#define AHCI_CMD_ATAPI          (1UL << 5)
-#define AHCI_CMD_WRITE          (1UL << 6)
-#define AHCI_CMD_PREFETCH       (1UL << 7)
-#define AHCI_CMD_RESET          (1UL << 8)
-#define AHCI_CMD_CLR_BUSY       (1UL << 10)
-
-#define RX_FIS_D2H_REG          0x40 /* offset of D2H Register FIS data */
-#define RX_FIS_SDB              0x58 /* offset of SDB FIS data */
-#define RX_FIS_UNK              0x60 /* offset of Unknown FIS data */
-
-#define board_ahci              0
-#define board_ahci_vt8251       1
-#define board_ahci_ign_iferr    2
-#define board_ahci_sb600        3
-#define board_ahci_mv           4
-#define board_ahci_sb700        5 /* for SB700 and SB800 */
-#define board_ahci_mcp65        6
-#define board_ahci_nopmp        7
-#define board_ahci_yesncq       8
-#define board_ahci_nosntf       9
-
-/* global controller registers */
-#define HOST_CAP                0x00 /* host capabilities */
-#define HOST_CTL                0x04 /* global host control */
-#define HOST_IRQ_STAT           0x08 /* interrupt status */
-#define HOST_PORTS_IMPL         0x0c /* bitmap of implemented ports */
-#define HOST_VERSION            0x10 /* AHCI spec. version compliancy */
-#define HOST_CCC                0x14 /* Command Completion Coalescing Control */
-#define HOST_CCC_PORTS          0x18 /* CCC ports */
-#define HOST_EM_LOC             0x1c /* Enclosure Management location */
-#define HOST_EM_CTL             0x20 /* Enclosure Management Control */
-#define HOST_CAP2               0x24 /* host capabilities, extended */
-
-/* HOST_CTL bits */
-#define HOST_RESET              (1UL << 0)  /* reset controller; self-clear */
-#define HOST_IRQ_EN             (1UL << 1)  /* global IRQ enable */
-#define HOST_AHCI_EN            (1UL << 31) /* AHCI enabled */
-
-/* HOST_CAP bits */
-#define HOST_CAP_SXS            (1UL << 5)  /* Supports External SATA */
-#define HOST_CAP_EMS            (1UL << 6)  /* Enclosure Management support */
-#define HOST_CAP_CCC            (1UL << 7)  /* Command Completion Coalescing */
-#define HOST_CAP_PART           (1UL << 13) /* Partial state capable */
-#define HOST_CAP_SSC            (1UL << 14) /* Slumber state capable */
-#define HOST_CAP_PIO_MULTI      (1UL << 15) /* PIO multiple DRQ support */
-#define HOST_CAP_FBS            (1UL << 16) /* FIS-based switching support */
-#define HOST_CAP_PMP            (1UL << 17) /* Port Multiplier support */
-#define HOST_CAP_ONLY           (1UL << 18) /* Supports AHCI mode only */
-#define HOST_CAP_CLO            (1UL << 24) /* Command List Override support */
-#define HOST_CAP_LED            (1UL << 25) /* Supports activity LED */
-#define HOST_CAP_ALPM           (1UL << 26) /* Aggressive Link PM support */
-#define HOST_CAP_SSS            (1UL << 27) /* Staggered Spin-up */
-#define HOST_CAP_MPS            (1UL << 28) /* Mechanical presence switch */
-#define HOST_CAP_SNTF           (1UL << 29) /* SNotification register */
-#define HOST_CAP_NCQ            (1UL << 30) /* Native Command Queueing */
-#define HOST_CAP_64             (1UL << 31) /* PCI DAC (64-bit DMA) support */
-
-/* HOST_CAP2 bits */
-#define HOST_CAP2_BOH           (1UL << 0)  /* BIOS/OS handoff supported */
-#define HOST_CAP2_NVMHCI        (1UL << 1)  /* NVMHCI supported */
-#define HOST_CAP2_APST          (1UL << 2)  /* Automatic partial to slumber */
-
-/* registers for each SATA port */
-#define PORT_LST_ADDR           0x00 /* command list DMA addr */
-#define PORT_LST_ADDR_HI        0x04 /* command list DMA addr hi */
-#define PORT_FIS_ADDR           0x08 /* FIS rx buf addr */
-#define PORT_FIS_ADDR_HI        0x0c /* FIS rx buf addr hi */
-#define PORT_IRQ_STAT           0x10 /* interrupt status */
-#define PORT_IRQ_MASK           0x14 /* interrupt enable/disable mask */
-#define PORT_CMD                0x18 /* port command */
-#define PORT_TFDATA             0x20 /* taskfile data */
-#define PORT_SIG                0x24 /* device TF signature */
-#define PORT_CMD_ISSUE          0x38 /* command issue */
-#define PORT_SCR_STAT           0x28 /* SATA phy register: SStatus */
-#define PORT_SCR_CTL            0x2c /* SATA phy register: SControl */
-#define PORT_SCR_ERR            0x30 /* SATA phy register: SError */
-#define PORT_SCR_ACT            0x34 /* SATA phy register: SActive */
-#define PORT_SCR_NTF            0x3c /* SATA phy register: SNotification */
-
-/* PORT_IRQ_{STAT,MASK} bits */
-#define PORT_IRQ_COLD_PRES      (1UL << 31) /* cold presence detect */
-#define PORT_IRQ_TF_ERR         (1UL << 30) /* task file error */
-#define PORT_IRQ_HBUS_ERR       (1UL << 29) /* host bus fatal error */
-#define PORT_IRQ_HBUS_DATA_ERR  (1UL << 28) /* host bus data error */
-#define PORT_IRQ_IF_ERR         (1UL << 27) /* interface fatal error */
-#define PORT_IRQ_IF_NONFATAL    (1UL << 26) /* interface non-fatal error */
-#define PORT_IRQ_OVERFLOW       (1UL << 24) /* xfer exhausted available S/G */
-#define PORT_IRQ_BAD_PMP        (1UL << 23) /* incorrect port multiplier */
-#define PORT_IRQ_PHYRDY         (1UL << 22) /* PhyRdy changed */
-#define PORT_IRQ_DEV_ILCK       (1UL << 7)  /* device interlock */
-#define PORT_IRQ_CONNECT        (1UL << 6)  /* port connect change status */
-#define PORT_IRQ_SG_DONE        (1UL << 5)  /* descriptor processed */
-#define PORT_IRQ_UNK_FIS        (1UL << 4)  /* unknown FIS rx'd */
-#define PORT_IRQ_SDB_FIS        (1UL << 3)  /* Set Device Bits FIS rx'd */
-#define PORT_IRQ_DMAS_FIS       (1UL << 2)  /* DMA Setup FIS rx'd */
-#define PORT_IRQ_PIOS_FIS       (1UL << 1)  /* PIO Setup FIS rx'd */
-#define PORT_IRQ_D2H_REG_FIS    (1UL << 0)  /* D2H Register FIS rx'd */
-#define PORT_IRQ_FREEZE         (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR   | \
-                                 PORT_IRQ_CONNECT  | PORT_IRQ_PHYRDY   | \
-                                 PORT_IRQ_UNK_FIS  | PORT_IRQ_BAD_PMP)
-#define PORT_IRQ_ERROR          (PORT_IRQ_FREEZE   | PORT_IRQ_TF_ERR   | \
-                                 PORT_IRQ_HBUS_DATA_ERR)
-#define DEF_PORT_IRQ            (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE     | \
-                                 PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS  | \
-                                 PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
-
-/* PORT_CMD bits */
-#define PORT_CMD_ASP            (1UL << 27) /* Aggressive Slumber/Partial */
-#define PORT_CMD_ALPE           (1UL << 26) /* Aggressive Link PM enable */
-#define PORT_CMD_ATAPI          (1UL << 24) /* Device is ATAPI */
-#define PORT_CMD_PMP            (1UL << 17) /* PMP attached */
-#define PORT_CMD_LIST_ON        (1UL << 15) /* cmd list DMA engine running */
-#define PORT_CMD_FIS_ON         (1UL << 14) /* FIS DMA engine running */
-#define PORT_CMD_FIS_RX         (1UL << 4)  /* Enable FIS receive DMA engine */
-#define PORT_CMD_CLO            (1UL << 3)  /* Command list override */
-#define PORT_CMD_POWER_ON       (1UL << 2)  /* Power up device */
-#define PORT_CMD_SPIN_UP        (1UL << 1)  /* Spin up device */
-#define PORT_CMD_START          (1UL << 0)  /* Enable port DMA engine */
-
-#define PORT_CMD_ICC_MASK       (0xfUL << 28) /* i/f ICC state mask */
-#define PORT_CMD_ICC_ACTIVE     (0x1UL << 28) /* Put i/f in active state */
-#define PORT_CMD_ICC_PARTIAL    (0x2UL << 28) /* Put i/f in partial state */
-#define PORT_CMD_ICC_SLUMBER    (0x6UL << 28) /* Put i/f in slumber state */
-
-/* driver status bits */
-#define AHCI_HFLAG_NO_NCQ               (1UL << 0)  /* no native cmd queuing */
-#define AHCI_HFLAG_IGN_IRQ_IF_ERR       (1UL << 1)  /* ignore IRQ_IF_ERR */
-#define AHCI_HFLAG_IGN_SERR_INTERNAL    (1UL << 2)  /* ignore SERR_INTERNAL */
-#define AHCI_HFLAG_32BIT_ONLY           (1UL << 3)  /* force 32bit */
-#define AHCI_HFLAG_MV_PATA              (1UL << 4)  /* PATA port */
-#define AHCI_HFLAG_NO_MSI               (1UL << 5)  /* no PCI MSI */
-#define AHCI_HFLAG_NO_PMP               (1UL << 6)  /* no PMP */
-#define AHCI_HFLAG_NO_HOTPLUG           (1UL << 7)  /* ignore PxSERR.DIAG.N */
-#define AHCI_HFLAG_SECT255              (1UL << 8)  /* max 255 sectors */
-#define AHCI_HFLAG_YES_NCQ              (1UL << 9)  /* force NCQ cap on */
-#define AHCI_HFLAG_NO_SUSPEND           (1UL << 10) /* don't suspend */
-#define AHCI_HFLAG_SRST_TOUT_IS_OFFLINE (1UL << 11) /* treat SRST timeout as
-                                                      link offline */
-#define AHCI_HFLAG_NO_SNTF              (1UL << 12) /* no sntf */
-
-#define ICH_MAP                         0x90 /* ICH MAP register */
-
-/* em constants */
-#define EM_MAX_SLOTS                    8
-#define EM_MAX_RETRY                    5
-
-/* em_ctl bits */
-#define EM_CTL_RST                      (1UL << 9)  /* Reset */
-#define EM_CTL_TM                       (1UL << 8)  /* Transmit Message */
-#define EM_CTL_ALHD                     (1UL << 26) /* Activity LED */
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* Primitive types
- *
- * Note: Since OS/2 is essentially an x86 OS and this driver, as well as the
- *       interface it's developed for, is based on x86 design patterns, we're
- *       not even going to start making a difference between little and big
- *       endian architectures. PCI is little endian, AHCI is little endian,
- *       x86 is little endian, and that's it.
- */
-typedef unsigned char    u8;
-typedef unsigned short   u16;
-typedef unsigned long    u32;
-
-/* AHCI S/G structure */
-typedef struct {
-  u32   addr;            /* address of S/G element */
-  u32   addr_hi;         /* address of S/G element (upper 32 bits) */
-  u32   reserved;
-  u32   size;            /* size of S/G element - 1; the high 10 bits are flags:
-                          *  31    : interrupt on completion of this S/G
-                          *  30-22 : reserved */
-} AHCI_SG;
-
-/* AHCI command header */
-typedef struct {
-  u32   options;         /* command options */
-  u32   status;          /* command status */
-  u32   tbl_addr;        /* command table address */
-  u32   tbl_addr_high;   /* command table address (upper 32 bits) */
-  u32   reserved[4];
-} AHCI_CMD_HDR;
-
-/* AHCI command table */
-typedef struct {
-  u8           cmd_fis[64];                /* ATA command FIS */
-  u8           atapi_cmd[16];              /* ATAPI command */
-  u8           reserved[48];
-  AHCI_SG      sg_list[AHCI_MAX_SG];       /* AHCI S/G list */
-} AHCI_CMD_TBL;
-
-/* AHCI port DMA scratch area */
-typedef struct {
-  AHCI_CMD_HDR   cmd_hdr[AHCI_MAX_CMDS];   /* command headers */
-  u8             rx_fis[AHCI_RX_FIS_SZ];   /* FIS RX area */
-  AHCI_CMD_TBL   cmd_tbl[AHCI_MAX_CMDS];   /* command table */
-} AHCI_PORT_DMA;
-
-/* AHCI port BIOS configuration save area */
-typedef struct {
-  u32   cmd_list;                          /* cmd list base address */
-  u32   cmd_list_h;                        /* cmd list base address high */
-  u32   fis_rx;                            /* FIS receive buffer */
-  u32   fis_rx_h;                          /* FIS receive bufffer high */
-  u32   irq_mask;                          /* IRQ mask */
-} AHCI_PORT_CFG;
-
Index: trunk/src/ata.c
===================================================================
--- trunk/src/ata.c	(revision 12)
+++ 	(revision )
@@ -1,778 +1,0 @@
-/******************************************************************************
- * ata.c - ATA command processing
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-#include "ata.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* -------------------------- function prototypes -------------------------- */
-
-/* ------------------------ global/static variables ------------------------ */
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * Initialize AHCI command slot, FIS and S/G list for the specified ATA
- * command. The command parameters are passed as a variable argument list
- * of type and value(s). The list is terminated by AP_END.
- *
- * Notes:
- *
- *  - The specified command slot is expected to be idle; no checks are
- *    performed to prevent messing with a busy port.
- *
- *  - Port multipliers are not supported, yet, thus 'd' should always
- *    be 0 for the time being.
- *
- *  - 'cmd' is passwd as 16-bit integer because the compiler would push
- *    a 'u8' as 16-bit value (it's a fixed argument) and the stdarg
- *    macros would screw up the address of the first variable argument
- *    if the size of the last fixed argument wouldn't match what the
- *    compiler pushed on the stack.
- *
- * Return values:
- *    0 : success
- *  > 0 : could not map all S/G entries; the return value is the number of
- *        S/G entries that could be mapped.
- *  < 0 : other error
- */
-int ata_cmd(AD_INFO *ai, int p, int d, int slot, int cmd, ...)
-{
-  va_list va;
-  va_start(va, cmd);
-  return(v_ata_cmd(ai, p, d, slot, cmd, va));
-}
-
-int v_ata_cmd(AD_INFO *ai, int p, int d, int slot, int cmd, va_list va)
-{
-  AHCI_PORT_DMA _far *dma_base_virt;
-  AHCI_CMD_HDR _far *cmd_hdr;
-  AHCI_CMD_TBL _far *cmd_tbl;
-  SCATGATENTRY _far *sg_list = NULL;
-  SCATGATENTRY sg_single;
-  ATA_PARM ap;
-  ATA_CMD ata_cmd;
-  void _far *atapi_cmd = NULL;
-  u32 dma_base_phys;
-  u16 atapi_cmd_len = 0;
-  u16 ahci_flags = 0;
-  u16 sg_cnt = 0;
-  int i;
-  int n;
-
-  /* --------------------------------------------------------------------------
-   * Initialize ATA command. The ATA command is set up with the main command
-   * value and a variable list of additional parameters such as the sector
-   * address, transfer count, ...
-   */
-  memset(&ata_cmd, 0x00, sizeof(ata_cmd));
-  ata_cmd.cmd = (u8) cmd;
-
-  /* parse variable arguments */
-  do {
-    switch ((ap = va_arg(va, ATA_PARM))) {
-    
-    case AP_AHCI_FLAGS:
-      ahci_flags |= va_arg(va, u16);
-      break;
-
-    case AP_WRITE:
-      ahci_flags |= AHCI_CMD_WRITE;
-      break;
-
-    case AP_FEATURES:
-      /* ATA features word */
-      ata_cmd.features = va_arg(va, u16);
-      break;
-
-    case AP_COUNT:
-      /* transfer count */
-      ata_cmd.count = va_arg(va, u16);
-      break;
-
-    case AP_SECTOR_28:
-      /* 28-bit sector address */
-      ata_cmd.lba_l = va_arg(va, u32);
-      if (ata_cmd.lba_l & 0xf0000000UL) {
-        dprintf("error: LBA-28 address %ld has more than 28 bits\n", ata_cmd.lba_l);
-        return(-1);
-      }
-      break;
-
-    case AP_SECTOR_48:
-      /* 48-bit sector address */
-      ata_cmd.lba_l = va_arg(va, u32);
-      ata_cmd.lba_h = va_arg(va, u16);
-      break;
-
-    case AP_DEVICE:
-      /* ATA device byte */
-      ata_cmd.device = va_arg(va, u16) >> 8;
-      break;
-
-    case AP_SGLIST:
-      /* scatter/gather list in SCATGATENTRY/count format */
-      sg_list = va_arg(va, void _far *);
-      sg_cnt = va_arg(va, u16);
-      break;
-
-    case AP_VADDR:
-      /* virtual buffer address in addr/len format (up to 4K) */
-      DevHelp_VirtToPhys(va_arg(va, void _far *), &sg_single.ppXferBuf);
-      sg_single.XferBufLen = va_arg(va, u16);
-      sg_list = &sg_single;
-      sg_cnt = 1;
-      break;
-
-    case AP_ATAPI_CMD:
-      atapi_cmd = va_arg(va, void _far *);
-      atapi_cmd_len = va_arg(va, u16);
-      ahci_flags |= AHCI_CMD_ATAPI;
-      break;
-
-    case AP_END:
-      break;
-
-    default:
-      dprintf("error: v_ata_cmd() called with invalid parameter type (%d)\n", (int) ap);
-      return(-1);
-    }
-
-  } while (ap != AP_END);
-
-  /* --------------------------------------------------------------------------
-   * Fill in AHCI ATA command information. This includes the port command slot,
-   * the corresponding command FIS and the S/G list. The layout of the AHCI
-   * port DMA region is based on the Linux AHCI driver and looks like this:
-   *
-   *  - 32 AHCI command headers (AHCI_CMD_HDR) with 32 bytes, each
-   *  -  1 FIS receive area with 256 bytes (AHCI_RX_FIS_SZ)
-   *  - 32 AHCI command tables, each consisting of
-   *    -  64 bytes for command FIS
-   *    -  16 bytes for ATAPI comands
-   *    -  48 bytes reserved
-   *    -  48 S/G entries (AHCI_SG) with 32 bytes, each
-   *
-   * Since the whole DMA buffer for all ports is larger than 64KB and we need
-   * multiple segments to address all of them, there are no virtual pointers
-   * to the individual elements in AD_INFO. Instead, we're relying on macros
-   * for getting the base address of a particular port's DMA region, then
-   * map a structure on top of that for convenience (AHCI_PORT_DMA).
-   */
-  dma_base_virt = port_dma_base(ai, p);
-  dma_base_phys = port_dma_base_phys(ai, p);
-  
-  /* AHCI command header */
-  cmd_hdr = dma_base_virt->cmd_hdr + slot;
-  memset(cmd_hdr, 0x00, sizeof(*cmd_hdr));
-  cmd_hdr->options  = ((d & 0x0f) << 12);
-  cmd_hdr->options |= ahci_flags;  /* AHCI commaand flags */
-  cmd_hdr->options |= 5;           /* length of command FIS in 32-bit words */
-  cmd_hdr->tbl_addr = dma_base_phys + offsetof(AHCI_PORT_DMA, cmd_tbl[slot]);
-
-  /* AHCI command table */
-  cmd_tbl = dma_base_virt->cmd_tbl + slot;
-  memset(cmd_tbl, 0x00, sizeof(*cmd_tbl));
-  ata_cmd_to_fis(cmd_tbl->cmd_fis, &ata_cmd, d);
-
-  if (atapi_cmd != NULL) {
-    /* copy ATAPI command */
-    memcpy(cmd_tbl->atapi_cmd, atapi_cmd, atapi_cmd_len);
-  }
-
-  /* PRDT (S/G list)
-   *
-   *  - The S/G list for AHCI adapters is limited to 22 bits for the transfer
-   *    size of each element, thus we need to split S/G elements larger than
-   *    22 bits into 2 AHCI_SG elements.
-   *
-   *  - The S/G element size for AHCI is what the spec calls "'0' based"
-   *    (i.e. 0 means 1 bytes). On top of that, the spec requires S/G transfer
-   *    sizes to be even in the context of 16-bit transfers, thus bit '1'
-   *    always needs to be set.
-   *
-   *  - AHCI_MAX_SG_ELEMENT_LEN defines the maximum size of an AHCI S/G
-   *    element in bytes, ignoring the '0'-based methodology (i.e. 1 << 22).
-   *
-   *  - There's a limit on the maximum number of S/G elements in the port DMA
-   *    buffer (AHCI_MAX_SG) which is lower than the HW maximum. It's beyond
-   *    the control of this function to split commands which require more
-   *    than AHCI_MAX_SG entries. In order to help the caller, the return value
-   *    of this function will indicate how many OS/2 S/G entries were
-   *    successfully mapped.
-   *
-   */
-  for (i = n = 0; i < sg_cnt; i++) {
-    u32 sg_addr = sg_list[i].ppXferBuf;
-    u32 sg_size = sg_list[i].XferBufLen;
-
-    do {
-      u32 chunk = (sg_size > AHCI_MAX_SG_ELEMENT_LEN) ? AHCI_MAX_SG_ELEMENT_LEN
-                                                      : sg_size;
-      if (n >= AHCI_MAX_SG) {
-        /* couldn't store all S/G elements in our DMA buffer */
-        ddprintf("ata_cmd(): too many S/G elements\n");
-        return(i - 1);
-      }
-      ddprintf("s/g list element: addr = 0x%08lx, size = 0x%04lx\n", sg_addr, chunk);
-      cmd_tbl->sg_list[n].addr = sg_addr;
-      cmd_tbl->sg_list[n].size = chunk - 1;
-      sg_addr += chunk;
-      sg_size -= chunk;
-      n++;
-    } while (sg_size > 0);
-  }
-
-  /* set final S/G count in AHCI command header */
-  cmd_hdr->options |= (u32) n << 16;
-
-  if (debug >= 2) {
-    printf("ATA command for %d.%d.%d:\n", ad_no(ai), p, d);
-    phex(cmd_hdr, offsetof(AHCI_CMD_HDR, reserved), "cmd_hdr:   ");
-    phex(&ata_cmd, sizeof(ata_cmd), "ata_cmd:   ");
-    if (atapi_cmd != NULL) {
-      phex(atapi_cmd, atapi_cmd_len, "atapi_cmd: ");
-    }
-    if (n > 0) {
-      phex(cmd_tbl->sg_list, sizeof(*cmd_tbl->sg_list) * n, "sg_list:   ");
-    }
-  }
-
-  return(0);
-}
-
-/******************************************************************************
- * Fill SATA command FIS with values extracted from an ATA command structure.
- * The command FIS buffer (fis) is expected to be initialized to 0s. The
- * structure of the FIS maps to the ATA shadow register block, including
- * registers which can be written twice to store 16 bits (called 'exp').
- *
- * The FIS structure looks like this (using LSB notation):
- *
- *       +----------------+----------------+----------------+----------------+
- *  00   | FIS type (27h) |   C|R|R|R|PMP  |   Command      |  Features      |
- *       +----------------+----------------+----------------+----------------+
- *  04   |   LBA 7:0      |   LBA 15:8     |   LBA 23:16    |  R|R|R|D|Head  |
- *       +----------------+----------------+----------------+----------------+
- *  08   |   LBA 31:24    |   LBA 40:32    |   LBA 47:40    |  Features exp  |
- *       +----------------+----------------+----------------+----------------+
- *  12   |   Count 7:0    |   Count 15:8   |   Reserved     |  Control       |
- *       +----------------+----------------+----------------+----------------+
- *  16   |   Reserved     |   Reserved     |   Reserved     |  Reserved      |
- *       +----------------+----------------+----------------+----------------+
- */
-void ata_cmd_to_fis(u8 _far *fis, ATA_CMD _far *ata_cmd, int d)
-{
-  fis[0]  = 0x27;                  /* register - host to device FIS */
-  fis[1]  = (u8) (d & 0xf);        /* port multiplier number */
-  fis[1] |= 0x80;                  /* bit 7 indicates Command FIS */
-  fis[2]  = (u8) ata_cmd->cmd;
-  fis[3]  = (u8) ata_cmd->features;
-
-  fis[4]  = (u8) ata_cmd->lba_l;
-  fis[5]  = (u8) (ata_cmd->lba_l >> 8);
-  fis[6]  = (u8) (ata_cmd->lba_l >> 16);
-  fis[7]  = (u8) ata_cmd->device;
-
-  fis[8]  = (u8) (ata_cmd->lba_l >> 24);
-  fis[9]  = (u8) ata_cmd->lba_h;
-  fis[10] = (u8) (ata_cmd->lba_h >> 8);
-  fis[11] = (u8) (ata_cmd->features >> 8);
-
-  fis[12] = (u8) ata_cmd->count;
-  fis[13] = (u8) (ata_cmd->count >> 8);
-}
-
-/******************************************************************************
- * Get index in S/G list for the number of transferred sectors in the IORB.
- *
- * Returning io->cSGList indicates an error.
- *
- * NOTE: OS/2 makes sure S/G lists are set up such that entries at the HW
- *       limit will never cross sector boundaries. This means that splitting
- *       S/G lists into multiple commands can be done without editing the S/G
- *       lists.
- */
-u16 ata_get_sg_indx(IORB_EXECUTEIO _far *io)
-{
-  ULONG offset = io->BlocksXferred * io->BlockSize;
-  USHORT i;
-
-  for (i = 0; i < io->cSGList && offset > 0; i++) {
-    offset -= io->pSGList[i].XferBufLen;
-  }
-
-  return(i);
-}
-
-/******************************************************************************
- * Get max S/G count which will fit into our HW S/G buffers. This function is
- * called when the S/G list is too long and we need to split the IORB into
- * multiple commands. It returns both the number of sectors and S/G list
- * elements that we can handle in a single command.
- *
- * The parameter 'sg_indx' indicates the current start index in the S/G list
- * (0 if this is the first command iteration).
- *
- * The parameter 'sg_max' is the return value of v_ata_cmd() and indicates
- * how many S/G elements were successfully mapped. Whatever we return needs to
- * be less or equal to this value.
- *
- * Returning 0 in *sg_cnt indicates an error.
- *
- * NOTE: OS/2 makes sure S/G lists are set up such that entries at HW limits
- *       will never cross sector boundaries. This means that splitting S/G
- *       lists into multiple commands can be done without editing S/G list
- *       elements. Since AHCI only allows 22 bits for each S/G element, the
- *       hardware limits are reported as AHCI_MAX_SG / 2 but will vary based
- *       on the actual length of S/G elements. This function looks for the
- *       maximum number of S/G elements that can be mapped on sector
- *       boundaries which will still fit into our HW S/G list.
- */
-void ata_max_sg_cnt(IORB_EXECUTEIO _far *io, USHORT sg_indx, USHORT sg_max,
-                    USHORT _far *sg_cnt, USHORT _far *sector_cnt)
-{
-  ULONG max_sector_cnt = 0;
-  USHORT max_sg_cnt = 0;
-  ULONG offset = 0;
-  USHORT i;
-
-  for (i = sg_indx; i < io->cSGList; i++) {
-    if (i - sg_indx >= sg_max) {
-      /* we're beyond the number of S/G elements we can map */
-      break;
-    }
-
-    offset += io->pSGList[i].XferBufLen;
-    if (offset % io->BlockSize == 0) {
-      /* this S/G element ends on a sector boundary */
-      max_sector_cnt = offset / io->BlockSize;
-      max_sg_cnt = i + 1;
-    }
-  }
-
-  /* return the best match we found so far (0 indicating failure) */
-  *sector_cnt = max_sector_cnt;
-  *sg_cnt = max_sg_cnt;
-}
-
-
-/******************************************************************************
- * Get device or media geometry. Device and media geometry are expected to be
- * the same for non-removable devices, which will always be the case for the
- * ATA devices we're dealing with (hard disks). ATAPI is a different story
- * and handled by atapi_get_geometry().
- */
-int ata_get_geometry(IORBH _far *iorb, int slot)
-{
-  ADD_WORKSPACE _far *aws = add_workspace(iorb);
-  int rc;
-
-  /* allocate buffer for ATA identify information */
-  if ((aws->buf = malloc(ATA_ID_WORDS * sizeof(u16))) == NULL) {
-    iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
-    return(-1);
-  }
-
-  /* request ATA identify information */
-  aws->ppfunc = ata_get_geometry_pp;
-  rc = ata_cmd(ad_infos + iorb_unit_adapter(iorb),
-               iorb_unit_port(iorb),
-               iorb_unit_device(iorb),
-               slot,
-               ATA_CMD_ID_ATA,
-               AP_VADDR, (void _far *) aws->buf, ATA_ID_WORDS * sizeof(u16),
-               AP_END);
-
-  if (rc != 0) {
-    free(aws->buf);
-    iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE);
-  }
-
-  return(rc);
-}
-
-/******************************************************************************
- * Post processing function for ata_get_geometry(): convert the ATA identify
- * information to OS/2 IOCC_GEOMETRY information.
- */
-void ata_get_geometry_pp(IORBH _far *iorb)
-{
-  GEOMETRY _far *geometry = ((IORB_GEOMETRY _far *) iorb)->pGeometry;
-  USHORT geometry_len =  ((IORB_GEOMETRY _far *) iorb)->GeometryLen;
-  u16 *id_buf = add_workspace(iorb)->buf;
-
-  /* Fill-in geometry information; os2ahci has been designed for devices
-   * adhering to the ATA spec v8 or later, thus the real geometry doesn't
-   * really matter (it's actually marked "obsolete" in the ATA 8 specs).
-   * In order to maintain compatibitily to the BIOS and partition tables,
-   * we'll use the same algorithm as used by typical PC BIOS versions:
-   *
-   *  - 512 bytes per sector
-   *  - 255 heads
-   *  -  63 sectors per track
-   *  -   x cylinders (calculated)
-   *
-   * Please note that os2ahci currently does not support ATA sectors larger
-   * than 512 bytes, therefore relies on the translation logic built into the
-   * corresponding ATA disks. In theory, partitions should be aligned to the
-   * large sectors to prevent needless mapping all over the place but HPFS
-   * uses logical block sizes smaller than the typical large sectors found on
-   * modern hard disks so this won't make much of a difference. Large sector
-   * support will be evaluated at a later time (it's unclear right now whether
-   * HPFS would even support anything larger than 512 bytes).
-   *
-   * Another limitation is that OS/2 has a 32-bit variable for the total number
-   * of sectors, limiting the maximum capacity to roughly 2TB. This is another
-   * issue that needs to be addressed sooner or later; large sectors could
-   * raise this limit to something like 8TB but this is not really much of a
-   * difference. Maybe there's something in later DDKs that allows more than
-   * 32 bits?
-   */
-  memset(geometry, 0x00, geometry_len);
-
-  /* extract total number of sectors */
-  if (id_buf[ATA_ID_CFS_ENABLE_2] & 0x40) {
-    /* 48-bit LBA supported */
-    if (id_buf[ATA_ID_LBA_CAPACITY_2 + 2] != 0) {
-      /* more than 32 bits for number of sectors */
-      dprintf("warning: limiting disk %d.%d.%d to 2TB\n",
-              iorb_unit_adapter(iorb), iorb_unit_port(iorb),
-              iorb_unit_device(iorb));
-      geometry->TotalSectors = 0xffffffffUL;
-    } else {
-      geometry->TotalSectors = *((u32 *) (id_buf + ATA_ID_LBA_CAPACITY_2));
-    }
-  } else {
-    /* 28-bit LBA */
-    geometry->TotalSectors = *((u32 *) (id_buf + ATA_ID_LBA_CAPACITY)) &
-                             0x0fffffffUL;
-  }
-
-  geometry->BytesPerSector  = 512;
-  geometry->NumHeads        = 255;
-  geometry->SectorsPerTrack = 63;
-  geometry->TotalCylinders  = geometry->TotalSectors /
-                               ((u32) geometry->NumHeads *
-                                (u32) geometry->SectorsPerTrack);
-
-  /* tell interrupt handler that this IORB is complete */
-  add_workspace(iorb)->complete = 1;
-}
-
-/******************************************************************************
- * Test whether unit is ready.
- */
-int ata_unit_ready(IORBH _far *iorb, int slot)
-{
-  /* This is a NOP for ATA devices (at least right now); returning an error
-   * without setting an error code means ahci_exec_iorb() will not queue any
-   * HW command and the IORB will complete successfully.
-   */
-  ((IORB_UNIT_STATUS _far *) iorb)->UnitStatus = US_READY | US_POWER;
-  return(-1);
-}
-
-/******************************************************************************
- * Read sectors from AHCI device.
- */
-int ata_read(IORBH _far *iorb, int slot)
-{
-  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
-  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
-  ULONG sector = io->RBA + io->BlocksXferred;
-  USHORT count = io->BlockCount - io->BlocksXferred;
-  USHORT sg_indx;
-  USHORT sg_cnt;
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-  int rc;
-
-  /* prepare read command while keeping an eye on S/G count limitations */
-  do {
-    sg_indx = ata_get_sg_indx(io);
-    sg_cnt = io->cSGList - sg_indx;
-
-    if (sector >= (1UL << 28) || count > 256) {
-      /* need LBA48 for this command */
-      if (!ai->ports[p].devs[d].lba48) {
-        iorb_seterr(iorb, IOERR_RBA_LIMIT);
-        return(-1);
-      }
-      rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ_EXT,
-                   AP_SECTOR_48, (u32) sector, (u16) 0,
-                   AP_COUNT,     (u16) count,
-                   AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
-                   AP_DEVICE,    0x4000,
-                   AP_END);
-    } else {
-      rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ,
-                   AP_SECTOR_28, (u32) sector,
-                   AP_COUNT,     (u16) count & 0xffU,
-                   AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
-                   AP_DEVICE,    0x4000,
-                   AP_END);
-    }
-
-    if (rc > 0) {
-      /* couldn't map all S/G elements */
-      ata_max_sg_cnt(io, sg_indx, (USHORT) rc, &sg_cnt, &count);
-    }
-  } while (rc > 0 && sg_cnt > 0);
-
-  if (rc == 0) {
-    add_workspace(iorb)->blocks = count;
-    add_workspace(iorb)->ppfunc = ata_read_pp;
-
-  } else if (rc > 0) {
-    iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD);
-
-  } else {
-    iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE);
-  }
-
-  return(rc);
-}
-
-/******************************************************************************
- * Post processing function for ata_read(); this function updates the
- * BlocksXferred counter in the IORB and, if not all blocks have been
- * transferred, requeues the IORB to process the remaining sectors.
- */
-void ata_read_pp(IORBH _far *iorb)
-{
-  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
-
-  io->BlocksXferred += add_workspace(iorb)->blocks;
-  ddprintf("ata_read_pp(): blocks transferred = %d\n", (int) io->BlocksXferred);
-
-  if (io->BlocksXferred >= io->BlockCount) {
-    /* we're done; tell IRQ handler the IORB is complete */
-    add_workspace(iorb)->complete = 1;
-  } else {
-    /* requeue this IORB for next iteration */
-    iorb_requeue(iorb);
-  }
-}
-
-/******************************************************************************
- * Verify readability of sectors on ATA device.
- */
-int ata_verify(IORBH _far *iorb, int slot)
-{
-  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
-  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-  int rc;
-
-  /* prepare verify command */
-  if (io->RBA >= (1UL << 28) || io->BlockCount > 256) {
-    /* need LBA48 for this command */
-    if (!ai->ports[p].devs[d].lba48) {
-      iorb_seterr(iorb, IOERR_RBA_LIMIT);
-      return(-1);
-    }
-    rc = ata_cmd(ai, p, d, slot, ATA_CMD_VERIFY_EXT,
-                 AP_SECTOR_48, (u32) io->RBA, (u16) 0,
-                 AP_COUNT,     (u16) io->BlockCount,
-                 AP_DEVICE,    0x4000,
-                 AP_END);
-  } else {
-    rc = ata_cmd(ai, p, d, slot, ATA_CMD_VERIFY,
-                 AP_SECTOR_28, (u32) io->RBA,
-                 AP_COUNT,     (u16) io->BlockCount & 0xffU,
-                 AP_END);
-  }
-
-  return(rc);
-}
-
-/******************************************************************************
- * Write sectors to AHCI device.
- */
-int ata_write(IORBH _far *iorb, int slot)
-{
-  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
-  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
-  ULONG sector = io->RBA + io->BlocksXferred;
-  USHORT count = io->BlockCount - io->BlocksXferred;
-  USHORT sg_indx;
-  USHORT sg_cnt;
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-  int rc;
-
-  /* prepare write command while keeping an eye on S/G count limitations */
-  do {
-    sg_indx = ata_get_sg_indx(io);
-    sg_cnt = io->cSGList - sg_indx;
-
-    if (sector >= (1UL << 28) || count > 256) {
-      /* need LBA48 for this command */
-      if (!ai->ports[p].devs[d].lba48) {
-        iorb_seterr(iorb, IOERR_RBA_LIMIT);
-        return(-1);
-      }
-      rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE_EXT,
-                   AP_SECTOR_48, (u32) sector, (u16) 0,
-                   AP_COUNT,     (u16) count,
-                   AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
-                   AP_DEVICE,    0x4000,
-                   AP_WRITE,
-                   AP_END);
-    } else {
-      rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE,
-                   AP_SECTOR_28, (u32) sector,
-                   AP_COUNT,     (u16) count & 0xffU,
-                   AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
-                   AP_DEVICE,    0x4000,
-                   AP_WRITE,
-                   AP_END);
-    }
-
-    if (rc > 0) {
-      /* couldn't map all S/G elements */
-      ata_max_sg_cnt(io, sg_indx, (USHORT) rc, &sg_cnt, &count);
-    }
-  } while (rc > 0 && sg_cnt > 0);
-
-  if (rc == 0) {
-    add_workspace(iorb)->blocks = count;
-    add_workspace(iorb)->ppfunc = ata_write_pp;
-
-  } else if (rc > 0) {
-    iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD);
-
-  } else {
-    iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE);
-  }
-
-  return(rc);
-}
-
-/******************************************************************************
- * Post processing function for ata_write(); this function updates the
- * BlocksXferred counter in the IORB and, if not all blocks have been
- * transferred, requeues the IORB to process the remaining sectors.
- */
-void ata_write_pp(IORBH _far *iorb)
-{
-  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
-
-  io->BlocksXferred += add_workspace(iorb)->blocks;
-  ddprintf("ata_write_pp(): blocks transferred = %d\n", (int) io->BlocksXferred);
-
-  if (io->BlocksXferred >= io->BlockCount) {
-    /* we're done; tell IRQ handler the IORB is complete */
-    add_workspace(iorb)->complete = 1;
-  } else {
-    /* requeue this IORB for next iteration */
-    iorb_requeue(iorb);
-  }
-}
-
-/******************************************************************************
- * Execute ATA command.
- */
-int ata_execute_ata(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
-/******************************************************************************
- * Request sense information for a failed command. Since there is no "request
- * sense" command for ATA devices, we need to read the current error code from
- * the AHCI task file register and fabricate the sense information.
- *
- * NOTES:
- *
- *   - This function must be called right after an ATA command has failed and
- *     before any other commands are queued on the corresponding port. This
- *     function is typically called in the port restart context hook which is
- *     triggered by an AHCI error interrupt.
- *
- *   - The ATA error bits are a complete mess. We'll try and catch the most
- *     interesting error codes (such as medium errors) and report everything
- *     else with a generic error code.
- */
-int ata_req_sense(IORBH _far *iorb, int slot)
-{
-  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
-  u8 _far *port_mmio = port_base(ai, iorb_unit_port(iorb));
-  u32 tf_data = readl(port_mmio + PORT_TFDATA);
-  u8 err = (u8) (tf_data >> 8);
-  u8 sts = (u8) (tf_data);
-
-  if (sts & ATA_ERR) {
-    if (sts & ATA_DF) {
-      /* there is a device-specific error condition */
-      if (err & ATA_ICRC) {
-        iorb_seterr(iorb, IOERR_ADAPTER_DEVICEBUSCHECK);
-      } else if (err & ATA_UNC) {
-        iorb_seterr(iorb, IOERR_MEDIA);
-      } else if (err & ATA_IDNF) {
-        iorb_seterr(iorb, IOERR_RBA_ADDRESSING_ERROR);
-      } else {
-        iorb_seterr(iorb, IOERR_DEVICE_NONSPECIFIC);
-      }
-    
-    } else {
-      iorb_seterr(iorb, IOERR_DEVICE_NONSPECIFIC);
-    }
-  }
-
-  /* TBD: fill in SCSI sense buffer in IORB */
-
-  /* Return an error to indicate there's no HW command to be submitted and
-   * that the IORB can be completed "as is" (the upstream code expects the
-   * IORB error code, if any, to be set when this happens and this is exactly
-   * what this function is all about).
-   */
-  return(-1);
-}
-
-/******************************************************************************
- * Extract vendor and device name from an ATA INDENTIFY buffer. Since strings
- * in the indentify buffer are byte-swapped, we need to swap them back.
- */
-char *ata_dev_name(u16 *id_buf)
-{
-  static char dev_name[ATA_ID_PROD_LEN + 1];
-  char *t = dev_name;
-  char *s = (char *) (id_buf + ATA_ID_PROD);
-  int i;
-
-  dev_name[sizeof(dev_name)-1] = '\0';
-
-  for (i = 0; i < ATA_ID_PROD_LEN / 2; i++) {
-    *(t++) = s[1];
-    *(t++) = s[0];
-    s += 2;
-  }
-
-  return(dev_name);
-}
-
Index: trunk/src/ata.h
===================================================================
--- trunk/src/ata.h	(revision 12)
+++ 	(revision )
@@ -1,461 +1,0 @@
-/******************************************************************************
- * ata.h - ATA structures and macros for os2ahci driver
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* -------------------------- macros and constants ------------------------- */
-
-/******************************************************************************
- * ATA IDENTIFY offsets and constants. The ATA IDENTIFY response is a bit of a
- * mess and after struggling to put this into some structure, I discovered the
- * Linux folks simply defined offsets and constants to those fields which are
- * actually needed. Thus, I copied the relevant bits from ata.h, replacing
- * the enum with precompiler definitions because our enums are signed 16 bits.
- *
- * Please note that strings such as the product name are stored using byte-
- * swapped 16-bit words.
- */
-#define ATA_ID_WORDS            256
-#define ATA_ID_CONFIG           0
-#define ATA_ID_CYLS             1
-#define ATA_ID_HEADS            3
-#define ATA_ID_SECTORS          6
-#define ATA_ID_SERNO            10
-#define ATA_ID_BUF_SIZE         21
-#define ATA_ID_FW_REV           23
-#define ATA_ID_PROD             27
-#define ATA_ID_MAX_MULTSECT     47
-#define ATA_ID_DWORD_IO         48
-#define ATA_ID_CAPABILITY       49
-#define ATA_ID_OLD_PIO_MODES    51
-#define ATA_ID_OLD_DMA_MODES    52
-#define ATA_ID_FIELD_VALID      53
-#define ATA_ID_CUR_CYLS         54
-#define ATA_ID_CUR_HEADS        55
-#define ATA_ID_CUR_SECTORS      56
-#define ATA_ID_MULTSECT         59
-#define ATA_ID_LBA_CAPACITY     60
-#define ATA_ID_SWDMA_MODES      62
-#define ATA_ID_MWDMA_MODES      63
-#define ATA_ID_PIO_MODES        64
-#define ATA_ID_EIDE_DMA_MIN     65
-#define ATA_ID_EIDE_DMA_TIME    66
-#define ATA_ID_EIDE_PIO         67
-#define ATA_ID_EIDE_PIO_IORDY   68
-#define ATA_ID_ADDITIONAL_SUPP  69
-#define ATA_ID_QUEUE_DEPTH      75
-#define ATA_ID_MAJOR_VER        80
-#define ATA_ID_COMMAND_SET_1    82
-#define ATA_ID_COMMAND_SET_2    83
-#define ATA_ID_CFSSE            84
-#define ATA_ID_CFSSE            84
-#define ATA_ID_CFS_ENABLE_1     85
-#define ATA_ID_CFS_ENABLE_2     86
-#define ATA_ID_CSF_DEFAULT      87
-#define ATA_ID_UDMA_MODES       88
-#define ATA_ID_HW_CONFIG        93
-#define ATA_ID_SPG              98
-#define ATA_ID_LBA_CAPACITY_2   100
-#define ATA_ID_SECTOR_SIZE      106
-#define ATA_ID_LAST_LUN         126
-#define ATA_ID_DLF              128
-#define ATA_ID_CSFO             129
-#define ATA_ID_CFA_POWER        160
-#define ATA_ID_CFA_KEY_MGMT     162
-#define ATA_ID_CFA_MODES        163
-#define ATA_ID_DATA_SET_MGMT    169
-#define ATA_ID_ROT_SPEED        217
-#define ATA_ID_PIO4             (1U << 1)
-
-#define ATA_ID_SERNO_LEN        20
-#define ATA_ID_FW_REV_LEN       8
-#define ATA_ID_PROD_LEN         40
-
-#define ATA_PCI_CTL_OFS         2
-
-#define ATA_PIO0                (1U << 0)
-#define ATA_PIO1                ATA_PIO0 | (1U << 1)
-#define ATA_PIO2                ATA_PIO1 | (1U << 2)
-#define ATA_PIO3                ATA_PIO2 | (1U << 3)
-#define ATA_PIO4                ATA_PIO3 | (1U << 4)
-#define ATA_PIO5                ATA_PIO4 | (1U << 5)
-#define ATA_PIO6                ATA_PIO5 | (1U << 6)
-
-#define ATA_PIO4_ONLY           (1U << 4)
-
-#define ATA_SWDMA0              (1U << 0)
-#define ATA_SWDMA1              ATA_SWDMA0 | (1U << 1)
-#define ATA_SWDMA2              ATA_SWDMA1 | (1U << 2)
-
-#define ATA_SWDMA2_ONLY         (1U << 2)
-
-#define ATA_MWDMA0              (1U << 0)
-#define ATA_MWDMA1              ATA_MWDMA0 | (1U << 1)
-#define ATA_MWDMA2              ATA_MWDMA1 | (1U << 2)
-#define ATA_MWDMA3              ATA_MWDMA2 | (1U << 3)
-#define ATA_MWDMA4              ATA_MWDMA3 | (1U << 4)
-
-#define ATA_MWDMA12_ONLY        (1U << 1) | (1U << 2)
-#define ATA_MWDMA2_ONLY         (1U << 2)
-
-#define ATA_UDMA0               (1U << 0)
-#define ATA_UDMA1               ATA_UDMA0 | (1U << 1)
-#define ATA_UDMA2               ATA_UDMA1 | (1U << 2)
-#define ATA_UDMA3               ATA_UDMA2 | (1U << 3)
-#define ATA_UDMA4               ATA_UDMA3 | (1U << 4)
-#define ATA_UDMA5               ATA_UDMA4 | (1U << 5)
-#define ATA_UDMA6               ATA_UDMA5 | (1U << 6)
-
-/******************************************************************************
- * Miscellanous constants copied from the Linux LIBATA code. Again, the enums
- * have been converted to preprocessor definitions to avoid problems with
- * signed 16-bit integers.
- *
- * Please note that Linux supports all kinds of IDE/EIDE/ATA/SATA/... devices
- * in LIBATA while we only need the ATA-8 bits, and those only as far as they
- * are relevant to AHCI.
- */
-
-/* bits in ATA command block registers */
-#define ATA_HOB                 (1U << 7)     /* LBA48 selector */
-#define ATA_NIEN                (1U << 1)     /* disable-irq flag */
-#define ATA_LBA                 (1U << 6)     /* LBA28 selector */
-#define ATA_DEV1                (1U << 4)     /* Select Device 1 (slave) */
-#define ATA_DEVICE_OBS          (1U << 7) | (1U << 5) /* obs bits in dev reg */
-#define ATA_DEVCTL_OBS          (1U << 3)     /* obsolete bit in devctl reg */
-#define ATA_BUSY                (1U << 7)     /* BSY status bit */
-#define ATA_DRDY                (1U << 6)     /* device ready */
-#define ATA_DF                  (1U << 5)     /* device fault */
-#define ATA_DSC                 (1U << 4)     /* drive seek complete */
-#define ATA_DRQ                 (1U << 3)     /* data request i/o */
-#define ATA_CORR                (1U << 2)     /* corrected data error */
-#define ATA_IDX                 (1U << 1)     /* index */
-#define ATA_ERR                 (1U << 0)     /* have an error */
-#define ATA_SRST                (1U << 2)     /* software reset */
-#define ATA_ICRC                (1U << 7)     /* interface CRC error */
-#define ATA_BBK                 ATA_ICRC      /* pre-EIDE: block marked bad */
-#define ATA_UNC                 (1U << 6)     /* uncorrectable media error */
-#define ATA_MC                  (1U << 5)     /* media changed */
-#define ATA_IDNF                (1U << 4)     /* ID not found */
-#define ATA_MCR                 (1U << 3)     /* media change requested */
-#define ATA_ABORTED             (1U << 2)     /* command aborted */
-#define ATA_TRK0NF              (1U << 1)     /* track 0 not found */
-#define ATA_AMNF                (1U << 0)     /* address mark not found */
-#define ATAPI_LFS               0xF0U         /* last failed sense */
-#define ATAPI_EOM               ATA_TRK0NF    /* end of media */
-#define ATAPI_ILI               ATA_AMNF      /* illegal length indication */
-#define ATAPI_IO                (1U << 1)
-#define ATAPI_COD               (1U << 0)
-
-/* ATA device commands */
-#define ATA_CMD_DEV_RESET               0x08  /* ATAPI device reset */
-#define ATA_CMD_CHK_POWER               0xE5  /* check power mode */
-#define ATA_CMD_STANDBY                 0xE2  /* place in standby power mode */
-#define ATA_CMD_IDLE                    0xE3  /* place in idle power mode */
-#define ATA_CMD_EDD                     0x90  /* execute device diagnostic */
-#define ATA_CMD_DOWNLOAD_MICRO          0x92 
-#define ATA_CMD_NOP                     0x00 
-#define ATA_CMD_FLUSH                   0xE7 
-#define ATA_CMD_FLUSH_EXT               0xEA 
-#define ATA_CMD_ID_ATA                  0xEC 
-#define ATA_CMD_ID_ATAPI                0xA1 
-#define ATA_CMD_SERVICE                 0xA2 
-#define ATA_CMD_READ                    0xC8 
-#define ATA_CMD_READ_EXT                0x25 
-#define ATA_CMD_READ_QUEUED             0x26 
-#define ATA_CMD_READ_STREAM_EXT         0x2B 
-#define ATA_CMD_READ_STREAM_DMA_EXT     0x2A 
-#define ATA_CMD_WRITE                   0xCA 
-#define ATA_CMD_WRITE_EXT               0x35 
-#define ATA_CMD_WRITE_QUEUED            0x36 
-#define ATA_CMD_WRITE_STREAM_EXT        0x3B 
-#define ATA_CMD_WRITE_STREAM_DMA_EXT    0x3A 
-#define ATA_CMD_WRITE_FUA_EXT           0x3D 
-#define ATA_CMD_WRITE_QUEUED_FUA_EXT    0x3E 
-#define ATA_CMD_FPDMA_READ              0x60 
-#define ATA_CMD_FPDMA_WRITE             0x61 
-#define ATA_CMD_PIO_READ                0x20 
-#define ATA_CMD_PIO_READ_EXT            0x24 
-#define ATA_CMD_PIO_WRITE               0x30 
-#define ATA_CMD_PIO_WRITE_EXT           0x34 
-#define ATA_CMD_READ_MULTI              0xC4 
-#define ATA_CMD_READ_MULTI_EXT          0x29 
-#define ATA_CMD_WRITE_MULTI             0xC5 
-#define ATA_CMD_WRITE_MULTI_EXT         0x39 
-#define ATA_CMD_WRITE_MULTI_FUA_EXT     0xCE 
-#define ATA_CMD_SET_FEATURES            0xEF 
-#define ATA_CMD_SET_MULTI               0xC6 
-#define ATA_CMD_PACKET                  0xA0 
-#define ATA_CMD_VERIFY                  0x40 
-#define ATA_CMD_VERIFY_EXT              0x42 
-#define ATA_CMD_WRITE_UNCORR_EXT        0x45 
-#define ATA_CMD_STANDBYNOW1             0xE0 
-#define ATA_CMD_IDLEIMMEDIATE           0xE1 
-#define ATA_CMD_SLEEP                   0xE6 
-#define ATA_CMD_INIT_DEV_PARAMS         0x91 
-#define ATA_CMD_READ_NATIVE_MAX         0xF8 
-#define ATA_CMD_READ_NATIVE_MAX_EXT     0x27 
-#define ATA_CMD_SET_MAX                 0xF9 
-#define ATA_CMD_SET_MAX_EXT             0x37 
-#define ATA_CMD_READ_LOG_EXT            0x2F 
-#define ATA_CMD_WRITE_LOG_EXT           0x3F 
-#define ATA_CMD_READ_LOG_DMA_EXT        0x47 
-#define ATA_CMD_WRITE_LOG_DMA_EXT       0x57 
-#define ATA_CMD_TRUSTED_RCV             0x5C 
-#define ATA_CMD_TRUSTED_RCV_DMA         0x5D 
-#define ATA_CMD_TRUSTED_SND             0x5E 
-#define ATA_CMD_TRUSTED_SND_DMA         0x5F 
-#define ATA_CMD_PMP_READ                0xE4 
-#define ATA_CMD_PMP_WRITE               0xE8 
-#define ATA_CMD_CONF_OVERLAY            0xB1 
-#define ATA_CMD_SEC_SET_PASS            0xF1 
-#define ATA_CMD_SEC_UNLOCK              0xF2 
-#define ATA_CMD_SEC_ERASE_PREP          0xF3 
-#define ATA_CMD_SEC_ERASE_UNIT          0xF4 
-#define ATA_CMD_SEC_FREEZE_LOCK         0xF5 
-#define ATA_CMD_SEC_DISABLE_PASS        0xF6 
-#define ATA_CMD_CONFIG_STREAM           0x51 
-#define ATA_CMD_SMART                   0xB0 
-#define ATA_CMD_MEDIA_LOCK              0xDE 
-#define ATA_CMD_MEDIA_UNLOCK            0xDF 
-#define ATA_CMD_DSM                     0x06 
-#define ATA_CMD_CHK_MED_CRD_TYP         0xD1 
-#define ATA_CMD_CFA_REQ_EXT_ERR         0x03 
-#define ATA_CMD_CFA_WRITE_NE            0x38 
-#define ATA_CMD_CFA_TRANS_SECT          0x87 
-#define ATA_CMD_CFA_ERASE               0xC0 
-#define ATA_CMD_CFA_WRITE_MULT_NE       0xCD 
-/* marked obsolete in the ATA/ATAPI-7 spec */
-#define ATA_CMD_RESTORE                 0x10 
-
-/* READ_LOG_EXT pages */
-#define ATA_LOG_SATA_NCQ                0x10 
-
-/* READ/WRITE LONG (obsolete) */
-#define ATA_CMD_READ_LONG               0x22 
-#define ATA_CMD_READ_LONG_ONCE          0x23 
-#define ATA_CMD_WRITE_LONG              0x32 
-#define ATA_CMD_WRITE_LONG_ONCE         0x33 
-
-/* SETFEATURES stuff */
-#define SETFEATURES_XFER        0x03 
-#define XFER_UDMA_7             0x47 
-#define XFER_UDMA_6             0x46 
-#define XFER_UDMA_5             0x45 
-#define XFER_UDMA_4             0x44 
-#define XFER_UDMA_3             0x43 
-#define XFER_UDMA_2             0x42 
-#define XFER_UDMA_1             0x41 
-#define XFER_UDMA_0             0x40 
-#define XFER_MW_DMA_4           0x24  /* CFA only */
-#define XFER_MW_DMA_3           0x23  /* CFA only */
-#define XFER_MW_DMA_2           0x22 
-#define XFER_MW_DMA_1           0x21 
-#define XFER_MW_DMA_0           0x20 
-#define XFER_SW_DMA_2           0x12 
-#define XFER_SW_DMA_1           0x11 
-#define XFER_SW_DMA_0           0x10 
-#define XFER_PIO_6              0x0E  /* CFA only */
-#define XFER_PIO_5              0x0D  /* CFA only */
-#define XFER_PIO_4              0x0C 
-#define XFER_PIO_3              0x0B 
-#define XFER_PIO_2              0x0A 
-#define XFER_PIO_1              0x09 
-#define XFER_PIO_0              0x08 
-#define XFER_PIO_SLOW           0x00 
-
-#define SETFEATURES_WC_ON       0x02  /* Enable write cache */
-#define SETFEATURES_WC_OFF      0x82  /* Disable write cache */
-
-/* Enable/Disable Automatic Acoustic Management */
-#define SETFEATURES_AAM_ON      0x42 
-#define SETFEATURES_AAM_OFF     0xC2 
-
-#define SETFEATURES_SPINUP      0x07  /* Spin-up drive */
-
-#define SETFEATURES_SATA_ENABLE  0x10  /* Enable use of SATA feature */
-#define SETFEATURES_SATA_DISABLE 0x90  /* Disable use of SATA feature */
-
-/* SETFEATURE Sector counts for SATA features */
-#define SATA_FPDMA_OFFSET       0x01  /* FPDMA non-zero buffer offsets */
-#define SATA_FPDMA_AA           0x02  /* FPDMA Setup FIS Auto-Activate */
-#define SATA_DIPM               0x03  /* Device Initiated Power Management */
-#define SATA_FPDMA_IN_ORDER     0x04  /* FPDMA in-order data delivery */
-#define SATA_AN                 0x05  /* Asynchronous Notification */
-#define SATA_SSP                0x06  /* Software Settings Preservation */
-
-/* feature values for SET_MAX */
-#define ATA_SET_MAX_ADDR        0x00 
-#define ATA_SET_MAX_PASSWD      0x01 
-#define ATA_SET_MAX_LOCK        0x02 
-#define ATA_SET_MAX_UNLOCK      0x03 
-#define ATA_SET_MAX_FREEZE_LOCK 0x04 
-
-/* feature values for DEVICE CONFIGURATION OVERLAY */
-#define ATA_DCO_RESTORE         0xC0 
-#define ATA_DCO_FREEZE_LOCK     0xC1 
-#define ATA_DCO_IDENTIFY        0xC2 
-#define ATA_DCO_SET             0xC3 
-
-/* feature values for SMART */
-#define ATA_SMART_ENABLE        0xD8 
-#define ATA_SMART_READ_VALUES   0xD0 
-#define ATA_SMART_READ_THRESHOLDS 0xD1 
-
-/* feature values for Data Set Management */
-#define ATA_DSM_TRIM            0x01 
-
-/* password used in LBA Mid / LBA High for executing SMART commands */
-#define ATA_SMART_LBAM_PASS     0x4F 
-#define ATA_SMART_LBAH_PASS     0xC2 
-
-/* ATAPI stuff */
-#define ATAPI_PKT_DMA           (1U << 0) 
-#define ATAPI_DMADIR            (1U << 2)      /* ATAPI data dir:
-                                                  0=to device, 1=to host */
-#define ATAPI_CDB_LEN           16
-
-/* PMP stuff */
-#define SATA_PMP_MAX_PORTS      15
-#define SATA_PMP_CTRL_PORT      15
-
-#define SATA_PMP_GSCR_DWORDS    128
-#define SATA_PMP_GSCR_PROD_ID   0
-#define SATA_PMP_GSCR_REV       1
-#define SATA_PMP_GSCR_PORT_INFO 2
-#define SATA_PMP_GSCR_ERROR     32
-#define SATA_PMP_GSCR_ERROR_EN  33
-#define SATA_PMP_GSCR_FEAT      64
-#define SATA_PMP_GSCR_FEAT_EN   96
-
-#define SATA_PMP_PSCR_STATUS    0
-#define SATA_PMP_PSCR_ERROR     1
-#define SATA_PMP_PSCR_CONTROL   2
-
-#define SATA_PMP_FEAT_BIST      (1U << 0)
-#define SATA_PMP_FEAT_PMREQ     (1U << 1)
-#define SATA_PMP_FEAT_DYNSSC    (1U << 2)
-#define SATA_PMP_FEAT_NOTIFY    (1U << 3)
-
-/* SATA Status and Control Registers */
-#define SCR_STATUS              0
-#define SCR_ERROR               1
-#define SCR_CONTROL             2
-#define SCR_ACTIVE              3
-#define SCR_NOTIFICATION        4
-
-/* SError bits */
-#define SERR_DATA_RECOVERED     (1UL << 0)  /* recovered data error */
-#define SERR_COMM_RECOVERED     (1UL << 1)  /* recovered comm failure */
-#define SERR_DATA               (1UL << 8)  /* unrecovered data error */
-#define SERR_PERSISTENT         (1UL << 9)  /* persistent data/comm error */
-#define SERR_PROTOCOL           (1UL << 10)  /* protocol violation */
-#define SERR_INTERNAL           (1UL << 11)  /* host internal error */
-#define SERR_PHYRDY_CHG         (1UL << 16)  /* PHY RDY changed */
-#define SERR_PHY_INT_ERR        (1UL << 17)  /* PHY internal error */
-#define SERR_COMM_WAKE          (1UL << 18)  /* Comm wake */
-#define SERR_10B_8B_ERR         (1UL << 19)  /* 10b to 8b decode error */
-#define SERR_DISPARITY          (1UL << 20)  /* Disparity */
-#define SERR_CRC                (1UL << 21)  /* CRC error */
-#define SERR_HANDSHAKE          (1UL << 22)  /* Handshake error */
-#define SERR_LINK_SEQ_ERR       (1UL << 23)  /* Link sequence error */
-#define SERR_TRANS_ST_ERROR     (1UL << 24)  /* Transport state trans. error */
-#define SERR_UNRECOG_FIS        (1UL << 25)  /* Unrecognized FIS */
-#define SERR_DEV_XCHG           (1UL << 26)  /* device exchanged */
-
-/******************************************************************************
- * Parameters for ATA commands. Those parameters are passed in a variable
- * argument list in the format:
- *
- *   AP_xxx, [val, ...], AP_xxx, [val, ...]
- *
- * The values expected by each parameter are indicated within square brackets.
- */
-typedef enum {
-  AP_FEATURES,     /* [u16]              ATA command features (read: flags) */
-  AP_COUNT,        /* [u16]              number of sectors (0 = 65536)      */
-  AP_SECTOR_28,    /* [u32]              28-bit sector address              */
-  AP_SECTOR_48,    /* [u32, u16]         48-bit sector address              */
-  AP_DEVICE,       /* [u16]              ATA command "device" field (LSB ignored) */
-  AP_SGLIST,       /* [void _far *, u16] buffer S/G (SCATGATENTRY/count)    */
-  AP_VADDR,        /* [void _far *, u16] buffer virtual address (buf/len)   */
-  AP_WRITE,        /* []                 indicates a DMA write operation    */
-  AP_AHCI_FLAGS,   /* [u16]              AHCI command header flags          */
-  AP_ATAPI_CMD,    /* [void _far *, u16] ATAPI command (CDB) and length     */
-  AP_END           /* []                 end of variable argument list      */
-} ATA_PARM;
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/******************************************************************************
- * Generic ATA-8 command structure. This is based on ATA-8, i.e. we won't have
- * to deal with different command structures depending on whether a device is
- * 48-bit capable, etc. because this is handled by the transport (AHCI in our
- * case). Unsupported bits have to be cleared to zero, of course (e.g. the
- * high 20 bits of the LBA when talking to a device that supports only 28
- * bits).
- */
-typedef struct {
-  u16  features;          /* feature bits */
-  u16  count;             /* count register (e.g. number of sectors) */
-  u32  lba_l;             /* low 28 bits of LBA (32 bits for 48-bit devices) */
-  u16  lba_h;             /* high 16 bits of LBA for 48-bit devices */
-  u8   cmd;               /* ATA command field */
-  u8   device;            /* ATA device field */
-} ATA_CMD;
-
-/* generic ATA-8 response structure */
-typedef struct {
-  u16  error;             /* error code, if any */
-  u16  count;             /* count register (e.g. number of sectors) */
-  u32  lba_l;             /* low 28 bits of LBA (32 bits for 48-bit devices) */
-  u16  lba_h;             /* high 16 bits of LBA for 48-bit devices */
-  u16  status;            /* response status bits */
-} ATA_RSP;
-
-/******************************************************************************
- * Command-specific ATA-8 structures.
- */
-
-/* -------------------------- function prototypes -------------------------- */
-
-extern int       ata_cmd             (AD_INFO *ai, int port, int device,
-                                      int slot, int cmd, ...);
-extern int       v_ata_cmd           (AD_INFO *ai, int port, int device,
-                                      int slot, int cmd, va_list va);
-extern void      ata_cmd_to_fis      (u8 _far *fis, ATA_CMD _far *cmd,
-                                      int device);
-extern USHORT    ata_get_sg_indx     (IORB_EXECUTEIO _far *io);
-extern void      ata_max_sg_cnt      (IORB_EXECUTEIO _far *io, USHORT sg_indx,
-                                      USHORT sg_max, USHORT _far *sg_cnt,
-                                      USHORT _far *sector_cnt);
-
-extern int       ata_get_geometry    (IORBH _far *iorb, int slot);
-extern void      ata_get_geometry_pp (IORBH _far *iorb);
-extern int       ata_unit_ready      (IORBH _far *iorb, int slot);
-extern int       ata_read            (IORBH _far *iorb, int slot);
-extern void      ata_read_pp         (IORBH _far *iorb);
-extern int       ata_verify          (IORBH _far *iorb, int slot);
-extern int       ata_write           (IORBH _far *iorb, int slot);
-extern void      ata_write_pp        (IORBH _far *iorb);
-extern int       ata_execute_ata     (IORBH _far *iorb, int slot);
-extern int       ata_req_sense       (IORBH _far *iorb, int slot);
-
-extern char     *ata_dev_name        (u16 *id_buf);
-
Index: trunk/src/atapi.c
===================================================================
--- trunk/src/atapi.c	(revision 12)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/******************************************************************************
- * atapi.c - ATAPI command processing
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-#include "ata.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* -------------------------- function prototypes -------------------------- */
-
-/* ------------------------ global/static variables ------------------------ */
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * Get device or media geometry. This function is expected to check the IORB
- * command codes and modifiers to see which one is requested.
- */
-int atapi_get_geometry(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
-/******************************************************************************
- * Test whether unit is ready.
- */
-int atapi_unit_ready(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
-/******************************************************************************
- * Read sectors from AHCI device.
- */
-int atapi_read(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}  
-
-/******************************************************************************
- * Verify readability of sectors on AHCI device.
- */
-int atapi_verify(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
-/******************************************************************************
- * Write sectors to AHCI device.
- */
-int atapi_write(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
-/******************************************************************************
- * Execute ATAPI command.
- */
-int atapi_execute_cdb(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
-/******************************************************************************
- * Request sense information.
- */
-int atapi_req_sense(IORBH _far *iorb, int slot)
-{
-  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-  return(-1);
-}
-
Index: trunk/src/atapi.h
===================================================================
--- trunk/src/atapi.h	(revision 12)
+++ 	(revision )
@@ -1,37 +1,0 @@
-/******************************************************************************
- * atapi.h - ATAPI structures and macros for os2ahci driver
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* ---------------------------- global variables --------------------------- */
-
-/* -------------------------- function prototypes -------------------------- */
-
-extern int     atapi_get_geometry     (IORBH _far *iorb, int slot);
-extern int     atapi_unit_ready       (IORBH _far *iorb, int slot);
-extern int     atapi_read             (IORBH _far *iorb, int slot);
-extern int     atapi_verify           (IORBH _far *iorb, int slot);
-extern int     atapi_write            (IORBH _far *iorb, int slot);
-extern int     atapi_execute_cdb      (IORBH _far *iorb, int slot);
-extern int     atapi_req_sense        (IORBH _far *iorb, int slot);
-
Index: trunk/src/ctxhook.c
===================================================================
--- trunk/src/ctxhook.c	(revision 12)
+++ 	(revision )
@@ -1,409 +1,0 @@
-/******************************************************************************
- * ctxhook.c - context hooks (kernel thread functions) for os2ahci
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-#include "ata.h"
-#include "atapi.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* -------------------------- function prototypes -------------------------- */
-
-/* ------------------------ global/static variables ------------------------ */
-
-/* port restart context hook and input data */
-ULONG           restart_ctxhook_h;
-volatile u32    ports_to_restart[MAX_AD];
-
-/* port reset context hook and input data */
-ULONG           reset_ctxhook_h;
-volatile u32    ports_to_reset[MAX_AD];
-IORB_QUEUE      abort_queue;
-
-/* trigger engine context hook and input data */
-ULONG           engine_ctxhook_h;
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * Port restart context hook. This context hook is executed at task time and
- * will handle ports which are stopped due to a device error condition. 
- *
- * The following conditions may exist:
- *
- *  - Only a single non-NCQ command is executed by the AHCI adapter at any
- *    given time (even if more are outstanding). This is the case for single
- *    devices or port multipliers without FIS-based command switching. Error
- *    recovery is simple because we know which command has failed and can
- *    requeue all commands, replacing the failing command with a "request
- *    sense" command to get error details.
- *
- *  - Multiple non-NCQ commands are executed on different devices behind a
- *    port multiplier which supports FIS-based command switching. This is
- *    more difficult to recover from but currently not an issue because we
- *    don't yet support FIS-based command switching (the FIS receive areas
- *    would become too large for the current data model).
- *
- *  - One or more NCQ commands were active at the time of the error, with or
- *    without FIS-based command switching. We would have to interrogate the
- *    corresponding devices to find out which command has failed but if this
- *    is combined with FIS-based command switching, even the AHCI spec
- *    recommends to reset the port. This leads to a much simpler approach:
- *    requeue all NCQ commands (they are idempotent per definition, otherwise
- *    they couldn't be reordered by the device) with the 'no_ncq' flag set
- *    in the IORB and reset the port. Then those comands will be executed as
- *    regular commands. The error, if it reoccurrs, can be then handled by
- *    one of the above cases.
- *
- * The upstream code will guarantee that we will never have a mix of NCQ and
- * non-NCQ commands active at the same time in order to reduce complexity
- * in the interrupt and error handlers.
- */
-void restart_ctxhook(ULONG parm)
-{
-  IORB_QUEUE done_queue;
-  AD_INFO *ai;
-  IORBH _far *problem_iorb;
-  IORBH _far *iorb;
-  IORBH _far *next = NULL;
-  u8 _far *port_mmio;
-  int rearm_ctx_hook = 0;
-  int need_reset;
-  int ccs;
-  int a;
-  int p;
-
-  dprintf("restart_ctxhook() started\n");
-  memset(&done_queue, 0x00, sizeof(done_queue));
-
-  spin_lock(drv_lock);
-
-  for (a = 0; a < ad_info_cnt; a++) {
-    ai = ad_infos + a;
-
-    if (ai->busy) {
-      /* this adapter is busy; leave it alone for now */
-      rearm_ctx_hook = 1;
-      continue;
-    }
-
-    for (p = 0; p <= ai->port_max; p++) {
-      if (ports_to_restart[a] & (1UL << p)) {
-        ports_to_restart[a] &= ~(1UL << p);
-
-        /* restart this port */
-        port_mmio = port_base(ai, p);
-        problem_iorb = NULL;
-        need_reset = 0;
-
-        /* get "current command slot"; only valid if there are no NCQ cmds */
-        ccs = (int) ((readl(port_mmio + PORT_CMD) >> 8) & 0x0f);
-
-        for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
-          ADD_WORKSPACE _far *aws = add_workspace(iorb);
-          next = iorb->pNxtIORB;
-
-          if (aws->queued_hw) {
-            if (ai->ports[p].ncq_cmds != 0) {
-              /* NCQ commands active; force non-NCQ mode and trigger port reset */
-              aws->no_ncq = 1;
-              need_reset = 1;
-            } else if (aws->cmd_slot == ccs) {
-              /* this is the non-NCQ comand that failed */
-              problem_iorb = iorb;
-            }
-            iorb_requeue(iorb);
-
-            /* remove requeued command from the issued command bitmaps */
-            ai->ports[p].ncq_cmds &= ~(1UL << p);
-            ai->ports[p].reg_cmds &= ~(1UL << p);
-          }
-        }
-
-        /* sanity check: issued command bitmaps should be 0 now */
-        if (ai->ports[p].ncq_cmds != 0 || ai->ports[p].reg_cmds != 0) {
-          dprintf("warning: commands issued not 0 (%08lx/%08lx)\n",
-                  ai->ports[p].ncq_cmds, ai->ports[p].reg_cmds);
-        }
-
-        if (!need_reset) {
-          if ((readl(port_mmio + PORT_TFDATA) & 0x88) != 0) {
-            /* device is not in an idle state */
-            need_reset = 1;
-          }
-        }
-
-        /* restart/reset port */
-        ai->busy = 1;
-        spin_unlock(drv_lock);
-        if (need_reset) {
-          ahci_reset_port(ai, p, 1);
-        } else {
-          ahci_stop_port(ai, p);
-          ahci_start_port(ai, p, 1);
-        }
-        spin_lock(drv_lock);
-        ai->busy = 0;
-
-        /* reset internal port status */
-        ai->ports[p].ncq_cmds = 0;
-        ai->ports[p].reg_cmds = 0;
-        ai->ports[p].cmd_slot = 0;
-
-        if (problem_iorb != NULL) {
-          /* get details about the error that caused this IORB to fail */
-          if (need_reset) {
-            /* no way to retrieve error details after a reset */
-            iorb_seterr(problem_iorb, IOERR_DEVICE_NONSPECIFIC);
-            iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
-            iorb_queue_add(&done_queue, problem_iorb);
-
-          } else {
-            /* get sense information */
-            ADD_WORKSPACE _far *aws = add_workspace(problem_iorb);
-            int d = iorb_unit_device(problem_iorb);
-            int (*req_sense)(IORBH _far *, int) = 
-              (ai->ports[p].devs[d].atapi) ? atapi_req_sense : ata_req_sense;
-
-            aws->processing = 1;
-            aws->queued_hw = 1;
-
-            if (req_sense(problem_iorb, 0) == 0) {
-              /* execute request sense on slot #0 before anything else comes along */
-              ai->ports[p].reg_cmds = 1;
-              writel(port_mmio + PORT_CMD_ISSUE, 1);
-              readl(port_mmio); /* flush */
-
-            } else {
-              /* IORB is expected to contain the error code; just move to done queue */
-              iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
-              iorb_queue_add(&done_queue, problem_iorb);
-            }
-          }
-        }
-      }
-    }
-  }
-
-  spin_unlock(drv_lock);
-
-  /* call notification routine on all IORBs which have completed */
-  for (iorb = done_queue.root; iorb != NULL; iorb = next) {
-    next = iorb->pNxtIORB;
-    if (iorb->RequestControl & IORB_ASYNC_POST) {
-      iorb->NotifyAddress(iorb);
-    }
-  }
-
-  /* restart engine to resume IORB processing */
-  spin_lock(drv_lock);
-  trigger_engine();
-  spin_unlock(drv_lock);
-
-  dprintf("restart_ctxhook() completed\n");
-
-  /* Check whether we have to rearm ourselves because some adapters were busy
-   * when we wanted to restart ports on them.
-   */
-  if (rearm_ctx_hook) {
-    msleep(250);
-    DevHelp_ArmCtxHook(0, restart_ctxhook_h);
-  }
-}
-
-/******************************************************************************
- * Reset and abort context hook. This function runs at task time and takes
- * care of port resets and their side effects. Input to this function are:
- *
- *  ports_to_reset[]   - array of port bitmaps, each bit indicating which port
- *                       should be reset unconditionally. This is primarily
- *                       used by the error interrupt handler.
- *
- *  abort_queue        - queue with IORBs to be arborted (timed-out, ...) If
- *                       any of these commands have reached the hardware, the
- *                       corresponding port is reset to interrupt command
- *                       execution. This is primarily used for timeout
- *                       handling and when IORBs are requested to be aborted.
- * 
- * After resetting the requested ports, all remaining active IORBs on those
- * ports have to be retried or aborted. Whether a retry is attempted depends
- * on the kind of IORB -- those which are idempotent are retried, all others
- * are aborted. This is different from the port restart hook because the
- * restart hook can assume it is called with the port in error state, thus
- * the controller will have stopped executing commands. The reset handler can
- * be called at any time and we can't tell what's going on in the controller.
- *
- * The IORBs in the global abort_queue are expected to have their error code
- * set (aborted, timeout, ...) but must not be marked as 'done'; otherwise,
- * the upstream code might reuse the IORBs before we're done with them.
- */
-void reset_ctxhook(ULONG parm)
-{
-  IORB_QUEUE done_queue;
-  AD_INFO *ai;
-  IORBH _far *iorb;
-  IORBH _far *next = NULL;
-  int rearm_ctx_hook = 0;
-  int a;
-  int p;
-
-  dprintf("reset_ctxhook() started\n");
-  memset(&done_queue, 0x00, sizeof(done_queue));
-
-  spin_lock(drv_lock);
-
-  /* add ports of active IORBs from the abort queue to ports_to_reset[] */
-  for (iorb = abort_queue.root; iorb != NULL; iorb = next) {
-    next = iorb->pNxtIORB;
-    a = iorb_unit_adapter(iorb);
-    p = iorb_unit_port(iorb);
-    ai = ad_infos + a;
-
-    if (ai->busy) {
-      /* this adapter is busy; leave it alone for now */
-      rearm_ctx_hook = 1;
-      continue;
-    }
-
-    /* move IORB to the local 'done' queue */
-    aws_free(add_workspace(iorb));
-    iorb_queue_del(&abort_queue, iorb);
-    iorb_queue_add(&done_queue, iorb);
-
-    /* reset port if the IORB has already been queued to hardware */
-    if (add_workspace(iorb)->queued_hw) {
-      /* prepare port reset */
-      ports_to_reset[a] |= (1UL << p);
-    }
-  }
-
-  /* reset all ports in 'ports_to_reset[]' */
-  for (a = 0; a < ad_info_cnt; a++) {
-    ai = ad_infos + a;
-
-    if (ai->busy) {
-      /* this adapter is busy; leave it alone for now */
-      rearm_ctx_hook = 1;
-      continue;
-    }
-
-    for (p = 0; p <= ai->port_max; p++) {
-      if (ports_to_reset[a] & (1UL << p)) {
-        ports_to_reset[a] &= ~(1UL << p);
-
-        /* Reset this port. Since this is a rather slow operation, we'll
-         * release the spinlock while doing so. The adapter is marked as
-         * 'busy' to prevent similar routines (e.g. an ahci port scan) from
-         * interfering. 
-         */
-        ai->busy = 1;
-        spin_unlock(drv_lock);
-        ahci_reset_port(ai, p, 1);
-        spin_lock(drv_lock);
-        ai->busy = 0;
-
-        /* reset port status */
-        ai->ports[p].ncq_cmds = 0;
-        ai->ports[p].reg_cmds = 0;
-        ai->ports[p].cmd_slot = 0;
-
-        /* retry or abort all remaining active commands on this port */
-        for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
-          next = iorb->pNxtIORB;
-          if (add_workspace(iorb)->queued_hw) {
-            /* this IORB had already been queued to HW when we reset the port */
-            aws_free(add_workspace(iorb));
-            if (add_workspace(iorb)->idempotent) {
-              /* We can retry this IORB; just reset its status and it will be
-               * picked up by subsequent trigger_engine() calls.
-               */
-              memset(&iorb->ADDWorkSpace, sizeof(iorb->ADDWorkSpace), 0x00);
-
-            } else {
-              /* we cannot retry this IORB; consider it aborted */
-              iorb->ErrorCode = IOERR_CMD_ABORTED;
-              iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
-              iorb_queue_add(&done_queue, iorb);
-            }
-          }
-        }
-      }
-    }
-  }
-
-  spin_unlock(drv_lock);
-
-  /* call notification routine on all aborted IORBs */
-  for (iorb = done_queue.root; iorb != NULL; iorb = next) {
-    next = iorb->pNxtIORB;
-    iorb->Status = IORB_DONE | IORB_ERROR;
-    if (iorb->RequestControl & IORB_ASYNC_POST) {
-      iorb->NotifyAddress(iorb);
-    }
-  }
-
-  /* restart engine to resume IORB processing */
-  spin_lock(drv_lock);
-  trigger_engine();
-  spin_unlock(drv_lock);
-
-  dprintf("reset_ctxhook() completed\n");
-
-  /* Check whether we have to rearm ourselves because some adapters were busy
-   * when we wanted to reset ports on them.
-   */
-  if (rearm_ctx_hook) {
-    msleep(250);
-    DevHelp_ArmCtxHook(0, reset_ctxhook_h);
-  }
-}
-
-/******************************************************************************
- * IORB Engine context hook. This hook is executed if trigger_engine() came
- * to the conclusion that some of the IORBs keep bouncing, most likely due to
- * some condition on the adapter such as being busy. It could also be a very
- * busy system. Either way, this requires some task-time help.
- */
-void engine_ctxhook(ULONG parm)
-{
-  int iorbs_sent;
-  int i;
-
-  dprintf("engine_ctxhook() started\n");
-
-  spin_lock(drv_lock);
-  for (i = 0; i < 10; i++) {
-    if ((iorbs_sent = trigger_engine_1()) == 0) {
-      break;
-    }
-  }
-  spin_unlock(drv_lock);
-
-  dprintf("engine_ctxhook() completed\n");
-
-  if (iorbs_sent != 0) {
-    /* need to rearm ourselves for another run */
-    msleep(250);
-    DevHelp_ArmCtxHook(0, engine_ctxhook_h);
-  }
-}
Index: trunk/src/init.asm
===================================================================
--- trunk/src/init.asm	(revision 12)
+++ 	(revision )
@@ -1,334 +1,0 @@
-; ----------------------------------------------------------------------------
-; Initialization routines and segment mappings for os2ahci driver, plus
-; some low-level functions that were implemented in assembler
-
-include devhdr.inc
-
-; -----------------------------------------------------------------------------
-; Public symbols
-
-                PUBLIC  _asm_strat           ; low-level strategy routine
-                PUBLIC  _readl               ; MMIO read (32 bits)
-                PUBLIC  _writel              ; MMIO write (32 bits)
-                PUBLIC  _memset              ; C memset() implementation
-                PUBLIC  _memcpy              ; C memcpy() implementation
-                PUBLIC  _restart_hook        ; port restart context hook
-                PUBLIC  _reset_hook          ; port reset context hook
-                PUBLIC  _engine_hook         ; engine trigger context hook
-                PUBLIC  _dev_hdr             ; device driver header
-                PUBLIC  _end_of_data         ; end of all data (label)
-                PUBLIC  _end_of_code         ; end of all code (label)
-
-; ----------------------------------------------------------------------------
-; Device Driver Header
-
-DEVHDR          SEGMENT WORD PUBLIC 'DATA'
-_dev_hdr        dd      -1                      ; no headers after this one
-                dw      DEVLEV_3 + DEV_CHAR_DEV ; flags for ADD drivers
-                dw      _asm_strat              ; strategy routine
-                dw      0                       ; no IDC routine
-                db      "OS2AHCI$"              ; name of character device
-                dq      0                       ; 8 reserved bytes
-                dd      DEV_ADAPTER_DD + DEV_INITCOMPLETE + 20h ; ADD flags
-                dw      0
-DEVHDR          ENDS
-
-; ----------------------------------------------------------------------------
-; Segment Definitions. We need to reference all segments here, even if they
-; are not used, to allow proper grouping of all segments into one group for
-; data and one group for code as well as proper ordering of the segments
-; within each group to make sure the end of segment markers are at the end
-; of each group.
-
-; On top of that, we need to make sure that the end of segment marker is in
-; a data segment of class 'BSS' because BSS segments are always put towards
-; the end of the executable when linking with high-level language objects
-; such as C.
-
-_DATA           SEGMENT WORD PUBLIC 'DATA'
-readl_dbg_fmt   db      "readl(%04x:%04x) = 0x%08lx [addr %% 0x80 = 0x%02x]"
-                db      10, 0
-writel_dbg_fmt  db      "writel(%04x:%04x, 0x%08lx) [addr %% 0x80 = 0x%02x]"
-                db      10, 0
-_DATA           ENDS
-
-CONST           SEGMENT WORD PUBLIC 'CONST'
-CONST           ENDS
-
-_BSS            SEGMENT WORD PUBLIC 'BSS'
-_BSS            ENDS
-
-c_common        SEGMENT WORD PUBLIC 'BSS'
-                EXTRN   _debug : WORD        ; global debug flag
-c_common        ENDS
-
-_z_data         SEGMENT WORD PUBLIC 'BSS'
-_end_of_data    db      0
-_z_data         ENDS
-
-_TEXT           SEGMENT WORD PUBLIC 'CODE'
-                EXTRN   _c_strat : NEAR           ; C strategy routine
-                EXTRN   _printf : NEAR            ; C printf routine
-                EXTRN   _restart_ctxhook : NEAR   ; C restart context hook
-                EXTRN   _reset_ctxhook : NEAR     ; C reset context hook
-                EXTRN   _engine_ctxhook : NEAR    ; C engine context hook
-_TEXT           ENDS
-
-CODE            SEGMENT WORD PUBLIC 'CODE'
-CODE            ENDS
-
-RMCode          SEGMENT WORD PUBLIC 'CODE'
-RMCode          ENDS
-
-LIBCODE         SEGMENT WORD PUBLIC 'CODE'
-LIBCODE         ENDS
-
-_z_text         SEGMENT WORD PUBLIC 'CODE'
-_end_of_code    LABEL NEAR
-_z_text         ENDS
-
-DGROUP          GROUP   DEVHDR, _DATA, CONST, _BSS, c_common, _z_data
-TGROUP          GROUP   _TEXT, CODE, _z_text
-
-; ----------------------------------------------------------------------------
-; Start of code
-
-_TEXT           SEGMENT WORD PUBLIC 'CODE'
-                ASSUME  DS:DGROUP
-                ASSUME  ES:NOTHING
-                ASSUME  SS:NOTHING
-
-
-; Device driver main entry point (strategy routine)
-_asm_strat      PROC    FAR
-
-                ; push request packet address
-                PUSH    ES
-                PUSH    BX
-                CLD
-
-                ; call C strategy routine
-                CALL    _c_strat
-
-                POP     BX
-                POP     ES
-                MOV     WORD PTR ES:[BX+3], AX
-                RET
-_asm_strat      ENDP
-
-                .386
-
-
-; Read long value from MMIO address; need to do this here to get real
-; 32-bit operations because at least the AHCI device in VirtualBox doesn't
-; seem to support reading 32-bit MMIO registers in two 16-bit steps.
-;
-; C prototype:  u32 readl(void _far *addr);
-_readl          PROC    NEAR
-                ENTER   0, 0
-
-                ; load 'addr' into ES:EAX
-                LES     AX, [BP+4]
-                AND     EAX, 0000FFFFh
-
-                ; read MMIO register into EDX and return it in DX:AX
-                MOV     EDX, ES:[EAX]
-
-                ; print debug message if debug level is 3+
-                CMP     _debug, 3
-                JB      no_debug
-                PUSH    EDX             ; save value read from MMIO port
-                MOV     BX, AX          ; addr & 0x7f (port reg index)
-                AND     BX, 7FH
-                PUSH    BX
-                PUSH    EDX             ; value read from MMIO address
-                PUSH    AX              ; offset of MMIO address
-                PUSH    ES              ; segment of MMIO address
-                PUSH    OFFSET readl_dbg_fmt
-                CALL    _printf
-                ADD     SP, 12
-                POP     EDX             ; restore value read from MMIO port
-
-no_debug:       MOV     EAX, EDX
-                SHR     EDX, 16
-
-                LEAVE
-                RET
-_readl          ENDP
-
-
-; Write long value to MMIO address;  need to do this here to get real
-; 32-bit operations because at least the AHCI device in VirtualBox doesn't
-; seem to support reading 32-bit MMIO registers in two 16-bit steps and the
-; assumption is that this is the same with writing 32-bit MMIO registers.
-;
-; C prototype:  void writel(void _far *addr, u32 val);
-_writel         PROC    NEAR
-                ENTER   0, 0
-
-                ; load 'addr' into ES:EAX
-                LES     AX, [BP+4]
-                AND     EAX, 0000FFFFh
-
-                ; load 'val' into EDX, preserving the upper 16 bits of EBP
-                MOV     EBX, EBP
-                AND     EBP, 0000FFFFh
-                MOV     EDX, [EBP+8]
-                MOV     EBP, EBX
-
-                ; store 'val' at MMIO address in ES:EAX
-                MOV     DWORD PTR ES:[EAX], EDX
-
-                ; print debug message if debug level is 3+
-                CMP     _debug, 3
-                JB      no_debug2
-                MOV     BX, AX          ; addr & 0x7f (port reg index)
-                AND     BX, 7FH
-                PUSH    BX
-                PUSH    EDX             ; value written to MMIO address
-                PUSH    AX              ; offset of MMIO address
-                PUSH    ES              ; segment of MMIO address
-                PUSH    OFFSET writel_dbg_fmt
-                CALL    _printf
-                ADD     SP, 12
-
-no_debug2:      LEAVE
-                RET
-_writel         ENDP
-
-
-; Halfway-decent 32-bit implementation of memset().
-;
-; C prototype: void *memset(void _far *s, int c, size_t n);
-_memset         PROC    NEAR
-                ENTER   0, 0
-
-                PUSH    DI
-
-                ; load 's' into ES:EDI
-                LES     DI, [BP+4]
-                AND     EDI, 0000FFFFh
-
-                ; load 'c' into EAX, replicating the low 8 bits all over
-                MOV     BL, [BP+8]
-                MOV     BH, BL
-                MOV     AX, BX
-                SHL     EAX, 16
-                MOV     AX, BX
-
-                ; load 'n / 4' into ECX
-                MOV     CX, [BP+10]
-                SHR     CX, 2
-                AND     ECX, 0000FFFFh
-
-                ; fill 's' with 32-bit chunks of EAX
-                REP     STOSD
-
-                ; set remaining bytes (n % 4)'
-                MOV     CX, [BP+10]
-                AND     CX, 0003h
-                REP     STOSB
-
-                ; return address of 's'
-                LES     AX, [BP+4]
-                PUSH    ES
-                POP     DX
-
-                POP     DI
-
-                LEAVE
-                RET
-_memset         ENDP
-
-
-; Halfway-decent 32-bit implementation of memcpy(). 
-;
-; C prototype: void *memcpy(void _far *d, void _far *s, size_t n);
-_memcpy         PROC    NEAR
-                ENTER   0, 0
-
-                INT     3
-                
-                PUSH    SI
-                PUSH    DI
-                PUSH    DS
-
-                ; load 'd' into ES:EDI
-                LES     DI, [BP+4]
-                AND     EDI, 0000FFFFh
-
-                ; load 's' into DS:ESI
-                LDS     SI, [BP+8]
-                AND     ESI, 0000FFFFh
-
-                ; load 'n / 4' into ECX
-                MOV     CX, [BP+12]
-                SHR     CX, 2
-                AND     ECX, 0000FFFFh
-
-                ; copy 's' to 'd' in 32-bit chunks
-                REP     MOVSD
-
-                ; copy remaining bytes (n % 4)'
-                MOV     CX, [BP+12]
-                AND     CX, 0003h
-                REP     MOVSB
-
-                ; return address of 'd'
-                LES     AX, [BP+4]
-                PUSH    ES
-                POP     DX
-
-                POP     DS
-                POP     DI
-                POP     SI
-
-                LEAVE
-                RET
-_memcpy         ENDP
-
-
-; Port restart context hook; context hooks need to save all registers
-; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus
-; we need a stub which calls the real context hook.
-_restart_hook   PROC    FAR
-                PUSHAD
-                PUSH    EAX
-                CALL    _restart_ctxhook
-                POP     EAX
-                POPAD
-                RET
-_restart_hook   ENDP
-
-
-; Port reset context hook; context hooks need to save all registers
-; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus
-; we need a stub which calls the real context hook.
-_reset_hook     PROC    FAR
-                PUSHAD
-                PUSH    EAX
-                CALL    _reset_ctxhook
-                POP     EAX
-                POPAD
-                RET
-_reset_hook     ENDP
-
-
-; Engine trigger context hook; context hooks need to save all registers
-; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus
-; we need a stub which calls the real context hook.
-_engine_hook    PROC    FAR
-                PUSHAD
-                PUSH    EAX
-                CALL    _engine_ctxhook
-                POP     EAX
-                POPAD
-                RET
-_engine_hook    ENDP
-
-                .286
-
-_TEXT           ENDS
-
-                END
-
Index: trunk/src/install.cmd
===================================================================
--- trunk/src/install.cmd	(revision 12)
+++ 	(revision )
@@ -1,2 +1,0 @@
-copy os2ahci.add c:\os2\boot\*
-copy os2ahci.sym c:\os2\boot\*
Index: trunk/src/libc.c
===================================================================
--- trunk/src/libc.c	(revision 12)
+++ 	(revision )
@@ -1,741 +1,0 @@
-/******************************************************************************
- * libc.c - minimal subset of C runtime library for os2ahci
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-#define MSG_REPLACEMENT_STRING 1178   /* empty message with a single %; used
-                                       * for printing custom messages via
-                                       * DevHelp_Save_Message() */
-
-#define COM_BASE              0x03f8  /* base address of COM port (COM1) */
-
-/* heap management constants */
-#define HEAP_SIZE             8192
-#define HEAP_UNIT              128
-#define HEAP_UNIT_CNT         (HEAP_SIZE / HEAP_UNIT)
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* mdelay() calibration status */
-typedef enum {
-  MD_NOT_CALIBRATED,                  /* delay loop not calibrated */
-  MD_CALIBRATION_START,               /* calibration run started */
-  MD_CALIBRATION_END,                 /* calibration run ended */
-  MD_CALIBRATION_DONE                 /* calibration complete */
-} MDCAL;
-
-/* -------------------------- function prototypes -------------------------- */
-
-static void      long_to_asc            (long val, char _far *buf, int base,
-                                         int zero, int flen);
-static void _far mdelay_timer_callback  (ULONG timer_handle, ULONG parm1,
-                                         ULONG parm2);
-static int       mdelay_cal_end         (void);
-
-/* ------------------------ global/static variables ------------------------ */
-
-static char  hex_digits[] = "0123456789abcdef";
-static ULONG com_lock;
-
-/* message table for DosHelp_Save_Message() which prints the first string */
-static MSGTABLE init_msgtbl = {
-  MSG_REPLACEMENT_STRING,
-  1,
-  0
-};
-
-/* COM port initialization sequence */
-static struct {
-  int reg;
-  u8  data;
-} com1_init_sequence[] = {
-  3, 0x80,   /* overlay divisor latch register at 0x3f8 and 0x3f9 */
-  0, 0x00,   /* set low byte of divisor to 1 (115200 baud) */
-  1, 0x01,   /* set high byte of divisor to 0 */
-  3, 0x03,   /* reset divisor latch register overlay and set 8,n,1 */
-  1, 0x00,   /* disable interrupts */
-  4, 0x0f,   /* modem control register */
-  -1, 0x00
-};
-
-/* delay loop calibration data */
-volatile MDCAL mdelay_cal_status = 0;     /* delay loop calibration status */
-volatile u32   mdelay_loops_per_ms = 0;   /* delay loop counter */
-
-/* very small heap for dynamic memory management */
-static u8 heap_buf[HEAP_SIZE];
-static u8 heap_units[HEAP_UNIT_CNT];
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * Initialize COM1 to 57600,n,8,1
- */
-void init_com1(void)
-{
-  int i;
-
-  DevHelp_CreateSpinLock(&com_lock);
-
-  for (i = 0; com1_init_sequence[i].reg != -1; i++) {
-    u16 port = COM_BASE + com1_init_sequence[i].reg;
-    u8  data = com1_init_sequence[i].data;
-    _asm {
-      mov  dx, port;
-      mov  al, data;
-      out  dx, al;
-    }
-  }
-}
-
-/******************************************************************************
- * Print a formatted message into a string buffer. This is very basic,
- * supporting only strings and integers (16 and 32 bits (l), decimal (d)
- * and hex (x)). Formatting length modifiers are only supported with a single
- * digit -- 32-bit numbers don't need more than 9 characters -- and an
- * optional '0' in front.
- */
-int vsprintf(char _far *buf, const char *fmt, va_list va)
-{
-  char _far *orig = buf;
-  char _far *s;
-  int lmod;
-  int fptr;
-  int zero;
-  int flen;
-
-  for (; *fmt != '\0'; fmt++) {
-    switch (*fmt) {
-
-    case '%':
-      fmt++;
-      zero = flen = 0;
-      if (*fmt >= '0' && *fmt <= '9') {
-        /* formatting length modifiers */
-        zero = (*fmt == '0') ? 1 : 0;
-        fmt += zero;
-        if ((flen = *fmt - '0') >= 1 && flen <= 9) {
-          fmt++;
-        }
-      }
-      
-      /* data type modifiers */
-      lmod = (*fmt == 'l') ? 1 : 0;
-      fptr = (*fmt == 'F') ? 1 : 0;
-      fmt += lmod + fptr;
-
-      switch (*fmt) {
-
-      case 's':
-        if (fptr) {
-          char _far *p = va_arg(va, char _far *);
-          s = (p == 0) ? "[null]" : p;
-        } else {
-          char *p = va_arg(va, char *);
-          s = (p == 0) ? "[null]" : p;
-        }
-        while ((*buf = *(s++)) != '\0')
-          buf++;
-        break;
-
-      case 'c':
-        *(buf++) = (char) va_arg(va, int); 
-        break;
-
-      case 'd':
-        long_to_asc((lmod) ? va_arg(va, long)
-                           : va_arg(va, int), buf, 10, zero, flen);
-        buf += strlen(buf);
-        break;
-
-      case 'x':
-        long_to_asc((lmod) ? va_arg(va, u32)
-                           : va_arg(va, u16), buf, 16, zero, flen);
-        buf += strlen(buf);
-        break;
-
-      case 'p':
-        if (fptr || lmod) {
-          u16 off = va_arg(va, u16);
-          u16 seg = va_arg(va, u16);
-          long_to_asc(seg, buf, 16, 1, 4);
-          buf += strlen(buf);
-          *(buf++) = ':';
-          long_to_asc(off, buf, 16, 1, 4);
-          buf += strlen(buf);
-        } else {
-          long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
-          buf += strlen(buf);
-        }
-        break;
-
-      default:
-        *(buf++) = *fmt;
-        break;
-      }
-    break;
-
-    case '\n':
-      *(buf++) = '\r';
-      *(buf++) = '\n';
-      break;
-
-    default:
-      *(buf++) = *fmt;
-      break;
-
-    }
-  }
-
-  *buf = '\0';
-  return((int) (buf - orig));
-}
-
-/*******************************************************************************
- * Print a formatted message into a string buffer. Relies on vsprintf()
- */
-int sprintf(char _far *buf, const char *fmt, ...)
-{
-  va_list va;
-
-  va_start(va, fmt);
-  return(vsprintf(buf, fmt, va));
-}
-
-/******************************************************************************
- * Print messages to COM
- *
- * NOTES: This function uses a 1K buffer for the resulting message. Thus,
- *        messages should never exceed 1024 bytes.
- */
-void vprintf(const char *fmt, va_list va)
-{
-  static char buf[1024];
-  char *s;
-
-  spin_lock(com_lock);
-
-  vsprintf(buf, fmt, va);
-
-  /* write debug message to COM1 */
-  for (s = buf; *s != '\0'; s++) {
-
-    /* inp() and outp() are redefined by the DDK in an incompatible
-     * way (only words). Instead of messing around with those
-     * definitions, it's safer and easier to put the whole thing
-     * into an _asm block.
-     * 
-     * The C equivalent would look like this:
-     *
-     *   while (!(inp(COM_BASE + 5) & 0x20));
-     *   outp(COM_BASE, *s);
-     */
-
-    _asm {
-      /* wait until COM transmitter is idle */
-      mov  dx, COM_BASE + 5;
-    transmitter_not_idle:
-      in   al, dx;
-      and  al, 0x20;
-      jz   transmitter_not_idle;
-
-      /* output character to be sent */
-      mov  dx, COM_BASE;
-      mov  bx, s;
-      mov  al, [bx];
-      out  dx, al;
-    };
-  }
-
-  spin_unlock(com_lock);
-}
-
-/******************************************************************************
- * Print messages to COM port
- */
-void printf(const char *fmt, ...)
-{
-  va_list va;
-  
-  va_start(va, fmt);
-  vprintf(fmt, va);
-}
-
-/******************************************************************************
- * Print a message to the system console. This works only during device driver
- * initialization.
- *
- * NOTE: This function uses a 1K buffer for the resulting message. Thus,
- *       messages should never exceed 1024 bytes...
- */
-void cprintf(const char *fmt, ...)
-{
-  static char buf[1024];
-  va_list va;
-  size_t len;
-  
-  va_start(va, fmt);
-  vsprintf(buf, fmt, va);
-
-  if (debug) {
-    /* print the same message to COM1 as well */
-    printf("%s", buf);
-  }
-
-  /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
-  if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
-    buf[len-2] = '\0';
-  }
-
-  init_msgtbl.MsgStrings[0] = buf;
-  DevHelp_Save_Message((NPBYTE) &init_msgtbl);
-}
-
-/******************************************************************************
- * Print hex buffer to COM port.
- */
-void phex(const void _far *p, int len, const char *fmt, ...)
-{
-  va_list va;
-  const unsigned char _far *buf = p;
-  long pos = 0;
-  int i;
-
-  if (!debug) {
-    return;
-  }
-
-  /* print header */
-  va_start(va, fmt);
-  vprintf(fmt, va);
-
-  /* print hex block */
-  while (len > 0) {
-    printf("%Fp ", buf);
-
-    /* print hex block */
-    for (i = 0; i < 16; i++) {
-      if (i < len) {
-        printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
-      } else {
-        printf("   ");
-      }
-    }
-
-    /* print ASCII block */
-    printf("   ");
-    for (i = 0; i < ((len > 16) ? 16 : len); i++) {
-      printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
-    }
-    printf("\n");
-
-    pos += 16;
-    buf += 16;
-    len -= 16;
-  }
-}
-
-/******************************************************************************
- * Return length of zero-terminated string
- */
-size_t strlen(const char _far *s)
-{
-  int len = 0;
-
-  while (*(s++) != '\0') {
-    len++;
-  }
-  return(len);
-}
-
-/******************************************************************************
- * Copy zero-terminated string
- */
-char _far *strcpy(char _far *dst, const char _far *src)
-{
-  char _far *orig = dst;
-
-  while ((*(dst++) = *(src++)) != '\0');
-  return(orig);
-}
-
-/******************************************************************************
- * Compare blocks of memory
- */
-int memcmp(void _far *p1, void _far *p2, size_t len)
-{
-  register char _far *s1 = p1;
-  register char _far *s2 = p2;
-  int n = 0;
-
-  while (len > 0) {
-    if ((n = *(s1++) - *(s2++)) != 0) {
-      return(n);
-    }
-    len--;
-  }
-  return(0);
-}
-
-/******************************************************************************
- * Convert a string to a long value using the specified base
- */
-long strtol(const char _far *buf, const char _far * _far *ep, int base)
-{
-  register const char _far *s = buf;
-  long val = 0;
-  int negative = 0;
-
-  /* skip leading whitespace */
-  while (*s == ' ' || *s == '\t') {
-    s++;
-  }
-
-  /* positive or negative */
-  if (*s == '-') {
-    negative = 1;
-    s++;
-  } else if (*s == '+') {
-    s++;
-  }
-
-  /* convert string to long integer */
-  for (;; s++) {
-    int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
-    if (digit < 0 || digit >= base) {
-      break;
-    }
-    val *= base;
-    val += digit;
-  }
-
-  if (ep != NULL) {
-    *ep = s;
-  }
-  if (negative) {
-    val = -val;
-  }
-  return(val);
-}
-
-/******************************************************************************
- * Extremely simple and stupid implementation of malloc(). The heap is very
- * small, only 8K at the moment, and the memory blocks are managed using a
- * simple array of "number of heap units allocated", zero meaning this unit is
- * available. Each heap unit is currently 128 bytes.
- *
- * Dynamic memory is primarily used for things like ATA identify, ATAPI
- * sense buffers, etc. and should be freed as soon as possible, otherwise
- * we'll quickly run out of memory.
- *
- * NOTE: This function is not reentrant, thus must be called with the driver-
- *       level spinlock held. The main reason for this design is that most
- *       functions that need dynamic memory are already holding the spinlock.
- */
-void *malloc(size_t len)
-{
-  u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
-  u16 i;
-  u16 n;
-
-  /* find a sequence of free heap units big enough for the requested length */
-  for (i = 0; i < HEAP_UNIT_CNT; i++) {
-    if (heap_units[i] == 0) {
-      for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
-        if (heap_units[n] != 0) {
-          break;
-        }
-      }
-      if (n == i + units) {
-        /* found a chunk large enough; update 'heap_units[]' */
-        for (; i < n; i++) {
-          heap_units[i] = (u8) (n - i);
-        }
-        return(heap_buf + (n - units) * HEAP_UNIT);
-      }
-      
-      /* keep searching... */
-      i = n;
-    } else {
-      /* skip occupied heap units */
-      i += heap_units[i] - 1;
-    }
-  }
-
-  /* out of memory */
-  dprintf("malloc(%d): out of memory\n", len);
-  return(NULL);
-}
-
-/******************************************************************************
- * Free block of memory allocted by malloc().
- *
- * NOTE: This function is not reentrant, thus must be called with the driver-
- *       level spinlock held. The main reason for this design is that most
- *       functions that need dynamic memory are already holding the spinlock.
- */
-void free(void *ptr)
-{
-  u8 *p = (u8 *) ptr;
-  u16 first_unit;
-  u16 units;
-  u16 i;
-
-  if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
-      (u16) (p - heap_buf) % HEAP_UNIT != 0) {
-    dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
-            (u16) p, (u16) heap_buf);
-    return;
-  }
-
-  /* clear unit allocation counters in heap_units[] */
-  first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
-  units = heap_units[first_unit];
-  for (i = first_unit; i < first_unit + units; i++) {
-    heap_units[i] = 0;
-  }
-}
-
-/******************************************************************************
- * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
- * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
- * calls mdelay() with a large milliseond value an initial delay loop counter
- * of 1,000,000. When the timer triggers, 'mdelay()' will stop and update
- * the delay loop counter.
- *
- * This function needs to be called at device driver init time. Since it uses
- * ADD timers, it must be called with interrupts enabled. All this is not very
- * precise (we should wait for a clock tick before starting, ...) but we don't
- * really need precise timers.
- */
-void mdelay_cal(void)
-{
-  ULONG timer_handle;
-
-  dprintf("calibrating delay loop... ");
-
-  mdelay_loops_per_ms = 100000;
-  mdelay_cal_status = MD_CALIBRATION_START;
-
-  ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
-  mdelay(999999999);
-  ADD_CancelTimer(timer_handle);
-
-  dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
-}
-
-/******************************************************************************
- * Wait specified number of milliseconds. This is implemented using a busy
- * loop and is only good for delays in the millisecond range but never for more
- * than a few milliseconds and only in situations where a proper timer won't do.
- * As a rule of thumb, don't call this function and use ADD timers, instead.
- *
- * NOTES:
- *
- *  - Timers are problematic on x86 platforms because there's no reliable
- *    hardware timer on all architectures and the CPU clock speed may change
- *    while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
- *    calibration routines won't really be sufficient. But this usually only
- *    extends the delay and we don't really need a high precision timer. The
- *    exception are things like notebooks that are clocked slower when on
- *    battery and which got booted while on battery. Should still be OK,
- *    though, because our requirements are not that strict.
- *
- *  - The code in this function is inefficient by design to make sure it
- *    will work with future CPUs which might otherwise be too fast for
- *    our loop counters. Part of this design is using volatile variables to
- *    force memory operations.
- *
- *  - Before using this function, call mdelay_calibrate() to determine the
- *    number of inner loops required per millisecond.
- */
-void mdelay(u32 millies)
-{
-  volatile u32 i;
-  volatile u32 n;
-
-  for (i = 0; i < millies; i++) {
-    for (n = 0; n < mdelay_loops_per_ms; n++) {
-      if (mdelay_cal_end()) {
-        /* this is a calibration run that just ended */
-        goto complete_calibration;
-      }
-    }
-  }
-  return;
-
-complete_calibration:
-  /* complete calibration cycle */
-  if (i < 1000) {
-    /* Initial value for delay loop was too high; interpolate results for
-     * an assumed initial delay loop divided by 1000.
-     */
-    i = i * 1000 + mdelay_loops_per_ms % 1000;
-    mdelay_loops_per_ms /= 1000;
-  }
-  mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
-  mdelay_cal_status = MD_CALIBRATION_DONE;
-}
-
-/******************************************************************************
- * Sleep specified number of milliseonds. This is implemented by yielding the
- * CPU until the system timer value indicates we're done. This function can
- * only be called at task time, or from a context hook.
- *
- * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
- *       can lead to intervals up to 55ms (18.2 timer interrupts per second).
- */
-void msleep(u32 millies)
-{
-  volatile PGINFOSEG gis;
-  ULONG start;
-  ULONG end;
-  PSEL p;
-
-  if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p)) {
-    /* no global info segment; use mdelay() */
-    mdelay(millies);
-    return;
-  }
-  gis = (PGINFOSEG) ((u32) *p << 16);
-  start = gis->msecs;
-  end = start + millies;
-
-  if (end < start) {
-    /* wrap-around; wait until 'msecs' has wrapped, too */
-    while (gis->msecs >= start) {
-      DevHelp_Yield();
-    }
-  }
-
-  while (gis->msecs <= end) {
-    DevHelp_Yield();
-  }
-}
-
-/******************************************************************************
- * Halt processing by submitting an internal error. This is a last resort and
- * should only be called when the system state is corrupt.
- */
-void panic(char *msg)
-{
-  DevHelp_InternalError(msg, strlen(msg));
-}
-
-/******************************************************************************
- * Disable interrupts. The reason for using a separate function for this is
- * that the presence of _asm statements will disable compiler optimizations.
- * In order to support nested calls, this function will return 0 if the
- * interrupts were already disabled or != 0, if not.
- *
- * NOTE: SMP systems must use spinlocks, thus this function will only be
- *       compiled on non-SMP builds.
- */
-#ifndef OS2AHCI_SMP
-int disable(void)
-{
-  int rc = 0;
-
-  _asm {
-    pushf
-    pop   ax
-    and   ax, 0x0200;  /* "interrupts enabled" bit */
-    mov   rc, ax;
-    cli
-  }
-
-  return(rc);
-}
-#endif
-
-/******************************************************************************
- * Enable interrupts. The reason for using a separate function for this is
- * that the presence of _asm statements will disable compiler optimizations.
- *
- * NOTE: SMP systems must use spinlocks, thus this function will only be
- *       compiled on non-SMP builds.
- */
-#ifndef OS2AHCI_SMP
-void enable(void)
-{
-  _asm sti;
-}
-#endif
-
-/******************************************************************************
- * Convert 'long' to ASCII with the specified base
- */
-static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
-{
-  register unsigned long abs_val = (unsigned long) val;
-  char tmp[80];
-  char _far *ptmp = tmp;
-  char _far *s;
-
-  if (base > 16) {
-    sprintf(buf, "[EVAL]");
-    return;
-  }
-
-  tmp[sizeof(tmp) - 1] = '\0';
-  for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
-    *s = hex_digits[abs_val % base];
-    flen--;
-    if ((abs_val /= base) == 0) {
-      break;
-    }
-  }
-
-  /* prepend minus sign if val was negative and base is decimal or less */
-  if (val < 0 && base <= 0) {
-    *(--s) = '-';
-    flen--;
-  }
-
-  /* left-pad the resulting number with zeros or spaces up to 'flen' */
-  while (flen > 0) {
-    *(--s) = (zero) ? '0' : ' ';
-    flen--;
-  }
-
-  strcpy(buf, s);
-}
-
-/******************************************************************************
- * Timer callback handler for 'mdelay_calibrate()'
- */
-static void _far mdelay_timer_callback(ULONG timer_handle, ULONG parm1,
-                                       ULONG parm2)
-{
-  mdelay_cal_status = MD_CALIBRATION_END;
-}
-
-/******************************************************************************
- * Determine whether an mdelay calibration run has just ended. This is in a
- * function to prevent overzealous optimizers from removing the whole delay
- * loop in mdelay().
- */
-static int mdelay_cal_end(void)
-{
-  return(mdelay_cal_status == MD_CALIBRATION_END);
-}
Index: trunk/src/os2ahci.c
===================================================================
--- trunk/src/os2ahci.c	(revision 12)
+++ 	(revision )
@@ -1,1011 +1,0 @@
-/******************************************************************************
- * os2ahci.c - main file for os2ahci driver
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* parse integer command line parameter */
-#define drv_parm_int(s, value, type, radix)               \
-  {                                                       \
-    char _far *_ep;                                       \
-    if ((s)[1] != ':') {                                  \
-      cprintf("missing colon (:) after /%c\n", *(s));     \
-      goto init_fail;                                     \
-    }                                                     \
-    value = (type) strtol((s) + 2,                        \
-			  (const char _far* _far*) &_ep,  \
-			  radix);                         \
-    s = _ep;                                              \
-  }
-
-
-/* MT: got to fix include paths... */
-#ifndef IOCM_EXECUTE_ATA
-#define IOCM_EXECUTE_ATA 0x0003 
-#endif
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/* -------------------------- function prototypes -------------------------- */
-
-/* ------------------------ global/static variables ------------------------ */
-
-int             debug = 0;         /* if > 0, print debug messages to COM1 */
-int             thorough_scan;     /* if != 0, perform thorough PCI scan */
-int             init_reset;        /* if != 0, reset ports during init */
-
-PFN             Device_Help = 0;   /* pointer to device helper entry point */
-ULONG           RMFlags = 0;       /* required by resource manager library */
-PFN             RM_Help0 = NULL;   /* required by resource manager library */
-PFN             RM_Help3 = NULL;   /* required by resource manager library */
-HDRIVER         rm_drvh;           /* resource manager driver handle */
-char            rm_drvname[80];    /* driver name as returned by RM */
-USHORT          add_handle;        /* driver handle (RegisterDeviceClass) */
-UCHAR           timer_pool[TIMER_POOL_SIZE]; /* timer pool */
-
-/* resource manager driver information structure */
-DRIVERSTRUCT rm_drvinfo = {
-  "OS2AHCI",                       /* driver name */
-  "AHCI SATA Driver",              /* driver description */
-  "GNU",                           /* vendor name */
-  CMVERSION_MAJOR,                 /* RM interface version major */
-  CMVERSION_MINOR,                 /* RM interface version minor */
-  2010, 4, 27,                     /* date */
-  0,                               /* driver flags */
-  DRT_ADDDM,                       /* driver type */
-  DRS_ADD,                         /* driver sub type */
-  NULL                             /* driver callback */
-};
-
-ULONG           drv_lock;          /* driver-level spinlock */
-IORB_QUEUE      driver_queue;      /* driver-level IORB queue */
-AD_INFO         ad_infos[MAX_AD];  /* adapter information list */
-int             ad_info_cnt;       /* number of entries in ad_infos[] */
-int             init_complete;     /* if != 0, initialization has completed */
-
-/* apapter/port-specific options saved when parsing the command line */
-int             link_speed[MAX_AD][AHCI_MAX_PORTS];
-
-static char     init_msg[] = "OS2AHCI driver version %d.%02d\n";
-static char     exit_msg[] = "OS2AHCI driver *not* installed\n";
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * OS/2 device driver main strategy function. This function is only used
- * for initialization purposes; all other calls go directly to the adapter
- * device driver's strategy function.
- */
-USHORT c_strat(RPH _far *req)
-{ 
-  u16 rc;
-
-  switch (req->Cmd) {
-
-  case CMDInitBase:
-    rc = init_drv((RPINITIN _far *) req);
-    break;
-
-  default:
-    rc = STDON | STATUS_ERR_UNKCMD;
-    break;
-  }
-
-  return(rc);
-}
-
-/******************************************************************************
- * Intialize the os2ahci driver. This includes command line parsing, scanning
- * the PCI bus for supported AHCI adapters, etc.
- */
-USHORT init_drv(RPINITIN _far *req)
-{
-  RPINITOUT _far *rsp = (RPINITOUT _far *) req;
-  DDD_PARM_LIST _far *ddd_pl = (DDD_PARM_LIST _far *) req->InitArgs;
-  APIRET rmrc;
-  char _far *cmd_line;
-  char _far *s;
-  int adapter_index;
-  int port_index;
-  u16 vendor;
-  u16 device;
-
-  /* set device helper entry point */
-  Device_Help = req->DevHlpEP;
-
-  /* create driver-level spinlock */
-  DevHelp_CreateSpinLock(&drv_lock);
-
-  if (debug) {
-    /* initialize debug interface (COM1) */
-    init_com1();
-  }
-
-  /* print initialization message */
-  cprintf(init_msg, VERSION / 100, VERSION % 100);
-
-  /* register driver with resource manager */
-  if ((rmrc = RMCreateDriver(&rm_drvinfo, &rm_drvh)) != RMRC_SUCCESS) {
-    cprintf("failed to register driver with resource manager (rc = %d)\n", rmrc);
-    goto init_fail;
-  }
-
-  /* parse command line parameters */
-  cmd_line = (char _far *) ((u32) ddd_pl & 0xffff0000l) + ddd_pl->cmd_line_args;
-  adapter_index = 0;
-  port_index = 0;
-
-  for (s = cmd_line; *s != 0; s++) {
-    if (*s == '/' && s[1] != '\0') {
-      s++;
-      switch(tolower(*s)) {
-
-      case 'd':
-        /* increase debug level */
-        debug++;
-        break;
-
-      case 'i':
-        /* add specfied PCI ID as a supported generic AHCI adapter  */
-        drv_parm_int(s, vendor, u16, 16);
-        drv_parm_int(s, device, u16, 16);
-        if (add_pci_id(vendor, device)) {
-          cprintf("failed to add PCI ID %04x:%04x\n", vendor, device);
-          goto init_fail;
-        }
-        thorough_scan = 1;
-        break;
-
-      case 't':
-        /* perform thorough PCI scan (i.e. look for individual supported PCI IDs) */
-        thorough_scan = 1;
-        break;
-
-      case 'r':
-        /* reset ports during initialization */
-        init_reset = 1;
-        break;
-
-      case 'a':
-        /* set adapter index for adapter and port-related options */
-        drv_parm_int(s, adapter_index, int, 10);
-        if (adapter_index < 0 || adapter_index >= MAX_AD) {
-          cprintf("invalid adapter index (%d)\n", adapter_index);
-          goto init_fail;
-        }
-        break;
-
-      case 'p':
-        /* set port index for port-related options */
-        drv_parm_int(s, port_index, int, 10);
-        if (port_index < 0 || port_index >= AHCI_MAX_PORTS) {
-          cprintf("invalid port index (%d)\n", port_index);
-          goto init_fail;
-        }
-        break;
-
-      case 's':
-        /* set link speed of current port on current adapter */
-        drv_parm_int(s, link_speed[adapter_index][port_index], u8, 10);
-        init_reset = 1;
-        break;
-
-      default:
-        cprintf("invalid option: /%c\n", *s);
-        goto init_fail;
-      }
-    }
-  }
-
-  /* scan PCI bus for supported devices */
-  scan_pci_bus();
-
-  if (ad_info_cnt > 0) {
-    /* initialization succeeded and we found at least one AHCI adapter */
-    ADD_InitTimer(timer_pool, sizeof(timer_pool));
-    mdelay_cal();
-
-    if (DevHelp_RegisterDeviceClass("OS2AHCI", (PFN) add_entry, 0, 1,
-        &add_handle)) {
-      cprintf("error: couldn't register device class\n");
-      goto init_fail;
-    }
-    
-    /* allocate context hooks */
-    if (DevHelp_AllocateCtxHook(mk_NPFN(restart_hook), &restart_ctxhook_h) != 0 ||
-        DevHelp_AllocateCtxHook(mk_NPFN(reset_hook), &reset_ctxhook_h) != 0 ||
-        DevHelp_AllocateCtxHook(mk_NPFN(engine_hook), &engine_ctxhook_h)) {
-      cprintf("failed to allocate task-time context hooks\n");
-      goto init_fail;
-    }
-
-    rsp->CodeEnd = (u16) end_of_code;
-    rsp->DataEnd = (u16) &end_of_data;
-    return(STDON);
-  }
-
-init_fail:
-  /* initialization failed; set segment sizes to 0 and return error */
-  cprintf(exit_msg);
-  rsp->CodeEnd = 0;
-  rsp->DataEnd = 0;
-
-  /* free context hooks */
-  if (engine_ctxhook_h != 0)  DevHelp_FreeCtxHook(engine_ctxhook_h);
-  if (reset_ctxhook_h != 0)   DevHelp_FreeCtxHook(reset_ctxhook_h);
-  if (restart_ctxhook_h != 0) DevHelp_FreeCtxHook(restart_ctxhook_h);
-
-  if (rm_drvh != 0) {
-    /* remove driver from resource manager */
-    RMDestroyDriver(rm_drvh);
-  }
-
-  cprintf(exit_msg);
-  return(STDON | ERROR_I24_QUIET_INIT_FAIL);
-}
-
-/******************************************************************************
- * ADD entry point. This is the main entry point for all ADD requests. Due to
- * the asynchronous nature of ADD drivers, this function primarily queues the
- * IORB(s) to the corresponding adapter or port queues, then triggers the
- * state machine to initiate processing queued IORBs.
- *
- * NOTE: In order to prevent race conditions or engine stalls, certain rules
- *       around locking, unlocking and IORB handling in general have been
- *       established. Refer to the comments in "trigger_engine()" for
- *       details.
- */
-void _far _loadds add_entry(IORBH _far *first_iorb)
-{
-  IORBH _far *iorb;
-  IORBH _far *next = NULL;
-
-  spin_lock(drv_lock);
-
-  for (iorb = first_iorb; iorb != NULL; iorb = next) {
-    /* Queue this IORB. Queues primarily exist on port level but there are
-     * some requests which affect the whole driver, most notably
-     * IOCC_CONFIGURATION. In either case, adding the IORB to the driver or
-     * port queue will change the links, thus we need to save the original
-     * link in 'next'.
-     */
-    next = (iorb->RequestControl | IORB_CHAIN) ? iorb->pNxtIORB : 0;
-
-    iorb->Status = 0;
-    iorb->ErrorCode = 0;
-    memset(&iorb->ADDWorkSpace, 0x00, sizeof(ADD_WORKSPACE));
-
-    if (iorb_driver_level(iorb)) {
-      /* adapter-level IORB */
-      iorb->UnitHandle = 0;
-      iorb_queue_add(&driver_queue, iorb);
-
-    } else {
-      /* port-level IORB */
-      int a = iorb_unit_adapter(iorb);
-      int p = iorb_unit_port(iorb);
-      int d = iorb_unit_device(iorb);
-      
-      if (a >= ad_info_cnt ||
-          p > ad_infos[a].port_max ||
-          d > ad_infos[a].ports[p].dev_max ||
-          (ad_infos[a].port_map & (1UL << p)) == 0) {
-
-        /* unit handle outside of the allowed range */
-        dprintf("warning: IORB for %d.%d.%d out of range\n", a, p, d);
-        iorb->Status = IORB_ERROR | IORB_DONE;
-        iorb->ErrorCode = IOERR_CMD_SYNTAX;
-        if (iorb->RequestControl & IORB_ASYNC_POST) {
-          iorb->NotifyAddress(iorb);
-        }
-        continue;
-      }
-
-      iorb_queue_add(&ad_infos[a].ports[p].iorb_queue, iorb);
-    }
-  }
-
-  /* trigger state machine */
-  trigger_engine();
-
-  spin_unlock(drv_lock);
-}
-
-/******************************************************************************
- * Trigger IORB queue engine. This is a wrapper function for trigger_engine_1()
- * which will try to get all IORBs sent on their way a couple of times. If
- * there are still IORBs ready for processing after this, this function will
- * hand off to a context hook which will continue to trigger the engine until
- * all IORBs have been sent.
- */
-void trigger_engine(void)
-{
-  int i;
-
-  for (i = 0; i < 3; i++) {
-    if (trigger_engine_1() == 0) {
-      /* done -- all IORBs have been sent on their way */
-      return;
-    }
-  }
-
-  /* Something keeps bouncing; hand off to the engine context hook which will
-   * keep trying in the background.
-   */
-  DevHelp_ArmCtxHook(0, engine_ctxhook_h);
-}
-
-/******************************************************************************
- * Trigger IORB queue engine in order to send commands in the driver/port IORB
- * queues to the AHCI hardware. This function will return the number of IORBs
- * sent. Keep in mind that IORBs might "bounce" if the adapter/port is not in
- * a state to accept the command, thus it might take quite a few calls to get
- * all IORBs on their way. This is why there's a wrapper function which tries
- * it a few times, then hands off to a context hook which will keep trying in
- * the background.
- *
- * IORBs might complete before send_iorb() has returned, at any time during
- * interrupt processing or on another CPU on SMP systems. IORB completion
- * means modifications to the corresponding IORB queue (the completed IORB
- * is removed from the queue) thus we need to protect the IORB queues from
- * race conditions. The safest approach short of keeping the driver-level
- * spinlock aquired permanently is to keep it throughout this function and
- * release it temporarily in send_iorb().
- *
- * This implies that the handler functions are fully responsible for aquiring
- * the driver-level spinlock when they need it, and for releasing it again.
- *
- * As a rule of thumb, get the driver-level spinlock whenever accessing
- * volatile variables (IORB queues, values in ad_info[], ...).
- *
- * Additional Notes:
- *
- * - This function is expected to be called with the spinlock aquired
- *
- * - The handler functions are free to process more than one IORB (i.e.
- *   command queuing). The idea is that the root IORB (and potential next
- *   IORBs) are marked as "processing" which keeps trigger_engine() from
- *   touching this IORB queue until all of those IORBs have completed
- *   processing and have been removed from the queue.
- *
- * - Adapters can be flagged as 'busy' which means no new IORBs are sent (they
- *   just remain in the queue). This can be used to release the driver-level
- *   spinlock while making sure no new IORBs are going to hit the hardware.
- *   In order to prevent engine stalls, all handlers using this functionality
- *   need to invoke trigger_engine() after resetting the busy flag.
- *
- *   - Handlers that use the adapter 'busy' flag when processing driver-level
- *     IORBs don't need to protect themselves against new IORBs on the driver
- *     level because those will always be queued at the end of the driver-
- *     level queue (there are no driver-level priority IORBs).
- */
-int trigger_engine_1(void)
-{
-  IORBH _far *iorb;
-  int iorbs_sent = 0;
-  int a;
-  int p;
-
-  iorbs_sent = 0;
-
-  /* process driver-level IORBs */
-  if ((iorb = driver_queue.root) != NULL && !add_workspace(iorb)->processing) {
-    send_iorb(iorb);
-    iorbs_sent++;
-  }
-
-  /* process port-level IORBs */
-  for (a = 0; a < ad_info_cnt; a++) {
-    AD_INFO *ai = ad_infos + a;
-    if (ai->busy) {
-      /* adapter is busy; don't process any IORBs */
-      continue;
-    }
-    for (p = 0; p <= ai->port_max; p++) {
-      if ((iorb = ai->ports[p].iorb_queue.root) != NULL &&
-          !add_workspace(iorb)->processing) {
-        send_iorb(iorb);
-        iorbs_sent++;
-      }
-    }
-  }
-
-  return(iorbs_sent);
-}
-
-/******************************************************************************
- * Send a single IORB to the corresponding AHCI adapter/port. This is just a
- * switch board for calling the corresponding iocc_*() handler function.
- *
- * NOTE: This function is expected to be called with the driver-level spinlock
- *       aquired. It will release it before calling any of the handler
- *       functions and re-aquire it when done.
- */
-void send_iorb(IORBH _far *iorb)
-{
-  /* Mark IORB as "processing" before doing anything else. Once the IORB is
-   * marked as "processing", we can release the spinlock because subsequent
-   * invocations of trigger_engine() (e.g. at interrupt time) will ignore this
-   * IORB.
-   *
-   * NOTE: Handler functions are free to look at follow-up IORBs in order to
-   *       queue them to the hardware (native command queueing). If they
-   *       choose to do so, they must mark those IORBs as "processing" as
-   *       well.
-   */
-  add_workspace(iorb)->processing = 1;
-  spin_unlock(drv_lock);
-
-  switch (iorb->CommandCode) {
-
-  case IOCC_CONFIGURATION:
-    iocc_configuration(iorb);
-    break;
-
-  case IOCC_DEVICE_CONTROL:
-    iocc_device_control(iorb);
-    break;
-
-  case IOCC_UNIT_CONTROL:
-    iocc_unit_control(iorb);
-    break;
-
-  case IOCC_GEOMETRY:
-    iocc_geometry(iorb);
-    break;
-
-  case IOCC_EXECUTE_IO:
-    iocc_execute_io(iorb);
-    break;
-
-  case IOCC_UNIT_STATUS:
-    iocc_unit_status(iorb);
-    break;
-
-  case IOCC_ADAPTER_PASSTHRU:
-    iocc_adapter_passthru(iorb);
-    break;
-
-  default:
-    /* unsupported call */
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-    break;
-  }
-
-  /* re-aquire spinlock before returning to trigger_engine() */
-  spin_lock(drv_lock);
-}
-
-/******************************************************************************
- * Handle IOCC_CONFIGURATION requests.
- */
-void iocc_configuration(IORBH _far *iorb)
-{
-  int a;
-
-  switch (iorb->CommandModifier) {
-
-  case IOCM_COMPLETE_INIT:
-    /* Complete initialization. From now on, we won't have to restore the BIOS
-     * configuration after each command and we're fully operational (i.e. will
-     * use interrupts, timers and context hooks instead of polling).
-     */
-    dprintf("leaving initialization mode\n");
-    spin_lock(drv_lock);
-    if (!init_complete) {
-      for (a = 0; a < ad_info_cnt; a++) {
-        ahci_complete_init(ad_infos + a);
-      }
-      init_complete = 1;
-    }
-    spin_unlock(drv_lock);
-    iorb_done(iorb);
-    break;
-
-  case IOCM_GET_DEVICE_TABLE:
-    /* construct a device table */
-    iocm_device_table(iorb);
-    break;
-
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-    break;
-  }
-}
-
-/******************************************************************************
- * Handle IOCC_DEVICE_CONTROL requests.
- */
-void iocc_device_control(IORBH _far *iorb)
-{
-  AD_INFO *ai;
-  IORBH _far *ptr;
-  IORBH _far *next = NULL;
-  int a = iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-
-  switch (iorb->CommandModifier) {
-  
-  case IOCM_ABORT:
-    /* abort all pending commands on specified port and device */
-    ai = ad_infos + a;
-    spin_lock(drv_lock);
-    for (ptr = ai->ports[p].iorb_queue.root; ptr != NULL; ptr = next) {
-      next = ptr->pNxtIORB;
-      /* move all matching IORBs to the abort queue */
-      if (ptr != iorb && iorb_unit_device(ptr) == d) {
-        iorb_queue_del(&ai->ports[p].iorb_queue, ptr);
-        iorb_queue_add(&abort_queue, ptr);
-        ptr->ErrorCode = IOERR_CMD_ABORTED;
-      }
-    }
-    spin_unlock(drv_lock);
-
-    /* trigger reset context hook which will finish the abort processing */
-    DevHelp_ArmCtxHook(0, reset_ctxhook_h);
-    break;
-    
-  case IOCM_SUSPEND:
-  case IOCM_RESUME:
-  case IOCM_GET_QUEUE_STATUS:
-    /* Suspend/resume operations allow access to the hardware for other
-     * entities such as IBMIDECD.FLT. Since os2ahci implements both ATA
-     * and ATAPI in the same driver, this won't be required.
-     */
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    break;
-
-  case IOCM_LOCK_MEDIA:
-  case IOCM_UNLOCK_MEDIA:
-  case IOCM_EJECT_MEDIA:
-    /* unit control commands to lock, unlock and eject media */
-    /* will be supported later... */
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    break;
-
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    break;
-  }
-
-  iorb_done(iorb);
-}
-
-/******************************************************************************
- * Handle IOCC_UNIT_CONTROL requests.
- */
-void iocc_unit_control(IORBH _far *iorb)
-{
-  IORB_UNIT_CONTROL _far *iorb_uc = (IORB_UNIT_CONTROL _far *) iorb;
-  int a = iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-  int d = iorb_unit_device(iorb);
-
-  spin_lock(drv_lock);
-  switch (iorb->CommandModifier) {
-
-  case IOCM_ALLOCATE_UNIT:
-    /* allocate unit for exclusive access */
-    if (ad_infos[a].ports[p].devs[d].allocated) {
-      iorb_seterr(iorb, IOERR_UNIT_ALLOCATED);
-    } else {
-      ad_infos[a].ports[p].devs[d].allocated = 1;
-    }
-    break;
-
-  case IOCM_DEALLOCATE_UNIT:
-    /* deallocate exclusive access to unit */
-    if (!ad_infos[a].ports[p].devs[d].allocated) {
-      iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
-    } else {
-      ad_infos[a].ports[p].devs[d].allocated = 0;
-    }
-    break;
-
-  case IOCM_CHANGE_UNITINFO:
-    /* Change unit (device) information. One reason for this IOCM is the
-     * interface for filter device drivers: a filter device driver can
-     * either change existing UNITINFOs or permanently allocate units
-     * and fabricate new [logical] units; the former is the reason why we
-     * must store the pointer to the updated UNITNIFO for subsequent
-     * IOCC_CONFIGURATION/IOCM_GET_DEVICE_TABLE calls.
-     */
-    if (!ad_infos[a].ports[p].devs[d].allocated) {
-      iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
-      break;
-    }
-    ad_infos[a].ports[p].devs[d].unit_info = iorb_uc->pUnitInfo;
-    break;
-
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    break;
-  }
-
-  spin_unlock(drv_lock);
-  iorb_done(iorb);
-}
-
-/******************************************************************************
- * Scan all ports for AHCI devices and construct a DASD device table.
- *
- * NOTE: This function may be called multiple times. Only the first invocation
- *       will actually scan for devices; all subsequent calls will merely
- *       return the results of the initial scan, potentially augmented by
- *       modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
- *       requests.
- */
-void iocm_device_table(IORBH _far *iorb)
-{
-  IORB_CONFIGURATION _far *iorb_conf;
-  DEVICETABLE _far *dt;
-  char _far *pos;
-  int rc;
-  int a;
-  int p;
-  int d;
-
-  iorb_conf = (IORB_CONFIGURATION _far *) iorb;
-  dt = iorb_conf->pDeviceTable;
-
-  spin_lock(drv_lock);
-
-  /* initialize device table header */
-  dt->ADDLevelMajor = ADD_LEVEL_MAJOR;
-  dt->ADDLevelMinor = ADD_LEVEL_MINOR;
-  dt->ADDHandle     = add_handle;
-  dt->TotalAdapters = ad_info_cnt;
-
-  /* Initial position of dynamic portion of device table (i.e. behind the
-   * array of ADAPTERINFO pointers, pAdapter, in the device table)
-   */
-  pos = (char _far *) (dt->pAdapter + ad_info_cnt);
-
-  for (a = 0; a < ad_info_cnt; a++) {
-    ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) pos;
-    AD_INFO *ad_info = ad_infos + a;
-    int units = 0;
-
-    /* sanity check for sufficient space in device table */
-    if ((u32) (ptr + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
-      dprintf("error: device table provided by DASD too small\n");
-      iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
-      goto iocm_device_table_done;
-    }
-
-    /* set ADAPTERINFO offset in device table */
-    dt->pAdapter[a] = (ADAPTERINFO _near *) ((u32) ptr & 0xffff);
-
-    /* fill in adapter information structure in device table */
-    memset(ptr, 0x00, sizeof(*ptr));
-    sprintf(ptr->AdapterName, "AHCI_%d", a);
-    ptr->AdapterDevBus   = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
-    ptr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
-    ptr->AdapterHostBus  = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
-    ptr->AdapterFlags    = AF_16M | AF_HW_SCATGAT;
-
-    /* AHCI limits S/G elements to 22 bits, thus we'll report only half of
-     * our S/G list buffers to reduce complexity. The command preparation code
-     * will always try to map as many S/G elements as possible so the physical
-     * S/G list capacity is not really wasted except in rare conditions where
-     * we need to split commands with long S/G lists without any suitable split
-     * points except those at the reported MaxHWSGList.
-     */
-    ptr->MaxHWSGList     = AHCI_MAX_SG / 2;
-
-    if (!ad_info->port_scan_done) {
-      /* First call; need to scan AHCI hardware for devices. Since this might
-       * be a lengthy operation, especially when init_reset is set, we'll mark
-       * the adapter as busy (new IORBs will only be queued but not executed)
-       * and release the spinlock while scanning the ports so interrupts will
-       * be processed.
-       */
-      if (ad_info->busy) {
-        dprintf("error: port scan requested while adapter was busy\n");
-        iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
-        goto iocm_device_table_done;
-      }
-      ad_info->busy = 1;
-      spin_unlock(drv_lock);
-      rc = ahci_scan_ports(ad_info);
-      spin_lock(drv_lock);
-      ad_info->busy = 0;
-
-      if (rc != 0) {
-        dprintf("error: port scan failed on adapter #%d\n", a);
-        iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
-        goto iocm_device_table_done;
-      }
-      ad_info->port_scan_done = 1;
-    }
-
-    /* insert devices (units) into the device table */
-    for (p = 0; p <= ad_info->port_max; p++) {
-      for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
-        if (ad_info->ports[p].devs[d].present) {
-          UNITINFO _far *ui = ptr->UnitInfo + units;
-
-          /* sanity check for sufficient space in device table */
-          if ((u32) (ui + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
-            dprintf("error: device table provided by DASD too small\n");
-            iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
-            goto iocm_device_table_done;
-          }
-
-          if (ad_info->ports[p].devs[d].unit_info == NULL) {
-            /* provide initial information about this device (unit) */
-            memset(ui, 0x00, sizeof(*ui));
-            ui->AdapterIndex = a;
-            ui->UnitIndex    = units;
-            ui->UnitHandle   = iorb_unit(a, p, d);
-            ui->UnitType     = ad_info->ports[p].devs[d].dev_type;
-            ui->QueuingCount = AHCI_MAX_CMDS;
-            if (ad_info->ports[p].devs[d].removable) {
-              ui->UnitFlags |= UF_REMOVABLE;
-            }
-          } else {
-            /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
-            memcpy(ui, ad_info->ports[p].devs[d].unit_info, sizeof(*ui));
-          }
-          units++;
-        }
-      }
-    }
-
-    /* set total device (unit) count for this adapter */
-    ptr->AdapterUnits = units;
-
-    /* calculate offset for next adapter */
-    pos = (char _far *) (ptr->UnitInfo + units);
-  }
-
-iocm_device_table_done:
-  spin_unlock(drv_lock);
-  iorb_done(iorb);
-}
-
-/******************************************************************************
- * Handle IOCC_GEOMETRY requests.
- */
-void iocc_geometry(IORBH _far *iorb)
-{
-  switch (iorb->CommandModifier) {
-
-  case IOCM_GET_MEDIA_GEOMETRY:
-  case IOCM_GET_DEVICE_GEOMETRY:
-    add_workspace(iorb)->idempotent = 1;
-    ahci_get_geometry(iorb);
-    break;
-
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-  }
-}
-
-/******************************************************************************
- * Handle IOCC_EXECUTE_IO requests.
- */
-void iocc_execute_io(IORBH _far *iorb)
-{
-  switch (iorb->CommandModifier) {
-
-  case IOCM_READ:
-    add_workspace(iorb)->idempotent = 1;
-    ahci_read(iorb);
-    break;
-
-  case IOCM_READ_VERIFY:
-    add_workspace(iorb)->idempotent = 1;
-    ahci_verify(iorb);
-    break;
-
-  case IOCM_WRITE:
-    add_workspace(iorb)->idempotent = 1;
-    ahci_write(iorb);
-    break;
-
-  case IOCM_WRITE_VERIFY:
-    add_workspace(iorb)->idempotent = 1;
-    ahci_write(iorb);
-    break;
-    
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-  }
-}
-
-/******************************************************************************
- * Handle IOCC_UNIT_STATUS requests.
- */
-void iocc_unit_status(IORBH _far *iorb)
-{
-  switch (iorb->CommandModifier) {
-
-  case IOCM_GET_UNIT_STATUS:
-    add_workspace(iorb)->idempotent = 1;
-    ahci_unit_ready(iorb);
-    break;
-
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-  }
-}
-
-/******************************************************************************
- * Handle IOCC_ADAPTER_PASSTHROUGH requests.
- */
-void iocc_adapter_passthru(IORBH _far *iorb)
-{
-  switch (iorb->CommandModifier) {
-
-  case IOCM_EXECUTE_CDB:
-    ahci_execute_cdb(iorb);
-    break;
-
-  case IOCM_EXECUTE_ATA:
-    ahci_execute_ata(iorb);
-    break;
-
-  default:
-    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
-    iorb_done(iorb);
-  }
-}
-
-/******************************************************************************
- * Add an IORB to the specified queue.
- */
-void iorb_queue_add(IORB_QUEUE _far *queue, IORBH _far *iorb)
-{
-  if (iorb_priority(iorb) {
-    /* priority IORB; insert at first position */
-    iorb->pNxtIORB = queue->root;
-    queue->root = iorb;
-
-  } else {
-    /* append IORB to end of queue */
-    iorb->pNxtIORB = NULL;
-
-    if (queue->root == NULL) {
-      queue->root = iorb;
-    } else {
-      queue->tail->pNxtIORB = iorb;
-    }
-    queue->tail = iorb;
-  }
-
-  ddprintf("IORB queued:  %d/%d (queue = %Fp, IORB = %Fp)\n",
-           iorb->CommandCode, iorb->CommandModifier, queue, iorb);
-}
-
-/******************************************************************************
- * Remove an IORB from the specified queue.
- */
-int iorb_queue_del(IORB_QUEUE _far *queue, IORBH _far *iorb)
-{
-  IORBH _far *_iorb;
-  IORBH _far *_prev = NULL;
-  int found = 0;
-
-  for (_iorb = queue->root; _iorb != NULL; _iorb = _iorb->pNxtIORB) {
-    if (_iorb == iorb) {
-      /* found the IORB to be removed */
-      if (_prev != NULL) {
-        _prev->pNxtIORB = _iorb->pNxtIORB;
-      } else {
-        queue->root = _iorb->pNxtIORB;
-      }
-      if (_iorb == queue->tail) {
-        queue->tail = _prev;
-      }
-      found = 1;
-      break;
-    }
-    _prev = _iorb;
-  }
-
-  if (found) {
-    ddprintf("IORB removed: %d/%d (queue = %Fp, IORB = %Fp) - %04x/%04x\n",
-             iorb->CommandCode, iorb->CommandModifier, queue, iorb,
-             iorb->Status, iorb->ErrorCode);
-  } else {
-    ddprintf("IORB %Fp not found in queue %Fp\n", iorb, queue);
-  }
-
-  return(!found);
-}
-        
-/******************************************************************************
- * Set the error code in the specified IORB
- * 
- * NOTE: This function does *not* call iorb_done(). It merely sets the IORB
- *       status to the specified error code.
- */
-void iorb_seterr(IORBH _far *iorb, USHORT error_code)
-{
-  iorb->ErrorCode = error_code;
-  iorb->Status = IORB_ERROR;
-}
-
-/******************************************************************************
- * Mark the specified IORB as done and notify the asynchronous post function,
- * if any. The IORB is also removed from the corresponding IORB queue.
- *
- * NOTES: This function does not clear the Status field; it merely adds the
- *        IORB_DONE flag.
- *
- *        This function is expected to be called *without* the corresponding
- *        driver-level drv_lock aquired. It will aquire the spinlock before
- *        updating the IORB queue and release it before notifying the upstream
- *        code in order to prevent deadlocks.
- *
- *        Due to this logic, this function is only good for simple task-time
- *        completions. Functions working on lists of IORBs (such as interrupt
- *        handlers or context hooks) should implement their own logic. See
- *        abort_ctxhook() for an example.
- */
-void iorb_done(IORBH _far *iorb)
-{
-  int a = iorb_unit_adapter(iorb);
-  int p = iorb_unit_port(iorb);
-
-  /* remove IORB from corresponding queue */
-  spin_lock(drv_lock);
-  if (iorb_driver_level(iorb)) {
-    iorb_queue_del(&driver_queue, iorb);
-  } else {
-    iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb);
-  }
-  aws_free(add_workspace(iorb));
-  spin_unlock(drv_lock);
-
-  /* notify caller, if requested */
-  iorb->Status |= IORB_DONE;
-  if (iorb->RequestControl & IORB_ASYNC_POST) {
-    iorb->NotifyAddress(iorb);
-  }
-}
-
-/******************************************************************************
- * Requeue the specified IORB such that it will be sent downstream for
- * processing again. This includes freeing all resources currently allocated
- * (timer, buffer, ...) and resetting the flags to 0.
- *
- * The following flags are preserved:
- *  - no_ncq
- */
-void iorb_requeue(IORBH _far *iorb)
-{
-  ADD_WORKSPACE _far *aws = add_workspace(iorb);
-  u16 no_ncq = aws->no_ncq;
-
-  aws_free(aws);
-  memset(aws, 0x00, sizeof(*aws));
-  aws->no_ncq = no_ncq;
-}
-
Index: trunk/src/os2ahci.def
===================================================================
--- trunk/src/os2ahci.def	(revision 12)
+++ 	(revision )
@@ -1,18 +1,0 @@
-library os2ahci
-description 'OS2 AHCI Driver'
-protmode
-
-segments
-  DEVHDR        CLASS 'DATA'
-  _DATA         CLASS 'DATA'
-  CONST         CLASS 'CONST'
-  _BSS          CLASS 'BSS'
-  c_common      CLASS 'BSS'
-  _z_data       CLASS 'BSS'
-  _TEXT         CLASS 'CODE' IOPL
-  'CODE'        CLASS 'CODE' IOPL
-  RMCode        CLASS 'CODE' IOPL
-  LIBCODE       CLASS 'CODE' IOPL
-  _z_text       CLASS 'CODE' IOPL
-
-
Index: trunk/src/os2ahci.h
===================================================================
--- trunk/src/os2ahci.h	(revision 12)
+++ 	(revision )
@@ -1,468 +1,0 @@
-/******************************************************************************
- * os2ahci.h - main header file for os2ahci driver
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/* ----------------------------- include files ----------------------------- */
-
-/* IMPORTANT NOTE: The DDK headers require tight structure packing and this
- * is controlled via compiler parameters. Thus, all stuctures in os2ahci.sys
- * are expected to be byte-aligned without the need of explicit pragma pack()
- * directives. Where possible, the structures are layed out such that words
- * and dwords are aligned at least on 2-byte boundaries.
- */
-
-#define INCL_NOPMAPI
-#define INCL_DOSINFOSEG
-#define INCL_NO_SCB
-#define INCL_DOSERRORS
-#include <os2.h>
-#include <dos.h>
-#include <bseerr.h>
-#include <dskinit.h>
-#include <scb.h>
-
-#include <devhdr.h>
-#include <iorb.h>
-#include <strat2.h>
-#include <reqpkt.h>
-#include <dhcalls.h>
-#include <addcalls.h>
-#include <rmcalls.h>
-#include <devclass.h>
-#include <devcmd.h>
-#include <rmbase.h>
-
-#include "ahci.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-#define VERSION            100       /* driver version (2 implied decimals) */
-#define MAX_AD               8       /* maximum number of adapters */
-
-/* Timer pool size. In theory, we need one timer per outstanding command plus
- * a few miscellaneous timers but it's unlikely we'll ever have outstanding
- * commands on all devices on all ports on all apapters -- this would be
- * 8 * 32 * 32 = 8192 outstanding commands on a maximum of 8 * 32 * 15 = 3840
- * devices and that's a bit of an exaggeration. It should be more than enough
- * to have 128 timers.
- */
-#define TIMER_COUNT        128
-#define TIMER_POOL_SIZE    (sizeof(ADD_TIMER_POOL) + \
-                            TIMER_COUNT * sizeof(ADD_TIMER_DATA))
-
-/* default command timeout (can be overwritten in the IORB) */
-#define DEFAULT_TIMEOUT  30000
-
-/* debug output macros */
-#define dprintf   if (debug > 0) printf
-#define dphex     if (debug > 0) phex
-#define ddprintf  if (debug > 1) printf
-#define ddphex    if (debug > 1) phex
-#define dddprintf if (debug > 2) printf
-#define dddphex   if (debug > 2) phex
-
-/* adapter number from AD_INFO pointer; mainly for dprintf() purposes */
-#define ad_no(ai) (((u16) ai - (u16) ad_infos) / sizeof(*ai))
-
-/* Convert far function address into NPFN (the DDK needs this all over the
- * place and just casting to NPFN will produce a "segment lost in conversion"
- * warning. Since casting to a u32 is a bit nasty for function pointers and
- * might have to be revised for different compilers, we'll use a central
- * macro for this crap.
- */
-#define mk_NPFN(func)      (NPFN) (u32) (func)
-
-/* stdarg.h macros with explicit far pointers
- *
- * NOTE: The compiler pushes fixed arguments with 16 bits minimum, thus
- *       the last fixed argument (i.e. the one passed to va_start) must
- *       have at least 16 bits. Otherwise, the address calculation in
- *       va_start() will fail.
- */
-typedef char _far *va_list;
-#define va_start(va, last)    va = (va_list) (&last + 1)
-#define va_arg(va, type)      ((type _far *) (va += sizeof(type)))[-1]
-#define va_end(va)            va = 0
-
-/* ctype macros */
-#define isupper(ch)           ((ch) >= 'A' && (ch) <= 'Z')
-#define tolower(ch)           (isupper(ch) ? (ch) - ('a' - 'A') : (ch))
-
-/* stddef macros */
-#define offsetof(s, e)        ((u16) &((s *) 0)->e)
-
-/* SMP spinlock compatibility macros for older DDKs using CLI/STI */
-#ifndef OS2AHCI_SMP
-#define DevHelp_CreateSpinLock(sph)    *(sph) = 0
-#define DevHelp_FreeSpinLock(sph)      0
-
-#define DevHelp_AquireSpinLock(sph)    if ((sph) != 0)                \
-                                         panic("recursive spinlock"); \
-                                       (sph) = disable()
-
-#define DevHelp_ReleaseSpinLock(sph)   if (sph) {                     \
-                                         (sph) = 0;                   \
-                                         enable();                    \
-                                       }
-#endif
-
-/* shortcut macros */
-#define spin_lock(sl)     DevHelp_AquireSpinLock(sl)
-#define spin_unlock(sl)   DevHelp_ReleaseSpinLock(sl)
-
-/* Get AHCI port MMIO base from AD_INFO and port number. For the time being,
- * MMIO addresses are assumed to be valid 16:16 pointers which implies
- * that one GDT selector is allocated per adapter.
- */
-#define port_base(ai, p)   ((u8 _far *) (ai)->mmio + 0x100 + (p) * 0x80)
-
-/* Get address of port-specific DMA scratch buffer. The total size of all DMA
- * buffers required for 32 ports exceeds 65536 bytes, thus we need multiple
- * GDT selectors to access all port DMA scratch buffers and some logic to map
- * a port number to the corresponding DMA scratch buffer address.
- */
-#define PORT_DMA_BUFS_PER_SEG  ((size_t) (65536UL / AHCI_PORT_PRIV_DMA_SZ))
-#define PORT_DMA_BUF_SEGS      ((AHCI_MAX_PORTS + PORT_DMA_BUFS_PER_SEG - 1) \
-                                / PORT_DMA_BUFS_PER_SEG)
-#define PORT_DMA_SEG_SIZE      ((u32) PORT_DMA_BUFS_PER_SEG * \
-                                (u32) AHCI_PORT_PRIV_DMA_SZ)
-
-#define port_dma_base(ai, p) \
-  ((AHCI_PORT_DMA _far *) ((ai)->dma_buf[(p) / PORT_DMA_BUFS_PER_SEG] + \
-                           ((p) % PORT_DMA_BUFS_PER_SEG) * AHCI_PORT_PRIV_DMA_SZ))
-
-#define port_dma_base_phys(ai, p) \
-  ((ai)->dma_buf_phys + (u32) (p) * AHCI_PORT_PRIV_DMA_SZ)
-
-/* Convert an SATA adapter/port/device address into a 16-bit IORB unit handle
- * (and the other way round). The mapping looks like this:
- *
- *  mapping                   comment
- *  -----------------------------------------------------------------------
- *  4 bits for the adapter    current max is 8 adapters 
- *  4 bits for the port       AHCI spec defines up to 32 ports
- *  4 bits for the device     SATA spec defines up to 15 devices behind PMP
- */
-#define iorb_unit(a, p, d)       ((((u16) (a) & 0x0fU) << 8) | \
-                                  (((u16) (p) & 0x0fU) << 4) | \
-                                  (((u16) (d) & 0x0fU)))
-#define iorb_unit_adapter(iorb)  (((u16) (iorb)->UnitHandle >> 8) & 0x07U)
-#define iorb_unit_port(iorb)     (((u16) (iorb)->UnitHandle >> 4) & 0x0fU)
-#define iorb_unit_device(iorb)   ((u16)  (iorb)->UnitHandle       & 0x0fU)
-
-/*******************************************************************************
- * Convenience macros for IORB processing functions
- */
-/* is this IORB on driver or port level? */
-#define iorb_driver_level(iorb) ((iorb)->CommandCode == IOCC_CONFIGURATION)
-
-/* is this IORB to be inserted at the beginnig of the IORB queue? */
-#define iorb_priority(iorb)     ((iorb)->CommandCode == IOCC_DEVICE_CONTROL && \
-                                 (iorb)->CommandModifier == IOCM_ABORT))
-
-/* access IORB ADD workspace */
-#define add_workspace(iorb)     ((ADD_WORKSPACE _far *) &(iorb)->ADDWorkSpace)
-
-/* free resources in ADD workspace (timer, buffer, ...) */
-#define aws_free(aws)           if ((aws)->timer != 0) {               \
-                                  ADD_CancelTimer((aws)->timer);       \
-                                  (aws)->timer = 0;                    \
-                                }                                      \
-                                if ((aws)->buf != NULL) {              \
-                                  free((aws)->buf);                    \
-                                  (aws)->buf = NULL;                   \
-                                }
-
-/******************************************************************************
- * PCI generic IDs and macros
- */
-#define PCI_ANY_ID                     0xffffU
-#define PCI_VDEVICE(vendor, device)    PCI_VENDOR_ID_##vendor, (device), \
-                                       PCI_ANY_ID, PCI_ANY_ID, 0, 0
-
-/******************************************************************************
- * PCI vendor IDs for AHCI adapters known to this driver (copied from Linux
- * pci_ids.h)
- */
-#define PCI_VENDOR_ID_AL       0x10b9
-#define PCI_VENDOR_ID_AMD      0x1022
-#define PCI_VENDOR_ID_AT       0x1259
-#define PCI_VENDOR_ID_ATI      0x1002
-#define PCI_VENDOR_ID_ATT      0x11c1
-#define PCI_VENDOR_ID_CMD      0x1095
-#define PCI_VENDOR_ID_CT       0x102c
-#define PCI_VENDOR_ID_INTEL    0x8086
-#define PCI_VENDOR_ID_JMICRON  0x197B
-#define PCI_VENDOR_ID_MARVELL  0x11ab
-#define PCI_VENDOR_ID_NVIDIA   0x10de
-#define PCI_VENDOR_ID_PROMISE  0x105a
-#define PCI_VENDOR_ID_SI       0x1039
-#define PCI_VENDOR_ID_VIA      0x1106
-
-/******************************************************************************
- * PCI class IDs we're interested in (copied from Linux pci_ids.h)
- */
-#define PCI_BASE_CLASS_STORAGE          0x01
-#define PCI_CLASS_STORAGE_SCSI          0x0100
-#define PCI_CLASS_STORAGE_IDE           0x0101
-#define PCI_CLASS_STORAGE_FLOPPY        0x0102
-#define PCI_CLASS_STORAGE_IPI           0x0103
-#define PCI_CLASS_STORAGE_RAID          0x0104
-#define PCI_CLASS_STORAGE_SATA          0x0106
-#define PCI_CLASS_STORAGE_SATA_AHCI     0x010601
-#define PCI_CLASS_STORAGE_SAS           0x0107
-#define PCI_CLASS_STORAGE_OTHER         0x0180
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-typedef unsigned int     size_t;
-
-/* PCI device information structure; this is used both for scanning and for
- * identification purposes in 'AD_INFO'; based on the Linux pci_device_id
- * structure but hard-wired to use board_* constants for 'driver_data'
- */
-typedef struct {
-  u16   vendor;          /* PCI device vendor/manufacturer */
-  u16   device;          /* PCI device ID inside vendor scope */
-  u16   subvendor;       /* subsystem vendor (unused so far) */
-  u16   subdevice;       /* subsystem device (unused so far) */
-  u32   class;           /* PCI device class */
-  u32   class_mask;      /* bits to match when scanning for 'class' */
-  u32   board;           /* AHCI controller board type (board_* constants) */
-} PCI_ID;
-
-/* IORB queue; since IORB queues are updated at interrupt time, the
- * corresponding pointers (not the data they point to) need to be volatile.
- */
-typedef struct {
-  IORBH _far *volatile root;           /* root of request list */
-  IORBH _far *volatile tail;           /* tail of request list */
-} IORB_QUEUE;
-
-/* port information structure */
-typedef struct {
-  IORB_QUEUE    iorb_queue;            /* IORB queue for this port */
-  unsigned      dev_max   : 4;         /* maximum device number on this port (0-15) */
-  unsigned      cmd_slot  : 4;         /* current command slot index (using round-
-                                        * robin indexes to prevent starvation) */
-
-  volatile u32  ncq_cmds;              /* bitmap for NCQ commands issued */
-  volatile u32  reg_cmds;              /* bitmap for regular commands issued */
-
-  struct {
-    unsigned    allocated : 1;         /* if != 0, device is allocated */
-    unsigned    present   : 1;         /* if != 0, device is present */
-    unsigned    lba48     : 1;         /* if != 0, device supports 48-bit LBA */
-    unsigned    atapi     : 1;         /* if != 0, this is an ATAPI device */
-    unsigned    atapi_16  : 1;         /* if != 0, device suports 16-byte cmds */
-    unsigned    removable : 1;         /* if != 0, device has removable media */
-    unsigned    dev_type  : 5;         /* device type (UIB_TYPE_* in iorb.h) */
-    unsigned    ncq_max   : 5;         /* maximum tag number for queued commands */
-    UNITINFO _far *unit_info;          /* pointer to modified unit info */
-  } devs[15];
-} P_INFO;
-
-/* adapter information structure */
-typedef struct {
-  PCI_ID       *pci;                   /* pointer to corresponding PCI ID */
-
-  unsigned      port_max : 4;          /* maximum port number (0-31) */
-  unsigned      cmd_max : 4;           /* maximum cmd slot number (0-31) */
-  unsigned      port_scan_done : 1;    /* if != 0, port scan already done */
-  unsigned      busy : 1;              /* if != 0, adapter is busy */
-
-  u32           port_map;              /* bitmap of active ports */
-
-  /* initial adapter configuration from BIOS */
-  u32           bios_config[HOST_CAP2 / sizeof(u32) + 1];
-
-  u32           cap;                   /* working copy of CAP register */
-  u32           cap2;                  /* working copy of CAP2 register */
-  u32           flags;                 /* adapter flags */
-
-  HRESOURCE     rm_adh;                /* resource handle for adapter */
-  HRESOURCE     rm_mmio;               /* resource handle for MMIO */
-  HRESOURCE     rm_irq;                /* resource handle for IRQ */
-
-  u8            bus;                   /* PCI bus number */
-  u8            dev_func;              /* PCI device and function number */
-  u16           irq;                   /* interrupt number */
-
-  u32           mmio_phys;             /* physical address of MMIO region */
-  u8      _far *mmio;                  /* pointer to this adapter's MMIO region */
-
-  u32           dma_buf_phys;          /* physical address of DMA scratch buffer */
-  u8      _far *dma_buf[PORT_DMA_BUF_SEGS]; /* DMA scatch buffer */
-
-  P_INFO        ports[AHCI_MAX_PORTS]; /* SATA ports on this adapter */
-} AD_INFO;
-
-/* ADD workspace in IORB (must not exceed 16 bytes) */
-typedef struct {
-  void (*ppfunc)(IORBH _far *iorb);    /* post-processing function */
-  void         *buf;                   /* response buffer (e.g. for identify cmds) */
-  ULONG         timer;                 /* timer for timeout procesing */
-  USHORT        blocks;                /* number of blocks to be transferred */
-  unsigned      processing    : 1;     /* IORB is being processd */
-  unsigned      idempotent    : 1;     /* IORB is idempotent (can be retried) */
-  unsigned      queued_hw     : 1;     /* IORB has been queued to hardware */
-  unsigned      no_ncq        : 1;     /* must not use native command queuing */
-  unsigned      is_ncq        : 1;     /* should use native command queueing */
-  unsigned      complete      : 1;     /* IORB has completed processing */
-  unsigned      cmd_slot      : 4;     /* AHCI command slot for this IORB */
-} ADD_WORKSPACE;
-
-/* -------------------------- function prototypes -------------------------- */
-
-/* init.asm */
-extern u32        readl         (void _far *addr);
-extern u32        writel        (void _far *addr, u32 val);
-extern void _far *memcpy        (void _far *v_dst, void _far *v_src, int len);
-extern void _far *memset        (void _far *p, int ch, size_t len);
-extern void _far  restart_hook  (void);
-extern void _far  reset_hook    (void);
-extern void _far  engine_hook   (void);
-
-/* os2ahci.c */
-extern USHORT  init_drv               (RPINITIN _far *req);
-extern void _far _loadds add_entry    (IORBH _far *iorb);
-extern void    trigger_engine         (void);
-extern int     trigger_engine_1       (void);
-extern void    send_iorb              (IORBH _far *iorb);
-extern void    iocc_configuration     (IORBH _far *iorb);
-extern void    iocc_device_control    (IORBH _far *iorb);
-extern void    iocc_unit_control      (IORBH _far *iorb);
-extern void    iocm_device_table      (IORBH _far *iorb);
-extern void    iocc_geometry          (IORBH _far *iorb);
-extern void    iocc_execute_io        (IORBH _far *iorb);
-extern void    iocc_unit_status       (IORBH _far *iorb);
-extern void    iocc_adapter_passthru  (IORBH _far *iorb);
-extern void    iorb_queue_add         (IORB_QUEUE _far *queue, IORBH _far *iorb);
-extern int     iorb_queue_del         (IORB_QUEUE _far *queue, IORBH _far *iorb);
-extern void    iorb_seterr            (IORBH _far *iorb, USHORT error_code);
-extern void    iorb_done              (IORBH _far *iorb);
-extern void    iorb_requeue           (IORBH _far *iorb);
-
-/* ahci.c */
-extern int     ahci_save_bios_config        (AD_INFO *ai);
-extern int     ahci_restore_bios_config     (AD_INFO *ai);
-extern int     ahci_restore_initial_config  (AD_INFO *ai);
-extern AHCI_PORT_CFG *ahci_save_port_config (AD_INFO *ai, int p);
-extern void    ahci_restore_port_config     (AD_INFO *ai, int p,
-                                             AHCI_PORT_CFG *pc);
-extern int     ahci_enable_ahci             (AD_INFO *ai);
-extern int     ahci_scan_ports              (AD_INFO *ai);
-extern int     ahci_complete_init           (AD_INFO *ai);
-extern int     ahci_reset_port              (AD_INFO *ai, int p, int ei);
-extern int     ahci_start_port              (AD_INFO *ai, int p, int ei);
-extern void    ahci_start_fis_rx            (AD_INFO *ai, int p);
-extern void    ahci_start_engine            (AD_INFO *ai, int p);
-extern int     ahci_stop_port               (AD_INFO *ai, int p);
-extern int     ahci_stop_fis_rx             (AD_INFO *ai, int p);
-extern int     ahci_stop_engine             (AD_INFO *ai, int p);
-extern void    ahci_exec_iorb               (IORBH _far *iorb, int ncq_capable,
-                                             int (*func)(IORBH _far *, int));
-extern void    ahci_exec_polled_iorb        (IORBH _far *iorb,
-                                             int (*func)(IORBH _far *, int),
-                                             ULONG timeout);
-extern int     ahci_exec_polled_cmd         (AD_INFO *ai, int p, int d,
-                                             int timeout, int cmd, ...);
-
-extern int     ahci_intr                    (u16 irq);
-extern void    ahci_port_intr               (AD_INFO *ai, int p);
-extern void    ahci_error_intr              (AD_INFO *ai, int p, u32 irq_stat);
-
-extern void    ahci_get_geometry            (IORBH _far *iorb);
-extern void    ahci_unit_ready              (IORBH _far *iorb);
-extern void    ahci_read                    (IORBH _far *iorb);
-extern void    ahci_verify                  (IORBH _far *iorb);
-extern void    ahci_write                   (IORBH _far *iorb);
-extern void    ahci_execute_cdb             (IORBH _far *iorb);
-extern void    ahci_execute_ata             (IORBH _far *iorb);
-
-/* libc.c */
-extern void       init_com1     (void);
-extern int        vsprintf      (char _far *buf, const char *fmt, va_list va);
-extern int        sprintf       (char _far *buf, const char *fmt, ...);
-extern void       vfprintf      (const char *fmt, va_list va);
-extern void       printf        (const char *fmt, ...);
-extern void       cprintf       (const char *fmt, ...);
-extern void       phex          (const void _far *p, int len,
-                                 const char *fmt, ...);
-extern size_t     strlen        (const char _far *s);
-extern char _far *strcpy        (char _far *dst, const char _far *src);
-extern int        memcmp        (void _far *p1, void _far *p2, size_t len);
-extern long       strtol        (const char _far *buf,
-                                 const char _far * _far *ep, int base);
-extern void      *malloc        (size_t len);
-extern void       free          (void *ptr);
-extern void       mdelay_cal    (void);
-extern void       mdelay        (u32 millies);
-extern void       msleep        (u32 millies);
-extern void       panic         (char *msg);
-extern int        disable       (void);
-extern void       enable        (void);
-
-/* pci.c */
-extern int       add_pci_id     (u16 vendor, u16 device);
-extern void      scan_pci_bus   (void);
-extern int       pci_enable_int (UCHAR bus, UCHAR dev_func);
-extern void      pci_hack_virtualbox(void);
-
-/* ctxhook.c */
-extern void      restart_ctxhook    (ULONG parm);
-extern void      reset_ctxhook      (ULONG parm);
-extern void      engine_ctxhook     (ULONG parm);
-
-/* ---------------------------- global variables --------------------------- */
-
-extern char          end_of_data;   /* label at the end of all data segments */
-extern void    _near end_of_code(); /* label at the end of all code segments */
-
-extern int           debug;         /* if != 0, print debug messages to COM1 */
-extern int           thorough_scan; /* if != 0, perform thorough PCI scan */
-extern int           init_reset;    /* if != 0, reset ports during init */
-
-extern HDRIVER       rm_drvh;       /* resource manager driver handle */
-extern USHORT        add_handle;    /* adapter device driver handle */
-extern UCHAR         timer_pool[];  /* timer pool */
-
-extern PCI_ID        pci_ids[];     /* SATA adapter PCI IDs */
-extern ULONG         drv_lock;      /* driver-level spinlock */
-extern IORB_QUEUE    driver_queue;  /* driver-level IORB queue */
-extern AD_INFO       ad_infos[];    /* adapter information list */
-extern int           ad_info_cnt;   /* number of entries in ad_infos[] */
-extern int           init_complete; /* if != 0, initialization has completed */
-
-/* port restart context hook and input data */
-extern ULONG         restart_ctxhook_h;
-extern volatile u32  ports_to_restart[MAX_AD];
-
-/* port reset context hook and input data */
-extern ULONG         reset_ctxhook_h;
-extern volatile u32  ports_to_reset[MAX_AD];
-extern IORB_QUEUE    abort_queue;
-
-/* trigger engine context hook and input data */
-extern ULONG         engine_ctxhook_h;
-
-/* apapter/port-specific options saved when parsing the command line */
-extern int           link_speed[MAX_AD][AHCI_MAX_PORTS];
-
Index: trunk/src/os2ahci/Makefile
===================================================================
--- trunk/src/os2ahci/Makefile	(revision 13)
+++ trunk/src/os2ahci/Makefile	(revision 13)
@@ -0,0 +1,107 @@
+###############################################################################
+# Makefile - makefile for os2ahci driver
+#
+# Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+# Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+###############################################################################
+# Tool Chain
+
+AS         = ml
+CC         = cl
+LD         = link
+
+AFLAGS     = -c -nologo
+CFLAGS     = -c -nologo -Asnw -W2 -G2ms -Zlpd -Fc -Zi
+LFLAGS     = /noe /nod /packd /a:16 /batch /map /line 
+
+###############################################################################
+# Environment
+
+# main path to OS/2 DDK; this needs to be set before this makefile will work
+DDK          = i:\ddk
+
+# main path to 16 bit C compiler
+CC16         = i:\c600
+
+CC_INCLUDE   = -I$(DDK)\base\h \
+               -I$(DDK)\base\ibmh \
+	       -I$(DDK)\base\src\dev\dasd\diskh \
+               -I$(CC16)\include
+
+AS_INCLUDE   = -I$(DDK)\base\inc \
+               -I$(DDK)\base\src\dev\dasd\diskinc
+
+LIB_DIRS     = $(DDK)\base\lib\ \
+	       $(DDK)\base\src\dev\dasd\devhlp\ \
+               $(CC16)\lib\ \
+
+
+###############################################################################
+# Main dependencies
+
+TARGET   = os2ahci.add
+
+LIBS     = addcalls dhcalls doscalls slibcep rmcalls
+
+SRCS     = init.asm libc.c os2ahci.c pci.c ahci.c ata.c atapi.c ctxhook.c
+
+OBJS     = init.obj libc.obj os2ahci.obj pci.obj ahci.obj ata.obj atapi.obj \
+           ctxhook.obj
+
+INCS     = os2ahci.h ahci.h
+
+
+all: $(TARGET)
+
+clean:
+	rm -f $(OBJS) $(TARGET)
+
+###############################################################################
+# Object/source dependencies
+
+init.obj:     init.asm   Makefile
+
+libc.obj:     libc.c     Makefile $(INCS)
+
+os2ahci.obj:  os2ahci.c  Makefile $(INCS)
+
+pci.obj:      pci.c      Makefile $(INCS)
+
+ahci.obj:     ahci.c     Makefile $(INCS) ata.h atapi.h
+
+ata.obj:      ata.c      Makefile $(INCS) ata.h
+
+atapi.obj:    atapi.c    Makefile $(INCS) ata.h atapi.h
+
+ctxhook.obj:  ctxhook.c  Makefile $(INCS) ata.h atapi.h
+
+###############################################################################
+# Action definitions (compile/link commands)
+
+.asm.obj:
+	$(AS) $(AFLAGS) $(AS_INCLUDE) $*.asm
+
+.c.obj:
+	$(CC) $(CFLAGS) $(CC_INCLUDE) $*.c
+
+$(TARGET): $(OBJS) os2ahci.def Makefile
+	$(LD) $(LFLAGS) $(OBJS),$(TARGET),$*.map,$(LIB_DIRS) $(LIBS),$*.def
+	mapsym os2ahci
+
+
Index: trunk/src/os2ahci/ahci.c
===================================================================
--- trunk/src/os2ahci/ahci.c	(revision 13)
+++ trunk/src/os2ahci/ahci.c	(revision 13)
@@ -0,0 +1,1491 @@
+/******************************************************************************
+ * ahci.c - ahci hardware access functions
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+#include "ata.h"
+#include "atapi.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* produce ata/atapi function pointer with the given func name */
+#define cmd_func(iorb, func)  ad_infos[iorb_unit_adapter(iorb)].     \
+                                 ports[iorb_unit_port(iorb)].        \
+                                  devs[iorb_unit_device(iorb)].atapi \
+                              ? atapi_##func : ata_##func
+
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+static void ahci_setup_device     (AD_INFO *ai, int p, int d, u16 *id_buf);
+static void _far timeout_callback (ULONG timer_handle, ULONG p1, ULONG p2);
+
+/* ------------------------ global/static variables ------------------------ */
+
+/* Initial driver status flags indexed by the board_* constants in os2ahci.h
+ *
+ * NOTE: The Linux AHCI driver uses a combination of board-specific quirk
+ *       flags and overriding certain libata service functions to handle
+ *       adapter flaws. However, there were only three overrides at the time
+ *       os2ahci was written, one for hard adapter resets and two for port
+ *       resets, and we can easily implement those within the corresponding
+ *       reset handlers. If this becomes more complex, this array of flags
+ *       should be converted into a structure array which contains function
+ *       pointers to all handler functions which may need to be overridden.
+ */
+u16 initial_flags[] = {
+  0,                                        /* board_ahci */
+  AHCI_HFLAG_NO_NCQ |                       /* board_ahci_vt8251 */
+    AHCI_HFLAG_NO_PMP,
+  AHCI_HFLAG_IGN_IRQ_IF_ERR,                /* board_ahci_ign_iferr */
+  AHCI_HFLAG_IGN_SERR_INTERNAL |            /* board_ahci_sb600 */
+    AHCI_HFLAG_NO_MSI |
+    AHCI_HFLAG_SECT255 |
+    AHCI_HFLAG_32BIT_ONLY,
+  AHCI_HFLAG_NO_NCQ |                       /* board_ahci_mv */
+    AHCI_HFLAG_NO_MSI |
+    AHCI_HFLAG_MV_PATA |
+    AHCI_HFLAG_NO_PMP,
+  AHCI_HFLAG_IGN_SERR_INTERNAL,             /* board_ahci_sb700 */
+  AHCI_HFLAG_YES_NCQ,                       /* board_ahci_mcp65 */
+  AHCI_HFLAG_NO_PMP,                        /* board_ahci_nopmp */
+  AHCI_HFLAG_YES_NCQ,                       /* board_ahci_yesncq */
+  AHCI_HFLAG_NO_SNTF,                       /* board_ahci_nosntf */
+};
+
+/* IRQ levels for stub interrupt handlers. OS/2 calls interrupt handlers
+ * without passing the IRQ level, yet it expects the interrupt handler to
+ * know the IRQ level for EOI processing. Thus we need multiple interrupt
+ * handlers, one for each IRQ, and some mapping from the interrupt handler
+ * index to the corresponding IRQ.
+ */
+static u16      irq_map[MAX_AD];   /* IRQ level for each stub IRQ func */
+static int      irq_map_cnt;       /* number of IRQ stub funcs used */
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Interrupt handlers. Those are stubs which call the real interrupt handler
+ * with the IRQ level as parameter. This mapping is required because OS/2
+ * calls interrupt handlers without any parameters, yet expects them to know
+ * which IRQ level to complete when calling DevHelp_EOI().
+ *
+ * This array of functions needs to be extended when increasing MAX_AD.
+ */
+#if MAX_AD > 8
+#error must extend irq_handler_xx and irq_handlers[] when increasing MAX_AD
+#endif
+
+/* Macro to call AHCI interrupt handler and set/clear carry flag accordingly.
+ * We need to set the carry flag if the interrupt was not handled. This is
+ * done by shifting the return value of ahci_intr() to the right, implying
+ * bit 0 will be set when the interrupt was not handled.
+ */
+#define call_ahci_intr(i)   return(ahci_intr(irq_map[i]) >> 1)
+
+static USHORT _far irq_handler_00(void)  { call_ahci_intr(0); }
+static USHORT _far irq_handler_01(void)  { call_ahci_intr(1); }
+static USHORT _far irq_handler_02(void)  { call_ahci_intr(2); }
+static USHORT _far irq_handler_03(void)  { call_ahci_intr(3); }
+static USHORT _far irq_handler_04(void)  { call_ahci_intr(4); }
+static USHORT _far irq_handler_05(void)  { call_ahci_intr(5); }
+static USHORT _far irq_handler_06(void)  { call_ahci_intr(6); }
+static USHORT _far irq_handler_07(void)  { call_ahci_intr(7); }
+
+PFN irq_handlers[] = {
+  (PFN) irq_handler_00, (PFN) irq_handler_01, (PFN) irq_handler_02,
+  (PFN) irq_handler_03, (PFN) irq_handler_04, (PFN) irq_handler_05,
+  (PFN) irq_handler_06, (PFN) irq_handler_07
+};
+
+/******************************************************************************
+ * Save BIOS configuration of AHCI adapter. As a side effect, this also saves
+ * generic configuration information which we may have to restore after an
+ * adapter reset.
+ *
+ * NOTE: This function also saves working copies of the CAP and CAP2 registers
+ *       as well as the initial port map in the AD_INFO structure after
+ *       removing features which are known to cause trouble on this specific
+ *       piece of hardware.
+ */
+int ahci_save_bios_config(AD_INFO *ai)
+{
+  int ports;
+  int i;
+
+  /* save BIOS configuration */
+  for (i = 0; i < HOST_CAP2; i += sizeof(u32)) {
+    ai->bios_config[i / sizeof(u32)] = readl(ai->mmio + i);
+  }
+
+  /* HOST_CAP2 only exists for AHCI V1.2 and later */
+  if (ai->bios_config[HOST_VERSION / sizeof(u32)] >= 0x00010200L) {
+    ai->bios_config[HOST_CAP2 / sizeof(u32)] = readl(ai->mmio + HOST_CAP2);
+  } else {
+    ai->bios_config[HOST_CAP2 / sizeof(u32)] = 0;
+  }
+
+  /* print AHCI register debug information */
+  if (debug) {
+    printf("AHCI global controller registers:\n");
+    for (i = 0; i <= HOST_CAP2 / sizeof(u32); i++) {
+      u32 val = ai->bios_config[i];
+      printf(" %02x: %08lx", i, val);
+
+      if (i == HOST_CAP) {
+        printf(" -");
+        if (val & HOST_CAP_64)         printf(" 64bit");
+        if (val & HOST_CAP_NCQ)        printf(" ncq");
+        if (val & HOST_CAP_SNTF)       printf(" sntf");
+        if (val & HOST_CAP_MPS)        printf(" mps");
+        if (val & HOST_CAP_SSS)        printf(" sss");
+        if (val & HOST_CAP_ALPM)       printf(" alpm");
+        if (val & HOST_CAP_LED)        printf(" led");
+        if (val & HOST_CAP_CLO)        printf(" clo");
+        if (val & HOST_CAP_ONLY)       printf(" ahci_only");
+        if (val & HOST_CAP_PMP)        printf(" pmp");
+        if (val & HOST_CAP_FBS)        printf(" fbs");
+        if (val & HOST_CAP_PIO_MULTI)  printf(" pio_multi");
+        if (val & HOST_CAP_SSC)        printf(" ssc");
+        if (val & HOST_CAP_PART)       printf(" part");
+        if (val & HOST_CAP_CCC)        printf(" ccc");
+        if (val & HOST_CAP_EMS)        printf(" ems");
+        if (val & HOST_CAP_SXS)        printf(" sxs");
+        printf(" cmd_slots:%d", (u16) ((val >> 8) & 0x1f) + 1);
+        printf(" ports:%d",     (u16) (val & 0x1f) + 1);
+
+      } else if (i == HOST_CTL) {
+        printf(" -");
+        if (val & HOST_AHCI_EN)        printf(" ahci_enabled");
+        if (val & HOST_IRQ_EN)         printf(" irq_enabled");
+        if (val & HOST_RESET)          printf(" resetting");
+
+      } else if (i == HOST_CAP2) {
+        printf(" -");
+        if (val & HOST_CAP2_BOH)       printf(" boh");
+        if (val & HOST_CAP2_NVMHCI)    printf(" nvmhci");
+        if (val & HOST_CAP2_APST)      printf(" apst");
+      
+      }
+      printf("\n");
+    }
+  }
+
+  /* Save working copies of CAP, CAP2 and port_map and remove broken feature
+   * bits. This is largely copied from the Linux AHCI driver -- the wisdom
+   * around quirks and faulty hardware is hard to come by...
+   */
+  ai->cap      = ai->bios_config[HOST_CAP        / sizeof(u32)];
+  ai->cap2     = ai->bios_config[HOST_CAP2       / sizeof(u32)];
+  ai->port_map = ai->bios_config[HOST_PORTS_IMPL / sizeof(u32)];
+
+  if (ai->pci->board >= sizeof(initial_flags) / sizeof(*initial_flags)) {
+    dprintf("error: invalid board index in PCI info\n");
+    return(-1);
+  }
+  ai->flags = initial_flags[ai->pci->board];
+
+  if ((ai->cap & HOST_CAP_64) && (ai->flags & AHCI_HFLAG_32BIT_ONLY)) {
+    /* disable 64-bit support for faulty controllers; OS/2 can't do 64 bits at
+     * this point, of course, but who knows where all this will be in a few
+     * years...
+     */
+    ai->cap &= ~HOST_CAP_64;
+  }
+
+  if ((ai->cap & HOST_CAP_NCQ) && (ai->flags & AHCI_HFLAG_NO_NCQ)) {
+    dprintf("controller can't do NCQ, turning off CAP_NCQ\n");
+    ai->cap &= ~HOST_CAP_NCQ;
+  }
+
+  if (!(ai->cap & HOST_CAP_NCQ) && (ai->flags & AHCI_HFLAG_YES_NCQ)) {
+    dprintf("controller can do NCQ, turning on CAP_NCQ\n");
+    ai->cap |= HOST_CAP_NCQ;
+  }
+
+  if ((ai->cap & HOST_CAP_PMP) && (ai->flags & AHCI_HFLAG_NO_PMP)) {
+    dprintf("controller can't do PMP, turning off CAP_PMP\n");
+    ai->cap |= HOST_CAP_PMP;
+  }
+
+  if ((ai->cap & HOST_CAP_SNTF) && (ai->flags & AHCI_HFLAG_NO_SNTF)) {
+    dprintf("controller can't do SNTF, turning off CAP_SNTF\n");
+    ai->cap &= ~HOST_CAP_SNTF;
+  }
+
+  if (ai->pci->vendor == PCI_VENDOR_ID_JMICRON &&
+      ai->pci->device == 0x2361 && ai->port_map != 1) {
+    dprintf("JMB361 has only one port, port_map 0x%x -> 0x%x\n", ai->port_map, 1);
+    ai->port_map = 1;
+  }
+
+  /* Correlate port map to number of ports reported in HOST_CAP
+   *
+   * NOTE: Port map and number of ports handling differs a bit from the
+   *       Linux AHCI driver because we're storing both in AI_INFO. As in the
+   *       Linux driver, the port map is the main driver for port scanning but
+   *       we're also saving a maximum port number in AI_INFO to reduce the
+   *       number of IORB queues to look at in trigger_engine(). This is done
+   *       in ahci_scan_ports().
+   */
+  ports = (ai->cap & 0x1f) + 1;
+  for (i = 0; i < AHCI_MAX_PORTS; i++) {
+    if (ai->port_map & (1UL << i)) {
+      ports--;
+    }
+  }
+  if (ports < 0) {
+    /* more ports in port_map than in HOST_CAP & 0x1f */
+    ports = (ai->cap & 0x1f) + 1;
+    dprintf("implemented port map (0x%lx) contains more "
+            "ports than nr_ports (%d), using nr_ports\n",
+            ai->port_map, ports);
+    ai->port_map = (1UL << ports) - 1UL;
+  }
+
+  /* set maximum command slot number */
+  ai->cmd_max = (u16) ((ai->cap >> 8) & 0x1f);
+
+  return(0);
+}
+
+/******************************************************************************
+ * Restore BIOS configuration of AHCI adapter. This is needed after scanning
+ * for devices because we still need the BIOS until the initial boot sequence
+ * has completed.
+ */
+int ahci_restore_bios_config(AD_INFO *ai)
+{
+  ddprintf("restoring AHCI BIOS configuration\n");
+
+  /* restore saved BIOS configuration */
+  writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
+  writel(ai->mmio + HOST_CCC,       ai->bios_config[HOST_CCC / sizeof(u32)]);
+  writel(ai->mmio + HOST_CCC_PORTS, ai->bios_config[HOST_CCC_PORTS / sizeof(u32)]);
+  writel(ai->mmio + HOST_EM_CTL,    ai->bios_config[HOST_EM_CTL / sizeof(u32)]);
+
+  /* flush PCI MMIO delayed write buffers */
+  readl(ai->mmio + HOST_EM_CTL);
+
+  return(0);
+}
+
+/******************************************************************************
+ * Restore initial configuration (e.g. after an adapter reset). This relies
+ * on information saved by 'ahci_save_bios_config()'.
+ */
+int ahci_restore_initial_config(AD_INFO *ai)
+{
+  ddprintf("restoring initial configuration\n");
+
+  /* restore saved BIOS configuration */
+  writel(ai->mmio + HOST_CTL,       ai->bios_config[HOST_CTL / sizeof(u32)]);
+  writel(ai->mmio + HOST_CCC,       ai->bios_config[HOST_CCC / sizeof(u32)]);
+  writel(ai->mmio + HOST_CCC_PORTS, ai->bios_config[HOST_CCC_PORTS / sizeof(u32)]);
+  writel(ai->mmio + HOST_EM_CTL,    ai->bios_config[HOST_EM_CTL / sizeof(u32)]);
+
+  /* flush PCI MMIO delayed write buffers */
+  readl(ai->mmio + HOST_EM_CTL);
+
+  /* (re-)enable AHCI mode */
+  ahci_enable_ahci(ai);
+
+  return(0);
+}
+
+/******************************************************************************
+ * Save port configuration. This is primarily used to save the BIOS port
+ * configuration (command list and FIS buffers and the IRQ mask).
+ *
+ * The port configuration returned by this function is dynamically allocated
+ * and automatically freed when calling ahci_restore_port_config().
+ */
+AHCI_PORT_CFG *ahci_save_port_config(AD_INFO *ai, int p)
+{
+  AHCI_PORT_CFG *pc;
+  u8 _far *port_mmio = port_base(ai, p);
+
+  if ((pc = malloc(sizeof(*pc))) == NULL) {
+    return(NULL);
+  }
+
+  pc->cmd_list   = readl(port_mmio + PORT_LST_ADDR);
+  pc->cmd_list_h = readl(port_mmio + PORT_LST_ADDR_HI);
+  pc->fis_rx     = readl(port_mmio + PORT_FIS_ADDR);
+  pc->fis_rx_h   = readl(port_mmio + PORT_FIS_ADDR_HI);
+  pc->irq_mask   = readl(port_mmio + PORT_IRQ_MASK);
+
+  return(pc);
+}
+
+/******************************************************************************
+ * Restore port configuration. This is primarily used to restore the BIOS port
+ * configuration (command list and FIS buffers and the IRQ mask).
+ *
+ * The port configuration automatically freed.
+ */
+void ahci_restore_port_config(AD_INFO *ai, int p, AHCI_PORT_CFG *pc)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+
+  writel(port_mmio + PORT_LST_ADDR,    pc->cmd_list);
+  writel(port_mmio + PORT_LST_ADDR_HI, pc->cmd_list_h);
+  writel(port_mmio + PORT_FIS_ADDR,    pc->fis_rx);
+  writel(port_mmio + PORT_FIS_ADDR_HI, pc->fis_rx_h);
+  writel(port_mmio + PORT_IRQ_MASK,    pc->irq_mask);
+
+  readl(port_base(ai, p) + PORT_IRQ_MASK); /* flush */
+
+  free(pc);
+}
+
+/******************************************************************************
+ * Enable AHCI mode on this controller.
+ */
+int ahci_enable_ahci(AD_INFO *ai)
+{
+  u32 ctl = readl(ai->mmio + HOST_CTL);
+  int i;
+
+  if (ctl & HOST_AHCI_EN) {
+    /* AHCI mode already enbled */
+    return(0);
+  }
+
+  /* some controllers need AHCI_EN to be written multiple times */
+  for (i = 0; i < 5; i++) {
+    ctl |= HOST_AHCI_EN;
+    writel(ai->mmio + HOST_CTL, ctl);
+    ctl = readl(ai->mmio + HOST_CTL);   /* flush && sanity check */
+    if (ctl & HOST_AHCI_EN) {
+      return(0);
+    }
+    mdelay(10);
+  }
+
+  /* couldn't enable AHCI mode */
+  dprintf("failed to enable AHCI mode on adapter #%d\n", ad_no(ai));
+  return(1);
+}
+
+/******************************************************************************
+ * Scan all ports for connected devices and fill in the corresponding device
+ * information.
+ *
+ * NOTES:
+ *
+ *  - The adapter is temporarily configured for os2ahci but the original BIOS
+ *    configuration will be restored when done. This happens only until we
+ *    have received the IOCC_COMPLETE_INIT command.
+ *
+ *  - Subsequent calls are currently not planned but may be required for
+ *    suspend/resume handling, hot swap functionality, etc.
+ *
+ *  - This function is expected to be called with the spinlock released but
+ *    the corresponding adapter's busy flag set. It will aquire the spinlock
+ *    temporarily to allocate/free memory for the ATA identify buffer.
+ */
+int ahci_scan_ports(AD_INFO *ai)
+{
+  AHCI_PORT_CFG *pc = NULL;
+  u16 *id_buf;
+  int rc;
+  int p;
+
+  spin_lock(drv_lock);
+  id_buf = malloc(ATA_ID_WORDS * sizeof(u16));
+  spin_unlock(drv_lock);
+  if (id_buf == NULL) {
+    return(-1);
+  }
+
+  if (ai->bios_config[0] == 0) {
+    /* first call */
+    ahci_save_bios_config(ai);
+  }
+
+  if (ahci_enable_ahci(ai)) {
+    goto exit_port_scan;
+  }
+  
+  /* perform port scan */
+  dprintf("scanning ports on adapter #%d\n", ad_no(ai));
+  for (p = 0; p < AHCI_MAX_PORTS; p++) {
+    if (ai->port_map & (1UL << p)) {
+
+      if (!init_complete) {
+        if ((pc = ahci_save_port_config(ai, p)) == NULL) {
+          goto exit_port_scan;
+        }
+      }
+
+      /* start/reset port; if no device is attached, this is expected to fail */
+      if (init_reset) {
+        ddprintf("resetting port #%d\n", p);
+        rc = ahci_reset_port(ai, p, 0);
+      } else {
+        ddprintf("(re)starting port #%d\n", p);
+        ahci_stop_port(ai, p);
+        rc = ahci_start_port(ai, p, 0);
+      }
+      if (rc) {
+        /* no device attached to this port */
+        ai->port_map &= ~(1UL << p);
+        goto restore_port_config;
+      }
+
+      /* this port has a device attached and is ready to accept commands */
+      ddprintf("port #%d seems to be attached to a device; probing...\n", p);
+      rc = ahci_exec_polled_cmd(ai, p, 0, 500, ATA_CMD_ID_ATA, 
+                                AP_VADDR, (void _far *) id_buf, 512,
+                                AP_END);
+
+      if (rc != 0 || id_buf[ATA_ID_CONFIG] & (1U << 15)) {
+        /* this might be an ATAPI device; run IDENTIFY_PACKET_DEVICE */
+        rc = ahci_exec_polled_cmd(ai, p, 0, 500, ATA_CMD_ID_ATAPI,
+                                  AP_VADDR, (void _far *) id_buf, 512,
+                                  AP_END);
+      }
+
+      if (rc == 0) {
+        /* we have a valid IDENTIFY or IDENTIFY_PACKET response */
+        ddphex(id_buf, 512, "ATA_IDENTIFY(_PACKET) results:\n");
+        ahci_setup_device(ai, p, 0, id_buf);
+      } else {
+        /* no device attached to this port */
+        ai->port_map &= ~(1UL << p);
+      }
+
+    restore_port_config:
+      if (pc != NULL) {
+        ahci_restore_port_config(ai, p, pc);
+      }
+    }
+  }
+
+exit_port_scan:
+  if (!init_complete) {
+    ahci_restore_bios_config(ai);
+  }
+  spin_lock(drv_lock);
+  free(id_buf);
+  spin_unlock(drv_lock);
+  return(0);
+}
+
+/******************************************************************************
+ * Complete initialization of adapter. This includes restarting all active
+ * ports and initializing interrupt processing. This is called when receiving
+ * the IOCM_COMPLETE_INIT request.
+ */
+int ahci_complete_init(AD_INFO *ai)
+{
+  int rc;
+  int p;
+  int i;
+
+  dprintf("completing initialization of adapter #%d\n", ad_no(ai));
+
+  /* register IRQ handlers; each IRQ level is registered only once */
+  for (i = 0; i < irq_map_cnt; i++) {
+    if (irq_map[i] == ai->irq) {
+      /* we already have this IRQ registered */
+      break;
+    }
+  }
+
+  if (i >= irq_map_cnt) {
+    dprintf("registering interrupt #%d\n", ai->irq);
+
+    if (DevHelp_SetIRQ(mk_NPFN(irq_handlers[irq_map_cnt]), ai->irq, 1) != 0) {
+      dprintf("failed to register shared interrupt\n");
+
+      if (DevHelp_SetIRQ(mk_NPFN(irq_handlers[irq_map_cnt]), ai->irq, 0) != 0) {
+        dprintf("failed to register exclusive interrupt\n");
+        return(-1);
+      }
+    }
+    irq_map[irq_map_cnt++] = ai->irq;
+  }
+
+  /* enable AHCI mode */
+  if ((rc = ahci_enable_ahci(ai)) != 0) {
+    return(rc);
+  }
+
+  /* Start all ports. The main purpose is to set the command list and FIS
+   * receive area addresses properly and to enable port-level interrupts; we
+   * don't really care about the return status because we'll find out soon
+   * enough if a previously detected device has problems.
+   */
+  for (p = 0; p < AHCI_MAX_PORTS; p++) {
+    if (ai->port_map & (1UL << p)) {
+      dprintf("restarting port #%d\n", p);
+      ahci_stop_port(ai, p);
+      ahci_start_port(ai, p, 1);
+    }
+  }
+
+  /* enable adapter-level interrupts */
+  writel(ai->mmio + HOST_CTL, HOST_IRQ_EN);
+  readl(ai->mmio + HOST_CTL); /* flush */
+
+  /* enable interrupts on PCI-level (PCI 2.3 added a feature to disable ints) */
+  pci_enable_int(ai->bus, ai->dev_func);
+
+  return(0);
+}
+
+/******************************************************************************
+ * Reset specified port. This function is typically called during adapter
+ * initialization and first gets the port into a defined status, then resets
+ * the port by sending a COMRESET signal.
+ *
+ * This function is also the location of the link speed initialization (link
+ * needs to be restablished after changing link speed, anyway).
+ *
+ * NOTE: This function uses a busy loop to wait for DMA engines to stop and
+ *       the COMRESET to complete. It should only be called at task time
+ *       during initialization or in a context hook.
+ */
+int ahci_reset_port(AD_INFO *ai, int p, int ei)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  u32 tmp;
+  int timeout = 500;
+
+  dprintf("resetting port %d.%d\n", ad_no(ai), p);
+
+  /* stop port engines (we don't care whether there is an error doing so) */
+  ahci_stop_port(ai, p);
+
+  /* clear SError */
+  tmp = readl(port_mmio + PORT_SCR_ERR);
+  ddprintf(" PORT_SCR_ERR  = 0x%lx\n", tmp);
+  writel(port_mmio + PORT_SCR_ERR, tmp);
+
+  /* clear pending port IRQs */
+  tmp = readl(port_mmio + PORT_IRQ_STAT);
+  if (tmp) {
+    writel(port_mmio + PORT_IRQ_STAT, tmp);
+  }
+  ddprintf(" PORT_IRQ_STAT = 0x%lx\n", tmp);
+  ddprintf(" PORT_IRQ_MASK = 0x%lx\n", readl(port_mmio + PORT_IRQ_MASK));
+  ddprintf(" HOST_IRQ_STAT = 0x%lx\n", readl(ai->mmio + HOST_IRQ_STAT));
+  writel(ai->mmio + HOST_IRQ_STAT, 1UL << p);
+
+  /* set link speed */
+  tmp = readl(port_mmio + PORT_SCR_CTL) & ~0x000000f0UL;
+  writel(port_mmio + PORT_SCR_CTL, tmp | (link_speed[ad_no(ai)][p] << 4));
+
+  /* issue COMRESET on the port */
+  tmp = readl(port_mmio + PORT_SCR_CTL) & ~0x0000000fUL;
+  writel(port_mmio + PORT_SCR_CTL, tmp | 1);
+  readl(port_mmio + PORT_SCR_CTL);  /* flush */
+
+  /* spec says "leave reset bit on for at least 1ms"; make it 2ms */
+  mdelay(2);
+
+  writel(port_mmio + PORT_SCR_CTL, tmp);
+  readl(port_mmio + PORT_SCR_CTL);  /* flush */
+
+  /* wait for communication to be re-established after port reset */
+  while (((tmp = readl(port_mmio + PORT_SCR_STAT) & 3)) != 3) {
+    mdelay(10);
+    timeout -= 10;
+    if (timeout <= 0) {
+      dprintf("no device present after resetting port #%d "
+              "(PORT_SCR_STAT = 0x%lx)\n", p, tmp);
+      return(-1);
+    }
+  }
+
+  /* clear SError again (recommended by AHCI spec) */
+  tmp = readl(port_mmio + PORT_SCR_ERR);
+  writel(port_mmio + PORT_SCR_ERR, tmp);
+
+  /* start port so we can receive the COMRESET FIS */
+  ahci_start_port(ai, p, ei);
+
+  /* wait for device to be ready ((PxTFD & (BSY | DRQ | ERR)) == 0) */
+  while (((tmp = readl(port_mmio + PORT_TFDATA)) & 0x89) != 0) {
+    mdelay(10);
+    timeout -= 10;
+    if (timeout <= 0) {
+      dprintf("device not ready on port #%d "
+              "(PORT_TFDATA = 0x%lx)\n", p, tmp);
+      ahci_stop_port(ai, p);
+      return(-1);
+    }
+  }
+  ddprintf(" PORT_TFDATA   = 0x%lx\n", readl(port_mmio + PORT_TFDATA));
+
+  return(0);
+}
+
+/******************************************************************************
+ * Start specified port.
+ */
+int ahci_start_port(AD_INFO *ai, int p, int ei)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  u32 status;
+
+  /* check whether device presence is detected and link established */
+  status = readl(port_mmio + PORT_SCR_STAT);
+  ddprintf(" PORT_SCR_STAT = 0x%lx\n", status);
+  if ((status & 0xf) != 3) {
+    return(-1);
+  }
+
+  /* enable FIS reception */
+  ahci_start_fis_rx(ai, p);
+
+  /* enable DMA */
+  ahci_start_engine(ai, p);
+
+  if (ei) {
+    /* enable port interrupts */
+    writel(port_mmio + PORT_IRQ_MASK, PORT_IRQ_TF_ERR |
+                                      PORT_IRQ_HBUS_ERR |
+                                      PORT_IRQ_HBUS_DATA_ERR |
+                                      PORT_IRQ_IF_ERR |
+                                      PORT_IRQ_OVERFLOW |
+                                      PORT_IRQ_BAD_PMP |
+                                      PORT_IRQ_UNK_FIS |
+                                      PORT_IRQ_SDB_FIS |
+                                      PORT_IRQ_D2H_REG_FIS);
+  } else {
+    writel(port_mmio + PORT_IRQ_MASK, 0);
+  }
+  readl(port_mmio + PORT_IRQ_MASK);  /* flush */
+
+  return(0);
+}
+
+/******************************************************************************
+ * Start port FIS reception. Copied from Linux AHCI driver and adopted to
+ * OS2AHCI.
+ */
+void ahci_start_fis_rx(AD_INFO *ai, int p)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  u32 port_dma = port_dma_base_phys(ai, p);
+  u32 tmp;
+
+  /* set comand header and FIS address registers */
+  writel(port_mmio + PORT_LST_ADDR, port_dma + offsetof(AHCI_PORT_DMA, cmd_hdr));
+  writel(port_mmio + PORT_LST_ADDR_HI, 0);
+  writel(port_mmio + PORT_FIS_ADDR, port_dma + offsetof(AHCI_PORT_DMA, rx_fis));
+  writel(port_mmio + PORT_FIS_ADDR_HI, 0);
+
+  /* enable FIS reception */
+  tmp = readl(port_mmio + PORT_CMD);
+  tmp |= PORT_CMD_FIS_RX;
+  writel(port_mmio + PORT_CMD, tmp);
+
+  /* flush */
+  readl(port_mmio + PORT_CMD);
+}
+
+/******************************************************************************
+ * Start port HW engine. Copied from Linux AHCI driver and adopted to OS2AHCI.
+ */
+void ahci_start_engine(AD_INFO *ai, int p)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  u32 tmp;
+
+  /* start DMA */
+  tmp = readl(port_mmio + PORT_CMD);
+  tmp |= PORT_CMD_START;
+  writel(port_mmio + PORT_CMD, tmp);
+  readl(port_mmio + PORT_CMD); /* flush */
+}
+
+/******************************************************************************
+ * Stop specified port
+ */
+int ahci_stop_port(AD_INFO *ai, int p)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  int rc;
+
+  /* disable FIS reception */
+  if ((rc = ahci_stop_fis_rx(ai, p)) != 0) {
+    dprintf("error: failed to stop FIS receive (%d)\n", rc);
+    return(rc);
+  }
+
+  /* disable DMA */
+  if ((rc = ahci_stop_engine(ai, p)) != 0) {
+    dprintf("error: failed to stop port HW engine (%d)\n", rc);
+    return(rc);
+  }
+
+  /* reset PxSACT register (tagged command queues, not reset by COMRESET) */
+  writel(port_mmio + PORT_SCR_ACT, 0);
+  readl(port_mmio + PORT_SCR_ACT);  /* flush */
+
+  return(0);
+}
+
+/******************************************************************************
+ * Stop port FIS reception. Copied from Linux AHCI driver and adopted to
+ * OS2AHCI.
+ *
+ * NOTE: This function uses a busy loop to wait for the DMA engine to stop. It
+ *       should only be called at task time during initialization or in a
+ *       context hook (e.g. when resetting a port).
+ */
+int ahci_stop_fis_rx(AD_INFO *ai, int p)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  int timeout = 1000;
+  u32 tmp;
+
+  /* disable FIS reception */
+  tmp = readl(port_mmio + PORT_CMD);
+  tmp &= ~PORT_CMD_FIS_RX;
+  writel(port_mmio + PORT_CMD, tmp);
+
+  /* wait for completion, spec says 500ms, give it 1000 */
+  while (timeout > 0 && (readl(port_mmio + PORT_CMD) & PORT_CMD_FIS_ON)) {
+    mdelay(10);
+    timeout -= 10;
+  }
+
+  return((timeout <= 0) ? -1 : 0);
+}
+
+/******************************************************************************
+ * Stop port HW engine. Copied from Linux AHCI driver and adopted to OS2AHCI.
+ *
+ * NOTE: This function uses a busy loop to wait for the DMA engine to stop. It
+ *       should only be called at task time during initialization or in a
+ *       context hook (e.g. when resetting a port).
+ */
+int ahci_stop_engine(AD_INFO *ai, int p)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  int timeout = 500;
+  u32 tmp;
+
+  tmp = readl(port_mmio + PORT_CMD);
+
+  /* check if the port is already stopped */
+  if ((tmp & (PORT_CMD_START | PORT_CMD_LIST_ON)) == 0) {
+    return 0;
+  }
+
+  /* set port to idle */
+  tmp &= ~PORT_CMD_START;
+  writel(port_mmio + PORT_CMD, tmp);
+
+  /* wait for engine to stop. This could be as long as 500 msec */
+  while (timeout > 0 && (readl(port_mmio + PORT_CMD) & PORT_CMD_LIST_ON)) {
+    mdelay(10);
+    timeout -= 10;
+  }
+
+  return((timeout <= 0) ? -1 : 0);
+}
+
+/******************************************************************************
+ * Execute AHCI command for given IORB. This includes all steps typically
+ * required by any of the ahci_*() IORB processing functions.
+ *
+ * NOTE: In order to prevent race conditions with port restart and reset
+ *       handlers, we either need to keep the spinlock during the whole
+ *       operation or set the adapter's busy flag. Since the expectation
+ *       is that command preparation will be quick (it certainly doesn't
+ *       involve delays), we're going with the spinlock for the time being.
+ */
+void ahci_exec_iorb(IORBH _far *iorb, int ncq_capable,
+                    int (*func)(IORBH _far *, int))
+{
+  volatile u32 *cmds;
+  ADD_WORKSPACE _far *aws = add_workspace(iorb);
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  P_INFO *port = ai->ports + iorb_unit_port(iorb);
+  ULONG timeout = (iorb->Timeout > 0) ? iorb->Timeout : DEFAULT_TIMEOUT;
+  u8 _far *port_mmio = port_base(ai, iorb_unit_port(iorb));
+  u16 cmd_max = ai->cmd_max;
+  int i;
+
+  /* determine whether this will be an NCQ request */
+  aws->is_ncq = 0;
+  if (ncq_capable && port->devs[iorb_unit_device(iorb)].ncq_max > 1 &&
+      (ai->cap & HOST_CAP_NCQ) && !aws->no_ncq && init_complete) {
+
+    /* We can make this an NCQ request; limit command slots to the maximum
+     * NCQ tag number reported by the device - 1. Why "minus one"? I seem to
+     * recall an issue related to using all 32 tag numbers but can't quite
+     * pinpoint it right now. One less won't make much of a difference...
+     */
+    aws->is_ncq = 1;
+    if ((cmd_max = port->devs[iorb_unit_device(iorb)].ncq_max - 1) > ai->cmd_max) {
+      cmd_max = ai->cmd_max;
+    }
+    ddprintf("NCQ command; cmd_max = %d->%d\n", (u16) ai->cmd_max, cmd_max);
+  }
+
+  /* make sure adapter is available */
+  spin_lock(drv_lock);
+  if (!ai->busy) {
+
+    if (!init_complete) {
+      ai->busy = 1;
+      spin_unlock(drv_lock);
+      ahci_exec_polled_iorb(iorb, func, timeout);
+      ai->busy = 0;
+      return;
+    }
+
+    /* make sure we don't mix NCQ and regular commands */
+    if (aws->is_ncq && port->reg_cmds == 0 || !aws->is_ncq && port->ncq_cmds == 0) {
+
+      /* Find next available command slot. We use a simple round-robin 
+       * algorithm for this to prevent commands with higher slot indexes
+       * from stalling when new commands are coming in frequently.
+       */
+      cmds = (aws->is_ncq) ? &port->ncq_cmds : &port->reg_cmds;
+      for (i = 0; i <= cmd_max; i++) {
+        if (++(port->cmd_slot) > cmd_max) {
+          port->cmd_slot = 0;
+        }
+        if ((*cmds & (1UL << port->cmd_slot)) == 0) {
+          break;
+        }
+      }
+
+      if ((*cmds & (1UL << port->cmd_slot)) == 0) {
+        /* prepare command */
+        if (func(iorb, port->cmd_slot)) {
+          /* Command preparation failed, or no HW command required; IORB
+           * will already have the error code if there was an error.
+           */
+          spin_unlock(drv_lock);
+          iorb_done(iorb);
+          return;
+        }
+
+        /* start timer for this IORB */
+        ADD_StartTimerMS(&aws->timer, timeout, (PFN) timeout_callback, iorb, 0);
+
+        /* update IORB */
+        aws->queued_hw = 1;
+        aws->cmd_slot = port->cmd_slot;
+
+        /* issue command to hardware */
+        ddprintf("issuing command on slot %d\n", port->cmd_slot);
+        *cmds |= (1UL << port->cmd_slot);
+        if (aws->is_ncq) {
+          writel(port_mmio + PORT_SCR_ACT, (1UL << port->cmd_slot));
+          readl(port_mmio + PORT_SCR_ACT); /* flush */
+        }
+        writel(port_mmio + PORT_CMD_ISSUE, (1UL << port->cmd_slot));
+        readl(port_mmio + PORT_CMD_ISSUE); /* flush */
+
+        spin_unlock(drv_lock);
+        return;
+      }
+    }
+  }
+
+  /* requeue this IORB; it will be picked up again in trigger_engine() */
+  aws->processing = 0;
+  spin_unlock(drv_lock);
+}
+
+/******************************************************************************
+ * Execute polled IORB command. This function is called by ahci_exec_iorb()
+ * when the initialization has not yet completed. The reasons for polling until
+ * initialization has completed are:
+ *
+ *  - We need to restore the BIOS configuration after we're done with this
+ *    command because someone might still call int 13h routines; sending
+ *    asynchronous commands and waiting for interrupts to indicate completion
+ *    won't work in such a scenario.
+ *  - Our context hooks won't work while the device managers are initializing
+ *    (they can't yield at init time).
+ *  - The device managers typically poll for command completion during
+ *    initialization so it won't make much of a difference, anyway.
+ *
+ * NOTE: This function must be called with the adapter-level busy flag set but
+ *       without the driver-level spinlock held.
+ */
+void ahci_exec_polled_iorb(IORBH _far *iorb, int (*func)(IORBH _far *, int),
+                           ULONG timeout)
+{
+  AHCI_PORT_CFG *pc = NULL;
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+  u8 _far *port_mmio = port_base(ai, p);
+
+  /* enable AHCI mode */
+  if (ahci_enable_ahci(ai) != 0) {
+    iorb_seterr(iorb, IOERR_ADAPTER_NONSPECIFIC);
+    goto restore_bios_config;
+  }
+
+  /* check whether command slot 0 is available */
+  if ((readl(port_mmio + PORT_CMD_ISSUE) & 1) != 0) {
+    iorb_seterr(iorb, IOERR_DEVICE_BUSY);
+    goto restore_bios_config;
+  }
+  
+  /* save port configuration */
+  if ((pc = ahci_save_port_config(ai, p)) == NULL) {
+    iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
+    goto restore_bios_config;
+  }
+
+  /* restart port (includes the necessary port configuration) */
+  if (ahci_stop_port(ai, p) || ahci_start_port(ai, p, 0)) {
+    iorb_seterr(iorb, IOERR_ADAPTER_NONSPECIFIC);
+    goto restore_bios_config;
+  }
+
+  /* prepare command */
+  if (func(iorb, 0) == 0) {
+    /* successfully prepared cmd; issue cmd and wait for completion */
+    ddprintf("executing polled cmd...");
+    writel(port_mmio + PORT_CMD_ISSUE, 1);
+    timeout /= 10;
+    while (timeout > 0 && (readl(port_mmio + PORT_CMD_ISSUE) & 1)) {
+      mdelay(10);
+      timeout--;
+    }
+    ddprintf(" done (time left = %ld)\n", timeout * 10);
+
+    if (timeout == 0) {
+      dprintf("timeout for IORB %Fp\n", iorb);
+      iorb_seterr(iorb, IOERR_ADAPTER_TIMEOUT);
+
+    } else if (readl(port_mmio + PORT_SCR_ERR) != 0 ||
+               readl(port_mmio + PORT_TFDATA) & 0x89) {
+      dprintf("polled cmd error for IORB %Fp\n", iorb);
+      iorb_seterr(iorb, IOERR_DEVICE_NONSPECIFIC);
+      ahci_reset_port(ai, iorb_unit_port(iorb), 0);
+
+    } else {
+      /* successfully executed command */
+      if (add_workspace(iorb)->ppfunc != NULL) {
+        add_workspace(iorb)->ppfunc(iorb);
+      } else {
+        add_workspace(iorb)->complete = 1;
+      }
+    }
+  }
+
+restore_bios_config:
+  /* restore BIOS configuration */
+  if (pc != NULL) {
+    ahci_restore_port_config(ai, p, pc);
+  }
+  ahci_restore_bios_config(ai);
+
+  if (add_workspace(iorb)->complete | (iorb->Status | IORB_ERROR)) {
+    aws_free(add_workspace(iorb));
+    iorb_done(iorb);
+  }
+  return;
+}
+
+/******************************************************************************
+ * Execute polled ATA/ATAPI command. This function will block until the command
+ * has completed or the timeout has expired, thus it should only be used during
+ * initialization. Furthermore, it will always use command slot zero.
+ *
+ * The difference to ahci_exec_polled_iorb() is that this function executes
+ * arbitrary ATA/ATAPI commands outside the context of an IORB. It's typically
+ * used when scanning for devices during initialization.
+ */
+int ahci_exec_polled_cmd(AD_INFO *ai, int p, int d, int timeout, int cmd, ...)
+{
+  va_list va;
+  u8 _far *port_mmio = port_base(ai, p);
+  u32 tmp;
+  int rc;
+
+  /* verify that command slot 0 is idle */
+  if (readl(port_mmio + PORT_CMD_ISSUE) & 1) {
+    ddprintf("port %d slot 0 is not idle; not executing polled cmd\n", p);
+    return(-1);
+  }
+
+  /* fill in command slot 0 */
+  va_start(va, cmd);
+  if ((rc = v_ata_cmd(ai, p, d, 0, cmd, va)) != 0) {
+    return(rc);
+  }
+
+  /* start command execution for slot 0 */
+  ddprintf("executing polled cmd...");
+  writel(port_mmio + PORT_CMD_ISSUE, 1);
+
+  /* wait until command has completed */
+  while (timeout > 0 && (readl(port_mmio + PORT_CMD_ISSUE) & 1)) {
+    mdelay(10);
+    timeout -= 10;
+  }
+  ddprintf(" done (time left = %d)\n", timeout);
+
+  /* check error condition */
+  if ((tmp = readl(port_mmio + PORT_SCR_ERR)) != 0) {
+    dprintf("SERR = 0x%08lx\n", tmp);
+    return(-1);
+  }
+  if (((tmp = readl(port_mmio + PORT_TFDATA)) & 0x89) != 0) {
+    dprintf("TFDATA = 0x%08lx\n", tmp);
+    return(-1);
+  }
+
+  return((timeout <= 0) ? -1 : 0);
+}
+
+/******************************************************************************
+ * AHCI top-level hardware interrupt handler. This handler finds the adapters
+ * and ports which have issued the interrupt and calls the corresponding
+ * port interrupt handler.
+ *
+ * On entry, OS/2 will have processor interrupts enabled because we're using
+ * shared IRQs but we won't be preempted by another interrupt on the same IRQ
+ * IRQ level until we indicated EOI. We'll keep it this way, only requesting
+ * the driver-level spinlock when actually changing the driver state (IORB
+ * queues, ...)
+ */
+int ahci_intr(u16 irq)
+{
+  u32 irq_stat;
+  int handled = 0;
+  int a;
+  int p;
+
+  /* find adapter(s) with pending interrupts */
+  for (a = 0; a < ad_info_cnt; a++) {
+    AD_INFO *ai = ad_infos + a;
+
+    if (ai->irq == irq && (irq_stat = readl(ai->mmio + HOST_IRQ_STAT)) != 0) {
+      /* this adapter has interrupts pending */
+      u32 irq_masked = irq_stat & ai->port_map;
+
+      for (p = 0; p <= ai->port_max; p++) {
+        if (irq_masked & (1UL << p)) {
+          ahci_port_intr(ai, p);
+        }
+      }
+
+      /* clear interrupt condition on the adapter */
+      writel(ai->mmio + HOST_IRQ_STAT, irq_stat);
+      readl(ai->mmio + HOST_IRQ_STAT); /* flush */
+      handled = 1;
+    }
+  }
+
+  if (handled) {
+    /* trigger state machine to process next IORBs, if any */
+    spin_lock(drv_lock);
+    trigger_engine();
+    spin_unlock(drv_lock);
+
+    /* complete the interrupt */
+    DevHelp_EOI(irq);
+    return(0);
+  } else {
+    return(1);
+  }
+}
+
+/******************************************************************************
+ * AHCI port-level interrupt handler. As described above, processor interrupts
+ * are enabled on entry thus we have to protect shared resources with a
+ * spinlock.
+ */
+void ahci_port_intr(AD_INFO *ai, int p)
+{
+  IORB_QUEUE done_queue;
+  IORBH _far *iorb;
+  IORBH _far *next = NULL;
+  u8 _far *port_mmio = port_base(ai, p);
+  u32 irq_stat;
+  u32 active_cmds;
+  u32 done_mask;
+  
+  ddprintf("port interrupt for adapter #%d, port #%d\n", ad_no(ai), p);
+  memset(&done_queue, 0x00, sizeof(done_queue));
+
+  /* get interrupt status and clear it right away */
+  irq_stat = readl(port_mmio + PORT_IRQ_STAT);
+  writel(port_mmio + PORT_IRQ_STAT, irq_stat);
+  readl(port_mmio + PORT_IRQ_STAT); /* flush */
+  
+  if (irq_stat & PORT_IRQ_ERROR) {
+    /* this is an error interrupt */
+    ahci_error_intr(ai, p, irq_stat);
+    return;
+  }
+
+  spin_lock(drv_lock);
+
+  /* Find out which command slots have completed. Since error recovery for
+   * NCQ commands interfers with non-NCQ commands, the upper layers will
+   * make sure there's never a mixture of NCQ and non-NCQ commands active
+   * on any port at any given time. This makes it easier to find out which
+   * commands have completed, too.
+   */
+  if (ai->ports[p].ncq_cmds != 0) {
+    active_cmds = readl(port_mmio + PORT_SCR_ACT);
+    done_mask = ai->ports[p].ncq_cmds ^ active_cmds;
+    ddprintf("[ncq_cmds]: active_cmds = 0x%08lx, done_mask = 0x%08lx\n",
+             active_cmds, done_mask);
+  } else {
+    active_cmds = readl(port_mmio + PORT_CMD_ISSUE);
+    done_mask = ai->ports[p].reg_cmds ^ active_cmds;
+    ddprintf("[reg_cmds]: active_cmds = 0x%08lx, done_mask = 0x%08lx\n",
+             active_cmds, done_mask);
+  }
+
+  /* Find the IORBs related to the completed commands and complete them.
+   *
+   * NOTES: The spinlock must not be released while in this loop to prevent
+   *        race conditions with timeout handlers or other threads in SMP
+   *        systems.
+   *
+   *        Since we hold the spinlock when IORBs complete, we can't call the
+   *        IORB notification routine right away because this routine might
+   *        schedule another IORB which could cause a deadlock. Thus, we'll
+   *        add all IORBs to be completed to a temporary queue which will be
+   *        processed after releasing the spinlock.
+   */
+  for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
+    ADD_WORKSPACE _far *aws = (ADD_WORKSPACE _far *) &iorb->ADDWorkSpace;
+    next = iorb->pNxtIORB;
+    if (aws->queued_hw && (done_mask & (1UL << aws->cmd_slot))) {
+      /* this command has completed */
+      if (aws->ppfunc != NULL) {
+        aws->ppfunc(iorb);
+      } else {
+        aws->complete = 1;
+      }
+
+      if (aws->complete) {
+        /* this IORB is complete */
+        aws_free(aws);
+
+        /* move IORB to our temporary done queue */
+        iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
+        iorb_queue_add(&done_queue, iorb);
+      }
+
+      /* clear corresponding bit in issued command bitmaps */
+      ai->ports[p].ncq_cmds &= ~(1UL << aws->cmd_slot);
+      ai->ports[p].reg_cmds &= ~(1UL << aws->cmd_slot);
+    }
+  }
+
+  spin_unlock(drv_lock);
+
+  /* call notification routines for all IORBs in the done queue */
+  for (iorb = done_queue.root; iorb != NULL; iorb = next) {
+    next = iorb->pNxtIORB;
+    iorb->Status = IORB_DONE;
+    if (iorb->RequestControl & IORB_ASYNC_POST) {
+      iorb->NotifyAddress(iorb);
+    }
+  }
+}
+
+/******************************************************************************
+ * AHCI error interrupt handler. Errors include interface errors and device
+ * errors (usually triggered by the error bit in the AHCI task file register).
+ *
+ * Since this involves long-running operations such as restarting or even
+ * resetting a port, this function is invoked at task time via a context
+ * hook.
+ *
+ * NOTE: AHCI controllers stop all processing when encountering an error
+ *       condition in order to give the driver time to find out what exactly
+ *       went wrong. This means no new commands will be processed until we
+ *       clear the error register and restore the "commands issued" register.
+ */
+void ahci_error_intr(AD_INFO *ai, int p, u32 irq_stat)
+{
+  u8 _far *port_mmio = port_base(ai, p);
+  int reset_port = 0;
+  u32 tmp;
+
+  /* Handle adapter and interface errors. Those typically require a port
+   * reset, or worse.
+   */
+  if (irq_stat & PORT_IRQ_UNK_FIS) {
+    u32 _far *unk = (u32 _far *) (port_dma_base(ai, p)->rx_fis + RX_FIS_UNK);
+    dprintf("warning: unknown FIS %08lx %08lx %08lx %08lx\n",
+            unk[0], unk[1], unk[2], unk[3]);
+    reset_port = 1;
+  }
+  if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
+    dprintf("warning: host bus [data] error for port #%d\n", p);
+    reset_port = 1;
+  }
+  if (irq_stat & PORT_IRQ_IF_ERR && !(ai->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)) {
+    dprintf("warning: interface fatal error for port #%d\n", p);
+    reset_port = 1;
+  }
+  if (reset_port) {
+    /* need to reset the port; leave this to the reset context hook */
+    ports_to_reset[ad_no(ai)] |= 1UL << p;
+    DevHelp_ArmCtxHook(0, reset_ctxhook_h);
+
+    /* no point analyzing device errors after a reset... */
+    return;
+  } 
+
+  /* Handle device-specific errors. Those errors typically involve restarting
+   * the corresponding port to resume operations which can take some time,
+   * thus we need to offload this functionality to the restart context hook.
+   */
+  if (irq_stat & PORT_IRQ_TF_ERR) {
+    ports_to_restart[ad_no(ai)] |= 1UL << p;
+    DevHelp_ArmCtxHook(0, restart_ctxhook_h);
+  }
+}
+   
+/******************************************************************************
+ * Get device or media geometry. Device and media geometry are expected to be
+ * the same for non-removable devices.
+ */
+void ahci_get_geometry(IORBH _far *iorb)
+{
+  dprintf("ahci_get_geometry(%d.%d.%d)\n", (int) iorb_unit_adapter(iorb),
+          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb));
+
+  ahci_exec_iorb(iorb, 0, cmd_func(iorb, get_geometry));
+}
+
+/******************************************************************************
+ * Test whether unit is ready.
+ */
+void ahci_unit_ready(IORBH _far *iorb)
+{
+  dprintf("ahci_unit_ready(%d.%d.%d)\n", (int) iorb_unit_adapter(iorb),
+          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb));
+
+  ahci_exec_iorb(iorb, 0, cmd_func(iorb, unit_ready));
+}
+
+/******************************************************************************
+ * Read sectors from AHCI device.
+ */
+void ahci_read(IORBH _far *iorb)
+{
+  dprintf("ahci_read(%d.%d.%d, %ld, %ld)\n", (int) iorb_unit_adapter(iorb),
+          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb),
+          (long) ((IORB_EXECUTEIO _far *) iorb)->RBA,
+          (long) ((IORB_EXECUTEIO _far *) iorb)->BlockCount);
+
+  ahci_exec_iorb(iorb, 1, cmd_func(iorb, read));
+}  
+
+/******************************************************************************
+ * Verify readability of sectors on AHCI device.
+ */
+void ahci_verify(IORBH _far *iorb)
+{
+  dprintf("ahci_verify(%d.%d.%d, %ld, %ld)\n", (int) iorb_unit_adapter(iorb),
+          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb),
+          (long) ((IORB_EXECUTEIO _far *) iorb)->RBA,
+          (long) ((IORB_EXECUTEIO _far *) iorb)->BlockCount);
+
+  ahci_exec_iorb(iorb, 0, cmd_func(iorb, verify));
+}
+
+/******************************************************************************
+ * Write sectors to AHCI device.
+ */
+void ahci_write(IORBH _far *iorb)
+{
+  dprintf("ahci_write(%d.%d.%d, %ld, %ld)\n", (int) iorb_unit_adapter(iorb),
+          (int) iorb_unit_port(iorb), (int) iorb_unit_device(iorb),
+          (long) ((IORB_EXECUTEIO _far *) iorb)->RBA,
+          (long) ((IORB_EXECUTEIO _far *) iorb)->BlockCount);
+
+  ahci_exec_iorb(iorb, 1, cmd_func(iorb, write));
+}
+
+/******************************************************************************
+ * Execute SCSI (ATAPI) command.
+ */
+void ahci_execute_cdb(IORBH _far *iorb)
+{
+  int a = iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+
+  dphex(((IORB_ADAPTER_PASSTHRU _far *) iorb)->pControllerCmd,
+        ((IORB_ADAPTER_PASSTHRU _far *) iorb)->ControllerCmdLen,
+        "ahci_execute_cdb(%d.%d.%d)", a, p, d);
+  
+  if (ad_infos[a].ports[p].devs[d].atapi) {
+    ahci_exec_iorb(iorb, 0, atapi_execute_cdb);
+  } else {
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+  }
+}
+
+/******************************************************************************
+ * Execute ATA command.
+ */
+void ahci_execute_ata(IORBH _far *iorb)
+{
+  int a = iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+  
+  dphex(((IORB_ADAPTER_PASSTHRU _far *) iorb)->pControllerCmd,
+        ((IORB_ADAPTER_PASSTHRU _far *) iorb)->ControllerCmdLen,
+        "ahci_execute_cdb(%d.%d.%d)", a, p, d);
+  
+  if (ad_infos[a].ports[p].devs[d].atapi) {
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+  } else {
+    ahci_exec_iorb(iorb, 0, ata_execute_ata);
+  }
+}
+
+/******************************************************************************
+ * Set up device attached to the specified port based on ATA_IDENTFY_DEVICE or
+ * ATA_IDENTFY_PACKET_DEVICE data.
+ *
+ * NOTE: Port multipliers are not supported, yet, thus the device number is
+ *       expected to be 0 for the time being.
+ */
+static void ahci_setup_device(AD_INFO *ai, int p, int d, u16 *id_buf)
+{
+  DEVICESTRUCT ds;
+  ADJUNCT adj;
+  HDEVICE dh;
+
+  if (ai->port_max < p) {
+    ai->port_max = p;
+  }
+  if (ai->ports[p].dev_max < d) {
+    ai->ports[p].dev_max = d;
+  }
+  memset(ai->ports[p].devs + d, 0x00, sizeof(*ai->ports[p].devs));
+
+  /* set generic device information (assuming an ATA disk device for now) */
+  ai->ports[p].devs[d].present   = 1;
+  ai->ports[p].devs[d].removable = (id_buf[ATA_ID_CONFIG] & 0x0080U) != 0;
+  ai->ports[p].devs[d].dev_type  = UIB_TYPE_DISK;
+
+  if (id_buf[ATA_ID_CONFIG] & 0x8000U) {
+    /* this is an ATAPI device; augment device information */
+    ai->ports[p].devs[d].atapi     = 1;
+    ai->ports[p].devs[d].atapi_16  = (id_buf[ATA_ID_CONFIG] & 0x0001U) != 0;
+    ai->ports[p].devs[d].dev_type  = (id_buf[ATA_ID_CONFIG] & 0x1f00U) >> 8;
+
+  } else {
+    /* complete ATA-specific device information */
+    if (!disable_ncq[ad_no(ai)][p]) {
+      ai->ports[p].devs[d].ncq_max = id_buf[ATA_ID_QUEUE_DEPTH] & 0x001fU;
+    }
+    if (id_buf[ATA_ID_CFS_ENABLE_2] & 0x0400U) {
+      ai->ports[p].devs[d].lba48   = 1;
+    }
+  }
+
+  dprintf("found device %d.%d.%d: removable = %d, dev_type = %d, atapi = %d\n",
+          ad_no(ai), p, d,
+          ai->ports[p].devs[d].removable,
+          ai->ports[p].devs[d].dev_type,
+          ai->ports[p].devs[d].atapi);
+
+  /* add device to resource manager; we don't really care about errors here */
+  memset(&ds, 0x00, sizeof(ds));
+  memset(&adj, 0x00, sizeof(adj));
+
+  adj.pNextAdj           = NULL;
+  adj.AdjLength          = sizeof(adj);
+  adj.AdjType            = ADJ_ADD_UNIT;
+  adj.Add_Unit.ADDHandle = rm_drvh;
+
+  ds.DevDescriptName = ata_dev_name(id_buf);
+  ds.DevFlags        = (ai->ports[p].devs[d].removable) ? DS_REMOVEABLE_MEDIA
+                                                        : DS_FIXED_LOGICALNAME;
+  ds.DevType         = ai->ports[p].devs[d].dev_type;
+
+  RMCreateDevice(rm_drvh, &dh, &ds, ai->rm_adh, NULL);
+
+  /* try to detect virtualbox environment to enable a hack for IRQ routing */
+  if (ai == ad_infos && p == 7 &&
+      ai->pci->vendor == 0x8086 && ai->pci->device == 0x2829 &&
+      !memcmp(ds.DevDescriptName, "VBOX HARDDISK", 13)) {
+    /* running inside virtualbox */
+    pci_hack_virtualbox();
+  }
+}
+
+/******************************************************************************
+ * Timeout handler for I/O commands. Since timeout handling can involve
+ * lengthy operations like port resets, the main code is located in a
+ * separate function which is invoked via a context hook.
+ */
+static void _far timeout_callback(ULONG timer_handle, ULONG p1, ULONG p2)
+{
+  IORBH _far *iorb = (IORBH _far *) p1;
+  int a = iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+
+  ADD_CancelTimer(timer_handle);
+  dprintf("timeout for IORB %Fp\n", iorb);
+
+  /* Move the timed-out IORB to the abort queue. Since it's possible that the
+   * IORB has completed after the timeout has expired but before we got to
+   * this line of code, we'll check the return code of iorb_queue_del(): If it
+   * returns an error, the IORB must have completed a few microseconds ago and
+   * there is no timeout.
+   */
+  spin_lock(drv_lock);
+  if (iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb) == 0) {
+    iorb_queue_add(&abort_queue, iorb);
+    iorb->ErrorCode = IOERR_ADAPTER_TIMEOUT;
+  }
+  spin_unlock(drv_lock);
+
+  /* Trigger abort processing function. We don't really care whether this
+   * succeeds because the only reason why it would fail should be multiple
+   * calls to DevHelp_ArmCtxHook() before the context hook had a chance to
+   * start executing, which leaves two scenarios:
+   *
+   *  - We succeded in arming the context hook. Fine.
+   *
+   *  - We armed the context hook a second time before it had a chance to
+   *    start executing. In this case, the already scheduled context hook
+   *    will process our IORB as well.
+   */
+  DevHelp_ArmCtxHook(0, reset_ctxhook_h);
+}
+
Index: trunk/src/os2ahci/ahci.h
===================================================================
--- trunk/src/os2ahci/ahci.h	(revision 13)
+++ trunk/src/os2ahci/ahci.h	(revision 13)
@@ -0,0 +1,260 @@
+/******************************************************************************
+ * ahci.h - AHCI-specific constants for os2ahci.h
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* ----------------------------- include files ----------------------------- */
+
+/* -------------------------- macros and constants ------------------------- */
+
+/******************************************************************************
+ * AHCI flags and constants; those were initially copied from the Linux AHCI
+ * driver but converted to macros because enums are 16 bits for OS/2 drivers
+ * (unless we use KEE and a 32-bit compiler, which we don't)
+ *
+ * Changes from the Linux source:
+ *
+ *  - reduced AHCI_MAX_SG from 168 to 48 because the port-specific DMA scratch
+ *    buffer needs to be less than 64K to allow mapping the whole DMA area to a
+ *    16-bit memory segment
+ *
+ *  - added AHCI_MAX_SG_ELEMENT_LEN constant
+ *
+ *  - replaced much of the top-level size/offset math with real structs and
+ *    corresponding sizeof() directives.
+ */
+#define AHCI_PCI_BAR            5
+#define AHCI_MAX_PORTS          32
+#define AHCI_MAX_SG             48 /* hardware max is 64K */
+#define AHCI_MAX_SG_ELEMENT_LEN (1UL << 22)
+#define AHCI_MAX_CMDS           32
+#define AHCI_RX_FIS_SZ          256
+
+/* port-specific DMA scratch buffer aligned to 1024 bytes */
+#define AHCI_PORT_PRIV_DMA_SZ   (((sizeof(AHCI_PORT_DMA) + 1023U) / 1024U) * 1024U)
+
+#define AHCI_IRQ_ON_SG          (1UL << 31)
+#define AHCI_CMD_ATAPI          (1UL << 5)
+#define AHCI_CMD_WRITE          (1UL << 6)
+#define AHCI_CMD_PREFETCH       (1UL << 7)
+#define AHCI_CMD_RESET          (1UL << 8)
+#define AHCI_CMD_CLR_BUSY       (1UL << 10)
+
+#define RX_FIS_D2H_REG          0x40 /* offset of D2H Register FIS data */
+#define RX_FIS_SDB              0x58 /* offset of SDB FIS data */
+#define RX_FIS_UNK              0x60 /* offset of Unknown FIS data */
+
+#define board_ahci              0
+#define board_ahci_vt8251       1
+#define board_ahci_ign_iferr    2
+#define board_ahci_sb600        3
+#define board_ahci_mv           4
+#define board_ahci_sb700        5 /* for SB700 and SB800 */
+#define board_ahci_mcp65        6
+#define board_ahci_nopmp        7
+#define board_ahci_yesncq       8
+#define board_ahci_nosntf       9
+
+/* global controller registers */
+#define HOST_CAP                0x00 /* host capabilities */
+#define HOST_CTL                0x04 /* global host control */
+#define HOST_IRQ_STAT           0x08 /* interrupt status */
+#define HOST_PORTS_IMPL         0x0c /* bitmap of implemented ports */
+#define HOST_VERSION            0x10 /* AHCI spec. version compliancy */
+#define HOST_CCC                0x14 /* Command Completion Coalescing Control */
+#define HOST_CCC_PORTS          0x18 /* CCC ports */
+#define HOST_EM_LOC             0x1c /* Enclosure Management location */
+#define HOST_EM_CTL             0x20 /* Enclosure Management Control */
+#define HOST_CAP2               0x24 /* host capabilities, extended */
+
+/* HOST_CTL bits */
+#define HOST_RESET              (1UL << 0)  /* reset controller; self-clear */
+#define HOST_IRQ_EN             (1UL << 1)  /* global IRQ enable */
+#define HOST_AHCI_EN            (1UL << 31) /* AHCI enabled */
+
+/* HOST_CAP bits */
+#define HOST_CAP_SXS            (1UL << 5)  /* Supports External SATA */
+#define HOST_CAP_EMS            (1UL << 6)  /* Enclosure Management support */
+#define HOST_CAP_CCC            (1UL << 7)  /* Command Completion Coalescing */
+#define HOST_CAP_PART           (1UL << 13) /* Partial state capable */
+#define HOST_CAP_SSC            (1UL << 14) /* Slumber state capable */
+#define HOST_CAP_PIO_MULTI      (1UL << 15) /* PIO multiple DRQ support */
+#define HOST_CAP_FBS            (1UL << 16) /* FIS-based switching support */
+#define HOST_CAP_PMP            (1UL << 17) /* Port Multiplier support */
+#define HOST_CAP_ONLY           (1UL << 18) /* Supports AHCI mode only */
+#define HOST_CAP_CLO            (1UL << 24) /* Command List Override support */
+#define HOST_CAP_LED            (1UL << 25) /* Supports activity LED */
+#define HOST_CAP_ALPM           (1UL << 26) /* Aggressive Link PM support */
+#define HOST_CAP_SSS            (1UL << 27) /* Staggered Spin-up */
+#define HOST_CAP_MPS            (1UL << 28) /* Mechanical presence switch */
+#define HOST_CAP_SNTF           (1UL << 29) /* SNotification register */
+#define HOST_CAP_NCQ            (1UL << 30) /* Native Command Queueing */
+#define HOST_CAP_64             (1UL << 31) /* PCI DAC (64-bit DMA) support */
+
+/* HOST_CAP2 bits */
+#define HOST_CAP2_BOH           (1UL << 0)  /* BIOS/OS handoff supported */
+#define HOST_CAP2_NVMHCI        (1UL << 1)  /* NVMHCI supported */
+#define HOST_CAP2_APST          (1UL << 2)  /* Automatic partial to slumber */
+
+/* registers for each SATA port */
+#define PORT_LST_ADDR           0x00 /* command list DMA addr */
+#define PORT_LST_ADDR_HI        0x04 /* command list DMA addr hi */
+#define PORT_FIS_ADDR           0x08 /* FIS rx buf addr */
+#define PORT_FIS_ADDR_HI        0x0c /* FIS rx buf addr hi */
+#define PORT_IRQ_STAT           0x10 /* interrupt status */
+#define PORT_IRQ_MASK           0x14 /* interrupt enable/disable mask */
+#define PORT_CMD                0x18 /* port command */
+#define PORT_TFDATA             0x20 /* taskfile data */
+#define PORT_SIG                0x24 /* device TF signature */
+#define PORT_CMD_ISSUE          0x38 /* command issue */
+#define PORT_SCR_STAT           0x28 /* SATA phy register: SStatus */
+#define PORT_SCR_CTL            0x2c /* SATA phy register: SControl */
+#define PORT_SCR_ERR            0x30 /* SATA phy register: SError */
+#define PORT_SCR_ACT            0x34 /* SATA phy register: SActive */
+#define PORT_SCR_NTF            0x3c /* SATA phy register: SNotification */
+
+/* PORT_IRQ_{STAT,MASK} bits */
+#define PORT_IRQ_COLD_PRES      (1UL << 31) /* cold presence detect */
+#define PORT_IRQ_TF_ERR         (1UL << 30) /* task file error */
+#define PORT_IRQ_HBUS_ERR       (1UL << 29) /* host bus fatal error */
+#define PORT_IRQ_HBUS_DATA_ERR  (1UL << 28) /* host bus data error */
+#define PORT_IRQ_IF_ERR         (1UL << 27) /* interface fatal error */
+#define PORT_IRQ_IF_NONFATAL    (1UL << 26) /* interface non-fatal error */
+#define PORT_IRQ_OVERFLOW       (1UL << 24) /* xfer exhausted available S/G */
+#define PORT_IRQ_BAD_PMP        (1UL << 23) /* incorrect port multiplier */
+#define PORT_IRQ_PHYRDY         (1UL << 22) /* PhyRdy changed */
+#define PORT_IRQ_DEV_ILCK       (1UL << 7)  /* device interlock */
+#define PORT_IRQ_CONNECT        (1UL << 6)  /* port connect change status */
+#define PORT_IRQ_SG_DONE        (1UL << 5)  /* descriptor processed */
+#define PORT_IRQ_UNK_FIS        (1UL << 4)  /* unknown FIS rx'd */
+#define PORT_IRQ_SDB_FIS        (1UL << 3)  /* Set Device Bits FIS rx'd */
+#define PORT_IRQ_DMAS_FIS       (1UL << 2)  /* DMA Setup FIS rx'd */
+#define PORT_IRQ_PIOS_FIS       (1UL << 1)  /* PIO Setup FIS rx'd */
+#define PORT_IRQ_D2H_REG_FIS    (1UL << 0)  /* D2H Register FIS rx'd */
+#define PORT_IRQ_FREEZE         (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR   | \
+                                 PORT_IRQ_CONNECT  | PORT_IRQ_PHYRDY   | \
+                                 PORT_IRQ_UNK_FIS  | PORT_IRQ_BAD_PMP)
+#define PORT_IRQ_ERROR          (PORT_IRQ_FREEZE   | PORT_IRQ_TF_ERR   | \
+                                 PORT_IRQ_HBUS_DATA_ERR)
+#define DEF_PORT_IRQ            (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE     | \
+                                 PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS  | \
+                                 PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
+
+/* PORT_CMD bits */
+#define PORT_CMD_ASP            (1UL << 27) /* Aggressive Slumber/Partial */
+#define PORT_CMD_ALPE           (1UL << 26) /* Aggressive Link PM enable */
+#define PORT_CMD_ATAPI          (1UL << 24) /* Device is ATAPI */
+#define PORT_CMD_PMP            (1UL << 17) /* PMP attached */
+#define PORT_CMD_LIST_ON        (1UL << 15) /* cmd list DMA engine running */
+#define PORT_CMD_FIS_ON         (1UL << 14) /* FIS DMA engine running */
+#define PORT_CMD_FIS_RX         (1UL << 4)  /* Enable FIS receive DMA engine */
+#define PORT_CMD_CLO            (1UL << 3)  /* Command list override */
+#define PORT_CMD_POWER_ON       (1UL << 2)  /* Power up device */
+#define PORT_CMD_SPIN_UP        (1UL << 1)  /* Spin up device */
+#define PORT_CMD_START          (1UL << 0)  /* Enable port DMA engine */
+
+#define PORT_CMD_ICC_MASK       (0xfUL << 28) /* i/f ICC state mask */
+#define PORT_CMD_ICC_ACTIVE     (0x1UL << 28) /* Put i/f in active state */
+#define PORT_CMD_ICC_PARTIAL    (0x2UL << 28) /* Put i/f in partial state */
+#define PORT_CMD_ICC_SLUMBER    (0x6UL << 28) /* Put i/f in slumber state */
+
+/* driver status bits */
+#define AHCI_HFLAG_NO_NCQ               (1UL << 0)  /* no native cmd queuing */
+#define AHCI_HFLAG_IGN_IRQ_IF_ERR       (1UL << 1)  /* ignore IRQ_IF_ERR */
+#define AHCI_HFLAG_IGN_SERR_INTERNAL    (1UL << 2)  /* ignore SERR_INTERNAL */
+#define AHCI_HFLAG_32BIT_ONLY           (1UL << 3)  /* force 32bit */
+#define AHCI_HFLAG_MV_PATA              (1UL << 4)  /* PATA port */
+#define AHCI_HFLAG_NO_MSI               (1UL << 5)  /* no PCI MSI */
+#define AHCI_HFLAG_NO_PMP               (1UL << 6)  /* no PMP */
+#define AHCI_HFLAG_NO_HOTPLUG           (1UL << 7)  /* ignore PxSERR.DIAG.N */
+#define AHCI_HFLAG_SECT255              (1UL << 8)  /* max 255 sectors */
+#define AHCI_HFLAG_YES_NCQ              (1UL << 9)  /* force NCQ cap on */
+#define AHCI_HFLAG_NO_SUSPEND           (1UL << 10) /* don't suspend */
+#define AHCI_HFLAG_SRST_TOUT_IS_OFFLINE (1UL << 11) /* treat SRST timeout as
+                                                      link offline */
+#define AHCI_HFLAG_NO_SNTF              (1UL << 12) /* no sntf */
+
+#define ICH_MAP                         0x90 /* ICH MAP register */
+
+/* em constants */
+#define EM_MAX_SLOTS                    8
+#define EM_MAX_RETRY                    5
+
+/* em_ctl bits */
+#define EM_CTL_RST                      (1UL << 9)  /* Reset */
+#define EM_CTL_TM                       (1UL << 8)  /* Transmit Message */
+#define EM_CTL_ALHD                     (1UL << 26) /* Activity LED */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* Primitive types
+ *
+ * Note: Since OS/2 is essentially an x86 OS and this driver, as well as the
+ *       interface it's developed for, is based on x86 design patterns, we're
+ *       not even going to start making a difference between little and big
+ *       endian architectures. PCI is little endian, AHCI is little endian,
+ *       x86 is little endian, and that's it.
+ */
+typedef unsigned char    u8;
+typedef unsigned short   u16;
+typedef unsigned long    u32;
+
+/* AHCI S/G structure */
+typedef struct {
+  u32   addr;            /* address of S/G element */
+  u32   addr_hi;         /* address of S/G element (upper 32 bits) */
+  u32   reserved;
+  u32   size;            /* size of S/G element - 1; the high 10 bits are flags:
+                          *  31    : interrupt on completion of this S/G
+                          *  30-22 : reserved */
+} AHCI_SG;
+
+/* AHCI command header */
+typedef struct {
+  u32   options;         /* command options */
+  u32   status;          /* command status */
+  u32   tbl_addr;        /* command table address */
+  u32   tbl_addr_high;   /* command table address (upper 32 bits) */
+  u32   reserved[4];
+} AHCI_CMD_HDR;
+
+/* AHCI command table */
+typedef struct {
+  u8           cmd_fis[64];                /* ATA command FIS */
+  u8           atapi_cmd[16];              /* ATAPI command */
+  u8           reserved[48];
+  AHCI_SG      sg_list[AHCI_MAX_SG];       /* AHCI S/G list */
+} AHCI_CMD_TBL;
+
+/* AHCI port DMA scratch area */
+typedef struct {
+  AHCI_CMD_HDR   cmd_hdr[AHCI_MAX_CMDS];   /* command headers */
+  u8             rx_fis[AHCI_RX_FIS_SZ];   /* FIS RX area */
+  AHCI_CMD_TBL   cmd_tbl[AHCI_MAX_CMDS];   /* command table */
+} AHCI_PORT_DMA;
+
+/* AHCI port BIOS configuration save area */
+typedef struct {
+  u32   cmd_list;                          /* cmd list base address */
+  u32   cmd_list_h;                        /* cmd list base address high */
+  u32   fis_rx;                            /* FIS receive buffer */
+  u32   fis_rx_h;                          /* FIS receive bufffer high */
+  u32   irq_mask;                          /* IRQ mask */
+} AHCI_PORT_CFG;
+
Index: trunk/src/os2ahci/ata.c
===================================================================
--- trunk/src/os2ahci/ata.c	(revision 13)
+++ trunk/src/os2ahci/ata.c	(revision 13)
@@ -0,0 +1,824 @@
+/******************************************************************************
+ * ata.c - ATA command processing
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+#include "ata.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+/* ------------------------ global/static variables ------------------------ */
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Initialize AHCI command slot, FIS and S/G list for the specified ATA
+ * command. The command parameters are passed as a variable argument list
+ * of type and value(s). The list is terminated by AP_END.
+ *
+ * Notes:
+ *
+ *  - The specified command slot is expected to be idle; no checks are
+ *    performed to prevent messing with a busy port.
+ *
+ *  - Port multipliers are not supported, yet, thus 'd' should always
+ *    be 0 for the time being.
+ *
+ *  - 'cmd' is passwd as 16-bit integer because the compiler would push
+ *    a 'u8' as 16-bit value (it's a fixed argument) and the stdarg
+ *    macros would screw up the address of the first variable argument
+ *    if the size of the last fixed argument wouldn't match what the
+ *    compiler pushed on the stack.
+ *
+ * Return values:
+ *    0 : success
+ *  > 0 : could not map all S/G entries; the return value is the number of
+ *        S/G entries that could be mapped.
+ *  < 0 : other error
+ */
+int ata_cmd(AD_INFO *ai, int p, int d, int slot, int cmd, ...)
+{
+  va_list va;
+  va_start(va, cmd);
+  return(v_ata_cmd(ai, p, d, slot, cmd, va));
+}
+
+int v_ata_cmd(AD_INFO *ai, int p, int d, int slot, int cmd, va_list va)
+{
+  AHCI_PORT_DMA _far *dma_base_virt;
+  AHCI_CMD_HDR _far *cmd_hdr;
+  AHCI_CMD_TBL _far *cmd_tbl;
+  SCATGATENTRY _far *sg_list = NULL;
+  SCATGATENTRY sg_single;
+  ATA_PARM ap;
+  ATA_CMD ata_cmd;
+  void _far *atapi_cmd = NULL;
+  u32 dma_base_phys;
+  u16 atapi_cmd_len = 0;
+  u16 ahci_flags = 0;
+  u16 sg_cnt = 0;
+  int i;
+  int n;
+
+  /* --------------------------------------------------------------------------
+   * Initialize ATA command. The ATA command is set up with the main command
+   * value and a variable list of additional parameters such as the sector
+   * address, transfer count, ...
+   */
+  memset(&ata_cmd, 0x00, sizeof(ata_cmd));
+  ata_cmd.cmd = (u8) cmd;
+
+  /* parse variable arguments */
+  do {
+    switch ((ap = va_arg(va, ATA_PARM))) {
+    
+    case AP_AHCI_FLAGS:
+      ahci_flags |= va_arg(va, u16);
+      break;
+
+    case AP_WRITE:
+      if (va_arg(va, u16) != 0) {
+        ahci_flags |= AHCI_CMD_WRITE;
+      }
+      break;
+
+    case AP_FEATURES:
+      /* ATA features word */
+      ata_cmd.features = va_arg(va, u16);
+      break;
+
+    case AP_COUNT:
+      /* transfer count */
+      ata_cmd.count = va_arg(va, u16);
+      break;
+
+    case AP_SECTOR_28:
+      /* 28-bit sector address */
+      ata_cmd.lba_l = va_arg(va, u32);
+      if (ata_cmd.lba_l & 0xf0000000UL) {
+        dprintf("error: LBA-28 address %ld has more than 28 bits\n", ata_cmd.lba_l);
+        return(-1);
+      }
+      break;
+
+    case AP_SECTOR_48:
+      /* 48-bit sector address */
+      ata_cmd.lba_l = va_arg(va, u32);
+      ata_cmd.lba_h = va_arg(va, u16);
+      break;
+    
+    case AP_DEVICE:
+      /* ATA device byte */
+      ata_cmd.device = va_arg(va, u16) >> 8;
+      break;
+
+    case AP_SGLIST:
+      /* scatter/gather list in SCATGATENTRY/count format */
+      sg_list = va_arg(va, void _far *);
+      sg_cnt = va_arg(va, u16);
+      break;
+
+    case AP_VADDR:
+      /* virtual buffer address in addr/len format (up to 4K) */
+      DevHelp_VirtToPhys(va_arg(va, void _far *), &sg_single.ppXferBuf);
+      sg_single.XferBufLen = va_arg(va, u16);
+      sg_list = &sg_single;
+      sg_cnt = 1;
+      break;
+
+    case AP_ATAPI_CMD:
+      /* ATAPI command */
+      atapi_cmd = va_arg(va, void _far *);
+      atapi_cmd_len = va_arg(va, u16);
+      ahci_flags |= AHCI_CMD_ATAPI;
+      break;
+
+    case AP_ATA_CMD:
+      /* ATA command "pass-through" */
+      memcpy(&ata_cmd, va_arg(va, void _far *), sizeof(ATA_CMD));
+      break;
+
+    case AP_END:
+      break;
+
+    default:
+      dprintf("error: v_ata_cmd() called with invalid parameter type (%d)\n", (int) ap);
+      return(-1);
+    }
+
+  } while (ap != AP_END);
+
+  /* --------------------------------------------------------------------------
+   * Fill in AHCI ATA command information. This includes the port command slot,
+   * the corresponding command FIS and the S/G list. The layout of the AHCI
+   * port DMA region is based on the Linux AHCI driver and looks like this:
+   *
+   *  - 32 AHCI command headers (AHCI_CMD_HDR) with 32 bytes, each
+   *  -  1 FIS receive area with 256 bytes (AHCI_RX_FIS_SZ)
+   *  - 32 AHCI command tables, each consisting of
+   *    -  64 bytes for command FIS
+   *    -  16 bytes for ATAPI comands
+   *    -  48 bytes reserved
+   *    -  48 S/G entries (AHCI_SG) with 32 bytes, each
+   *
+   * Since the whole DMA buffer for all ports is larger than 64KB and we need
+   * multiple segments to address all of them, there are no virtual pointers
+   * to the individual elements in AD_INFO. Instead, we're relying on macros
+   * for getting the base address of a particular port's DMA region, then
+   * map a structure on top of that for convenience (AHCI_PORT_DMA).
+   */
+  dma_base_virt = port_dma_base(ai, p);
+  dma_base_phys = port_dma_base_phys(ai, p);
+  
+  /* AHCI command header */
+  cmd_hdr = dma_base_virt->cmd_hdr + slot;
+  memset(cmd_hdr, 0x00, sizeof(*cmd_hdr));
+  cmd_hdr->options  = ((d & 0x0f) << 12);
+  cmd_hdr->options |= ahci_flags;  /* AHCI commaand flags */
+  cmd_hdr->options |= 5;           /* length of command FIS in 32-bit words */
+  cmd_hdr->tbl_addr = dma_base_phys + offsetof(AHCI_PORT_DMA, cmd_tbl[slot]);
+
+  /* AHCI command table */
+  cmd_tbl = dma_base_virt->cmd_tbl + slot;
+  memset(cmd_tbl, 0x00, sizeof(*cmd_tbl));
+  ata_cmd_to_fis(cmd_tbl->cmd_fis, &ata_cmd, d);
+
+  if (atapi_cmd != NULL) {
+    /* copy ATAPI command */
+    memcpy(cmd_tbl->atapi_cmd, atapi_cmd, atapi_cmd_len);
+  }
+
+  /* PRDT (S/G list)
+   *
+   *  - The S/G list for AHCI adapters is limited to 22 bits for the transfer
+   *    size of each element, thus we need to split S/G elements larger than
+   *    22 bits into 2 AHCI_SG elements.
+   *
+   *  - The S/G element size for AHCI is what the spec calls "'0' based"
+   *    (i.e. 0 means 1 bytes). On top of that, the spec requires S/G transfer
+   *    sizes to be even in the context of 16-bit transfers, thus bit '1'
+   *    always needs to be set.
+   *
+   *  - AHCI_MAX_SG_ELEMENT_LEN defines the maximum size of an AHCI S/G
+   *    element in bytes, ignoring the '0'-based methodology (i.e. 1 << 22).
+   *
+   *  - There's a limit on the maximum number of S/G elements in the port DMA
+   *    buffer (AHCI_MAX_SG) which is lower than the HW maximum. It's beyond
+   *    the control of this function to split commands which require more
+   *    than AHCI_MAX_SG entries. In order to help the caller, the return value
+   *    of this function will indicate how many OS/2 S/G entries were
+   *    successfully mapped.
+   *
+   */
+  for (i = n = 0; i < sg_cnt; i++) {
+    u32 sg_addr = sg_list[i].ppXferBuf;
+    u32 sg_size = sg_list[i].XferBufLen;
+
+    do {
+      u32 chunk = (sg_size > AHCI_MAX_SG_ELEMENT_LEN) ? AHCI_MAX_SG_ELEMENT_LEN
+                                                      : sg_size;
+      if (n >= AHCI_MAX_SG) {
+        /* couldn't store all S/G elements in our DMA buffer */
+        ddprintf("ata_cmd(): too many S/G elements\n");
+        return(i - 1);
+      }
+      cmd_tbl->sg_list[n].addr = sg_addr;
+      cmd_tbl->sg_list[n].size = chunk - 1;
+      sg_addr += chunk;
+      sg_size -= chunk;
+      n++;
+    } while (sg_size > 0);
+  }
+
+  /* set final S/G count in AHCI command header */
+  cmd_hdr->options |= (u32) n << 16;
+
+  if (debug >= 2) {
+    printf("ATA command for %d.%d.%d:\n", ad_no(ai), p, d);
+    phex(cmd_hdr, offsetof(AHCI_CMD_HDR, reserved), "cmd_hdr:   ");
+    phex(&ata_cmd, sizeof(ata_cmd), "ata_cmd:   ");
+    if (atapi_cmd != NULL) {
+      phex(atapi_cmd, atapi_cmd_len, "atapi_cmd: ");
+    }
+    if (n > 0) {
+      phex(cmd_tbl->sg_list, sizeof(*cmd_tbl->sg_list) * n, "sg_list:   ");
+    }
+  }
+
+  return(0);
+}
+
+/******************************************************************************
+ * Fill SATA command FIS with values extracted from an ATA command structure.
+ * The command FIS buffer (fis) is expected to be initialized to 0s. The
+ * structure of the FIS maps to the ATA shadow register block, including
+ * registers which can be written twice to store 16 bits (called 'exp').
+ *
+ * The FIS structure looks like this (using LSB notation):
+ *
+ *       +----------------+----------------+----------------+----------------+
+ *  00   | FIS type (27h) |   C|R|R|R|PMP  |   Command      |  Features      |
+ *       +----------------+----------------+----------------+----------------+
+ *  04   |   LBA 7:0      |   LBA 15:8     |   LBA 23:16    |  R|R|R|D|Head  |
+ *       +----------------+----------------+----------------+----------------+
+ *  08   |   LBA 31:24    |   LBA 40:32    |   LBA 47:40    |  Features exp  |
+ *       +----------------+----------------+----------------+----------------+
+ *  12   |   Count 7:0    |   Count 15:8   |   Reserved     |  Control       |
+ *       +----------------+----------------+----------------+----------------+
+ *  16   |   Reserved     |   Reserved     |   Reserved     |  Reserved      |
+ *       +----------------+----------------+----------------+----------------+
+ */
+void ata_cmd_to_fis(u8 _far *fis, ATA_CMD _far *ata_cmd, int d)
+{
+  fis[0]  = 0x27;                  /* register - host to device FIS */
+  fis[1]  = (u8) (d & 0xf);        /* port multiplier number */
+  fis[1] |= 0x80;                  /* bit 7 indicates Command FIS */
+  fis[2]  = (u8) ata_cmd->cmd;
+  fis[3]  = (u8) ata_cmd->features;
+
+  fis[4]  = (u8) ata_cmd->lba_l;
+  fis[5]  = (u8) (ata_cmd->lba_l >> 8);
+  fis[6]  = (u8) (ata_cmd->lba_l >> 16);
+  fis[7]  = (u8) ata_cmd->device;
+
+  fis[8]  = (u8) (ata_cmd->lba_l >> 24);
+  fis[9]  = (u8) ata_cmd->lba_h;
+  fis[10] = (u8) (ata_cmd->lba_h >> 8);
+  fis[11] = (u8) (ata_cmd->features >> 8);
+
+  fis[12] = (u8) ata_cmd->count;
+  fis[13] = (u8) (ata_cmd->count >> 8);
+}
+
+/******************************************************************************
+ * Get index in S/G list for the number of transferred sectors in the IORB.
+ *
+ * Returning io->cSGList indicates an error.
+ *
+ * NOTE: OS/2 makes sure S/G lists are set up such that entries at the HW
+ *       limit will never cross sector boundaries. This means that splitting
+ *       S/G lists into multiple commands can be done without editing the S/G
+ *       lists.
+ */
+u16 ata_get_sg_indx(IORB_EXECUTEIO _far *io)
+{
+  ULONG offset = io->BlocksXferred * io->BlockSize;
+  USHORT i;
+
+  for (i = 0; i < io->cSGList && offset > 0; i++) {
+    offset -= io->pSGList[i].XferBufLen;
+  }
+
+  return(i);
+}
+
+/******************************************************************************
+ * Get max S/G count which will fit into our HW S/G buffers. This function is
+ * called when the S/G list is too long and we need to split the IORB into
+ * multiple commands. It returns both the number of sectors and S/G list
+ * elements that we can handle in a single command.
+ *
+ * The parameter 'sg_indx' indicates the current start index in the S/G list
+ * (0 if this is the first command iteration).
+ *
+ * The parameter 'sg_max' is the return value of v_ata_cmd() and indicates
+ * how many S/G elements were successfully mapped. Whatever we return needs to
+ * be less or equal to this value.
+ *
+ * Returning 0 in *sg_cnt indicates an error.
+ *
+ * NOTE: OS/2 makes sure S/G lists are set up such that entries at HW limits
+ *       will never cross sector boundaries. This means that splitting S/G
+ *       lists into multiple commands can be done without editing S/G list
+ *       elements. Since AHCI only allows 22 bits for each S/G element, the
+ *       hardware limits are reported as AHCI_MAX_SG / 2 but will vary based
+ *       on the actual length of S/G elements. This function looks for the
+ *       maximum number of S/G elements that can be mapped on sector
+ *       boundaries which will still fit into our HW S/G list.
+ */
+void ata_max_sg_cnt(IORB_EXECUTEIO _far *io, USHORT sg_indx, USHORT sg_max,
+                    USHORT _far *sg_cnt, USHORT _far *sector_cnt)
+{
+  ULONG max_sector_cnt = 0;
+  USHORT max_sg_cnt = 0;
+  ULONG offset = 0;
+  USHORT i;
+
+  for (i = sg_indx; i < io->cSGList; i++) {
+    if (i - sg_indx >= sg_max) {
+      /* we're beyond the number of S/G elements we can map */
+      break;
+    }
+
+    offset += io->pSGList[i].XferBufLen;
+    if (offset % io->BlockSize == 0) {
+      /* this S/G element ends on a sector boundary */
+      max_sector_cnt = offset / io->BlockSize;
+      max_sg_cnt = i + 1;
+    }
+  }
+
+  /* return the best match we found so far (0 indicating failure) */
+  *sector_cnt = max_sector_cnt;
+  *sg_cnt = max_sg_cnt;
+}
+
+
+/******************************************************************************
+ * Get device or media geometry. Device and media geometry are expected to be
+ * the same for non-removable devices, which will always be the case for the
+ * ATA devices we're dealing with (hard disks). ATAPI is a different story
+ * and handled by atapi_get_geometry().
+ */
+int ata_get_geometry(IORBH _far *iorb, int slot)
+{
+  ADD_WORKSPACE _far *aws = add_workspace(iorb);
+  int rc;
+
+  /* allocate buffer for ATA identify information */
+  if ((aws->buf = malloc(ATA_ID_WORDS * sizeof(u16))) == NULL) {
+    iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
+    return(-1);
+  }
+
+  /* request ATA identify information */
+  aws->ppfunc = ata_get_geometry_pp;
+  rc = ata_cmd(ad_infos + iorb_unit_adapter(iorb),
+               iorb_unit_port(iorb),
+               iorb_unit_device(iorb),
+               slot,
+               ATA_CMD_ID_ATA,
+               AP_VADDR, (void _far *) aws->buf, ATA_ID_WORDS * sizeof(u16),
+               AP_END);
+
+  if (rc != 0) {
+    free(aws->buf);
+    iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE);
+  }
+
+  return(rc);
+}
+
+/******************************************************************************
+ * Post processing function for ata_get_geometry(): convert the ATA identify
+ * information to OS/2 IOCC_GEOMETRY information.
+ */
+void ata_get_geometry_pp(IORBH _far *iorb)
+{
+  GEOMETRY _far *geometry = ((IORB_GEOMETRY _far *) iorb)->pGeometry;
+  USHORT geometry_len =  ((IORB_GEOMETRY _far *) iorb)->GeometryLen;
+  u16 *id_buf = add_workspace(iorb)->buf;
+
+  /* Fill-in geometry information; os2ahci has been designed for devices
+   * adhering to the ATA spec v8 or later, thus the real geometry doesn't
+   * really matter (it's actually marked "obsolete" in the ATA 8 specs).
+   * In order to maintain compatibitily to the BIOS and partition tables,
+   * we'll use the same algorithm as used by typical PC BIOS versions:
+   *
+   *  - 512 bytes per sector
+   *  - 255 heads
+   *  -  63 sectors per track
+   *  -   x cylinders (calculated)
+   *
+   * Please note that os2ahci currently does not support ATA sectors larger
+   * than 512 bytes, therefore relies on the translation logic built into the
+   * corresponding ATA disks. In theory, partitions should be aligned to the
+   * large sectors to prevent needless mapping all over the place but HPFS
+   * uses logical block sizes smaller than the typical large sectors found on
+   * modern hard disks so this won't make much of a difference. Large sector
+   * support will be evaluated at a later time (it's unclear right now whether
+   * HPFS would even support anything larger than 512 bytes).
+   *
+   * Another limitation is that OS/2 has a 32-bit variable for the total number
+   * of sectors, limiting the maximum capacity to roughly 2TB. This is another
+   * issue that needs to be addressed sooner or later; large sectors could
+   * raise this limit to something like 8TB but this is not really much of a
+   * difference. Maybe there's something in later DDKs that allows more than
+   * 32 bits?
+   */
+  memset(geometry, 0x00, geometry_len);
+
+  /* extract total number of sectors */
+  if (id_buf[ATA_ID_CFS_ENABLE_2] & 0x40) {
+    /* 48-bit LBA supported */
+    if (id_buf[ATA_ID_LBA_CAPACITY_2 + 2] != 0) {
+      /* more than 32 bits for number of sectors */
+      dprintf("warning: limiting disk %d.%d.%d to 2TB\n",
+              iorb_unit_adapter(iorb), iorb_unit_port(iorb),
+              iorb_unit_device(iorb));
+      geometry->TotalSectors = 0xffffffffUL;
+    } else {
+      geometry->TotalSectors = *((u32 *) (id_buf + ATA_ID_LBA_CAPACITY_2));
+    }
+  } else {
+    /* 28-bit LBA */
+    geometry->TotalSectors = *((u32 *) (id_buf + ATA_ID_LBA_CAPACITY)) &
+                             0x0fffffffUL;
+  }
+
+  geometry->BytesPerSector  = 512;
+  geometry->NumHeads        = 255;
+  geometry->SectorsPerTrack = 63;
+  geometry->TotalCylinders  = geometry->TotalSectors /
+                               ((u32) geometry->NumHeads *
+                                (u32) geometry->SectorsPerTrack);
+
+  /* tell interrupt handler that this IORB is complete */
+  add_workspace(iorb)->complete = 1;
+}
+
+/******************************************************************************
+ * Test whether unit is ready.
+ */
+int ata_unit_ready(IORBH _far *iorb, int slot)
+{
+  /* This is a NOP for ATA devices (at least right now); returning an error
+   * without setting an error code means ahci_exec_iorb() will not queue any
+   * HW command and the IORB will complete successfully.
+   */
+  ((IORB_UNIT_STATUS _far *) iorb)->UnitStatus = US_READY | US_POWER;
+  return(-1);
+}
+
+/******************************************************************************
+ * Read sectors from AHCI device.
+ */
+int ata_read(IORBH _far *iorb, int slot)
+{
+  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  ULONG sector = io->RBA + io->BlocksXferred;
+  USHORT count = io->BlockCount - io->BlocksXferred;
+  USHORT sg_indx;
+  USHORT sg_cnt;
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+  int rc;
+
+  /* prepare read command while keeping an eye on S/G count limitations */
+  do {
+    sg_indx = ata_get_sg_indx(io);
+    sg_cnt = io->cSGList - sg_indx;
+
+    if (sector >= (1UL << 28) || count > 256 || add_workspace(iorb)->is_ncq) {
+      /* need LBA48 for this command */
+      if (!ai->ports[p].devs[d].lba48) {
+        iorb_seterr(iorb, IOERR_RBA_LIMIT);
+        return(-1);
+      }
+      if (add_workspace(iorb)->is_ncq) {
+        /* use NCQ read; count goes into feature register, tag into count! */
+        rc = ata_cmd(ai, p, d, slot, ATA_CMD_FPDMA_READ,
+                     AP_SECTOR_48, (u32) sector, (u16) 0,
+                     AP_FEATURES,  (u16) count,
+                     AP_COUNT,     (u16) slot, /* tag = slot */
+                     AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
+                     AP_DEVICE,    0x4000,
+                     AP_END);
+      } else {
+        rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ_EXT,
+                     AP_SECTOR_48, (u32) sector, (u16) 0,
+                     AP_COUNT,     (u16) count,
+                     AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
+                     AP_DEVICE,    0x4000,
+                     AP_END);
+      }
+
+    } else {
+      rc = ata_cmd(ai, p, d, slot, ATA_CMD_READ,
+                   AP_SECTOR_28, (u32) sector,
+                   AP_COUNT,     (u16) count & 0xffU,
+                   AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
+                   AP_DEVICE,    0x4000,
+                   AP_END);
+    }
+
+    if (rc > 0) {
+      /* couldn't map all S/G elements */
+      ata_max_sg_cnt(io, sg_indx, (USHORT) rc, &sg_cnt, &count);
+    }
+  } while (rc > 0 && sg_cnt > 0);
+
+  if (rc == 0) {
+    add_workspace(iorb)->blocks = count;
+    add_workspace(iorb)->ppfunc = ata_read_pp;
+
+  } else if (rc > 0) {
+    iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD);
+
+  } else {
+    iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE);
+  }
+
+  return(rc);
+}
+
+/******************************************************************************
+ * Post processing function for ata_read(); this function updates the
+ * BlocksXferred counter in the IORB and, if not all blocks have been
+ * transferred, requeues the IORB to process the remaining sectors.
+ */
+void ata_read_pp(IORBH _far *iorb)
+{
+  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
+
+  io->BlocksXferred += add_workspace(iorb)->blocks;
+  ddprintf("ata_read_pp(): blocks transferred = %d\n", (int) io->BlocksXferred);
+
+  if (io->BlocksXferred >= io->BlockCount) {
+    /* we're done; tell IRQ handler the IORB is complete */
+    add_workspace(iorb)->complete = 1;
+  } else {
+    /* requeue this IORB for next iteration */
+    iorb_requeue(iorb);
+  }
+}
+
+/******************************************************************************
+ * Verify readability of sectors on ATA device.
+ */
+int ata_verify(IORBH _far *iorb, int slot)
+{
+  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+  int rc;
+
+  /* prepare verify command */
+  if (io->RBA >= (1UL << 28) || io->BlockCount > 256) {
+    /* need LBA48 for this command */
+    if (!ai->ports[p].devs[d].lba48) {
+      iorb_seterr(iorb, IOERR_RBA_LIMIT);
+      return(-1);
+    }
+    rc = ata_cmd(ai, p, d, slot, ATA_CMD_VERIFY_EXT,
+                 AP_SECTOR_48, (u32) io->RBA, (u16) 0,
+                 AP_COUNT,     (u16) io->BlockCount,
+                 AP_DEVICE,    0x4000,
+                 AP_END);
+  } else {
+    rc = ata_cmd(ai, p, d, slot, ATA_CMD_VERIFY,
+                 AP_SECTOR_28, (u32) io->RBA,
+                 AP_COUNT,     (u16) io->BlockCount & 0xffU,
+                 AP_END);
+  }
+
+  return(rc);
+}
+
+/******************************************************************************
+ * Write sectors to AHCI device.
+ */
+int ata_write(IORBH _far *iorb, int slot)
+{
+  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  ULONG sector = io->RBA + io->BlocksXferred;
+  USHORT count = io->BlockCount - io->BlocksXferred;
+  USHORT sg_indx;
+  USHORT sg_cnt;
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+  int rc;
+
+  /* prepare write command while keeping an eye on S/G count limitations */
+  do {
+    sg_indx = ata_get_sg_indx(io);
+    sg_cnt = io->cSGList - sg_indx;
+
+    if (sector >= (1UL << 28) || count > 256 || add_workspace(iorb)->is_ncq) {
+      /* need LBA48 for this command */
+      if (!ai->ports[p].devs[d].lba48) {
+        iorb_seterr(iorb, IOERR_RBA_LIMIT);
+        return(-1);
+      }
+      if (add_workspace(iorb)->is_ncq) {
+        /* use NCQ write; count goes into feature register, tag into count! */
+        rc = ata_cmd(ai, p, d, slot, ATA_CMD_FPDMA_WRITE,
+                     AP_SECTOR_48, (u32) sector, (u16) 0,
+                     AP_FEATURES,  (u16) count,
+                     AP_COUNT,     (u16) slot, /* tag = slot */
+                     AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
+                     AP_DEVICE,    0xc000,   /* force unit access (FUA) */
+                     AP_WRITE,     1,
+                     AP_END);
+      } else {
+        rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE_EXT,
+                     AP_SECTOR_48, (u32) sector, (u16) 0,
+                     AP_COUNT,     (u16) count,
+                     AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
+                     AP_DEVICE,    0x4000,
+                     AP_WRITE,     1,
+                     AP_END);
+      }
+      
+    } else {
+      rc = ata_cmd(ai, p, d, slot, ATA_CMD_WRITE,
+                   AP_SECTOR_28, (u32) sector,
+                   AP_COUNT,     (u16) count & 0xffU,
+                   AP_SGLIST,    io->pSGList + sg_indx, (u16) sg_cnt,
+                   AP_DEVICE,    0x4000,
+                   AP_WRITE,
+                   AP_END);
+    }
+
+    if (rc > 0) {
+      /* couldn't map all S/G elements */
+      ata_max_sg_cnt(io, sg_indx, (USHORT) rc, &sg_cnt, &count);
+    }
+  } while (rc > 0 && sg_cnt > 0);
+
+  if (rc == 0) {
+    add_workspace(iorb)->blocks = count;
+    add_workspace(iorb)->ppfunc = ata_write_pp;
+
+  } else if (rc > 0) {
+    iorb_seterr(iorb, IOERR_CMD_SGLIST_BAD);
+
+  } else {
+    iorb_seterr(iorb, IOERR_CMD_ADD_SOFTWARE_FAILURE);
+  }
+
+  return(rc);
+}
+
+/******************************************************************************
+ * Post processing function for ata_write(); this function updates the
+ * BlocksXferred counter in the IORB and, if not all blocks have been
+ * transferred, requeues the IORB to process the remaining sectors.
+ */
+void ata_write_pp(IORBH _far *iorb)
+{
+  IORB_EXECUTEIO _far *io = (IORB_EXECUTEIO _far *) iorb;
+
+  io->BlocksXferred += add_workspace(iorb)->blocks;
+  ddprintf("ata_write_pp(): blocks transferred = %d\n", (int) io->BlocksXferred);
+
+  if (io->BlocksXferred >= io->BlockCount) {
+    /* we're done; tell IRQ handler the IORB is complete */
+    add_workspace(iorb)->complete = 1;
+  } else {
+    /* requeue this IORB for next iteration */
+    iorb_requeue(iorb);
+  }
+}
+
+/******************************************************************************
+ * Execute ATA command.
+ */
+int ata_execute_ata(IORBH _far *iorb, int slot)
+{
+  IORB_ADAPTER_PASSTHRU _far *apt = (IORB_ADAPTER_PASSTHRU _far *) iorb;
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+  int rc;
+
+  if (apt->ControllerCmdLen != sizeof(ATA_CMD)) {
+    iorb_seterr(iorb, IOERR_CMD_SYNTAX);
+    return(-1);
+  }
+
+  rc = ata_cmd(ai, p, d, slot, 0,
+               AP_SGLIST,   apt->pSGList, apt->ppSGLIST,
+               AP_ATA_CMD,  apt->pControllerCmd,
+               AP_WRITE,    !(apt->Flags & PT_DIRECTION_IN),
+               AP_END);
+
+  return(rc);
+}
+
+/******************************************************************************
+ * Request sense information for a failed command. Since there is no "request
+ * sense" command for ATA devices, we need to read the current error code from
+ * the AHCI task file register and fabricate the sense information.
+ *
+ * NOTES:
+ *
+ *   - This function must be called right after an ATA command has failed and
+ *     before any other commands are queued on the corresponding port. This
+ *     function is typically called in the port restart context hook which is
+ *     triggered by an AHCI error interrupt.
+ *
+ *   - The ATA error bits are a complete mess. We'll try and catch the most
+ *     interesting error codes (such as medium errors) and report everything
+ *     else with a generic error code.
+ */
+int ata_req_sense(IORBH _far *iorb, int slot)
+{
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  u8 _far *port_mmio = port_base(ai, iorb_unit_port(iorb));
+  u32 tf_data = readl(port_mmio + PORT_TFDATA);
+  u8 err = (u8) (tf_data >> 8);
+  u8 sts = (u8) (tf_data);
+
+  if (sts & ATA_ERR) {
+    if (sts & ATA_DF) {
+      /* there is a device-specific error condition */
+      if (err & ATA_ICRC) {
+        iorb_seterr(iorb, IOERR_ADAPTER_DEVICEBUSCHECK);
+      } else if (err & ATA_UNC) {
+        iorb_seterr(iorb, IOERR_MEDIA);
+      } else if (err & ATA_IDNF) {
+        iorb_seterr(iorb, IOERR_RBA_ADDRESSING_ERROR);
+      } else {
+        iorb_seterr(iorb, IOERR_DEVICE_NONSPECIFIC);
+      }
+    
+    } else {
+      iorb_seterr(iorb, IOERR_DEVICE_NONSPECIFIC);
+    }
+  }
+
+  /* Return an error to indicate there's no HW command to be submitted and
+   * that the IORB can be completed "as is" (the upstream code expects the
+   * IORB error code, if any, to be set when this happens and this is exactly
+   * what this function is all about).
+   */
+  return(-1);
+}
+
+/******************************************************************************
+ * Extract vendor and device name from an ATA INDENTIFY buffer. Since strings
+ * in the indentify buffer are byte-swapped, we need to swap them back.
+ */
+char *ata_dev_name(u16 *id_buf)
+{
+  static char dev_name[ATA_ID_PROD_LEN + 1];
+  char *t = dev_name;
+  char *s = (char *) (id_buf + ATA_ID_PROD);
+  int i;
+
+  dev_name[sizeof(dev_name)-1] = '\0';
+
+  for (i = 0; i < ATA_ID_PROD_LEN / 2; i++) {
+    *(t++) = s[1];
+    *(t++) = s[0];
+    s += 2;
+  }
+
+  return(dev_name);
+}
+
Index: trunk/src/os2ahci/ata.h
===================================================================
--- trunk/src/os2ahci/ata.h	(revision 13)
+++ trunk/src/os2ahci/ata.h	(revision 13)
@@ -0,0 +1,462 @@
+/******************************************************************************
+ * ata.h - ATA structures and macros for os2ahci driver
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* -------------------------- macros and constants ------------------------- */
+
+/******************************************************************************
+ * ATA IDENTIFY offsets and constants. The ATA IDENTIFY response is a bit of a
+ * mess and after struggling to put this into some structure, I discovered the
+ * Linux folks simply defined offsets and constants to those fields which are
+ * actually needed. Thus, I copied the relevant bits from ata.h, replacing
+ * the enum with precompiler definitions because our enums are signed 16 bits.
+ *
+ * Please note that strings such as the product name are stored using byte-
+ * swapped 16-bit words.
+ */
+#define ATA_ID_WORDS            256
+#define ATA_ID_CONFIG           0
+#define ATA_ID_CYLS             1
+#define ATA_ID_HEADS            3
+#define ATA_ID_SECTORS          6
+#define ATA_ID_SERNO            10
+#define ATA_ID_BUF_SIZE         21
+#define ATA_ID_FW_REV           23
+#define ATA_ID_PROD             27
+#define ATA_ID_MAX_MULTSECT     47
+#define ATA_ID_DWORD_IO         48
+#define ATA_ID_CAPABILITY       49
+#define ATA_ID_OLD_PIO_MODES    51
+#define ATA_ID_OLD_DMA_MODES    52
+#define ATA_ID_FIELD_VALID      53
+#define ATA_ID_CUR_CYLS         54
+#define ATA_ID_CUR_HEADS        55
+#define ATA_ID_CUR_SECTORS      56
+#define ATA_ID_MULTSECT         59
+#define ATA_ID_LBA_CAPACITY     60
+#define ATA_ID_SWDMA_MODES      62
+#define ATA_ID_MWDMA_MODES      63
+#define ATA_ID_PIO_MODES        64
+#define ATA_ID_EIDE_DMA_MIN     65
+#define ATA_ID_EIDE_DMA_TIME    66
+#define ATA_ID_EIDE_PIO         67
+#define ATA_ID_EIDE_PIO_IORDY   68
+#define ATA_ID_ADDITIONAL_SUPP  69
+#define ATA_ID_QUEUE_DEPTH      75
+#define ATA_ID_MAJOR_VER        80
+#define ATA_ID_COMMAND_SET_1    82
+#define ATA_ID_COMMAND_SET_2    83
+#define ATA_ID_CFSSE            84
+#define ATA_ID_CFSSE            84
+#define ATA_ID_CFS_ENABLE_1     85
+#define ATA_ID_CFS_ENABLE_2     86
+#define ATA_ID_CSF_DEFAULT      87
+#define ATA_ID_UDMA_MODES       88
+#define ATA_ID_HW_CONFIG        93
+#define ATA_ID_SPG              98
+#define ATA_ID_LBA_CAPACITY_2   100
+#define ATA_ID_SECTOR_SIZE      106
+#define ATA_ID_LAST_LUN         126
+#define ATA_ID_DLF              128
+#define ATA_ID_CSFO             129
+#define ATA_ID_CFA_POWER        160
+#define ATA_ID_CFA_KEY_MGMT     162
+#define ATA_ID_CFA_MODES        163
+#define ATA_ID_DATA_SET_MGMT    169
+#define ATA_ID_ROT_SPEED        217
+#define ATA_ID_PIO4             (1U << 1)
+
+#define ATA_ID_SERNO_LEN        20
+#define ATA_ID_FW_REV_LEN       8
+#define ATA_ID_PROD_LEN         40
+
+#define ATA_PCI_CTL_OFS         2
+
+#define ATA_PIO0                (1U << 0)
+#define ATA_PIO1                ATA_PIO0 | (1U << 1)
+#define ATA_PIO2                ATA_PIO1 | (1U << 2)
+#define ATA_PIO3                ATA_PIO2 | (1U << 3)
+#define ATA_PIO4                ATA_PIO3 | (1U << 4)
+#define ATA_PIO5                ATA_PIO4 | (1U << 5)
+#define ATA_PIO6                ATA_PIO5 | (1U << 6)
+
+#define ATA_PIO4_ONLY           (1U << 4)
+
+#define ATA_SWDMA0              (1U << 0)
+#define ATA_SWDMA1              ATA_SWDMA0 | (1U << 1)
+#define ATA_SWDMA2              ATA_SWDMA1 | (1U << 2)
+
+#define ATA_SWDMA2_ONLY         (1U << 2)
+
+#define ATA_MWDMA0              (1U << 0)
+#define ATA_MWDMA1              ATA_MWDMA0 | (1U << 1)
+#define ATA_MWDMA2              ATA_MWDMA1 | (1U << 2)
+#define ATA_MWDMA3              ATA_MWDMA2 | (1U << 3)
+#define ATA_MWDMA4              ATA_MWDMA3 | (1U << 4)
+
+#define ATA_MWDMA12_ONLY        (1U << 1) | (1U << 2)
+#define ATA_MWDMA2_ONLY         (1U << 2)
+
+#define ATA_UDMA0               (1U << 0)
+#define ATA_UDMA1               ATA_UDMA0 | (1U << 1)
+#define ATA_UDMA2               ATA_UDMA1 | (1U << 2)
+#define ATA_UDMA3               ATA_UDMA2 | (1U << 3)
+#define ATA_UDMA4               ATA_UDMA3 | (1U << 4)
+#define ATA_UDMA5               ATA_UDMA4 | (1U << 5)
+#define ATA_UDMA6               ATA_UDMA5 | (1U << 6)
+
+/******************************************************************************
+ * Miscellanous constants copied from the Linux LIBATA code. Again, the enums
+ * have been converted to preprocessor definitions to avoid problems with
+ * signed 16-bit integers.
+ *
+ * Please note that Linux supports all kinds of IDE/EIDE/ATA/SATA/... devices
+ * in LIBATA while we only need the ATA-8 bits, and those only as far as they
+ * are relevant to AHCI.
+ */
+
+/* bits in ATA command block registers */
+#define ATA_HOB                 (1U << 7)     /* LBA48 selector */
+#define ATA_NIEN                (1U << 1)     /* disable-irq flag */
+#define ATA_LBA                 (1U << 6)     /* LBA28 selector */
+#define ATA_DEV1                (1U << 4)     /* Select Device 1 (slave) */
+#define ATA_DEVICE_OBS          (1U << 7) | (1U << 5) /* obs bits in dev reg */
+#define ATA_DEVCTL_OBS          (1U << 3)     /* obsolete bit in devctl reg */
+#define ATA_BUSY                (1U << 7)     /* BSY status bit */
+#define ATA_DRDY                (1U << 6)     /* device ready */
+#define ATA_DF                  (1U << 5)     /* device fault */
+#define ATA_DSC                 (1U << 4)     /* drive seek complete */
+#define ATA_DRQ                 (1U << 3)     /* data request i/o */
+#define ATA_CORR                (1U << 2)     /* corrected data error */
+#define ATA_IDX                 (1U << 1)     /* index */
+#define ATA_ERR                 (1U << 0)     /* have an error */
+#define ATA_SRST                (1U << 2)     /* software reset */
+#define ATA_ICRC                (1U << 7)     /* interface CRC error */
+#define ATA_BBK                 ATA_ICRC      /* pre-EIDE: block marked bad */
+#define ATA_UNC                 (1U << 6)     /* uncorrectable media error */
+#define ATA_MC                  (1U << 5)     /* media changed */
+#define ATA_IDNF                (1U << 4)     /* ID not found */
+#define ATA_MCR                 (1U << 3)     /* media change requested */
+#define ATA_ABORTED             (1U << 2)     /* command aborted */
+#define ATA_TRK0NF              (1U << 1)     /* track 0 not found */
+#define ATA_AMNF                (1U << 0)     /* address mark not found */
+#define ATAPI_LFS               0xF0U         /* last failed sense */
+#define ATAPI_EOM               ATA_TRK0NF    /* end of media */
+#define ATAPI_ILI               ATA_AMNF      /* illegal length indication */
+#define ATAPI_IO                (1U << 1)
+#define ATAPI_COD               (1U << 0)
+
+/* ATA device commands */
+#define ATA_CMD_DEV_RESET               0x08  /* ATAPI device reset */
+#define ATA_CMD_CHK_POWER               0xE5  /* check power mode */
+#define ATA_CMD_STANDBY                 0xE2  /* place in standby power mode */
+#define ATA_CMD_IDLE                    0xE3  /* place in idle power mode */
+#define ATA_CMD_EDD                     0x90  /* execute device diagnostic */
+#define ATA_CMD_DOWNLOAD_MICRO          0x92 
+#define ATA_CMD_NOP                     0x00 
+#define ATA_CMD_FLUSH                   0xE7 
+#define ATA_CMD_FLUSH_EXT               0xEA 
+#define ATA_CMD_ID_ATA                  0xEC 
+#define ATA_CMD_ID_ATAPI                0xA1 
+#define ATA_CMD_SERVICE                 0xA2 
+#define ATA_CMD_READ                    0xC8 
+#define ATA_CMD_READ_EXT                0x25 
+#define ATA_CMD_READ_QUEUED             0x26 
+#define ATA_CMD_READ_STREAM_EXT         0x2B 
+#define ATA_CMD_READ_STREAM_DMA_EXT     0x2A 
+#define ATA_CMD_WRITE                   0xCA 
+#define ATA_CMD_WRITE_EXT               0x35 
+#define ATA_CMD_WRITE_QUEUED            0x36 
+#define ATA_CMD_WRITE_STREAM_EXT        0x3B 
+#define ATA_CMD_WRITE_STREAM_DMA_EXT    0x3A 
+#define ATA_CMD_WRITE_FUA_EXT           0x3D 
+#define ATA_CMD_WRITE_QUEUED_FUA_EXT    0x3E 
+#define ATA_CMD_FPDMA_READ              0x60 
+#define ATA_CMD_FPDMA_WRITE             0x61 
+#define ATA_CMD_PIO_READ                0x20 
+#define ATA_CMD_PIO_READ_EXT            0x24 
+#define ATA_CMD_PIO_WRITE               0x30 
+#define ATA_CMD_PIO_WRITE_EXT           0x34 
+#define ATA_CMD_READ_MULTI              0xC4 
+#define ATA_CMD_READ_MULTI_EXT          0x29 
+#define ATA_CMD_WRITE_MULTI             0xC5 
+#define ATA_CMD_WRITE_MULTI_EXT         0x39 
+#define ATA_CMD_WRITE_MULTI_FUA_EXT     0xCE 
+#define ATA_CMD_SET_FEATURES            0xEF 
+#define ATA_CMD_SET_MULTI               0xC6 
+#define ATA_CMD_PACKET                  0xA0 
+#define ATA_CMD_VERIFY                  0x40 
+#define ATA_CMD_VERIFY_EXT              0x42 
+#define ATA_CMD_WRITE_UNCORR_EXT        0x45 
+#define ATA_CMD_STANDBYNOW1             0xE0 
+#define ATA_CMD_IDLEIMMEDIATE           0xE1 
+#define ATA_CMD_SLEEP                   0xE6 
+#define ATA_CMD_INIT_DEV_PARAMS         0x91 
+#define ATA_CMD_READ_NATIVE_MAX         0xF8 
+#define ATA_CMD_READ_NATIVE_MAX_EXT     0x27 
+#define ATA_CMD_SET_MAX                 0xF9 
+#define ATA_CMD_SET_MAX_EXT             0x37 
+#define ATA_CMD_READ_LOG_EXT            0x2F 
+#define ATA_CMD_WRITE_LOG_EXT           0x3F 
+#define ATA_CMD_READ_LOG_DMA_EXT        0x47 
+#define ATA_CMD_WRITE_LOG_DMA_EXT       0x57 
+#define ATA_CMD_TRUSTED_RCV             0x5C 
+#define ATA_CMD_TRUSTED_RCV_DMA         0x5D 
+#define ATA_CMD_TRUSTED_SND             0x5E 
+#define ATA_CMD_TRUSTED_SND_DMA         0x5F 
+#define ATA_CMD_PMP_READ                0xE4 
+#define ATA_CMD_PMP_WRITE               0xE8 
+#define ATA_CMD_CONF_OVERLAY            0xB1 
+#define ATA_CMD_SEC_SET_PASS            0xF1 
+#define ATA_CMD_SEC_UNLOCK              0xF2 
+#define ATA_CMD_SEC_ERASE_PREP          0xF3 
+#define ATA_CMD_SEC_ERASE_UNIT          0xF4 
+#define ATA_CMD_SEC_FREEZE_LOCK         0xF5 
+#define ATA_CMD_SEC_DISABLE_PASS        0xF6 
+#define ATA_CMD_CONFIG_STREAM           0x51 
+#define ATA_CMD_SMART                   0xB0 
+#define ATA_CMD_MEDIA_LOCK              0xDE 
+#define ATA_CMD_MEDIA_UNLOCK            0xDF 
+#define ATA_CMD_DSM                     0x06 
+#define ATA_CMD_CHK_MED_CRD_TYP         0xD1 
+#define ATA_CMD_CFA_REQ_EXT_ERR         0x03 
+#define ATA_CMD_CFA_WRITE_NE            0x38 
+#define ATA_CMD_CFA_TRANS_SECT          0x87 
+#define ATA_CMD_CFA_ERASE               0xC0 
+#define ATA_CMD_CFA_WRITE_MULT_NE       0xCD 
+/* marked obsolete in the ATA/ATAPI-7 spec */
+#define ATA_CMD_RESTORE                 0x10 
+
+/* READ_LOG_EXT pages */
+#define ATA_LOG_SATA_NCQ                0x10 
+
+/* READ/WRITE LONG (obsolete) */
+#define ATA_CMD_READ_LONG               0x22 
+#define ATA_CMD_READ_LONG_ONCE          0x23 
+#define ATA_CMD_WRITE_LONG              0x32 
+#define ATA_CMD_WRITE_LONG_ONCE         0x33 
+
+/* SETFEATURES stuff */
+#define SETFEATURES_XFER        0x03 
+#define XFER_UDMA_7             0x47 
+#define XFER_UDMA_6             0x46 
+#define XFER_UDMA_5             0x45 
+#define XFER_UDMA_4             0x44 
+#define XFER_UDMA_3             0x43 
+#define XFER_UDMA_2             0x42 
+#define XFER_UDMA_1             0x41 
+#define XFER_UDMA_0             0x40 
+#define XFER_MW_DMA_4           0x24  /* CFA only */
+#define XFER_MW_DMA_3           0x23  /* CFA only */
+#define XFER_MW_DMA_2           0x22 
+#define XFER_MW_DMA_1           0x21 
+#define XFER_MW_DMA_0           0x20 
+#define XFER_SW_DMA_2           0x12 
+#define XFER_SW_DMA_1           0x11 
+#define XFER_SW_DMA_0           0x10 
+#define XFER_PIO_6              0x0E  /* CFA only */
+#define XFER_PIO_5              0x0D  /* CFA only */
+#define XFER_PIO_4              0x0C 
+#define XFER_PIO_3              0x0B 
+#define XFER_PIO_2              0x0A 
+#define XFER_PIO_1              0x09 
+#define XFER_PIO_0              0x08 
+#define XFER_PIO_SLOW           0x00 
+
+#define SETFEATURES_WC_ON       0x02  /* Enable write cache */
+#define SETFEATURES_WC_OFF      0x82  /* Disable write cache */
+
+/* Enable/Disable Automatic Acoustic Management */
+#define SETFEATURES_AAM_ON      0x42 
+#define SETFEATURES_AAM_OFF     0xC2 
+
+#define SETFEATURES_SPINUP      0x07  /* Spin-up drive */
+
+#define SETFEATURES_SATA_ENABLE  0x10  /* Enable use of SATA feature */
+#define SETFEATURES_SATA_DISABLE 0x90  /* Disable use of SATA feature */
+
+/* SETFEATURE Sector counts for SATA features */
+#define SATA_FPDMA_OFFSET       0x01  /* FPDMA non-zero buffer offsets */
+#define SATA_FPDMA_AA           0x02  /* FPDMA Setup FIS Auto-Activate */
+#define SATA_DIPM               0x03  /* Device Initiated Power Management */
+#define SATA_FPDMA_IN_ORDER     0x04  /* FPDMA in-order data delivery */
+#define SATA_AN                 0x05  /* Asynchronous Notification */
+#define SATA_SSP                0x06  /* Software Settings Preservation */
+
+/* feature values for SET_MAX */
+#define ATA_SET_MAX_ADDR        0x00 
+#define ATA_SET_MAX_PASSWD      0x01 
+#define ATA_SET_MAX_LOCK        0x02 
+#define ATA_SET_MAX_UNLOCK      0x03 
+#define ATA_SET_MAX_FREEZE_LOCK 0x04 
+
+/* feature values for DEVICE CONFIGURATION OVERLAY */
+#define ATA_DCO_RESTORE         0xC0 
+#define ATA_DCO_FREEZE_LOCK     0xC1 
+#define ATA_DCO_IDENTIFY        0xC2 
+#define ATA_DCO_SET             0xC3 
+
+/* feature values for SMART */
+#define ATA_SMART_ENABLE        0xD8 
+#define ATA_SMART_READ_VALUES   0xD0 
+#define ATA_SMART_READ_THRESHOLDS 0xD1 
+
+/* feature values for Data Set Management */
+#define ATA_DSM_TRIM            0x01 
+
+/* password used in LBA Mid / LBA High for executing SMART commands */
+#define ATA_SMART_LBAM_PASS     0x4F 
+#define ATA_SMART_LBAH_PASS     0xC2 
+
+/* ATAPI stuff */
+#define ATAPI_PKT_DMA           (1U << 0) 
+#define ATAPI_DMADIR            (1U << 2)      /* ATAPI data dir:
+                                                  0=to device, 1=to host */
+#define ATAPI_CDB_LEN           16
+
+/* PMP stuff */
+#define SATA_PMP_MAX_PORTS      15
+#define SATA_PMP_CTRL_PORT      15
+
+#define SATA_PMP_GSCR_DWORDS    128
+#define SATA_PMP_GSCR_PROD_ID   0
+#define SATA_PMP_GSCR_REV       1
+#define SATA_PMP_GSCR_PORT_INFO 2
+#define SATA_PMP_GSCR_ERROR     32
+#define SATA_PMP_GSCR_ERROR_EN  33
+#define SATA_PMP_GSCR_FEAT      64
+#define SATA_PMP_GSCR_FEAT_EN   96
+
+#define SATA_PMP_PSCR_STATUS    0
+#define SATA_PMP_PSCR_ERROR     1
+#define SATA_PMP_PSCR_CONTROL   2
+
+#define SATA_PMP_FEAT_BIST      (1U << 0)
+#define SATA_PMP_FEAT_PMREQ     (1U << 1)
+#define SATA_PMP_FEAT_DYNSSC    (1U << 2)
+#define SATA_PMP_FEAT_NOTIFY    (1U << 3)
+
+/* SATA Status and Control Registers */
+#define SCR_STATUS              0
+#define SCR_ERROR               1
+#define SCR_CONTROL             2
+#define SCR_ACTIVE              3
+#define SCR_NOTIFICATION        4
+
+/* SError bits */
+#define SERR_DATA_RECOVERED     (1UL << 0)  /* recovered data error */
+#define SERR_COMM_RECOVERED     (1UL << 1)  /* recovered comm failure */
+#define SERR_DATA               (1UL << 8)  /* unrecovered data error */
+#define SERR_PERSISTENT         (1UL << 9)  /* persistent data/comm error */
+#define SERR_PROTOCOL           (1UL << 10)  /* protocol violation */
+#define SERR_INTERNAL           (1UL << 11)  /* host internal error */
+#define SERR_PHYRDY_CHG         (1UL << 16)  /* PHY RDY changed */
+#define SERR_PHY_INT_ERR        (1UL << 17)  /* PHY internal error */
+#define SERR_COMM_WAKE          (1UL << 18)  /* Comm wake */
+#define SERR_10B_8B_ERR         (1UL << 19)  /* 10b to 8b decode error */
+#define SERR_DISPARITY          (1UL << 20)  /* Disparity */
+#define SERR_CRC                (1UL << 21)  /* CRC error */
+#define SERR_HANDSHAKE          (1UL << 22)  /* Handshake error */
+#define SERR_LINK_SEQ_ERR       (1UL << 23)  /* Link sequence error */
+#define SERR_TRANS_ST_ERROR     (1UL << 24)  /* Transport state trans. error */
+#define SERR_UNRECOG_FIS        (1UL << 25)  /* Unrecognized FIS */
+#define SERR_DEV_XCHG           (1UL << 26)  /* device exchanged */
+
+/******************************************************************************
+ * Parameters for ATA commands. Those parameters are passed in a variable
+ * argument list in the format:
+ *
+ *   AP_xxx, [val, ...], AP_xxx, [val, ...]
+ *
+ * The values expected by each parameter are indicated within square brackets.
+ */
+typedef enum {
+  AP_FEATURES,     /* [u16]               ATA command features (read: flags) */
+  AP_COUNT,        /* [u16]               number of sectors (0 = 65536)      */
+  AP_SECTOR_28,    /* [u32]               28-bit sector address              */
+  AP_SECTOR_48,    /* [u32, u16]          48-bit sector address              */
+  AP_DEVICE,       /* [u16]               ATA cmd "device" field (LSB masked) */
+  AP_SGLIST,       /* [void _far *, u16]  buffer S/G (SCATGATENTRY/count)    */
+  AP_VADDR,        /* [void _far *, u16]  buffer virtual address (buf/len)   */
+  AP_WRITE,        /* [u16]               if != 0, data is written to device */
+  AP_AHCI_FLAGS,   /* [u16]               AHCI command header flags          */
+  AP_ATAPI_CMD,    /* [void _far *, u16]  ATAPI command (CDB) and length     */
+  AP_ATA_CMD,      /* [void _far *]       ATA command (fixed len)            */
+  AP_END           /* []                  end of variable argument list      */
+} ATA_PARM;
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/******************************************************************************
+ * Generic ATA-8 command structure. This is based on ATA-8, i.e. we won't have
+ * to deal with different command structures depending on whether a device is
+ * 48-bit capable, etc. because this is handled by the transport (AHCI in our
+ * case). Unsupported bits have to be cleared to zero, of course (e.g. the
+ * high 20 bits of the LBA when talking to a device that supports only 28
+ * bits).
+ */
+typedef struct {
+  u16  features;          /* feature bits */
+  u16  count;             /* count register (e.g. number of sectors) */
+  u32  lba_l;             /* low 28 bits of LBA (32 bits for 48-bit devices) */
+  u16  lba_h;             /* high 16 bits of LBA for 48-bit devices */
+  u8   cmd;               /* ATA command field */
+  u8   device;            /* ATA device field */
+} ATA_CMD;
+
+/* generic ATA-8 response structure */
+typedef struct {
+  u16  error;             /* error code, if any */
+  u16  count;             /* count register (e.g. number of sectors) */
+  u32  lba_l;             /* low 28 bits of LBA (32 bits for 48-bit devices) */
+  u16  lba_h;             /* high 16 bits of LBA for 48-bit devices */
+  u16  status;            /* response status bits */
+} ATA_RSP;
+
+/******************************************************************************
+ * Command-specific ATA-8 structures.
+ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+extern int       ata_cmd             (AD_INFO *ai, int port, int device,
+                                      int slot, int cmd, ...);
+extern int       v_ata_cmd           (AD_INFO *ai, int port, int device,
+                                      int slot, int cmd, va_list va);
+extern void      ata_cmd_to_fis      (u8 _far *fis, ATA_CMD _far *cmd,
+                                      int device);
+extern USHORT    ata_get_sg_indx     (IORB_EXECUTEIO _far *io);
+extern void      ata_max_sg_cnt      (IORB_EXECUTEIO _far *io, USHORT sg_indx,
+                                      USHORT sg_max, USHORT _far *sg_cnt,
+                                      USHORT _far *sector_cnt);
+
+extern int       ata_get_geometry    (IORBH _far *iorb, int slot);
+extern void      ata_get_geometry_pp (IORBH _far *iorb);
+extern int       ata_unit_ready      (IORBH _far *iorb, int slot);
+extern int       ata_read            (IORBH _far *iorb, int slot);
+extern void      ata_read_pp         (IORBH _far *iorb);
+extern int       ata_verify          (IORBH _far *iorb, int slot);
+extern int       ata_write           (IORBH _far *iorb, int slot);
+extern void      ata_write_pp        (IORBH _far *iorb);
+extern int       ata_execute_ata     (IORBH _far *iorb, int slot);
+extern int       ata_req_sense       (IORBH _far *iorb, int slot);
+
+extern char     *ata_dev_name        (u16 *id_buf);
+
Index: trunk/src/os2ahci/atapi.c
===================================================================
--- trunk/src/os2ahci/atapi.c	(revision 13)
+++ trunk/src/os2ahci/atapi.c	(revision 13)
@@ -0,0 +1,104 @@
+/******************************************************************************
+ * atapi.c - ATAPI command processing
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+#include "ata.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+/* ------------------------ global/static variables ------------------------ */
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Get device or media geometry. This function is expected to check the IORB
+ * command codes and modifiers to see which one is requested.
+ */
+int atapi_get_geometry(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}
+
+/******************************************************************************
+ * Test whether unit is ready.
+ */
+int atapi_unit_ready(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}
+
+/******************************************************************************
+ * Read sectors from AHCI device.
+ */
+int atapi_read(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}  
+
+/******************************************************************************
+ * Verify readability of sectors on AHCI device.
+ */
+int atapi_verify(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}
+
+/******************************************************************************
+ * Write sectors to AHCI device.
+ */
+int atapi_write(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}
+
+/******************************************************************************
+ * Execute ATAPI command.
+ */
+int atapi_execute_cdb(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}
+
+/******************************************************************************
+ * Request sense information for a failed command.
+ *
+ * NOTE: This function must be called right after an ATAPI command has failed
+ *       and before any other commands are queued on the corresponding device.
+ *       This function is typically called in the port restart context hook
+ *       which is triggered by an AHCI error interrupt.
+ *
+ */
+int atapi_req_sense(IORBH _far *iorb, int slot)
+{
+  iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+  return(-1);
+}
+
Index: trunk/src/os2ahci/atapi.h
===================================================================
--- trunk/src/os2ahci/atapi.h	(revision 13)
+++ trunk/src/os2ahci/atapi.h	(revision 13)
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * atapi.h - ATAPI structures and macros for os2ahci driver
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* ---------------------------- global variables --------------------------- */
+
+/* -------------------------- function prototypes -------------------------- */
+
+extern int     atapi_get_geometry     (IORBH _far *iorb, int slot);
+extern int     atapi_unit_ready       (IORBH _far *iorb, int slot);
+extern int     atapi_read             (IORBH _far *iorb, int slot);
+extern int     atapi_verify           (IORBH _far *iorb, int slot);
+extern int     atapi_write            (IORBH _far *iorb, int slot);
+extern int     atapi_execute_cdb      (IORBH _far *iorb, int slot);
+extern int     atapi_req_sense        (IORBH _far *iorb, int slot);
+
Index: trunk/src/os2ahci/ctxhook.c
===================================================================
--- trunk/src/os2ahci/ctxhook.c	(revision 13)
+++ trunk/src/os2ahci/ctxhook.c	(revision 13)
@@ -0,0 +1,409 @@
+/******************************************************************************
+ * ctxhook.c - context hooks (kernel thread functions) for os2ahci
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+#include "ata.h"
+#include "atapi.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+/* ------------------------ global/static variables ------------------------ */
+
+/* port restart context hook and input data */
+ULONG           restart_ctxhook_h;
+volatile u32    ports_to_restart[MAX_AD];
+
+/* port reset context hook and input data */
+ULONG           reset_ctxhook_h;
+volatile u32    ports_to_reset[MAX_AD];
+IORB_QUEUE      abort_queue;
+
+/* trigger engine context hook and input data */
+ULONG           engine_ctxhook_h;
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Port restart context hook. This context hook is executed at task time and
+ * will handle ports which are stopped due to a device error condition. 
+ *
+ * The following conditions may exist:
+ *
+ *  - Only a single non-NCQ command is executed by the AHCI adapter at any
+ *    given time (even if more are outstanding). This is the case for single
+ *    devices or port multipliers without FIS-based command switching. Error
+ *    recovery is simple because we know which command has failed and can
+ *    requeue all commands, replacing the failing command with a "request
+ *    sense" command to get error details.
+ *
+ *  - Multiple non-NCQ commands are executed on different devices behind a
+ *    port multiplier which supports FIS-based command switching. This is
+ *    more difficult to recover from but currently not an issue because we
+ *    don't yet support FIS-based command switching (the FIS receive areas
+ *    would become too large for the current data model).
+ *
+ *  - One or more NCQ commands were active at the time of the error, with or
+ *    without FIS-based command switching. We would have to interrogate the
+ *    corresponding devices to find out which command has failed but if this
+ *    is combined with FIS-based command switching, even the AHCI spec
+ *    recommends to reset the port. This leads to a much simpler approach:
+ *    requeue all NCQ commands (they are idempotent per definition, otherwise
+ *    they couldn't be reordered by the device) with the 'no_ncq' flag set
+ *    in the IORB and reset the port. Then those comands will be executed as
+ *    regular commands. The error, if it reoccurrs, can be then handled by
+ *    one of the above cases.
+ *
+ * The upstream code will guarantee that we will never have a mix of NCQ and
+ * non-NCQ commands active at the same time in order to reduce complexity
+ * in the interrupt and error handlers.
+ */
+void restart_ctxhook(ULONG parm)
+{
+  IORB_QUEUE done_queue;
+  AD_INFO *ai;
+  IORBH _far *problem_iorb;
+  IORBH _far *iorb;
+  IORBH _far *next = NULL;
+  u8 _far *port_mmio;
+  int rearm_ctx_hook = 0;
+  int need_reset;
+  int ccs;
+  int a;
+  int p;
+
+  dprintf("restart_ctxhook() started\n");
+  memset(&done_queue, 0x00, sizeof(done_queue));
+
+  spin_lock(drv_lock);
+
+  for (a = 0; a < ad_info_cnt; a++) {
+    ai = ad_infos + a;
+
+    if (ai->busy) {
+      /* this adapter is busy; leave it alone for now */
+      rearm_ctx_hook = 1;
+      continue;
+    }
+
+    for (p = 0; p <= ai->port_max; p++) {
+      if (ports_to_restart[a] & (1UL << p)) {
+        ports_to_restart[a] &= ~(1UL << p);
+
+        /* restart this port */
+        port_mmio = port_base(ai, p);
+        problem_iorb = NULL;
+        need_reset = 0;
+
+        /* get "current command slot"; only valid if there are no NCQ cmds */
+        ccs = (int) ((readl(port_mmio + PORT_CMD) >> 8) & 0x0f);
+
+        for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
+          ADD_WORKSPACE _far *aws = add_workspace(iorb);
+          next = iorb->pNxtIORB;
+
+          if (aws->queued_hw) {
+            if (ai->ports[p].ncq_cmds != 0) {
+              /* NCQ commands active; force non-NCQ mode and trigger port reset */
+              aws->no_ncq = 1;
+              need_reset = 1;
+            } else if (aws->cmd_slot == ccs) {
+              /* this is the non-NCQ comand that failed */
+              problem_iorb = iorb;
+            }
+            iorb_requeue(iorb);
+
+            /* remove requeued command from the issued command bitmaps */
+            ai->ports[p].ncq_cmds &= ~(1UL << p);
+            ai->ports[p].reg_cmds &= ~(1UL << p);
+          }
+        }
+
+        /* sanity check: issued command bitmaps should be 0 now */
+        if (ai->ports[p].ncq_cmds != 0 || ai->ports[p].reg_cmds != 0) {
+          dprintf("warning: commands issued not 0 (%08lx/%08lx)\n",
+                  ai->ports[p].ncq_cmds, ai->ports[p].reg_cmds);
+        }
+
+        if (!need_reset) {
+          if ((readl(port_mmio + PORT_TFDATA) & 0x88) != 0) {
+            /* device is not in an idle state */
+            need_reset = 1;
+          }
+        }
+
+        /* restart/reset port */
+        ai->busy = 1;
+        spin_unlock(drv_lock);
+        if (need_reset) {
+          ahci_reset_port(ai, p, 1);
+        } else {
+          ahci_stop_port(ai, p);
+          ahci_start_port(ai, p, 1);
+        }
+        spin_lock(drv_lock);
+        ai->busy = 0;
+
+        /* reset internal port status */
+        ai->ports[p].ncq_cmds = 0;
+        ai->ports[p].reg_cmds = 0;
+        ai->ports[p].cmd_slot = 0;
+
+        if (problem_iorb != NULL) {
+          /* get details about the error that caused this IORB to fail */
+          if (need_reset) {
+            /* no way to retrieve error details after a reset */
+            iorb_seterr(problem_iorb, IOERR_DEVICE_NONSPECIFIC);
+            iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
+            iorb_queue_add(&done_queue, problem_iorb);
+
+          } else {
+            /* get sense information */
+            ADD_WORKSPACE _far *aws = add_workspace(problem_iorb);
+            int d = iorb_unit_device(problem_iorb);
+            int (*req_sense)(IORBH _far *, int) = 
+              (ai->ports[p].devs[d].atapi) ? atapi_req_sense : ata_req_sense;
+
+            aws->processing = 1;
+            aws->queued_hw = 1;
+
+            if (req_sense(problem_iorb, 0) == 0) {
+              /* execute request sense on slot #0 before anything else comes along */
+              ai->ports[p].reg_cmds = 1;
+              writel(port_mmio + PORT_CMD_ISSUE, 1);
+              readl(port_mmio); /* flush */
+
+            } else {
+              /* IORB is expected to contain the error code; just move to done queue */
+              iorb_queue_del(&ai->ports[p].iorb_queue, problem_iorb);
+              iorb_queue_add(&done_queue, problem_iorb);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  spin_unlock(drv_lock);
+
+  /* call notification routine on all IORBs which have completed */
+  for (iorb = done_queue.root; iorb != NULL; iorb = next) {
+    next = iorb->pNxtIORB;
+    if (iorb->RequestControl & IORB_ASYNC_POST) {
+      iorb->NotifyAddress(iorb);
+    }
+  }
+
+  /* restart engine to resume IORB processing */
+  spin_lock(drv_lock);
+  trigger_engine();
+  spin_unlock(drv_lock);
+
+  dprintf("restart_ctxhook() completed\n");
+
+  /* Check whether we have to rearm ourselves because some adapters were busy
+   * when we wanted to restart ports on them.
+   */
+  if (rearm_ctx_hook) {
+    msleep(250);
+    DevHelp_ArmCtxHook(0, restart_ctxhook_h);
+  }
+}
+
+/******************************************************************************
+ * Reset and abort context hook. This function runs at task time and takes
+ * care of port resets and their side effects. Input to this function are:
+ *
+ *  ports_to_reset[]   - array of port bitmaps, each bit indicating which port
+ *                       should be reset unconditionally. This is primarily
+ *                       used by the error interrupt handler.
+ *
+ *  abort_queue        - queue with IORBs to be arborted (timed-out, ...) If
+ *                       any of these commands have reached the hardware, the
+ *                       corresponding port is reset to interrupt command
+ *                       execution. This is primarily used for timeout
+ *                       handling and when IORBs are requested to be aborted.
+ * 
+ * After resetting the requested ports, all remaining active IORBs on those
+ * ports have to be retried or aborted. Whether a retry is attempted depends
+ * on the kind of IORB -- those which are idempotent are retried, all others
+ * are aborted. This is different from the port restart hook because the
+ * restart hook can assume it is called with the port in error state, thus
+ * the controller will have stopped executing commands. The reset handler can
+ * be called at any time and we can't tell what's going on in the controller.
+ *
+ * The IORBs in the global abort_queue are expected to have their error code
+ * set (aborted, timeout, ...) but must not be marked as 'done'; otherwise,
+ * the upstream code might reuse the IORBs before we're done with them.
+ */
+void reset_ctxhook(ULONG parm)
+{
+  IORB_QUEUE done_queue;
+  AD_INFO *ai;
+  IORBH _far *iorb;
+  IORBH _far *next = NULL;
+  int rearm_ctx_hook = 0;
+  int a;
+  int p;
+
+  dprintf("reset_ctxhook() started\n");
+  memset(&done_queue, 0x00, sizeof(done_queue));
+
+  spin_lock(drv_lock);
+
+  /* add ports of active IORBs from the abort queue to ports_to_reset[] */
+  for (iorb = abort_queue.root; iorb != NULL; iorb = next) {
+    next = iorb->pNxtIORB;
+    a = iorb_unit_adapter(iorb);
+    p = iorb_unit_port(iorb);
+    ai = ad_infos + a;
+
+    if (ai->busy) {
+      /* this adapter is busy; leave it alone for now */
+      rearm_ctx_hook = 1;
+      continue;
+    }
+
+    /* move IORB to the local 'done' queue */
+    aws_free(add_workspace(iorb));
+    iorb_queue_del(&abort_queue, iorb);
+    iorb_queue_add(&done_queue, iorb);
+
+    /* reset port if the IORB has already been queued to hardware */
+    if (add_workspace(iorb)->queued_hw) {
+      /* prepare port reset */
+      ports_to_reset[a] |= (1UL << p);
+    }
+  }
+
+  /* reset all ports in 'ports_to_reset[]' */
+  for (a = 0; a < ad_info_cnt; a++) {
+    ai = ad_infos + a;
+
+    if (ai->busy) {
+      /* this adapter is busy; leave it alone for now */
+      rearm_ctx_hook = 1;
+      continue;
+    }
+
+    for (p = 0; p <= ai->port_max; p++) {
+      if (ports_to_reset[a] & (1UL << p)) {
+        ports_to_reset[a] &= ~(1UL << p);
+
+        /* Reset this port. Since this is a rather slow operation, we'll
+         * release the spinlock while doing so. The adapter is marked as
+         * 'busy' to prevent similar routines (e.g. an ahci port scan) from
+         * interfering. 
+         */
+        ai->busy = 1;
+        spin_unlock(drv_lock);
+        ahci_reset_port(ai, p, 1);
+        spin_lock(drv_lock);
+        ai->busy = 0;
+
+        /* reset port status */
+        ai->ports[p].ncq_cmds = 0;
+        ai->ports[p].reg_cmds = 0;
+        ai->ports[p].cmd_slot = 0;
+
+        /* retry or abort all remaining active commands on this port */
+        for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
+          next = iorb->pNxtIORB;
+          if (add_workspace(iorb)->queued_hw) {
+            /* this IORB had already been queued to HW when we reset the port */
+            aws_free(add_workspace(iorb));
+            if (add_workspace(iorb)->idempotent) {
+              /* We can retry this IORB; just reset its status and it will be
+               * picked up by subsequent trigger_engine() calls.
+               */
+              memset(&iorb->ADDWorkSpace, sizeof(iorb->ADDWorkSpace), 0x00);
+
+            } else {
+              /* we cannot retry this IORB; consider it aborted */
+              iorb->ErrorCode = IOERR_CMD_ABORTED;
+              iorb_queue_del(&ai->ports[p].iorb_queue, iorb);
+              iorb_queue_add(&done_queue, iorb);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  spin_unlock(drv_lock);
+
+  /* call notification routine on all aborted IORBs */
+  for (iorb = done_queue.root; iorb != NULL; iorb = next) {
+    next = iorb->pNxtIORB;
+    iorb->Status = IORB_DONE | IORB_ERROR;
+    if (iorb->RequestControl & IORB_ASYNC_POST) {
+      iorb->NotifyAddress(iorb);
+    }
+  }
+
+  /* restart engine to resume IORB processing */
+  spin_lock(drv_lock);
+  trigger_engine();
+  spin_unlock(drv_lock);
+
+  dprintf("reset_ctxhook() completed\n");
+
+  /* Check whether we have to rearm ourselves because some adapters were busy
+   * when we wanted to reset ports on them.
+   */
+  if (rearm_ctx_hook) {
+    msleep(250);
+    DevHelp_ArmCtxHook(0, reset_ctxhook_h);
+  }
+}
+
+/******************************************************************************
+ * IORB Engine context hook. This hook is executed if trigger_engine() came
+ * to the conclusion that some of the IORBs keep bouncing, most likely due to
+ * some condition on the adapter such as being busy. It could also be a very
+ * busy system. Either way, this requires some task-time help.
+ */
+void engine_ctxhook(ULONG parm)
+{
+  int iorbs_sent;
+  int i;
+
+  dprintf("engine_ctxhook() started\n");
+
+  spin_lock(drv_lock);
+  for (i = 0; i < 10; i++) {
+    if ((iorbs_sent = trigger_engine_1()) == 0) {
+      break;
+    }
+  }
+  spin_unlock(drv_lock);
+
+  dprintf("engine_ctxhook() completed\n");
+
+  if (iorbs_sent != 0) {
+    /* need to rearm ourselves for another run */
+    msleep(250);
+    DevHelp_ArmCtxHook(0, engine_ctxhook_h);
+  }
+}
Index: trunk/src/os2ahci/init.asm
===================================================================
--- trunk/src/os2ahci/init.asm	(revision 13)
+++ trunk/src/os2ahci/init.asm	(revision 13)
@@ -0,0 +1,334 @@
+; ----------------------------------------------------------------------------
+; Initialization routines and segment mappings for os2ahci driver, plus
+; some low-level functions that were implemented in assembler
+
+include devhdr.inc
+
+; -----------------------------------------------------------------------------
+; Public symbols
+
+                PUBLIC  _asm_strat           ; low-level strategy routine
+                PUBLIC  _readl               ; MMIO read (32 bits)
+                PUBLIC  _writel              ; MMIO write (32 bits)
+                PUBLIC  _memset              ; C memset() implementation
+                PUBLIC  _memcpy              ; C memcpy() implementation
+                PUBLIC  _restart_hook        ; port restart context hook
+                PUBLIC  _reset_hook          ; port reset context hook
+                PUBLIC  _engine_hook         ; engine trigger context hook
+                PUBLIC  _dev_hdr             ; device driver header
+                PUBLIC  _end_of_data         ; end of all data (label)
+                PUBLIC  _end_of_code         ; end of all code (label)
+
+; ----------------------------------------------------------------------------
+; Device Driver Header
+
+DEVHDR          SEGMENT WORD PUBLIC 'DATA'
+_dev_hdr        dd      -1                      ; no headers after this one
+                dw      DEVLEV_3 + DEV_CHAR_DEV ; flags for ADD drivers
+                dw      _asm_strat              ; strategy routine
+                dw      0                       ; no IDC routine
+                db      "OS2AHCI$"              ; name of character device
+                dq      0                       ; 8 reserved bytes
+                dd      DEV_ADAPTER_DD + DEV_INITCOMPLETE + 20h ; ADD flags
+                dw      0
+DEVHDR          ENDS
+
+; ----------------------------------------------------------------------------
+; Segment Definitions. We need to reference all segments here, even if they
+; are not used, to allow proper grouping of all segments into one group for
+; data and one group for code as well as proper ordering of the segments
+; within each group to make sure the end of segment markers are at the end
+; of each group.
+
+; On top of that, we need to make sure that the end of segment marker is in
+; a data segment of class 'BSS' because BSS segments are always put towards
+; the end of the executable when linking with high-level language objects
+; such as C.
+
+_DATA           SEGMENT WORD PUBLIC 'DATA'
+readl_dbg_fmt   db      "readl(%04x:%04x) = 0x%08lx [addr %% 0x80 = 0x%02x]"
+                db      10, 0
+writel_dbg_fmt  db      "writel(%04x:%04x, 0x%08lx) [addr %% 0x80 = 0x%02x]"
+                db      10, 0
+_DATA           ENDS
+
+CONST           SEGMENT WORD PUBLIC 'CONST'
+CONST           ENDS
+
+_BSS            SEGMENT WORD PUBLIC 'BSS'
+_BSS            ENDS
+
+c_common        SEGMENT WORD PUBLIC 'BSS'
+                EXTRN   _debug : WORD        ; global debug flag
+c_common        ENDS
+
+_z_data         SEGMENT WORD PUBLIC 'BSS'
+_end_of_data    db      0
+_z_data         ENDS
+
+_TEXT           SEGMENT WORD PUBLIC 'CODE'
+                EXTRN   _c_strat : NEAR           ; C strategy routine
+                EXTRN   _printf : NEAR            ; C printf routine
+                EXTRN   _restart_ctxhook : NEAR   ; C restart context hook
+                EXTRN   _reset_ctxhook : NEAR     ; C reset context hook
+                EXTRN   _engine_ctxhook : NEAR    ; C engine context hook
+_TEXT           ENDS
+
+CODE            SEGMENT WORD PUBLIC 'CODE'
+CODE            ENDS
+
+RMCode          SEGMENT WORD PUBLIC 'CODE'
+RMCode          ENDS
+
+LIBCODE         SEGMENT WORD PUBLIC 'CODE'
+LIBCODE         ENDS
+
+_z_text         SEGMENT WORD PUBLIC 'CODE'
+_end_of_code    LABEL NEAR
+_z_text         ENDS
+
+DGROUP          GROUP   DEVHDR, _DATA, CONST, _BSS, c_common, _z_data
+TGROUP          GROUP   _TEXT, CODE, _z_text
+
+; ----------------------------------------------------------------------------
+; Start of code
+
+_TEXT           SEGMENT WORD PUBLIC 'CODE'
+                ASSUME  DS:DGROUP
+                ASSUME  ES:NOTHING
+                ASSUME  SS:NOTHING
+
+
+; Device driver main entry point (strategy routine)
+_asm_strat      PROC    FAR
+
+                ; push request packet address
+                PUSH    ES
+                PUSH    BX
+                CLD
+
+                ; call C strategy routine
+                CALL    _c_strat
+
+                POP     BX
+                POP     ES
+                MOV     WORD PTR ES:[BX+3], AX
+                RET
+_asm_strat      ENDP
+
+                .386
+
+
+; Read long value from MMIO address; need to do this here to get real
+; 32-bit operations because at least the AHCI device in VirtualBox doesn't
+; seem to support reading 32-bit MMIO registers in two 16-bit steps.
+;
+; C prototype:  u32 readl(void _far *addr);
+_readl          PROC    NEAR
+                ENTER   0, 0
+
+                ; load 'addr' into ES:EAX
+                LES     AX, [BP+4]
+                AND     EAX, 0000FFFFh
+
+                ; read MMIO register into EDX and return it in DX:AX
+                MOV     EDX, ES:[EAX]
+
+                ; print debug message if debug level is 3+
+                CMP     _debug, 3
+                JB      no_debug
+                PUSH    EDX             ; save value read from MMIO port
+                MOV     BX, AX          ; addr & 0x7f (port reg index)
+                AND     BX, 7FH
+                PUSH    BX
+                PUSH    EDX             ; value read from MMIO address
+                PUSH    AX              ; offset of MMIO address
+                PUSH    ES              ; segment of MMIO address
+                PUSH    OFFSET readl_dbg_fmt
+                CALL    _printf
+                ADD     SP, 12
+                POP     EDX             ; restore value read from MMIO port
+
+no_debug:       MOV     EAX, EDX
+                SHR     EDX, 16
+
+                LEAVE
+                RET
+_readl          ENDP
+
+
+; Write long value to MMIO address; need to do this here to get real
+; 32-bit operations because at least the AHCI device in VirtualBox doesn't
+; seem to support reading 32-bit MMIO registers in two 16-bit steps and the
+; assumption is that this is the same with writing 32-bit MMIO registers.
+;
+; C prototype:  void writel(void _far *addr, u32 val);
+_writel         PROC    NEAR
+                ENTER   0, 0
+
+                ; load 'addr' into ES:EAX
+                LES     AX, [BP+4]
+                AND     EAX, 0000FFFFh
+
+                ; load 'val' into EDX, preserving the upper 16 bits of EBP
+                MOV     EBX, EBP
+                AND     EBP, 0000FFFFh
+                MOV     EDX, [EBP+8]
+                MOV     EBP, EBX
+
+                ; store 'val' at MMIO address in ES:EAX
+                MOV     DWORD PTR ES:[EAX], EDX
+
+                ; print debug message if debug level is 3+
+                CMP     _debug, 3
+                JB      no_debug
+                MOV     BX, AX          ; addr & 0x7f (port reg index)
+                AND     BX, 7FH
+                PUSH    BX
+                PUSH    EDX             ; value written to MMIO address
+                PUSH    AX              ; offset of MMIO address
+                PUSH    ES              ; segment of MMIO address
+                PUSH    OFFSET writel_dbg_fmt
+                CALL    _printf
+                ADD     SP, 12
+
+no_debug:       LEAVE
+                RET
+_writel         ENDP
+
+
+; Halfway-decent 32-bit implementation of memset().
+;
+; C prototype: void *memset(void _far *s, int c, size_t n);
+_memset         PROC    NEAR
+                ENTER   0, 0
+
+                PUSH    DI
+
+                ; load 's' into ES:EDI
+                LES     DI, [BP+4]
+                AND     EDI, 0000FFFFh
+
+                ; load 'c' into EAX, replicating the low 8 bits all over
+                MOV     BL, [BP+8]
+                MOV     BH, BL
+                MOV     AX, BX
+                SHL     EAX, 16
+                MOV     AX, BX
+
+                ; load 'n / 4' into ECX
+                MOV     CX, [BP+10]
+                SHR     CX, 2
+                AND     ECX, 0000FFFFh
+
+                ; fill 's' with 32-bit chunks of EAX
+                REP     STOSD
+
+                ; set remaining bytes (n % 4)'
+                MOV     CX, [BP+10]
+                AND     CX, 0003h
+                REP     STOSB
+
+                ; return address of 's'
+                LES     AX, [BP+4]
+                PUSH    ES
+                POP     DX
+
+                POP     DI
+
+                LEAVE
+                RET
+_memset         ENDP
+
+
+; Halfway-decent 32-bit implementation of memcpy(). 
+;
+; C prototype: void *memcpy(void _far *d, void _far *s, size_t n);
+_memcpy         PROC    NEAR
+                ENTER   0, 0
+
+                INT     3
+                
+                PUSH    SI
+                PUSH    DI
+                PUSH    DS
+
+                ; load 'd' into ES:EDI
+                LES     DI, [BP+4]
+                AND     EDI, 0000FFFFh
+
+                ; load 's' into DS:ESI
+                LDS     SI, [BP+8]
+                AND     ESI, 0000FFFFh
+
+                ; load 'n / 4' into ECX
+                MOV     CX, [BP+12]
+                SHR     CX, 2
+                AND     ECX, 0000FFFFh
+
+                ; copy 's' to 'd' in 32-bit chunks
+                REP     MOVSD
+
+                ; copy remaining bytes (n % 4)'
+                MOV     CX, [BP+12]
+                AND     CX, 0003h
+                REP     MOVSB
+
+                ; return address of 'd'
+                LES     AX, [BP+4]
+                PUSH    ES
+                POP     DX
+
+                POP     DS
+                POP     DI
+                POP     SI
+
+                LEAVE
+                RET
+_memcpy         ENDP
+
+
+; Port restart context hook; context hooks need to save all registers
+; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus
+; we need a stub which calls the real context hook.
+_restart_hook   PROC    FAR
+                PUSHAD
+                PUSH    EAX
+                CALL    _restart_ctxhook
+                POP     EAX
+                POPAD
+                RET
+_restart_hook   ENDP
+
+
+; Port reset context hook; context hooks need to save all registers
+; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus
+; we need a stub which calls the real context hook.
+_reset_hook     PROC    FAR
+                PUSHAD
+                PUSH    EAX
+                CALL    _reset_ctxhook
+                POP     EAX
+                POPAD
+                RET
+_reset_hook     ENDP
+
+
+; Engine trigger context hook; context hooks need to save all registers
+; and get the parameter passed to DevHelp_ArmCtxHook() in EAX, thus
+; we need a stub which calls the real context hook.
+_engine_hook    PROC    FAR
+                PUSHAD
+                PUSH    EAX
+                CALL    _engine_ctxhook
+                POP     EAX
+                POPAD
+                RET
+_engine_hook    ENDP
+
+                .286
+
+_TEXT           ENDS
+
+                END
+
Index: trunk/src/os2ahci/install.cmd
===================================================================
--- trunk/src/os2ahci/install.cmd	(revision 13)
+++ trunk/src/os2ahci/install.cmd	(revision 13)
@@ -0,0 +1,2 @@
+copy os2ahci.add c:\os2\boot\*
+copy os2ahci.sym c:\os2\boot\*
Index: trunk/src/os2ahci/libc.c
===================================================================
--- trunk/src/os2ahci/libc.c	(revision 13)
+++ trunk/src/os2ahci/libc.c	(revision 13)
@@ -0,0 +1,743 @@
+/******************************************************************************
+ * libc.c - minimal subset of C runtime library for os2ahci
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+#define MSG_REPLACEMENT_STRING 1178   /* empty message with a single %; used
+                                       * for printing custom messages via
+                                       * DevHelp_Save_Message() */
+
+/* heap management constants */
+#define HEAP_SIZE             8192
+#define HEAP_UNIT              128
+#define HEAP_UNIT_CNT         (HEAP_SIZE / HEAP_UNIT)
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* mdelay() calibration status */
+typedef enum {
+  MD_NOT_CALIBRATED,                  /* delay loop not calibrated */
+  MD_CALIBRATION_START,               /* calibration run started */
+  MD_CALIBRATION_END,                 /* calibration run ended */
+  MD_CALIBRATION_DONE                 /* calibration complete */
+} MDCAL;
+
+/* -------------------------- function prototypes -------------------------- */
+
+static void      long_to_asc            (long val, char _far *buf, int base,
+                                         int zero, int flen);
+static void _far mdelay_timer_callback  (ULONG timer_handle, ULONG parm1,
+                                         ULONG parm2);
+static int       mdelay_cal_end         (void);
+
+/* ------------------------ global/static variables ------------------------ */
+
+/* debug COM port base address */
+u16 com_base = 0x03f8;
+
+static char  hex_digits[] = "0123456789abcdef";
+static ULONG com_lock;
+
+/* message table for DosHelp_Save_Message() which prints the first string */
+static MSGTABLE init_msgtbl = {
+  MSG_REPLACEMENT_STRING,
+  1,
+  0
+};
+
+/* COM port initialization sequence */
+static struct {
+  int reg;
+  u8  data;
+} com1_init_sequence[] = {
+  3, 0x80,   /* overlay divisor latch register at 0x3f8 and 0x3f9 */
+  0, 0x00,   /* set low byte of divisor to 1 (115200 baud) */
+  1, 0x01,   /* set high byte of divisor to 0 */
+  3, 0x03,   /* reset divisor latch register overlay and set 8,n,1 */
+  1, 0x00,   /* disable interrupts */
+  4, 0x0f,   /* modem control register */
+  -1, 0x00
+};
+
+/* delay loop calibration data */
+volatile MDCAL mdelay_cal_status = 0;     /* delay loop calibration status */
+volatile u32   mdelay_loops_per_ms = 0;   /* delay loop counter */
+
+/* very small heap for dynamic memory management */
+static u8 heap_buf[HEAP_SIZE];
+static u8 heap_units[HEAP_UNIT_CNT];
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Initialize COM1 to 57600,n,8,1
+ */
+void init_com1(void)
+{
+  int i;
+
+  DevHelp_CreateSpinLock(&com_lock);
+
+  for (i = 0; com1_init_sequence[i].reg != -1; i++) {
+    u16 port = com_base + com1_init_sequence[i].reg;
+    u8  data = com1_init_sequence[i].data;
+    _asm {
+      mov  dx, port;
+      mov  al, data;
+      out  dx, al;
+    }
+  }
+}
+
+/******************************************************************************
+ * Print a formatted message into a string buffer. This is very basic,
+ * supporting only strings and integers (16 and 32 bits (l), decimal (d)
+ * and hex (x)). Formatting length modifiers are only supported with a single
+ * digit -- 32-bit numbers don't need more than 9 characters -- and an
+ * optional '0' in front.
+ */
+int vsprintf(char _far *buf, const char *fmt, va_list va)
+{
+  char _far *orig = buf;
+  char _far *s;
+  int lmod;
+  int fptr;
+  int zero;
+  int flen;
+
+  for (; *fmt != '\0'; fmt++) {
+    switch (*fmt) {
+
+    case '%':
+      fmt++;
+      zero = flen = 0;
+      if (*fmt >= '0' && *fmt <= '9') {
+        /* formatting length modifiers */
+        zero = (*fmt == '0') ? 1 : 0;
+        fmt += zero;
+        if ((flen = *fmt - '0') >= 1 && flen <= 9) {
+          fmt++;
+        }
+      }
+      
+      /* data type modifiers */
+      lmod = (*fmt == 'l') ? 1 : 0;
+      fptr = (*fmt == 'F') ? 1 : 0;
+      fmt += lmod + fptr;
+
+      switch (*fmt) {
+
+      case 's':
+        if (fptr) {
+          char _far *p = va_arg(va, char _far *);
+          s = (p == 0) ? "[null]" : p;
+        } else {
+          char *p = va_arg(va, char *);
+          s = (p == 0) ? "[null]" : p;
+        }
+        while ((*buf = *(s++)) != '\0')
+          buf++;
+        break;
+
+      case 'c':
+        *(buf++) = (char) va_arg(va, int); 
+        break;
+
+      case 'd':
+        long_to_asc((lmod) ? va_arg(va, long)
+                           : va_arg(va, int), buf, 10, zero, flen);
+        buf += strlen(buf);
+        break;
+
+      case 'x':
+        long_to_asc((lmod) ? va_arg(va, u32)
+                           : va_arg(va, u16), buf, 16, zero, flen);
+        buf += strlen(buf);
+        break;
+
+      case 'p':
+        if (fptr || lmod) {
+          u16 off = va_arg(va, u16);
+          u16 seg = va_arg(va, u16);
+          long_to_asc(seg, buf, 16, 1, 4);
+          buf += strlen(buf);
+          *(buf++) = ':';
+          long_to_asc(off, buf, 16, 1, 4);
+          buf += strlen(buf);
+        } else {
+          long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
+          buf += strlen(buf);
+        }
+        break;
+
+      default:
+        *(buf++) = *fmt;
+        break;
+      }
+    break;
+
+    case '\n':
+      *(buf++) = '\r';
+      *(buf++) = '\n';
+      break;
+
+    default:
+      *(buf++) = *fmt;
+      break;
+
+    }
+  }
+
+  *buf = '\0';
+  return((int) (buf - orig));
+}
+
+/*******************************************************************************
+ * Print a formatted message into a string buffer. Relies on vsprintf()
+ */
+int sprintf(char _far *buf, const char *fmt, ...)
+{
+  va_list va;
+
+  va_start(va, fmt);
+  return(vsprintf(buf, fmt, va));
+}
+
+/******************************************************************************
+ * Print messages to COM
+ *
+ * NOTES: This function uses a 1K buffer for the resulting message. Thus,
+ *        messages should never exceed 1024 bytes.
+ */
+void vprintf(const char *fmt, va_list va)
+{
+  static char buf[1024];
+  char *s;
+
+  spin_lock(com_lock);
+
+  vsprintf(buf, fmt, va);
+
+  /* write debug message to COM1 */
+  for (s = buf; *s != '\0'; s++) {
+
+    /* inp() and outp() are redefined by the DDK in an incompatible
+     * way (only words). Instead of messing around with those
+     * definitions, it's safer and easier to put the whole thing
+     * into an _asm block.
+     * 
+     * The C equivalent would look like this:
+     *
+     *   while (!(inp(com_base + 5) & 0x20));
+     *   outp(com_base, *s);
+     */
+
+    _asm {
+      /* wait until COM transmitter is idle */
+      mov  dx, com_base;
+      add  dx, 5;
+    transmitter_not_idle:
+      in   al, dx;
+      and  al, 0x20;
+      jz   transmitter_not_idle;
+
+      /* output character to be sent */
+      mov  dx, com_base;
+      mov  bx, s;
+      mov  al, [bx];
+      out  dx, al;
+    };
+  }
+
+  spin_unlock(com_lock);
+}
+
+/******************************************************************************
+ * Print messages to COM port
+ */
+void printf(const char *fmt, ...)
+{
+  va_list va;
+  
+  va_start(va, fmt);
+  vprintf(fmt, va);
+}
+
+/******************************************************************************
+ * Print a message to the system console. This works only during device driver
+ * initialization.
+ *
+ * NOTE: This function uses a 1K buffer for the resulting message. Thus,
+ *       messages should never exceed 1024 bytes...
+ */
+void cprintf(const char *fmt, ...)
+{
+  static char buf[1024];
+  va_list va;
+  size_t len;
+  
+  va_start(va, fmt);
+  vsprintf(buf, fmt, va);
+
+  if (debug) {
+    /* print the same message to COM1 as well */
+    printf("%s", buf);
+  }
+
+  /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
+  if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
+    buf[len-2] = '\0';
+  }
+
+  init_msgtbl.MsgStrings[0] = buf;
+  DevHelp_Save_Message((NPBYTE) &init_msgtbl);
+}
+
+/******************************************************************************
+ * Print hex buffer to COM port.
+ */
+void phex(const void _far *p, int len, const char *fmt, ...)
+{
+  va_list va;
+  const unsigned char _far *buf = p;
+  long pos = 0;
+  int i;
+
+  if (!debug) {
+    return;
+  }
+
+  /* print header */
+  va_start(va, fmt);
+  vprintf(fmt, va);
+
+  /* print hex block */
+  while (len > 0) {
+    printf("%Fp ", buf);
+
+    /* print hex block */
+    for (i = 0; i < 16; i++) {
+      if (i < len) {
+        printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
+      } else {
+        printf("   ");
+      }
+    }
+
+    /* print ASCII block */
+    printf("   ");
+    for (i = 0; i < ((len > 16) ? 16 : len); i++) {
+      printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
+    }
+    printf("\n");
+
+    pos += 16;
+    buf += 16;
+    len -= 16;
+  }
+}
+
+/******************************************************************************
+ * Return length of zero-terminated string
+ */
+size_t strlen(const char _far *s)
+{
+  int len = 0;
+
+  while (*(s++) != '\0') {
+    len++;
+  }
+  return(len);
+}
+
+/******************************************************************************
+ * Copy zero-terminated string
+ */
+char _far *strcpy(char _far *dst, const char _far *src)
+{
+  char _far *orig = dst;
+
+  while ((*(dst++) = *(src++)) != '\0');
+  return(orig);
+}
+
+/******************************************************************************
+ * Compare blocks of memory
+ */
+int memcmp(void _far *p1, void _far *p2, size_t len)
+{
+  register char _far *s1 = p1;
+  register char _far *s2 = p2;
+  int n = 0;
+
+  while (len > 0) {
+    if ((n = *(s1++) - *(s2++)) != 0) {
+      return(n);
+    }
+    len--;
+  }
+  return(0);
+}
+
+/******************************************************************************
+ * Convert a string to a long value using the specified base
+ */
+long strtol(const char _far *buf, const char _far * _far *ep, int base)
+{
+  register const char _far *s = buf;
+  long val = 0;
+  int negative = 0;
+
+  /* skip leading whitespace */
+  while (*s == ' ' || *s == '\t') {
+    s++;
+  }
+
+  /* positive or negative */
+  if (*s == '-') {
+    negative = 1;
+    s++;
+  } else if (*s == '+') {
+    s++;
+  }
+
+  /* convert string to long integer */
+  for (;; s++) {
+    int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
+    if (digit < 0 || digit >= base) {
+      break;
+    }
+    val *= base;
+    val += digit;
+  }
+
+  if (ep != NULL) {
+    *ep = s;
+  }
+  if (negative) {
+    val = -val;
+  }
+  return(val);
+}
+
+/******************************************************************************
+ * Extremely simple and stupid implementation of malloc(). The heap is very
+ * small, only 8K at the moment, and the memory blocks are managed using a
+ * simple array of "number of heap units allocated", zero meaning this unit is
+ * available. Each heap unit is currently 128 bytes.
+ *
+ * Dynamic memory is primarily used for things like ATA identify, ATAPI
+ * sense buffers, etc. and should be freed as soon as possible, otherwise
+ * we'll quickly run out of memory.
+ *
+ * NOTE: This function is not reentrant, thus must be called with the driver-
+ *       level spinlock held. The main reason for this design is that most
+ *       functions that need dynamic memory are already holding the spinlock.
+ */
+void *malloc(size_t len)
+{
+  u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
+  u16 i;
+  u16 n;
+
+  /* find a sequence of free heap units big enough for the requested length */
+  for (i = 0; i < HEAP_UNIT_CNT; i++) {
+    if (heap_units[i] == 0) {
+      for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
+        if (heap_units[n] != 0) {
+          break;
+        }
+      }
+      if (n == i + units) {
+        /* found a chunk large enough; update 'heap_units[]' */
+        for (; i < n; i++) {
+          heap_units[i] = (u8) (n - i);
+        }
+        return(heap_buf + (n - units) * HEAP_UNIT);
+      }
+      
+      /* keep searching... */
+      i = n;
+    } else {
+      /* skip occupied heap units */
+      i += heap_units[i] - 1;
+    }
+  }
+
+  /* out of memory */
+  dprintf("malloc(%d): out of memory\n", len);
+  return(NULL);
+}
+
+/******************************************************************************
+ * Free block of memory allocted by malloc().
+ *
+ * NOTE: This function is not reentrant, thus must be called with the driver-
+ *       level spinlock held. The main reason for this design is that most
+ *       functions that need dynamic memory are already holding the spinlock.
+ */
+void free(void *ptr)
+{
+  u8 *p = (u8 *) ptr;
+  u16 first_unit;
+  u16 units;
+  u16 i;
+
+  if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
+      (u16) (p - heap_buf) % HEAP_UNIT != 0) {
+    dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
+            (u16) p, (u16) heap_buf);
+    return;
+  }
+
+  /* clear unit allocation counters in heap_units[] */
+  first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
+  units = heap_units[first_unit];
+  for (i = first_unit; i < first_unit + units; i++) {
+    heap_units[i] = 0;
+  }
+}
+
+/******************************************************************************
+ * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
+ * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
+ * calls mdelay() with a large milliseond value an initial delay loop counter
+ * of 1,000,000. When the timer triggers, 'mdelay()' will stop and update
+ * the delay loop counter.
+ *
+ * This function needs to be called at device driver init time. Since it uses
+ * ADD timers, it must be called with interrupts enabled. All this is not very
+ * precise (we should wait for a clock tick before starting, ...) but we don't
+ * really need precise timers.
+ */
+void mdelay_cal(void)
+{
+  ULONG timer_handle;
+
+  dprintf("calibrating delay loop... ");
+
+  mdelay_loops_per_ms = 100000;
+  mdelay_cal_status = MD_CALIBRATION_START;
+
+  ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
+  mdelay(999999999);
+  ADD_CancelTimer(timer_handle);
+
+  dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
+}
+
+/******************************************************************************
+ * Wait specified number of milliseconds. This is implemented using a busy
+ * loop and is only good for delays in the millisecond range but never for more
+ * than a few milliseconds and only in situations where a proper timer won't do.
+ * As a rule of thumb, don't call this function and use ADD timers, instead.
+ *
+ * NOTES:
+ *
+ *  - Timers are problematic on x86 platforms because there's no reliable
+ *    hardware timer on all architectures and the CPU clock speed may change
+ *    while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
+ *    calibration routines won't really be sufficient. But this usually only
+ *    extends the delay and we don't really need a high precision timer. The
+ *    exception are things like notebooks that are clocked slower when on
+ *    battery and which got booted while on battery. Should still be OK,
+ *    though, because our requirements are not that strict.
+ *
+ *  - The code in this function is inefficient by design to make sure it
+ *    will work with future CPUs which might otherwise be too fast for
+ *    our loop counters. Part of this design is using volatile variables to
+ *    force memory operations.
+ *
+ *  - Before using this function, call mdelay_calibrate() to determine the
+ *    number of inner loops required per millisecond.
+ */
+void mdelay(u32 millies)
+{
+  volatile u32 i;
+  volatile u32 n;
+
+  for (i = 0; i < millies; i++) {
+    for (n = 0; n < mdelay_loops_per_ms; n++) {
+      if (mdelay_cal_end()) {
+        /* this is a calibration run that just ended */
+        goto complete_calibration;
+      }
+    }
+  }
+  return;
+
+complete_calibration:
+  /* complete calibration cycle */
+  if (i < 1000) {
+    /* Initial value for delay loop was too high; interpolate results for
+     * an assumed initial delay loop divided by 1000.
+     */
+    i = i * 1000 + mdelay_loops_per_ms % 1000;
+    mdelay_loops_per_ms /= 1000;
+  }
+  mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
+  mdelay_cal_status = MD_CALIBRATION_DONE;
+}
+
+/******************************************************************************
+ * Sleep specified number of milliseonds. This is implemented by yielding the
+ * CPU until the system timer value indicates we're done. This function can
+ * only be called at task time, or from a context hook.
+ *
+ * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
+ *       can lead to intervals up to 55ms (18.2 timer interrupts per second).
+ */
+void msleep(u32 millies)
+{
+  volatile PGINFOSEG gis;
+  ULONG start;
+  ULONG end;
+  PSEL p;
+
+  if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p)) {
+    /* no global info segment; use mdelay() */
+    mdelay(millies);
+    return;
+  }
+  gis = (PGINFOSEG) ((u32) *p << 16);
+  start = gis->msecs;
+  end = start + millies;
+
+  if (end < start) {
+    /* wrap-around; wait until 'msecs' has wrapped, too */
+    while (gis->msecs >= start) {
+      DevHelp_Yield();
+    }
+  }
+
+  while (gis->msecs <= end) {
+    DevHelp_Yield();
+  }
+}
+
+/******************************************************************************
+ * Halt processing by submitting an internal error. This is a last resort and
+ * should only be called when the system state is corrupt.
+ */
+void panic(char *msg)
+{
+  DevHelp_InternalError(msg, strlen(msg));
+}
+
+/******************************************************************************
+ * Disable interrupts. The reason for using a separate function for this is
+ * that the presence of _asm statements will disable compiler optimizations.
+ * In order to support nested calls, this function will return 0 if the
+ * interrupts were already disabled or != 0, if not.
+ *
+ * NOTE: SMP systems must use spinlocks, thus this function will only be
+ *       compiled on non-SMP builds.
+ */
+#ifndef OS2AHCI_SMP
+int disable(void)
+{
+  int rc;
+
+  _asm {
+    pushf
+    pop   ax
+    and   ax, 0x0200;  /* "interrupts enabled" bit */
+    mov   rc, ax;
+    cli
+  }
+
+  return(rc);
+}
+#endif
+
+/******************************************************************************
+ * Enable interrupts. The reason for using a separate function for this is
+ * that the presence of _asm statements will disable compiler optimizations.
+ *
+ * NOTE: SMP systems must use spinlocks, thus this function will only be
+ *       compiled on non-SMP builds.
+ */
+#ifndef OS2AHCI_SMP
+void enable(void)
+{
+  _asm sti;
+}
+#endif
+
+/******************************************************************************
+ * Convert 'long' to ASCII with the specified base
+ */
+static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
+{
+  register unsigned long abs_val = (unsigned long) val;
+  char tmp[80];
+  char _far *ptmp = tmp;
+  char _far *s;
+
+  if (base > 16) {
+    sprintf(buf, "[EVAL]");
+    return;
+  }
+
+  tmp[sizeof(tmp) - 1] = '\0';
+  for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
+    *s = hex_digits[abs_val % base];
+    flen--;
+    if ((abs_val /= base) == 0) {
+      break;
+    }
+  }
+
+  /* prepend minus sign if val was negative and base is decimal or less */
+  if (val < 0 && base <= 0) {
+    *(--s) = '-';
+    flen--;
+  }
+
+  /* left-pad the resulting number with zeros or spaces up to 'flen' */
+  while (flen > 0) {
+    *(--s) = (zero) ? '0' : ' ';
+    flen--;
+  }
+
+  strcpy(buf, s);
+}
+
+/******************************************************************************
+ * Timer callback handler for 'mdelay_calibrate()'
+ */
+static void _far mdelay_timer_callback(ULONG timer_handle, ULONG parm1,
+                                       ULONG parm2)
+{
+  mdelay_cal_status = MD_CALIBRATION_END;
+}
+
+/******************************************************************************
+ * Determine whether an mdelay calibration run has just ended. This is in a
+ * function to prevent overzealous optimizers from removing the whole delay
+ * loop in mdelay().
+ */
+static int mdelay_cal_end(void)
+{
+  return(mdelay_cal_status == MD_CALIBRATION_END);
+}
Index: trunk/src/os2ahci/os2ahci.c
===================================================================
--- trunk/src/os2ahci/os2ahci.c	(revision 13)
+++ trunk/src/os2ahci/os2ahci.c	(revision 13)
@@ -0,0 +1,1018 @@
+/******************************************************************************
+ * os2ahci.c - main file for os2ahci driver
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* parse integer command line parameter */
+#define drv_parm_int(s, value, type, radix)               \
+  {                                                       \
+    char _far *_ep;                                       \
+    if ((s)[1] != ':') {                                  \
+      cprintf("missing colon (:) after /%c\n", *(s));     \
+      goto init_fail;                                     \
+    }                                                     \
+    value = (type) strtol((s) + 2, &_ep, radix);          \
+    s = _ep;                                              \
+  }
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+/* ------------------------ global/static variables ------------------------ */
+
+int             debug = 0;         /* if > 0, print debug messages to COM1 */
+int             thorough_scan;     /* if != 0, perform thorough PCI scan */
+int             init_reset;        /* if != 0, reset ports during init */
+
+PFN             Device_Help = 0;   /* pointer to device helper entry point */
+ULONG           RMFlags = 0;       /* required by resource manager library */
+PFN             RM_Help0 = NULL;   /* required by resource manager library */
+PFN             RM_Help3 = NULL;   /* required by resource manager library */
+HDRIVER         rm_drvh;           /* resource manager driver handle */
+char            rm_drvname[80];    /* driver name as returned by RM */
+USHORT          add_handle;        /* driver handle (RegisterDeviceClass) */
+UCHAR           timer_pool[TIMER_POOL_SIZE]; /* timer pool */
+
+/* resource manager driver information structure */
+DRIVERSTRUCT rm_drvinfo = {
+  "OS2AHCI",                       /* driver name */
+  "AHCI SATA Driver",              /* driver description */
+  "GNU",                           /* vendor name */
+  CMVERSION_MAJOR,                 /* RM interface version major */
+  CMVERSION_MINOR,                 /* RM interface version minor */
+  2010, 4, 27,                     /* date */
+  0,                               /* driver flags */
+  DRT_ADDDM,                       /* driver type */
+  DRS_ADD,                         /* driver sub type */
+  NULL                             /* driver callback */
+};
+
+ULONG           drv_lock;          /* driver-level spinlock */
+IORB_QUEUE      driver_queue;      /* driver-level IORB queue */
+AD_INFO         ad_infos[MAX_AD];  /* adapter information list */
+int             ad_info_cnt;       /* number of entries in ad_infos[] */
+int             init_complete;     /* if != 0, initialization has completed */
+
+/* apapter/port-specific options saved when parsing the command line */
+u8              link_speed[MAX_AD][AHCI_MAX_PORTS];
+u8              disable_ncq[MAX_AD][AHCI_MAX_PORTS];
+
+static char     init_msg[] = "OS2AHCI driver version %d.%02d\n";
+static char     exit_msg[] = "OS2AHCI driver *not* installed\n";
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * OS/2 device driver main strategy function. This function is only used
+ * for initialization purposes; all other calls go directly to the adapter
+ * device driver's strategy function.
+ */
+USHORT c_strat(RPH _far *req)
+{ 
+  u16 rc;
+
+  switch (req->Cmd) {
+
+  case CMDInitBase:
+    rc = init_drv((RPINITIN _far *) req);
+    break;
+
+  default:
+    rc = STDON | STATUS_ERR_UNKCMD;
+    break;
+  }
+
+  return(rc);
+}
+
+/******************************************************************************
+ * Intialize the os2ahci driver. This includes command line parsing, scanning
+ * the PCI bus for supported AHCI adapters, etc.
+ */
+USHORT init_drv(RPINITIN _far *req)
+{
+  RPINITOUT _far *rsp = (RPINITOUT _far *) req;
+  DDD_PARM_LIST _far *ddd_pl = (DDD_PARM_LIST _far *) req->InitArgs;
+  APIRET rmrc;
+  PSEL p;
+  char _far *cmd_line;
+  char _far *ep;
+  char _far *s;
+  int adapter_index;
+  int port_index;
+  u16 vendor;
+  u16 device;
+
+  /* set device helper entry point */
+  Device_Help = req->DevHlpEP;
+
+  /* create driver-level spinlock */
+  DevHelp_CreateSpinLock(&drv_lock);
+
+  if (debug) {
+    /* initialize debug interface (COM1) */
+    init_com1();
+  }
+
+  /* print initialization message */
+  cprintf(init_msg, VERSION / 100, VERSION % 100);
+
+  /* register driver with resource manager */
+  if ((rmrc = RMCreateDriver(&rm_drvinfo, &rm_drvh)) != RMRC_SUCCESS) {
+    cprintf("failed to register driver with resource manager (rc = %d)\n", rmrc);
+    goto init_fail;
+  }
+
+  /* parse command line parameters */
+  cmd_line = (char _far *) ((u32) ddd_pl & 0xffff0000l) + ddd_pl->cmd_line_args;
+  adapter_index = 0;
+  port_index = 0;
+
+  for (s = cmd_line; *s != 0; s++) {
+    if (*s == '/' && s[1] != '\0') {
+      s++;
+      switch(tolower(*s)) {
+
+      case 'c':
+        /* set COM port base address for debug messages */
+        drv_parm_int(s, com_base, u16, 16);
+        break;
+
+      case 'd':
+        /* increase debug level */
+        debug++;
+        break;
+
+      case 'i':
+        /* add specfied PCI ID as a supported generic AHCI adapter  */
+        drv_parm_int(s, vendor, u16, 16);
+        drv_parm_int(s, device, u16, 16);
+        if (add_pci_id(vendor, device)) {
+          cprintf("failed to add PCI ID %04x:%04x\n", vendor, device);
+          goto init_fail;
+        }
+        thorough_scan = 1;
+        break;
+
+      case 't':
+        /* perform thorough PCI scan (i.e. look for individual supported PCI IDs) */
+        thorough_scan = 1;
+        break;
+
+      case 'r':
+        /* reset ports during initialization */
+        init_reset = 1;
+        break;
+
+      case 'a':
+        /* set adapter index for adapter and port-related options */
+        drv_parm_int(s, adapter_index, int, 10);
+        if (adapter_index < 0 || adapter_index >= MAX_AD) {
+          cprintf("invalid adapter index (%d)\n", adapter_index);
+          goto init_fail;
+        }
+        break;
+
+      case 'p':
+        /* set port index for port-related options */
+        drv_parm_int(s, port_index, int, 10);
+        if (port_index < 0 || port_index >= AHCI_MAX_PORTS) {
+          cprintf("invalid port index (%d)\n", port_index);
+          goto init_fail;
+        }
+        break;
+
+      case 's':
+        /* set link speed of current port on current adapter */
+        drv_parm_int(s, link_speed[adapter_index][port_index], u8, 10);
+        init_reset = 1;
+        break;
+
+      case 'n':
+        /* disable NCQ on this port */
+        drv_parm_int(s, disable_ncq[adapter_index][port_index], u8, 10);
+        break;
+
+      default:
+        cprintf("invalid option: /%c\n", *s);
+        goto init_fail;
+      }
+    }
+  }
+
+  /* scan PCI bus for supported devices */
+  scan_pci_bus();
+
+  if (ad_info_cnt > 0) {
+    /* initialization succeeded and we found at least one AHCI adapter */
+    ADD_InitTimer(timer_pool, sizeof(timer_pool));
+    mdelay_cal();
+
+    if (DevHelp_RegisterDeviceClass("OS2AHCI", (PFN) add_entry, 0, 1,
+        &add_handle)) {
+      cprintf("error: couldn't register device class\n");
+      goto init_fail;
+    }
+    
+    /* allocate context hooks */
+    if (DevHelp_AllocateCtxHook(mk_NPFN(restart_hook), &restart_ctxhook_h) != 0 ||
+        DevHelp_AllocateCtxHook(mk_NPFN(reset_hook), &reset_ctxhook_h) != 0 ||
+        DevHelp_AllocateCtxHook(mk_NPFN(engine_hook), &engine_ctxhook_h)) {
+      cprintf("failed to allocate task-time context hooks\n");
+      goto init_fail;
+    }
+
+    rsp->CodeEnd = (u16) end_of_code;
+    rsp->DataEnd = (u16) &end_of_data;
+    return(STDON);
+  }
+
+init_fail:
+  /* initialization failed; set segment sizes to 0 and return error */
+  cprintf(exit_msg);
+  rsp->CodeEnd = 0;
+  rsp->DataEnd = 0;
+
+  /* free context hooks */
+  if (engine_ctxhook_h != 0)  DevHelp_FreeCtxHook(engine_ctxhook_h);
+  if (reset_ctxhook_h != 0)   DevHelp_FreeCtxHook(reset_ctxhook_h);
+  if (restart_ctxhook_h != 0) DevHelp_FreeCtxHook(restart_ctxhook_h);
+
+  if (rm_drvh != 0) {
+    /* remove driver from resource manager */
+    RMDestroyDriver(rm_drvh);
+  }
+
+  cprintf(exit_msg);
+  return(STDON | ERROR_I24_QUIET_INIT_FAIL);
+}
+
+/******************************************************************************
+ * ADD entry point. This is the main entry point for all ADD requests. Due to
+ * the asynchronous nature of ADD drivers, this function primarily queues the
+ * IORB(s) to the corresponding adapter or port queues, then triggers the
+ * state machine to initiate processing queued IORBs.
+ *
+ * NOTE: In order to prevent race conditions or engine stalls, certain rules
+ *       around locking, unlocking and IORB handling in general have been
+ *       established. Refer to the comments in "trigger_engine()" for
+ *       details.
+ */
+void _far _loadds add_entry(IORBH _far *first_iorb)
+{
+  IORBH _far *iorb;
+  IORBH _far *next = NULL;
+
+  spin_lock(drv_lock);
+
+  for (iorb = first_iorb; iorb != NULL; iorb = next) {
+    /* Queue this IORB. Queues primarily exist on port level but there are
+     * some requests which affect the whole driver, most notably
+     * IOCC_CONFIGURATION. In either case, adding the IORB to the driver or
+     * port queue will change the links, thus we need to save the original
+     * link in 'next'.
+     */
+    next = (iorb->RequestControl | IORB_CHAIN) ? iorb->pNxtIORB : 0;
+
+    iorb->Status = 0;
+    iorb->ErrorCode = 0;
+    memset(&iorb->ADDWorkSpace, 0x00, sizeof(ADD_WORKSPACE));
+
+    if (iorb_driver_level(iorb)) {
+      /* adapter-level IORB */
+      iorb->UnitHandle = 0;
+      iorb_queue_add(&driver_queue, iorb);
+
+    } else {
+      /* port-level IORB */
+      int a = iorb_unit_adapter(iorb);
+      int p = iorb_unit_port(iorb);
+      int d = iorb_unit_device(iorb);
+      
+      if (a >= ad_info_cnt ||
+          p > ad_infos[a].port_max ||
+          d > ad_infos[a].ports[p].dev_max ||
+          (ad_infos[a].port_map & (1UL << p)) == 0) {
+
+        /* unit handle outside of the allowed range */
+        dprintf("warning: IORB for %d.%d.%d out of range\n", a, p, d);
+        iorb->Status = IORB_ERROR | IORB_DONE;
+        iorb->ErrorCode = IOERR_CMD_SYNTAX;
+        if (iorb->RequestControl & IORB_ASYNC_POST) {
+          iorb->NotifyAddress(iorb);
+        }
+        continue;
+      }
+
+      iorb_queue_add(&ad_infos[a].ports[p].iorb_queue, iorb);
+    }
+  }
+
+  /* trigger state machine */
+  trigger_engine();
+
+  spin_unlock(drv_lock);
+}
+
+/******************************************************************************
+ * Trigger IORB queue engine. This is a wrapper function for trigger_engine_1()
+ * which will try to get all IORBs sent on their way a couple of times. If
+ * there are still IORBs ready for processing after this, this function will
+ * hand off to a context hook which will continue to trigger the engine until
+ * all IORBs have been sent.
+ */
+void trigger_engine(void)
+{
+  int i;
+
+  for (i = 0; i < 3; i++) {
+    if (trigger_engine_1() == 0) {
+      /* done -- all IORBs have been sent on their way */
+      return;
+    }
+  }
+
+  /* Something keeps bouncing; hand off to the engine context hook which will
+   * keep trying in the background.
+   */
+  DevHelp_ArmCtxHook(0, engine_ctxhook_h);
+}
+
+/******************************************************************************
+ * Trigger IORB queue engine in order to send commands in the driver/port IORB
+ * queues to the AHCI hardware. This function will return the number of IORBs
+ * sent. Keep in mind that IORBs might "bounce" if the adapter/port is not in
+ * a state to accept the command, thus it might take quite a few calls to get
+ * all IORBs on their way. This is why there's a wrapper function which tries
+ * it a few times, then hands off to a context hook which will keep trying in
+ * the background.
+ *
+ * IORBs might complete before send_iorb() has returned, at any time during
+ * interrupt processing or on another CPU on SMP systems. IORB completion
+ * means modifications to the corresponding IORB queue (the completed IORB
+ * is removed from the queue) thus we need to protect the IORB queues from
+ * race conditions. The safest approach short of keeping the driver-level
+ * spinlock aquired permanently is to keep it throughout this function and
+ * release it temporarily in send_iorb().
+ *
+ * This implies that the handler functions are fully responsible for aquiring
+ * the driver-level spinlock when they need it, and for releasing it again.
+ *
+ * As a rule of thumb, get the driver-level spinlock whenever accessing
+ * volatile variables (IORB queues, values in ad_info[], ...).
+ *
+ * Additional Notes:
+ *
+ * - This function is expected to be called with the spinlock aquired
+ *
+ * - Adapters can be flagged as 'busy' which means no new IORBs are sent (they
+ *   just remain in the queue). This can be used to release the driver-level
+ *   spinlock while making sure no new IORBs are going to hit the hardware.
+ *   In order to prevent engine stalls, all handlers using this functionality
+ *   need to invoke trigger_engine() after resetting the busy flag.
+ *
+ * - Driver-level IORBs are not synchronized by adapter-level 'busy' flags.
+ *   However, the driver-level queue is worked "one entry at a time" which
+ *   means that no new IORBs will be queued on the driver-level queue until
+ *   the head element has completed processing. This means that driver-
+ *   level IORB handlers don't need to protect against each other. But they
+ *   they do need to keep in mind interference with port-level IORBs:
+ *
+ *   - Driver-level IORB handlers must obtain the spinlock and/or flag all
+ *     adapters as 'busy' which are affected by the driver-level IORB
+ *
+ *   - Driver-level IORB handlers must not access the hardware of a
+ *     particular adapter if it's flagged as 'busy'
+ */
+int trigger_engine_1(void)
+{
+  IORBH _far *iorb;
+  IORBH _far *next;
+  int iorbs_sent = 0;
+  int a;
+  int p;
+
+  iorbs_sent = 0;
+
+  /* process driver-level IORBs */
+  if ((iorb = driver_queue.root) != NULL && !add_workspace(iorb)->processing) {
+    send_iorb(iorb);
+    iorbs_sent++;
+  }
+
+  /* process port-level IORBs */
+  for (a = 0; a < ad_info_cnt; a++) {
+    AD_INFO *ai = ad_infos + a;
+    if (ai->busy) {
+      /* adapter is busy; don't process any IORBs */
+      continue;
+    }
+    for (p = 0; p <= ai->port_max; p++) {
+      /* send all queued IORBs on this port */
+      next = NULL;
+      for (iorb = ai->ports[p].iorb_queue.root; iorb != NULL; iorb = next) {
+        next = iorb->pNxtIORB;
+        if (!add_workspace(iorb)->processing) {
+          send_iorb(iorb);
+          iorbs_sent++;
+        }
+      }
+    }
+  }
+
+  return(iorbs_sent);
+}
+
+/******************************************************************************
+ * Send a single IORB to the corresponding AHCI adapter/port. This is just a
+ * switch board for calling the corresponding iocc_*() handler function.
+ *
+ * NOTE: This function is expected to be called with the driver-level spinlock
+ *       aquired. It will release it before calling any of the handler
+ *       functions and re-aquire it when done.
+ */
+void send_iorb(IORBH _far *iorb)
+{
+  /* Mark IORB as "processing" before doing anything else. Once the IORB is
+   * marked as "processing", we can release the spinlock because subsequent
+   * invocations of trigger_engine() (e.g. at interrupt time) will ignore this
+   * IORB.
+   */
+  add_workspace(iorb)->processing = 1;
+  spin_unlock(drv_lock);
+
+  switch (iorb->CommandCode) {
+
+  case IOCC_CONFIGURATION:
+    iocc_configuration(iorb);
+    break;
+
+  case IOCC_DEVICE_CONTROL:
+    iocc_device_control(iorb);
+    break;
+
+  case IOCC_UNIT_CONTROL:
+    iocc_unit_control(iorb);
+    break;
+
+  case IOCC_GEOMETRY:
+    iocc_geometry(iorb);
+    break;
+
+  case IOCC_EXECUTE_IO:
+    iocc_execute_io(iorb);
+    break;
+
+  case IOCC_UNIT_STATUS:
+    iocc_unit_status(iorb);
+    break;
+
+  case IOCC_ADAPTER_PASSTHRU:
+    iocc_adapter_passthru(iorb);
+    break;
+
+  default:
+    /* unsupported call */
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+    break;
+  }
+
+  /* re-aquire spinlock before returning to trigger_engine() */
+  spin_lock(drv_lock);
+}
+
+/******************************************************************************
+ * Handle IOCC_CONFIGURATION requests.
+ */
+void iocc_configuration(IORBH _far *iorb)
+{
+  int a;
+
+  switch (iorb->CommandModifier) {
+
+  case IOCM_COMPLETE_INIT:
+    /* Complete initialization. From now on, we won't have to restore the BIOS
+     * configuration after each command and we're fully operational (i.e. will
+     * use interrupts, timers and context hooks instead of polling).
+     */
+    if (!init_complete) {
+      dprintf("leaving initialization mode\n");
+      spin_lock(drv_lock);
+      for (a = 0; a < ad_info_cnt; a++) {
+        ahci_complete_init(ad_infos + a);
+      }
+      init_complete = 1;
+      spin_unlock(drv_lock);
+    }
+    iorb_done(iorb);
+    break;
+
+  case IOCM_GET_DEVICE_TABLE:
+    /* construct a device table */
+    iocm_device_table(iorb);
+    break;
+
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+    break;
+  }
+}
+
+/******************************************************************************
+ * Handle IOCC_DEVICE_CONTROL requests.
+ */
+void iocc_device_control(IORBH _far *iorb)
+{
+  AD_INFO *ai = ad_infos + iorb_unit_adapter(iorb);
+  IORBH _far *ptr;
+  IORBH _far *next = NULL;
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+
+  switch (iorb->CommandModifier) {
+  
+  case IOCM_ABORT:
+    /* abort all pending commands on specified port and device */
+    spin_lock(drv_lock);
+    for (ptr = ai->ports[p].iorb_queue.root; ptr != NULL; ptr = next) {
+      next = ptr->pNxtIORB;
+      /* move all matching IORBs to the abort queue */
+      if (ptr != iorb && iorb_unit_device(ptr) == d) {
+        iorb_queue_del(&ai->ports[p].iorb_queue, ptr);
+        iorb_queue_add(&abort_queue, ptr);
+        ptr->ErrorCode = IOERR_CMD_ABORTED;
+      }
+    }
+    spin_unlock(drv_lock);
+
+    /* trigger reset context hook which will finish the abort processing */
+    DevHelp_ArmCtxHook(0, reset_ctxhook_h);
+    break;
+    
+  case IOCM_SUSPEND:
+  case IOCM_RESUME:
+  case IOCM_GET_QUEUE_STATUS:
+    /* Suspend/resume operations allow access to the hardware for other
+     * entities such as IBMIDECD.FLT. Since os2ahci implements both ATA
+     * and ATAPI in the same driver, this won't be required.
+     */
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    break;
+
+  case IOCM_LOCK_MEDIA:
+  case IOCM_UNLOCK_MEDIA:
+  case IOCM_EJECT_MEDIA:
+    /* unit control commands to lock, unlock and eject media */
+    /* will be supported later... */
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    break;
+
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    break;
+  }
+
+  iorb_done(iorb);
+}
+
+/******************************************************************************
+ * Handle IOCC_UNIT_CONTROL requests.
+ */
+void iocc_unit_control(IORBH _far *iorb)
+{
+  IORB_UNIT_CONTROL _far *iorb_uc = (IORB_UNIT_CONTROL _far *) iorb;
+  int a = iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+  int d = iorb_unit_device(iorb);
+
+  spin_lock(drv_lock);
+  switch (iorb->CommandModifier) {
+
+  case IOCM_ALLOCATE_UNIT:
+    /* allocate unit for exclusive access */
+    if (ad_infos[a].ports[p].devs[d].allocated) {
+      iorb_seterr(iorb, IOERR_UNIT_ALLOCATED);
+    } else {
+      ad_infos[a].ports[p].devs[d].allocated = 1;
+    }
+    break;
+
+  case IOCM_DEALLOCATE_UNIT:
+    /* deallocate exclusive access to unit */
+    if (!ad_infos[a].ports[p].devs[d].allocated) {
+      iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
+    } else {
+      ad_infos[a].ports[p].devs[d].allocated = 0;
+    }
+    break;
+
+  case IOCM_CHANGE_UNITINFO:
+    /* Change unit (device) information. One reason for this IOCM is the
+     * interface for filter device drivers: a filter device driver can
+     * either change existing UNITINFOs or permanently allocate units
+     * and fabricate new [logical] units; the former is the reason why we
+     * must store the pointer to the updated UNITNIFO for subsequent
+     * IOCC_CONFIGURATION/IOCM_GET_DEVICE_TABLE calls.
+     */
+    if (!ad_infos[a].ports[p].devs[d].allocated) {
+      iorb_seterr(iorb, IOERR_UNIT_NOT_ALLOCATED);
+      break;
+    }
+    ad_infos[a].ports[p].devs[d].unit_info = iorb_uc->pUnitInfo;
+    break;
+
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    break;
+  }
+
+  spin_unlock(drv_lock);
+  iorb_done(iorb);
+}
+
+/******************************************************************************
+ * Scan all ports for AHCI devices and construct a DASD device table.
+ *
+ * NOTE: This function may be called multiple times. Only the first invocation
+ *       will actually scan for devices; all subsequent calls will merely
+ *       return the results of the initial scan, potentially augmented by
+ *       modified unit infos after IOCC_CONFIGURATION/IOCM_CHANGE_UNITINFO
+ *       requests.
+ */
+void iocm_device_table(IORBH _far *iorb)
+{
+  IORB_CONFIGURATION _far *iorb_conf;
+  DEVICETABLE _far *dt;
+  char _far *pos;
+  int rc;
+  int a;
+  int p;
+  int d;
+
+  iorb_conf = (IORB_CONFIGURATION _far *) iorb;
+  dt = iorb_conf->pDeviceTable;
+
+  spin_lock(drv_lock);
+
+  /* initialize device table header */
+  dt->ADDLevelMajor = ADD_LEVEL_MAJOR;
+  dt->ADDLevelMinor = ADD_LEVEL_MINOR;
+  dt->ADDHandle     = add_handle;
+  dt->TotalAdapters = ad_info_cnt;
+
+  /* Initial position of dynamic portion of device table (i.e. behind the
+   * array of ADAPTERINFO pointers, pAdapter, in the device table)
+   */
+  pos = (char _far *) (dt->pAdapter + ad_info_cnt);
+
+  for (a = 0; a < ad_info_cnt; a++) {
+    ADAPTERINFO _far *ptr = (ADAPTERINFO _far *) pos;
+    AD_INFO *ad_info = ad_infos + a;
+    int units = 0;
+
+    /* sanity check for sufficient space in device table */
+    if ((u32) (ptr + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
+      dprintf("error: device table provided by DASD too small\n");
+      iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
+      goto iocm_device_table_done;
+    }
+
+    /* set ADAPTERINFO offset in device table */
+    dt->pAdapter[a] = (ADAPTERINFO _near *) ((u32) ptr & 0xffff);
+
+    /* fill in adapter information structure in device table */
+    memset(ptr, 0x00, sizeof(*ptr));
+    sprintf(ptr->AdapterName, "AHCI_%d", a);
+    ptr->AdapterDevBus   = AI_DEVBUS_ST506 | AI_DEVBUS_32BIT;
+    ptr->AdapterIOAccess = AI_IOACCESS_BUS_MASTER;
+    ptr->AdapterHostBus  = AI_HOSTBUS_OTHER | AI_BUSWIDTH_32BIT;
+    ptr->AdapterFlags    = AF_16M | AF_HW_SCATGAT;
+
+    /* AHCI limits S/G elements to 22 bits, thus we'll report only half of
+     * our S/G list buffers to reduce complexity. The command preparation code
+     * will always try to map as many S/G elements as possible so the physical
+     * S/G list capacity is not really wasted except in rare conditions where
+     * we need to split commands with long S/G lists without any suitable split
+     * points except those at the reported MaxHWSGList.
+     */
+    ptr->MaxHWSGList     = AHCI_MAX_SG / 2;
+
+    if (!ad_info->port_scan_done) {
+      /* First call; need to scan AHCI hardware for devices. Since this might
+       * be a lengthy operation, especially when init_reset is set, we'll mark
+       * the adapter as busy (new IORBs will only be queued but not executed)
+       * and release the spinlock while scanning the ports so interrupts will
+       * be processed.
+       */
+      if (ad_info->busy) {
+        dprintf("error: port scan requested while adapter was busy\n");
+        iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
+        goto iocm_device_table_done;
+      }
+      ad_info->busy = 1;
+      spin_unlock(drv_lock);
+      rc = ahci_scan_ports(ad_info);
+      spin_lock(drv_lock);
+      ad_info->busy = 0;
+
+      if (rc != 0) {
+        dprintf("error: port scan failed on adapter #%d\n", a);
+        iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
+        goto iocm_device_table_done;
+      }
+      ad_info->port_scan_done = 1;
+    }
+
+    /* insert devices (units) into the device table */
+    for (p = 0; p <= ad_info->port_max; p++) {
+      for (d = 0; d <= ad_info->ports[p].dev_max; d++) {
+        if (ad_info->ports[p].devs[d].present) {
+          UNITINFO _far *ui = ptr->UnitInfo + units;
+
+          /* sanity check for sufficient space in device table */
+          if ((u32) (ui + 1) - (u32) dt > iorb_conf->DeviceTableLen) {
+            dprintf("error: device table provided by DASD too small\n");
+            iorb_seterr(iorb, IOERR_CMD_SW_RESOURCE);
+            goto iocm_device_table_done;
+          }
+
+          if (ad_info->ports[p].devs[d].unit_info == NULL) {
+            /* provide initial information about this device (unit) */
+            memset(ui, 0x00, sizeof(*ui));
+            ui->AdapterIndex = a;
+            ui->UnitIndex    = units;
+            ui->UnitHandle   = iorb_unit(a, p, d);
+            ui->UnitType     = ad_info->ports[p].devs[d].dev_type;
+            ui->QueuingCount = ad_info->ports[p].devs[d].ncq_max;;
+            if (ad_info->ports[p].devs[d].removable) {
+              ui->UnitFlags |= UF_REMOVABLE;
+            }
+          } else {
+            /* copy updated device (unit) information (IOCM_CHANGE_UNITINFO) */
+            memcpy(ui, ad_info->ports[p].devs[d].unit_info, sizeof(*ui));
+          }
+          units++;
+        }
+      }
+    }
+
+    /* set total device (unit) count for this adapter */
+    ptr->AdapterUnits = units;
+
+    /* calculate offset for next adapter */
+    pos = (char _far *) (ptr->UnitInfo + units);
+  }
+
+iocm_device_table_done:
+  spin_unlock(drv_lock);
+  iorb_done(iorb);
+}
+
+/******************************************************************************
+ * Handle IOCC_GEOMETRY requests.
+ */
+void iocc_geometry(IORBH _far *iorb)
+{
+  switch (iorb->CommandModifier) {
+
+  case IOCM_GET_MEDIA_GEOMETRY:
+  case IOCM_GET_DEVICE_GEOMETRY:
+    add_workspace(iorb)->idempotent = 1;
+    ahci_get_geometry(iorb);
+    break;
+
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+  }
+}
+
+/******************************************************************************
+ * Handle IOCC_EXECUTE_IO requests.
+ */
+void iocc_execute_io(IORBH _far *iorb)
+{
+  switch (iorb->CommandModifier) {
+
+  case IOCM_READ:
+    add_workspace(iorb)->idempotent = 1;
+    ahci_read(iorb);
+    break;
+
+  case IOCM_READ_VERIFY:
+    add_workspace(iorb)->idempotent = 1;
+    ahci_verify(iorb);
+    break;
+
+  case IOCM_WRITE:
+    add_workspace(iorb)->idempotent = 1;
+    ahci_write(iorb);
+    break;
+
+  case IOCM_WRITE_VERIFY:
+    add_workspace(iorb)->idempotent = 1;
+    ahci_write(iorb);
+    break;
+    
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+  }
+}
+
+/******************************************************************************
+ * Handle IOCC_UNIT_STATUS requests.
+ */
+void iocc_unit_status(IORBH _far *iorb)
+{
+  switch (iorb->CommandModifier) {
+
+  case IOCM_GET_UNIT_STATUS:
+    add_workspace(iorb)->idempotent = 1;
+    ahci_unit_ready(iorb);
+    break;
+
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+  }
+}
+
+/******************************************************************************
+ * Handle IOCC_ADAPTER_PASSTHROUGH requests.
+ */
+void iocc_adapter_passthru(IORBH _far *iorb)
+{
+  switch (iorb->CommandModifier) {
+
+  case IOCM_EXECUTE_CDB:
+    add_workspace(iorb)->idempotent = 0;
+    ahci_execute_cdb(iorb);
+    break;
+
+  case IOCM_EXECUTE_ATA:
+    add_workspace(iorb)->idempotent = 0;
+    ahci_execute_ata(iorb);
+    break;
+
+  default:
+    iorb_seterr(iorb, IOERR_CMD_NOT_SUPPORTED);
+    iorb_done(iorb);
+  }
+}
+
+/******************************************************************************
+ * Add an IORB to the specified queue.
+ */
+void iorb_queue_add(IORB_QUEUE _far *queue, IORBH _far *iorb)
+{
+  if (iorb_priority(iorb) {
+    /* priority IORB; insert at first position */
+    iorb->pNxtIORB = queue->root;
+    queue->root = iorb;
+
+  } else {
+    /* append IORB to end of queue */
+    iorb->pNxtIORB = NULL;
+
+    if (queue->root == NULL) {
+      queue->root = iorb;
+    } else {
+      queue->tail->pNxtIORB = iorb;
+    }
+    queue->tail = iorb;
+  }
+
+  dprintf("IORB queued:  %d/%d (queue = %Fp, IORB = %Fp)\n",
+          iorb->CommandCode, iorb->CommandModifier, queue, iorb);
+}
+
+/******************************************************************************
+ * Remove an IORB from the specified queue.
+ */
+int iorb_queue_del(IORB_QUEUE _far *queue, IORBH _far *iorb)
+{
+  IORBH _far *_iorb;
+  IORBH _far *_prev = NULL;
+  int found = 0;
+
+  for (_iorb = queue->root; _iorb != NULL; _iorb = _iorb->pNxtIORB) {
+    if (_iorb == iorb) {
+      /* found the IORB to be removed */
+      if (_prev != NULL) {
+        _prev->pNxtIORB = _iorb->pNxtIORB;
+      } else {
+        queue->root = _iorb->pNxtIORB;
+      }
+      if (_iorb == queue->tail) {
+        queue->tail = _prev;
+      }
+      found = 1;
+      break;
+    }
+    _prev = _iorb;
+  }
+
+  if (found) {
+    dprintf("IORB removed: %d/%d (queue = %Fp, IORB = %Fp) - %04x/%04x\n",
+            iorb->CommandCode, iorb->CommandModifier, queue, iorb,
+            iorb->Status, iorb->ErrorCode);
+  } else {
+    dprintf("IORB %Fp not found in queue %Fp\n", iorb, queue);
+  }
+
+  return(!found);
+}
+        
+/******************************************************************************
+ * Set the error code in the specified IORB
+ * 
+ * NOTE: This function does *not* call iorb_done(). It merely sets the IORB
+ *       status to the specified error code.
+ */
+void iorb_seterr(IORBH _far *iorb, USHORT error_code)
+{
+  iorb->ErrorCode = error_code;
+  iorb->Status = IORB_ERROR;
+}
+
+/******************************************************************************
+ * Mark the specified IORB as done and notify the asynchronous post function,
+ * if any. The IORB is also removed from the corresponding IORB queue.
+ *
+ * NOTES: This function does not clear the Status field; it merely adds the
+ *        IORB_DONE flag.
+ *
+ *        This function is expected to be called *without* the corresponding
+ *        driver-level drv_lock aquired. It will aquire the spinlock before
+ *        updating the IORB queue and release it before notifying the upstream
+ *        code in order to prevent deadlocks.
+ *
+ *        Due to this logic, this function is only good for simple task-time
+ *        completions. Functions working on lists of IORBs (such as interrupt
+ *        handlers or context hooks) should implement their own logic. See
+ *        abort_ctxhook() for an example.
+ */
+void iorb_done(IORBH _far *iorb)
+{
+  int a = iorb_unit_adapter(iorb);
+  int p = iorb_unit_port(iorb);
+
+  /* remove IORB from corresponding queue */
+  spin_lock(drv_lock);
+  if (iorb_driver_level(iorb)) {
+    iorb_queue_del(&driver_queue, iorb);
+  } else {
+    iorb_queue_del(&ad_infos[a].ports[p].iorb_queue, iorb);
+  }
+  aws_free(add_workspace(iorb));
+  spin_unlock(drv_lock);
+
+  /* notify caller, if requested */
+  iorb->Status |= IORB_DONE;
+  if (iorb->RequestControl & IORB_ASYNC_POST) {
+    iorb->NotifyAddress(iorb);
+  }
+}
+
+/******************************************************************************
+ * Requeue the specified IORB such that it will be sent downstream for
+ * processing again. This includes freeing all resources currently allocated
+ * (timer, buffer, ...) and resetting the flags to 0.
+ *
+ * The following flags are preserved:
+ *  - no_ncq
+ */
+void iorb_requeue(IORBH _far *iorb)
+{
+  ADD_WORKSPACE _far *aws = add_workspace(iorb);
+  u16 no_ncq = aws->no_ncq;
+
+  aws_free(aws);
+  memset(aws, 0x00, sizeof(*aws));
+  aws->no_ncq = no_ncq;
+}
+
Index: trunk/src/os2ahci/os2ahci.def
===================================================================
--- trunk/src/os2ahci/os2ahci.def	(revision 13)
+++ trunk/src/os2ahci/os2ahci.def	(revision 13)
@@ -0,0 +1,18 @@
+library os2ahci
+description 'OS2 AHCI Driver'
+protmode
+
+segments
+  DEVHDR        CLASS 'DATA'
+  _DATA         CLASS 'DATA'
+  CONST         CLASS 'CONST'
+  _BSS          CLASS 'BSS'
+  c_common      CLASS 'BSS'
+  _z_data       CLASS 'BSS'
+  _TEXT         CLASS 'CODE' IOPL
+  'CODE'        CLASS 'CODE' IOPL
+  RMCode        CLASS 'CODE' IOPL
+  LIBCODE       CLASS 'CODE' IOPL
+  _z_text       CLASS 'CODE' IOPL
+
+
Index: trunk/src/os2ahci/os2ahci.h
===================================================================
--- trunk/src/os2ahci/os2ahci.h	(revision 13)
+++ trunk/src/os2ahci/os2ahci.h	(revision 13)
@@ -0,0 +1,471 @@
+/******************************************************************************
+ * os2ahci.h - main header file for os2ahci driver
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* ----------------------------- include files ----------------------------- */
+
+/* IMPORTANT NOTE: The DDK headers require tight structure packing and this
+ * is controlled via compiler parameters. Thus, all stuctures in os2ahci.sys
+ * are expected to be byte-aligned without the need of explicit pragma pack()
+ * directives. Where possible, the structures are layed out such that words
+ * and dwords are aligned at least on 2-byte boundaries.
+ */
+
+#define INCL_NOPMAPI
+#define INCL_DOSINFOSEG
+#define INCL_NO_SCB
+#define INCL_DOSERRORS
+#include <os2.h>
+#include <dos.h>
+#include <bseerr.h>
+#include <dskinit.h>
+#include <scb.h>
+
+#include <devhdr.h>
+#include <iorb.h>
+#include <strat2.h>
+#include <reqpkt.h>
+#include <dhcalls.h>
+#include <addcalls.h>
+#include <rmcalls.h>
+#include <devclass.h>
+#include <devcmd.h>
+#include <rmbase.h>
+
+#include "ahci.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+#define VERSION            100       /* driver version (2 implied decimals) */
+#define MAX_AD               8       /* maximum number of adapters */
+
+/* Timer pool size. In theory, we need one timer per outstanding command plus
+ * a few miscellaneous timers but it's unlikely we'll ever have outstanding
+ * commands on all devices on all ports on all apapters -- this would be
+ * 8 * 32 * 32 = 8192 outstanding commands on a maximum of 8 * 32 * 15 = 3840
+ * devices and that's a bit of an exaggeration. It should be more than enough
+ * to have 128 timers.
+ */
+#define TIMER_COUNT        128
+#define TIMER_POOL_SIZE    (sizeof(ADD_TIMER_POOL) + \
+                            TIMER_COUNT * sizeof(ADD_TIMER_DATA))
+
+/* default command timeout (can be overwritten in the IORB) */
+#define DEFAULT_TIMEOUT  30000
+
+/* debug output macros */
+#define dprintf   if (debug > 0) printf
+#define dphex     if (debug > 0) phex
+#define ddprintf  if (debug > 1) printf
+#define ddphex    if (debug > 1) phex
+#define dddprintf if (debug > 2) printf
+#define dddphex   if (debug > 2) phex
+
+/* adapter number from AD_INFO pointer; mainly for dprintf() purposes */
+#define ad_no(ai) (((u16) ai - (u16) ad_infos) / sizeof(*ai))
+
+/* Convert far function address into NPFN (the DDK needs this all over the
+ * place and just casting to NPFN will produce a "segment lost in conversion"
+ * warning. Since casting to a u32 is a bit nasty for function pointers and
+ * might have to be revised for different compilers, we'll use a central
+ * macro for this crap.
+ */
+#define mk_NPFN(func)      (NPFN) (u32) (func)
+
+/* stdarg.h macros with explicit far pointers
+ *
+ * NOTE: The compiler pushes fixed arguments with 16 bits minimum, thus
+ *       the last fixed argument (i.e. the one passed to va_start) must
+ *       have at least 16 bits. Otherwise, the address calculation in
+ *       va_start() will fail.
+ */
+typedef char _far *va_list;
+#define va_start(va, last)    va = (va_list) (&last + 1)
+#define va_arg(va, type)      ((type _far *) (va += sizeof(type)))[-1]
+#define va_end(va)            va = 0
+
+/* ctype macros */
+#define isupper(ch)           ((ch) >= 'A' && (ch) <= 'Z')
+#define tolower(ch)           (isupper(ch) ? (ch) - ('a' - 'A') : (ch))
+
+/* stddef macros */
+#define offsetof(s, e)        ((u16) &((s *) 0)->e)
+
+/* SMP spinlock compatibility macros for older DDKs using CLI/STI */
+#ifndef OS2AHCI_SMP
+#define DevHelp_CreateSpinLock(sph)    *(sph) = 0
+#define DevHelp_FreeSpinLock(sph)      0
+
+#define DevHelp_AquireSpinLock(sph)    if ((sph) != 0)                \
+                                         panic("recursive spinlock"); \
+                                       (sph) = disable()
+
+#define DevHelp_ReleaseSpinLock(sph)   if (sph) {                     \
+                                         (sph) = 0;                   \
+                                         enable();                    \
+                                       }
+#endif
+
+/* shortcut macros */
+#define spin_lock(sl)     DevHelp_AquireSpinLock(sl)
+#define spin_unlock(sl)   DevHelp_ReleaseSpinLock(sl)
+
+/* Get AHCI port MMIO base from AD_INFO and port number. For the time being,
+ * MMIO addresses are assumed to be valid 16:16 pointers which implies
+ * that one GDT selector is allocated per adapter.
+ */
+#define port_base(ai, p)   ((u8 _far *) (ai)->mmio + 0x100 + (p) * 0x80)
+
+/* Get address of port-specific DMA scratch buffer. The total size of all DMA
+ * buffers required for 32 ports exceeds 65536 bytes, thus we need multiple
+ * GDT selectors to access all port DMA scratch buffers and some logic to map
+ * a port number to the corresponding DMA scratch buffer address.
+ */
+#define PORT_DMA_BUFS_PER_SEG  ((size_t) (65536UL / AHCI_PORT_PRIV_DMA_SZ))
+#define PORT_DMA_BUF_SEGS      ((AHCI_MAX_PORTS + PORT_DMA_BUFS_PER_SEG - 1) \
+                                / PORT_DMA_BUFS_PER_SEG)
+#define PORT_DMA_SEG_SIZE      ((u32) PORT_DMA_BUFS_PER_SEG * \
+                                (u32) AHCI_PORT_PRIV_DMA_SZ)
+
+#define port_dma_base(ai, p) \
+  ((AHCI_PORT_DMA _far *) ((ai)->dma_buf[(p) / PORT_DMA_BUFS_PER_SEG] + \
+                           ((p) % PORT_DMA_BUFS_PER_SEG) * AHCI_PORT_PRIV_DMA_SZ))
+
+#define port_dma_base_phys(ai, p) \
+  ((ai)->dma_buf_phys + (u32) (p) * AHCI_PORT_PRIV_DMA_SZ)
+
+/* Convert an SATA adapter/port/device address into a 16-bit IORB unit handle
+ * (and the other way round). The mapping looks like this:
+ *
+ *  mapping                   comment
+ *  -----------------------------------------------------------------------
+ *  4 bits for the adapter    current max is 8 adapters 
+ *  4 bits for the port       AHCI spec defines up to 32 ports
+ *  4 bits for the device     SATA spec defines up to 15 devices behind PMP
+ */
+#define iorb_unit(a, p, d)       ((((u16) (a) & 0x0fU) << 8) | \
+                                  (((u16) (p) & 0x0fU) << 4) | \
+                                  (((u16) (d) & 0x0fU)))
+#define iorb_unit_adapter(iorb)  (((u16) (iorb)->UnitHandle >> 8) & 0x07U)
+#define iorb_unit_port(iorb)     (((u16) (iorb)->UnitHandle >> 4) & 0x0fU)
+#define iorb_unit_device(iorb)   ((u16)  (iorb)->UnitHandle       & 0x0fU)
+
+/*******************************************************************************
+ * Convenience macros for IORB processing functions
+ */
+/* is this IORB on driver or port level? */
+#define iorb_driver_level(iorb) ((iorb)->CommandCode == IOCC_CONFIGURATION)
+
+/* is this IORB to be inserted at the beginnig of the IORB queue? */
+#define iorb_priority(iorb)     ((iorb)->CommandCode == IOCC_DEVICE_CONTROL && \
+                                 (iorb)->CommandModifier == IOCM_ABORT))
+
+/* access IORB ADD workspace */
+#define add_workspace(iorb)     ((ADD_WORKSPACE _far *) &(iorb)->ADDWorkSpace)
+
+/* free resources in ADD workspace (timer, buffer, ...) */
+#define aws_free(aws)           if ((aws)->timer != 0) {               \
+                                  ADD_CancelTimer((aws)->timer);       \
+                                  (aws)->timer = 0;                    \
+                                }                                      \
+                                if ((aws)->buf != NULL) {              \
+                                  free((aws)->buf);                    \
+                                  (aws)->buf = NULL;                   \
+                                }
+
+/******************************************************************************
+ * PCI generic IDs and macros
+ */
+#define PCI_ANY_ID                     0xffffU
+#define PCI_VDEVICE(vendor, device)    PCI_VENDOR_ID_##vendor, (device), \
+                                       PCI_ANY_ID, PCI_ANY_ID, 0, 0
+
+/******************************************************************************
+ * PCI vendor IDs for AHCI adapters known to this driver (copied from Linux
+ * pci_ids.h)
+ */
+#define PCI_VENDOR_ID_AL       0x10b9
+#define PCI_VENDOR_ID_AMD      0x1022
+#define PCI_VENDOR_ID_AT       0x1259
+#define PCI_VENDOR_ID_ATI      0x1002
+#define PCI_VENDOR_ID_ATT      0x11c1
+#define PCI_VENDOR_ID_CMD      0x1095
+#define PCI_VENDOR_ID_CT       0x102c
+#define PCI_VENDOR_ID_INTEL    0x8086
+#define PCI_VENDOR_ID_JMICRON  0x197B
+#define PCI_VENDOR_ID_MARVELL  0x11ab
+#define PCI_VENDOR_ID_NVIDIA   0x10de
+#define PCI_VENDOR_ID_PROMISE  0x105a
+#define PCI_VENDOR_ID_SI       0x1039
+#define PCI_VENDOR_ID_VIA      0x1106
+
+/******************************************************************************
+ * PCI class IDs we're interested in (copied from Linux pci_ids.h)
+ */
+#define PCI_BASE_CLASS_STORAGE          0x01
+#define PCI_CLASS_STORAGE_SCSI          0x0100
+#define PCI_CLASS_STORAGE_IDE           0x0101
+#define PCI_CLASS_STORAGE_FLOPPY        0x0102
+#define PCI_CLASS_STORAGE_IPI           0x0103
+#define PCI_CLASS_STORAGE_RAID          0x0104
+#define PCI_CLASS_STORAGE_SATA          0x0106
+#define PCI_CLASS_STORAGE_SATA_AHCI     0x010601
+#define PCI_CLASS_STORAGE_SAS           0x0107
+#define PCI_CLASS_STORAGE_OTHER         0x0180
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+typedef unsigned int     size_t;
+
+/* PCI device information structure; this is used both for scanning and for
+ * identification purposes in 'AD_INFO'; based on the Linux pci_device_id
+ * structure but hard-wired to use board_* constants for 'driver_data'
+ */
+typedef struct {
+  u16   vendor;          /* PCI device vendor/manufacturer */
+  u16   device;          /* PCI device ID inside vendor scope */
+  u16   subvendor;       /* subsystem vendor (unused so far) */
+  u16   subdevice;       /* subsystem device (unused so far) */
+  u32   class;           /* PCI device class */
+  u32   class_mask;      /* bits to match when scanning for 'class' */
+  u32   board;           /* AHCI controller board type (board_* constants) */
+} PCI_ID;
+
+/* IORB queue; since IORB queues are updated at interrupt time, the
+ * corresponding pointers (not the data they point to) need to be volatile.
+ */
+typedef struct {
+  IORBH _far *volatile root;           /* root of request list */
+  IORBH _far *volatile tail;           /* tail of request list */
+} IORB_QUEUE;
+
+/* port information structure */
+typedef struct {
+  IORB_QUEUE    iorb_queue;            /* IORB queue for this port */
+  unsigned      dev_max   : 4;         /* maximum device number on this port (0-15) */
+  unsigned      cmd_slot  : 5;         /* current command slot index (using round-
+                                        * robin indexes to prevent starvation) */
+
+  volatile u32  ncq_cmds;              /* bitmap for NCQ commands issued */
+  volatile u32  reg_cmds;              /* bitmap for regular commands issued */
+
+  struct {
+    unsigned    allocated : 1;         /* if != 0, device is allocated */
+    unsigned    present   : 1;         /* if != 0, device is present */
+    unsigned    lba48     : 1;         /* if != 0, device supports 48-bit LBA */
+    unsigned    atapi     : 1;         /* if != 0, this is an ATAPI device */
+    unsigned    atapi_16  : 1;         /* if != 0, device suports 16-byte cmds */
+    unsigned    removable : 1;         /* if != 0, device has removable media */
+    unsigned    dev_type  : 5;         /* device type (UIB_TYPE_* in iorb.h) */
+    unsigned    ncq_max   : 5;         /* maximum tag number for queued commands */
+    UNITINFO _far *unit_info;          /* pointer to modified unit info */
+  } devs[15];
+} P_INFO;
+
+/* adapter information structure */
+typedef struct {
+  PCI_ID       *pci;                   /* pointer to corresponding PCI ID */
+
+  unsigned      port_max : 5;          /* maximum port number (0-31) */
+  unsigned      cmd_max : 5;           /* maximum cmd slot number (0-31) */
+  unsigned      port_scan_done : 1;    /* if != 0, port scan already done */
+  unsigned      busy : 1;              /* if != 0, adapter is busy */
+
+  u32           port_map;              /* bitmap of active ports */
+
+  /* initial adapter configuration from BIOS */
+  u32           bios_config[HOST_CAP2 / sizeof(u32) + 1];
+
+  u32           cap;                   /* working copy of CAP register */
+  u32           cap2;                  /* working copy of CAP2 register */
+  u32           flags;                 /* adapter flags */
+
+  HRESOURCE     rm_adh;                /* resource handle for adapter */
+  HRESOURCE     rm_mmio;               /* resource handle for MMIO */
+  HRESOURCE     rm_irq;                /* resource handle for IRQ */
+
+  u8            bus;                   /* PCI bus number */
+  u8            dev_func;              /* PCI device and function number */
+  u16           irq;                   /* interrupt number */
+
+  u32           mmio_phys;             /* physical address of MMIO region */
+  u8      _far *mmio;                  /* pointer to this adapter's MMIO region */
+
+  u32           dma_buf_phys;          /* physical address of DMA scratch buffer */
+  u8      _far *dma_buf[PORT_DMA_BUF_SEGS]; /* DMA scatch buffer */
+
+  P_INFO        ports[AHCI_MAX_PORTS]; /* SATA ports on this adapter */
+} AD_INFO;
+
+/* ADD workspace in IORB (must not exceed 16 bytes) */
+typedef struct {
+  void (*ppfunc)(IORBH _far *iorb);    /* post-processing function */
+  void         *buf;                   /* response buffer (e.g. for identify cmds) */
+  ULONG         timer;                 /* timer for timeout procesing */
+  USHORT        blocks;                /* number of blocks to be transferred */
+  unsigned      processing    : 1;     /* IORB is being processd */
+  unsigned      idempotent    : 1;     /* IORB is idempotent (can be retried) */
+  unsigned      queued_hw     : 1;     /* IORB has been queued to hardware */
+  unsigned      no_ncq        : 1;     /* must not use native command queuing */
+  unsigned      is_ncq        : 1;     /* should use native command queueing */
+  unsigned      complete      : 1;     /* IORB has completed processing */
+  unsigned      cmd_slot      : 5;     /* AHCI command slot for this IORB */
+} ADD_WORKSPACE;
+
+/* -------------------------- function prototypes -------------------------- */
+
+/* init.asm */
+extern u32        readl         (void _far *addr);
+extern u32        writel        (void _far *addr, u32 val);
+extern void _far *memcpy        (void _far *v_dst, void _far *v_src, int len);
+extern void _far *memset        (void _far *p, int ch, size_t len);
+extern void _far  restart_hook  (void);
+extern void _far  reset_hook    (void);
+extern void _far  engine_hook   (void);
+
+/* os2ahci.c */
+extern USHORT  init_drv               (RPINITIN _far *req);
+extern void _far _loadds add_entry    (IORBH _far *iorb);
+extern void    trigger_engine         (void);
+extern int     trigger_engine_1       (void);
+extern void    send_iorb              (IORBH _far *iorb);
+extern void    iocc_configuration     (IORBH _far *iorb);
+extern void    iocc_device_control    (IORBH _far *iorb);
+extern void    iocc_unit_control      (IORBH _far *iorb);
+extern void    iocm_device_table      (IORBH _far *iorb);
+extern void    iocc_geometry          (IORBH _far *iorb);
+extern void    iocc_execute_io        (IORBH _far *iorb);
+extern void    iocc_unit_status       (IORBH _far *iorb);
+extern void    iocc_adapter_passthru  (IORBH _far *iorb);
+extern void    iorb_queue_add         (IORB_QUEUE _far *queue, IORBH _far *iorb);
+extern int     iorb_queue_del         (IORB_QUEUE _far *queue, IORBH _far *iorb);
+extern void    iorb_seterr            (IORBH _far *iorb, USHORT error_code);
+extern void    iorb_done              (IORBH _far *iorb);
+extern void    iorb_requeue           (IORBH _far *iorb);
+
+/* ahci.c */
+extern int     ahci_save_bios_config        (AD_INFO *ai);
+extern int     ahci_restore_bios_config     (AD_INFO *ai);
+extern int     ahci_restore_initial_config  (AD_INFO *ai);
+extern AHCI_PORT_CFG *ahci_save_port_config (AD_INFO *ai, int p);
+extern void    ahci_restore_port_config     (AD_INFO *ai, int p,
+                                             AHCI_PORT_CFG *pc);
+extern int     ahci_enable_ahci             (AD_INFO *ai);
+extern int     ahci_scan_ports              (AD_INFO *ai);
+extern int     ahci_complete_init           (AD_INFO *ai);
+extern int     ahci_reset_port              (AD_INFO *ai, int p, int ei);
+extern int     ahci_start_port              (AD_INFO *ai, int p, int ei);
+extern void    ahci_start_fis_rx            (AD_INFO *ai, int p);
+extern void    ahci_start_engine            (AD_INFO *ai, int p);
+extern int     ahci_stop_port               (AD_INFO *ai, int p);
+extern int     ahci_stop_fis_rx             (AD_INFO *ai, int p);
+extern int     ahci_stop_engine             (AD_INFO *ai, int p);
+extern void    ahci_exec_iorb               (IORBH _far *iorb, int ncq_capable,
+                                             int (*func)(IORBH _far *, int));
+extern void    ahci_exec_polled_iorb        (IORBH _far *iorb,
+                                             int (*func)(IORBH _far *, int),
+                                             ULONG timeout);
+extern int     ahci_exec_polled_cmd         (AD_INFO *ai, int p, int d,
+                                             int timeout, int cmd, ...);
+
+extern int     ahci_intr                    (u16 irq);
+extern void    ahci_port_intr               (AD_INFO *ai, int p);
+extern void    ahci_error_intr              (AD_INFO *ai, int p, u32 irq_stat);
+
+extern void    ahci_get_geometry            (IORBH _far *iorb);
+extern void    ahci_unit_ready              (IORBH _far *iorb);
+extern void    ahci_read                    (IORBH _far *iorb);
+extern void    ahci_verify                  (IORBH _far *iorb);
+extern void    ahci_write                   (IORBH _far *iorb);
+extern void    ahci_execute_cdb             (IORBH _far *iorb);
+extern void    ahci_execute_ata             (IORBH _far *iorb);
+
+/* libc.c */
+extern void       init_com1     (void);
+extern int        vsprintf      (char _far *buf, const char *fmt, va_list va);
+extern int        sprintf       (char _far *buf, const char *fmt, ...);
+extern void       vfprintf      (const char *fmt, va_list va);
+extern void       printf        (const char *fmt, ...);
+extern void       cprintf       (const char *fmt, ...);
+extern void       phex          (const void _far *p, int len,
+                                 const char *fmt, ...);
+extern size_t     strlen        (const char _far *s);
+extern char _far *strcpy        (char _far *dst, const char _far *src);
+extern int        memcmp        (void _far *p1, void _far *p2, size_t len);
+extern long       strtol        (const char _far *buf,
+                                 const char _far * _far *ep, int base);
+extern void      *malloc        (size_t len);
+extern void       free          (void *ptr);
+extern void       mdelay_cal    (void);
+extern void       mdelay        (u32 millies);
+extern void       msleep        (u32 millies);
+extern void       panic         (char *msg);
+extern int        disable       (void);
+extern void       enable        (void);
+
+/* pci.c */
+extern int       add_pci_id     (u16 vendor, u16 device);
+extern void      scan_pci_bus   (void);
+extern int       pci_enable_int (UCHAR bus, UCHAR dev_func);
+extern void      pci_hack_virtualbox(void);
+
+/* ctxhook.c */
+extern void      restart_ctxhook    (ULONG parm);
+extern void      reset_ctxhook      (ULONG parm);
+extern void      engine_ctxhook     (ULONG parm);
+
+/* ---------------------------- global variables --------------------------- */
+
+extern char          end_of_data;   /* label at the end of all data segments */
+extern void    _near end_of_code(); /* label at the end of all code segments */
+
+extern int           debug;         /* if != 0, print debug messages to COM1 */
+extern int           thorough_scan; /* if != 0, perform thorough PCI scan */
+extern int           init_reset;    /* if != 0, reset ports during init */
+
+extern HDRIVER       rm_drvh;       /* resource manager driver handle */
+extern USHORT        add_handle;    /* adapter device driver handle */
+extern UCHAR         timer_pool[];  /* timer pool */
+
+extern PCI_ID        pci_ids[];     /* SATA adapter PCI IDs */
+extern ULONG         drv_lock;      /* driver-level spinlock */
+extern IORB_QUEUE    driver_queue;  /* driver-level IORB queue */
+extern AD_INFO       ad_infos[];    /* adapter information list */
+extern int           ad_info_cnt;   /* number of entries in ad_infos[] */
+extern int           init_complete; /* if != 0, initialization has completed */
+
+extern u16           com_base;      /* debug COM port base address */
+
+/* port restart context hook and input data */
+extern ULONG         restart_ctxhook_h;
+extern volatile u32  ports_to_restart[MAX_AD];
+
+/* port reset context hook and input data */
+extern ULONG         reset_ctxhook_h;
+extern volatile u32  ports_to_reset[MAX_AD];
+extern IORB_QUEUE    abort_queue;
+
+/* trigger engine context hook and input data */
+extern ULONG         engine_ctxhook_h;
+
+/* apapter/port-specific options saved when parsing the command line */
+extern u8            link_speed[MAX_AD][AHCI_MAX_PORTS];
+extern u8            disable_ncq[MAX_AD][AHCI_MAX_PORTS];
+
Index: trunk/src/os2ahci/pci.c
===================================================================
--- trunk/src/os2ahci/pci.c	(revision 13)
+++ trunk/src/os2ahci/pci.c	(revision 13)
@@ -0,0 +1,896 @@
+/******************************************************************************
+ * pci.c - PCI constants and detection code for os2ahci driver
+ *
+ * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
+ * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "os2ahci.h"
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* offset of PCI base address register (BAR) in the PCI config space */
+#define PCI_BAR(reg)   (0x10 + (reg) * sizeof(u32))
+
+/* offset of MMIO base address register (BAR) for AHCI adapters */
+#define AHCI_MMIO      PCI_BAR(AHCI_PCI_BAR)
+
+/******************************************************************************
+ * OEMHLP constants for PCI access
+ */
+#define GENERIC_IOCTL       0x10 
+#define OH_CATEGORY         0x00
+#define OH_FUNC_PCI         0x0b
+
+/* subfunctions */
+#define OH_BIOS_INFO        0x00
+#define OH_FIND_DEVICE      0x01
+#define OH_FIND_CLASS       0x02
+#define OH_READ_CONFIG      0x03
+#define OH_WRITE_CONFIG     0x04
+
+/* return codes */
+#define OH_SUCCESS          0x00
+#define OH_NOT_SUPPORTED    0x81
+#define OH_BAD_VENDOR       0x83
+#define OH_NOT_FOUND        0x86
+#define OH_BAD_REGISTER     0x87
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/******************************************************************************
+ * OEMHLP IOCtl parameter union. The parameter area is generally used as input
+ * to the OEMHLP IOCtl calls.
+ */
+typedef union {
+
+  /* query PCI BIOS information" */
+  struct {
+    UCHAR    subfunction;
+  } bios_info;
+
+  /* find PCI device */
+  struct {
+    UCHAR    subfunction;
+    USHORT   device;
+    USHORT   vendor;
+    UCHAR    index;
+  } find_device;
+
+  /* find PCI class code */
+  struct {
+    UCHAR    subfunction;
+    ULONG    class;
+    UCHAR    index;
+  } find_class;
+
+  /* read PCI configuration space */
+  struct {
+    UCHAR    subfunction;
+    UCHAR    bus;
+    UCHAR    dev_func;
+    UCHAR    reg;
+    UCHAR    size;
+  } read_config;
+
+  /* write PCI configuration space */
+  struct {
+    UCHAR    subfunction;
+    UCHAR    bus;
+    UCHAR    dev_func;
+    UCHAR    reg;
+    UCHAR    size;
+    ULONG    data;
+  } write_config;
+
+} OH_PARM;
+  
+/******************************************************************************
+ * OEMHLP IOCtl data union. The data area is generally used as output from the
+ * OEMHLP IOCtl calls.
+ */
+typedef union {
+
+  /* query PCI BIOS information" */
+  struct {
+    UCHAR    rc;
+    UCHAR    hw_mech;
+    UCHAR    major_version;
+    UCHAR    minor_version;
+    UCHAR    last_bus;
+  } bios_info;
+
+  /* find PCI device */
+  struct {
+    UCHAR    rc;
+    UCHAR    bus;
+    UCHAR    dev_func;
+  } find_device;
+
+  /* find PCI class code */
+  struct {
+    UCHAR    rc;
+    UCHAR    bus;
+    UCHAR    dev_func;
+  } find_class;
+
+  /* read PCI confguration space */
+  struct {
+    UCHAR    rc;
+    ULONG    data;
+  } read_config;
+
+  /* write PCI confguration space */
+  struct {
+    UCHAR    rc;
+  } write_config;
+
+} OH_DATA;
+
+/* -------------------------- function prototypes -------------------------- */
+
+static void  add_pci_device   (PCI_ID *pci_id, OH_DATA _far *data);
+static UCHAR pci_read_conf    (UCHAR bus, UCHAR dev_func, UCHAR indx,
+                               UCHAR size, ULONG _far *val);
+static UCHAR pci_write_conf   (UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
+                               ULONG val);
+static int   oemhlp_call      (UCHAR subfunction, OH_PARM _far *parm,
+                               OH_DATA _far *data);
+
+/* ------------------------ global/static variables ------------------------ */
+
+/******************************************************************************
+ * PCI vendor and device IDs for known AHCI adapters. Copied from the Linux
+ * AHCI driver.
+ */
+
+PCI_ID pci_ids[] = {
+
+  /* Intel */
+  { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */
+  { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */
+  { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */
+  { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */
+  { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */
+  { PCI_VDEVICE(AL,    0x5288), board_ahci_ign_iferr }, /* ULi M5288 */
+  { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */
+  { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
+  { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
+  { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
+  { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
+  { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */
+  { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
+  { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
+  { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
+  { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
+  { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
+  { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
+  { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
+  { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
+  { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
+  { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
+  { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
+  { PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */
+  { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
+  { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
+  { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
+  { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
+  { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
+  { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
+  { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
+  { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
+  { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
+  { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
+  { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
+  { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
+  { PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */
+  { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
+  { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
+  { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
+
+  /* JMicron 360/1/3/5/6, match class to avoid IDE function */
+  { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_STORAGE_SATA_AHCI, 0xffffffL, board_ahci_ign_iferr },
+
+  /* ATI */
+  { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
+  { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb700 }, /* ATI SB700/800 */
+  { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb700 }, /* ATI SB700/800 */
+  { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb700 }, /* ATI SB700/800 */
+  { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb700 }, /* ATI SB700/800 */
+  { PCI_VDEVICE(ATI, 0x4394), board_ahci_sb700 }, /* ATI SB700/800 */
+  { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */
+
+  /* AMD */
+  { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */
+  /* AMD is using RAID class only for ahci controllers */
+  { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_STORAGE_RAID << 8, 0xffffffL, board_ahci },
+
+  /* VIA */
+  { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
+  { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
+
+  /* NVIDIA */
+  { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 },      /* MCP65 */
+  { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq },     /* MCP67 */
+  { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_yesncq },     /* Linux ID */
+  { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_yesncq },     /* MCP73 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci },            /* MCP77 */
+  { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci },            /* MCP79 */
+  { PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci },            /* MCP89 */
+  { PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci },            /* MCP89 */
+
+  /* SiS */
+  { PCI_VDEVICE(SI, 0x1184), board_ahci },                /* SiS 966 */
+  { PCI_VDEVICE(SI, 0x1185), board_ahci },                /* SiS 968 */
+  { PCI_VDEVICE(SI, 0x0186), board_ahci },                /* SiS 968 */
+
+  /* Marvell */
+  { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv },        /* 6145 */
+  { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv },        /* 6121 */
+
+  /* Promise */
+  { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci },   /* PDC42819 */
+
+  /* Generic, PCI class code for AHCI */
+  { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+    PCI_CLASS_STORAGE_SATA_AHCI, 0xffffffL, board_ahci },
+
+  /* end of list, including a few slots to define custom adapters (10) */
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },
+
+  { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+/******************************************************************************
+ * OEMHLP$ is used by OS/2 to provide access to OEM-specific machine resources
+ * like PCI BIOS access. We're using this to enumerate the PCI bus. Due to
+ * BIOS bugs, it may be necessary to use I/O operations for this purpose but
+ * so far I think this is only relevant for rather old PCs and SATA is not
+ * expected to be a priority on those machines.
+ */
+static IDCTABLE     oemhlp;            /* OEMHLP$ IDC entry point */
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Add specified PCI vendor and device ID to the list of supported AHCI
+ * controllers. Please note that the last slot in pci_ids needs to remain
+ * empty because it's used as end marker.
+ */
+int add_pci_id(u16 vendor, u16 device)
+{
+  int max_slot = sizeof(pci_ids) / sizeof(*pci_ids) - 2;
+  int i;
+
+  /* search for last used slot in 'pci_ids' */
+  for (i = max_slot; i >= 0 && pci_ids[i].vendor == 0; i--);
+  if (i >= max_slot) {
+    /* all slots in use */
+    return(-1);
+  }
+
+  /* use slot after the last used slot */
+  i++;
+  pci_ids[i].vendor = vendor;
+  pci_ids[i].device = device;
+  pci_ids[i].board = board_ahci;
+  return(0);
+}
+
+/******************************************************************************
+ * Scan PCI bus using OEMHLP$ IOCTLs and build adapter list.
+ */
+void scan_pci_bus(void)
+{
+  OH_PARM parm;
+  OH_DATA data;
+  UCHAR index;
+  UCHAR rc;
+  int i;
+
+  ddprintf("scanning PCI bus...\n");
+
+  /* verify that we have a PCI system */
+  memset(&parm, 0x00, sizeof(parm));
+  if (oemhlp_call(OH_BIOS_INFO, &parm, &data) != OH_SUCCESS) {
+    cprintf("couldn't get PCI BIOS information\n");
+    return;
+  }
+
+  /* Go through the list of PCI IDs and search for each device 
+   *
+   * NOTES:
+   *
+   *  - When searching via class code, the OEMHLP$ interface doesn't allow
+   *    setting a bitmask to look for individual portions of class code,
+   *    subclass code and programming interface. However, all bitmasks in the
+   *    PCI list currently use 0xffffff, thus this should not be a problem at
+   *    this point in time.
+   *
+   *  - Scanning via OEMHLP$ seems rather slow, at least in the virtual
+   *    machine I'm currenly using to test this driver. Thus, class code
+   *    scans are preferred unless the option "-t" (thorough_scan) has been
+   *    specified. The assumption is that most, if not all, modern AHCI
+   *    adapters have the correct class code (PCI_CLASS_STORAGE_SATA_AHCI).
+   */
+  for (i = 0; pci_ids[i].vendor != 0; i++) {
+    index = 0;
+    do {
+      if (pci_ids[i].device == PCI_ANY_ID || pci_ids[i].vendor == PCI_ANY_ID) {
+        /* look for class code */
+        memset(&parm, 0x00, sizeof(parm));
+        parm.find_class.class = pci_ids[i].class;
+        parm.find_class.index = index;
+        rc = oemhlp_call(OH_FIND_CLASS, &parm, &data);
+
+      } else if (thorough_scan) {
+        /* look for this specific vendor and device ID */
+        memset(&parm, 0x00, sizeof(parm));
+        parm.find_device.device = pci_ids[i].device;
+        parm.find_device.vendor = pci_ids[i].vendor;
+        parm.find_device.index = index;
+        rc = oemhlp_call(OH_FIND_DEVICE, &parm, &data);
+
+      } else {
+        rc = OH_NOT_FOUND;
+      }
+
+      if (rc == OH_SUCCESS) {
+        /* found a device */
+        add_pci_device(pci_ids + i, &data);
+        if (++index > 180) {
+          /* something's wrong here... */
+          return;
+        }
+      }
+
+    } while (rc == OH_SUCCESS);
+  }
+}
+
+/******************************************************************************
+ * Enable interrupt generation. PCI 2.3 added a bit which allows disabling
+ * interrupt generation for a device. This function clears the corresponding
+ * bit in the configuration space command register.
+ */
+int pci_enable_int(UCHAR bus, UCHAR dev_func)
+{
+  ULONG tmp;
+
+  if (pci_read_conf (bus, dev_func, 4, sizeof(u32), &tmp) != OH_SUCCESS ||
+      pci_write_conf(bus, dev_func, 4, sizeof(u32), tmp & ~(1UL << 10)) != OH_SUCCESS) {
+    return(-1);
+  }
+  return(0);
+}
+
+/******************************************************************************
+ * Hack to set up proper IRQ mappings in the emulated PIIX3 ISA bridge in
+ * VirtualBox (for some reason, the first mapped IRQ is 0x80 without this
+ * hack).
+ */
+void pci_hack_virtualbox(void)
+{
+  ULONG irq = 0;
+
+  if (pci_read_conf(0, 0x08, 0x60, 1, &irq) == OH_SUCCESS && irq == 0x80) {
+    /* set IRQ for first device/func to 11 */
+    dprintf("hacking virtualbox PIIX3 PCI to ISA bridge IRQ mapping\n");
+    irq = ad_infos[0].irq;
+    pci_write_conf(0, 0x08, 0x60, 1, irq);
+  }
+}
+
+/******************************************************************************
+ * Add a single PCI device to the list of adapters.
+ */
+static void add_pci_device(PCI_ID *pci_id, OH_DATA _far *data)
+{
+  char rc_list_buf[sizeof(AHRESOURCE) + sizeof(HRESOURCE) * 4];
+  AHRESOURCE _far *rc_list = (AHRESOURCE _far *) rc_list_buf;
+  RESOURCESTRUCT resource;
+  ADAPTERSTRUCT adapter;
+  ADJUNCT adj;
+  AD_INFO *ad_info;
+  APIRET ret;
+  UCHAR bus       = data->find_class.bus;
+  UCHAR dev_func  = data->find_class.dev_func;
+  ULONG val;
+  UCHAR rc;
+  SEL gdt[PORT_DMA_BUF_SEGS + 1];
+  char tmp[40];
+  u16 device;
+  u16 vendor;
+  u32 class;
+  u32 mmio_bios = 0;
+  u32 mmio_size;
+  u32 mmio_rqd;
+  int irq;
+  int pin;
+  int i;
+
+  /*****************************************************************************
+   * Part 1: Get further information about the device to be added; PCI ID...
+   */
+  if (pci_read_conf(bus, dev_func, 0x00, sizeof(ULONG), &val) != OH_SUCCESS) {
+    return;
+  }
+  device = (u16) (val >> 16);
+  vendor = (u16) (val & 0xffff);
+
+  /* ... and class code */
+  if (pci_read_conf(bus, dev_func, 0x08, sizeof(ULONG), &val) != OH_SUCCESS) {
+    return;
+  } 
+  class = (u32) (val >> 8);
+
+  if (pci_id->device == PCI_ANY_ID) {
+    /* We found this device in a wildcard search. There are two possible
+     * reasons which require a different handling:
+     *
+     *  1) This device uses a non-standard PCI class and has been identified
+     *     with the corresponding class in pci_ids[] (e.g. the entry
+     *     PCI_VENDOR_ID_JMICRON), but there is a vendor ID in pci_ids[]. In
+     *     this case, we need to verify that the vendor is correct (see
+     *     comments regarding OEMHLP limitations in 'scan_pci_bus()')
+     *
+     *  2) This device was identified using a generic PCI class for AHCI
+     *     adapters such as PCI_CLASS_STORAGE_SATA_AHCI and we need to map
+     *     the device and vendor ID to the corresponding index in pci_ids[]
+     *     if there is such an entry; the index passed to this function will
+     *     be the generic class-based index which is fine as long as there's
+     *     not special treatment required as indicated by the board_*
+     *     constants in pci_ids[]...
+     *
+     *     The main reason for this kludge is that it seems as if OEMHLP$
+     *     is rather slow searching for PCI devices, adding around 30s
+     *     to the boot time when scanning for individual AHCI PCI IDs. Thus,
+     *     the OS2AHCI driver avoids this kind of scan in favor of a class-
+     *     based scan (unless overridden with the "/T" option).
+     */
+    if (pci_id->vendor != PCI_ANY_ID) {
+      /* case 1: the vendor is known but we found the PCI device using a class
+       * search; verify vendor matches the one in pci_ids[]
+       */
+      if (pci_id->vendor != vendor) {
+        /* vendor doesn't match */
+        return;
+      }
+
+    } else {
+      /* case 2: we found this device using a generic class search; if the
+       * device/vendor is listed in pci_ids[], use this entry in favor of the
+       * one passed in 'pci_id'
+       */
+      for (i = 0; pci_ids[i].vendor != 0; i++) {
+        if (pci_ids[i].device == device && pci_ids[i].vendor == vendor) {
+          pci_id = pci_ids + i;
+          break;
+        }
+      }
+    }
+  }
+
+  /* found a supported AHCI device */
+  cprintf("found AHCI device: %04x:%04x (class = 0x%06lx, bus = %d, dev_func = 0x%02x)\n",
+          vendor, device, class, bus, dev_func);
+
+  /* make sure we got room in the adapter information array */
+  if (ad_info_cnt >= MAX_AD - 1) {
+    cprintf("error: too many AHCI devices\n");
+    return;
+  }
+
+  /****************************************************************************
+   * Part 2: Determine resource requirements and allocate resources with the
+   *         OS/2 resource manager. While doing so, some of the entries of the
+   *         corresponding slot in the AD_INFO array, namely resource manager
+   *         handles, are initialized so we need prepare the slot.
+   *
+   * NOTE: While registering resources with the resource manager, each new
+   *       resource is added to the corresponding rc_list.hResource[] slot.
+   *       rc_list is used further down to associate resources to adapters
+   *       whe the adapter itself is registered with the OS/2 resource manager.
+   */
+  ad_info = ad_infos + ad_info_cnt;
+  memset(ad_info, 0x00, sizeof(*ad_info));
+  rc_list->NumResource = 0;
+
+  /* Register IRQ with resource manager
+   *
+   * NOTE: We rely on the IRQ number saved in the PCI config space by the PCI
+   *       BIOS. There's no reliable way to find out the IRQ number in any
+   *       other way unless we start using message-driven interrupts (which
+   *       is out of scope for the time being).
+   */
+  if (pci_read_conf(bus, dev_func, 0x3c, sizeof(u32), &val) != OH_SUCCESS) {
+    return;
+  }
+  irq = (int) (val & 0xff);
+  pin = (int) ((val >> 8) & 0xff);
+
+  memset(&resource, 0x00, sizeof(resource));
+  resource.ResourceType          = RS_TYPE_IRQ;
+  resource.IRQResource.IRQLevel  = irq;
+  resource.IRQResource.PCIIrqPin = pin;
+  resource.IRQResource.IRQFlags  = RS_IRQ_SHARED;
+
+  ret = RMAllocResource(rm_drvh, &ad_info->rm_irq, &resource);
+  if (ret != RMRC_SUCCESS) {
+    cprintf("error: couldn't register IRQ %d (rc = %d)\n", irq, ret);
+    return;
+  }
+  rc_list->hResource[rc_list->NumResource++] = ad_info->rm_irq;
+
+  /* Determine MMIO size for this device
+   *
+   * NOTE: In order to do this, we need to temporarily write 0xffffffff to
+   *       the MMIO base address register (BAR), read back the resulting value
+   *       and check the 0 bits from the right end. After doing this, we must
+   *       restore the original value set up by the BIOS because we're not yet
+   *       ready to take over.
+   *
+   * The least significant 4 bits are not relevant for the MMIO address, thus
+   * we'll start at 0x10:
+   *
+   *  31                                                        4 3 2 1 0
+   *  -------------------------------------------------------------------
+   *                        base address                          P T T I
+   *  P = prefetchable
+   *  T = type
+   *  I = I/O (1) or memory (0)
+   */
+  if (pci_read_conf (bus, dev_func, AHCI_MMIO, sizeof(u32), &mmio_bios) != OH_SUCCESS ||
+      pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), ~(0UL))     != OH_SUCCESS ||
+      pci_read_conf (bus, dev_func, AHCI_MMIO, sizeof(u32), &mmio_rqd)  != OH_SUCCESS ||
+      pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), mmio_bios)  != OH_SUCCESS) {
+
+    cprintf("error: couldn't determine MMIO size\n");
+    if (mmio_bios != 0) {
+      cprintf("restoring BIOS MMIO address\n");
+      pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), mmio_bios);
+    }
+    goto add_pci_fail;
+  }
+  for (mmio_size = 0x00000010UL;
+       mmio_size < 0x10000000UL && (mmio_rqd & mmio_size) == 0;
+       mmio_size <<= 1);
+
+  ddprintf("MMIO size            = %ld\n", mmio_size);
+  ddprintf("MMIO address (BIOS)  = 0x%08lx\n", mmio_bios & 0xfffffff0UL);
+
+  /* register BIOS MMIO address space with resource manager */
+  memset(&resource, 0x00, sizeof(resource));
+  resource.ResourceType         = RS_TYPE_MEM;
+  resource.MEMResource.MemBase  = mmio_bios & 0xfffffff0UL;
+  resource.MEMResource.MemSize  = mmio_size;
+  resource.MEMResource.MemFlags = RS_MEM_EXCLUSIVE;
+
+  ret = RMAllocResource(rm_drvh, &ad_info->rm_mmio, &resource);
+
+  if (ret != RMRC_SUCCESS) {
+    /* didn't work; try to find another MMIO region */
+    cprintf("warning: BIOS MMIO address not accepted by resource manager\n");
+    memset(&resource, 0x00, sizeof(resource));
+    resource.ResourceType         = RS_TYPE_MEM;
+    resource.MEMResource.MemSize  = mmio_size;
+    resource.MEMResource.MemFlags = RS_MEM_EXCLUSIVE | RS_SEARCH;
+
+    ret = RMAllocResource(rm_drvh, &ad_info->rm_mmio, &resource);
+  }
+
+  if (ret != RMRC_SUCCESS) {
+    cprintf("error: couldn't register MMIO region (rc = %d)\n", ret);
+    goto add_pci_fail;
+  }
+  rc_list->hResource[rc_list->NumResource++] = ad_info->rm_mmio;
+  ddprintf("MMIO address (final) = 0x%08lx\n", resource.MEMResource.MemBase);
+
+  /****************************************************************************
+   * Part 3: Fill in the remaining fields in the AD_INFO slot and allocate
+   * memory and GDT selectors for the adapter. Finally, register the adapter
+   * itself with the OS/2 resource manager
+   */
+  ad_info->pci      = pci_ids + i;
+  ad_info->bus      = bus;
+  ad_info->dev_func = dev_func;
+  ad_info->irq      = irq;
+  ad_info->mmio_phys = resource.MEMResource.MemBase;
+    
+  /* allocate memory for port-specific DMA scratch buffers */
+  if (DevHelp_AllocPhys((long) AHCI_PORT_PRIV_DMA_SZ * AHCI_MAX_PORTS,
+                        MEMTYPE_ABOVE_1M, &ad_info->dma_buf_phys) != 0) {
+    cprintf("error: couldn't allocate DMA scratch buffers for AHCI ports\n");
+    ad_info->dma_buf_phys = 0;
+    goto add_pci_fail;
+  }
+
+  /* allocate GDT selectors for memory-mapped I/O and DMA scratch buffers */
+  if (DevHelp_AllocGDTSelector(gdt, PORT_DMA_BUF_SEGS + 1) != 0) {
+    cprintf("error: couldn't allocate GDT selectors\n");
+    memset(gdt, 0x00, sizeof(gdt));
+    goto add_pci_fail;
+  }
+
+  /* map MMIO address to first GDT selector */
+  if (DevHelp_PhysToGDTSelector(ad_info->mmio_phys, (USHORT) mmio_size,
+                                gdt[0]) != 0) {
+    cprintf("error: couldn't map MMIO address to GDT selector\n");
+    goto add_pci_fail;
+  }
+
+  /* map DMA scratch buffers to remaining GDT selectors */
+  for (i = 0; i < PORT_DMA_BUF_SEGS; i++) {
+    ULONG addr = ad_info->dma_buf_phys + i * PORT_DMA_SEG_SIZE;
+    USHORT len = AHCI_PORT_PRIV_DMA_SZ * PORT_DMA_BUFS_PER_SEG;
+
+    if (DevHelp_PhysToGDTSelector(addr, len, gdt[i+1]) != 0) {
+      cprintf("error: couldn't map DMA scratch buffer to GDT selector\n");
+      goto add_pci_fail;
+    }
+  }
+
+  /* fill in MMIO and DMA scratch buffer addresses in adapter info */
+  ad_info->mmio = (u8 _far *) ((u32) gdt[0] << 16);
+  for (i = 0; i < PORT_DMA_BUF_SEGS; i++) {
+    ad_info->dma_buf[i] = (u8 _far *) ((u32) gdt[i+1] << 16);
+  }
+
+  /* register adapter with resource manager */
+  memset(&adj, 0x00, sizeof(adj));
+  adj.pNextAdj             = NULL;
+  adj.AdjLength            = sizeof(adj);
+  adj.AdjType              = ADJ_ADAPTER_NUMBER;
+  adj.Adapter_Number       = ad_info_cnt;
+
+  memset(&adapter, 0x00, sizeof(adapter));
+  sprintf(tmp, "AHCI_%d Controller", ad_info_cnt);
+  adapter.AdaptDescriptName = tmp;
+  adapter.AdaptFlags        = 0;
+  adapter.BaseType          = AS_BASE_MSD;
+  adapter.SubType           = AS_SUB_IDE;
+  adapter.InterfaceType     = AS_INTF_GENERIC;
+  adapter.HostBusType       = AS_HOSTBUS_PCI;
+  adapter.HostBusWidth      = AS_BUSWIDTH_32BIT;
+  adapter.pAdjunctList      = &adj;
+
+  ret = RMCreateAdapter(rm_drvh, &ad_info->rm_adh, &adapter, NULL, rc_list);
+  if (ret != RMRC_SUCCESS) {
+    cprintf("error: couldn't register adapter (rc = %d)\n", ret);
+    goto add_pci_fail;
+  }
+
+  /* Successfully added the adapter and reserved its resources; the adapter
+   * is still under BIOS control so we're not going to do anything else at
+   * this point.
+   */
+  ad_info_cnt++;
+  return;
+
+add_pci_fail:
+  /* something went wrong; try to clean up as far as possible */
+  if (ad_info->rm_mmio != 0) {
+    RMDeallocResource(rm_drvh, ad_info->rm_mmio);
+  }
+  if (ad_info->rm_irq != 0) {
+    RMDeallocResource(rm_drvh, ad_info->rm_irq);
+  }
+  if (&ad_info->dma_buf_phys != 0) {
+    DevHelp_FreePhys(ad_info->dma_buf_phys);
+  }
+  for (i = 0; i < sizeof(gdt) / sizeof(*gdt); i++) {
+    if (gdt[i] != 0) {
+      DevHelp_FreeGDTSelector(gdt[i]);
+    }
+  }
+}
+
+/******************************************************************************
+ * Read PCI configuration space register
+ */
+static UCHAR pci_read_conf(UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
+                           ULONG _far *val)
+{
+  OH_PARM parm;
+  OH_DATA data;
+  UCHAR rc;
+
+  memset(&parm, 0x00, sizeof(parm));
+  parm.read_config.bus      = bus;
+  parm.read_config.dev_func = dev_func;
+  parm.read_config.reg      = indx;
+  parm.read_config.size     = size;
+  if ((rc = oemhlp_call(OH_READ_CONFIG, &parm, &data) != OH_SUCCESS)) {
+    cprintf("error: couldn't read config space (bus = %d, dev_func = 0x%02x, indx = 0x%02x, rc = %d)\n",
+            bus, dev_func, indx,  rc);
+    return(rc);
+  }
+
+  *val = data.read_config.data;
+  return(OH_SUCCESS);
+}
+
+/******************************************************************************
+ * Write PCI configuration space register
+ */
+static UCHAR pci_write_conf(UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
+                            ULONG val)
+{
+  OH_PARM parm;
+  OH_DATA data;
+  UCHAR rc;
+
+  memset(&parm, 0x00, sizeof(parm));
+  parm.write_config.bus      = bus;
+  parm.write_config.dev_func = dev_func;
+  parm.write_config.reg      = indx;
+  parm.write_config.size     = size;
+  parm.write_config.data     = val;
+
+  if ((rc = oemhlp_call(OH_WRITE_CONFIG, &parm, &data) != OH_SUCCESS)) {
+    cprintf("error: couldn't write config space (bus = %d, dev_func = 0x%02x, indx = 0x%02x, rc = %d)\n",
+            bus, dev_func, indx, rc);
+    return(rc);
+  }
+
+  return(OH_SUCCESS);
+}
+/******************************************************************************
+ * Call OEMHLP$ IDC entry point with the specified IOCtl parameter and data
+ * packets.
+ */
+static int oemhlp_call(UCHAR subfunction, OH_PARM _far *parm,
+                       OH_DATA _far *data)
+{
+  void (_far *func)(void);
+  RP_GENIOCTL ioctl;
+
+  if (oemhlp.ProtIDCEntry == NULL || oemhlp.ProtIDC_DS == 0) {
+    /* attach to OEMHLP$ device driver */
+    if (DevHelp_AttachDD("OEMHLP$ ", (NPBYTE) &oemhlp) ||
+        oemhlp.ProtIDCEntry == NULL ||
+        oemhlp.ProtIDC_DS == 0) {
+      cprintf("couldn't attach to OEMHLP$\n");
+      return(OH_NOT_SUPPORTED);
+    }
+  }
+
+  /* store subfuntion in first byte of pararameter packet */
+  parm->bios_info.subfunction = subfunction;
+  memset(data, 0x00, sizeof(*data));
+
+  /* assemble IOCtl request */
+  memset(&ioctl, 0x00, sizeof(ioctl));
+  ioctl.rph.Len     = sizeof(ioctl);
+  ioctl.rph.Unit    = 0;
+  ioctl.rph.Cmd     = GENERIC_IOCTL;
+  ioctl.rph.Status  = 0;
+
+  ioctl.Category    = OH_CATEGORY;
+  ioctl.Function    = OH_FUNC_PCI;
+  ioctl.ParmPacket  = (PUCHAR) parm;
+  ioctl.DataPacket  = (PUCHAR) data;
+  ioctl.ParmLen     = sizeof(*parm);
+  ioctl.DataLen     = sizeof(*data);
+
+  /* Call OEMHLP's IDC routine. Before doing so, we need to assign the address
+   * to be called to a stack variable because the inter-device driver calling
+   * convention forces us to set DS to the device driver's data segment and ES
+   * to the segment of the request packet. 
+   */
+  func = oemhlp.ProtIDCEntry;
+
+  _asm {
+    push ds;
+    push es;
+    push bx;
+    push si;
+    push di;
+
+    push ss
+    pop  es
+    lea  bx, ioctl;
+    mov  ds, oemhlp.ProtIDC_DS;
+    call dword ptr [func];
+
+    pop  di;
+    pop  si;
+    pop  bx;
+    pop  es;
+    pop  ds;
+  }
+
+  dddphex(parm, sizeof(*parm), "oemhlp_parm: ");
+  dddphex(data, sizeof(*data), "oemhlp_data: ");
+
+  if (ioctl.rph.Status & STERR) {
+    return(OH_NOT_SUPPORTED);
+  }
+  return(data->bios_info.rc);
+}
Index: trunk/src/os2ahci/wmakefile
===================================================================
--- trunk/src/os2ahci/wmakefile	(revision 13)
+++ trunk/src/os2ahci/wmakefile	(revision 13)
@@ -0,0 +1,144 @@
+###############################################################################
+#
+# wmakefile - Watcom makefile for os2ahci driver
+#
+# NOTE: This is a wmake file! See here:
+# http://www.openwatcom.org/index.php/Using_wmake
+#
+
+#
+# Copyright (c) 2010 Markus Thielen, Christian Mueller. 
+# Parts copied from/inspired by the Linux AHCI driver; 
+# those parts are (c) Linux AHCI/ATA maintainers
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+###############################################################################
+# wmake options
+
+# disable prompt to delete out of date files on compile errors
+.ERASE   
+
+
+###############################################################################
+# Tool Chain
+
+AS            = wasm    # Watcom assembler  
+CC            = wcc     # Watcom 16bit compiler
+LD            = wlink   # Watcom linker
+
+
+###############################################################################
+# Environment
+#
+# we need the following set before this makefile is called:
+#
+# WATCOM - to the base WATCOM installation directory
+# DDK    - to the base dir of the OS/2 DDK to use
+# PS     - the path separator (/ or \)
+#
+
+DDK          = $(%DDK)
+WATCOM       = $(%WATCOM)
+!ifndef %PS
+PS           = \
+!else
+PS           = $(%PS)
+!endif
+
+CC_INCLUDE   = -I$(DDK)$(PS)BASE$(PS)H &
+               -I$(DDK)$(PS)BASE$(PS)IBMH &
+               -I$(DDK)$(PS)BASE$(PS)SRC$(PS)DEV$(PS)DASD$(PS)DISKH &
+               -I$(WATCOM)$(PS)h &
+
+AS_INCLUDE   = -I$(DDK)$(PS)BASE$(PS)INC &
+               -I$(DDK)$(PS)BASE$(PS)SRC$(PS)DEV$(PS)DASD$(PS)DISKINC
+
+LIB_DIRS     = $(DDK)$(PS)BASE$(PS)LIB$;$(DDK)$(PS)BASE$(PS)SRC$(PS)DEV$(PS)DASD$(PS)DEVHLP;$(WATCOM)$(PS)LIB;
+
+
+###############################################################################
+# Main dependencies
+
+TARGET   = os2ahci.add
+
+LIBS     = os2,addcalls,dhcalls,doscalls,rmcalls
+
+SRCS     = init.asm libc.c os2ahci.c pci.c ahci.c ata.c atapi.c ctxhook.c
+
+OBJS     = init.obj libc.obj os2ahci.obj pci.obj ahci.obj ata.obj atapi.obj&
+           ctxhook.obj
+
+#          the Watcom linker needs commas between object files
+LDOBJS   = init.obj,libc.obj,os2ahci.obj,pci.obj,ahci.obj,ata.obj,atapi.obj,&
+           ctxhook.obj
+
+INCS     = os2ahci.h ahci.h
+
+
+all: $(TARGET)
+
+clean:
+	rm -f $(OBJS) $(TARGET)
+
+
+###############################################################################
+# compiler/linker flags
+
+AFLAGS        = -2 -bt=os2 -w3 -zq -ms -zcm
+CFLAGS        = -bt=os2 -ms -s -zdp -zff -zgf -zu -zl -2 -w2 -wcd=138 -zp=1 -q
+CFLAGS_DEBUG  = -d3 -hc
+LFLAGS        = name $(TARGET) sys os2 dll initglobal option map option quiet &
+                option nodefaultlibs libpath $(LIB_DIRS) file $(LDOBJS) &
+		library $(LIBS) 
+LFLAGS_DEBUG  = DEBUG CODEVIEW option cvpack
+
+!IFDEF DEBUG
+CFLAGS = $(CFLAGS) $(CFLAGS_DEBUG)
+LFLAGS = $(LFLAGS) $(LFLAGS_DEBUG)
+!ENDIF
+
+###############################################################################
+# Object/source dependencies
+
+init.obj:     init.asm   wmakefile
+
+libc.obj:     libc.c     wmakefile $(INCS)
+
+os2ahci.obj:  os2ahci.c  wmakefile $(INCS)
+
+pci.obj:      pci.c      wmakefile $(INCS)
+
+ahci.obj:     ahci.c     wmakefile $(INCS) ata.h atapi.h
+
+ata.obj:      ata.c      wmakefile $(INCS) ata.h
+
+atapi.obj:    atapi.c    wmakefile $(INCS) ata.h atapi.h
+
+ctxhook.obj:  ctxhook.c  wmakefile $(INCS)
+
+###############################################################################
+# Action definitions (compile/link commands)
+
+.asm.obj:
+	$(AS) $(AFLAGS) $(AS_INCLUDE) $*.asm -fo=$*.obj
+
+.c.obj:
+	$(CC) $(CFLAGS) $(CC_INCLUDE) $*.c
+
+$(TARGET): $(OBJS) os2ahci.def wmakefile
+	$(LD) $(LFLAGS) 
+
+
Index: trunk/src/pci.c
===================================================================
--- trunk/src/pci.c	(revision 12)
+++ 	(revision )
@@ -1,901 +1,0 @@
-/******************************************************************************
- * pci.c - PCI constants and detection code for os2ahci driver
- *
- * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
- * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "os2ahci.h"
-
-/* -------------------------- macros and constants ------------------------- */
-
-/* offset of PCI base address register (BAR) in the PCI config space */
-#define PCI_BAR(reg)   (0x10 + (reg) * sizeof(u32))
-
-/* offset of MMIO base address register (BAR) for AHCI adapters */
-#define AHCI_MMIO      PCI_BAR(AHCI_PCI_BAR)
-
-/******************************************************************************
- * OEMHLP constants for PCI access
- */
-#define GENERIC_IOCTL       0x10 
-#define OH_CATEGORY         0x00
-#define OH_FUNC_PCI         0x0b
-
-/* subfunctions */
-#define OH_BIOS_INFO        0x00
-#define OH_FIND_DEVICE      0x01
-#define OH_FIND_CLASS       0x02
-#define OH_READ_CONFIG      0x03
-#define OH_WRITE_CONFIG     0x04
-
-/* return codes */
-#define OH_SUCCESS          0x00
-#define OH_NOT_SUPPORTED    0x81
-#define OH_BAD_VENDOR       0x83
-#define OH_NOT_FOUND        0x86
-#define OH_BAD_REGISTER     0x87
-
-/* ------------------------ typedefs and structures ------------------------ */
-
-/******************************************************************************
- * OEMHLP IOCtl parameter union. The parameter area is generally used as input
- * to the OEMHLP IOCtl calls.
- */
-typedef union {
-
-  /* query PCI BIOS information" */
-  struct {
-    UCHAR    subfunction;
-  } bios_info;
-
-  /* find PCI device */
-  struct {
-    UCHAR    subfunction;
-    USHORT   device;
-    USHORT   vendor;
-    UCHAR    index;
-  } find_device;
-
-  /* find PCI class code */
-  struct {
-    UCHAR    subfunction;
-    ULONG    class;
-    UCHAR    index;
-  } find_class;
-
-  /* read PCI configuration space */
-  struct {
-    UCHAR    subfunction;
-    UCHAR    bus;
-    UCHAR    dev_func;
-    UCHAR    reg;
-    UCHAR    size;
-  } read_config;
-
-  /* write PCI configuration space */
-  struct {
-    UCHAR    subfunction;
-    UCHAR    bus;
-    UCHAR    dev_func;
-    UCHAR    reg;
-    UCHAR    size;
-    ULONG    data;
-  } write_config;
-
-} OH_PARM;
-  
-/******************************************************************************
- * OEMHLP IOCtl data union. The data area is generally used as output from the
- * OEMHLP IOCtl calls.
- */
-typedef union {
-
-  /* query PCI BIOS information" */
-  struct {
-    UCHAR    rc;
-    UCHAR    hw_mech;
-    UCHAR    major_version;
-    UCHAR    minor_version;
-    UCHAR    last_bus;
-  } bios_info;
-
-  /* find PCI device */
-  struct {
-    UCHAR    rc;
-    UCHAR    bus;
-    UCHAR    dev_func;
-  } find_device;
-
-  /* find PCI class code */
-  struct {
-    UCHAR    rc;
-    UCHAR    bus;
-    UCHAR    dev_func;
-  } find_class;
-
-  /* read PCI confguration space */
-  struct {
-    UCHAR    rc;
-    ULONG    data;
-  } read_config;
-
-  /* write PCI confguration space */
-  struct {
-    UCHAR    rc;
-  } write_config;
-
-} OH_DATA;
-
-/* -------------------------- function prototypes -------------------------- */
-
-static void  add_pci_device   (PCI_ID *pci_id, OH_DATA _far *data);
-static UCHAR pci_read_conf    (UCHAR bus, UCHAR dev_func, UCHAR indx,
-                               UCHAR size, ULONG _far *val);
-static UCHAR pci_write_conf   (UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
-                               ULONG val);
-static int   oemhlp_call      (UCHAR subfunction, OH_PARM _far *parm,
-                               OH_DATA _far *data);
-
-/* ------------------------ global/static variables ------------------------ */
-
-/******************************************************************************
- * PCI vendor and device IDs for known AHCI adapters. Copied from the Linux
- * AHCI driver.
- */
-
-PCI_ID pci_ids[] = {
-
-  /* Intel */
-  { PCI_VDEVICE(INTEL, 0x2652), board_ahci }, /* ICH6 */
-  { PCI_VDEVICE(INTEL, 0x2653), board_ahci }, /* ICH6M */
-  { PCI_VDEVICE(INTEL, 0x27c1), board_ahci }, /* ICH7 */
-  { PCI_VDEVICE(INTEL, 0x27c5), board_ahci }, /* ICH7M */
-  { PCI_VDEVICE(INTEL, 0x27c3), board_ahci }, /* ICH7R */
-  { PCI_VDEVICE(AL,    0x5288), board_ahci_ign_iferr }, /* ULi M5288 */
-  { PCI_VDEVICE(INTEL, 0x2681), board_ahci }, /* ESB2 */
-  { PCI_VDEVICE(INTEL, 0x2682), board_ahci }, /* ESB2 */
-  { PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
-  { PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
-  { PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
-  { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */
-  { PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
-  { PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
-  { PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
-  { PCI_VDEVICE(INTEL, 0x2922), board_ahci }, /* ICH9 */
-  { PCI_VDEVICE(INTEL, 0x2923), board_ahci }, /* ICH9 */
-  { PCI_VDEVICE(INTEL, 0x2924), board_ahci }, /* ICH9 */
-  { PCI_VDEVICE(INTEL, 0x2925), board_ahci }, /* ICH9 */
-  { PCI_VDEVICE(INTEL, 0x2927), board_ahci }, /* ICH9 */
-  { PCI_VDEVICE(INTEL, 0x2929), board_ahci }, /* ICH9M */
-  { PCI_VDEVICE(INTEL, 0x292a), board_ahci }, /* ICH9M */
-  { PCI_VDEVICE(INTEL, 0x292b), board_ahci }, /* ICH9M */
-  { PCI_VDEVICE(INTEL, 0x292c), board_ahci }, /* ICH9M */
-  { PCI_VDEVICE(INTEL, 0x292f), board_ahci }, /* ICH9M */
-  { PCI_VDEVICE(INTEL, 0x294d), board_ahci }, /* ICH9 */
-  { PCI_VDEVICE(INTEL, 0x294e), board_ahci }, /* ICH9M */
-  { PCI_VDEVICE(INTEL, 0x502a), board_ahci }, /* Tolapai */
-  { PCI_VDEVICE(INTEL, 0x502b), board_ahci }, /* Tolapai */
-  { PCI_VDEVICE(INTEL, 0x3a05), board_ahci }, /* ICH10 */
-  { PCI_VDEVICE(INTEL, 0x3a22), board_ahci }, /* ICH10 */
-  { PCI_VDEVICE(INTEL, 0x3a25), board_ahci }, /* ICH10 */
-  { PCI_VDEVICE(INTEL, 0x3b22), board_ahci }, /* PCH AHCI */
-  { PCI_VDEVICE(INTEL, 0x3b23), board_ahci }, /* PCH AHCI */
-  { PCI_VDEVICE(INTEL, 0x3b24), board_ahci }, /* PCH RAID */
-  { PCI_VDEVICE(INTEL, 0x3b25), board_ahci }, /* PCH RAID */
-  { PCI_VDEVICE(INTEL, 0x3b29), board_ahci }, /* PCH AHCI */
-  { PCI_VDEVICE(INTEL, 0x3b2b), board_ahci }, /* PCH RAID */
-  { PCI_VDEVICE(INTEL, 0x3b2c), board_ahci }, /* PCH RAID */
-  { PCI_VDEVICE(INTEL, 0x3b2f), board_ahci }, /* PCH AHCI */
-
-  /* JMicron 360/1/3/5/6, match class to avoid IDE function */
-  { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
-    PCI_CLASS_STORAGE_SATA_AHCI, 0xffffffL, board_ahci_ign_iferr },
-
-  /* ATI */
-  { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
-  { PCI_VDEVICE(ATI, 0x4390), board_ahci_sb700 }, /* ATI SB700/800 */
-  { PCI_VDEVICE(ATI, 0x4391), board_ahci_sb700 }, /* ATI SB700/800 */
-  { PCI_VDEVICE(ATI, 0x4392), board_ahci_sb700 }, /* ATI SB700/800 */
-  { PCI_VDEVICE(ATI, 0x4393), board_ahci_sb700 }, /* ATI SB700/800 */
-  { PCI_VDEVICE(ATI, 0x4394), board_ahci_sb700 }, /* ATI SB700/800 */
-  { PCI_VDEVICE(ATI, 0x4395), board_ahci_sb700 }, /* ATI SB700/800 */
-
-  /* AMD */
-  { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */
-  /* AMD is using RAID class only for ahci controllers */
-  { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
-    PCI_CLASS_STORAGE_RAID << 8, 0xffffffL, board_ahci },
-
-  /* VIA */
-  { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
-  { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
-
-  /* NVIDIA */
-  { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x044d), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x044e), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x044f), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x045c), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x045d), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x045e), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x045f), board_ahci_mcp65 },      /* MCP65 */
-  { PCI_VDEVICE(NVIDIA, 0x0550), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0551), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0552), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0553), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0554), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0555), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0556), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0557), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0558), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0559), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x055a), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x055b), board_ahci_yesncq },     /* MCP67 */
-  { PCI_VDEVICE(NVIDIA, 0x0580), board_ahci_yesncq },     /* Linux ID */
-  { PCI_VDEVICE(NVIDIA, 0x07f0), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f1), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f2), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f3), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f4), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f5), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f6), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f7), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f8), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07f9), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07fa), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x07fb), board_ahci_yesncq },     /* MCP73 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad0), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad1), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad2), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad3), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad4), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad5), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad6), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad7), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad8), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ad9), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ada), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0adb), board_ahci },            /* MCP77 */
-  { PCI_VDEVICE(NVIDIA, 0x0ab4), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0ab5), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0ab6), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0ab7), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0ab8), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0ab9), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0aba), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0abb), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0abc), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0abd), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0abe), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0abf), board_ahci },            /* MCP79 */
-  { PCI_VDEVICE(NVIDIA, 0x0d84), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d85), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d86), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d87), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d88), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d89), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d8a), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d8b), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d8c), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d8d), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d8e), board_ahci },            /* MCP89 */
-  { PCI_VDEVICE(NVIDIA, 0x0d8f), board_ahci },            /* MCP89 */
-
-  /* SiS */
-  { PCI_VDEVICE(SI, 0x1184), board_ahci },                /* SiS 966 */
-  { PCI_VDEVICE(SI, 0x1185), board_ahci },                /* SiS 968 */
-  { PCI_VDEVICE(SI, 0x0186), board_ahci },                /* SiS 968 */
-
-  /* Marvell */
-  { PCI_VDEVICE(MARVELL, 0x6145), board_ahci_mv },        /* 6145 */
-  { PCI_VDEVICE(MARVELL, 0x6121), board_ahci_mv },        /* 6121 */
-
-  /* Promise */
-  { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci },   /* PDC42819 */
-
-  /* Generic, PCI class code for AHCI */
-  { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
-    PCI_CLASS_STORAGE_SATA_AHCI, 0xffffffL, board_ahci },
-
-  /* end of list, including a few slots to define custom adapters (10) */
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-  { 0, 0, 0, 0, 0, 0, 0 },
-
-  { 0, 0, 0, 0, 0, 0, 0 }
-};
-
-/******************************************************************************
- * OEMHLP$ is used by OS/2 to provide access to OEM-specific machine resources
- * like PCI BIOS access. We're using this to enumerate the PCI bus. Due to
- * BIOS bugs, it may be necessary to use I/O operations for this purpose but
- * so far I think this is only relevant for rather old PCs and SATA is not
- * expected to be a priority on those machines.
- */
-static IDCTABLE     oemhlp;            /* OEMHLP$ IDC entry point */
-
-/* ----------------------------- start of code ----------------------------- */
-
-/******************************************************************************
- * Add specified PCI vendor and device ID to the list of supported AHCI
- * controllers. Please note that the last slot in pci_ids needs to remain
- * empty because it's used as end marker.
- */
-int add_pci_id(u16 vendor, u16 device)
-{
-  int max_slot = sizeof(pci_ids) / sizeof(*pci_ids) - 2;
-  int i;
-
-  /* search for last used slot in 'pci_ids' */
-  for (i = max_slot; i >= 0 && pci_ids[i].vendor == 0; i--);
-  if (i >= max_slot) {
-    /* all slots in use */
-    return(-1);
-  }
-
-  /* use slot after the last used slot */
-  i++;
-  pci_ids[i].vendor = vendor;
-  pci_ids[i].device = device;
-  pci_ids[i].board = board_ahci;
-  return(0);
-}
-
-/******************************************************************************
- * Scan PCI bus using OEMHLP$ IOCTLs and build adapter list.
- */
-void scan_pci_bus(void)
-{
-  OH_PARM parm;
-  OH_DATA data;
-  UCHAR index;
-  UCHAR rc;
-  int i;
-
-  ddprintf("scanning PCI bus...\n");
-
-  /* verify that we have a PCI system */
-  memset(&parm, 0x00, sizeof(parm));
-  if (oemhlp_call(OH_BIOS_INFO, &parm, &data) != OH_SUCCESS) {
-    cprintf("couldn't get PCI BIOS information\n");
-    return;
-  }
-
-  /* Go through the list of PCI IDs and search for each device 
-   *
-   * NOTES:
-   *
-   *  - When searching via class code, the OEMHLP$ interface doesn't allow
-   *    setting a bitmask to look for individual portions of class code,
-   *    subclass code and programming interface. However, all bitmasks in the
-   *    PCI list currently use 0xffffff, thus this should not be a problem at
-   *    this point in time.
-   *
-   *  - Scanning via OEMHLP$ seems rather slow, at least in the virtual
-   *    machine I'm currenly using to test this driver. Thus, class code
-   *    scans are preferred unless the option "-t" (thorough_scan) has been
-   *    specified. The assumption is that most, if not all, modern AHCI
-   *    adapters have the correct class code (PCI_CLASS_STORAGE_SATA_AHCI).
-   */
-  for (i = 0; pci_ids[i].vendor != 0; i++) {
-    index = 0;
-    do {
-      if (pci_ids[i].device == PCI_ANY_ID || pci_ids[i].vendor == PCI_ANY_ID) {
-        /* look for class code */
-        memset(&parm, 0x00, sizeof(parm));
-        parm.find_class.class = pci_ids[i].class;
-        parm.find_class.index = index;
-        rc = oemhlp_call(OH_FIND_CLASS, &parm, &data);
-
-      } else if (thorough_scan) {
-        /* look for this specific vendor and device ID */
-        memset(&parm, 0x00, sizeof(parm));
-        parm.find_device.device = pci_ids[i].device;
-        parm.find_device.vendor = pci_ids[i].vendor;
-        parm.find_device.index = index;
-        rc = oemhlp_call(OH_FIND_DEVICE, &parm, &data);
-
-      } else {
-        rc = OH_NOT_FOUND;
-      }
-
-      if (rc == OH_SUCCESS) {
-        /* found a device */
-        add_pci_device(pci_ids + i, &data);
-        if (++index > 180) {
-          /* something's wrong here... */
-          return;
-        }
-      }
-
-    } while (rc == OH_SUCCESS);
-  }
-}
-
-/******************************************************************************
- * Enable interrupt generation. PCI 2.3 added a bit which allows disabling
- * interrupt generation for a device. This function clears the corresponding
- * bit in the configuration space command register.
- */
-int pci_enable_int(UCHAR bus, UCHAR dev_func)
-{
-  ULONG tmp;
-
-  if (pci_read_conf (bus, dev_func, 4, sizeof(u32), &tmp) != OH_SUCCESS ||
-      pci_write_conf(bus, dev_func, 4, sizeof(u32), tmp & ~(1UL << 10)) != OH_SUCCESS) {
-    return(-1);
-  }
-  return(0);
-}
-
-/******************************************************************************
- * Hack to set up proper IRQ mappings in the emulated PIIX3 ISA bridge in
- * VirtualBox (for some reason, the first mapped IRQ is 0x80 without this
- * hack).
- */
-void pci_hack_virtualbox(void)
-{
-  ULONG irq = 0;
-
-  if (pci_read_conf(0, 0x08, 0x60, 1, &irq) == OH_SUCCESS && irq == 0x80) {
-    /* set IRQ for first device/func to 11 */
-    dprintf("hacking virtualbox PIIX3 PCI to ISA bridge IRQ mapping\n");
-    irq = ad_infos[0].irq;
-    pci_write_conf(0, 0x08, 0x60, 1, irq);
-  }
-}
-
-/******************************************************************************
- * Add a single PCI device to the list of adapters.
- */
-static void add_pci_device(PCI_ID *pci_id, OH_DATA _far *data)
-{
-  char rc_list_buf[sizeof(AHRESOURCE) + sizeof(HRESOURCE) * 4];
-  AHRESOURCE _far *rc_list = (AHRESOURCE _far *) rc_list_buf;
-  RESOURCESTRUCT resource;
-  ADAPTERSTRUCT adapter;
-  ADJUNCT adj;
-  AD_INFO *ad_info;
-  APIRET ret;
-  UCHAR bus       = data->find_class.bus;
-  UCHAR dev_func  = data->find_class.dev_func;
-  ULONG val;
-  SEL gdt[PORT_DMA_BUF_SEGS + 1];
-  char tmp[40];
-  u16 device;
-  u16 vendor;
-  u32 class;
-  u32 mmio_bios = 0;
-  u32 mmio_size;
-  u32 mmio_rqd;
-  int irq;
-  int pin;
-  int i;
-
-  /*****************************************************************************
-   * Part 1: Get further information about the device to be added; PCI ID...
-   */
-  if (pci_read_conf(bus, dev_func, 0x00, sizeof(ULONG), &val) != OH_SUCCESS) {
-    return;
-  }
-  device = (u16) (val >> 16);
-  vendor = (u16) (val & 0xffff);
-
-  /* ... and class code */
-  if (pci_read_conf(bus, dev_func, 0x08, sizeof(ULONG), &val) != OH_SUCCESS) {
-    return;
-  } 
-  class = (u32) (val >> 8);
-
-  if (pci_id->device == PCI_ANY_ID) {
-    /* We found this device in a wildcard search. There are two possible
-     * reasons which require a different handling:
-     *
-     *  1) This device uses a non-standard PCI class and has been identified
-     *     with the corresponding class in pci_ids[] (e.g. the entry
-     *     PCI_VENDOR_ID_JMICRON), but there is a vendor ID in pci_ids[]. In
-     *     this case, we need to verify that the vendor is correct (see
-     *     comments regarding OEMHLP limitations in 'scan_pci_bus()')
-     *
-     *  2) This device was identified using a generic PCI class for AHCI
-     *     adapters such as PCI_CLASS_STORAGE_SATA_AHCI and we need to map
-     *     the device and vendor ID to the corresponding index in pci_ids[]
-     *     if there is such an entry; the index passed to this function will
-     *     be the generic class-based index which is fine as long as there's
-     *     not special treatment required as indicated by the board_*
-     *     constants in pci_ids[]...
-     *
-     *     The main reason for this kludge is that it seems as if OEMHLP$
-     *     is rather slow searching for PCI devices, adding around 30s
-     *     to the boot time when scanning for individual AHCI PCI IDs. Thus,
-     *     the OS2AHCI driver avoids this kind of scan in favor of a class-
-     *     based scan (unless overridden with the "/T" option).
-     */
-    if (pci_id->vendor != PCI_ANY_ID) {
-      /* case 1: the vendor is known but we found the PCI device using a class
-       * search; verify vendor matches the one in pci_ids[]
-       */
-      if (pci_id->vendor != vendor) {
-        /* vendor doesn't match */
-        return;
-      }
-
-    } else {
-      /* case 2: we found this device using a generic class search; if the
-       * device/vendor is listed in pci_ids[], use this entry in favor of the
-       * one passed in 'pci_id'
-       */
-      for (i = 0; pci_ids[i].vendor != 0; i++) {
-        if (pci_ids[i].device == device && pci_ids[i].vendor == vendor) {
-          pci_id = pci_ids + i;
-          break;
-        }
-      }
-    }
-  }
-
-  /* found a supported AHCI device */
-  cprintf("found AHCI device: %04x:%04x (class = 0x%06lx, bus = %d, dev_func = 0x%02x)\n",
-          vendor, device, class, bus, dev_func);
-
-  /* make sure we got room in the adapter information array */
-  if (ad_info_cnt >= MAX_AD - 1) {
-    cprintf("error: too many AHCI devices\n");
-    return;
-  }
-
-  /****************************************************************************
-   * Part 2: Determine resource requirements and allocate resources with the
-   *         OS/2 resource manager. While doing so, some of the entries of the
-   *         corresponding slot in the AD_INFO array, namely resource manager
-   *         handles, are initialized so we need prepare the slot.
-   *
-   * NOTE: While registering resources with the resource manager, each new
-   *       resource is added to the corresponding rc_list.hResource[] slot.
-   *       rc_list is used further down to associate resources to adapters
-   *       whe the adapter itself is registered with the OS/2 resource manager.
-   */
-  ad_info = ad_infos + ad_info_cnt;
-  memset(ad_info, 0x00, sizeof(*ad_info));
-  rc_list->NumResource = 0;
-
-  /* Register IRQ with resource manager
-   *
-   * NOTE: We rely on the IRQ number saved in the PCI config space by the PCI
-   *       BIOS. There's no reliable way to find out the IRQ number in any
-   *       other way unless we start using message-driven interrupts (which
-   *       is out of scope for the time being).
-   */
-  if (pci_read_conf(bus, dev_func, 0x3c, sizeof(u32), &val) != OH_SUCCESS) {
-    return;
-  }
-  irq = (int) (val & 0xff);
-  pin = (int) ((val >> 8) & 0xff);
-
-  memset(&resource, 0x00, sizeof(resource));
-  resource.ResourceType          = RS_TYPE_IRQ;
-  resource.IRQResource.IRQLevel  = irq;
-  resource.IRQResource.PCIIrqPin = pin;
-  resource.IRQResource.IRQFlags  = RS_IRQ_SHARED;
-
-  ret = RMAllocResource(rm_drvh, &ad_info->rm_irq, &resource);
-  if (ret != RMRC_SUCCESS) {
-    cprintf("error: couldn't register IRQ %d (rc = %d)\n", irq, ret);
-    return;
-  }
-  rc_list->hResource[rc_list->NumResource++] = ad_info->rm_irq;
-
-  /* Determine MMIO size for this device
-   *
-   * NOTE: In order to do this, we need to temporarily write 0xffffffff to
-   *       the MMIO base address register (BAR), read back the resulting value
-   *       and check the 0 bits from the right end. After doing this, we must
-   *       restore the original value set up by the BIOS because we're not yet
-   *       ready to take over.
-   *
-   * The least significant 4 bits are not relevant for the MMIO address, thus
-   * we'll start at 0x10:
-   *
-   *  31                                                        4 3 2 1 0
-   *  -------------------------------------------------------------------
-   *                        base address                          P T T I
-   *  P = prefetchable
-   *  T = type
-   *  I = I/O (1) or memory (0)
-   */
-  if (pci_read_conf (bus, dev_func, AHCI_MMIO, sizeof(u32), &mmio_bios) != OH_SUCCESS ||
-      pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), ~(0UL))     != OH_SUCCESS ||
-      pci_read_conf (bus, dev_func, AHCI_MMIO, sizeof(u32), &mmio_rqd)  != OH_SUCCESS ||
-      pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), mmio_bios)  != OH_SUCCESS) {
-
-    cprintf("error: couldn't determine MMIO size\n");
-    if (mmio_bios != 0) {
-      cprintf("restoring BIOS MMIO address\n");
-      pci_write_conf(bus, dev_func, AHCI_MMIO, sizeof(u32), mmio_bios);
-    }
-    goto add_pci_fail;
-  }
-  for (mmio_size = 0x00000010UL;
-       mmio_size < 0x10000000UL && (mmio_rqd & mmio_size) == 0;
-       mmio_size <<= 1);
-
-  ddprintf("MMIO size            = %ld\n", mmio_size);
-  ddprintf("MMIO address (BIOS)  = 0x%08lx\n", mmio_bios & 0xfffffff0UL);
-
-  /* register BIOS MMIO address space with resource manager */
-  memset(&resource, 0x00, sizeof(resource));
-  resource.ResourceType         = RS_TYPE_MEM;
-  resource.MEMResource.MemBase  = mmio_bios & 0xfffffff0UL;
-  resource.MEMResource.MemSize  = mmio_size;
-  resource.MEMResource.MemFlags = RS_MEM_EXCLUSIVE;
-
-  ret = RMAllocResource(rm_drvh, &ad_info->rm_mmio, &resource);
-
-  if (ret != RMRC_SUCCESS) {
-    /* didn't work; try to find another MMIO region */
-    cprintf("warning: BIOS MMIO address not accepted by resource manager\n");
-    memset(&resource, 0x00, sizeof(resource));
-    resource.ResourceType         = RS_TYPE_MEM;
-    resource.MEMResource.MemSize  = mmio_size;
-    resource.MEMResource.MemFlags = RS_MEM_EXCLUSIVE | RS_SEARCH;
-
-    ret = RMAllocResource(rm_drvh, &ad_info->rm_mmio, &resource);
-  }
-
-  if (ret != RMRC_SUCCESS) {
-    cprintf("error: couldn't register MMIO region (rc = %d)\n", ret);
-    goto add_pci_fail;
-  }
-  rc_list->hResource[rc_list->NumResource++] = ad_info->rm_mmio;
-  ddprintf("MMIO address (final) = 0x%08lx\n", resource.MEMResource.MemBase);
-
-  /****************************************************************************
-   * Part 3: Fill in the remaining fields in the AD_INFO slot and allocate
-   * memory and GDT selectors for the adapter. Finally, register the adapter
-   * itself with the OS/2 resource manager
-   */
-  ad_info->pci      = pci_ids + i;
-  ad_info->bus      = bus;
-  ad_info->dev_func = dev_func;
-  ad_info->irq      = irq;
-  ad_info->mmio_phys = resource.MEMResource.MemBase;
-    
-  /* allocate memory for port-specific DMA scratch buffers */
-  if (DevHelp_AllocPhys((long) AHCI_PORT_PRIV_DMA_SZ * AHCI_MAX_PORTS,
-                        MEMTYPE_ABOVE_1M, &ad_info->dma_buf_phys) != 0) {
-    cprintf("error: couldn't allocate DMA scratch buffers for AHCI ports\n");
-    ad_info->dma_buf_phys = 0;
-    goto add_pci_fail;
-  }
-
-  /* allocate GDT selectors for memory-mapped I/O and DMA scratch buffers */
-  if (DevHelp_AllocGDTSelector(gdt, PORT_DMA_BUF_SEGS + 1) != 0) {
-    cprintf("error: couldn't allocate GDT selectors\n");
-    memset(gdt, 0x00, sizeof(gdt));
-    goto add_pci_fail;
-  }
-
-  /* map MMIO address to first GDT selector */
-  if (DevHelp_PhysToGDTSelector(ad_info->mmio_phys, (USHORT) mmio_size,
-                                gdt[0]) != 0) {
-    cprintf("error: couldn't map MMIO address to GDT selector\n");
-    goto add_pci_fail;
-  }
-
-  /* map DMA scratch buffers to remaining GDT selectors */
-  for (i = 0; i < PORT_DMA_BUF_SEGS; i++) {
-    ULONG addr = ad_info->dma_buf_phys + i * PORT_DMA_SEG_SIZE;
-    USHORT len = AHCI_PORT_PRIV_DMA_SZ * PORT_DMA_BUFS_PER_SEG;
-
-    if (DevHelp_PhysToGDTSelector(addr, len, gdt[i+1]) != 0) {
-      cprintf("error: couldn't map DMA scratch buffer to GDT selector\n");
-      goto add_pci_fail;
-    }
-  }
-
-  /* fill in MMIO and DMA scratch buffer addresses in adapter info */
-  ad_info->mmio = (u8 _far *) ((u32) gdt[0] << 16);
-  for (i = 0; i < PORT_DMA_BUF_SEGS; i++) {
-    ad_info->dma_buf[i] = (u8 _far *) ((u32) gdt[i+1] << 16);
-  }
-
-  /* register adapter with resource manager */
-  memset(&adj, 0x00, sizeof(adj));
-  adj.pNextAdj             = NULL;
-  adj.AdjLength            = sizeof(adj);
-  adj.AdjType              = ADJ_ADAPTER_NUMBER;
-  adj.Adapter_Number       = ad_info_cnt;
-
-  memset(&adapter, 0x00, sizeof(adapter));
-  sprintf(tmp, "AHCI_%d Controller", ad_info_cnt);
-  adapter.AdaptDescriptName = tmp;
-  adapter.AdaptFlags        = 0;
-  adapter.BaseType          = AS_BASE_MSD;
-  adapter.SubType           = AS_SUB_IDE;
-  adapter.InterfaceType     = AS_INTF_GENERIC;
-  adapter.HostBusType       = AS_HOSTBUS_PCI;
-  adapter.HostBusWidth      = AS_BUSWIDTH_32BIT;
-  adapter.pAdjunctList      = &adj;
-
-  ret = RMCreateAdapter(rm_drvh, &ad_info->rm_adh, &adapter, NULL, rc_list);
-  if (ret != RMRC_SUCCESS) {
-    cprintf("error: couldn't register adapter (rc = %d)\n", ret);
-    goto add_pci_fail;
-  }
-
-  /* Successfully added the adapter and reserved its resources; the adapter
-   * is still under BIOS control so we're not going to do anything else at
-   * this point.
-   */
-  ad_info_cnt++;
-  return;
-
-add_pci_fail:
-  /* something went wrong; try to clean up as far as possible */
-  if (ad_info->rm_mmio != 0) {
-    RMDeallocResource(rm_drvh, ad_info->rm_mmio);
-  }
-  if (ad_info->rm_irq != 0) {
-    RMDeallocResource(rm_drvh, ad_info->rm_irq);
-  }
-  if (&ad_info->dma_buf_phys != 0) {
-    DevHelp_FreePhys(ad_info->dma_buf_phys);
-  }
-  for (i = 0; i < sizeof(gdt) / sizeof(*gdt); i++) {
-    if (gdt[i] != 0) {
-      DevHelp_FreeGDTSelector(gdt[i]);
-    }
-  }
-}
-
-/******************************************************************************
- * Read PCI configuration space register
- */
-static UCHAR pci_read_conf(UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
-                           ULONG _far *val)
-{
-  OH_PARM parm;
-  OH_DATA data;
-  UCHAR rc;
-
-  memset(&parm, 0x00, sizeof(parm));
-  parm.read_config.bus      = bus;
-  parm.read_config.dev_func = dev_func;
-  parm.read_config.reg      = indx;
-  parm.read_config.size     = size;
-  if ((rc = oemhlp_call(OH_READ_CONFIG, &parm, &data) != OH_SUCCESS)) {
-    cprintf("error: couldn't read config space (bus = %d, dev_func = 0x%02x, indx = 0x%02x, rc = %d)\n",
-            bus, dev_func, indx,  rc);
-    return(rc);
-  }
-
-  *val = data.read_config.data;
-  return(OH_SUCCESS);
-}
-
-/******************************************************************************
- * Write PCI configuration space register
- */
-static UCHAR pci_write_conf(UCHAR bus, UCHAR dev_func, UCHAR indx, UCHAR size,
-                            ULONG val)
-{
-  OH_PARM parm;
-  OH_DATA data;
-  UCHAR rc;
-
-  memset(&parm, 0x00, sizeof(parm));
-  parm.write_config.bus      = bus;
-  parm.write_config.dev_func = dev_func;
-  parm.write_config.reg      = indx;
-  parm.write_config.size     = size;
-  parm.write_config.data     = val;
-
-  if ((rc = oemhlp_call(OH_WRITE_CONFIG, &parm, &data) != OH_SUCCESS)) {
-    cprintf("error: couldn't write config space (bus = %d, dev_func = 0x%02x, indx = 0x%02x, rc = %d)\n",
-            bus, dev_func, indx, rc);
-    return(rc);
-  }
-
-  return(OH_SUCCESS);
-}
-/******************************************************************************
- * Call OEMHLP$ IDC entry point with the specified IOCtl parameter and data
- * packets.
- */
-static int oemhlp_call(UCHAR subfunction, OH_PARM _far *parm,
-                       OH_DATA _far *data)
-{
-  void (_far *func)(void);
-  unsigned short prot_idc_DS;
-  RP_GENIOCTL ioctl;
-
-  if (oemhlp.ProtIDCEntry == NULL || oemhlp.ProtIDC_DS == 0) {
-    /* attach to OEMHLP$ device driver */
-    if (DevHelp_AttachDD("OEMHLP$ ", (NPBYTE) &oemhlp) ||
-        oemhlp.ProtIDCEntry == NULL ||
-        oemhlp.ProtIDC_DS == 0) {
-      cprintf("couldn't attach to OEMHLP$\n");
-      return(OH_NOT_SUPPORTED);
-    }
-  }
-
-  /* store subfuntion in first byte of pararameter packet */
-  parm->bios_info.subfunction = subfunction;
-  memset(data, 0x00, sizeof(*data));
-
-  /* assemble IOCtl request */
-  memset(&ioctl, 0x00, sizeof(ioctl));
-  ioctl.rph.Len     = sizeof(ioctl);
-  ioctl.rph.Unit    = 0;
-  ioctl.rph.Cmd     = GENERIC_IOCTL;
-  ioctl.rph.Status  = 0;
-
-  ioctl.Category    = OH_CATEGORY;
-  ioctl.Function    = OH_FUNC_PCI;
-  ioctl.ParmPacket  = (PUCHAR) parm;
-  ioctl.DataPacket  = (PUCHAR) data;
-  ioctl.ParmLen     = sizeof(*parm);
-  ioctl.DataLen     = sizeof(*data);
-
-  /* Call OEMHLP's IDC routine. Before doing so, we need to assign the address
-   * to be called to a stack variable because the inter-device driver calling
-   * convention forces us to set DS to the device driver's data segment and ES
-   * to the segment of the request packet. 
-   */
-  func = oemhlp.ProtIDCEntry;
-
-  /* MT: the Watcom inline assembler does not support struct member reference,
-   * so we have to pass it in a stack variable
-   */
-  prot_idc_DS = oemhlp.ProtIDC_DS;
-
-  _asm {
-    push ds;
-    push es;
-    push bx;
-    push si;
-    push di;
-
-    push ss
-    pop  es
-    lea  bx, ioctl;
-    mov  ds, prot_idc_DS;
-    call dword ptr [func];
-
-    pop  di;
-    pop  si;
-    pop  bx;
-    pop  es;
-    pop  ds;
-  }
-
-  dddphex(parm, sizeof(*parm), "oemhlp_parm: ");
-  dddphex(data, sizeof(*data), "oemhlp_data: ");
-
-  if (ioctl.rph.Status & STERR) {
-    return(OH_NOT_SUPPORTED);
-  }
-  return(data->bios_info.rc);
-}
Index: trunk/src/wmakefile
===================================================================
--- trunk/src/wmakefile	(revision 12)
+++ 	(revision )
@@ -1,144 +1,0 @@
-###############################################################################
-#
-# wmakefile - Watcom makefile for os2ahci driver
-#
-# NOTE: This is a wmake file! See here:
-# http://www.openwatcom.org/index.php/Using_wmake
-#
-
-#
-# Copyright (c) 2010 Markus Thielen, Christian Mueller. 
-# Parts copied from/inspired by the Linux AHCI driver; 
-# those parts are (c) Linux AHCI/ATA maintainers
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-###############################################################################
-# wmake options
-
-# disable prompt to delete out of date files on compile errors
-.ERASE   
-
-
-###############################################################################
-# Tool Chain
-
-AS            = wasm    # Watcom assembler  
-CC            = wcc     # Watcom 16bit compiler
-LD            = wlink   # Watcom linker
-
-
-###############################################################################
-# Environment
-#
-# we need the following set before this makefile is called:
-#
-# WATCOM - to the base WATCOM installation directory
-# DDK    - to the base dir of the OS/2 DDK to use
-# PS     - the path separator (/ or \)
-#
-
-DDK          = $(%DDK)
-WATCOM       = $(%WATCOM)
-!ifndef %PS
-PS           = \
-!else
-PS           = $(%PS)
-!endif
-
-CC_INCLUDE   = -I$(DDK)$(PS)BASE$(PS)H &
-               -I$(DDK)$(PS)BASE$(PS)IBMH &
-               -I$(DDK)$(PS)BASE$(PS)SRC$(PS)DEV$(PS)DASD$(PS)DISKH &
-               -I$(WATCOM)$(PS)h &
-
-AS_INCLUDE   = -I$(DDK)$(PS)BASE$(PS)INC &
-               -I$(DDK)$(PS)BASE$(PS)SRC$(PS)DEV$(PS)DASD$(PS)DISKINC
-
-LIB_DIRS     = $(DDK)$(PS)BASE$(PS)LIB$;$(DDK)$(PS)BASE$(PS)SRC$(PS)DEV$(PS)DASD$(PS)DEVHLP;$(WATCOM)$(PS)LIB;
-
-
-###############################################################################
-# Main dependencies
-
-TARGET   = os2ahci.add
-
-LIBS     = os2,addcalls,dhcalls,doscalls,rmcalls
-
-SRCS     = init.asm libc.c os2ahci.c pci.c ahci.c ata.c atapi.c ctxhook.c
-
-OBJS     = init.obj libc.obj os2ahci.obj pci.obj ahci.obj ata.obj atapi.obj&
-           ctxhook.obj
-
-#          the Watcom linker needs commas between object files
-LDOBJS   = init.obj,libc.obj,os2ahci.obj,pci.obj,ahci.obj,ata.obj,atapi.obj,&
-           ctxhook.obj
-
-INCS     = os2ahci.h ahci.h
-
-
-all: $(TARGET)
-
-clean:
-	rm -f $(OBJS) $(TARGET)
-
-
-###############################################################################
-# compiler/linker flags
-
-AFLAGS        = -2 -bt=os2 -w3 -zq -ms -zcm
-CFLAGS        = -bt=os2 -ms -s -zdp -zff -zgf -zu -zl -2 -w2 -wcd=138 -zp=1 -q
-CFLAGS_DEBUG  = -d3 -hc
-LFLAGS        = name $(TARGET) sys os2 dll initglobal option map option quiet &
-                option nodefaultlibs libpath $(LIB_DIRS) file $(LDOBJS) &
-		library $(LIBS) 
-LFLAGS_DEBUG  = DEBUG CODEVIEW option cvpack
-
-!IFDEF DEBUG
-CFLAGS = $(CFLAGS) $(CFLAGS_DEBUG)
-LFLAGS = $(LFLAGS) $(LFLAGS_DEBUG)
-!ENDIF
-
-###############################################################################
-# Object/source dependencies
-
-init.obj:     init.asm   wmakefile
-
-libc.obj:     libc.c     wmakefile $(INCS)
-
-os2ahci.obj:  os2ahci.c  wmakefile $(INCS)
-
-pci.obj:      pci.c      wmakefile $(INCS)
-
-ahci.obj:     ahci.c     wmakefile $(INCS) ata.h atapi.h
-
-ata.obj:      ata.c      wmakefile $(INCS) ata.h
-
-atapi.obj:    atapi.c    wmakefile $(INCS) ata.h atapi.h
-
-ctxhook.obj:  ctxhook.c  wmakefile $(INCS)
-
-###############################################################################
-# Action definitions (compile/link commands)
-
-.asm.obj:
-	$(AS) $(AFLAGS) $(AS_INCLUDE) $*.asm -fo=$*.obj
-
-.c.obj:
-	$(CC) $(CFLAGS) $(CC_INCLUDE) $*.c
-
-$(TARGET): $(OBJS) os2ahci.def wmakefile
-	$(LD) $(LFLAGS) 
-
-
