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 controller duplication bugfix #12745

Closed
wants to merge 20 commits into from
Closed

macOS controller duplication bugfix #12745

wants to merge 20 commits into from

Conversation

ComradeEcho
Copy link
Contributor

@ComradeEcho ComradeEcho commented Aug 6, 2021

Description

Hey y'all.

Just to preface, I have ~zero experience with github, and this is literally my first time ever touching C or anything close to this low-level. Please scrutinize the heck out of this, because I don't know what I'm doing. Especially pointer/memory/free()/deinit stuff.

This PR aims to take care of a couple of things having to do with the macOS HID Driver.

  1. The IOHIDDeviceRegisterRemovalCallback simply was not working as I believe it should. I don't know if it was implimented improperly, or if there's a bug in macOS related to it. It was being set, but it wouldn't trigger reliably. I replaced IOHIDDeviceRegisterRemovalCallback with IOHIDManagerRegisterDeviceMatchingCallback which seemed to have perfect detection of device removal on my system.

  2. Upon fixing the first issue, I discovered that devices matched by the autoconfigurator still were not disconnecting properly. This was caused an struct iohidmanager_hid_adaptor being improperly cast as a struct pad_connection, and having its data overwritten/corrupted.
    I solved this issue by adding a void* connection to joypad_connection and assigning the offending struct pad_connection there instead of it going in void* data, thus removing the data collision.

  3. I removed all references to uniqueId because I assumed it accomplished nothing. I later realized what it was accomplishing, but decided to try to implement the same goal with kIOHIDLocationIDKey instead, because kIOHIDUniqueIDKey is unsupported by powerPC
    What it was accomplishing was: controllers are detected both when the application is launched, and by the controller connection callback. uniqueId was being used to make sure the controllers are not duplicated by getting added by both of those.

  4. I made a two (unrelated) changes to how debug logs are shows in the logs. I don't know how to exclude those from my PR, but it fixes what I assume is a bug. (debug-level logs would output as info-level logs) :(

Notes

I have done testing solely on M1 macOS 11.

I have done testing with these controllers:

  • xbox series s (bluetooth)
  • ps3 (bluetooth)
  • ps4 (wired and bluetooth)

I have access to a few more untested controllers including:

  • Mayflash
  • 360
  • joyconns
  • switch pro
  • switch-gamecube
  • n64 usb adapter?
  • maybe many more!

I have absolutely no idea if this affects windows/linux/android. It seems like a very Apple-heavy driver, and I don't know if the HID input driver is even functional on other platforms.

Related Issues

For sure:
#12561
#11294
#10546

Possibly:
#3414
#2808
#4215
#3415
#6731
#4793

Reviewers

@gblues @jdgleaver @hunterk @markwkidd

@twinaphex With the changes made surrounding uniqueId - I think this needs a good test on powerPC

It seems the intention of uniqueId added in 9008684 was to reconnect controllers to their previous port based on the "uniqueId" of the device reported by the operating system. However, as noted in the comments, the uniqueId changes every time a device is reconnected and so this code can never accomplish its goal.
In this commit, I have disabled the pad-matching feature at ~L:218 in joypad_connection.c
You can re-enable the functionality from there to experience the current issues.
When I plug in a PS4 controller, the iohidmanager_hid_adapter is being overwritten by what I believe is a pad_controller struct within hidpad_ps4_init()
The *data being passed to hidpad_ps4_init() is a struct iohidmanager_hid_adapter, but it's being de-referenced as a pad_connection
I do not know what this function is intended to be doing - but it's certainly not this.
This is likely an issue across most/all of the other connect_xxx.c files.
I suspect this code was broken for a very long time, but went unnoticed because the thing it broke was already broken.
What seems to have been happening is that the iohidmanager_hid_adaptor was getting cast as a pad_connection and then overwritten, resulting in corrupt data and bad behavior.
I solved this by breaking these two structures apart.
I added a `void* connection` to joypad_connection for the pad_connection to get stored in, and then changed the instances of joyconn->data to joyconn->connection so that the iohidmanager_hid_adaptor no longer gets overwritten.
within struct joypad_connection:
void* connection = pad_connect
void* data = iohidmanager_hid_adapter
These two structures of data should no longer collide.
Turns out uniqueId DID do something. It prevented a controller from being added a second time - once on launch, and again when the controller-connection-callback is started
Can we accomplish the same thing with the kIOHIDLocationIDKey?
I'd like to avoid using kIOHIDUniqueIDKey because it's incompatible with powerPC.
I don't know C.
I'm changing some of this stuff back to how it was before, because my changes resulted in warnings, and I'm not experienced enough to fix them.
Might need this for memory reasons - I'm not sure
@ComradeEcho
Copy link
Contributor Author

