Search code examples
c++macosdriverkitmacos-system-extension

How to access the IOMemoryBufferDescriptor in CompleteAsyncIO, which is sent through AsyncIO on Interrupt EP


I am trying to send request using AsyncIO for Interrupt EP, for AsyncIO I have created IOMemoryBufferDescriptor, once IOMemoryBufferDescriptor, Create is success I used GetAddressRange and stored Address in ivars structure of dext. For this request completion(CompleteAsyncIO) is called using action-> GetReference() I got ivars structure, I was expecting interrupt completed data received from USB device, unfortunately I am not seeing related data. In wireshark I tried to debug data received is 16 bytes and CompleteAsyncIO actualbytes also 16.

What is the correct way to get interrupt data received from device using IOMemoryBufferDescriptor?

OSAction Create for CompleteAsyncIO

ret = OSAction::Create(this,
                       Data_interruptComplete_ID,
                       IOUSBHostPipe_CompleteAsyncIO_ID,
                       sizeof(IntActRef),
                       &ivars->interruptComplete);

IOMemoryBufferDescriptor Allocation for USB Interrupt EP:

IOBufferMemoryDescriptor*       fCommPipeMDP; 

ivars->fCommPipeMDP->Create(kIOMemoryDirectionIn,
                            ivars->fcomBuffSize,
                            0,
                            &ivars->fCommPipeMDP);

    ivars->fCommPipeMDP->SetLength(ivars->fcomBuffSize); 
    ivars->fCommPipeMDP->GetAddressRange(&aRange);
    ivars->fCommPipeBuffer = (uint8_t*)&aRange.address;

Send AsyncIO Request to Interrupt EP

ret = ivars->fCommPipe->AsyncIO(ivars->fCommPipeMDP,
                                ivars->fcomBuffSize,
                                ivars->interruptComplete,
                                0);

CompleteAsyncIO called by framework

void
IMPL (ClassData,interruptComplete)
{
struct interruptActionRef *actionref = (struct interruptActionRef*)action->GetReference();
Data_IVars * livars = actionref->interruptactionref;

  for(tmp = 0; tmp < actualByteCount; tmp++)
  os_log(OS_LOG_DEFAULT, "%x",livars->fCommPipeBuffer[tmp]); 
  //TRYING PRINT DATA RECEIVED FROM USB DEVICE IN INTERRUPT COMPLETION(CompleteAsyncIO)
  //UNFORTUNATELY DATA IS NOT MATCHING
}

How to get actual data that is received from USB device for interrupt completion using IOBufferMemoryDescriptor which i have sent using AsyncIO ? Do I need to MAP address to current process address space?

which I am seeing wireshark with USB filter only actual data length is matching.

Wireshark Logs a1 20 00 00 01 00 02 00 03 00 00 00 00 00 00 00 (16 Bytes Data) "3029","32.105745","64.16.4","host","USB","40","URB_INTERRUPT in (submitted)" "3030","32.169565","64.16.4","host","USB","56","URB_INTERRUPT in (completed)"

0000   01 01 28 01 10 00 00 00 00 00 00 00 00 00 00 00   ..(.............
0010   31 d8 05 00 00 00 00 00 00 00 40 14 02 10 84 03   1.........@.....
0020   ff 02 01 00 04 10 3e 63 a1 20 00 00 01 00 02 00   ......>c. ......
0030   03 00 00 00 00 00 00 00 

Solution

  • The problem is on this line:

    ivars->fCommPipeBuffer = (uint8_t*)&aRange.address;
    

    This is saving the pointer to the address field of the IOAddressSegment struct variable, not the pointer to the buffer itself. You want:

    ivars->fCommPipeBuffer = (uint8_t*)aRange.address;
    

    or, less error-prone and more idiomatic C++:

    ivars->fCommPipeBuffer = reinterpret_cast<uint8_t*>(aRange.address);
    

    (Though to be fair the type checker would still not have caught the bug; static analysis might have, however.)

    With the correct buffer pointer it should start outputting the correct data.