summaryrefslogtreecommitdiff
path: root/sound/usb/endpoint.c
diff options
context:
space:
mode:
authorRicard Wanderlof <ricard.wanderlof@axis.com>2015-10-19 08:52:53 +0200
committerTakashi Iwai <tiwai@suse.de>2015-10-19 12:38:09 +0200
commite05704467736231199503e5a21c587e7ec36b829 (patch)
tree669b563156bd36cb3aeffe706917605596438949 /sound/usb/endpoint.c
parentb97a936910c8d668d25d60acbf62aea0d2ff587e (diff)
ALSA: USB-audio: Add quirk for Zoom R16/24 playback
The Zoom R16/24 have a nonstandard playback format where each isochronous packet contains a length descriptor in the first four bytes. (Curiously, capture data does not contain this and requires no quirk.) The quirk involves adding the extra length descriptor whenever outgoing isochronous packets are generated, both in pcm.c (outgoing audio) and endpoint.c (silent data). In order to make the quirk as unintrusive as possible, for pcm.c:prepare_playback_urb(), the isochronous packet descriptors are initially set up in the same way no matter if the quirk is enabled or not. Once it is time to actually copy the data into the outgoing packet buffer (together with the added length descriptors) the isochronous descriptors are adjusted in order take the increased payload length into account. For endpoint.c:prepare_silent_urb() it makes more sense to modify the actual function, partly because the function is less complex to start with and partly because it is not as time-critical as prepare_playback_urb() (whose bulk is run with interrupts disabled), so the (minute) additional time spent in the non-quirk case is motivated by the simplicity of having a single function for all cases. The quirk is controlled by the new tx_length_quirk member in struct snd_usb_substream and struct snd_usb_audio, which is conveyed to pcm.c and endpoint.c from quirks.c in a similar manner to the txfr_quirk member in the same structs. In contrast to txfr_quirk however, the quirk is enabled directly in quirks.c:create_standard_audio_quirk() by checking the USB ID in that function. Another option would be to introduce a new QUIRK_AUDIO_ZOOM_INTERFACE or somesuch, which would have made the quirk very plain to see in the quirk table, but it was felt that the additional code needed to implement it this way would just make the implementation more complex with no real gain. Tested with a Zoom R16, both by doing capture and playback separately using arecord and aplay (8 channel capture and 2 channel playback, respectively), as well as capture and playback together using Ardour, as well as Audacity and Qtractor together with jackd. The R24 is reportedly compatible with the R16 when used as an audio interface. Both devices share the same USB ID and have the same number of inputs (8) and outputs (2). Therefore "R16/24" is mentioned throughout the patch. Regression tested using an Edirol UA-5 in both class compliant (16-bit) and "advanced" (24 bit, forces the use of quirks) modes. Signed-off-by: Ricard Wanderlof <ricardw@axis.com> Tested-by: Panu Matilainen <pmatilai@laiskiainen.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/endpoint.c')
-rw-r--r--sound/usb/endpoint.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 825a06ce83a9..0cc64bd4d0a4 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -188,9 +188,17 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
{
struct urb *urb = ctx->urb;
unsigned int offs = 0;
+ unsigned int extra = 0;
+ __le32 packet_length;
int i;
+ /* For tx_length_quirk, put packet length at start of packet */
+ if (ep->chip->tx_length_quirk)
+ extra = sizeof(packet_length);
+
for (i = 0; i < ctx->packets; ++i) {
+ unsigned int offset;
+ unsigned int length;
int counts;
if (ctx->packet_size[i])
@@ -198,15 +206,22 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
else
counts = snd_usb_endpoint_next_packet_size(ep);
- urb->iso_frame_desc[i].offset = offs * ep->stride;
- urb->iso_frame_desc[i].length = counts * ep->stride;
+ length = counts * ep->stride; /* number of silent bytes */
+ offset = offs * ep->stride + extra * i;
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = length + extra;
+ if (extra) {
+ packet_length = cpu_to_le32(length);
+ memcpy(urb->transfer_buffer + offset,
+ &packet_length, sizeof(packet_length));
+ }
+ memset(urb->transfer_buffer + offset + extra,
+ ep->silence_value, length);
offs += counts;
}
urb->number_of_packets = ctx->packets;
- urb->transfer_buffer_length = offs * ep->stride;
- memset(urb->transfer_buffer, ep->silence_value,
- offs * ep->stride);
+ urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
}
/*