linux/sound/usb
Ricard Wanderlof ab30965d9b ALSA: usb-audio: Fix max packet size calculation for USB audio
Rounding must take place before multiplication with the frame size, since
each packet contains a whole number of frames.

We must also properly consider the data interval, as a larger data
interval will result in larger packets, which, depending on the sampling
frequency, can result in packet sizes that are less than integral
multiples of the packet size for a lower data interval.

Detailed explanation and rationale:

The code before this commit had the following expression on line 613 to
calculate the maximum isochronous packet size:

	maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
			>> (16 - ep->datainterval);

Here, ep->freqmax is the maximum assumed sample frequency, calculated from the
nominal sample frequency plus 25%. It is ultimately derived from ep->freqn,
which is in the units of frames per packet, from get_usb_full_speed_rate()
or usb_high_speed_rate(), as applicable, in Q16.16 format.

The expression essentially adds the Q16.16 equivalent of 0.999... (i.e.
the largest number less than one) to the sample rate, in order to get a
rate whose integer part is rounded up from the fractional value. The
multiplication with (frame_bits >> 3) yields the number of bytes in a
packet, and the (16 >> ep->datainterval) then converts it from Q16.16 back
to an integer, taking into consideration the bDataInterval field of the
endpoint descriptor (which describes how often isochronous packets are
transmitted relative to the (micro)frame rate (125us or 1ms, for USB high
speed and full speed, respectively)). For this discussion we will initially
assume a bDataInterval of 0, so the second line of the expression just
converts the Q16.16 value to an integer.

In order to illustrate the problem, we will set frame_bits 64, which
corresponds to a frame size of 8 bytes.

The problem here is twofold. First, the rounding operation consists
of the addition of 0x0.ffff and subsequent conversion to integer, but as the
expression stands, the conversion to integer is done after multiplication
with the frame size, rather than before. This results in the resulting
maxsize becoming too large.

Let's take an example. We have a sample rate of 96 kHz, so our ep->freqn is
0xc0000 (see usb_high_speed_rate()). Add 25% (line 612) and we get 0xf0000.
The calculated maxsize is then ((0xf0000 + 0x0ffff) * 8) >> 16 = 127 .
However, if we do the number of bytes calculation in a less obscure way it's
more apparent what the true corresponding packet size is: we get
ceil(96000 * 1.25 / 8000) * 8 = 120, where 1.25 is the 25% from line 612,
and the 8000 is the number of isochronous packets per second on a high
speed USB connection (125 us microframe interval).

This is fixed by performing the complete rounding operation prior to
multiplication with the frame rate.

The second problem is that when considering the ep->datainterval, this
must be done before rounding, in order to take the advantage of the fact
that if the number of bytes per packet is not an integer, the resulting
rounded-up integer is not necessarily a factor of two when the data
interval is increased by the same factor.

For instance, assuming a freqency of 41 kHz, the resulting
bytes-per-packet value for USB high speed is 41 kHz / 8000 = 5.125, or
0x52000 in Q16.16 format. With a data interval of 1 (ep->datainterval = 0),
this means that 6 frames per packet are needed, whereas with a data
interval of 2 we need 10.25, i.e. 11 frames needed.

Rephrasing the maxsize expression to:

	maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) *
			 (frame_bits >> 3);

for the above 96 kHz example we instead get
((0xf0000 + 0xffff) >> 16) * 8 = 120 which is the correct value.

We can also do the calculation with a non-integer sample rate which is when
rounding comes into effect: say we have 44.1 kHz (resulting ep->freqn =
0x58333, and resulting ep->freqmax 0x58333 * 1.25 = 0x6e3ff (rounded down)):

Original maxsize = ((0x6e3ff + 0xffff) * 8) << 16 = 63 (63.124.. rounded down)
True maxsize = ceil(44100 * 1.25 / 8000) * 8 = 7 * 8 = 56
New maxsize = ((0x6e3ff + 0xffff) >> 16) * 8 = 7 * 8 = 56

This is also corroborated by the wMaxPacketSize check on line 616. Assume
that wMaxPacketSize = 104, with ep->maxpacksize then having the same value.
As 104 < 127, we get maxsize = 104. ep->freqmax is then recalculated to
(104 / 8) << 16 = 0xd0000 . Putting that rate into the original maxsize
calculation yields a maxsize of ((0xd0000 + 0xffff) * 8) >> 16 = 111
(with decimals 111.99988). Clearly, we should get back the 104 here,
which we would with the new expression: ((0xd0000 + 0xffff) >> 16) * 8 = 104 .

(The error has not been a problem because it only results in maxsize being
a bit too big which just wastes a couple of bytes, either as a result of
the first maxsize calculation, or because the resulting calculation will
hit the wMaxPacketSize value before the packet is too big, resulting in
fixing the size to wMaxPacketSize even though the packet is actually not
too long.)

