1

Topic: Signed Axis Minimum values are forced to 0

Hi,

Thanks for this library. There's a problem in the report descriptor parsing with signed values for axis (PhysicalMinimum and LogicalMinimum).

When a negative value is found, it is forced to 0 in Reports/Encoding/EncodedItems, line 160 and 162 :

Changing the following two lines fixes the bug:

EncodedItem.cs : L160 change
{ DataValue = (uint)(sbyte)value; if (value < 0) { Data.Add(0); } }
to
{ DataValue = (uint)(sbyte)value; }

and EncodedItem:L162 change
{ DataValue = (uint)(short)value; if (value < 0) { Data.Add(0); Data.Add(0); } }
to
{ DataValue = (uint)(short)value; }

I don't understand what is the purpose of these 2 tests, and if it is a correct fix. Currently I'm stuck in the development of a plugin for a software that uses this library in a dll because of this bug.

2

Re: Signed Axis Minimum values are forced to 0

So, this is a funny one about HID report descriptors.
For physical and logical ranges, they can be signed or unsigned.
For example, if MINIMUM is 0 and MAXIMUM is 255, many devices still encode this with a single byte.
If we interpreted it as signed, this is MINIMUM 0 MAXIMUM -1.
So, the thing to do is to compare MINIMUM and MAXIMUM, and if MAXIMUM < MINIMUM when signed, to treat it as unsigned. It's something real-world devices do.

The current behavior is definitely wrong, looking at it.

EncodedItem is per-item, so if it's MINIMUM it doesn't know about MAXIMUM. There's no ideal fix with the way the API is currently structured. However, I think the case that matters the most here is the Windows report descriptor reconstruction. Are you encountering this while trying to decode on Windows?

Would you mind trying something for me? Try changing Data.Add(0) to Data.Add(0xff). That would sign extend the negative value. It's not actually the right fix, but it works that'd give me a good direction for making a proper fix.

Also, what device do you encounter this with? I like to have actual physical hardware to test with if possible. If it's something relatively cheap (gamepad, joystick, etc.) I may just buy it.

Thanks!

God bless.

James

3 (edited by Etienne 2025-10-09 16:08:52)

Re: Signed Axis Minimum values are forced to 0

Thanks for the explanation.

I tried to change Data.Add(0) to Data.Add(0xff), but it doesn't solve it.
In fact, after " DataValue = (uint)(short)value;" , there's already 4 bytes in Data array (which have the correct value when it's negative, the cast works), but after 2 Data.Add there's 6 bytes, which is not correct (this is for 16 bits values, for 8 bits it ends up with 5 bytes).
So I still don't understand why there's Data.Add , as the casts work without them.

