/**
 * USBA driver election callback.
 *
 * @returns USB_SUCCESS if we want to capture the device, USB_FAILURE otherwise.
 * @param   pDevDesc        The parsed device descriptor (does not include subconfigs).
 * @param   pDevStrings     Device strings: Manufacturer, Product, Serial Number.
 * @param   pszDevicePath   The physical path of the device being attached.
 * @param   Bus             The Bus number on which the device is on.
 * @param   Port            The Port number on the bus.
 * @param   ppszDrv         The name of the driver we wish to capture the device with.
 * @param   pvReserved      Reserved for future use.
 */
int VBoxUSBMonSolarisElectDriver(usb_dev_descr_t *pDevDesc, usb_dev_str_t *pDevStrings, char *pszDevicePath, int Bus, int Port,
                                char **ppszDrv, void *pvReserved)
{
    LogFunc((DEVICE_NAME ":VBoxUSBMonSolarisElectDriver pDevDesc=%p pDevStrings=%p pszDevicePath=%s Bus=%d Port=%d\n", pDevDesc,
            pDevStrings, pszDevicePath, Bus, Port));

    AssertPtrReturn(pDevDesc, USB_FAILURE);
    AssertPtrReturn(pDevStrings, USB_FAILURE);

    /*
     * Create a filter from the device being attached.
     */
    USBFILTER Filter;
    USBFilterInit(&Filter, USBFILTERTYPE_CAPTURE);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_VENDOR_ID, pDevDesc->idVendor, true);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_PRODUCT_ID, pDevDesc->idProduct, true);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_REV, pDevDesc->bcdDevice, true);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_CLASS, pDevDesc->bDeviceClass, true);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS, pDevDesc->bDeviceSubClass, true);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_DEVICE_PROTOCOL, pDevDesc->bDeviceProtocol, true);
    USBFilterSetNumExact(&Filter, USBFILTERIDX_BUS, 0x0 /* Bus */, true); /* Use 0x0 as userland initFilterFromDevice function in Main: see comment on "SetMustBePresent" below */
    USBFilterSetNumExact(&Filter, USBFILTERIDX_PORT, Port, true);
    USBFilterSetStringExact(&Filter, USBFILTERIDX_MANUFACTURER_STR, pDevStrings->usb_mfg, true);
    USBFilterSetStringExact(&Filter, USBFILTERIDX_PRODUCT_STR, pDevStrings->usb_product, true);
    USBFilterSetStringExact(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR, pDevStrings->usb_serialno, true);

    /* This doesn't work like it should (USBFilterMatch fails on matching field (6) i.e. Bus despite this. Investigate later. */
    USBFilterSetMustBePresent(&Filter, USBFILTERIDX_BUS, false /* fMustBePresent */);

    Log((DEVICE_NAME ":VBoxUSBMonSolarisElectDriver: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
              USBFilterGetNum(&Filter, USBFILTERIDX_VENDOR_ID),
              USBFilterGetNum(&Filter, USBFILTERIDX_PRODUCT_ID),
              USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_REV),
              USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_CLASS),
              USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_SUB_CLASS),
              USBFilterGetNum(&Filter, USBFILTERIDX_DEVICE_PROTOCOL),
              USBFilterGetNum(&Filter, USBFILTERIDX_BUS),
              USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
    Log((DEVICE_NAME ":VBoxUSBMonSolarisElectDriver: Manufacturer=%s Product=%s Serial=%s\n",
              USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR)  ? USBFilterGetString(&Filter, USBFILTERIDX_MANUFACTURER_STR)  : "<null>",
              USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR)       ? USBFilterGetString(&Filter, USBFILTERIDX_PRODUCT_STR)       : "<null>",
              USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(&Filter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));

    /*
     * Run through user filters and try to see if it has a match.
     */
    uintptr_t uId = 0;
    RTPROCESS Owner = VBoxUSBFilterMatch(&Filter, &uId);
    USBFilterDelete(&Filter);
    if (Owner == NIL_RTPROCESS)
    {
        Log((DEVICE_NAME ":No matching filters, device %#x:%#x uninteresting.\n", pDevDesc->idVendor, pDevDesc->idProduct));
        return USB_FAILURE;
    }

    *ppszDrv = ddi_strdup(VBOXUSB_DRIVER_NAME, KM_SLEEP);
    LogRel((DEVICE_NAME ": Capturing %s %#x:%#x:%s\n", pDevStrings->usb_product ? pDevStrings->usb_product : "<Unnamed USB device>",
                    pDevDesc->idVendor, pDevDesc->idProduct, pszDevicePath));
    return USB_SUCCESS;
}
/**
 *  Initializes the USB device filter object (short version).
 *
 *  @param aParent  Handle of the parent object.
 */
