Index: /trunk/src/VBox/Devices/USB/DevOHCI.cpp
===================================================================
--- /trunk/src/VBox/Devices/USB/DevOHCI.cpp	(revision 54071)
+++ /trunk/src/VBox/Devices/USB/DevOHCI.cpp	(revision 54072)
@@ -119,9 +119,16 @@
 
 
-/** Number of Downstream Ports on the root hub.
+/** Maximum supported number of Downstream Ports on the root hub. 15 ports
+ * is the maximum defined by the OHCI spec.
  * If you change this you need to add more status register words to the 'opreg'
  * array.
  */
-#define OHCI_NDP 15
+#define OHCI_NDP_MAX        15
+
+/** Default NDP, chosen to be compatible with everything. */
+#define OHCI_NDP_DEFAULT    12
+
+/* Macro to query the number of currently configured ports. */
+#define OHCI_NDP_CFG(pohci) ((pohci)->RootHub.desc_a & OHCI_RHA_NDP)
 
 /** Pointer to OHCI device data. */
@@ -183,5 +190,5 @@
     uint32_t                            Alignment0; /**< Align aPorts on a 8 byte boundary. */
 #endif
-    OHCIHUBPORT                         aPorts[OHCI_NDP];
+    OHCIHUBPORT                         aPorts[OHCI_NDP_MAX];
     R3PTRTYPE(POHCI)                    pOhci;
 } OHCIROOTHUB;
@@ -204,5 +211,5 @@
     unsigned cDevs;
     /** Array of devices which were detached. */
-    PVUSBIDEVICE apDevs[OHCI_NDP];
+    PVUSBIDEVICE apDevs[OHCI_NDP_MAX];
 } OHCILOAD;
 /** Pointer to an OHCILOAD structure. */
@@ -921,5 +928,5 @@
 
     PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
