I'm using DriverKit in my iPadOS.
I'm trying to create a IOBufferMemoryDescriptor
and add the data I've got in an OSData
object.
I'm creating the IOBufferMemoryDescriptor
with:
ret = ivars->interface->CreateIOBuffer(kIOMemoryDirectionOut,
length,
&myBuffer);
Although I think it can be created with this too:
IOBufferMemoryDescriptor::Create(kIOMemoryDirectionOut, length, 0, &mybuffer);
How do I add the data from my OSData
object. I've seen examples of:
IOMemoryDescriptor *buffer = IOMemoryDescriptor::withAddress((void*)firmwareData->getBytesNoCopy(), firmwareData->getLength(), kIODirectionOut);
(from How to use deviceRequest of IOUSBHostDevice / IOUSBHostInterface? )
But that method doesn't seem to work in DriverKit.
I've also tried Mapping the Buffer:
uint64_t bufferAddress;
uint64_t bufferLength;
ret = myBuffer->Map(0, 0, 0, 0, &bufferAddress, &bufferLength);
and then copying the data with:
memcpy(reinterpret_cast<void*>(bufferAddress), inputOsData->getBytesNoCopy(), length);
But this would always make the driver to crash when tries to run the memcpy
Thanks
Update: On first reading I missed that you used kIOMemoryDirectionOut
. In my experience, this doesn't seem to work reliably for buffer memory descriptors, probably because write-only mapping isn't available on most CPU architectures. Use kIOMemoryDirectionInOut
.
Main answer:
To obtain a pointer to the IOBufferMemoryDescriptor
's internal buffer, you need to use the GetAddressRange
method. The returned IOAddressSegment
struct's address
field can then be reinterpret_cast
to void*
and filled with memcpy
or similar.
The API is rather clumsy, so I recommend wrapping it into a helper function. I have something like this:
djt_buffer djt_iobmd_get_byte_range(IOBufferMemoryDescriptor* buffer)
{
IOAddressSegment range = {};
kern_return_t ret = buffer->GetAddressRange(&range);
if (ret != kIOReturnSuccess)
return djt_buffer{};
return djt_buffer { reinterpret_cast<void*>(range.address), range.length };
}
where djt_buffer
is defined as:
struct djt_buffer
{
void* bytes;
size_t size;
};
Note that the CreateIOBuffer
method of creating the buffer is preferable when you're going to use the buffer for USB I/O, as it's supposed to prevent the use of bounce buffers. The generic IOBufferMemoryDescriptor::Create()
variant is fine for other purposes.
Note also that every memory descriptor creation, every call to GetAddressRange
or GetLength
, etc. requires a round trip to the kernel, so I recommend re-using/pooling descriptors instead of destroying/recreating them for every IO operation. You can also cache the address pointer and length of each descriptor in your driver.