HRESULT HostUSBDeviceFilter::init(Host *aParent, IN_BSTR aName)
{
    LogFlowThisFunc(("aParent=%p\n", aParent));

    ComAssertRet(aParent && aName && *aName, E_INVALIDARG);

    /* Enclose the state transition NotReady->InInit->Ready */
    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    unconst(mParent) = aParent;

    /* register with parent early, since uninit() will unconditionally
     * unregister on failure */
    mParent->i_addChild(this);

    bd.allocate();

    bd->mData.strName = Utf8Str(aName);
    bd->mData.fActive = FALSE;
    mInList = false;
    USBFilterInit(&bd->mUSBFilter, USBFILTERTYPE_IGNORE);
    bd->mRemote = NULL;
    bd->mData.ulMaskedInterfaces = 0;

    /* Confirm successful initialization */
    autoInitSpan.setSucceeded();

    return S_OK;
}
/**
 *  Initializes the USB device filter object (short version).
 *
 *  @param aParent  Handle of the parent object.
 */
HRESULT USBDeviceFilter::init(USBDeviceFilters *aParent, IN_BSTR aName)
{
    LogFlowThisFunc(("aParent=%p\n", aParent));

    ComAssertRet(aParent && aName && *aName, E_INVALIDARG);

    /* Enclose the state transition NotReady->InInit->Ready */
    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    unconst(mParent) = aParent;
    /* mPeer is left null */

    m_fModified = false;

    bd.allocate();

    bd->mData.strName = Utf8Str(aName);
    bd->mData.fActive = FALSE;
    bd->mData.ulMaskedInterfaces = 0;

    /* initialize all filters to any match using null string */
    USBFilterInit(&bd->mUSBFilter, USBFILTERTYPE_CAPTURE);
    bd->mRemote = NULL;

    mInList = false;

    /* Confirm successful initialization */
    autoInitSpan.setSucceeded();

    return S_OK;
}
Example #4
0
int USBProxyServiceDarwin::releaseDevice(HostUSBDevice *aDevice)
{
    /*
     * Check preconditions.
     */
    AssertReturn(aDevice, VERR_GENERAL_FAILURE);
    AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);

    AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
    LogFlowThisFunc(("aDevice=%s\n", aDevice->getName().c_str()));

    Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost);

#ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN
    /*
     * Fake it.
     */
    ASMAtomicWriteBool(&mFakeAsync, true);
    devLock.release();
    interruptWait();
    return VINF_SUCCESS;

#else
    /*
     * Create a one-shot ignore filter for the device
     * and trigger a re-enumeration of it.
     */
    USBFILTER Filter;
    USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE);
    initFilterFromDevice(&Filter, aDevice);
    Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
    Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS)));

    void *pvId = USBLibAddFilter(&Filter);
    if (!pvId)
        return VERR_GENERAL_FAILURE;

    int rc = DarwinReEnumerateUSBDevice(aDevice->mUsb);
    if (RT_SUCCESS(rc))
        aDevice->mOneShotId = pvId;
    else
    {
        USBLibRemoveFilter(pvId);
        pvId = NULL;
    }
    LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
    return rc;
#endif
}
int USBProxyServiceWindows::releaseDevice(HostUSBDevice *aDevice)
{
    /*
     * Check preconditions.
     */
    AssertReturn(aDevice, VERR_GENERAL_FAILURE);
    AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);

    AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
    LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));

    Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);

    /*
     * Create a one-shot ignore filter for the device
     * and trigger a re-enumeration of it.
     */
    USBFILTER Filter;
    USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE);
    initFilterFromDevice(&Filter, aDevice);
    Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
    Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS)));

    void *pvId = USBLibAddFilter(&Filter);
    if (!pvId)
    {
        AssertMsgFailed(("Add one-shot Filter failed\n"));
        return VERR_GENERAL_FAILURE;
    }

    int rc = USBLibRunFilters();
    if (!RT_SUCCESS(rc))
    {
        AssertMsgFailed(("Run Filters failed\n"));
        USBLibRemoveFilter(pvId);
        return rc;
    }


    return VINF_SUCCESS;
}
int USBProxyBackendSolaris::releaseDevice(HostUSBDevice *aDevice)
{
    /*
     * Check preconditions.
     */
    AssertReturn(aDevice, VERR_GENERAL_FAILURE);
    AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);

    AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
    LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));

    Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
    AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER);

    /*
     * Create a one-shot ignore filter for the device and reset it.
     */
    USBFILTER Filter;
    USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE);
    initFilterFromDevice(&Filter, aDevice);

    void *pvId = USBLibAddFilter(&Filter);
    if (!pvId)
    {
        LogRel(("USBService: Adding ignore filter failed!\n"));
        return VERR_GENERAL_FAILURE;
    }

    PUSBDEVICE pDev = aDevice->i_getUsbData();
    int rc = USBLibResetDevice(pDev->pszDevicePath, true /* Re-attach */);
    if (RT_SUCCESS(rc))
        aDevice->i_setBackendUserData(pvId);
    else
    {
        USBLibRemoveFilter(pvId);
        pvId = NULL;
    }
    LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
    return rc;
}
int USBProxyServiceSolaris::captureDevice(HostUSBDevice *aDevice)
{
    /*
     * Check preconditions.
     */
    AssertReturn(aDevice, VERR_GENERAL_FAILURE);
    AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);

    AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
    LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));

    Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
    AssertReturn(aDevice->mUsb, VERR_INVALID_POINTER);

    /*
     * Create a one-shot capture filter for the device and reset the device.
     */
    USBFILTER Filter;
    USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE);
    initFilterFromDevice(&Filter, aDevice);

    void *pvId = USBLibAddFilter(&Filter);
    if (!pvId)
    {
        LogRel(("USBService: failed to add filter\n"));
        return VERR_GENERAL_FAILURE;
    }

    PUSBDEVICE pDev = aDevice->mUsb;
    int rc = USBLibResetDevice(pDev->pszDevicePath, true);
    if (RT_SUCCESS(rc))
        aDevice->mOneShotId = pvId;
    else
    {
        USBLibRemoveFilter(pvId);
        pvId = NULL;
    }
    LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId));
    return rc;
}
/**
 *  Initializes the USB device filter object.
 *
 *  @param aParent  Handle of the parent object.
 */
HRESULT HostUSBDeviceFilter::init(Host *aParent,
                                  const settings::USBDeviceFilter &data)
{
    LogFlowThisFunc(("aParent=%p\n", aParent));

    ComAssertRet(aParent && !data.strName.isEmpty(), E_INVALIDARG);

    /* Enclose the state transition NotReady->InInit->Ready */
    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    unconst(mParent) = aParent;

    /* register with parent early, since uninit() will unconditionally
     * unregister on failure */
    mParent->i_addChild(this);

    bd.allocate();
    bd->mData.strName = data.strName;
    bd->mData.fActive = data.fActive;
    USBFilterInit (&bd->mUSBFilter, USBFILTERTYPE_IGNORE);
    bd->mRemote = NULL;
    bd->mData.ulMaskedInterfaces = 0;

    mInList = false;

    /* use setters for the attributes below to reuse parsing errors
     * handling */

    HRESULT rc = S_OK;
    do
    {
        rc = setAction(data.action);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_VENDOR_ID, data.strVendorId);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_PRODUCT_ID, data.strProductId);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_DEVICE, data.strRevision);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_MANUFACTURER_STR, data.strManufacturer);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_PRODUCT_ID, data.strProduct);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_SERIAL_NUMBER_STR, data.strSerialNumber);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_PORT, data.strPort);
        if (FAILED(rc)) break;
    }
    while (0);

    /* Confirm successful initialization when it's the case */
    if (SUCCEEDED(rc))
        autoInitSpan.setSucceeded();

    return rc;
}
/**
 *  Initializes the USB device filter object.
 *
 *  @param aParent  Handle of the parent object.
 *  @param data     Reference filter settings.
 */
HRESULT USBDeviceFilter::init(USBDeviceFilters *aParent,
                              const settings::USBDeviceFilter &data)
{
    LogFlowThisFunc(("aParent=%p\n", aParent));

    ComAssertRet(aParent && !data.strName.isEmpty(), E_INVALIDARG);

    /* Enclose the state transition NotReady->InInit->Ready */
    AutoInitSpan autoInitSpan(this);
    AssertReturn(autoInitSpan.isOk(), E_FAIL);

    unconst(mParent) = aParent;
    /* mPeer is left null */

    m_fModified = false;

    bd.allocate();
    bd->mData.strName = data.strName;
    bd->mData.fActive = data.fActive;
    bd->mData.ulMaskedInterfaces = 0;

    /* initialize all filters to any match using null string */
    USBFilterInit(&bd->mUSBFilter, USBFILTERTYPE_CAPTURE);
    bd->mRemote = NULL;

    mInList = false;

    /* use setters for the attributes below to reuse parsing errors
     * handling */

    HRESULT rc = S_OK;
    do
    {
        rc = i_usbFilterFieldSetter(USBFILTERIDX_VENDOR_ID, data.strVendorId);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_PRODUCT_ID, data.strProductId);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_DEVICE, data.strRevision);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_MANUFACTURER_STR, data.strManufacturer);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_PRODUCT_STR, data.strProduct);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_SERIAL_NUMBER_STR, data.strSerialNumber);
        if (FAILED(rc)) break;

        rc = i_usbFilterFieldSetter(USBFILTERIDX_PORT, data.strPort);
        if (FAILED(rc)) break;

        rc = COMSETTER(Remote)(Bstr(data.strRemote).raw());
        if (FAILED(rc)) break;

        rc = COMSETTER(MaskedInterfaces)(data.ulMaskedInterfaces);
        if (FAILED(rc)) break;
    }
    while (0);

    /* Confirm successful initialization when it's the case */
    if (SUCCEEDED(rc))
        autoInitSpan.setSucceeded();

    return rc;
}
/**
 *  Returns true if the given USB device matches to at least one of
 *  this controller's USB device filters.
 *
 *  A generic version that accepts any IUSBDevice on input.
 *
 *  @note
 *      This method MUST correlate with HostUSBDevice::isMatch()
 *      in the sense of the device matching logic.
 *
 *  @note Locks this object for reading.
 */