-    for (iPort = 0; iPort < RT_ELEMENTS(pThis->RootHub.aPorts); iPort++)
+    for (iPort = 0; iPort < OHCI_NDP_CFG(pThis); iPort++)
     {
         if (!pThis->RootHub.aPorts[iPort].pDev)
@@ -963,5 +970,5 @@
      * Validate and adjust input.
      */
-    Assert(uPort >= 1 && uPort <= RT_ELEMENTS(pThis->RootHub.aPorts));
+    Assert(uPort >= 1 && uPort <= OHCI_NDP_CFG(pThis));
     uPort--;
     Assert(!pThis->RootHub.aPorts[uPort].pDev);
@@ -1000,5 +1007,5 @@
      * Validate and adjust input.
      */
-    Assert(uPort >= 1 && uPort <= RT_ELEMENTS(pThis->RootHub.aPorts));
+    Assert(uPort >= 1 && uPort <= OHCI_NDP_CFG(pThis));
     uPort--;
     Assert(pThis->RootHub.aPorts[uPort].pDev == pDev);
@@ -1057,5 +1064,5 @@
 
     pThis->RootHub.status = 0;
-    pThis->RootHub.desc_a = OHCI_RHA_NPS | OHCI_NDP;
+    pThis->RootHub.desc_a = OHCI_RHA_NPS | OHCI_NDP_CFG(pThis); /* Preserve NDP value. */
     pThis->RootHub.desc_b = 0x0; /* Impl. specific */
 
@@ -1072,5 +1079,5 @@
      * into this. For the time being we stick with simple.
      */
-    for (unsigned iPort = 0; iPort < RT_ELEMENTS(pThis->RootHub.aPorts); iPort++)
+    for (unsigned iPort = 0; iPort < OHCI_NDP_CFG(pThis); iPort++)
     {
         if (pThis->RootHub.aPorts[iPort].pDev)
@@ -4633,5 +4640,5 @@
     Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
           val,
-          chg & 0xff      ?"!!!": "", OHCI_NDP,
+          chg & 0xff      ?"!!!": "", val & 0xff,
           (chg >>  8) & 1 ? "*" : "", (val >>  8) & 1,
           (chg >>  9) & 1 ? "*" : "", (val >>  9) & 1,
@@ -4646,10 +4653,10 @@
 
 
-    if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP)
+    if ((val & (OHCI_RHA_NDP | OHCI_RHA_DT)) != OHCI_NDP_CFG(pThis))
     {
         Log(("ohci: %s: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n",
                 pThis->PciDev.name, val));
         val &= ~(OHCI_RHA_NDP | OHCI_RHA_DT);
-        val |= OHCI_NDP;
+        val |= OHCI_NDP_CFG(pThis);
     }
 
@@ -4726,7 +4733,7 @@
     if ( val & OHCI_RHS_LPSC )
     {
-        int i;
+        unsigned i;
         Log2(("ohci: %s: global power up\n", pThis->PciDev.name));
-        for (i = 0; i < OHCI_NDP; i++)
+        for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
             rhport_power(&pThis->RootHub, i, true /* power up */);
     }
@@ -4735,7 +4742,7 @@
     if ( val & OHCI_RHS_LPS )
     {
-        int i;
+        unsigned i;
         Log2(("ohci: %s: global power down\n", pThis->PciDev.name));
-        for (i = 0; i < OHCI_NDP; i++)
+        for (i = 0; i < OHCI_NDP_CFG(pThis); i++)
             rhport_power(&pThis->RootHub, i, false /* power down */);
     }
@@ -4800,5 +4807,5 @@
     POHCIHUBPORT pPort = NULL;
     unsigned iPort;
-    for (iPort = 0; iPort < RT_ELEMENTS(pThis->RootHub.aPorts); iPort++) /* lazy bird */
+    for (iPort = 0; iPort < OHCI_NDP_CFG(pThis); iPort++) /* lazy bird */
         if (pThis->RootHub.aPorts[iPort].pDev == pDev)
         {
@@ -5021,5 +5028,5 @@
 
     /* The number of port status register depends on the definition
-     * of OHCI_NDP macro
+     * of OHCI_NDP_MAX macro
      */
     { "HcRhPortStatus[0]",   HcRhPortStatus_r,       HcRhPortStatus_w },        /* 21 */
@@ -5040,4 +5047,11 @@
 };
 
+/* Quick way to determine how many op regs are valid. Since at least one port must 
+ * be configured (and no more than 15), there will be between 22 and 36 registers.
+ */
+#define NUM_OP_REGS(pohci)  (21 + OHCI_NDP_CFG(pohci))
+
+AssertCompile(RT_ELEMENTS(g_aOpRegs) > 21);
+AssertCompile(RT_ELEMENTS(g_aOpRegs) <= 36);
 
 /**
@@ -5057,5 +5071,5 @@
     int rc;
     const uint32_t iReg = (GCPhysAddr - pThis->MMIOBase) >> 2;
-    if (iReg < RT_ELEMENTS(g_aOpRegs))
+    if (iReg < NUM_OP_REGS(pThis))
     {
         const OHCIOPREG *pReg = &g_aOpRegs[iReg];
@@ -5064,5 +5078,5 @@
     else
     {
-        Log(("ohci: Trying to read register %u/%u!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
+        Log(("ohci: Trying to read register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
         rc = VINF_IOM_MMIO_UNUSED_FF;
     }
@@ -5087,5 +5101,5 @@
     int rc;
     const uint32_t iReg = (GCPhysAddr - pThis->MMIOBase) >> 2;
-    if (iReg < RT_ELEMENTS(g_aOpRegs))
+    if (iReg < NUM_OP_REGS(pThis))
     {
         const OHCIOPREG *pReg = &g_aOpRegs[iReg];
@@ -5094,5 +5108,5 @@
     else
     {
-        Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, RT_ELEMENTS(g_aOpRegs)));
+        Log(("ohci: Trying to write to register %u/%u!!!\n", iReg, NUM_OP_REGS(pThis)));
         rc = VINF_SUCCESS;
     }
@@ -5703,5 +5717,6 @@
 static DECLCALLBACK(int) ohciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
 {
-    POHCI pThis = PDMINS_2_DATA(pDevIns, POHCI);
+    POHCI       pThis = PDMINS_2_DATA(pDevIns, POHCI);
+    uint32_t    cPorts;
     PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
 
@@ -5740,5 +5755,5 @@
 
     /*
-     * Read configuration. No configuration keys are currently supported.
+     * Read configuration.
      */
     PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "RZEnabled", "");
@@ -5746,4 +5761,17 @@
     AssertLogRelRCReturn(rc, rc);
 
+    /* Number of ports option. */
+    rc = CFGMR3QueryU32Def(pCfg, "Ports", &cPorts, OHCI_NDP_DEFAULT);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("OHCI configuration error: failed to read Ports as integer"));
+
+    if (cPorts == 0 || cPorts > OHCI_NDP_MAX)
+        return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+                                   N_("OHCI configuration error: Ports must be in range [%u,%u]"),
+                                   1, OHCI_NDP_MAX);
+
+    /* Store the configured NDP; it will be used everywhere else from now on. */
+    pThis->RootHub.desc_a = cPorts;
 
     /*
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 54071)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 54072)
@@ -992,5 +992,5 @@
     GEN_CHECK_OFF(OHCIROOTHUB, aPorts);
     GEN_CHECK_OFF(OHCIROOTHUB, aPorts[1]);
-    GEN_CHECK_OFF(OHCIROOTHUB, aPorts[OHCI_NDP - 1]);
+    GEN_CHECK_OFF(OHCIROOTHUB, aPorts[OHCI_NDP_MAX - 1]);
     GEN_CHECK_OFF(OHCIROOTHUB, pOhci);
 
