I've been trying to set up a USB Audio Input Stream on my microcontroller device. I know that each USB Audio stream has two alternate settings; Alternate setting 0 is where no stream is available; Alternate setting 1 is when there is a stream available.
I've already set up USB Audio Output so I know the stream descriptors work fine. Obviously, the microcontroller calls a USB interrupt to activate the output when the host tells it when audio is coming through (tells microcontroller to enable alternate setting 1...). However, now I'm confused how to enable the USB Audio input side. I'm confused because obviously the host doesn't tell the microcontroller that input is coming through... rather the device tells the host it's sending data through.
If anyone could give me insight how I would properly enable the input stream properly that would be wonderful. I'm wondering if I should just hard enable the endpoint and just send data that way? I can give more code to if need be but I guess this is more of a thinking type/algorithmic approach of question.
Here is my descriptor of the stream's alternate settings:
.iface_alt0.bLength = sizeof(usb_iface_desc_t)
.iface_alt0.bDescriptorType = USB_DT_INTERFACE
.iface_alt0.bInterfaceNumber = UDI_AUDIO_IFACE_DATA_IN_NUMBER
.iface_alt0.bAlternateSetting = 0
.iface_alt0.bNumEndpoints = 0
.iface_alt0.bInterfaceClass = AUDIO_IFACE_CLASS
.iface_alt0.bInterfaceSubClass = AUDIO_IFACE_SUBCLASS_STREAMING
.iface_alt0.bInterfaceProtocol = AUDIO_IFACE_IP_VERSION_02_00
.iface_alt0.iInterface = 0
.iface_alt1.bLength = sizeof(usb_iface_desc_t)
.iface_alt1.bDescriptorType = USB_DT_INTERFACE
.iface_alt1.bInterfaceNumber = UDI_AUDIO_IFACE_DATA_IN_NUMBER
.iface_alt1.bAlternateSetting = 1
.iface_alt1.bNumEndpoints = UDI_AUDIO_IN_NB_ENDPOINTS
.iface_alt1.bInterfaceClass = AUDIO_IFACE_CLASS
.iface_alt1.bInterfaceSubClass = AUDIO_IFACE_SUBCLASS_STREAMING
.iface_alt1.bInterfaceProtocol = AUDIO_IFACE_IP_VERSION_02_00
.iface_alt1.iInterface = 0
Thanks!
EDIT - Just read this source:
"When this configuration is enabled, the first two interface descriptors with bAlternativeSettings equal to zero is used. However during operation the host can send a SetInterface request directed to that of Interface one with a alternative setting of one to enable the other interface descriptor." - USB in a Nutshell
Revised Question: How do I send a SetInterface Request to enable the USB device accept an input stream?
new update - Is there a way I can set the alternate setting active through the descriptors? I was reading this about the stream descriptor -> "The bmControls field contains a set of bit pairs, indicating which Controls are present and what their capabilities are." "D1..0 Active Alternate Setting Control", "D3..2 Valid Alternate Setting Control".
resolved sort of -
So it looks like I just had to open up an audio app on my host device to enable the alternate setting... I didn't know that was the case.
int libusb_set_interface_alt_setting (libusb_device_handle * dev,
int interface_number, int alternate_setting)
http://libusb.org/static/api-1.0/group__dev.html#ga3047fea29830a56524388fd423068b53
in general the fields in a descriptor are like pointers to memory locations. if the mapping is faulty the device will not work.as the host has a certain mapping in its driver the device has to obey this mapping
in http://www.usb.org/developers/docs/devclass_docs/audio10.pdf on page 117 is said that there is a top-level Standard AudioControl descriptor and lower-level Class-Specific Audio Control Descriptors.
Beside the AudioStreaming descriptor you have to set other descriptors also correctly. In the example in http://www.usb.org/developers/docs/devclass_docs/audio10.pdf page 126 has to be set Standard Audio Streaming Interface Descriptor, Class-Specific Audio Streaming Descriptor, Type I format descriptor, Standard Endpoint Descriptor, Class-specific Isochronous Audio Data Endpoint Descriptor
i do not know what class your device implements, maybe you should set all these descriptors then it will possibly work
i can not find a bmControl field in the AudioStreaming descriptor.
Normally alternate settings are used to switch between the endpoints or the AudioStreaming Interfaces, see the Class-specific Interface Descriptor on page 117
in http://www.usb.org/developers/docs/devclass_docs/audio10.pdf from page 58-64 are all audio streaming relevant descriptors
in the linux USB audio driver there is a bmControl field:
/* 22 * bmControl field decoders
23 *
24 * From the USB Audio spec v2.0:
25 *
26 * bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
27 * each containing a set of bit pairs. **If a Control is present,
28 * it must be Host readable.** If a certain Control is not
29 * present then the bit pair must be set to 0b00.
30 * If a Control is present but read-only, the bit pair must be
31 * set to 0b01. If a Control is also Host programmable, the bit
32 * pair must be set to 0b11. The value 0b10 is not allowed.
33 *
34 */
http://lxr.free-electrons.com/source/include/linux/usb/audio-v2.h
(http://www.usb.org/developers/docs/devclass_docs/audio10.pdf on page 36)
Related
There are two commands for setting "speed" - cfsetospeed and cfsetispeed.
But why only one "speed" is shown by stty?
According to bits/termios.h, c_ispeed and c_ospeed are "input speed" and "output speed".
I tried to set B4800 to "input speed" and "B57600" to "output speed", and vice versa at the other
end of serial channel. But data is corrupted. Why there are two separate speeds if it is impossible to set
them separately?
stty shows the speed which was set by cfsetospeed or cfsetispeed - whichever was called last.
Besides, B0 setting takes effect only with cfsetospeed. Is it documented somewhere?
Moreover, while B0 is set, I can receive and transmit data with whatever speed was
active before B0 was set. Is it documented somewhere? Is it undefined behavior or it is in POSIX?
EDIT:
I conducted the same tests on ordinary serial port (i.e., no usb) and obtained the following curious difference with usb serial port:
Ordinary serial port uses 9600 "speed" if we set B0, whereas
usb serial port uses whatever speed was selected before B0 was set.
(OS: Linux)
Why there are two separate speeds if it is impossible to set them separately?
Some (older) UARTs (e.g. the ubiquitous 8250 and its successors) actually have a crystal input for the transmitter and another clock input for the receiver. Hence the input baudrate can be different from the output baudrate on such UARTs.
But most (if not all) boards feed the transmit clock to the receive clock to negate this feature (e.g. the typical 8250/165x0 datasheet will show the RCLK input driven by the BAUDOUT output).
The separate termios speed elements simply reflects this obscure hardware capability (that is rarely actually available).
Also most UARTs (that are not 8250-based) in SoCs have a common clock input for transmit and receive, so the baudrate setting has to apply to both.
So specifying "separate" baudrates is typically a useless configuration.
Besides, B0 setting takes effect only with cfsetospeed. Is it documented somewhere?
Code is the documentation.
Most serial port drivers call uart_get_baud_rate() in drivers/tty/serial/serial_core.c to decode the baudrate settings from the termios structure.
/**
* uart_get_baud_rate - return baud rate for a particular port
* #port: uart_port structure describing the port in question.
* #termios: desired termios settings.
* #old: old termios (or NULL)
* #min: minimum acceptable baud rate
* #max: maximum acceptable baud rate
*
* Decode the termios structure into a numeric baud rate,
* taking account of the magic 38400 baud rate (with spd_*
* flags), and mapping the %B0 rate to 9600 baud.
*
* If the new baud rate is invalid, try the old termios setting.
* If it's still invalid, we try 9600 baud.
*
* Update the #termios structure to reflect the baud rate
* we're actually going to be using. Don't do this for the case
* where B0 is requested ("hang up").
*/
Note the special handling mentioned for B0.
I conducted the same tests on ordinary serial port (i.e., no usb) and obtained the following curious difference with usb serial port:...
The list of drivers that use uart_get_baud_rate() does not seem to include any USB serial port adapters: https://elixir.free-electrons.com/linux/latest/ident/uart_get_baud_rate
You may have to inspect the driver for your specific USB serial port adapter for its handling of the termios baudrate specifers.
I'm a little confused how to add support for multiple bit rates/sample rates in USB Audio Class 2.0. Compared to USB Audio Class 1.0 (which gives you an option on adding multiple rates and such), USB Audio Class 2.0 doesn't give that option. How would I change my descriptors for that? I read somewhere that you use more alternate settings but I don't know how that would help...
Here's my audio format descriptor:
audioformat.bLength = sizeof(usb_audio_format_type_1_desc_t)
audioformat.bDescriptorType = 0x01
audioformat.bDescriptorSubtype = 0x02
audioformat.bFormatType = 0x01
audioformat.bSubSlotSize = 3
audioformat.bBitResolution = 24
Thanks!
Yea so I figured it out and thus bounty doesn't really matter.
To do multiple bit rates: Have alternate settings with its corresponding audio data format descriptors/etc to support different bit rates in order; for example: Alternate Setting 0 (no endpoints), Alternate Setting 1 (with all stream/class descriptors, format descriptor supports 16 bits), Alternate Setting 2 (with all stream/class descriptors, format descriptor supports 24 bits).
To do multiple sample rates: You have to follow the USB Audio Class 2.0 doc with the CUR, MIN, MAX format and give control to the host.
For example:
#define USB_AUDIO_SAMP_RATE_RANGE { CPU16_TO_LE8_ARRAY(2), \
CPU32_TO_LE8_ARRAY(SAMPLE_RATE_44_1), \
CPU32_TO_LE8_ARRAY(SAMPLE_RATE_44_1), \
CPU32_TO_LE8_ARRAY(0), \
CPU32_TO_LE8_ARRAY(SAMPLE_RATE_48_0), \
CPU32_TO_LE8_ARRAY(SAMPLE_RATE_48_0), \
CPU32_TO_LE8_ARRAY(0)}
As reference, LE8 is lower endian 8 bits and the defines are converting a 16 or 32 bit integer into an array of 8 bit integers formatted in lower endian.
There are some Clock Entities in USB Audio 2.0 (UAC2). But USB Audio 1.0 (UAC1) doesn't have it. UAC1 directly provide various Samples rates by which we can request.
But in UAC2, we has to use the Clock Entities. There are some specific descriptor gives the information about clock entities.
I'm new to USB development, and i'm quite confused about what data rates are realistic.
I'm trying to develop an external sound card connected on an AVR32 processor, which supports USB Full Speed(12 Mb/s). I'll use USB audio class 1 to send the audio data to a PC. I need to send 24 bit, 48kHz, 2 channels as INput to the computer, but also 24 bit, 48kHz, 1 channel OUTput from the computer. Streaming both ways.
That gives me a data rate of: 24 bit * 48kHz * 3 channels = 3,5 Mb/s, which should be possible by using USB Full Speed?
I understand that the Audio Class sends data via an Isochronous transfer, but i'm confused about how many transactions ( e.g. IN = 256 bytes ) it is possible to make in one frame? according to the USB specification (http://www.usb.org/developers/docs/usb20_docs/#usb20spec - > table 5-4) it seems to be possible to send more than one transaction per frame?
Is it possible to send both IN and OUT packets within one frame?
Thanks in advance!
In Linux, how do you create an input event interface with a user specified event number and map that to a specific device event?
I'm using the gpio-keys driver to translate key presses from a keypad. I define the keys to be used in my board configuration source file as shown below
static struct gpio_keys_button ev_keys[] = {
[0] = {
.type = EV_KEY,
.active_low = 1,
.wakeup = 0,
.debounce_interval = KEYS_DEBOUNCE_MS,
.code = KEY_MUTE,
.desc = "mute",
.gpio = PUSHBUTTON_MUTE,
}
};
and register this with the kernel.
And I enable the event interface and GPIO buttons when building the kernel.
Device Drivers ---> Input device support --> Event interface
Device Drivers ---> Input device support --> Keyboards --> GPIO buttons
This creates a node to the event at /dev/input/event0 to which the GPIO button events are mapped. In a system that uses only one event interface I can call poll() on the fd to /dev/input/event0 and everything works as expected.
Now, I have second peripheral on my system that uses /dev/input/event0 by default and I need to map the events from the gpio-keys driver to another event. Any suggestions on how I go about creating an event with a number/id I can specify and then map this to the gpio-keys events?
Thanks.
If you mean by "mapping" specifying the name of the /dev/input/eventX 'file', you should use Udev. The kernel assigns the number of the event device, it is a bad idea and probably impossible to try and force this number since you never know which other device may have gotten this number first.
My recommendation would be to let Udev create a symlink that points to your device; you can choose your own name and use that in your program (i.e. /dev/my_first_keypad). For example, my Wacom tablet is assigned /dev/wacom with the following udev rule:
KERNEL=="event*", SUBSYSTEM=="input", SUBSYSTEMS=="input", ATTRS{name}=="Wacom Volito", SYMLINK+="wacom"
The trick is to find the proper set of variables to exactly specify your keypad. If it is USB based, the vender/product ID are a good start. Otherwise, use udevadm info --export-db to get a full dump of the Udev database. Udev rules go in files in /etc/udev/rules.d/ or /lib/udev.d, depending on the Linux distribution you are using.
You can check System.map file for functions that register event interface. The one that comes first, usually gets lowest eventX number and later functions gets eventX number increased by one. IMO, its ok to rely on static device node file for embedded device where device configuration is static and is not going to change during operation, but generally you should use udev for you purposes.
Hi I have a SPI touch device with 24 keys, each read will return 3 bytes, containing exact all 24 keys status. My hardware is a custom made beaglebone like device, spi0 is able to read its own write by connecting MISO to MOSI.
Everything (wiring, software) is working perfectly matching this guide: http://communistcode.co.uk/blog/blogPost.php?blogPostID=1
Now my touch device is CS active high device, but it requires to drive the chip select to low and back to high before actual read. I don't seems to have control with SPI cs value, I can only either control it within an actual read phase by specifying CS high or low. From my knowledge I have to use another GPIO to emulate the CS.
Question: any way to control cs freely? (i.e. set 0 or 1 by my code directly?)
I have solved this by pinmux the CS pin to a GPIO, and control it manually prior to actual communication.