Reading and writing to USB (HID) interrupt endpoints on Mac

后端 未结 3 401
广开言路
广开言路 2020-12-08 01:43

I am attempting to communicate with a rather specific USB device and developing both Windows and Mac code to do so.

The device is a USB device with a HID interface

3条回答
  •  独厮守ぢ
    2020-12-08 02:06

    After reading this question a few times and thinking about it for a bit, I thought of another solution for emulating blocking read behavior, but using the HID manager instead of replacing it.

    A blocking read function can register an input callback for the device, register the device on the current run loop, and then block by calling CFRunLoopRun(). The input callback can then copy the report into a shared buffer and call CFRunLoopStop(), which causes CFRunLoopRun() to return, thereby unblocking read(). Then, read() can return the report to the caller.

    The first issue I can think of is the case where the device is already scheduled on a run loop. Scheduling and then unscheduling the device in the read function may have adverse affects. But that would only be a problem if the application is trying to use both synchronous and asynchronous calls on the same device.

    The second thing that comes to mind is the case where the calling code already has a run loop running (Cocoa and Qt apps for example). But, the documentation for CFRunLoopStop() seems to indicate that nested calls to CFRunLoopRun() are handled properly. So, it should be ok.

    Here's a bit of simplified code to go with that. I just implemented something similar in my HID Library and it seems to work, although I haven't tested it extensively.

    /* An IN report callback that stops its run loop when called. 
       This is purely for emulating blocking behavior in the read() method */
    static void input_oneshot(void*           context,
                              IOReturn        result,
                              void*           deviceRef,
                              IOHIDReportType type,
                              uint32_t        reportID,
                              uint8_t*        report,
                              CFIndex         length)
    {
        buffer_type *const buffer = static_cast(context);
    
        /* If the report is valid, copy it into the caller's buffer
             The Report ID is prepended to the buffer so the caller can identify
             the report */
        if( buffer )
        {
            buffer->clear();    // Return an empty buffer on error
            if( !result && report && deviceRef )
            {
                buffer->reserve(length+1);
                buffer->push_back(reportID);
                buffer->insert(buffer->end(), report, report+length);
            }
        }
    
        CFRunLoopStop(CFRunLoopGetCurrent());
    }
    
    // Block while waiting for an IN interrupt report
    bool read(buffer_type& buffer)
    {
        uint8_t _bufferInput[_lengthInputBuffer];
    
        // Register a callback
        IOHIDDeviceRegisterInputReportCallback(deviceRef, _bufferInput, _lengthInputBuffer, input_oneshot, &buffer);
    
        // Schedule the device on the current run loop
        IOHIDDeviceScheduleWithRunLoop(deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    
        // Trap in the run loop until a report is received
        CFRunLoopRun();
    
        // The run loop has returned, so unschedule the device
        IOHIDDeviceUnscheduleFromRunLoop(deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    
        if( buffer.size() )
            return true;
        return false;
    }
    

提交回复
热议问题