Tested with an Edirol UA-5 both at 44.1 kHz and 96 kHz.

Signed-off-by: Ricard Wanderlof <ricardw@axis.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-10-13 11:40:44 +02:00
..
6fire ALSA: 6fire: Convert byte_rev_table uses to bitrev8 2014-11-14 08:01:53 +01:00
bcd2000 ALSA: bcd2000: Make local data static 2015-05-26 13:00:01 +02:00
caiaq ALSA: snd-usb-caiaq: fix stream count check 2015-01-05 08:56:19 +01:00
hiface ALSA: usb: Convert to snd_card_new() with a device pointer 2014-02-12 11:18:00 +01:00
line6 ALSA: line6: Fix -EBUSY error during active monitoring 2015-07-14 15:19:37 +02:00
misc ALSA: pcm: Add snd_pcm_stop_xrun() helper 2014-11-09 18:20:40 +01:00
usx2y ALSA: usx2y: Move UAPI definition into include/uapi/sound/usb_stream.h 2015-01-28 17:33:49 +01:00
card.c ALSA: usb-audio: Handle normal and auto-suspend equally 2015-08-26 16:12:25 +02:00
card.h ALSA: usb: update trigger timestamp on first non-zero URB submitted 2015-02-09 16:02:43 +01:00
clock.c ALSA: usb-audio: Don't attempt to get Lifecam HD-5000 sample rate 2015-02-17 07:20:04 +01:00
clock.h ALSA: usb-audio: UAC2: do clock validity check earlier 2013-04-04 08:30:59 +02:00
debug.h
endpoint.c ALSA: usb-audio: Fix max packet size calculation for USB audio 2015-10-13 11:40:44 +02:00
endpoint.h ALSA: usb-audio: Pass direct struct pointer instead of list_head 2014-11-04 15:09:10 +01:00
format.c ALSA: usb-audio: Fix audio output on Roland SC-D70 sound module 2015-04-21 07:59:10 +02:00
format.h ALSA: usb-audio: store protocol version in struct audioformat 2013-06-27 21:59:47 +02:00
helper.c ALSA: usb-audio: support wireless devices in snd_usb_parse_datainterval 2013-10-07 12:52:21 +02:00
helper.h ALSA: usb-audio: increase control transfer timeout 2011-09-27 09:21:48 +02:00
Kconfig ALSA: move line6 usb driver into sound/usb 2015-01-12 22:29:57 +01:00
Makefile ALSA: line6: Split to each driver 2015-01-20 08:14:17 +01:00
midi.c ALSA: usb-audio: Allow any MIDI endpoint to drive use of interrupt transfer on newer Roland devices 2015-10-11 18:18:59 +02:00
midi.h ALSA: usb-audio: Whitespace cleanups for sound/usb/midi.* 2014-08-05 20:08:00 +02:00
mixer_maps.c ALSA: usb-audio: add dB range mapping for some devices 2015-07-29 09:28:02 +02:00
mixer_quirks.c ALSA: usb-audio: harmless underflow in snd_audigy2nx_led_put() 2015-09-28 14:33:03 +02:00
mixer_quirks.h
mixer_scarlett.c ALSA: usb-audio: Fix Scarlett 6i6 initialization typo 2014-12-18 08:39:17 +01:00
mixer_scarlett.h ALSA: usb-audio: Scarlett mixer interface for 6i6, 18i6, 18i8 and 18i20 2014-11-13 07:32:39 +01:00
mixer.c ALSA: usb-audio: correct the value cache check. 2015-08-28 10:38:25 +02:00
mixer.h ALSA: usb-audio: Fix parameter block size for UAC2 control requests 2015-08-14 16:26:50 +02:00
pcm.c ALSA: usb-audio: Avoid nested autoresume calls 2015-08-26 15:38:25 +02:00
pcm.h
power.h
proc.c ALSA: usb-audio: Avoid nested autoresume calls 2015-08-26 15:38:25 +02:00
proc.h
quirks-table.h ALSA: usb-audio: Add MIDI support for Steinberg MI2/MI4 2015-07-01 17:29:40 +02:00
quirks.c ALSA: usb: Add native DSD support for Gustard DAC-X20U 2015-08-21 10:27:35 +02:00
quirks.h ALSA: usb-audio: Don't attempt to get Lifecam HD-5000 sample rate 2015-02-17 07:20:04 +01:00
stream.c ALSA: usb-audio: Change internal PCM order 2015-09-07 10:57:27 +02:00
stream.h
usbaudio.h ALSA: usb-audio: Replace probing flag with active refcount 2015-08-26 15:40:18 +02:00