Index: trunk/src/os2ahci/apm.c
===================================================================
--- trunk/src/os2ahci/apm.c	(revision 84)
+++ trunk/src/os2ahci/apm.c	(revision 84)
@@ -0,0 +1,171 @@
+/******************************************************************************
+ * apm.c - Functions to interface with the APM driver.
+ *
+ * Copyright (c) 2010 Christian Mueller, Markus Thielen.
+ * 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 <apmcalls.h>
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* -------------------------- function prototypes -------------------------- */
+
+/* ------------------------ global/static variables ------------------------ */
+
+USHORT _far _cdecl apm_event    (APMEVENT _far *evt);
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Connect to APM driver and register for power state change events.
+ */
+void apm_init(void)
+{
+  USHORT rc;
+
+  /* connect to APM driver */
+  if ((rc = APMAttach()) != 0) {
+    dprintf("couldn't connect to APM driver (rc = %d)\n", rc);
+    return;
+  }
+
+  /* register for suspend/resume events */
+  if ((rc = APMRegister(apm_event, APM_NOTIFYSETPWR |
+                                   APM_NOTIFYNORMRESUME |
+                                   APM_NOTIFYCRITRESUME, 0)) != 0) {
+    dprintf("couldn't register for power event notificatins (rc = %d)\n", rc);
+    return;
+  }
+}
+
+/******************************************************************************
+ * APM event handler
+ */
+USHORT _far _cdecl apm_event(APMEVENT _far *evt)
+{
+  USHORT msg = (USHORT) evt->ulParm1;
+
+  dprintf("received APM event: 0x%lx/0x%lx\n");
+
+  switch (msg) {
+
+  case APM_SETPWRSTATE:
+    if (evt->ulParm2 >> 16 != APM_PWRSTATEREADY) {
+      /* we're suspending */
+      apm_suspend();
+    }
+    break;
+
+  case APM_NORMRESUMEEVENT:
+  case APM_CRITRESUMEEVENT:
+    /* we're resuming */
+    apm_resume();
+    break;
+
+  default:
+    dprintf("unknown APM event; ignoring...\n");
+    break;
+  }
+
+  return(0);
+}
+
+/******************************************************************************
+ * APM suspend handler. In a nutshell, it'll turn of interrupts and flush all
+ * write caches.
+ */
+void apm_suspend(void)
+{
+  int a;
+  int p;
+  int d;
+
+  dprintf("apm_suspend()\n");
+
+  /* restart all ports with interrupts disabled */
+  for (a = 0; a < ad_info_cnt; a++) {
+    AD_INFO *ai = ad_infos + a;
+
+    lock_adapter(ai);
+    for (p = 0; p <= ai->port_max; p++) {
+      /* wait until all active commands have completed on this port */
+      while (ahci_port_busy(ai, p)) {
+        msleep(250);
+      }
+
+      /* restart port with interrupts disabled */
+      ahci_stop_port(ai, p);
+      ahci_start_port(ai, p, 0);
+
+      /* flush cache on all attached devices */
+      for (d = 0; d <= ai->ports[p].dev_max; d++) {
+        if (ai->ports[p].devs[d].present) {
+	  ahci_flush_cache(ai, p, d);
+	}
+      }
+    }
+  }
+
+  /* reset init_complete so that we can process IORBs without interrupts */
+  init_complete = 0;
+
+  /* restore BIOS configuration for each adapter and release the adapter */
+  for (a = 0; a < ad_info_cnt; a++) {
+    ahci_restore_bios_config(ad_infos + a);
+    unlock_adapter(ad_infos + a);
+  }
+
+  dprintf("apm_suspend() finished\n");
+}
+
+/******************************************************************************
+ * APM resume handler. All ports are restarted with interrupts enabled using
+ * the same function as the IOCM_COMPLETE_INIT handler does.
+ */
+void apm_resume(void)
+{
+  int a;
+
+  dprintf("apm_resume()\n");
+
+  for (a = 0; a < ad_info_cnt; a++) {
+    AD_INFO *ai = ad_infos + a;
+
+    /* Complete initialization of this adapter; this will restart the ports
+     * with interrupts enabled and take care of whatever else needs to be
+     * done to get the adapter and its ports up and running.
+     */
+    lock_adapter(ai);
+    ahci_complete_init(ai);
+  }
+
+  /* tell the driver we're again fully operational */
+  init_complete = 1;
+
+  /* unlock all adapters now that we have set the init_complete flag */
+  for (a = 0; a < ad_info_cnt; a++) {
+    unlock_adapter(ad_infos + a);
+  }
+
+  dprintf("apm_resume() finished\n");
+}
Index: trunk/src/os2ahci/ioctl.c
===================================================================
--- trunk/src/os2ahci/ioctl.c	(revision 84)
+++ trunk/src/os2ahci/ioctl.c	(revision 84)
@@ -0,0 +1,336 @@
+/******************************************************************************
+ * ioctl.c - Generic IOCTL 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 "atapi.h"
+#include "ioctl.h"
+
+#include <scsi.h>
+
+#pragma pack(1)
+
+/* -------------------------- macros and constants ------------------------- */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+/* Memory area for IOCTLs which send IORBs downstream; currently only
+ * OS2AHCI_IOCTL_PASSTHROUGH falls into this category, thus we're simply
+ * reusing the IORB_ADAPTER_PASSTHRU structure for now. If this ever changes,
+ * we'll need to define a union to cover all IORB types in question.
+ */
+typedef struct {
+  IORB_ADAPTER_PASSTHRU iorb;                  /* IORB */
+  SCSI_STATUS_BLOCK   ssb;                     /* SCSI status block */
+  UCHAR               sense[ATAPI_SENSE_LEN];  /* sense buffer */
+  SCATGATENTRY        sg_lst[AHCI_MAX_SG / 2]; /* scatter/gather list */
+  ULONG               sg_cnt;                  /* number of S/G elements */
+  UCHAR               lh[16];                  /* lock handle for VMLock() */
+} IOCTL_CONTEXT;
+
+/* -------------------------- function prototypes -------------------------- */
+
+static LIN lin(void _far *p);
+
+IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb);
+
+/* ------------------------ global/static variables ------------------------ */
+
+/* ----------------------------- start of code ----------------------------- */
+
+/******************************************************************************
+ * Return device list to allow the ring 3 application to figure out which
+ * adapter/port/device combinations are available.
+ */
+USHORT ioctl_get_devlist(RP_GENIOCTL _far *ioctl)
+{
+  OS2AHCI_DEVLIST _far *devlst = (OS2AHCI_DEVLIST _far *) ioctl->DataPacket;
+  USHORT maxcnt = 0;
+  USHORT cnt = 0;
+  USHORT a;
+  USHORT p;
+  USHORT d;
+
+  /* verify addressability of parm buffer (number of devlst elements) */
+  if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->ParmPacket >> 16),
+                            sizeof(USHORT),
+                            (USHORT) (ULONG) ioctl->ParmPacket,
+                            VERIFY_READONLY) != 0) {
+    return(STDON | STERR | 0x05);
+  }
+
+  maxcnt = *((USHORT _far *) ioctl->ParmPacket);
+
+  /* verify addressability of return buffer (OS2AHCI_DEVLIST) */
+  if (DevHelp_VerifyAccess((SEL) ((ULONG) devlst >> 16),
+                            offsetof(OS2AHCI_DEVLIST, devs) +
+                              sizeof(devlst->devs) * maxcnt,
+                            (USHORT) (ULONG) devlst,
+                            VERIFY_READWRITE) != 0) {
+    return(STDON | STERR | 0x05);
+  }
+
+  /* fill-in device list */
+  for (a = 0; a < ad_info_cnt; a++) {
+    AD_INFO *ai = ad_infos + a;
+
+    for (p = 0; p <= ai->port_max; p++) {
+      P_INFO *pi = ai->ports + p;
+
+      for (d = 0; d <= pi->dev_max; d++) {
+        if (pi->devs[d].present) {
+          /* add this device to the device list */
+          if (cnt >= maxcnt) {
+            /* not enough room in devlst */
+            goto ioctl_get_device_done;
+          }
+
+          devlst->devs[cnt].adapter  = a;
+          devlst->devs[cnt].port     = p;
+          devlst->devs[cnt].device   = d;
+          devlst->devs[cnt].type     = pi->devs[d].dev_type;
+          devlst->devs[cnt].ncq_max  = pi->devs[d].ncq_max;
+
+          if (pi->devs[d].lba48)      devlst->devs[cnt].flags |= DF_LBA48;
+          if (pi->devs[d].atapi)      devlst->devs[cnt].flags |= DF_ATAPI;
+          if (pi->devs[d].atapi_16)   devlst->devs[cnt].flags |= DF_ATAPI_16;
+          if (pi->devs[d].removable)  devlst->devs[cnt].flags |= DF_REMOVABLE;
+          cnt++;
+        }
+      }
+    }
+  }
+
+ioctl_get_device_done:
+  devlst->cnt = cnt;
+  return(STDON);
+}
+
+/******************************************************************************
+ * Adapter passthrough IOCTL. This IOCTL covers both ATA and ATAPI passthrough
+ * requests.
+ */
+USHORT ioctl_passthrough(RP_GENIOCTL _far *ioctl)
+{
+  OS2AHCI_PASSTHROUGH _far *req = (OS2AHCI_PASSTHROUGH _far *) ioctl->ParmPacket;
+  char _far *sense_buf = (char _far *) ioctl->DataPacket;
+  IOCTL_CONTEXT *ic;
+  USHORT ret;
+  USHORT a;
+  USHORT p;
+  USHORT d;
+
+  /* verify addressability of parm buffer (OS2AHCI_PASSTHROUGH) */
+  if (DevHelp_VerifyAccess((SEL) ((ULONG) req >> 16),
+                            sizeof(OS2AHCI_PASSTHROUGH),
+                            (USHORT) (ULONG) req,
+                            VERIFY_READWRITE) != 0) {
+    return(STDON | STERR | 0x05);
+  }
+
+  /* verify addressability of data buffer (sense data) */
+  if (req->sense_len > 0) {
+    if (DevHelp_VerifyAccess((SEL) ((ULONG) sense_buf >> 16),
+                              req->sense_len,
+                              (USHORT) (ULONG) sense_buf,
+                              VERIFY_READWRITE) != 0) {
+      return(STDON | STERR | 0x05);
+    }
+  }
+
+  /* Verify basic request parameters such as adapter/port/device, size of
+   * DMA buffer (the S/G list can't have more than AHCI_MAX_SG / 2 entries), ...
+   */
+  a = req->adapter;
+  p = req->port;
+  d = req->device;
+  if (a >= ad_info_cnt || p > ad_infos[a].port_max ||
+      d > ad_infos[a].ports[p].dev_max || !ad_infos[a].ports[p].devs[d].present) {
+    return(STDON | STERR | ERROR_I24_BAD_UNIT);
+  }
+  if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2) {
+    return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
+  }
+
+  /* allocate IOCTL context data */
+  if ((ic = malloc(sizeof(*ic))) == NULL) {
+    return(STDON | STERR | ERROR_I24_GEN_FAILURE);
+  }
+  memset(ic, 0x00, sizeof(*ic));
+
+  /* lock DMA transfer buffer into memory and construct S/G list */
+  if (req->buflen > 0) {
+    if (DevHelp_VMLock(VMDHL_LONG | !((req->flags & PT_WRITE) ? VMDHL_WRITE : 0),
+                       req->buf, req->buflen, lin(ic->sg_lst), lin(&ic->lh),
+                       &ic->sg_cnt) != 0) {
+      /* couldn't lock buffer and/or produce a S/G list */
+      free(ic);
+      return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
+    }
+  }
+
+  /* fill in adapter passthrough fields */
+  ic->iorb.iorbh.Length           = sizeof(ic->iorb);
+  ic->iorb.iorbh.UnitHandle       = iorb_unit(a, p, d);
+  ic->iorb.iorbh.CommandCode      = IOCC_ADAPTER_PASSTHRU;
+  ic->iorb.iorbh.CommandModifier  = (req->flags & PT_ATAPI) ? IOCM_EXECUTE_CDB
+                                                            : IOCM_EXECUTE_ATA;
+  ic->iorb.iorbh.RequestControl   = IORB_ASYNC_POST;
+  ic->iorb.iorbh.Timeout          = req->timeout;
+  ic->iorb.iorbh.NotifyAddress    = ioctl_wakeup;
+
+  ic->iorb.cSGList          = ic->sg_cnt;
+  ic->iorb.pSGList          = ic->sg_lst;
+  DevHelp_VirtToPhys(ic->sg_lst, &ic->iorb.ppSGLIST);
+
+  ic->iorb.ControllerCmdLen = req->cmdlen;
+  ic->iorb.pControllerCmd   = req->cmd.cdb;
+  ic->iorb.Flags            = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
+
+  if (req->sense_len > 0) {
+    /* initialize SCSI status block to allow getting sense data */
+    ic->iorb.iorbh.pStatusBlock     = (BYTE *) &ic->ssb;
+    ic->iorb.iorbh.StatusBlockLen   = sizeof(ic->ssb);
+    ic->ssb.SenseData               = (SCSI_REQSENSE_DATA _far *) ic->sense;
+    ic->ssb.ReqSenseLen             = sizeof(ic->sense);
+    ic->iorb.iorbh.RequestControl  |= IORB_REQ_STATUSBLOCK;
+  }
+
+  /* send IORB on its way */
+  add_entry(&ic->iorb.iorbh);
+
+  /* Wait for IORB completion. On SMP kernels, ProcBlock will release
+   * all spinlocks currently held, in addition to re-enabling interrupts,
+   * so we can use spinlocks to protect the time between checking the
+   * IORB_DONE flag and calling ProcBlock.
+   * 
+   * However, if OS2AHCI_SMP is not defined, we're emulating spinlocks
+   * via disabling interrupts and there's a sanity check in the emulation
+   * code to catch recursive spinlocks, thus we need to reset the emulated
+   * spinlock status before calling ProcBlock. Otherwise, the driver will
+   * will get caught in the sanity checks when processing the next IORB or
+   * interrupt while we're waiting.
+   */
+  spin_lock(drv_lock);
+  while (!(ic->iorb.iorbh.Status & IORB_DONE)) {
+#   ifndef OS2AHCI_SMP
+    drv_lock = 0;
+#   endif
+    DevHelp_ProcBlock((ULONG) (void _far *) &ic->iorb.iorbh, 30000, 1);
+    spin_lock(drv_lock);
+  }
+  spin_unlock(drv_lock);
+
+  ret = STDON;
+
+  /* map IORB error codes to device driver error codes */
+  if (ic->iorb.iorbh.Status & IORB_ERROR) {
+    ret |= STERR;
+
+    switch (ic->iorb.iorbh.ErrorCode) {
+
+    case IOERR_UNIT_NOT_READY:
+      ret |= ERROR_I24_NOT_READY;
+      break;
+
+    case IOERR_MEDIA_CHANGED:
+      ret |= ERROR_I24_DISK_CHANGE;
+      break;
+
+    case IOERR_MEDIA:
+    case IOERR_MEDIA_NOT_FORMATTED:
+      ret |= ERROR_I24_CRC;
+      break;
+
+    case IOERR_CMD_SYNTAX:
+    case IOERR_CMD_NOT_SUPPORTED:
+      ret |= ERROR_I24_BAD_COMMAND;
+      break;
+
+    case IOERR_MEDIA_WRITE_PROTECT:
+      ret |= ERROR_I24_WRITE_PROTECT;
+      break;
+
+    case IOERR_CMD_ABORTED:
+      ret |= ERROR_I24_CHAR_CALL_INTERRUPTED;
+      break;
+
+    case IOERR_RBA_ADDRESSING_ERROR:
+      ret |= ERROR_I24_SEEK;
+      break;
+
+    case IOERR_RBA_LIMIT:
+      ret |= ERROR_I24_SECTOR_NOT_FOUND;
+      break;
+
+    case IOERR_CMD_SGLIST_BAD:
+      ret |= ERROR_I24_INVALID_PARAMETER;
+      break;
+
+    case IOERR_DEVICE_NONSPECIFIC:
+    case IOERR_ADAPTER_TIMEOUT:
+    case IOERR_ADAPTER_DEVICEBUSCHECK:
+    case IOERR_CMD_ADD_SOFTWARE_FAILURE:
+    case IOERR_CMD_SW_RESOURCE:
+    default:
+      ret |= ERROR_I24_GEN_FAILURE;
+      break;
+    }
+
+    /* copy sense information, if there is any */
+    if ((ic->iorb.iorbh.Status & IORB_STATUSBLOCK_AVAIL) &&
+        (ic->ssb.Flags | STATUS_SENSEDATA_VALID)) {
+      memcpy(sense_buf, ic->ssb.SenseData,
+             min(ic->ssb.ReqSenseLen, req->sense_len));
+    }
+  }
+
+  free(ic);
+  if (req->buflen > 0) {
+    DevHelp_VMUnLock(lin(ic->lh));
+  }
+  return(ret);
+}
+
+/******************************************************************************
+ * Get linear address for specified virtual address.
+ */
+static LIN lin(void _far *p)
+{
+  LIN l;
+
+  if (DevHelp_VirtToLin((SEL) ((ULONG) p >> 16), (USHORT) (ULONG) p, &l) != 0) {
+    return(0);
+  }
+
+  return(l);
+}
+
+/******************************************************************************
+ * IORB notification routine; used to wake up the sleeping application thread
+ * when the IOCTL IORB is complete.
+ */
+IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb)
+{
+  USHORT awake_count;
+
+  DevHelp_ProcRun((ULONG) iorb, &awake_count);
+}
+
Index: trunk/src/os2ahci/ioctl.h
===================================================================
--- trunk/src/os2ahci/ioctl.h	(revision 84)
+++ trunk/src/os2ahci/ioctl.h	(revision 84)
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * ioctl.h - IOCTL structures and constants 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 ------------------------- */
+
+/* IOCTL categories and functions */
+#define OS2AHCI_IOCTL_CATEGORY         0x80
+#define OS2AHCI_IOCTL_GET_DEVLIST      0x01
+#define OS2AHCI_IOCTL_PASSTHROUGH      0x02
+
+/* device flags */
+#define DF_LBA48            0x0001U
+#define DF_ATAPI            0x0002U
+#define DF_ATAPI_16         0x0004U
+#define DF_REMOVABLE        0x0008U
+
+/* passthrough flags */
+#define PT_WRITE            0x0001U    /* transfer direction host -> device */
+#define PT_ATAPI            0x0002U    /* ATAPI command (ATA if not set) */
+
+/* ------------------------ typedefs and structures ------------------------ */
+
+#ifndef VMDHL_WRITE
+typedef void *LIN;
+#endif
+
+#pragma pack(1)
+
+/******************************************************************************
+ * Generic ATA-8 command structure. This is loosely based on ATA-8, i.e. we
+ * don't have to deal with shadow registers and map them to the low bytes of
+ * adjecent words, etc. but there are still some oddities which need to be
+ * taken into consideration. For example, ATA-8 says LBA-28 sector addresses
+ * are simply the lower 28 bits in the LBA field. However, the underlying
+ * transport (SATA in our case) only looks at the three lower bytes of the
+ * LBA field, much like an IDE device would do. This means that only 3 bytes
+ * of the LBA field are processed and this yields only 24 bits. The remaining
+ * 4 bits need to be stored in the "device" register....
+ *
+ * Everything else seems to behave normally, as far as this term can be
+ * applied to IDE/ATA. Further details can be found in section 7.1.5.1 of the
+ * ATA-8 spec (Inputs for 28-bit Read/Write Commands).
+ */
+typedef struct {
+  USHORT  features;       /* feature bits */
+  USHORT  count;          /* count register (e.g. number of sectors) */
+  ULONG   lba_l;          /* low 24 bits of LBA-28 (32 bits for 48-bit devices) */
+  USHORT  lba_h;          /* high 16 bits of LBA for 48-bit devices */
+  UCHAR   cmd;            /* ATA command field */
+  UCHAR   device;         /* ATA device field and upper 4 bits of LBA-28 */
+} ATACMD;
+
+/******************************************************************************
+ * Data structure for OS2AHCI_IOCTL_GET_DEVLIST; the parameter for this IOCTL
+ * is a USHORT with the number of entries in 'devs'.
+ */
+typedef struct {
+  USHORT  cnt;            /* number of entries in 'devs' */
+  struct {
+    USHORT  adapter;      /* adapter */
+    USHORT  port;         /* port */
+    USHORT  device;       /* device (port multiplier) */
+    USHORT  type;         /* device type; see UIB_TYPE_* in iorb.h */
+    USHORT  ncq_max;      /* maximum number of queued commands */
+    USHORT  flags;        /* device flags; see DF_* above */
+  } devs[1];
+} OS2AHCI_DEVLIST;
+
+/******************************************************************************
+ * Parameter structure for OS2AHCI_IOCTL_PASSTHROUGH; the data structure for
+ * this IOCTL is a buffer for sense data in case of errors.
+ */
+typedef struct {
+  USHORT  adapter;        /* adapter */
+  USHORT  port;           /* port */
+  USHORT  device;         /* device (port multiplier) */
+  USHORT  flags;          /* request flags; see PT_* above */
+  ULONG   timeout;        /* timeout in seconds (0 = default of 30s) */
+
+  USHORT  cmdlen;         /* length of ATA or ATAPI command */
+  union {
+    ATACMD ata;           /* ATA command */
+    UCHAR  cdb[20];       /* ATAPI command */
+  } cmd;
+
+  ULONG   buflen;         /* length of buffer for data transfers */
+  LIN     buf;            /* buffer for data transfers (32-bit linear address) */
+  USHORT  sense_len;      /* length of sense data in IOCTL DataPacket */
+} OS2AHCI_PASSTHROUGH;
+
+#pragma pack()
+
