Index: trunk/src/os2ahci/Makefile
===================================================================
--- trunk/src/os2ahci/Makefile	(revision 123)
+++ trunk/src/os2ahci/Makefile	(revision 125)
@@ -108,5 +108,5 @@
 apm.obj:      apm.c      Makefile $(INCS)
 
-ioctl.obj:    ioctl.c    Makefile $(INCS) atapi.h ioctl.h
+ioctl.obj:    ioctl.c    Makefile $(INCS) ata.h atapi.h ioctl.h
 
 trace.obj:    trace.c    Makefile $(INCS)
Index: trunk/src/os2ahci/ata.c
===================================================================
--- trunk/src/os2ahci/ata.c	(revision 123)
+++ trunk/src/os2ahci/ata.c	(revision 125)
@@ -234,5 +234,5 @@
    *    22 bits into 2 AHCI_SG elements.
    *
-   *  - The S/G element size for AHCI is what the spec calls "'0' based"
+   *  - 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'
@@ -248,5 +248,4 @@
    *    of this function will indicate how many OS/2 S/G entries were
    *    successfully mapped.
-   *
    */
   for (i = n = 0; i < sg_cnt; i++) {
@@ -891,5 +890,59 @@
                AP_END);
 
+  if (rc == 0) {
+    add_workspace(iorb)->ppfunc = ata_execute_ata_pp;
+  }
+
   return(rc);
+}
+
+/******************************************************************************
+ * Post processing function for ata_execute_ata(); the main purpose of this
+ * function is to copy the received D2H FIS (i.e. the device registers after
+ * command completion) back to the ATA command structure.
+ *
+ * See ata_cmd_to_fis() for an explanation of the mapping.
+ */
+void ata_execute_ata_pp(IORBH _far *iorb)
+{
+  AHCI_PORT_DMA _far *dma_base;
+  ATA_CMD _far *cmd;
+  AD_INFO *ai;
+  u8 _far *fis;
+  int p;
+
+  /* get address of D2H FIS */
+  ai = ad_infos + iorb_unit_adapter(iorb);
+  p = iorb_unit_port(iorb);
+  dma_base = port_dma_base(ai, p);
+  fis = dma_base->rx_fis + 0x40;
+
+  if (fis[0] != 0x34) {
+    /* this is not a D2H FIS - give up silently */
+    ddprintf("ata_execute_ata_pp(): D2H FIS type incorrect: %d\n", fis[0]);
+    add_workspace(iorb)->complete = 1;
+    return;
+  }
+
+  /* map D2H FIS to the original ATA controller command structure */
+  cmd = (ATA_CMD _far *) ((IORB_ADAPTER_PASSTHRU _far *) iorb)->pControllerCmd;
+
+  cmd->cmd      = fis[2];
+  cmd->device   = fis[7];
+  cmd->features = ((u16) fis[3])
+                | ((u16) fis[11]);
+  cmd->lba_l    = ((u32) fis[4])
+                | ((u32) fis[5] << 8)
+                | ((u32) fis[6] << 16)
+                | ((u32) fis[8] << 24);
+  cmd->lba_h    = ((u16) fis[9])
+                | ((u16) fis[10] << 8);
+  cmd->count    = ((u16) fis[12])
+                | ((u16) fis[13] << 8);
+
+  dphex(cmd, sizeof(*cmd), "ahci_execute_ata_pp(): cmd after completion:\n");
+
+  /* signal completion to interrupt handler */
+  add_workspace(iorb)->complete = 1;
 }
 
@@ -1036,8 +1089,10 @@
                    AP_SECTOR_48, (u32) sector, (u16) 0,
                    AP_FEATURES,  (u16) count,
-                   AP_COUNT,     (u16) (slot << 3), /* tag = slot */
+                   /* tag = slot */
+                   AP_COUNT,     (u16) (slot << 3),
                    AP_SGLIST,    sg_list, (u16) sg_cnt,
                    AP_DEVICE,    0x40,
-                   AP_DEVICE,    (write_through) ? 0x80 : 0, /* force unit access */
+                   /* force unit access */
+                   AP_DEVICE,    (write_through && !force_write_cache) ? 0x80 : 0,
                    AP_WRITE,     1,
                    AP_END);