I'm testing with boards I've designed (ElectroSeed SamD21 WB), with my own descriptor (I'm using specific tool to program them, Node Blue). I don't have commercial products that have signed 16 bits axis, but I've also tested with a Fanatec Force Feedback base (CSL DD), which has signed 8 bits axis, and the problem appears as well.

If you have boards that are supported by Node Blue, I can give you access to my tool so you can try out. Or I may be able to send you a pre-programmed board, depending on where you are located.

4

Re: Signed Axis Minimum values are forced to 0

Ah, that's true, I was thinking it was doing (ushort)(short)value.

Have you encountered any problems after having removed the Data.Add?

While it is somewhat wasteful on report descriptor space (which would matter if someone were to make an editor using HIDSharp's report features), for actually using the report descriptor (as on Windows), yes, getting rid of that is actually perfectly fine and correct.

5

Re: Signed Axis Minimum values are forced to 0

No problem, but I could only test with your test project. I couldn't test on the final project I'm working on (a plugin for Simhub software), as this program is closed source and is using a dll of your Lib. So to test in the final context, I believe I would need to add your sources and change the namespace.

6

Re: Signed Axis Minimum values are forced to 0

Can you not copy the recompiled HidSharp.dll on top of the one used by the plugin? Or do you mean its code is just compiled into the plugin?

7

Re: Signed Axis Minimum values are forced to 0

I'm not sure, he's not using the latest version of the dll, but I'll try.

8 (edited by Etienne 2025-10-12 10:30:39)

Re: Signed Axis Minimum values are forced to 0

Couldn't make it work, Simhub is compiled with .net 4.8, and I didn't manage to install it in order to compile HidSharp with 4.8 too.

Edit : finally managed to change target framework to 4.8 (I had to edit the .csproj manually).

But it doesn't work as before, it's not selecting the right device now. It's weird as in your test project it's all correct. I'll debug that, as it's probably in my code this time smile.

I also have to figure out why I have lots of 'System.ObjectDisposedException' in HidSharp.dll errors in my project, but it has probably nothing to do with your code. I don't know how I'm going to find this one as it's not stopping when the exception is thrown. I'm a newbie in c#...

If you want me to send you a sample board to do some tests on your side, tell me. I'll send it as a gift.

Edit : Found my bug, I have the right device now (I'm testing on a composite device, which has 2 HID joysticks and 1 raw HID).

9

Re: Signed Axis Minimum values are forced to 0

Did you end up getting it working? I've posted this fix in HIDSharp 2.6.4 - looking at it I'm fairly sure it is a correct fix. Thanks for reporting this bug!

As to hardware, I'd certainly be interested in taking a look. E-mail me at hidsharp@seekye.com -- I don't want to give out my address on a search-engine-crawled forum.

God bless.

James

10 (edited by Etienne 2025-10-14 12:22:45)

Re: Signed Axis Minimum values are forced to 0

Thank you very much for the new version.

I tested with a custom board (SamD21 WB) with 2 signed axis, 8 and 16 bits (can't do other size like 10 bits yet). It work both in Simhub and with your test program. Signed minimums are correctly extracted.

I tested with the same board, with a Fanatec wheel conversion code, it works with your test program (using Raw method),
but in Simhub I can retrieve input reports, but got lots of 'System.ObjectDisposedException' in HidSharp.dll errors.
I don't understand yet why I have these errors with this specific device and not with the simple 2 axis board, but I don't think it comes from Hidsharp as it works with your test program. It probably comes from a multithreading access issue.

Don't know if it can help, this is the log of the error :

ERROR - First chance exception error System.ObjectDisposedException: Closed.
   at HidSharp.Platform.Windows.NativeMethods.OverlappedOperation(IntPtr ioHandle, IntPtr eventHandle, Int32 eventTimeout, IntPtr closeEventHandle, Boolean overlapResult, NativeOverlapped* overlapped, UInt32& bytesTransferred) in C:\Code\src\oss\hidsharp\hid\HidSharp\Platform\Windows\NativeMethods.cs:line 934
Stacktrace :    at SimHubWPF.ErrorManagers.CurrentDomain_FirstChanceException(Object sender, FirstChanceExceptionEventArgs e)
   at HidSharp.Platform.Windows.NativeMethods.OverlappedOperation(IntPtr ioHandle, IntPtr eventHandle, Int32 eventTimeout, IntPtr closeEventHandle, Boolean overlapResult, NativeOverlapped* overlapped, UInt32& bytesTransferred) in C:\Code\src\oss\hidsharp\hid\HidSharp\Platform\Windows\NativeMethods.cs:line 937
   at HidSharp.Platform.Windows.WinHidStream.Read(Byte[] buffer, Int32 offset, Int32 count) in C:\Code\src\oss\hidsharp\hid\HidSharp\Platform\Windows\WinHidStream.cs:line 123
   at HidSharp.DeviceStream.<>c__DisplayClass1.<BeginRead>b__0() in C:\Code\src\oss\hidsharp\hid\HidSharp\DeviceStream.cs:line 67
   at HidSharp.AsyncResult`1.<>c__DisplayClass1.<BeginOperation>b__0(Object self_) in C:\Code\src\oss\hidsharp\hid\HidSharp\AsyncResult.cs:line 75
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

With a Fanatec CSL DD base, device is detected but inputs can't be read, the async resut never gets completed (ar.IsCompleted is always false).

I'll send you an email to get your address.

11

Re: Signed Axis Minimum values are forced to 0

Does it use multiple threads to run whatever code is accessing the HID? To me this looks like the HidStream has been closed closed. Is the HidStream instance a global or somesuch? If so, does the other instance Dispose of it perhaps?

For what it's worth, if you control every bit of code that's accessing the HID, HIDSharp does have its own exclusion mechanism available -- see OpenOption.Exclusive. It's not a HID feature, it's a HIDSharp feature, so if some other program is accessing the device it can't help you, but if your accessing is unpredictable you may find it useful. It works interprocess on Windows, Mac, and Linux. Within a single process you might just want to use a lock.

God bless.

James

12

Re: Signed Axis Minimum values are forced to 0

By any chance does the device that has no response at all use GET REPORT setup commands only and not use an interrupt endpoint?

13 (edited by Etienne 2025-10-14 17:09:24)

Re: Signed Axis Minimum values are forced to 0

Hid access is only done in one thread (well, I think so), and if it was not the case, I would have the error with any type of device.
The stream was global but I changed it to local (so it's recreated at each refresh), and it doesn't solve the problem.

The problem is that I don't know how I can catch where the stream is closed.
I can't get any exclusive access as I'm dealing with gaming devices, so games are supposed to be able to access them at any time.

I think it's related to Simhub, as there is a lot of other plugins that are accessing hid devices. And when I deactivate / reactive any plugin, everything is reinitialized and the problem disappears ! (I just discovered that).
I'll see with Simhub developer.

The device that has no response is using interrupt endpoints, and it is read by windows, games, and another standalone program I've written.

14

Re: Signed Axis Minimum values are forced to 0

Does SimHub provide the HidStream by any chance?

On Windows, HIDSharp only raises that exception when it is doing an overlapped operation (Read, Write, etc.) and the "closed" event object has been set. In other words, it only says that when someone has called Close(), Dispose(), etc. on the stream.

I very intentionally do not track open HidStream objects from HidDevice, so there is no way for the library to call Close() on the stream. It has to be the user of the library. (HidDevice very intentionally has no setters, only getters and a method to open the stream. That way it can be cached, passed around freely, etc.) The whole library is designed to be thread-safe so that I can concurrently read and write from different threads, etc. so that should not be the problem.

As to access, HID devices, on Windows, Mac, and Linux, effectively share their read data between processes. There's no exclusivity, and any number of HID streams should be able to be open at the same time without problems. HID streams can't see what other processes are writing to the device, but all streams read back the device's responses.

I mention the HIDSharp exclusivity feature only because it's useful at times -- for example, if you are compiling and uploading to your device, you can use the exclusivity feature to block (and notify for interruption, actually, it supports that too) other parts/processes of your program (such as a device monitor or serial output tool) until the upload is complete. I use this extensively. But I don't think it'd help you at all with what you are describing.

There is actually one way you could catch the Close(). If you are using Visual Studio, use Debug->Attach To Process, and make sure the HidSharp PDB is there and you have HidSharp compiled in Debug mode, so that breakpoints work reliably. Navigate to a part of your code that calls Close() so that you can step into it, and set a breakpoint in WinHidStream.Dispose(bool disposing). Then next time you attach into the process, you'll have a breakpoint and can catch whatever is doing it.

Hope this helps.

God bless.

James

15 (edited by Etienne 2025-10-15 10:15:13)

Re: Signed Axis Minimum values are forced to 0

Hi,

Problem solved ! I now use a direct, synchronous stream.read instead of the async mehod stream.beginRead(), etc...

The problem disapeard.

And also, since I'm putting boards in bootloader mode in order to upload fw updates, your advices on getting exclusive access will be useful too.

Thanks a lot for your support. I sent you an email btw.