INQUIRY command is ILLEGAL REQUEST

I am developing a DriverKit driver with the goal of sending vendor-specific commands to a USB storage device. I have successfully created the DriverKit driver, and when I connect the USB storage device, it appears correctly in IORegistryExplorer. My driver class inherits from IOUserSCSIPeripheralDeviceType00 in the SCSIPeripheralsDriverKit framework.

I also created a UserClient class that inherits from IOUserClient, and from its ExternalMethod I tried sending an INQUIRY command as a basic test to confirm that command transmission works. However, the device returns an ILLEGAL REQUEST (Sense Key 0x5 / ASC 0x20).

Could someone advise what I might be doing wrong?

Below are the logs output from the driver:

2025-11-14 21:00:43.573730+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] Driver - NewUserClient() - Finished.
2025-11-14 21:00:43.573733+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - Start()
2025-11-14 21:00:43.573807+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - Start() - Finished.
2025-11-14 21:00:43.574249+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - ExternalMethod() called
2025-11-14 21:00:43.574258+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - ----- SCSICmdINQUIRY -----
2025-11-14 21:00:43.574268+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - command.fRequestedByteCountOfTransfer = 512
2025-11-14 21:00:43.575980+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fCompletionStatus = 0x0
2025-11-14 21:00:43.575988+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fServiceResponse = 0x2
2025-11-14 21:00:43.575990+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB fSenseDataValid = 0x1
2025-11-14 21:00:43.575992+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB VALID_RESPONSE_CODE = 0x70
2025-11-14 21:00:43.575994+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB SENSE_KEY = 0x5
2025-11-14 21:00:43.575996+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE = 0x20
2025-11-14 21:00:43.575998+0900 0x26e9 Default 0x0 0 0 kernel: (SampleDriverKitApp.SampleDriverKitDriver.dext) [DEBUG] UserClient - SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE_QUALIFIER = 0x0

Here is the UserClient class:

class SampleDriverKitUserClient: public IOUserClient
{
public:
    virtual bool init(void) override;
    virtual kern_return_t Start(IOService* provider) override;
    virtual kern_return_t Stop(IOService* provider) override;
    virtual void free(void) override;

    virtual kern_return_t ExternalMethod(
        uint64_t selector,
        IOUserClientMethodArguments* arguments,
        const IOUserClientMethodDispatch* dispatch,
        OSObject* target,
        void* reference) override;

    void SCSICmdINQUIRY(SampleDriverKitDriver *driver) LOCALONLY;
};

Here is the part that sends the INQUIRY command:

void SampleDriverKitUserClient::SCSICmdINQUIRY(SampleDriverKitDriver *driver)
{
    kern_return_t kr = KERN_SUCCESS;
    SCSIType00OutParameters command = {};
    UInt8 dataBuffer[512] = {0};
    SCSI_Sense_Data senseData = {0};

    Log("----- SCSICmdINQUIRY -----");

    SetCommandCDB(&command.fCommandDescriptorBlock, 0x12, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

    command.fLogicalUnitNumber = 0;
    command.fTimeoutDuration = 10000;   // milliseconds

    command.fRequestedByteCountOfTransfer = sizeof(dataBuffer);
    Log("command.fRequestedByteCountOfTransfer = %lld", command.fRequestedByteCountOfTransfer);

    command.fBufferDirection = kIOMemoryDirectionIn;
    command.fDataTransferDirection = kSCSIDataTransfer_FromTargetToInitiator;

    command.fDataBufferAddr = reinterpret_cast<uint64_t>(dataBuffer);
    command.fSenseBufferAddr = reinterpret_cast<uint64_t>(&senseData);
    command.fSenseLengthRequested = sizeof(senseData);
    
    if( driver ) {
        SCSIType00InParameters response = {};
        kr = driver->UserSendCDB(command, &response);
        if( kr != KERN_SUCCESS ) {
            Log("SCSICmdINQUIRY() UserSendCDB failed (0x%x)", kr);
            return;
        }
        Log("SCSICmdINQUIRY() UserSendCDB fCompletionStatus = 0x%x", response.fCompletionStatus);
        Log("SCSICmdINQUIRY() UserSendCDB fServiceResponse = 0x%x", response.fServiceResponse);
        Log("SCSICmdINQUIRY() UserSendCDB fSenseDataValid = 0x%x", response.fSenseDataValid);
        
        Log("SCSICmdINQUIRY() UserSendCDB VALID_RESPONSE_CODE = 0x%x", senseData.VALID_RESPONSE_CODE);
        Log("SCSICmdINQUIRY() UserSendCDB SENSE_KEY = 0x%x", senseData.SENSE_KEY);
        Log("SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE = 0x%x", senseData.ADDITIONAL_SENSE_CODE);
        Log("SCSICmdINQUIRY() UserSendCDB ADDITIONAL_SENSE_CODE_QUALIFIER = 0x%x", senseData.ADDITIONAL_SENSE_CODE_QUALIFIER);

        if( response.fServiceResponse == kSCSIServiceResponse_TASK_COMPLETE ) {
            Log("SCSICmdINQUIRY() UserSendCDB complete success!!");
        }
        
        
        for( int i=0; i < 5; i++ ) {
            Log("data [%04d]=0x%x  [%04d]=0x%x  [%04d]=0x%x  [%04d]=0x%x  [%04d]=0x%x  [%04d]=0x%x  [%04d]=0x%x  [%04d]=0x%x",
                i*8+0, dataBuffer[i*8+0],
                i*8+1, dataBuffer[i*8+1],
                i*8+2, dataBuffer[i*8+2],
                i*8+3, dataBuffer[i*8+3],
                i*8+4, dataBuffer[i*8+4],
                i*8+5, dataBuffer[i*8+5],
                i*8+6, dataBuffer[i*8+6],
                i*8+7, dataBuffer[i*8+7] );
        }
        char vendorID[9] = {0};
        memcpy(vendorID, &dataBuffer[8], 8);
        Log("vendorID = %s",vendorID);
        char productID[17] = {0};
        memcpy(productID, &dataBuffer[16], 16);
        Log("productID = %s",productID);
    }
}

My environment is: MacBook Pro (M2), macOS 15.6

If anyone has insight into what causes the ILLEGAL REQUEST, or what I am missing when using IOUserSCSIPeripheralDeviceType00 and UserSendCDB, I would greatly appreciate your help.

Thank you.

Answered by DTS Engineer in 866243022

However, the device returns an ILLEGAL REQUEST (Sense Key 0x5 / ASC 0x20). Could someone advise what I might be doing wrong?

Have you tried using the "INQUIRY()" (see IOUserSCSIPeripheralDeviceHelper.h) helper function? Either directly or by comparing/modifying it's configuration after it returns the command to you?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

However, the device returns an ILLEGAL REQUEST (Sense Key 0x5 / ASC 0x20). Could someone advise what I might be doing wrong?

Have you tried using the "INQUIRY()" (see IOUserSCSIPeripheralDeviceHelper.h) helper function? Either directly or by comparing/modifying it's configuration after it returns the command to you?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

INQUIRY command is ILLEGAL REQUEST
 
 
Q