google / gousb

gousb provides low-level interface for accessing USB devices
Apache License 2.0
838 stars 124 forks source link

Cannot set direction in control request #33

Closed nkovacs closed 6 years ago

nkovacs commented 6 years ago

In the old gousb, EndpointDirection was a uint8, with the following values:

ENDPOINT_DIR_IN   EndpointDirection = C.LIBUSB_ENDPOINT_IN
ENDPOINT_DIR_OUT  EndpointDirection = C.LIBUSB_ENDPOINT_OUT

In the current version it is a boolean, and the C library constants are not exposed. There's endpointDirectionMask, but it's private. This is a problem because I need the direction constants for control requests. With the old gousb, I had this code:

n, err := device.Control(uint8(usb.ENDPOINT_DIR_IN|usb.REQUEST_TYPE_VENDOR), 51, 0, 0, data)

I can't do this now.

zagrodzki commented 6 years ago

For what it's worth, the ENDPOINT_IN math worked only accidentally, because USB specification defines control request direction on bit 7 of the bmRequestType, and libusb happens to express the endpoint direction in the same way. USB actually uses discrete tokens to represent direction that are independent of the endpoint address, so it's just a libusb implementation quirk.

I think this suggests a need for a different control API, preferably with separate args for direction, request type and request recipient. But that would create compatibility issues, so I'd rather leave it until the next major change.

In the meantime I'll add relevant constants for building the bmRequestType. Alternatively I can suggest treating request types as opaque blobs defined by the protocol of the device you use, in the same spirit as USB spec defines standard control requests (section 9.4 of USB 2.0). I.e. just declare constants of myDevOutReqType = 0x20, myDevInReqType = 0xa0 and ignore the internal structure of the bit field entirely.

nkovacs commented 6 years ago

I don't understand your comment. Libusb is following the USB specification, since the direction must be set on bit 7.

But I agree with you that a nicer, higher-level API would be better.

zagrodzki commented 6 years ago

Libusb uses ENDPOINT_IN bit for specifying both data transfer direction (as a bit field on endpoint number) as well as control transfer (as a bit field on control request type).

However, USB specification uses two distinct tokens (1001 and 0001) for performing in/out transactions. The endpoint number is always passed as-is.

The decision to bundle the transaction direction as a bit field together with the endpoint number was an arbitrary decision by libusb. Other libraries may express that same functionality differently - e.g. WinUSB uses separate "TransferFlags" field in it's API with separate flag values for in/out transfers.

For gousb, I'd like to hide internals of libusb as much as possible - everything should relate to USB directly, rather than to the implementation that libusb happened to pick.

zagrodzki commented 6 years ago

You can now use dev.Control(gousb.ControlIn | gousb.ControlVendor | gousb.ControlDevice, ...)