unsigned int rate_min, rate_max; /* min/max rates */
unsigned int nr_rates; /* number of rate table entries */
unsigned int *rate_table; /* rate table */
- unsigned int needs_knot; /* any unusual rates? */
};
struct snd_usb_substream;
u64 formats; /* format bitmasks (all or'ed) */
unsigned int num_formats; /* number of supported audio formats (list) */
struct list_head fmt_list; /* format list */
+ struct snd_pcm_hw_constraint_list rate_list; /* limited rates */
spinlock_t lock;
struct snd_urb_ops ops; /* callbacks (must be filled at init) */
struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ 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;
struct urb *urb)
{
unsigned char *cp = urb->transfer_buffer;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ 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;
struct urb *urb)
{
int i, offs;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
offs = 0;
urb->dev = ctx->subs->dev; /* we need to set this at each time */
}
urb->transfer_buffer_length = offs;
urb->number_of_packets = ctx->packets;
-#if 0 // for check
- if (! urb->bandwidth) {
- int bustime;
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0)
- return bustime;
- printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets);
- usb_claim_bandwidth(urb->dev, urb, bustime, 1);
- }
-#endif // for check
return 0;
}
return 0;
}
+/*
+ * Process after capture complete when paused. Nothing to do.
+ */
+static int retire_paused_capture_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ return 0;
+}
+
/*
* prepare urb for full speed playback sync pipe
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ 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;
struct snd_pcm_runtime *runtime,
struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ 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;
}
/*
- * Prepare urb for streaming before playback starts.
+ * Prepare urb for streaming before playback starts or when paused.
*
- * We don't yet have data, so we send a frame of silence.
+ * We don't have any data, so we send a frame of silence.
*/
-static int prepare_startup_playback_urb(struct snd_usb_substream *subs,
- struct snd_pcm_runtime *runtime,
- struct urb *urb)
+static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
{
unsigned int i, offs, counts;
struct snd_urb_ctx *ctx = urb->context;
unsigned int counts;
unsigned long flags;
int period_elapsed = 0;
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
stride = runtime->frame_bits >> 3;
*/
static struct snd_urb_ops audio_urb_ops[2] = {
{
- .prepare = prepare_startup_playback_urb,
+ .prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb,
.retire_sync = retire_playback_sync_urb,
static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
{
- .prepare = prepare_startup_playback_urb,
+ .prepare = prepare_nodata_playback_urb,
.retire = retire_playback_urb,
.prepare_sync = prepare_playback_sync_urb_hs,
.retire_sync = retire_playback_sync_urb_hs,
*/
static void snd_complete_urb(struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_substream *subs = ctx->subs;
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
int err = 0;
*/
static void snd_complete_sync_urb(struct urb *urb)
{
- struct snd_urb_ctx *ctx = (struct snd_urb_ctx *)urb->context;
+ struct snd_urb_ctx *ctx = urb->context;
struct snd_usb_substream *subs = ctx->subs;
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
int err = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
subs->ops.prepare = prepare_playback_urb;
return 0;
case SNDRV_PCM_TRIGGER_STOP:
return deactivate_urbs(subs, 0, 0);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->ops.prepare = prepare_nodata_playback_urb;
+ return 0;
default:
return -EINVAL;
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ subs->ops.retire = retire_capture_urb;
return start_urbs(subs, substream->runtime);
case SNDRV_PCM_TRIGGER_STOP:
return deactivate_urbs(subs, 0, 0);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->ops.retire = retire_paused_capture_urb;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->ops.retire = retire_capture_urb;
+ return 0;
default:
return -EINVAL;
}
/* close the old interface */
if (subs->interface >= 0 && subs->interface != fmt->iface) {
- usb_set_interface(subs->dev, subs->interface, 0);
+ if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
+ snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
+ dev->devnum, fmt->iface, fmt->altsetting);
+ return -EIO;
+ }
subs->interface = -1;
subs->format = 0;
}
static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data;
+ struct snd_usb_substream *subs = substream->runtime->private_data;
struct audioformat *fmt;
unsigned int channels, rate, format;
int ret, changed;
*/
static int snd_usb_hw_free(struct snd_pcm_substream *substream)
{
- struct snd_usb_substream *subs = (struct snd_usb_substream *)substream->runtime->private_data;
+ struct snd_usb_substream *subs = substream->runtime->private_data;
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
- subs->ops.prepare = prepare_startup_playback_urb;
+ subs->ops.prepare = prepare_nodata_playback_urb;
return start_urbs(subs, runtime);
} else
return 0;
}
-static struct snd_pcm_hardware snd_usb_playback =
-{
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .buffer_bytes_max = 1024 * 1024,
- .period_bytes_min = 64,
- .period_bytes_max = 512 * 1024,
- .periods_min = 2,
- .periods_max = 1024,
-};
-
-static struct snd_pcm_hardware snd_usb_capture =
+static struct snd_pcm_hardware snd_usb_hardware =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_PAUSE,
.buffer_bytes_max = 1024 * 1024,
.period_bytes_min = 64,
.period_bytes_max = 512 * 1024,
channels[f->format] |= (1 << f->channels);
rates[f->format] |= f->rates;
/* needs knot? */
- if (f->needs_knot)
+ if (f->rates & SNDRV_PCM_RATE_KNOT)
goto __out;
}
/* check whether channels and rates match for all formats */
static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
struct snd_usb_substream *subs)
{
- struct list_head *p;
- struct snd_pcm_hw_constraint_list constraints_rates;
+ struct audioformat *fp;
+ int count = 0, needs_knot = 0;
int err;
- list_for_each(p, &subs->fmt_list) {
- struct audioformat *fp;
- fp = list_entry(p, struct audioformat, list);
-
- if (!fp->needs_knot)
- continue;
-
- constraints_rates.count = fp->nr_rates;
- constraints_rates.list = fp->rate_table;
- constraints_rates.mask = 0;
-
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_rates);
-
- if (err < 0)
- return err;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+ return 0;
+ count += fp->nr_rates;
+ if (fp->rates & SNDRV_PCM_RATE_KNOT)
+ needs_knot = 1;
}
+ if (!needs_knot)
+ return 0;
+
+ subs->rate_list.count = count;
+ subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+ subs->rate_list.mask = 0;
+ count = 0;
+ list_for_each_entry(fp, &subs->fmt_list, list) {
+ int i;
+ for (i = 0; i < fp->nr_rates; i++)
+ subs->rate_list.list[count++] = fp->rate_table[i];
+ }
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &subs->rate_list);
+ if (err < 0)
+ return err;
return 0;
}
}
/* set the period time minimum 1ms */
+ /* FIXME: high-speed mode allows 125us minimum period, but many parts
+ * in the current code assume the 1ms period.
+ */
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
1000 * MIN_PACKS_URB,
/*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX);
return 0;
}
-static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction,
- struct snd_pcm_hardware *hw)
+static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
{
struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
subs->interface = -1;
subs->format = 0;
- runtime->hw = *hw;
+ runtime->hw = snd_usb_hardware;
runtime->private_data = subs;
subs->pcm_substream = substream;
return setup_hw_info(runtime, subs);
static int snd_usb_playback_open(struct snd_pcm_substream *substream)
{
- return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback);
+ return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
}
static int snd_usb_playback_close(struct snd_pcm_substream *substream)
static int snd_usb_capture_open(struct snd_pcm_substream *substream)
{
- return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture);
+ return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
}
static int snd_usb_capture_close(struct snd_pcm_substream *substream)
kfree(fp->rate_table);
kfree(fp);
}
+ kfree(subs->rate_list.list);
}
return 1;
break;
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
- return 1;
+ if (device_setup[chip->index] == 0x00 ||
+ fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+ return 1;
}
return 0;
}
unsigned char *fmt, int offset)
{
int nr_rates = fmt[offset];
- int found;
+
if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
chip->dev->devnum, fp->iface, fp->altsetting);
/*
* build the rate table and bitmap flags
*/
- int r, idx, c;
- /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
- static unsigned int conv_rates[] = {
- 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
- 64000, 88200, 96000, 176400, 192000
- };
+ int r, idx;
+ unsigned int nonzero_rates = 0;
+
fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
if (fp->rate_table == NULL) {
snd_printk(KERN_ERR "cannot malloc\n");
return -1;
}
- fp->needs_knot = 0;
fp->nr_rates = nr_rates;
fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
fp->altsetting == 5 && fp->maxpacksize == 392)
rate = 96000;
fp->rate_table[r] = rate;
+ nonzero_rates |= rate;
if (rate < fp->rate_min)
fp->rate_min = rate;
else if (rate > fp->rate_max)
fp->rate_max = rate;
- found = 0;
- for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) {
- if (rate == conv_rates[c]) {
- found = 1;
- fp->rates |= (1 << c);
- break;
- }
- }
- if (!found)
- fp->needs_knot = 1;
+ fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+ }
+ if (!nonzero_rates) {
+ hwc_debug("All rates were zero. Skipping format!\n");
+ return -1;
}
- if (fp->needs_knot)
- fp->rates |= SNDRV_PCM_RATE_KNOT;
} else {
/* continuous rates */
fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
* but we give normal PCM format to get the existing
* apps working...
*/
- pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+ switch (chip->usb_id) {
+
+ case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+ if (device_setup[chip->index] == 0x00 &&
+ fp->altsetting == 6)
+ pcm_format = SNDRV_PCM_FORMAT_S16_BE;
+ else
+ pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ default:
+ pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+ }
} else {
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
if (pcm_format < 0)
/* skip non-supported classes */
continue;
}
+ if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
+ snd_printk(KERN_ERR "low speed audio streaming not supported\n");
+ continue;
+ }
if (! parse_audio_endpoints(chip, j)) {
usb_set_interface(dev, j, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
struct audioformat *fp;
struct usb_host_interface *alts;
int stream, err;
- int *rate_table = NULL;
+ unsigned *rate_table = NULL;
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
if (! fp) {
return 0;
}
+/*
+ * Create a stream for an Edirol UA-101 interface.
+ * Copy, paste and modify from Edirol UA-1000
+ */
+static int create_ua101_quirk(struct snd_usb_audio *chip,
+ struct usb_interface *iface,
+ const struct snd_usb_audio_quirk *quirk)
+{
+ static const struct audioformat ua101_format = {
+ .format = SNDRV_PCM_FORMAT_S32_LE,
+ .fmt_type = USB_FORMAT_TYPE_I,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = 0,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ };
+ struct usb_host_interface *alts;
+ struct usb_interface_descriptor *altsd;
+ struct audioformat *fp;
+ int stream, err;
+
+ if (iface->num_altsetting != 2)
+ return -ENXIO;
+ alts = &iface->altsetting[1];
+ altsd = get_iface_desc(alts);
+ if (alts->extralen != 18 || alts->extra[1] != USB_DT_CS_INTERFACE ||
+ altsd->bNumEndpoints != 1)
+ return -ENXIO;
+
+ fp = kmemdup(&ua101_format, sizeof(*fp), GFP_KERNEL);
+ if (!fp)
+ return -ENOMEM;
+
+ fp->channels = alts->extra[11];
+ fp->iface = altsd->bInterfaceNumber;
+ fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+ fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]);
+
+ stream = (fp->endpoint & USB_DIR_IN)
+ ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+ err = add_audio_endpoint(chip, stream, fp);
+ if (err < 0) {
+ kfree(fp);
+ return err;
+ }
+ /* FIXME: playback must be synchronized to capture */
+ usb_set_interface(chip->dev, fp->iface, 0);
+ return 0;
+}
+
static int snd_usb_create_quirk(struct snd_usb_audio *chip,
struct usb_interface *iface,
const struct snd_usb_audio_quirk *quirk);
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
int iface, int altno)
{
+ /* Reset ALL ifaces to 0 altsetting.
+ * Call it for every possible altsetting of every interface.
+ */
+ usb_set_interface(chip->dev, iface, 0);
+
if (device_setup[chip->index] & AUDIOPHILE_SET) {
if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
&& altno != 6)
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
+ [QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk,
};
if (quirk->type < QUIRK_TYPE_COUNT) {
*rchip = NULL;
- if (snd_usb_get_speed(dev) != USB_SPEED_FULL &&
+ if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
+ snd_usb_get_speed(dev) != USB_SPEED_FULL &&
snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
return -ENXIO;
usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
strlcat(card->longname,
- snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" : ", high speed",
+ snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
+ snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
+ ", high speed",
sizeof(card->longname));
snd_usb_audio_create_proc(chip);