It was noted to me that there's quite a few whitespace inconsistencies. That wasn't something I could see while working on this in xcode, but will need to get fixed. There's also still a handful of debug printf() that will need to be removed as well. Although, perhaps some useful RARCH_DBG() could be added.

@ComradeEcho ComradeEcho changed the title Bugfix/controller duplication bug macOS controller duplication bugfix Aug 6, 2021
@lgtm-com
Copy link

lgtm-com bot commented Aug 6, 2021

This pull request introduces 2 alerts when merging 6b89aee into a2fb084 - view on LGTM.com

new alerts:

  • 2 for Implicit function declaration

{
struct iohidmanager_hid_adapter *adapter = NULL;
//loop though the controller ports and find the device with a matching IOHINDeviceRef
for (int i=0; i<MAX_USERS; i++)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this? Don't use internal declaration for loops. Instead, define 'int i' outside of the for loop, at the start of a code function or block. This makes the code C89-compliant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

static void iohidmanager_hid_device_remove(IOHIDDeviceRef device, iohidmanager_hid_t* hid)
{
struct iohidmanager_hid_adapter *adapter = NULL;
//loop though the controller ports and find the device with a matching IOHINDeviceRef
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change these and other comments to C-based comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
if (!adapter)
{
printf("Error removing device %p from HID %p\n",device, hid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all these printfs necessary? We could replace them with RARCH_LOG/RARCH_ERR/RARCH_WARN if they are truly essential. Do note that the logging code is not thread-safe so if this function happens on a thread, perhaps not logging at all would be the safest choice. But I'll leave this determination up to you to decide.

Copy link
Contributor Author

@ComradeEcho ComradeEcho Aug 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The printfs aren't necessary, but I was reluctant to remove them yet as they may still be useful as things get tested. The list_controllers() function can also be axed before merge - that's just something I added to aid in testing.

However, I think some additional logs would be nice. As of right now the only log that's output is when a controller is connected. I think we at least have logs when a controller is disconnected as well as a log detailing whether the device was matched by the autoconfigurator.

I'm not sure where any of this falls in regards to threading. My untrained eye sees that this all seems to be going on within thread 1 - but I have no idea tbh. I'd love to learn about that :)
Screen Shot 2021-08-06 at 12 05 51 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the printfs, and added a couple logs, both info and debug

declare i outside of the for loop, and use C style comments.

Style changes
@lgtm-com
Copy link

lgtm-com bot commented Aug 6, 2021

This pull request introduces 2 alerts when merging cb147f7 into 64244cd - view on LGTM.com

new alerts:

  • 2 for Implicit function declaration

@ComradeEcho
Copy link
Contributor Author

I am currently getting a SIGABRT seemingly randomly while connecting/disconnecting controllers. I can't reproduce it. I can sit here connecting and reconnecting various controllers without incident, but every once in a while this will happen. It may be time-based.

pointer being freed was not allocated while trying to free a iohidmanager_hid_adapter *adapter

This is the point where I have no idea what the actual issue is, or how to solve it.

Screen Shot 2021-08-06 at 5 21 22 PM

@ComradeEcho ComradeEcho marked this pull request as draft August 7, 2021 00:13
@ComradeEcho ComradeEcho closed this Aug 7, 2021
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

Successfully merging this pull request may close these issues.

3 participants