Index: trunk/src/os2ahci/ata.h
===================================================================
--- trunk/src/os2ahci/ata.h	(revision 123)
+++ trunk/src/os2ahci/ata.h	(revision 125)
@@ -344,7 +344,15 @@
 
 /* feature values for SMART */
-#define ATA_SMART_ENABLE        0xD8
-#define ATA_SMART_READ_VALUES   0xD0
-#define ATA_SMART_READ_THRESHOLDS 0xD1
+#define ATA_SMART_READ_VALUES       0xD0
+#define ATA_SMART_READ_THRESHOLDS   0xD1
+#define ATA_SMART_AUTOSAVE          0xD2
+#define ATA_SMART_SAVE              0xD3
+#define ATA_SMART_IMMEDIATE_OFFLINE 0xD4
+#define ATA_SMART_READ_LOG          0xD5
+#define ATA_SMART_WRITE_LOG         0xD6
+#define ATA_SMART_ENABLE            0xD8
+#define ATA_SMART_DISABLE           0xD9
+#define ATA_SMART_STATUS            0xDA
+#define ATA_SMART_AUTO_OFFLINE      0xDB
 
 /* feature values for Data Set Management */
@@ -505,4 +513,5 @@
 extern void      ata_write_pp             (IORBH _far *iorb);
 extern int       ata_execute_ata          (IORBH _far *iorb, int slot);
+extern void      ata_execute_ata_pp       (IORBH _far *iorb);
 extern int       ata_req_sense            (IORBH _far *iorb, int slot);
 
Index: trunk/src/os2ahci/ctxhook.c
===================================================================
--- trunk/src/os2ahci/ctxhook.c	(revision 123)
+++ trunk/src/os2ahci/ctxhook.c	(revision 125)
@@ -127,5 +127,5 @@
 
         /* get "current command slot"; only valid if there are no NCQ cmds */
-        ccs = (int) ((readl(port_mmio + PORT_CMD) >> 8) & 0x0f);
+        ccs = (int) ((readl(port_mmio + PORT_CMD) >> 8) & 0x1f);
         ddprintf(" PORT_CMD      = 0x%x\n", ccs);
 
@@ -145,4 +145,5 @@
               if (aws->cmd_slot == ccs) {
                 /* this is the non-NCQ command that failed */
+                ddprintf("failing IORB: %Fp\n", iorb);
                 problem_iorb = iorb;
               }
Index: trunk/src/os2ahci/ioctl.c
===================================================================
--- trunk/src/os2ahci/ioctl.c	(revision 123)
+++ trunk/src/os2ahci/ioctl.c	(revision 125)
@@ -27,4 +27,5 @@
 #include "os2ahci.h"
 #include "atapi.h"
+#include "ata.h"
 #include "ioctl.h"
 
@@ -42,7 +43,12 @@
  * we'll need to define a union to cover all IORB types in question.
  */
+#define CMDLEN sizeof(((OS2AHCI_PASSTHROUGH *) 0)->cmd)
 typedef struct {
   IORB_ADAPTER_PASSTHRU iorb;                  /* IORB */
   SCSI_STATUS_BLOCK   ssb;                     /* SCSI status block */
+  UCHAR               cmd[CMDLEN];             /* copy of passthrough cmd
+                                                * (need to fill ATA return
+                                                * registers at interrupt time)
+                                                */
   UCHAR               sense[ATAPI_SENSE_LEN];  /* sense buffer */
   SCATGATENTRY        sg_lst[AHCI_MAX_SG / 2]; /* scatter/gather list */
@@ -53,5 +59,8 @@
 /* -------------------------- function prototypes -------------------------- */
 
-static LIN lin(void _far *p);
+static USHORT do_smart  (BYTE unit, BYTE sub_func, BYTE parm, void _far *buf);
+static int    map_unit  (BYTE unit, USHORT _far *a, USHORT _far *p,
+                         USHORT _far *d);
+static LIN    lin       (void _far *p);
 
 IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb);
@@ -171,5 +180,6 @@
     return(STDON | STERR | ERROR_I24_BAD_UNIT);
   }
-  if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2) {
+  if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2 ||
+      req->cmdlen < 6 || req->cmdlen > sizeof(req->cmd)) {
     return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
   }
@@ -206,6 +216,7 @@
   ic->iorb.ppSGLIST         = virt_to_phys(ic->sg_lst);
 
+  memcpy(ic->cmd, req->cmd.cdb, sizeof(ic->cmd));
   ic->iorb.ControllerCmdLen = req->cmdlen;
