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

Usb Alternate example #186

Open
VincentGijsen opened this issue Nov 11, 2018 · 6 comments
Open

Usb Alternate example #186

VincentGijsen opened this issue Nov 11, 2018 · 6 comments

Comments

@VincentGijsen
Copy link

Hi,

Ive tried various ways to get USB Alternate function on an interface running. I seem to miss something as some mailing-lists seem to imply this functionality should be working. I guess an example would be most helpful for the user-base of libopencm3.

so far i have usb functionality running with both a BULK and Interrupt endpoint combo, but when i try to register them both on the same interface, and make the bulk interface an alternate, windows doens't like my usbdev anymore.

I'm using an stm32f1 with libusb

so far i have these descriptors:

usbd_device *usbd_dev;

static const struct usb_device_descriptor dev = {
	.bLength = USB_DT_DEVICE_SIZE,
	.bDescriptorType = USB_DT_DEVICE,
	.bcdUSB = 0x0200,
	.bDeviceClass = 0xff,
	.bDeviceSubClass = 0,
	.bDeviceProtocol = 0,
	.bMaxPacketSize0 = 64,
	.idVendor = USB_DEVICE_VENDOR_ID,
	.idProduct = USB_DEVICE_PRODUCT_ID,
	.bcdDevice = 0x0200,
	.iManufacturer = 1,
	.iProduct = 2,
	.iSerialNumber = 3,
	.bNumConfigurations = 1,
};

static const struct usb_endpoint_descriptor intr_endp[] = {{
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = EP_INT_IN,
	.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
	.wMaxPacketSize = 64,
	.bInterval = 1,
}, {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = EP_INT_OUT,
	.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
	.wMaxPacketSize = 64,
	.bInterval = 1,
}};


static const struct usb_endpoint_descriptor data_endp[] = {{
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = EP_BULK_IN,
	.bmAttributes = USB_ENDPOINT_ATTR_BULK,
	.wMaxPacketSize = 64,
	.bInterval = 1,
}, {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = EP_BULK_OUT,
	.bmAttributes = USB_ENDPOINT_ATTR_BULK,
	.wMaxPacketSize = 64,
	.bInterval = 1,
}};

static const struct usb_interface_descriptor intr_iface[] = {{
	.bLength = USB_DT_INTERFACE_SIZE,
	.bDescriptorType = USB_DT_INTERFACE,
	.bInterfaceNumber = 0,
	.bAlternateSetting = 0,
	.bNumEndpoints = 2,
	.bInterfaceClass = 0xff,
	.bInterfaceSubClass = 0,
	.bInterfaceProtocol = 0,
	.iInterface = 0,

	.endpoint = intr_endp,
}};


static const struct usb_interface_descriptor data_iface[] = {{
	.bLength = USB_DT_INTERFACE_SIZE,
	.bDescriptorType = USB_DT_INTERFACE,
	.bInterfaceNumber = 1,
	.bAlternateSetting = 0,
	.bNumEndpoints = 2,
	.bInterfaceClass = 0xff,
	.bInterfaceSubClass = 0,
	.bInterfaceProtocol = 0,
	.iInterface = 0,

	.endpoint = data_endp,
}};

static const struct usb_interface ifaces[] = {
{
		.num_altsetting = 1,
		.altsetting=intr_iface
},
{
.num_altsetting = 1,
.altsetting = data_iface
}
};

static const struct usb_config_descriptor config = {
	.bLength = USB_DT_CONFIGURATION_SIZE,
	.bDescriptorType = USB_DT_CONFIGURATION,
	.wTotalLength = 0,
	.bNumInterfaces = 2,
	.bConfigurationValue = 1,
	.iConfiguration = 0,
	.bmAttributes = 0x80,
	.bMaxPower = 0x32,

	.interface = ifaces,
};


but setting the **bAlternateSetting = 1 ** doesnt seem to be enough (besides the callbacks for BULK and INTERRUPT).


static const struct usb_interface_descriptor data_iface[] = {{
	.bLength = USB_DT_INTERFACE_SIZE,
	.bDescriptorType = USB_DT_INTERFACE,
	.bInterfaceNumber = 1,
	.bAlternateSetting = 0,
	.bNumEndpoints = 2,
	.bInterfaceClass = 0xff,
	.bInterfaceSubClass = 0,
	.bInterfaceProtocol = 0,
	.iInterface = 0,

	.endpoint = data_endp,
}};

There is also this function in the doc:


