-
Notifications
You must be signed in to change notification settings - Fork 8
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
joystick: Rewrite driver for replacement USB stack #38
Conversation
Tested with NevolutionX, works great! 💪 |
src/joystick/xbox/SDL_xboxjoystick.c
Outdated
joystick->hwdata->padData = &g_Pads[index]; | ||
joystick->guid = SDL_XBOX_JoystickGetDeviceGUID(index); | ||
static const char* SDL_XBOX_JoystickGetDeviceName(Sint32 device_index) { | ||
HID_DEV_T *hdev = usbh_get_hdev_from_device_index(device_index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With explicit hotplugging support, beware of race conditions like #30 / #31.
Imagine these steps in an app:
- A user asks SDL for the number of connected joystick devices
- User disconnects the USB device
- User takes device count from step 1. to loop over SDL joystick devices to ask for their name
This will probably work fine if the SDL code is updating USB exclusively or if USB isn't handled by interrupts. You'd be using the USB driver for synchronization (Edit: Just realized that usbh_get_hdev_from_device_index
calls usbh_pooling_hubs
, so this race condition probably exists).
However, if USB updates the device list in the background this will create this race condition.
You should check if / how other backends take care of this.
(If you already query devices in (Edit: Nevermind, the USB stack does it here: https://github.com/Ryzee119/ohci_usbhostlib/blame/607ffc8222a82f2f6dc0770c9bab4466dcab9452/src_hid/hid_driver.c#L158)detect
, that'd also be a potential location to set the LEDs for Xbox 360 controllers - but that's another discussion)
src/joystick/xbox/SDL_xboxjoystick.c
Outdated
assert(button_bit <= 128); | ||
return (joystick->hwdata->padData->CurrentButtons.usDigitalButtons & button_bit) != 0; | ||
JOY_DBGMSG("SDL_XBOX_JoystickGetDevicePlayerIndex: %i\n", hdev->uid); | ||
return hdev->uid; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What prevents you from using the same code as here: https://github.com/Ryzee119/ohci_usbhostlib/blame/607ffc8222a82f2f6dc0770c9bab4466dcab9452/src_hid/hid_driver.c#L160-L161 (other than code quality) ?
iface_t* iface = hdev->iface;
if (iface == NULL)
return -1;
uint8_t port = iface->udev->port_num;
port += (port <= 2) ? 2 : -2; //Xbox ports are numbered 3,4,1,2
return port;
It would just need more fine grained checks to query for more than 4 players.
But I'd rather have port number (even if multiple devices share the same port), than to have large and almost meaningless tick values as player index.
I wouldn't be surprised if some applications use the return value from this to access an array by index. So the tick solution is bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might interact with #29, which might have to add this (and I'm not sure how SDL uses it internally):
static void SDL_XBOX_JoystickSetDevicePlayerIndex(int device_index, int player_index) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most drivers just return -1 for this but I think its a good function to have for xbox.
I wasnt too happy with just using the uid hence the comment.
Good point about the array index.
Just using port number is tricky. Devices on hubs or with multiple interfaces will return the same number. Also 1.0 with the daughter will need to be considered.
I can think about this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ive updated this so playerindex just returns to device_index. This will just return 0-n, the lower number being the device that was plugged in first. Ideally this should reflect the physical port locations in a future update but it got quite complex managing downstream hubs too, so feel needs more thought.
src/joystick/xbox/SDL_xboxjoystick.c
Outdated
switch (hdev->type) | ||
{ | ||
case XBOXOG_CONTROLLER: | ||
return "Original Xbox Controller"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this driver (and the new USB HID driver) becoming the standard driver, please create issue after merge, about also querying what kind of subdevice this is ("Lightgun"/"Steering Wheel"/...).
This might also be necessary to properly handle the device input updates, so this driver has to take care of it.
Previously this could have been responsibility of the nxdk repository / xinput, but now the responsibility moves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted. I looked into it a bit, but needs some investigaion on where the Xinput subtype comes from. If its just in the interface descriptor or some other control request. I dont have other xb360 peripheral types to investigate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into it a bit, but needs some investigaion on where the Xinput subtype comes from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went on a tangent looking up xbox360 protocol for some reason. I already get bType in the usb stack to differentiate between steel battalion and xremote. Subtype shouldnt be too hard to query at enumeration stage so getting this should be easy at some stage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay.
I'm keeping this unresolved so it's clear that an issue will have to be created.
The issue should also contain the content from this discussion (or reference it, but note #40, so links might be dead in the future if it's decided migrate repositories).
@@ -485,6 +493,8 @@ static void SDL_XBOX_JoystickClose(SDL_Joystick *joystick) | |||
static void SDL_XBOX_JoystickQuit(void) { | |||
JOY_DBGMSG("SDL_XBOX_JoystickQuit\n"); | |||
usbh_install_hid_conn_callback(NULL, NULL); | |||
//We dont call usbh_core_deinit() here incase the user is using | |||
//the USB stack in other parts of their application other than game controllers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally the USB stack would refcount internally; did you check if it already does that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
USB might be used from different spots (like network, which also has this problem).
For the user-facing USB (and network) API design this leaves few possibilities:
- Should be managed globally using a singleton (avoid conflicting
init
/deinit
at runtime from different locations, and do those automatically at startup / shutdown) - USB code must be able to handle multiple
init
/deinit
without breaking other code (the typical solution here is reference-counting). Like this (imagine these calls coming from different drivers):(There might also be some extra-work required to verify that all of these calls came from the same thread, or initialized with compatible settings)// === app loading phase starts here === // Boot initializes some `unsigned int refcount = 0;` in the USB_Stack // === app startup phase starts here === // Internally does realInit(), because we had a refcount of 0 on entry usbh_core_init(); // refcount++ -> refcount = 1; // Internally nop() because refcount was non-zero on entry (implies this was already initialized; so nothing to do) usbh_core_init(); // refcount++ -> refcount = 2 usbh_core_init(); // refcount++ -> refcount = 3 // === app shutdown phase starts here === // Internally nop() because refcount was non-zero on exit (implies this is still being used elsewhere; so nothing to do) usbh_core_deinit(); // refcount-- -> refcount = 2 usbh_core_deinit(); // refcount-- -> refcount = 1 // Internally does realDeinit(), because we had a refcount of 0 on exit usbh_core_deinit(); // refcount-- -> refcount = 0 // Internally does error(), because we had a refcount of 0 on entry (implies that the lib wasn't initialized anymore) usbh_core_deinit(); // refcount-- -> refcount = 0
Some APIs will do this internally, so I recommend to check if the USB stack might already implement something like this.
Note that this expects that USB also manage access to devices so it's not possible for 2 drivers to access the same device (usually by blocking access to an already opened device).
Details of this should probably be discussed on the USB PR (+ some network API issue).
I just thought I'd mention it, because the USB stack isn't even merged, and it's already becoming a real-world problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend does not do this at all (apart from a basic initialisation guard to prevent issues by reinit the ohci hardware repeatedly). I dont think it will be too hard to add atleast to a basic level of this. I would prefer to leave this for now as I wont have time for a while.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keeping this unresolved until issue has been created elsewhere (because this already contains a lot of documentation about why this is necessary, that should be part of an issue)
src/joystick/xbox/SDL_xboxjoystick.c
Outdated
XInput_Init(); | ||
return 0; | ||
} | ||
static Sint32 xinput_parse_input(HID_DEV_T *hdev, PXINPUT_GAMEPAD controller, Uint8 *rdata); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This should eventually move into a separate file / driver (probably outside of SDL).
Fine as-is, because we'll consider SDL the single source of truth for input related stuff after this PR, but we should have an issue that mentions that this is intended to be moved away.
#ifdef SDL_JOYSTICK_XBOX_DEBUG | ||
#define JOY_DBGMSG debugPrint | ||
#else | ||
#define JOY_DBGMSG(...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do these macro names come from? They don't feel very SDL2-esque.
I'm against these - shortened - custom prefixes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just made them up. It's just for debugging.
What do you propose? I dont want to have the back and forward commits just changing macro names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you propose?
No idea to be honest.
Feel free to ignore this request; although I'd be leaning towards renaming it LOGD
(~LOGD...EBUG)
SDL code is way more inconsistent than I had assume.
I wish the backend code was more consistent - also something to fix upstream probably.
Because apparently SDL is the wild west of codebases (a bit of grep
):
./video/directfb/SDL_DirectFB_video.h:#define SDL_DFB_DEBUG(x...) SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, x)
./video/directfb/SDL_DirectFB_video.h:#define SDL_DFB_DEBUG(x...) do {} while (0)
./video/directfb/SDL_DirectFB_video.h:#define SDL_DFB_LOG(x...) SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, x)
./video/directfb/SDL_DirectFB_video.h:#define SDL_DFB_LOG(x...) do {} while (0)
./audio/openslES/SDL_openslES.c:#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
./audio/openslES/SDL_openslES.c:#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
./hidapi/libusb/hid.c:#define LOG(...) fprintf(stderr, __VA_ARGS__)
./hidapi/libusb/hid.c:#define LOG(...) do {} while (0)
./hidapi/android/hid.cpp:#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
./hidapi/android/hid.cpp:#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
./hidapi/android/hid.cpp:#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
May I propose we merge this and open issues if there are unresolved comments here? |
This PR has been here for less than a week. Also this has to wait until we are sure that XboxDev/nxdk#438 is complete (which is a lot of code), and that has gotten very little review (I personally didn't look much at it yet either), despite being there for more than 3 weeks. The related nxdk USB PR also removes the old USB code, so it will have to update the SDL submodule to the code from this PR in the same step, or SDL would break. So that ties reviews of both PRs + the USB stack together. (Personally, I'd have liked to see the USB stack get integrated first, then the XID driver, and then the SDL driver coming last, then removal of the old USB stack - but whatever) I could imagine that the underlying USB code on the nxdk PR will get some more discussion before it's merged (which might demand changes in this PR). Changing the USB code is also a significant step, and we are likely one of only a handful of projects using the new USB stack for a purpose like ours. I think we all agree that slow review and nitpicking too much is annoying, but I think it's equally bad to deal with fallout from a broken or unmaintainable USB stack, or many follow up PRs which move stuff around. @Ryzee119 has been here for very long and understands our quality requirements. People can still try or use this PR by checking it out manually; it doesn't have to be on master right now (although it would be nice). |
b13c2a1
to
9cf1609
Compare
Merging in line with nxdk#438. This still has some minor issues, but seems strictly better than the existing implementation. |
Rewrote the sdl_joystick driver to be compatible with the proposed replacement USB stack for nxdk (XboxDev/nxdk#438)
I added some features and fixed some issues in the legacy driver.
It now has:
-DCONFIG_HID_MAX_DEV=x
flag