-  ic->iorb.pControllerCmd   = req->cmd.cdb;
+  ic->iorb.pControllerCmd   = ic->cmd;
   ic->iorb.Flags            = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
 
@@ -236,7 +247,9 @@
   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);
@@ -306,4 +319,11 @@
              min(ic->ssb.ReqSenseLen, req->sense_len));
     }
+
+  } else if ((req->flags & PT_ATAPI) == 0) {
+    /* Copy ATA cmd back to IOCTL request (ATA commands are effectively
+     * registers which are sometimes used to indicate return conditions,
+     * e.g. when requesting the smart status)
+     */
+    memcpy(&req->cmd.ata, ic->cmd, sizeof(req->cmd.ata));
   }
 
@@ -316,4 +336,308 @@
 
 /******************************************************************************
+ * Generic disk IOCTL handler; this IOCTL category has originally been defined
+ * in IBM1S506; the code has been more or less copied from DANIS506.
+ *
+ * NOTE: Only a subset of the IOCTL calls are implemented in OS2AHCI at this
+ *       point, basically those calls required to get HDMON working.
+ */
+USHORT ioctl_gen_dsk(RP_GENIOCTL _far *ioctl)
+{
+  DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
+  UnitInformationData _far *ui;
+  OS2AHCI_PASSTHROUGH pt;
+  RP_GENIOCTL tmp_ioctl;
+  USHORT size = 0;
+  USHORT ret;
+  USHORT a;
+  USHORT p;
+  USHORT d;
+  UCHAR unit;
+  
+  /* verify addressability of parm buffer (DSKSP_CommandParameters) */
+  if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
+                            sizeof(DSKSP_CommandParameters),
+                            (USHORT) (ULONG) cp,
+                            VERIFY_READONLY) != 0) {
+    return(STDON | STERR | 0x05);
+  }
+  unit = cp->byPhysicalUnit;
+
+  /* verify addressability of data buffer (depends on function code) */
+  switch (ioctl->Function) {
+
+  case DSKSP_GEN_GET_COUNTERS:
+    size = sizeof(DeviceCountersData);
+    break;
+
+  case DSKSP_GET_UNIT_INFORMATION:
+    size = sizeof(UnitInformationData);
+    break;
+
+  case DSKSP_GET_INQUIRY_DATA:
+    size = ATA_ID_WORDS * sizeof(u16);
+    break;
+  }
+
+  if (size > 0) {
+    if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
+                             size, (USHORT) (ULONG) ioctl->DataPacket,
+                             VERIFY_READWRITE) != 0) {
+      return(STDON | STERR | 0x05);
+    }
+  }
+
+  if (map_unit(unit, &a, &p, &d)) {
+    return(STDON | STERR | ERROR_I24_BAD_UNIT);
+  }
+
+  /* execute generic disk request */
+  switch (ioctl->Function) {
+
+  case DSKSP_GEN_GET_COUNTERS:
+    /* Not supported, yet; we would need dynamically allocated device
+     * structures to cope with the memory requirements of the corresponding
+     * statistics buffer. For the time being, we'll return an empty buffer.
+     */
+    memset(ioctl->DataPacket, 0x00, sizeof(DeviceCountersData));
+    ret = STDON;
+    break;
+
+  case DSKSP_GET_UNIT_INFORMATION:
+    /* get unit information; things like port addresses won't fit so we don't
+     * even bother returning those.
+     */
+    ui = (UnitInformationData _far *) ioctl->DataPacket;
+    memset(ui, 0x00, sizeof(*ui));
+
+    ui->wRevisionNumber = 1;
+    ui->wIRQ            = ad_infos[a].irq;
+    ui->wFlags          = UIF_VALID;
+    ui->wFlags         |= UIF_RUNNING_BMDMA;
+    ui->wFlags         |= (unit & 0x0001) ? UIF_SLAVE : 0;
+    ui->wFlags         |= (ad_infos[a].ports[p].devs[d].atapi) ? UIF_ATAPI : 0;
+    ui->wFlags         |= UIF_SATA;
+
+    ret = STDON;
+    break;
+
+  case DSKSP_GET_INQUIRY_DATA:
+    /* return ATA ID buffer */
+    memset(&tmp_ioctl, 0x00, sizeof(tmp_ioctl));
+    tmp_ioctl.Category   = OS2AHCI_IOCTL_CATEGORY;
+    tmp_ioctl.Function   = OS2AHCI_IOCTL_PASSTHROUGH;
+    tmp_ioctl.ParmPacket = (void _far *) &pt;
+
+    memset(&pt, 0x00, sizeof(pt));
+    pt.adapter          = a;
+    pt.port             = p;
+    pt.device           = d;
+    pt.cmdlen           = sizeof(pt.cmd.ata);
+    pt.cmd.ata.cmd      = (ad_infos[a].ports[p].devs[d].atapi) ?
+                           ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA;
+    pt.buflen           = size;
+    pt.buf              = lin(ioctl->DataPacket);
+
+    ret = gen_ioctl(&tmp_ioctl);
+    break;
+
+  default:
+    ret = STDON | STATUS_ERR_UNKCMD;
+    break;
+  }
+
+  return(ret);
+}
+
+/******************************************************************************
+ * SMART IOCTL handler; this IOCTL category has originally been defined in
+ * IBM1S506; the code has been more or less copied from DANIS506.
+ */
+USHORT ioctl_smart(RP_GENIOCTL _far *ioctl)
+{
+  DSKSP_CommandParameters _far *cp = (DSKSP_CommandParameters _far *) ioctl->ParmPacket;
+  USHORT size = 0;
+  USHORT ret;
+  UCHAR unit;
+  UCHAR parm;
+
+  /* verify addressability of parm buffer (DSKSP_CommandParameters) */
+  if (DevHelp_VerifyAccess((SEL) ((ULONG) cp >> 16),
+                            sizeof(DSKSP_CommandParameters),
+                            (USHORT) (ULONG) cp,
+                            VERIFY_READONLY) != 0) {
+    return(STDON | STERR | 0x05);
+  }
+  unit = cp->byPhysicalUnit;
+
+  /* verify addressability of data buffer (depends on SMART function) */
+  switch (ioctl->Function) {
+
+  case DSKSP_SMART_GETSTATUS:
+    size = sizeof(ULONG);
+    break;
+
+  case DSKSP_SMART_GET_ATTRIBUTES:
+  case DSKSP_SMART_GET_THRESHOLDS:
+  case DSKSP_SMART_GET_LOG:
+    size = 512;
+    break;
+  }
+
+  if (size > 0) {
+    if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->DataPacket >> 16),
+                             size, (USHORT) (ULONG) ioctl->DataPacket,
+                             VERIFY_READWRITE) != 0) {
+      return(STDON | STERR | 0x05);
+    }
+    parm = ioctl->DataPacket[0];
+  }
+
+  /* execute SMART request */
+  switch (ioctl->Function) {
+
+  case DSKSP_SMART_ONOFF:
+    ret = do_smart(unit,
+                   (BYTE) ((parm) ? ATA_SMART_ENABLE
+                                  : ATA_SMART_DISABLE),
+                   0, NULL);
+    break;
+
+  case DSKSP_SMART_AUTOSAVE_ONOFF:
+    ret = do_smart(unit, ATA_SMART_AUTOSAVE,
+                   (BYTE) ((parm) ? (BYTE) 0xf1 : 0),
+                   NULL);
+    break;
+
+  case DSKSP_SMART_AUTO_OFFLINE:
+    ret = do_smart(unit, ATA_SMART_AUTO_OFFLINE, parm, NULL);
+    break;
+
+  case DSKSP_SMART_EXEC_OFFLINE:
+    ret = do_smart(unit, ATA_SMART_IMMEDIATE_OFFLINE, parm, NULL);
+    break;
+
+  case DSKSP_SMART_SAVE:
+    ret = do_smart(unit, ATA_SMART_SAVE, 0, NULL);
+    break;
+
+  case DSKSP_SMART_GETSTATUS:
+    ret = do_smart(unit, ATA_SMART_STATUS, 0, ioctl->DataPacket);
+    break;
+
+  case DSKSP_SMART_GET_ATTRIBUTES:
+    ret = do_smart(unit, ATA_SMART_READ_VALUES, 0, ioctl->DataPacket);
+    break;
+
+  case DSKSP_SMART_GET_THRESHOLDS:
+    ret = do_smart(unit, ATA_SMART_READ_THRESHOLDS, 0, ioctl->DataPacket);
+    break;
+
+  case DSKSP_SMART_GET_LOG:
+    ret = do_smart(unit, ATA_SMART_READ_LOG, parm, ioctl->DataPacket);
+    break;
+
+  default:
+    ret = STDON | STATUS_ERR_UNKCMD;
+  }
+
+  return(ret);
+}
+
+/******************************************************************************
+ * Perform SMART request. The code has been more or less copied from DANIS506.
+ */
+static USHORT do_smart(BYTE unit, BYTE sub_func, BYTE parm, void _far *buf)
+{
+  OS2AHCI_PASSTHROUGH pt;
+  RP_GENIOCTL ioctl;
+  USHORT ret;
+  USHORT a;
+  USHORT p;
+  USHORT d;
+
+  if (map_unit(unit, &a, &p, &d)) {
+    return(STDON | STERR | ERROR_I24_BAD_UNIT);
+  }
+
+  /* Perform SMART request using the existing OS2AHCI_IOTCL_PASSTHROUGH IOCTL
+   * interface which already takes care of allocating an IORB, s/g lists, etc.
+   */
+  memset(&ioctl, 0x00, sizeof(ioctl));
+  ioctl.Category   = OS2AHCI_IOCTL_CATEGORY;
+  ioctl.Function   = OS2AHCI_IOCTL_PASSTHROUGH;
+  ioctl.ParmPacket = (void _far *) &pt;
+
+  memset(&pt, 0x00, sizeof(pt));
+  pt.adapter          = a;
+  pt.port             = p;
+  pt.device           = d;
+  pt.cmdlen           = sizeof(pt.cmd.ata);
+  pt.cmd.ata.features = sub_func;
+  pt.cmd.ata.count    = parm;
+  pt.cmd.ata.lba_l    = (0xc24fL << 8) | parm;
+  pt.cmd.ata.cmd      = ATA_CMD_SMART;
+
+  if (buf != NULL && sub_func != ATA_SMART_STATUS) {
+    pt.buflen         = 512;
+    pt.buf            = lin(buf);
+  }
+
+  if (((ret = gen_ioctl(&ioctl)) & STERR) == 0 &&
+      sub_func == ATA_SMART_STATUS) {
+
+    /* ATA_SMART_STATUS doesn't transfer anything but instead relies on the
+     * returned D2H FIS, mapped to the ATA CMD, to have a certain value
+     * (0xf42c); the IOCTL result is expected to be returned as a ULONG in
+     * the data buffer.
+     */
+    if (((pt.cmd.ata.lba_l >> 8) & 0xffff) == 0xf42c) {
+      *((ULONG _far *) buf) = 1;
+    } else {
+      *((ULONG _far *) buf) = 0;
+    }
+  }
+
+  return(ret);
+}
+
+/******************************************************************************
+ * Map DSKSP unit number to corresponding adapter/port/device number. Units
+ * are identified by an 8-bit adapter/device number with the lowest bit
+ * selecting between master (0) and slave (1). This number is mapped to our
+ * ATA/ATAPI units sequentially.
+ */
+static int map_unit(BYTE unit, USHORT _far *a, USHORT _far *p, USHORT _far *d)
+{
+  USHORT _a;
+  USHORT _p;
+  USHORT _d;
+
+  /* map unit to adapter/port/device */
+  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) {
+          if (--unit == 0) {
+            /* found the device */
+            *a = _a;
+            *p = _p;
+            *d = _d;
+            return(0);
+          }
+        }
+      }
+    }
+  }
+
+  /* unit not found */
+  return(-1);
+}
+
+/******************************************************************************
  * Get linear address for specified virtual address.
  */