bool USBController::hasMatchingFilter (IUSBDevice *aUSBDevice, ULONG *aMaskedIfs)
{
    LogFlowThisFuncEnter();

    AutoCaller autoCaller(this);
    AssertComRCReturn (autoCaller.rc(), false);

    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);

    /* Disabled USB controllers cannot actually work with USB devices */
    if (!m->bd->fEnabled)
        return false;

    HRESULT rc = S_OK;

    /* query fields */
    USBFILTER dev;
    USBFilterInit (&dev, USBFILTERTYPE_CAPTURE);

    USHORT vendorId = 0;
    rc = aUSBDevice->COMGETTER(VendorId) (&vendorId);
    ComAssertComRCRet(rc, false);
    ComAssertRet(vendorId, false);
    int vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_VENDOR_ID, vendorId, true); AssertRC(vrc);

    USHORT productId = 0;
    rc = aUSBDevice->COMGETTER(ProductId) (&productId);
    ComAssertComRCRet(rc, false);
    vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_PRODUCT_ID, productId, true); AssertRC(vrc);

    USHORT revision;
    rc = aUSBDevice->COMGETTER(Revision) (&revision);
    ComAssertComRCRet(rc, false);
    vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_DEVICE, revision, true); AssertRC(vrc);

    Bstr manufacturer;
    rc = aUSBDevice->COMGETTER(Manufacturer) (manufacturer.asOutParam());
    ComAssertComRCRet(rc, false);
    if (!manufacturer.isEmpty())
        USBFilterSetStringExact (&dev, USBFILTERIDX_MANUFACTURER_STR, Utf8Str(manufacturer).c_str(), true);

    Bstr product;
    rc = aUSBDevice->COMGETTER(Product) (product.asOutParam());
    ComAssertComRCRet(rc, false);
    if (!product.isEmpty())
        USBFilterSetStringExact (&dev, USBFILTERIDX_PRODUCT_STR, Utf8Str(product).c_str(), true);

    Bstr serialNumber;
    rc = aUSBDevice->COMGETTER(SerialNumber) (serialNumber.asOutParam());
    ComAssertComRCRet(rc, false);
    if (!serialNumber.isEmpty())
        USBFilterSetStringExact (&dev, USBFILTERIDX_SERIAL_NUMBER_STR, Utf8Str(serialNumber).c_str(), true);

    Bstr address;
    rc = aUSBDevice->COMGETTER(Address) (address.asOutParam());
    ComAssertComRCRet(rc, false);

    USHORT port = 0;
    rc = aUSBDevice->COMGETTER(Port)(&port);
    ComAssertComRCRet(rc, false);
    USBFilterSetNumExact (&dev, USBFILTERIDX_PORT, port, true);

    BOOL remote = FALSE;
    rc = aUSBDevice->COMGETTER(Remote)(&remote);
    ComAssertComRCRet(rc, false);
    ComAssertRet(remote == TRUE, false);

    bool match = false;

    /* apply self filters */
    for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
         it != m->llDeviceFilters->end();
         ++ it)
    {
        AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
        const USBDeviceFilter::Data &aData = (*it)->getData();

        if (!aData.mActive)
            continue;
        if (!aData.mRemote.isMatch (remote))
            continue;
        if (!USBFilterMatch (&aData.mUSBFilter, &dev))
            continue;

        match = true;
        *aMaskedIfs = aData.mMaskedIfs;
        break;
    }

    LogFlowThisFunc(("returns: %d\n", match));
    LogFlowThisFuncLeave();

    return match;
}
Example #11
0
int main()
{
    unsigned cErrors = 0;
    RTR3InitExeNoArguments(0);

    /*
     * Basic property setting and simple matching.
     */
    USBFILTER Flt1;
    USBFilterInit(&Flt1, USBFILTERTYPE_CAPTURE);
    /* numbers */
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_VENDOR_ID, 0x1111, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PRODUCT_ID, 0x2222, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE, 0, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_CLASS, 0, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_SUB_CLASS, 0, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_DEVICE_PROTOCOL, 0xff, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
    TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_BUS));
    TST_CHECK_RC(USBFilterSetPresentOnly(&Flt1, USBFILTERIDX_BUS));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, false));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 1, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 1, false));
    TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_PORT));
    /* strings */
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "foobar", true, false ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR, "foobar", true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString128, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString128, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString128, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString128, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString128, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR,       "barbar", true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR,       g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR,       g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, g_szString64, true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_MANUFACTURER_STR,  "vendor", true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR,       "product", true, false  ));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_SERIAL_NUMBER_STR, "serial", true, false  ));

    /* cloning */
    USBFILTER Dev;
    USBFilterClone(&Dev, &Flt1);

    TST_CHECK_EXPR(USBFilterIsIdentical(&Dev, &Flt1));
    TST_CHECK_EXPR(USBFilterMatch(&Dev, &Flt1));

    USBFilterDelete(&Flt1);
    USBFilterDelete(&Dev);

    /* make a sample device */
    USBFilterInit(&Dev, USBFILTERTYPE_CAPTURE);
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_VENDOR_ID,         0x1111, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_PRODUCT_ID,        0x2222, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_DEVICE,            0, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_DEVICE_CLASS,      0, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_DEVICE_SUB_CLASS,  0, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_DEVICE_PROTOCOL,   0xff, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_BUS,               1, true));
    TST_CHECK_RC(USBFilterSetNumExact(&Dev,     USBFILTERIDX_PORT,              2, true));
    TST_CHECK_RC(USBFilterSetStringExact(&Dev,  USBFILTERIDX_MANUFACTURER_STR,  "vendor", true, false ));
    TST_CHECK_RC(USBFilterSetStringExact(&Dev,  USBFILTERIDX_PRODUCT_STR,       "product", true, false ));
    TST_CHECK_RC(USBFilterSetStringExact(&Dev,  USBFILTERIDX_SERIAL_NUMBER_STR, "serial", true, false ));

    /* do some basic matching tests */
    USBFilterInit(&Flt1, USBFILTERTYPE_CAPTURE);
    TST_CHECK_EXPR(!USBFilterHasAnySubstatialCriteria(&Flt1));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev) /* 100% ignore filter */);

    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 3, true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_PORT, 2, true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 2, true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExact(&Flt1, USBFILTERIDX_BUS, 1, true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "no match", true, false ));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringExact(&Flt1, USBFILTERIDX_PRODUCT_STR, "product", true, false ));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    /* string patterns */
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*product", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "product*", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*t", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*uct", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*uct", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*duct", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "pro*x", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*product*", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*oduct*", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "*produc*", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?t", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?*?*?***??t", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "?r??u*?*?*?***??", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*d*t", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetStringPattern(&Flt1, USBFILTERIDX_PRODUCT_STR, "p*x*t", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetIgnore(&Flt1, USBFILTERIDX_PRODUCT_STR));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    /* numeric patterns */
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0X1111", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4369", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010421", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111-0x1111", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4369-4369", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010421-010421", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1110-0x1112", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "4360-4370", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "010420-010422", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1112-0x1110", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0x1f", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-19", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-017", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0xffff", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-65535", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0-177777", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0-0XABCD", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x0EF-0XABCD", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0X0ef-0Xabcd", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "42|1|0x1111", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "42|0x1111|1", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1111|42|1", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x1112|42|1", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));

    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "39-59|0x256-0x101f|0xfffff-0xf000|0x1000-0x2000", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "0x000256-0x0101f|0xf000-0xfffff|0x000008000-0x2000|39-59", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "| | \t \t\t| 0x256 - 0x101f   | 0xf000 - 0xfeff\t| 0x1000 -\t0x6000 | 1- 0512", true));
    TST_CHECK_EXPR(USBFilterMatch(&Flt1, &Dev));
    TST_CHECK_RC(USBFilterSetNumExpression(&Flt1, USBFILTERIDX_VENDOR_ID, "| | \t \t\t| 0x256 - 0x101f   | 0xf000 - 0xfeff\t| 0x1112 -\t0x6000 | 1- 0512", true));
    TST_CHECK_EXPR(!USBFilterMatch(&Flt1, &Dev));


    USBFilterDelete(&Flt1);

    /*
     * string overflow
     */
    struct
    {
        uint64_t  u64Pre;
        USBFILTER Flt;
        uint64_t  u64Post;
    } sOf;
    sOf.u64Pre = sOf.u64Post = UINT64_C(0x1234567887654321);

    USBFilterInit(&sOf.Flt, USBFILTERTYPE_CAPTURE);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));

    AssertCompileMemberSize(USBFILTER, achStrTab, 256);
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   &g_szString256[0],  true, false  ) == VERR_BUFFER_OVERFLOW);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   &g_szString256[1],  true, false  ) == VERR_BUFFER_OVERFLOW);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   &g_szString256[2],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   &g_szString256[3],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    /* 0 + 1 */
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   "",                 true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR,         &g_szString256[2],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR,         &g_szString256[1],  true, false  ) == VERR_BUFFER_OVERFLOW);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    /* 0 + 2 */
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR,         &g_szString128[2],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   &g_szString128[1],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    /* 3 */
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_SERIAL_NUMBER_STR,   &g_szString64[0],   true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_PRODUCT_STR,         &g_szString64[0],   true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR,    &g_szString128[4],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR,    &g_szString128[4],  true, false  ) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetStringExact(&sOf.Flt, USBFILTERIDX_MANUFACTURER_STR,    &g_szString128[3],  true, false  ) == VERR_BUFFER_OVERFLOW);
    TST_CHECK_EXPR(sOf.u64Pre == UINT64_C(0x1234567887654321)); TST_CHECK_EXPR(sOf.u64Post == UINT64_C(0x1234567887654321));

    /*
     * Check for a string replacement bug.
     */
    USBFILTER Dev2;
    USBFilterInit(&Dev2, USBFILTERTYPE_CAPTURE);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_VENDOR_ID,         0x19b6, true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_PRODUCT_ID,        0x1024, true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_DEVICE_REV,        0x0141, true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_DEVICE_CLASS,      0,      true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_DEVICE_SUB_CLASS,  0,      true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_DEVICE_PROTOCOL,   0,      true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetNumExact(&Dev2,      USBFILTERIDX_PORT,              0x1,    true) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2,   USBFILTERIDX_MANUFACTURER_STR, "Generic", true, false ) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2,   USBFILTERIDX_PRODUCT_STR, "Mass Storage Device", true, false ) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterSetStringExact(&Dev2,   USBFILTERIDX_MANUFACTURER_STR, "YBU1PPRS", true, false ) == VINF_SUCCESS);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_VENDOR_ID) == 0x19b6);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_PRODUCT_ID) == 0x1024);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_REV) == 0x0141);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_CLASS) == 0);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_SUB_CLASS) == 0);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_DEVICE_PROTOCOL) == 0);
    TST_CHECK_EXPR(USBFilterGetNum(&Dev2, USBFILTERIDX_PORT) == 1);


    /*
     * Summary.
     */
    if (!cErrors)
        RTPrintf(TESTCASE ": SUCCESS\n");
    else
        RTPrintf(TESTCASE ": FAILURE - %d errors\n", cErrors);
    return !!cErrors;
}