Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 218695
b: refs/heads/master
c: 89e1e66
h: refs/heads/master
i:
  218693: ecceffd
  218691: 1318df0
  218687: c37007c
v: v3
  • Loading branch information
Clemens Ladisch authored and Takashi Iwai committed Oct 27, 2010
1 parent 230ccfd commit 4c8679d
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 113 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0d040df9984c8fcb6a777a8f6d5dc513eaefd2de
refs/heads/master: 89e1e66d6be8a520cdcd26043cda2cc870a34015
2 changes: 2 additions & 0 deletions trunk/sound/usb/card.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ struct snd_usb_substream {
unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
int freqshift; /* how much to shift the feedback value to get Q16.16 */
unsigned int freqmax; /* maximum sampling rate, used for buffer management */
unsigned int phase; /* phase accumulator */
unsigned int maxpacksize; /* max packet size in bytes */
unsigned int maxframesize; /* max packet size in frames */
unsigned int curpacksize; /* current packet size in bytes (for capture) */
unsigned int curframesize; /* current packet size in frames (for capture) */
unsigned int syncmaxsize; /* sync endpoint packet size */
unsigned int fill_max: 1; /* fill max packet size always */
unsigned int txfr_quirk:1; /* allow sub-frame alignment */
unsigned int fmt_type; /* USB audio format type (1-3) */
Expand Down
2 changes: 2 additions & 0 deletions trunk/sound/usb/pcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->datainterval = fmt->datainterval;
subs->syncpipe = subs->syncinterval = 0;
subs->maxpacksize = fmt->maxpacksize;
subs->syncmaxsize = 0;
subs->fill_max = 0;

/* we need a sync pipe in async OUT or adaptive IN mode */
Expand Down Expand Up @@ -283,6 +284,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
else
subs->syncinterval = 3;
subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
}

/* always fill max packet size */
Expand Down
5 changes: 5 additions & 0 deletions trunk/sound/usb/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn
? get_full_speed_hz(subs->freqm)
: get_high_speed_hz(subs->freqm),
subs->freqm >> 16, subs->freqm & 0xffff);
if (subs->freqshift != INT_MIN)
snd_iprintf(buffer, " Feedback Format = %d.%d\n",
(subs->syncmaxsize > 3 ? 32 : 24)
- (16 - subs->freqshift),
16 - subs->freqshift);
} else {
snd_iprintf(buffer, " Status: Stop\n");
}
Expand Down
170 changes: 58 additions & 112 deletions trunk/sound/usb/urb.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
else
subs->freqn = get_usb_high_speed_rate(rate);
subs->freqm = subs->freqn;
subs->freqshift = INT_MIN;
/* calculate max. frequency */
if (subs->maxpacksize) {
/* whatever fits into a max. size packet */
Expand Down Expand Up @@ -513,115 +514,89 @@ static int retire_paused_capture_urb(struct snd_usb_substream *subs,


/*
* prepare urb for full speed playback sync pipe
* prepare urb for playback sync pipe
*
* set up the offset and length to receive the current frequency.
*/

static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;

urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 3;
urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
urb->iso_frame_desc[0].offset = 0;
return 0;
}

/*
* prepare urb for high speed playback sync pipe
* process after playback sync complete
*
* set up the offset and length to receive the current frequency.
*/

static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
struct snd_urb_ctx *ctx = urb->context;

urb->dev = ctx->subs->dev; /* we need to set this at each time */
urb->iso_frame_desc[0].length = 4;
urb->iso_frame_desc[0].offset = 0;
return 0;
}

/*
* process after full speed playback sync complete
*
* retrieve the current 10.14 frequency from pipe, and set it.
* the value is referred in prepare_playback_urb().
* Full speed devices report feedback values in 10.14 format as samples per
* frame, high speed devices in 16.16 format as samples per microframe.
* Because the Audio Class 1 spec was written before USB 2.0, many high speed
* devices use a wrong interpretation, some others use an entirely different
* format. Therefore, we cannot predict what format any particular device uses
* and must detect it automatically.
*/
static int retire_playback_sync_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int f;
int shift;
unsigned long flags;

if (urb->iso_frame_desc[0].status == 0 &&
urb->iso_frame_desc[0].actual_length == 3) {
f = combine_triple((u8*)urb->transfer_buffer) << 2;
if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
}
}

return 0;
}
if (urb->iso_frame_desc[0].status != 0 ||
urb->iso_frame_desc[0].actual_length < 3)
return 0;

/*
* process after high speed playback sync complete
*
* retrieve the current 12.13 frequency from pipe, and set it.
* the value is referred in prepare_playback_urb().
*/
static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int f;
unsigned long flags;
f = le32_to_cpup(urb->transfer_buffer);
if (urb->iso_frame_desc[0].actual_length == 3)
f &= 0x00ffffff;
else
f &= 0x0fffffff;
if (f == 0)
return 0;

if (urb->iso_frame_desc[0].status == 0 &&
urb->iso_frame_desc[0].actual_length == 4) {
f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
if (unlikely(subs->freqshift == INT_MIN)) {
/*
* The first time we see a feedback value, determine its format
* by shifting it left or right until it matches the nominal
* frequency value. This assumes that the feedback does not
* differ from the nominal value more than +50% or -25%.
*/
shift = 0;
while (f < subs->freqn - subs->freqn / 4) {
f <<= 1;
shift++;
}
while (f > subs->freqn + subs->freqn / 2) {
f >>= 1;
shift--;
}
subs->freqshift = shift;
}
else if (subs->freqshift >= 0)
f <<= subs->freqshift;
else
f >>= -subs->freqshift;

return 0;
}

/*
* process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
*
* These devices return the number of samples per packet instead of the number
* of samples per microframe.
*/
static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
unsigned int f;
unsigned long flags;

if (urb->iso_frame_desc[0].status == 0 &&
urb->iso_frame_desc[0].actual_length == 4) {
f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
f >>= subs->datainterval;
if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
}
if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
/*
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
*/
spin_lock_irqsave(&subs->lock, flags);
subs->freqm = f;
spin_unlock_irqrestore(&subs->lock, flags);
} else {
/*
* Out of range; maybe the shift value is wrong.
* Reset it so that we autodetect again the next time.
*/
subs->freqshift = INT_MIN;
}

return 0;
Expand Down Expand Up @@ -878,21 +853,6 @@ static struct snd_urb_ops audio_urb_ops[2] = {
},
};

static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
{
.prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb_hs,
.retire_sync = retire_playback_sync_urb_hs,
},
{
.prepare = prepare_capture_urb,
.retire = retire_capture_urb,
.prepare_sync = prepare_capture_sync_urb_hs,
.retire_sync = retire_capture_sync_urb,
},
};

/*
* initialize the substream instance.
*/
Expand All @@ -909,23 +869,9 @@ void snd_usb_init_substream(struct snd_usb_stream *as,
subs->direction = stream;
subs->dev = as->chip->dev;
subs->txfr_quirk = as->chip->txfr_quirk;
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
subs->ops = audio_urb_ops[stream];
} else {
subs->ops = audio_urb_ops_high_speed[stream];
switch (as->chip->usb_id) {
case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
break;
case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
subs->ops.prepare_sync = prepare_playback_sync_urb;
subs->ops.retire_sync = retire_playback_sync_urb;
break;
}
}
subs->ops = audio_urb_ops[stream];
if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
subs->ops.prepare_sync = prepare_capture_sync_urb_hs;

snd_usb_set_pcm_ops(as->pcm, stream);

Expand Down

0 comments on commit 4c8679d

Please sign in to comment.