Index: trunk/src/os2ahci/ioctl.h
===================================================================
--- trunk/src/os2ahci/ioctl.h	(revision 123)
+++ trunk/src/os2ahci/ioctl.h	(revision 125)
@@ -28,7 +28,36 @@
 
 /* IOCTL categories and functions */
-#define OS2AHCI_IOCTL_CATEGORY         0x80
+#define OS2AHCI_IOCTL_CATEGORY         0x87
 #define OS2AHCI_IOCTL_GET_DEVLIST      0x01
 #define OS2AHCI_IOCTL_PASSTHROUGH      0x02
+
+/* IOCTL definitions from s506oem.h (primarily required for SMART calls) */
+#define DSKSP_CAT_SMART             0x80  /* SMART IOCTL category */
+#define DSKSP_SMART_ONOFF           0x20  /* turn SMART on or off */
+#define DSKSP_SMART_AUTOSAVE_ONOFF  0x21  /* turn SMART autosave on or off */
+#define DSKSP_SMART_SAVE            0x22  /* force save of SMART data */
+#define DSKSP_SMART_GETSTATUS       0x23  /* get SMART status (pass/fail) */
+#define DSKSP_SMART_GET_ATTRIBUTES  0x24  /* get SMART attributes table */
+#define DSKSP_SMART_GET_THRESHOLDS  0x25  /* get SMART thresholds table */
+#define DSKSP_SMART_GET_LOG         0x26  /* get SMART log        table */
+#define DSKSP_SMART_AUTO_OFFLINE    0x27  /* set SMART offline autosave timer */
+#define DSKSP_SMART_EXEC_OFFLINE    0x28  /* execute SMART immediate offline */
+
+#define SMART_CMD_ON                   1  /* on value for related SMART functions */
+#define SMART_CMD_OFF                  0  /* off value for related SMART functions */
+
+#define DSKSP_CAT_GENERIC           0x90  /* generic IOCTL category */
+#define DSKSP_GEN_GET_COUNTERS      0x40  /* get general counter values table */
+#define DSKSP_GET_UNIT_INFORMATION  0x41  /* get unit configuration and BM DMA counters */
+#define DSKSP_GET_INQUIRY_DATA      0x42  /* get ATA/ATAPI inquiry data */
+
+/* unit information structure flags from s506oem.h */
+#define UIF_VALID           0x8000U       /* unit information valid */
+#define UIF_TIMINGS_VALID   0x4000U       /* timing information valid */
+#define UIF_RUNNING_BMDMA   0x2000U       /* running Bus Master DMA on unit */
+#define UIF_RUNNING_DMA     0x1000U       /* running slave DMA on unit */
+#define UIF_SATA            0x0004U       /* SATA             */
+#define UIF_SLAVE           0x0002U       /* slave on channel */
+#define UIF_ATAPI           0x0001U       /* ATAPI device if 1, ATA otherwise */
 
 /* device flags */
