I already have a working KEXT which I use for sending SCSI vendor commands for a SCSI Peripheral Type 0 device with IOSCSIPeripheralDeviceNub as the provider. Currently, in the process of migrating to DriverKit, I tried using the IOUserSCSIPeripheralDeviceType00 class of SCSIPeripheralsDriverKit as the methods here seem familiar to the ones in IOKit and as suggested here.

I am able to successfully match and load the driver against the desired nub once approved and I can verify the same from IORegistryExplorer and by using the systemextensionsctl list command. However, all the different methods under this class seem to fail with kIOReturnUnsupported (0xe00002c7) no matter what I do.

Here is the property list I have used for matching and the basic code I am using to initialise the driver and to send INQUIRY command to the underlying device.

Info.plist

<key>IOKitPersonalities</key>
    <dict>
        <key>MyDriver</key>
        <dict>
            <key>CFBundleIdentifier</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
            <key>CFBundleIdentifierKernel</key>
            <string>com.apple.kpi.iokit</string>
            <key>IOClass</key>
            <string>IOUserService</string>
            <key>IOProbeScore</key>
            <integer>16000</integer>
            <key>IOProviderClass</key>
            <string>IOSCSIPeripheralDeviceNub</string>
            <key>IOResourceMatch</key>
            <string>IOKit</string>
            <key>IOUserClass</key>
            <string>MyDriver</string>
            <key>IOUserServerName</key>
            <string>com.example.MyDriver</string>
            <key>Peripheral Device Type</key>
            <integer>0</integer>
            <key>Product Identification</key>
            <string>Product Name</string>
            <key>UserClientProperties</key>
            <dict>
                <key>IOClass</key>
                <string>IOUserUserClient</string>
                <key>IOUserClass</key>
                <string>MyDriverUserClient</string>
            </dict>
            <key>Vendor Identification</key>
            <string>Vendor Name</string>
        </dict>
    </dict>

IORegistryExplorer driver stack

IOUSBHostInterface@0
  +-o IOUSBMassStorageInterfaceNub  
    +-o IOUSBMassStorageDriverNub
      +-o IOUSBMassStorageUASDriver
        +-o IOSCSITargetDevice
          +-o IOSCSIHierarchicalLogicalUnit@0000000000000000
            +-o MyDriver

MyDriver.iig

#include <Availability.h>
#include <DriverKit/IOService.iig>
#include <DriverKit/IOUserClient.iig>
#include <SCSIPeripheralsDriverKit/IOUserSCSIPeripheralDeviceType00.iig>

class MyDriver: public IOUserSCSIPeripheralDeviceType00
{
public:
    virtual bool init() override;
    virtual kern_return_t Start(IOService * provider) override;
    virtual kern_return_t Stop(IOService *provider) override;
    virtual void free() override;
    virtual kern_return_t NewUserClient(uint32_t type, IOUserClient **userClient) override;
    virtual kern_return_t UserDetermineDeviceCharacteristics(bool* result) override;

public:
    kern_return_t UserClientToMyDriver(IOUserClientMethodArguments* arguments) LOCALONLY;
};

MyDriver.cpp

#include <os/log.h>

#include <DriverKit/IOUserServer.h>
#include <DriverKit/IOLib.h>
#include <DriverKit/IOMemoryMap.h>
#include <DriverKit/OSData.h>
#include <DriverKit/IOBufferMemoryDescriptor.h>
#include <SCSIPeripheralsDriverKit/IOUserSCSIPeripheralDeviceType00.h>
#include <SCSIPeripheralsDriverKit/IOUserSCSIPeripheralDeviceHelper.h>

#include "MyDriver.h"

bool
MyDriver::init()
{
    if (!super::init())
    {
        return false;
    }
    return true;
}

kern_return_t
IMPL(MyDriver, Start)
{
    kern_return_t ret = kIOReturnSuccess;

    ret = Start(provider, SUPERDISPATCH);
    if(ret != kIOReturnSuccess)
    {
        Stop(provider, SUPERDISPATCH);
        return kIOReturnNoDevice;
    }

    if (kIOReturnSuccess != RegisterService())
    {
        return kIOReturnError;
    }
    
    return kIOReturnSuccess;
}

kern_return_t
IMPL(MyDriver, Stop)
{
    kern_return_t ret = kIOReturnSuccess;
    
    ret = Stop(provider, SUPERDISPATCH);
    if(ret != kIOReturnSuccess)
    {
        return ret;
    }
    return kIOReturnSuccess;
}

void
MyDriver::free()
{   
    super::free();
}

kern_return_t
IMPL(MyDriver, NewUserClient)
{   
    IOService* client;

    auto kr = Create(this, "UserClientProperties", &client);
    if (kr != kIOReturnSuccess) {
        return kr;
    }

    *userClient = OSDynamicCast(IOUserClient, client);
    if (!*userClient) {
        client->release();
        return kIOReturnError;
    }

    return kIOReturnSuccess;
}

// Not called. As per the documentation, this should be called during enumeration. 
kern_return_t IMPL(MyDriver, UserDetermineDeviceCharacteristics)
{
    *result = true;
    return kIOReturnSuccess;
}

kern_return_t
MyDriver::UserClientToMyDriver(IOUserClientMethodArguments* arguments)
{    
    SCSIServiceResponse resp;
    SCSIType00OutParameters request;
    SCSIType00InParameters response;
    SCSIType00InParameters cmdResponse;
    UInt64 blockSize = 0;
    bool status = false;
    
    // returns 2c7, tried sending command with and without calling this
    kern_return_t ret = UserSuspendServices();

    // returns 2c7    
    ret = UserReportMediumBlockSize(&blockSize);
    
    IOBufferMemoryDescriptor* reqBuf = nullptr;
    IOBufferMemoryDescriptor* senseBuf = nullptr;
    UInt64 reqBufAddr = 0; UInt64 reqBufLen = 0; IOAddressSegment reqBufSegment;
    UInt64 senseBufAddr = 0; UInt64 senseBufLen = 0; IOAddressSegment senseBufSegment;
    
    ret = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOut, sizeof(SCSICmd_INQUIRY_StandardData), 0, &reqBuf);
    if (ret != kIOReturnSuccess)
    {
        goto Cleanup;
    }
    
    ret = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionIn, sizeof(SCSI_Sense_Data), 0, &senseBuf);
    if (ret != kIOReturnSuccess)
    {
        goto Cleanup;
    }
    
    ret = reqBuf->GetAddressRange(&reqBufSegment);
    if (ret != kIOReturnSuccess)
    {
        goto Cleanup;
    }
    reqBufAddr = reqBufSegment.address; reqBufLen = reqBufSegment.length;
    
    ret = senseBuf->GetAddressRange(&senseBufSegment);
    if (ret != kIOReturnSuccess)
    {
        goto Cleanup;
    }
    senseBufAddr = senseBufSegment.address; senseBufLen = senseBufSegment.length;
    
    status = INQUIRY(&request, reqBufAddr, &response, senseBufAddr);
    
    // The fields in request are filled properly with respect to INQUIRY, returns 2c7
    ret = UserSendCDB(request, &cmdResponse);
    
Cleanup:
    if (reqBuf)
    {
        reqBuf->release();
        reqBuf = nullptr;
    }
    
    if (senseBuf)
    {
        senseBuf->release();
        senseBuf = nullptr;
    }
    
    // returns 2c7
    kr = UserResumeServices();
    
    return ret;
}

I have also tried sending custom CDB for vendor specific commands but to no avail. Has anyone been successful in sending a SCSI command to a peripheral type 0 device using this SCSIPeripheralsDriverKit?

0

There are 0 best solutions below