I've made a dext and a user client that overrides IOUserSCSIPeripheralDeviceType00, with the object of writing device firmware to the driver. I can gain and relinquish exclusive access to the device, I can call UserReportMediumBlockSize and get back a sensible answer (512). I can build command parameters with the INQUIRY macro from IOUserSCSIPeripheralDeviceHelper.h and send that command successfully using UserSendCB, and I receive sensible-looking Inquiry data from the device.
However, what I really want to do is send a WriteBuffer command (opcode 0x3B), and that doesn't work. I have yet to put a bus analyzer on it, but I don't think the command goes out on the bus - there's no valid sense data, and the error returned is 0xe00002bc, or kIOReturnError, which isn't helpful.
This is the code I have which doesn't work.
kern_return_t driver::writeChunk(const char * buf, size_t atOffset, size_t length, bool lastOne)
{
DebugMsg("writeChunk %p at %ld for %ld", buf, atOffset, length);
SCSIType00OutParameters outParameters;
SCSIType00InParameters response;
memset(&outParameters, 0, sizeof(outParameters));
memset(&response, 0, sizeof(response));
SetCommandCDB(&outParameters.fCommandDescriptorBlock,
0x3B, // byte 0, opcode WriteBuffer command
lastOne ? 0x0E : 0x0F, // byte 1 mode: E=save deferred, F = download and defer save
0, // byte 2 bufferID
(atOffset >> 16), // byte 3
(atOffset >> 8), // byte 4
atOffset, // byte 5
(length >> 16), // byte 6
(length >> 8), // byte 7
length, // byte 8
0, // control, byte 9
0, 0, 0, 0, 0, 0); // bytes 10..15
outParameters.fLogicalUnitNumber = 0;
outParameters.fBufferDirection = kIOMemoryDirectionOut;
outParameters.fDataTransferDirection = kSCSIDataTransfer_FromInitiatorToTarget;
outParameters.fTimeoutDuration = 1000; // milliseconds
outParameters.fRequestedByteCountOfTransfer = length;
outParameters.fDataBufferAddr = reinterpret_cast<uint64_t>(buf);
uint8_t senseBuffer[255] = {0};
outParameters.fSenseBufferAddr = reinterpret_cast<uint64_t>(senseBuffer);
outParameters.fSenseLengthRequested = sizeof(senseBuffer);
kern_return_t retVal = UserSendCDB(outParameters, &response);
return retVal;
}