@@ -112,4 +141,66 @@
 } OS2AHCI_PASSTHROUGH;
 
+/******************************************************************************
+ * DSKSP command parameters; copied from s506oem.h
+ */
+typedef struct _DSKSP_CommandParameters
+{
+  BYTE        byPhysicalUnit;             /* physical unit number 0-n */
+                                          /* 0 = Pri/Mas, 1=Pri/Sla, 2=Sec/Mas, etc. */
+} DSKSP_CommandParameters;
+
+/******************************************************************************
+ * DSKSP device counters data; copied from s506oem.h
+ */
+typedef struct _DeviceCountersData
+{
+  USHORT      wRevisionNumber;            /* counter structure revision */
+  ULONG       TotalReadOperations;        /* total read operations performed */
+  ULONG       TotalWriteOperations;       /* total write operations performed */
+  ULONG       TotalWriteErrors;           /* total write errors encountered */
+  ULONG       TotalReadErrors;            /* total read errors encountered */
+  ULONG       TotalSeekErrors;            /* total seek errors encountered */
+  ULONG       TotalSectorsRead;           /* total number of sectors read */
+  ULONG       TotalSectorsWritten;        /* total number of sectors written */
+
+  ULONG       TotalBMReadOperations;      /* total bus master DMA read operations */
+  ULONG       TotalBMWriteOperations;     /* total bus master DMA write operations */
+  ULONG       ByteMisalignedBuffers;      /* total buffers on odd byte boundary */
+  ULONG       TransfersAcross64K;         /* total buffers crossing a 64K page boundary */
+  USHORT      TotalBMStatus;              /* total bad busmaster status */
+  USHORT      TotalBMErrors;              /* total bad busmaster error */
+  ULONG       TotalIRQsLost;              /* total lost interrupts */
+  USHORT      TotalDRQsLost;              /* total lost data transfer requests */
+  USHORT      TotalBusyErrors;            /* total device busy timeouts        */
+  USHORT      TotalBMStatus2;             /* total bad busmaster status */
+  USHORT      TotalChipStatus;            /* total bad chip status */
+  USHORT      ReadErrors[4];
+  USHORT      WriteErrors[2];
+  USHORT      SeekErrors[2];
+  USHORT      SATAErrors;
+} DeviceCountersData, NEAR *NPDeviceCountersData, FAR *PDeviceCountersData;
+
+/******************************************************************************
+ * DSKSP unit information data; copied from s506oem.h
+ */
+typedef struct _UnitInformationData
+{
+  USHORT      wRevisionNumber;            /* structure revision number */
+  union {
+    struct {
+      USHORT  wTFBase;                    /* task file register base addr */
+      USHORT  wDevCtl;                    /* device control register addr */
+    } rev0;
+    ULONG     dTFBase;                    /* task file register base addr */
+  };
+  USHORT      wIRQ;                       /* interrupt request level */
+  USHORT      wFlags;                     /* flags */
+  UCHAR       byPIO_Mode;                 /* PIO transfer mode programmed */
+  UCHAR       byDMA_Mode;                 /* DMA transfer mode programmed */
+  ULONG       UnitFlags1;
+  USHORT      UnitFlags2;
+} UnitInformationData, NEAR *NPUnitInformationData, FAR *PUnitInformationData;
+
+
 #pragma pack()
 
