X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=sound%2Fusb%2Fusbaudio.c;h=8fa93566570272036365009776ec0fa952c1303d;hb=7924e4f6519dd5b349ed146fe9fe35206730be67;hp=b6d886373bb001b46da476a0cb1a2f3c6d44db19;hpb=b643b0fdbc59cf6bbb086974b29d2571e9e9f646;p=linux-2.6 diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index b6d886373b..8fa9356657 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -38,7 +38,6 @@ */ -#include #include #include #include @@ -123,7 +122,6 @@ struct audioformat { 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; @@ -1309,7 +1307,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* 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; } @@ -1761,7 +1763,7 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) 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 */ @@ -1817,7 +1819,7 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) return 0; count += fp->nr_rates; - if (fp->needs_knot) + if (fp->rates & SNDRV_PCM_RATE_KNOT) needs_knot = 1; } if (!needs_knot) @@ -1878,6 +1880,9 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre } /* 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); @@ -2072,6 +2077,14 @@ static int usb_audio_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_audio_disconnect(struct usb_interface *intf); +#ifdef CONFIG_PM +static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message); +static int usb_audio_resume(struct usb_interface *intf); +#else +#define usb_audio_suspend NULL +#define usb_audio_resume NULL +#endif + static struct usb_device_id usb_audio_ids [] = { #include "usbquirks.h" { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), @@ -2086,6 +2099,8 @@ static struct usb_driver usb_audio_driver = { .name = "snd-usb-audio", .probe = usb_audio_probe, .disconnect = usb_audio_disconnect, + .suspend = usb_audio_suspend, + .resume = usb_audio_resume, .id_table = usb_audio_ids, }; @@ -2347,7 +2362,9 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat * 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; } @@ -2448,7 +2465,7 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform 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); @@ -2459,20 +2476,15 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform /* * build the rate table and bitmap flags */ - int r, idx, c; + int r, idx; unsigned int nonzero_rates = 0; - /* 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 - }; + 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) { @@ -2488,23 +2500,12 @@ static int parse_audio_format_rates(struct snd_usb_audio *chip, struct audioform 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; @@ -2527,7 +2528,18 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat * * 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) @@ -2841,6 +2853,10 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) /* 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); @@ -2860,7 +2876,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, 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) { @@ -3248,6 +3264,11 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev) 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) @@ -3378,7 +3399,8 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, *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; @@ -3452,7 +3474,9 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, 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); @@ -3639,6 +3663,45 @@ static void usb_audio_disconnect(struct usb_interface *intf) dev_get_drvdata(&intf->dev)); } +#ifdef CONFIG_PM +static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev); + struct list_head *p; + struct snd_usb_stream *as; + + if (chip == (void *)-1L) + return 0; + + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + if (!chip->num_suspended_intf++) { + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, struct snd_usb_stream, list); + snd_pcm_suspend_all(as->pcm); + } + } + + return 0; +} + +static int usb_audio_resume(struct usb_interface *intf) +{ + struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev); + + if (chip == (void *)-1L) + return 0; + if (--chip->num_suspended_intf) + return 0; + /* + * ALSA leaves material resumption to user space + * we just notify + */ + + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + + return 0; +} +#endif /* CONFIG_PM */ static int __init snd_usb_audio_init(void) {