int libusb_set_interface_alt_setting | ( | libusb_device_handle * | dev,
-- | -- | -- | --
  |   | int | interface_number,
  |   | int | alternate_setting


but i'm not sure if and if so, how to use it.

Any thoughs/help on fixing this (and perhaps turning it into a example for all to benefit?)

@karlp
Copy link
Member

karlp commented Nov 11, 2018

I've not explicited tried alternates like this, but you can have alternate configurations, those are demoed in the gadget-zero tests. If you want to add an alternate setting there too, feel very welcome. https://github.com/libopencm3/libopencm3/blob/master/tests/gadget-zero/usb-gadget0.c#L79

@VincentGijsen
Copy link
Author

Hi karlp,

thanks for the reply, ive seen that example. But the nice thing about this 'alternate' method is that one can quickly switch between endpoint-types, atleast according to the theory. But simply registering two usb_interface_descriptor 's to the an interface, one with bAlternateSetting = 0, and the other bAlternateSetting =1 is appearently not going to cut it.. i guess i'll just have to wait a bit for someone else who has done it, or try some other permutations i haven't gotten to. The thing is, i really want the usb-interface to be compatible to an existing peace of software, rather than also changing that piece.
Should i find a working setup, i'll be sure to create a PR in the examples section.

@kuldeepdhaka
Copy link

Hello @VincentGijsen

Something like this:

static uint8_t my_alt_setting;

static const struct usb_interface ifaces[] = {{
		.num_altsetting = 2,
		.cur_altsetting = &my_alt_setting,
		.altsetting=ifaces_collection /* Pointer to an array of iface */
}};

Idea was to store alternative setting in a variable provided by user (pointer) when SET_INTERFACE is called and read the variable when GET_INTERFACE is called.

Consideration:
Pointer because descriptors are usually store in flash (const) so a pointer to memory location in RAM did the trick.

if the pointer is NULL (cur_altsetting = NULL) it will force just to accept alternate-setting = 0.

Reference:
libopencm3/libopencm3#305
https://github.com/libopencm3/libopencm3/blob/01f33f47b720e3222dc8560a3f000745d5fa5006/lib/usb/usb_standard.c#L335
https://github.com/libopencm3/libopencm3/blob/6fa75afbc66f5f612db212c77d66b3dfa3314ff8/include/libopencm3/usb/usbstd.h#L161

Hope this solves your problem.

@kuldeepdhaka
Copy link

Tip: Usually BULK endpoint containing interface is usually the alternate-setting 0 instead of 1 because BULK endpoint do not require allocation of any bandwidth but INTERRUPT interface require allocation of bandwidth.
So i would recommend that you keep BULK endpoints in bAlternateSetting=0 and INTERRUPT endpoints in bAlternateSetting=1

@VincentGijsen
Copy link
Author

@kuldeepdhaka

Thanks for the eleborate help/pointers, I'll be sure to give it a try, seems to me you're way more into the USB-stack versed than I am 👍

@VincentGijsen
Copy link
Author

VincentGijsen commented Dec 2, 2018

Edit: ok... ignore below! I didn't catch that all endpoinds are defined together in the alt interface, so both INTERRUPT + BULK..., so the descripter denotes 3 endpoints in ALT 1

So i think its now works, turns out, the client-code somehow messed-up the alternate stuff.

So to clearify, in order to make a correct example:

I now have a devices with a single interface, and two alt's the first bulk, second interrupt.
When I'm about to use the interrupt endpoints, i call (in host app) libusb_set_interface_alt_setting(handle, 0,1) than i can use the interrupt endpoint, and callbacks are fired.

When I then want to do a bulk transfer, I first all libusb_set_interface_alt_setting(handle, 0, 0)

and do the bulk tranfser by calling something like libusb_bulk_transfer(usbHandle, EP_BULK_OUT, frameBuffer, frameBufferSize, &actualLength, 100) (params not really matter).

But this is done async on the host, and when the application than moves back to 'interrupt' mode, it messes up the transfer if not completed sending all (8k) bytes in background.

Somehow in the original code (atmels stack + libusb) , the author only switches to alt_setting (0, 1) a single time, does some Interupt endpoint stuff and than mixes BULK and INTERRUPT traffic without doing any explicit switching alt-modeswhere if I try that, libusb gives an error that the endpoint isn't available

the original host code:
https://github.com/Grix/helios_dac/blob/master/sdk/HeliosDac.cpp
the line where the alt settings is used once: https://github.com/Grix/helios_dac/blob/ca9cdeae14800db7389a725aaf7cbd05a3cfc9a9/sdk/HeliosDac.cpp#L66
than the interrupt calls: https://github.com/Grix/helios_dac/blob/ca9cdeae14800db7389a725aaf7cbd05a3cfc9a9/sdk/HeliosDac.cpp#L258

and the bulk to write chunks of data: https://github.com/Grix/helios_dac/blob/ca9cdeae14800db7389a725aaf7cbd05a3cfc9a9/sdk/HeliosDac.cpp#L363

So my question is; is using two alts interfaces without switching supposed to work (so there is still an issue in my code somehwere) , or is my understanding correct that one needs to swap between modes, given the need to do interrupt or bulk transfers (and the atmel-stack magically works somehow) ?

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

No branches or pull requests

3 participants