Index: trunk/src/os2ahci/os2ahci.c
===================================================================
--- trunk/src/os2ahci/os2ahci.c	(revision 123)
+++ trunk/src/os2ahci/os2ahci.c	(revision 125)
@@ -73,4 +73,5 @@
 int             thorough_scan = 1; /* if != 0, perform thorough PCI scan */
 int             init_reset;        /* if != 0, reset ports during init */
+int             force_write_cache; /* if != 0, force write cache */
 int             verbosity = 1;     /* == 1 -> show sign on banner
                                     *  > 1 -> show adapter info during boot */
@@ -233,4 +234,9 @@
         /* reset ports during initialization */
 	init_reset = 1;
+        break;
+
+      case 'f':
+        /* force write cache regardless of IORB flags */
+        force_write_cache = 1;
         break;
 
@@ -387,12 +393,14 @@
  * Generic IOCTL via character device driver. IOCTLs are used to control the
  * driver operation and to execute native ATA and ATAPI (SCSI) commands from
- * ring 3 applications.
+ * ring 3 applications. On top of that, some predefined IOCTLs (e.g. SMART
+ * commands for ATA disks) are implemented here.
  */
 USHORT gen_ioctl(RP_GENIOCTL _far *ioctl)
 {
+  dprintf("IOCTL 0x%x/0x%x\n", (u16) ioctl->Category, (u16) ioctl->Function);
+
   switch (ioctl->Category) {
 
   case OS2AHCI_IOCTL_CATEGORY:
-
     switch (ioctl->Function) {
 
@@ -404,5 +412,13 @@
 
     }
-  }
+
+  case DSKSP_CAT_GENERIC:
+    return(ioctl_gen_dsk(ioctl));
+    
+  case DSKSP_CAT_SMART:
+    return(ioctl_smart(ioctl));
+
+  }
+
   return(STDON | STATUS_ERR_UNKCMD);
 }
