Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS support #17

Open
mcuee opened this issue Jun 21, 2021 · 71 comments
Open

macOS support #17

mcuee opened this issue Jun 21, 2021 · 71 comments

Comments

@mcuee
Copy link

mcuee commented Jun 21, 2021

It would be nice to have macOS support. macOS has native HID API which should do the work.

@cvuchener
Copy link
Owner

It is difficult to write applications for macOS when you don't have a mac. I won't do it myself. But If you or someone else wants to try, I can answer questions.

Most of the work should be writing the implementation of the HID::DeviceMonitor and HID::RawDevice classes.

@mcuee
Copy link
Author

mcuee commented Jun 21, 2021

Fair enough. Unfortunately I am not a programmer myself and my role in the open source projects I am involved (libusb, libusb-win32, libusbk, pyusb, etc) is mainly testing and support side. Hopefully someone can jump in in the future.

@noah-nuebling
Copy link

noah-nuebling commented Aug 7, 2021

Hi there, I’m interested in adding full support for Logitech mice to https://github.com/noah-nuebling/mac-mouse-fix

Of course I’d like to use a library for that if possible so it’s not as much work.

@cvuchener Do you recommend I spend the time to try and port this library to macOS for this purpose?

I read that you recommend people use ratbag instead of hidpp to build apps (source: #1). But it seems ratbag depends on lots of Linux-specific APIs and would be really hard to port (source). Whereas your library already works on Windows and Linux - which makes me think it’s much easier to port to macOS as well.

The most crucial functionalities which I hope to use through this library are

  • Making tilting scroll wheels act like normal buttons
  • Making “Gesture buttons” act like normal buttons

But other functionality such as

  • Reading battery level
  • Changing the free-spin threshold
  • Reading the touch sensor on the MX Master horizontal scroll wheel

would be really interesting, too

Thanks a lot for your help, and kudos for making this!

@cvuchener
Copy link
Owner

Do you recommend I spend the time to try and port this library to macOS for this purpose?

If you are familiar with macOS HID API and system programming, it should not be too hard. For DeviceMonitor you don't have to support hot-plugging from the beginning (it is not implemented on Windows), but if you want to write drivers, it may be useful to have it.

About writing new tools, everything that only configure the device should be portable. But if you have to generate input events, it will likely be macOS specific. I have a linux-specific driver example (using uinput): hidpp20-raw-touchpad-driver. The way thread are managed are a bit annoying, I should improve that.

  • Making tilting scroll wheels act like normal buttons

Depending on the exact feature used by your mouse, this may be already supported.

  • Making “Gesture buttons” act like normal buttons

I don't have any code for gestures, but I have a few notes on "0x6501-Gesture2". This will need more research.

  • Reading battery level

I think it's done by the kernel driver on Linux. I did not look at it, but it is documented as part of the Unifying Receiver registers (https://docs.google.com/document/d/0BxbRzx7vEV7eNDBheWY0UHM5dEU/edit?resourcekey=0-SPDGsNiO52FX6E-mJIXYXQ)

  • Changing the free-spin threshold
  • Reading the touch sensor on the MX Master horizontal scroll wheel

I don't know anything about that, this will need research too.

@noah-nuebling
Copy link

noah-nuebling commented Aug 7, 2021

Hey @cvuchener thanks for the quick reply.

If you are familiar with macOS HID API and system programming [...]

I don't have any experience with system programming. So that's surely going to trip me up. I'm familiar with the macOS HID API, though. If I keep close to the Windows implementation I hope I won't make too many mistakes.


For DeviceMonitor you don't have to support hot-plugging from the beginning (it is not implemented on Windows), but if you want to write drivers, it may be useful to have it.

Hot-plugging definitely sounds very useful for my use case. I'll try to implement it if I work on this.


[...] everything that only configure the device should be portable. But if you have to generate input events, it will likely be macOS specific.

I luckily already have macOS code for intercepting and creating input events. So this shouldn't be an issue for my case.

However, if you think an update to the command-line tools for macOS is warranted, I should be able to help as I do have experience writing macOS code around input events.


Thanks for your assessment on the different features I'd like.

I also had a look a the links in your README, and they look very promising!

I found what seem to be interfaces for all the features I'm looking for in the Logitech GDrive you've linked (I think so at least, I don't really understand any of this).


Do you think that these interfaces would serve my needs? And do you think your API would make using them easier?

@noah-nuebling
Copy link

noah-nuebling commented Aug 8, 2021

Hey @cvuchener, I wrote up some stuff in this fork.

I adjusted some of the CMake files and added new classes DeviceMonitor_macos, RawDevice_macos, and Utility_macos.

It compiles fine under macOS and functionality-wise it's mostly done. But it doesn't work yet. (And it's probably bad code - again I have very little experience with Cpp or systems programming)


I had some questions and concerns while writing the code. You can read them in these commit messages:


Right now I'm stuck. I'll try to work on this more some other time, but I thought I'd describe the problem in case you know more:

I tried to use the hidpp-list-devices command-line-tool, but it just runs forever and doesn't do anything, even when I attach my Logitech M720.
I used the lldb debugger via VSCode to step through the program. Here's where it goes wrong:

  • The program tries to construct a SimpleDispatcher for the first device it finds. In my case it was the backlight of the built-in keyboard on my laptop.
  • The constructor of SimpleDispatcher throws an error because the keyboard backlight doesn't follow Logitech's HID++ protocol
  • The SimpleDispatcher constructor returns due to the error and the debugger is now focused on the line HIDPP::SimpleDispatcher dispatcher (path); in the file hidpp-list-devices.cpp
  • After that absolutely nothing happens. The debugger just freezes. I can’t step through the code any further.

@cvuchener
Copy link
Owner

I had some questions and concerns while writing the code. You can read them in these commit messages:

Commit messages are not a place for discussion. It would be more practical to ask your questions here or in a pull request.

What exactly are the paths that are used in the path-based constructor? Do they have a specific platform-independent format? I used IORegistry paths in my implementation. (See https://developer.apple.com/documentation/iokit/1514802-ioregistryentryfrompath?language=objc). I'm fairly certain that those have an Apple-specific format.

It's platform-specific, use whatever you prefer. You will have to write those strings on the command line.

What exactly do the RawDevice::RawDevice(const RawDevice &other) and RawDevice::RawDevice(const RawDevice &&other) constructors do? What's the difference between them?

Copy constructor and move constructor. The copy constructor makes a deep copy: the new object should open the same device but do not share any resource with the other object. The move constructor steals the resources from the other object, the other object does need to be in a valid state any more, but the destructor should still work on it. They are important concept of C++ you should learn about them.

In IOKit you need to specify an IOHIDReportType when sending or requesting reports from a device. Do you know which of these to use for reading and writing?

Feature reports are not used, you only need to read input reports and write output reports.

What is the expected output when readReport() times out?

Return 0 whenever a read is timed out or interrupted.

I'm wondering what the addDevice() and removeDevice() functions do, and whether I'm calling them at the right times.

They are simple callbacks. The DeviceMonitor user will define them. The expected usage is to try to create a new RawDevice when addDevice is called. removeDevice tells you the device no longer exists and can be forgotten. You don't need a matching removeDevice for each addDevice. The only use for DeviceMonitor right now is hidpp-list-devices and it only uses enumerate and don't care about removeDevice.

After that absolutely nothing happens. The debugger just freezes.

I hope the debuggee freezes, not the debugger. You should be able to interrupt the debuggee and see where it is stuck. I expects it is in RawDevice::readReport. Your issue may be that your not running the run loop, someone needs to read the reports and the condition variable won't do it. I don't know how to best implement interruptRead with run loops if there is exactly one per thread, it may be difficult.

@cvuchener
Copy link
Owner

Just so you don't get stuck, know that you don't need to implement interruptRead for the basic command line tools. You can leave it empty for now (or better with an assert or error logging) and concentrate on your other issues.

@mcuee
Copy link
Author

mcuee commented Aug 8, 2021

For macOS specific codes, maybe HIDAPI can be of some reference.
https://github.com/libusb/hidapi/blob/master/mac/hid.c

@cvuchener
Copy link
Owner

The way to implement interruptRead may be to use a CFRunLoopSource. You would create one CFRunLoopSource for each RawDevice with the same life-time. In readReport you add both the source and the HID device to the current thread run loop, wait for something to happen, remove your source and HID device from the run loop so someone else can re-use it and exits. interruptRead would simply signal the source for stopping the readReport run loop.

You need to make sure that the run loop still detects the source event even if it was signaled before readReport entered the loop. The documentation says it can be used for signaling across threads, so it should be possible. But there is also two kind of source, I don't know which one is the best.

@cvuchener
Copy link
Owner

The first kind of source (version 0) are not able to wake up the run loop thread, and CFRunLoopWakeUp must be called after signaling the source. This means that interruptRead must know which run loop readReport is using (if any). You would need some kind of synchronisation between the two functions. HIDAPI uses this kind of source, but it also creates a dedicated thread for reading the device reports.

The second kind (version 1) uses mach ports. If I understand correctly mach ports can work similarly to POSIX pipes. Being a kernel mechanism, it will be able to wake up the run loop thread, in the same way the HID reports do. This is closer to what I'm doing on Linux or Windows. You don't have to care which thread will receive the message and it should not require any synchronization. They may be a bit more complex to set up though.

@noah-nuebling
Copy link

noah-nuebling commented Aug 9, 2021

Hey guys thanks for all the feedback and info!

I read up some more on runLoops, and looked at the hidapi source. Both of which have been very insightful.
Not calling CFRunLoopRun() is probably the reason for the freezing I saw as @cvuchener suggested.


Regarding interruptRead()

Regarding @cvuchener 's ideas as to how to implement interruptRead - the current architecture of readReport() should already allow for interrupting!

Let me explain the current architecture and the logic behind it:

AFAIK, readReport() is supposed to block until one of these happens:

  • There is a report from the device
  • The function times out
  • interruptRead is called

But if we create a runLoop on a thread and then we block that thread, the runLoop will be blocked as well and won't do anything.
So if we create the runLoop for receiving reports on the thread that called readReport(), and then block that thread, we'll never receive any input because the runLoop will be blocked, too!

To circumvent this, I created a separate thread just to run the runLoop on. hidapi also creates a separate thread for the runLoop.

So if the current architecture ends up working, then the mechanisms for unblocking readReport() once an input report is received and once interruptRead() is called should be almost exactly the same. Therefore interruptRead() should work without much extra effort.

You can look at the current readReport() implementation here.


Regarding current issues with the code

The code doesn't freeze anymore now, but there are some other issues.

  • readReport() seems to always time out and never read anything. Edit: That should be fixed. There was a wrong parameter in the registerInputReportCallback function.
  • The code always crashes when trying to destruct some Log object (when it's calling ~Log()).

The weird thing is that the way it fails looks slightly different if I stop at the breakpoints and step through vs if I just click the Continue button whenever I hit a breakpoint.
This is probably just a quirk of the debugger but I documented both cases below anyways.

Here are some screenshots of what it looks like when I step through after I hit a breakpoint:

Screen Shot 2021-08-09 at 11 30 45

Screen Shot 2021-08-09 at 11 43 29

Screen Shot 2021-08-09 at 11 45 29

Here's a screenshot of what it looks like when I click continue whenever I hit a breakpoint:

Screen Shot 2021-08-09 at 15 40 25


Edit: After incorporating the latest changes (See commit 544d201) into my fork I now get this error.

The error:
Screen Shot 2021-08-09 at 19 09 06

The last code in the stack trace that comes from hidpp:
Screen Shot 2021-08-09 at 19 09 12

The program doesn't seem to reach readReport() before crashing, so I can't test it right now. (At least not through hidpp-list-devices - I haven't tried anything else)

@noah-nuebling
Copy link

noah-nuebling commented Aug 9, 2021

HIDAPI uses this kind of source, but it also creates a dedicated thread for reading the device reports.

Yes I was wondering why hidapi uses a custom runLoopSource to make the runLoop exit. From my understanding, just calling CFRunLoopStop() or even just removing all sources from the runLoop should cause it to exit, too - and it's much simpler!

@cvuchener
Copy link
Owner

To circumvent this, I created a separate thread just to run the runLoop on. hidapi also creates a separate thread for the runLoop.

I would prefer to not create any thread at this level. And I don't think you need to. Please consider the mach ports way.

You can look at the current readReport() implementation here.

A lot is wrong with this:

In general, if you have to use multiple threads avoid as much as possible sharing mutable data and prefer message passing instead.

@noah-nuebling
Copy link

noah-nuebling commented Aug 9, 2021

Hey, thanks for your feedback.


Please consider the mach ports way

I'm open to using the approach that you prefer. I'm definitely aware how easily multiple threads can introduce issues that are hard to fix, and I'm fully on board with avoiding multiple threads if possible.

However, I don't understand how the implementation would be possible without threads, just using mach ports.

To recap and hopefully clarify what I wrote in this reply, the main reason I started using threads, is because of this logic:

Preconditions:

  • Input reports are delivered on a runLoop called R
  • readReport() is running on a thread called T
  • T must block - that's because it must wait for an input report, before it can return that input report.
  • T will only unblock once R has delivered an input report (or once it has timed out etc, but we'll ignore that for the sake of argument)
  • If any thread T' is blocked, and there's a runLoop R' that runs on thread T', then the runLoop R' can't do anything because it will be blocked as well.

Inference:

  • Therefore it holds: If we assign the runLoop R to thread T, then T and R will be in a deadlock. Because T will wait for R to deliver and event before T can unblock, and R can't deliver an event because it's blocked as long as T is blocked.
  • To prevent this deadlock, we need to assign the runLoop R to a different thread than T.

If you think my argument is flawed, or that there's also a way to prevent this deadlock using mach ports, could you elaborate? Thank you.

I hope this is understandable, and if you have more questions let me know.

Edit: I thought about it again, and I think I see what you mean now. I now understand how an approach that doesn't spawn an extra thread is possible and I definitely think it's preferable to the current approach.

  • This Is what I have in mind: you could run the report-receiving runLoop on the readReport() thread, and use the runLoop itself to block readReport() from returning. You could use CFRunLoopRunInMode() to specify a timeout, and use CFRunLoopStop() when a report has been read or to interrupt reading. Do you think that makes sense?

I guess I didn’t come up with this before because I thought a runLoop was some magical background thing that doesn’t block when it’s invoked and automatically does its work whenever the thread is finished with everything else. But it actually seems to be just a plain while(true){} loop under the hood that will block all statements after CFRunLoopRun() until the loop exits.


There are race conditions on waitingForInputReport and inputReportRunLoop

Could you elaborate on this as well?
I only see race conditions on inputReportRunLoop.
The race condition I see would only occur when a thread enters readReport() while another thread is still running readReport() / while runLoopThread is still running. Does that match your observations?

Edit: I added a mutex that, along with the new thread.join() call, should avoid any race conditions I could make out.


Your thread is not stopped nor joined

I was under the assumption that the thread would be stopped automatically because the following sequence of events will happen

  • We unschedule the device from the runLoop in readReport() → the runLoop has no more input sources → the runLoop exits → the runLoopThread is unblocked → the runLoopThread's lambda function finishes execution → the thread stops

But I haven't been able to test this, yet, and I agree that it's pretty opaque. Also, I see that this can lead to crashes when the thread is destroyed while it's still running.

How would you suggest we stop / join the thread?

Edit: I added a .join() call and explicit CFRunLoopStop() calls


Regarding your other feedback - I mostly agree, and I'll improve the details once everything is functional.


Oh and do you have any idea how to fix the errors that I documented in the screenshots above? That would make further testing and debugging much easier. At this point the list-devices tool crashes for me before it even gets to readReport().

@noah-nuebling
Copy link

Hey guys, I won't have time to work on this in a while because I'm about to move to another country, but if you have questions about the code I wrote I'll try to find the time to answer.

@cvuchener
Copy link
Owner

AFAIK, readReport() is supposed to block until one of these happens:

* There is a report from the device
* The function times out
* `interruptRead` is called

readReport should also not block if interruptRead was called before readReport. A simplified use case to explain this:
You have a thread waiting for events, it runs a loop like this:

while (0 != device.readReport(report))
    process(report);

At some point you want to stop reading reports because the device has been removed or you want to quit the application. From another thread, you call device.interruptRead(); so the thread exits it loop. You have no idea what the reading thread is doing at this time: it can be at any point in readReport or even outside (testing the while loop or executing process). The contract is that after calling interruptRead, the current or the next call to readReport will not block. So when that other threads calls interruptRead, the reading thread will either exits the loop immediately (if it was blocking in readReport) or process one last report before calling readReport again which will exits immediately.

This is the only thread safety constraint that RawDevice has. readReport does not need to be thread-safe itself, it should not be called on the same device from different threads at the same time.

To prevent this deadlock, we need to assign the runLoop R to a different thread than T.

Your new thread and its loop will still have to wait for two different events: the HID report and whatever mechanism cancel it. You could have used the same mechanism in the first thread.

* This Is what I have in mind: you could run the report-receiving runLoop on the `readReport()` thread, and use the runLoop **itself** to block `readReport()` from returning. You could use `CFRunLoopRunInMode()` to specify a timeout, and use `CFRunLoopStop()` when a report has been read or to interrupt reading. Do you think that makes sense?

Yes.

There are race conditions on waitingForInputReport and inputReportRunLoop

Could you elaborate on this as well?
I only see race conditions on inputReportRunLoop.
The race condition I see would only occur when a thread enters readReport() while another thread is still running readReport() / while runLoopThread is still running. Does that match your observations?

Edit: I added a mutex that, along with the new thread.join() call, should avoid any race conditions I could make out.

Actually, I think I was wrong about inputReportRunLoop because I did not read the busy waiting correctly at first. But if you need to wait for a thread to initialize some value a promise/future pair works well (here is an example).

As I said above you don't need to protect readReport from concurrent calls. The issue is with a call to interruptRead before _p->waitingForInputReport = true; because it overwrites the value set by interruptRead.

* We unschedule the device from the runLoop in `readReport()` → the runLoop has no more input sources → the runLoop exits

I did not understand that, the thread is stopped then if you are right. The call to join is mandatory still.

Edit: After incorporating the latest changes (See commit 544d201) into my fork I now get this error.

There has been changes with the report descriptor: instead of storing a raw report descriptor, a parsed report descriptor is stored. It is worrying that you did not get build error from that change. If macOS gives you the raw report descriptor, you need to parse it with ReportDescriptor::fromRawData like it is done on Linux.

try {
_report_desc = ReportDescriptor::fromRawData (rdesc.value, rdesc.size);
logReportDescriptor ();
}
catch (std::exception &e) {
Log::error () << "Invalid report descriptor: " << e.what () << std::endl;
}

Oh and do you have any idea how to fix the errors that I documented in the screenshots above? That would make further testing and debugging much easier.

Threads are likely the cause of the inconsistent behaviors. Start with something simple, make sure it works, then build upon it.
The minimal implementation would only have RawDevice without interruptRead. If you know the paths for the HID devices you can test hidpp-list-features (or any other tool that takes a device path as an argument) without the need for DeviceMonitor. If you implement DeviceMonitor::enumerate you can test hidpp-list-devices. No tool in this repository (that can be built on macOS) actually uses RawDevice::interruptRead or DeviceMonitor::run. You could also write your own simple tests, like open one HID device, read one report, then exits. You don't even need a HID++ device for testing that.

(untested code as example)

#include <hid/RawDevice.h>

int main (int argc, char *argv[])
{
	HID::RawDevice device (argv[1]);
	std::vector<uint8_t> report (256);
	int res = device.readReport (report, 10000);
	std::cerr << "readReport exited with result " << res << std::endl;
	return 0;
}

@noah-nuebling
Copy link

noah-nuebling commented Aug 11, 2021

Thanks for your clear and informative reply. This should greatly help development.

Regarding the crash on the line Log::warning () << "Missing input report for report " << type << std::endl; (Which I posted a screenshot of):

The latest version of the macOS port (under which I encountered this is error) was already using ReportDescriptor::fromRawData(). Although without the logging or error handling of the Linux version.
(ReportDescriptor::fromRawData() is used in IOHIDDeviceGetReportDescriptor() if you want to have a look)

Since the error occurred when trying to log something in a part of the codebase which I didn't write, I thought the crash might be unrelated to my code. But given what you told me I now assume it's due to my IOHIDDeviceGetReportDescriptor() function doing something wrong. I'll investigate that more when I have time to work on this more.

But even if my IOHIDDeviceGetReportDescriptor() is doing something wrong, shouldn't the error occur much earlier and not just cause a crash when trying to log something in a completely different part of the codebase?

The same goes for the error where the Log::~Log() function crashed (which I also posted screenshots of). The error went away after I got my readReport() function to not always time out anymore, but still - the error shouldn't occur even when readReport() does time out, because while that would probably be rare, it's still very possible, right?

Other than this I think we're on the same page now.

@noah-nuebling
Copy link

noah-nuebling commented Aug 11, 2021

I just thought of one more thing:

The contract is that after calling interruptRead, the current or the next call to readReport will not block

Couldn't that be problematic if you have a scenario like this:

for _ in 1...5:
    report = device.readReport(report)
    process(report)

device.interruptRead()

# ...
# (Much later somewhere else in the program)
# ...

report = device.readReport(report) # Problem: Will be interrupted immediately due to the previous device.interruptRead() call
process(report)

A more elegant solution to this might be to use a function RawDevice::readReportsWithCallback() or something similar for reapeated reading. If you need it to behave like the while (0 != device.readReport(report)) loop from above you could have a blocking variant called RawDevice::readReportsWithCallbackAndBlock() or something.

On macOS at least, this would also be more efficient than calling readReport() repeatedly, and it should also be pretty easy to implement.

@cvuchener
Copy link
Owner

Will be interrupted immediately due to the previous device.interruptRead() call

This is the expected behavior, not a problem.

A more elegant solution to this might be to use a function RawDevice::readReportsWithCallback() or something similar for reapeated reading. If you need it to behave like the while (0 != device.readReport(report)) loop from above you could have a blocking variant called RawDevice::readReportsWithCallbackAndBlock() or something.

This is what is implemented in the dispatcher classes. You will have callbacks for HID++ events.

On macOS at least, this would also be more efficient than calling readReport() repeatedly, and it should also be pretty easy to implement.

It would only be easy if you were writing code for macOS only. The code outside of RawDevice cannot know there a CFRunLoop running and take advantage of it. And since the run loop belong to the thread and not the RawDevice, you will have to set up and clean up the loop in each readReport call. If this becomes too big of a performance issue, I may have an idea to fix it later, I'll have to think about it.

@noah-nuebling
Copy link

noah-nuebling commented Aug 11, 2021

Thanks for the quick reply and information

If that's expected behaviour then I see nothing wrong with the current approach. On macOS, using a readReportsWithCallback() function would surely be somewhat faster than calling readReport() repeatedly, and I didn’t quite understand the technical reasons you named as to why it would be hard to implement, but if there are no performance issues with the current approach then there's no need to optimize prematurely.

It's also good to know that there's a callback for HID++ events implemented, that should come in handy.

@noah-nuebling
Copy link

noah-nuebling commented Aug 12, 2021

I assume DeviceMonitor::run() is supposed to block, too right?

@noah-nuebling
Copy link

noah-nuebling commented Aug 12, 2021

Something really weird is going on.

I removed the threading now, and added safety/logging code around retrieving the reportDescriptor (copied it from the Linux version).

Even though the report descriptor looks fine and there's only one thread operating, I still get the weird ~Log() crash I posted screenshots of above. This time it occurs after retrieving the reportDescriptor, and then calling RawDevice::logReportDescriptor(). Once logReportDescriptor() exits and the Log type object that it created is destroyed there's a crash.

Screen Shot 2021-08-12 at 02 39 15

The exact same error occurs using hidpp-check-device

@noah-nuebling
Copy link

All the crashes I'm experiencing seem to have to have to do with the Log class.

Do you think there might be something about it that doesn't port well to macOS?

@noah-nuebling
Copy link

noah-nuebling commented Aug 12, 2021

After replacing Log::info () << ... with std::cout << ... hidpp-check-device runs fine.

So the Log class really seems to be the culprit. I think whenever hidpp tries to use it to print anything or when it tries to destruct a Log instance the program crashes. I've never seen it print anything successfully. (At least nothing ever showed up in the vscode console)

@cvuchener
Copy link
Owner

Are you running with logging enabled? If not add -vinfo,debug to the command line. Does it make a difference?

Try adding this line when running without logging enabled.

diff --git a/src/libhidpp/misc/Log.cpp b/src/libhidpp/misc/Log.cpp
index 7974254..30a63e1 100644
--- a/src/libhidpp/misc/Log.cpp
+++ b/src/libhidpp/misc/Log.cpp
@@ -64,6 +64,7 @@ Log::Category Log::Debug ("debug");
 std::mutex Log::_mutex;
 
 Log::Log ():
+       std::ostream (nullptr),
        _buf ("null")
 {
 }

Otherwise try running with valgrind (if it is available for your platform) or any address sanitizer.

@noah-nuebling
Copy link

noah-nuebling commented Aug 12, 2021

Adding that line fixed it!

Do you understand what went wrong?

@noah-nuebling
Copy link

But there are more problems:

When I attach my M720 via USB (Unifying receiver) it shows up in hidpp-list-devices, but hidpp-list-features crashes.

Screen Shot 2021-08-12 at 13 17 08

When I attach it via Bluetood, HIDPP doesn't seem to recognize it at all.

@noah-nuebling
Copy link

Here's the verbose output:

Screen Shot 2021-08-12 at 13 21 45

Let me know what I can to help solve this.

@cvuchener
Copy link
Owner

Here is the backtraces with debug build.

This looks like there is no vendor id defined on one of your devices and the code doesn’t expect that and crashes. @cvuchener how would you recommend we deal with this? Should we just ignore that device or set its vendor id to some default value? Should I implement safety checks on all device properties that are retrieved, like product name etc, or are there certain properties that are guaranteed to exist?

I've never heard of such device. If it really exists you can set the ID to an obvious dummy value (0? 0xFFFF?) and maybe log a warning. If it is a system error, throw an exception. All properties must be valid, but they could be empty. IDs are simply integers, any value is valid, they may be used to find the device in a database (HID++ 1.0 requires some quirks, not everything can be guessed). Names are used for display. Report descriptors are used for checking which reports are present (an empty descriptor would be valid but would not be accepted as a HID++ device).

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

This is what I get under macOS using HIDAPI. Somehow the Logitech bluetooth mouse M557 got an "unknown" entry for "Manufacturer". But the following device is causing the segfault mentioned in the above backtrace.

Device Found
  type: 0000 0000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/aop@4A400000/AppleASCWrapV4/iop-aop-nub/RTBuddyV2/AOPEndpoint6/AppleSPU@10000001/wakehint/AppleSPUHIDDevice
  serial_number: 
  Manufacturer: Apple
  Product:      
  Release:      0
  Interface:    -1
  Usage (page): 0xff (0xff00)
hidapi/buildarm64 on  master [?] via △ v3.21.1 ❯ ./hidtest/hidtest 
hidapi test/example tool. Compiled with hidapi version 0.11.0, runtime version 0.11.0.
Compile-time version matches runtime version of hidapi.

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    2
  Usage (page): 0x1 (0xff00)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    2
  Usage (page): 0x2 (0xff00)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    2
  Usage (page): 0x4 (0xff00)

Device Found
  type: 0000 0000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM
  serial_number: 
  Manufacturer: APPL
  Product:      BTM
  Release:      0
  Interface:    -1
  Usage (page): 0x48 (0xff00)

Device Found
  type: 0000 0000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/i2c2@35018000/AppleS5L8940XI2CController/audio-codec-output@48/AppleCS42L83Audio/AppleCS42L83Mikey
  serial_number: 
  Manufacturer: Apple
  Product:      Headset
  Release:      0
  Interface:    -1
  Usage (page): 0x1 (0xc)

Device Found
  type: 047f c025
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212600/Plantronics C320-M@02212600/IOUSBHostInterface@3/AppleUserUSBHostHIDDevice
  serial_number: CB13A3E40E8E47D6A40769C27E90A38E
  Manufacturer: Plantronics
  Product:      Plantronics C320-M
  Release:      135
  Interface:    3
  Usage (page): 0x1 (0xc)

Device Found
  type: 047f c025
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212600/Plantronics C320-M@02212600/IOUSBHostInterface@3/AppleUserUSBHostHIDDevice
  serial_number: CB13A3E40E8E47D6A40769C27E90A38E
  Manufacturer: Plantronics
  Product:      Plantronics C320-M
  Release:      135
  Interface:    3
  Usage (page): 0x5 (0xb)

Device Found
  type: 047f c025
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212600/Plantronics C320-M@02212600/IOUSBHostInterface@3/AppleUserUSBHostHIDDevice
  serial_number: CB13A3E40E8E47D6A40769C27E90A38E
  Manufacturer: Plantronics
  Product:      Plantronics C320-M
  Release:      135
  Interface:    3
  Usage (page): 0x1 (0xffa0)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@0/AppleUserUSBHostHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    0
  Usage (page): 0x6 (0x1)

Device Found
  type: 046d b010
  path: IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver
  serial_number: 34-88-5d-ab-0c-12
  Manufacturer: Unknown
  Product:      Bluetooth Mouse M557
  Release:      1002
  Interface:    -1
  Usage (page): 0x2 (0x1)

Device Found
  type: 046d b010
  path: IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver
  serial_number: 34-88-5d-ab-0c-12
  Manufacturer: Unknown
  Product:      Bluetooth Mouse M557
  Release:      1002
  Interface:    -1
  Usage (page): 0x1 (0x1)

Device Found
  type: 046d b010
  path: IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver
  serial_number: 34-88-5d-ab-0c-12
  Manufacturer: Unknown
  Product:      Bluetooth Mouse M557
  Release:      1002
  Interface:    -1
  Usage (page): 0x1 (0xc)

Device Found
  type: 046d b010
  path: IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver
  serial_number: 34-88-5d-ab-0c-12
  Manufacturer: Unknown
  Product:      Bluetooth Mouse M557
  Release:      1002
  Interface:    -1
  Usage (page): 0x1 (0xff00)

Device Found
  type: 046d b010
  path: IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver
  serial_number: 34-88-5d-ab-0c-12
  Manufacturer: Unknown
  Product:      Bluetooth Mouse M557
  Release:      1002
  Interface:    -1
  Usage (page): 0x2 (0xff00)

Device Found
  type: 046d b010
  path: IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver
  serial_number: 34-88-5d-ab-0c-12
  Manufacturer: Unknown
  Product:      Bluetooth Mouse M557
  Release:      1002
  Interface:    -1
  Usage (page): 0x6 (0x1)

Device Found
  type: 046d 1000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice/LogiVirtualHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      LogiVirtualHIDDevice
  Release:      1
  Interface:    2
  Usage (page): 0x2 (0x1)

Device Found
  type: 046d 1000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice/LogiVirtualHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      LogiVirtualHIDDevice
  Release:      1
  Interface:    2
  Usage (page): 0x1 (0x1)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@1/AppleUserUSBHostHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    1
  Usage (page): 0x2 (0x1)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@1/AppleUserUSBHostHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    1
  Usage (page): 0x1 (0x1)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@1/AppleUserUSBHostHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    1
  Usage (page): 0x1 (0xc)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@1/AppleUserUSBHostHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    1
  Usage (page): 0x80 (0x1)

Device Found
  type: 046d c52b
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@1/AppleUserUSBHostHIDDevice
  serial_number: 
  Manufacturer: Logitech
  Product:      USB Receiver
  Release:      2410
  Interface:    1
  Usage (page): 0x88 (0xffbc)

Device Found
  type: 0000 0000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/aop@4A400000/AppleASCWrapV4/iop-aop-nub/RTBuddyV2/AOPEndpoint6/AppleSPU@10000001/wakehint/AppleSPUHIDDevice
  serial_number: 
  Manufacturer: Apple
  Product:      
  Release:      0
  Interface:    -1
  Usage (page): 0xff (0xff00)

unable to open device

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

But there is anothe Apple device which can cause the segfaults as well. HIDAPI hidtest is having the following output for this device. This is a bit strange.

Device Found
  type: 0000 0000
  path: IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM
  serial_number: 
  Manufacturer: APPL
  Product:      BTM
  Release:      0
  Interface:    -1
  Usage (page): 0x48 (0xff00)

hidpp_mac on  master [!?] via △ v3.21.1 ❯ lldb ./build/src/tools/hidpp-list-devices 
(lldb) target create "./build/src/tools/hidpp-list-devices"
Current executable set to '/Users/mcuee/build/hidapi/hidpp_mac/build/src/tools/hidpp-list-devices' (arm64).
(lldb) run
Process 1999 launched: '/Users/mcuee/build/hidapi/hidpp_mac/build/src/tools/hidpp-list-devices' (arm64)
IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice:  (046d:c52b) HID++ 1.0
2021-08-15 08:10:36.281811+0800 hidpp-list-devices[1999:23806] [default] 0x10000098a: TCC deny IOHIDDeviceOpen
2021-08-15 08:10:36.282298+0800 hidpp-list-devices[1999:23806] [default] 0x10000098d: TCC deny IOHIDDeviceOpen
Process 1999 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x0000000192b9950c CoreFoundation`CFNumberGetType + 88
CoreFoundation`CFNumberGetType:
->  0x192b9950c <+88>:  ldr    x9, [x0]
    0x192b99510 <+92>:  adrp   x10, 389000
    0x192b99514 <+96>:  ldr    x10, [x10, #0x38]
    0x192b99518 <+100>: bic    x8, x9, x10
Target 0: (hidpp-list-devices) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x0000000192b9950c CoreFoundation`CFNumberGetType + 88
    frame #1: 0x000000010027fccc libhidpp.dylib`Utility_macos::CFNumberToInt(cfNumber=0x0000000000000000) at Utility_macos.cpp:40:31
    frame #2: 0x00000001002800d0 libhidpp.dylib`Utility_macos::IOHIDDeviceGetIntProperty(device=0x0000000101106ec0, key="VendorID") at Utility_macos.cpp:79:12
    frame #3: 0x000000010018b998 libhidpp.dylib`HID::RawDevice::RawDevice(this=0x000000016fdff190, path="IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM") at RawDevice_macos.cpp:112:18
    frame #4: 0x000000010018bdd4 libhidpp.dylib`HID::RawDevice::RawDevice(this=0x000000016fdff190, path="IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM") at RawDevice_macos.cpp:69:85
    frame #5: 0x00000001001b5738 libhidpp.dylib`HIDPP::SimpleDispatcher::SimpleDispatcher(this=0x000000016fdff168, path="IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM") at SimpleDispatcher.cpp:30:2
    frame #6: 0x00000001001b587c libhidpp.dylib`HIDPP::SimpleDispatcher::SimpleDispatcher(this=0x000000016fdff168, path="IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM") at SimpleDispatcher.cpp:31:1
    frame #7: 0x00000001000032a4 hidpp-list-devices`DevicePrinter::addDevice(this=0x000000016fdff348, path="IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM") at hidpp-list-devices.cpp:39:28
    frame #8: 0x00000001001959bc libhidpp.dylib`HID::DeviceMonitor::enumerate(this=0x000000016fdff348) at DeviceMonitor_macos.cpp:122:3
    frame #9: 0x0000000100002e40 hidpp-list-devices`main(argc=1, argv=0x000000016fdff518) at hidpp-list-devices.cpp:112:21
    frame #10: 0x0000000192af5430 libdyld.dylib`start + 4
(lldb) 

hidpp_mac on  master [!?] via △ v3.21.1 ❯ ./build/src/tools/hidpp-list-devices -v:debug
Invalid log category tag: 
IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver: ?&Qo (046d:b010) HID++ 2.0
IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver (device 0): ?&Qo (046d:b010) HID++ 2.0
[warning] Missing input report for report 
[warning] Missing output report for report 
IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice:  (046d:c52b) HID++ 1.0
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 1) timed out
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 2) timed out
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 3) timed out
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 4) timed out
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 5) timed out
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 6) timed out
zsh: segmentation fault  ./build/src/tools/hidpp-list-devices -v:debug```

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

Normally macOS should prompt you to enable the necessary permissions when a program tries to do something it needs these permissions for for the first time. I‘m pretty sure macOS prompted me when I tried to run hidpp for the first time.

Yes macOS prompt me for the "Input Monitoring" permissions for hidpp-list-devices, but no prompt for hidpp-list-features. Let me try to add the pemissions for hidpp-list-features to see how it goes.

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

@noah-nuebling Looks like the following is similar to yours (Logitech M557 bluetooth mouse).


hidpp_mac on  master [!?] via △ v3.21.1 ❯ ./build/src/tools/hidpp-list-features -v:debug "IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver" 
Invalid log category tag: 
 (046d:b010) is a HID++ 2.0 device
libc++abi: terminating with uncaught exception of type HIDPP::Dispatcher::TimeoutError: readReport timed out
zsh: abort      ./build/src/tools/hidpp-list-features -v:debug 

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

It is also the same for the Logitech MK710 Combo (Unifying receiver with keyboard and M705 mouse)


hidpp_mac on  master [!?] via △ v3.21.1 ❯ ./build/src/tools/hidpp-list-features -vdebug -d 2 "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port1@02100000/USB Receiver@02100000/IOUSBHostInterface@2/LogiUnifyingUSBDevice"
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] Collection: ff000004
[debug:reportdesc] - Report Input 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Input 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[debug:reportdesc] - Report Output 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Output 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[warning] Missing input report for report 
[warning] Missing output report for report 
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 21 00 00
libc++abi: terminating with uncaught exception of type HIDPP::Dispatcher::TimeoutError: readReport timed out
zsh: abort      ./build/src/tools/hidpp-list-features -vdebug -d 2 

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

Not so sure if the Linux output will help or not.

hidpp/build on  master [?] via △ v3.18.4 took 2s ❯ sudo ./src/tools/hidpp-list-features -d 1 /dev/hidraw3
M705 (046d:101b) is a HID++ 1.0 device
Register 0x00 read   3: 10 00 00
Register 0x01 read   3: 42 00 00
Register 0x0d read   3: 5f 47 34
Register 0xd0 read   3: 00 00 00
Register 0xd2 read   3: 00 00 03
Register 0xd4 read   3: 00 00 04
Register 0xf1 read   3: Invalid value (0x03)
Register 0xf3 read   3: 00 00 00

hidpp/build on  master [?] via △ v3.18.4 took 53s ❯ sudo ./src/tools/hidpp-list-features -d 2 /dev/hidraw3
MK700 (046d:2008) is a HID++ 1.0 device
Register 0x00 read   3: 10 00 00
Register 0x01 read   3: 00 00 00
Register 0x02 read   3: 00 00 39
Register 0x07 read   3: 07 00 00
Register 0x09 read   3: 00 00 00
Register 0xf1 read   3: Invalid value (0x03)
Register 0xf3 read   3: 00 00 00

hidpp/build on  master [?] via △ v3.18.4 ❯ sudo ./src/tools/hidpp-list-features /dev/hidraw3 
Logitech USB Receiver (046d:c52b) is a HID++ 1.0 device
Register 0x00 read   3: 00 01 00
Register 0x01 read   3: 00 00 00
Register 0x02 read   3: 00 02 00
Register 0x03 read   3: 00 00 00
Register 0xb3 read  16: 6d 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Register 0xb5 read  16: Invalid value (0x03)
Register 0xd0 read   3: 00 00 00
Register 0xd2 read  16: Invalid value (0x03)
Register 0xd4 read   3: 00 00 00
Register 0xe6 read   3: 00 00 00
Register 0xf0 read   3: 00 00 00
Register 0xf1 read   3: Invalid value (0x03)

hidpp/build on  master [?] via △ v3.18.4 ❯ sudo ./src/tools/hidpp-list-features /dev/hidraw6
Bluetooth Mouse M557 (046d:b010) is a HID++ 2.0 device
Feature 0x01: [0x0001] Feature set
Feature 0x02: [0x0002] Feature info
Feature 0x03: [0x0003] Device FW version
Feature 0x04: [0x0005] Device name
Feature 0x05: [0x1e00] Enable hidden features (hidden)
Feature 0x06: [0x1800] Generic Test (hidden, internal)
Feature 0x07: [0x1000] Battery status
Feature 0x08: [0x1b00] Reprog controls
Feature 0x09: [0x1b03] Reprog controls v3
Feature 0x0a: [0x2100] Vertical scrolling
Feature 0x0b: [0x2200] Mouse pointer
hidpp/build on  master [?] via △ v3.18.4 ❯ sudo ./src/tools/hidpp-list-features -d 0 /dev/hidraw6
Bluetooth Mouse M557 (046d:b010) is a HID++ 2.0 device
Feature 0x01: [0x0001] Feature set
Feature 0x02: [0x0002] Feature info
Feature 0x03: [0x0003] Device FW version
Feature 0x04: [0x0005] Device name
Feature 0x05: [0x1e00] Enable hidden features (hidden)
Feature 0x06: [0x1800] Generic Test (hidden, internal)
Feature 0x07: [0x1000] Battery status
Feature 0x08: [0x1b00] Reprog controls
Feature 0x09: [0x1b03] Reprog controls v3
Feature 0x0a: [0x2100] Vertical scrolling
Feature 0x0b: [0x2200] Mouse pointer


@noah-nuebling
Copy link

noah-nuebling commented Aug 15, 2021

Wow thanks for the in-depth feedback. I’ll have a closer look at some point.

Just one quick thing I spotted, in one of the logs is you posted it says:

TCC deny IOHIDDeviceOpen

TCC manages the privacy permissions, so some of the errors might have something to do with that.

So to me it seems like some of the times that the VendorID is NULL it’s because the device actually doesn’t have a vendorID and sometimes it’s because it failed to open the device due to TCC. Pretty weird indeed.

…But actually I think opening the device shouldn’t be necessary to read data from it if I remember correctly. So it’s just weird

@mcuee
Copy link
Author

mcuee commented Aug 15, 2021

2021-08-15 08:10:36.281811+0800 hidpp-list-devices[1999:23806] [default] 0x10000098a: TCC deny IOHIDDeviceOpen

@noah-nuebling Is this 1999:23806 vid:pid? It translates to 0x07cf:0x5cfe but there is no such device in my system.

@noah-nuebling
Copy link

I have no idea to be honest. We should probably update the code to check whether IOHIDDeviceOpen() was successful and log the circumstances of the failure to investigate this further

@mcuee
Copy link
Author

mcuee commented Aug 16, 2021

I have no idea to be honest. We should probably update the code to check whether IOHIDDeviceOpen() was successful and log the circumstances of the failure to investigate this further

Yes, that would be good. If possible, please also help to implement the "no vendor id" suggestion by Clément.

@noah-nuebling
Copy link

noah-nuebling commented Aug 17, 2021

I added some of the logging features and default values we talked about on the plane today. I didn't have a mouse or internet so it might not work properly, and I also can't help if something doesn't work because I'm really busy right now. But if you want to take a look it's on my fork.

@mcuee
Copy link
Author

mcuee commented Aug 21, 2021

@noah-nuebling Great, now hidpp-list-devices works fine under my Mac Mini M1.


hidpp_mac/build on  master [!?] via △ v3.21.1 took 6s ❯ ./src/tools/hidpp-list-devices 
IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice:  (046d:c52b) HID++ 1.0
IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver: ???m (046d:b010) HID++ 2.0
IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver (device 0): ???m (046d:b010) HID++ 2.0

Debug log for your info.

click to expand for the full debug log

hidpp_mac/build on  master [!?] via △ v3.21.1 ❯ ./src/tools/hidpp-list-devices -vdebug
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 1
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 2*1
[debug:reportdesc]     Usages: c00e9 c00ea
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 16*1
[debug:reportdesc]     Usages: c0000
[debug:reportdesc] - Report Input 5
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 32*8
[debug:reportdesc]     Usages: c0000
[debug:reportdesc] - Report Input 7
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 32*8
[debug:reportdesc]     Usages: c0000
[debug:reportdesc] - Report Output 4
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 36*8
[debug:reportdesc]     Usages: c0000
[debug:reportdesc] - Report Output 6
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 36*8
[debug:reportdesc]     Usages: c0000
[debug:reportdesc] Collection: b0005
[debug:reportdesc] - Report Input 8
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: b002f
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 2*1
[debug:reportdesc]     Usages: b0020 b0021
[debug:reportdesc] - Report Output 9
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 80009
[debug:reportdesc] - Report Output 23
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 80017
[debug:reportdesc] - Report Output 24
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 80018
[debug:reportdesc] - Report Output 30
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 8001e
[debug:reportdesc] - Report Output 32
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 80020
[debug:reportdesc] - Report Output 42
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 8002a
[debug:reportdesc] Collection: ffa00001
[debug:reportdesc] - Report Input 3
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 32*8
[debug:reportdesc]     Usages: ffa00030
[debug:reportdesc] - Report Input 20
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 5*1
[debug:reportdesc]     Usages: ffa000b1 ffa000b2 ffa000b5 ffa000b7 ffa000b3
[debug:reportdesc] - Report Input 21
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*16
[debug:reportdesc]     Usages: ffa0008c
[debug:reportdesc] - Report Input 31
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: ffa0009c
[debug:reportdesc] - Report Output 3
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 32*8
[debug:reportdesc]     Usages: ffa00030
[debug:reportdesc] - Report Output 25
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 4*1
[debug:reportdesc]     Usages: ffa0008d ffa0008f ffa0009e ffa000dc
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 2*1
[debug:reportdesc]     Usages: ffa000d2 ffa000d9
[debug:reportdesc] - Report Output 26
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: ffa000b5
[debug:reportdesc] - Report Feature 27
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 3*1
[debug:reportdesc]     Usages: ffa000cf ffa000b5 ffa000de
[debug:reportdesc]   - Flags: 23 (Constant, Variable), Size: 1*1
[debug:reportdesc]     Usages: ffa000d8
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 6*1
[debug:reportdesc]     Usages: ffa00009 ffa00017 ffa00018 ffa0001e ffa00020 ffa0002a
[warning] Property "VendorID" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM". Using 0 instead.
[warning] Property "ProductID" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/nub-spmi@3D0D9300/AppleT8101SPMIController/btm@F/AppleBTM". Using 0 instead.
[debug:reportdesc] Collection: ff000048
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 22 (Data, Variable), Size: 1*16024
[debug:reportdesc]     Usages: ff290003
[debug:reportdesc] - Report Feature 1
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: ff290001
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: ff290002
[debug:reportdesc] - Report Feature 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*568
[debug:reportdesc]     Usages: ff290004
[debug:reportdesc] Collection: 10002
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 16*1
[debug:reportdesc]     Usages: [90001, 90010]
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 2*12
[debug:reportdesc]     Usages: 10030 10031
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 10038
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: c0238
[warning] Property "VendorID" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/i2c2@35018000/AppleS5L8940XI2CController/audio-codec-output@48/AppleCS42L83Audio/AppleCS42L83Mikey". Using 0 instead.
[warning] Property "ProductID" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/i2c2@35018000/AppleS5L8940XI2CController/audio-codec-output@48/AppleCS42L83Audio/AppleCS42L83Mikey". Using 0 instead.
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 0
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 3*1
[debug:reportdesc]     Usages: c00cd c00ea c00e9
[warning] Property "VendorID" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/aop@4A400000/AppleASCWrapV4/iop-aop-nub/RTBuddyV2/AOPEndpoint6/AppleSPU@10000001/wakehint/AppleSPUHIDDevice". Using 0 instead.
[warning] Property "ProductID" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/aop@4A400000/AppleASCWrapV4/iop-aop-nub/RTBuddyV2/AOPEndpoint6/AppleSPU@10000001/wakehint/AppleSPUHIDDevice". Using 0 instead.
[warning] Property "Product" was NULL on device "IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/aop@4A400000/AppleASCWrapV4/iop-aop-nub/RTBuddyV2/AOPEndpoint6/AppleSPU@10000001/wakehint/AppleSPUHIDDevice". Using empty string instead.
[debug:reportdesc] Collection: ff0000ff
[debug:reportdesc] - Report Input 0
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: [0, 0]
[debug:reportdesc] Collection: 10002
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 8*1
[debug:reportdesc]     Usages: [90001, 90008]
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 2*12
[debug:reportdesc]     Usages: 10030 10031
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 10038
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: c0238
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 3
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 60020
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] Collection: 10006
[debug:reportdesc] - Report Input 4
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 8*1
[debug:reportdesc]     Usages: [700e0, 700e7]
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: [70000, 700ff]
[debug:reportdesc] - Report Output 4
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 5*1
[debug:reportdesc]     Usages: [80001, 80005]
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 5
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 2*1
[debug:reportdesc]     Usages: c0225 c0224
[debug] inputReportRunLoop exited with result: Stopped
IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver: ?d?l (046d:b010) HID++ 2.0
[debug] inputReportRunLoop exited with result: Stopped
IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver (device 0): ?d?l (046d:b010) HID++ 2.0
[debug:reportdesc] Collection: 10006
[debug:reportdesc] - Report Input 0
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 8*1
[debug:reportdesc]     Usages: [700e0, 700e7]
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: [70000, 700a4]
[debug:reportdesc] - Report Output 0
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 5*1
[debug:reportdesc]     Usages: [80001, 80005]
[debug:reportdesc] Collection: 10002
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 16*1
[debug:reportdesc]     Usages: [90001, 90010]
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 2*12
[debug:reportdesc]     Usages: 10030 10031
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 10038
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: c0238
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 3
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 2*16
[debug:reportdesc]     Usages: [c0001, c02ff]
[debug:reportdesc] Collection: 10080
[debug:reportdesc] - Report Input 4
[debug:reportdesc]   - Flags: 60 (Data, Array), Size: 1*2
[debug:reportdesc]     Usages: 10082 10081 10083
[debug:reportdesc] Collection: ffbc0088
[debug:reportdesc] - Report Input 8
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 1*8
[debug:reportdesc]     Usages: [ffbc0001, ffbc00ff]
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] Collection: ff000004
[debug:reportdesc] - Report Input 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Input 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[debug:reportdesc] - Report Output 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Output 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[warning] Missing input report for report 
[warning] Missing output report for report 
[debug] inputReportRunLoop exited with result: Stopped
IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice:  (046d:c52b) HID++ 1.0
[debug] inputReportRunLoop exited with result: Stopped
[debug] inputReportRunLoop exited with result: Stopped
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 20 00 00
[debug] inputReportRunLoop exited with result: TimedOut
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 1) timed out
[debug] inputReportRunLoop exited with result: Stopped
[debug] inputReportRunLoop exited with result: Stopped
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 21 00 00
[debug] inputReportRunLoop exited with result: TimedOut
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 2) timed out
[debug] inputReportRunLoop exited with result: Stopped
[debug] inputReportRunLoop exited with result: Stopped
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 22 00 00
[debug] inputReportRunLoop exited with result: TimedOut
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 3) timed out
[debug] inputReportRunLoop exited with result: Stopped
[debug:dispatcher] Ignored HID++1.0 error response.
[debug] inputReportRunLoop exited with result: Stopped
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 23 00 00
[debug] inputReportRunLoop exited with result: TimedOut
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 4) timed out
[debug] inputReportRunLoop exited with result: Stopped
[debug:dispatcher] Ignored HID++1.0 error response.
[debug] inputReportRunLoop exited with result: Stopped
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 24 00 00
[debug] inputReportRunLoop exited with result: TimedOut
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 5) timed out
[debug] inputReportRunLoop exited with result: Stopped
[debug:dispatcher] Ignored HID++1.0 error response.
[debug] inputReportRunLoop exited with result: Stopped
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 25 00 00
[debug] inputReportRunLoop exited with result: TimedOut
[warning] Device IOService:/AppleARMPE/arm-io@10F00000/AppleT810xIO/apcie@90000000/AppleT8103PCIe/pci-bridge1@1/IOPP/pcie-xhci@0/AppleT8103USBXHCIFL1100@02000000/pcie-xhci-hs-port2@02200000/4-Port USB 2.0 Hub@02200000/AppleUSB20Hub@02200000/AppleUSB20HubPort@02210000/4-Port USB 2.0 Hub@02210000/AppleUSB20Hub@02210000/AppleUSB20HubPort@02212000/USB 2.0 Hub [MTT]@02212000/AppleUSB20Hub@02212000/AppleUSB20HubPort@02212500/USB 2.0 Hub [MTT]@02212500/AppleUSB20Hub@02212500/AppleUSB20HubPort@02212560/USB Receiver@02212560/IOUSBHostInterface@2/LogiUnifyingUSBDevice (index 6) timed out

@mcuee
Copy link
Author

mcuee commented Aug 21, 2021

No change for hidpp-list-features but I understand it will take you more time to debug this.

hidpp_mac/build on  master [!?] via △ v3.21.1 ❯ ./src/tools/hidpp-list-features "IOService:/IOResources/IOBluetoothHCIController/AppleBroadcomBluetoothHostController/IOBluetoothDevice/IOBluetoothL2CAPChannel/IOBluetoothHIDDriver" -vdebug
[debug:reportdesc] Collection: 10002
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 8*1
[debug:reportdesc]     Usages: [90001, 90008]
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 2*12
[debug:reportdesc]     Usages: 10030 10031
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 10038
[debug:reportdesc]   - Flags: 6 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: c0238
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 3
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 60020
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] Collection: 10006
[debug:reportdesc] - Report Input 4
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 8*1
[debug:reportdesc]     Usages: [700e0, 700e7]
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: [70000, 700ff]
[debug:reportdesc] - Report Output 4
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 5*1
[debug:reportdesc]     Usages: [80001, 80005]
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 5
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 2*1
[debug:reportdesc]     Usages: c0225 c0224
[debug] inputReportRunLoop exited with result: Stopped
?'bo (046d:b010) is a HID++ 2.0 device
[debug:call] Calling feature 0x00/function 0
[debug:call] Parameters: 00 01
[debug] inputReportRunLoop exited with result: TimedOut
libc++abi: terminating with uncaught exception of type HIDPP::Dispatcher::TimeoutError: readReport timed out
zsh: abort      ./src/tools/hidpp-list-features  -vdebug

@noah-nuebling
Copy link

noah-nuebling commented Dec 29, 2021

Hey guys, hope you had a nice Holidays! I just reworked RawDevice_macos and now hidpp-list-features works on my end!!

I think the issue was that to readReport() took too long to start listening for reports, which caused us to sometimes miss the device's response when calling writeReport() and then readReport() right after. After missing the response, readReport() would then listen for nothing until it timed out.

To fix this I put the runLoop on its own thread and let it listen for reports all the time. I know Clement was against this, which I understand. It did make things a lot more complicated and error prone. But it works and it's the only thing I can think of that works.

Issues I'm aware of:

  • There might be race conditions in the IOHIDDeviceRegisterInputReportCallback() callback and the interruptRead() method, because they manipulate shared state without being locked with the mutex. I think it works as it is though.
  • The command line tools are very slow for some reason. I had to override the timeout in readReport() and set it to 5 seconds in order for things to work properly.
  • I only have a Logitech M720 (hid++ 1.0) to test, and some of the other tools still don't work.
  • I noticed some more minor race conditions and other issues which I noted in the code comments. I don't think they make a difference though.

@cvuchener
Copy link
Owner

The command line tools are very slow for some reason. I had to override the timeout in readReport() and set it to 5 seconds in order for things to work properly.

Only the first report, like the device needs to wake up? Or every report is that slow?

I only have a Logitech M720 (hid++ 1.0) to test, and some of the other tools still don't work.

It's enough for testing the lowest part of the protocol (the part that is system specific). The bad news is that if the device is really HID++ 1.0, there may not be much code you can reuse. But since the device was released in 2016, I'm surprised it is using the older protocol. Can you try adding -d1 (or other numbers, from 0 to 6) to hidpp-list-features options? Make sure you are not interacting with the receiver only.

@noah-nuebling
Copy link

noah-nuebling commented Dec 29, 2021

Only the first report, like the device needs to wake up? Or every report is that slow?

I'm not sure it seems to be pretty inconsistent. I think it's just list-devices. list-features is quite fast.

Make sure you are not interacting with the receiver only

I was interacting with the receiver only.

list-features works now!

Screen Shot 2021-12-29 at 4 00 32 AM

And call-function, too!!

Screen Shot 2021-12-29 at 4 01 23 AM

Does this mean my mouse has 32% battery? (See hidpp 2.0 spec)

@noah-nuebling
Copy link

noah-nuebling commented Dec 29, 2021

I found the issue with the timeouts! RawDevice_macos::readReport(timeout) is called with a timeout of -1.
Do you have any idea why this is and how to best handle it? Maybe use a default value when the timeout is -1?

@cvuchener
Copy link
Owner

* \param[out] report HID report
* \param[in] timeout Time-out in milliseconds, negative for no timeout.
*
* \returns report size or 0 if interrupted or timed out.
*/
int readReport (std::vector<uint8_t> &report, int timeout = -1);

-1 means no timeout

@noah-nuebling
Copy link

noah-nuebling commented Dec 29, 2021

-1 means no timeout

Oh cool! Everything seems to be working great now.

But I'm trying to build hidpp as a universal binary, so that it can run natively on both x86 and arm Macs, which is causing some issues.

The problem is that I can't find an easy way to install tinyxml2 as a universal binary. If tinyxml2 is not universal, then building hidpp as a universal binary fails with a linker error. I was using the homebrew package manager to install tinyxml2, but it doesn't support universal binaries.

Not sure what to do about this. Maybe build tinyxml2 as a universal binary ourselves and include that in the repo?

I also tried the MacPorts package manager which does support universal binaries but unfortunately it doesn't have tinyxml2

@noah-nuebling
Copy link

noah-nuebling commented Dec 30, 2021

Using vcpkg to include tinyxml2 might be a good idea. See the tinyxml2 readme.

@noah-nuebling
Copy link

This Razer driver for macOS sets and retrieves reports without a separate thread using IOUSBDeviceInterface:

https://github.com/1kc/librazermacos/blob/2ca85a2bcdbf5e4b3938b95754d06b96de382bd9/src/lib/razercommon.c#L48

@noah-nuebling
Copy link

noah-nuebling commented Jan 11, 2022

The DeviceRequest() function which they are using can be found here https://opensource.apple.com/source/IOUSBFamily/IOUSBFamily-630.4.5/IOUSBFamily/Headers/IOUSBLib.h.auto.html

@noah-nuebling
Copy link

noah-nuebling commented Jan 11, 2022

The current code seems to work but this would probably simplify it a lot. Let me know if you would prefer I redo it using these functions.

@noah-nuebling
Copy link

noah-nuebling commented Jan 11, 2022

DeviceRequestTO() can be used to specify a timeout and USBDeviceAbortPipeZero() might be used to interrupt read requests. (Both found in the same header as DeviceRequest()) This seems perfect.

@noah-nuebling
Copy link

noah-nuebling commented Jan 11, 2022

IOHIDDeviceCreate() might be used as a guide for obtaining those plug-in interfaces. See:

https://opensource.apple.com/source/IOKitUser/IOKitUser-1845.100.19/hid.subproj/IOHIDDevice.c.auto.html

@noah-nuebling
Copy link

Here’s how the razer driver gets the usb interface:

https://github.com/1kc/razer-macos/blob/47f2345d32d03d421accd824357709f5bca0d05c/src/driver/razerdevice.c

@noah-nuebling
Copy link

Okay I dug into this some more and I don't think it will work. I think the way we currently do things with the threads and stuff is the best way to do things. Even though it's not good. See this commit message for more info.

@mcuee
Copy link
Author

mcuee commented Apr 15, 2023

The macOS fork from @noah-nuebling seems to work pretty well.

mcuee@mcuees-Mac-mini tools % ./hidpp-list-devices 
dev://4294969978:  (046d:b33d) HID++ 4.5
dev://4294969978 (device 0):  (046d:b33d) HID++ 4.5
dev://4294971506:  (046d:c52b) HID++ 1.0
dev://4294971506 (device 1): M705 (046d:101b) HID++ 4.5
dev://4294971506 (device 2): MK700 (046d:2008) HID++ 1.0

mcuee@mcuees-Mac-mini tools % ./hidpp-list-features -vdebug -d 1 dev://4294971506
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] Collection: ff000004
[debug:reportdesc] - Report Input 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Input 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[debug:reportdesc] - Report Output 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Output 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[debug] Starting inputRunLoop on device dev://4294971506
[debug] Constructed device dev://4294971506
[warning] Missing input report for report 
[warning] Missing output report for report 
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 20 00 00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 20 07 08 10 1b 04 00 02 06 00 00 00 00 00 00 00
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 40 00 00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 40 04 4d 37 30 35 00 00 00 00 00 00 00 00 00 00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
M705 (046d:101b) is a HID++ 4.5 device
[debug:call] Calling feature 0x00/function 0
[debug:call] Parameters: 00 01
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[debug:call] Calling feature 0x01/function 0
[debug:call] Parameters:
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 01
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x01: [0x0001] Feature set
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 02
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x02: [0x0003] Device FW version
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 03
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x03: [0x0005] Device name
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 04
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x04: [0x0020] Reset
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 05
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x05: [0x1000] Battery status
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 06
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 1b 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x06: [0x1b04] Reprog controls v4
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 07
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 21 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x07: [0x2121] Hi-res wheel
[debug:call] Calling feature 0x01/function 1
[debug:call] Parameters: 08
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:call] Results: 22 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Feature 0x08: [0x2205] Pointer speed
[debug] Destroying device dev://4294971506
[debug] inputReportRunLoop exited with result: Stopped

mcuee@mcuees-Mac-mini tools % ./hidpp-list-features -vdebug -d 2 dev://4294971506
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] Collection: ff000004
[debug:reportdesc] - Report Input 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Input 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[debug:reportdesc] - Report Output 32
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 14*8
[debug:reportdesc]     Usages: ff000041
[debug:reportdesc] - Report Output 33
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 31*8
[debug:reportdesc]     Usages: ff000042
[debug] Starting inputRunLoop on device dev://4294971506
[debug] Constructed device dev://4294971506
[warning] Missing input report for report 
[warning] Missing output report for report 
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 21 00 00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 21 08 14 20 08 04 02 01 0d 00 00 00 00 00 00 00
[debug:register] Getting long register 0xb5
[debug:register] Parameters: 41 00 00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 41 05 4d 4b 37 30 30 20 20 20 20 20 20 20 20 20
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
MK700 (046d:2008) is a HID++ 1.0 device
[debug:register] Getting short register 0x00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 00 00 00
Register 0x00 read   3: 00 00 00
[debug:register] Getting long register 0x00
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x01
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 00 00 00
Register 0x01 read   3: 00 00 00
[debug:register] Getting long register 0x01
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x02
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 00 00 39
Register 0x02 read   3: 00 00 39
[debug:register] Getting long register 0x02
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x03
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x03
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x04
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x04
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x05
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x05
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x06
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x06
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x07
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 03 00 00
Register 0x07 read   3: 03 00 00
[debug:register] Getting long register 0x07
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x08
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x08
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x09
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Results: 00 00 00
Register 0x09 read   3: 00 00 00
[debug:register] Getting long register 0x09
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x0a
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x0a
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x0b
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x0b
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x0c
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x0c
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x0d
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x0d
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x0e
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x0e
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x0f
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x0f
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting short register 0x10
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
[debug] Received input from device dev://4294971506
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:register] Getting long register 0x10
[debug] writeReport called on dev://4294971506
[debug] readReport called on dev://4294971506
[debug] Wait for device dev://4294971506
^C
mcuee@mcuees-Mac-mini tools % ./hidpp-list-features -vdebug -d 0 dev://4294969978
[debug:reportdesc] Collection: 10006
[debug:reportdesc] - Report Input 1
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 8*1
[debug:reportdesc]     Usages: [700e0, 700e7]
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: [70000, 700ff]
[debug:reportdesc] - Report Output 1
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 5*1
[debug:reportdesc]     Usages: [80001, 80005]
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 2
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 13*1
[debug:reportdesc]     Usages: c0224 c0040 c0223 c01ae c0221 c00b6 c00cd c00b5 c00e2 c00ea c00e9 c0030 c0040
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 3
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*8
[debug:reportdesc]     Usages: 60020
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 6*8
[debug:reportdesc]     Usages: ffbcbdad
[debug:reportdesc] Collection: 10080
[debug:reportdesc] - Report Input 4
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*1
[debug:reportdesc]     Usages: 10082
[debug:reportdesc] Collection: c0001
[debug:reportdesc] - Report Input 255
[debug:reportdesc]   - Flags: 2 (Data, Variable), Size: 1*2
[debug:reportdesc]     Usages: [60024, 60026]
[debug:reportdesc] Collection: ff000001
[debug:reportdesc] - Report Input 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] - Report Output 16
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 6*8
[debug:reportdesc]     Usages: ff000001
[debug:reportdesc] Collection: ff000002
[debug:reportdesc] - Report Input 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug:reportdesc] - Report Output 17
[debug:reportdesc]   - Flags: 0 (Data, Array), Size: 19*8
[debug:reportdesc]     Usages: ff000002
[debug] Starting inputRunLoop on device dev://4294969978
[debug] Constructed device dev://4294969978
[debug] writeReport called on dev://4294969978
[debug] readReport called on dev://4294969978
[debug] Wait for device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:dispatcher] Ignored response because of different device index.
[debug] readReport called on dev://4294969978
[debug] Wait for device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:dispatcher] Ignored response because of different device index.
[debug] readReport called on dev://4294969978
[debug] Wait for device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:dispatcher] Ignored response because of different device index.
[debug] readReport called on dev://4294969978
[debug] Wait for device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:dispatcher] Ignored response because of different device index.
[debug] readReport called on dev://4294969978
[debug] Wait for device dev://4294969978
[debug] Received input from device dev://4294969978
[debug] Waiting for inputReport stopped with state: newReport: 1 timedOut: 0 interrupted: 0
[debug:dispatcher] Ignored response because of different device index.
[debug] readReport called on dev://4294969978
[debug] Wait for device dev://4294969978
[debug] Waiting for inputReport stopped with state: newReport: 0 timedOut: 1 interrupted: 0
libc++abi: terminating due to uncaught exception of type HIDPP::Dispatcher::TimeoutError: readReport timed out
zsh: abort      ./hidpp-list-features -vdebug -d 0 dev://4294969978

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants