-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Replace option-based RAW_IO support with new API functions. #1297
base: master
Are you sure you want to change the base?
Conversation
@tormodvolden and @hjelmn Please help to look at the proposed API. Thanks. |
Maybe it is easier to review if one commit reverses commit 9e4bb9c and a second commit adds the new functions "from scratch"? I also note that LIBUSB_OPTION_LOG_CB and LIBUSB_OPTION_MAX should be decremented. @hjelmn What do you think about the suggested API functions here? Could this be the lesser of evils? |
This reverts commit 9e4bb9c.
f571b0c
to
4ca460a
Compare
4ca460a
to
4a07ea0
Compare
Changed to two commits, one reverting the old API and one adding the new one. |
One thought I've had about the proposed API is that at the moment, whether an endpoint supports RAW_IO depends only on the backend in use, and the proposed What we actually need to know is just whether we can enable RAW_IO on a specific endpoint on an open device, so perhaps I should replace: int libusb_backend_supports_raw_io(void); with: int libusb_endpoint_supports_raw_io(libusb_device_handle *dev_handle, unsigned int endpoint_address); |
On that last point, see also the discussion in #1260. |
@hjelmn |
Sorry to disturb you as well. I understand that you are mostly out of libusb project in the past few years, still just wondering if you have some comments here for this new API proposal, as you are very much familiar with libusb and Linux USB internals. Thanks. |
@LudovicRousseau |
Reference: From Martin:
|
I do not use or know Windows. |
Isn't Or you could have |
That was basically the approach I had in this earlier proposal. I rejected it in favour of this version because in practice, the extra function made for cleaner and clearer code. It is a lot simpler to check first whether the whole RAW_IO business is necessary, and then if it is, to treat any subsequent error as a failure, than to need to special-case one particular error code that should be treated differently from others. If we're already making the decision to add new API functions for this feature, I don't think there is any particular reason to aggressively minimise the number of functions involved. That was the approach of the previously merged
With that design, you would not be able to check what the maximum transfer size would be with RAW_IO, until you have already enabled RAW_IO. In practice that value can be obtained in advance, and it makes sense to do so so that buffer sizes etc can be allocated accordingly. I also think that a generically named |
I realised recently that there is another approach we could take with this. It requires a little more work behind the scenes on the libusb side, but would make for a much simpler API, with only one new function needed - one that simply enforces an existing best-practice recommendation. As we've already discussed, it's not possible for libusb to abstract away all of this RAW_IO business, because there is no safe way to handle the case where the application submits a transfer with a sub-max-packet size. However, we can handle the case where the application submits a transfer larger than the WinUSB maximum: we just split that transfer into multiple OS transfers behind the scenes. And we can do that perfectly safely for a RAW_IO endpoint, so long as the application's transfer size is a multiple of the endpoint's maximum packet size. If that condition is met, we can split the transfer into smaller OS transfers that each meet the same requirement. Therefore, the only API we actually need for RAW_IO is a way for the application to guarantee that its transfers on a given endpoint will be a multiple of the maximum packet size. Once we have that commitment from the application, we can safely go ahead and enable RAW_IO for the endpoint, and deal with the other requirements behind the scenes. Making that commitment is good practice for the application anyway, because of the issue of overflow. In practice, a device can send any packet length up to the maximum in response to an IN token. If the application isn't ready for all the resulting bytes, libusb is stuck with a the problem of what to do with the excess. As discussed in the documentation, there's no good way to deal with this, so it's advised that applications should just stick to multiples of the endpoint maximum packet size to avoid this being a problem. In fact, the issue of overflow is so fundamentally messy, that it's quite arguable that libusb should never have supported transfer sizes that weren't a multiple of the maximum packet size. It's too late to go back on that decision now, but we can allow the application to opt-in to this requirement, and it makes sense to do so. So how about a single call, something like: /* Allow libusb to require that subsequent transfers, on the specified endpoint, will have a size
* that is a multiple of the maximum packet size returned by \ref libusb_get_max_packet_size
* for that endpoint.
*
* While this requirement is in force, transfers with a size that is not a multiple of the endpoint's
* maximum packet size will be rejected.
*
* Enabling this requirement avoids the possibility of an overflow where a device sends a packet
* larger than expected (see \ref libusb_packetoverflow). It also allows libusb to enable features
* to improve performance, such as the use of RAW_IO on WinUSB.
*/
int libusb_enforce_max_packet_transfers(
libusb_device_handle *dev_handle,
unsigned int endpoint_address,
int enable); Behind the scenes, this call would have the side effect of enabling RAW_IO. |
I think realistically nobody is going to write that code and get the error handling right etc, and this PR does what we want, so I'm in favour of sticking with this approach. There's a bunch of packet sizes you can choose that always work, e.g. 64k, and higher on bulk, so all this packet sizes stuff is not crucial anyway for the usual RAW usecase of recieving a long stream of bytes. |
I would really like to have this available for the Glasgow Interface Explorer--any way to move this forward? |
usbi_err(ctx, "unable to match endpoint to an open interface for RAW_IO"); | ||
return LIBUSB_ERROR_NOT_FOUND; | ||
} | ||
return LIBUSB_SUCCESS; |
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.
Shouldn't this function return max_transfer_size
?
return LIBUSB_ERROR_NOT_FOUND; | ||
} | ||
|
||
CHECK_WINUSBX_AVAILABLE(sub_api); |
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 am currently designing a composite usb device with multiple interfaces.
This leads to the device getting the USB_API_COMPOSITE
backend and the raw_io
functions failing.
But the interface (which is the one which winusb_handle
is actually used) does have the correct API.
Looking at how all the other calls are routed (e.g. submit_bulk_transfer
) I think to be consistent you should add functions composite_get_max_raw_io_transfer_size()
and _set_raw_io()
, which are added to the windows_usb_api_backend
struct. Then in the composite_()
functions dispatch based on the interface.
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.
A quick fix for testing I am using is to set
sub_api = priv->usb_interface[interface].sub_api;
This PR implements an improved API for controlling the WinUSB
RAW_IO
feature.The
LIBUSB_OPTION_WINUSB_RAW_IO
option is removed. This is not an API break, as this option was not yet included in a release. The aim is to replace this API before the release of 1.0.27.The option-based approach worked, but the requirement to attach several variadic arguments made it ugly and error-prone. It also had the undesirable effect of coupling two operations: fetching the maximum supported transfer size, and enabling or disabling raw I/O.
Instead of the option-based mechanism, three new functions are added to the API as follows:
For further background, see #1208 (comment) and the discussion preceding it.