Index: trunk/src/os2ahci/os2ahci.h
===================================================================
--- trunk/src/os2ahci/os2ahci.h	(revision 123)
+++ trunk/src/os2ahci/os2ahci.h	(revision 125)
@@ -517,4 +517,6 @@
 extern USHORT      ioctl_get_devlist  (RP_GENIOCTL _far *ioctl);
 extern USHORT      ioctl_passthrough  (RP_GENIOCTL _far *ioctl);
+extern USHORT      ioctl_gen_dsk      (RP_GENIOCTL _far *ioctl);
+extern USHORT      ioctl_smart        (RP_GENIOCTL _far *ioctl);
 
 
@@ -527,4 +529,5 @@
 extern int           thorough_scan; /* if != 0, perform thorough PCI scan */
 extern int           init_reset;    /* if != 0, reset ports during init */
+extern int           force_write_cache; /* if != 0, force write cache */
 extern int           verbosity;     /* if != 0, show some info during boot */
 
Index: trunk/src/os2ahci/version.h
===================================================================
--- trunk/src/os2ahci/version.h	(revision 123)
+++ trunk/src/os2ahci/version.h	(revision 125)
@@ -14,4 +14,4 @@
 
 
-#define VERSION            119       /* driver version (2 implied decimals) */
+#define VERSION            121       /* driver version (2 implied decimals) */
 
