From b769f49463711205d57286e64cf535ed4daf59e9 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Wed, 23 Mar 2011 10:53:41 -0400 Subject: [PATCH 01/11] sound/oss: remove offset from load_patch callbacks Was: [PATCH] sound/oss/midi_synth: prevent underflow, use of uninitialized value, and signedness issue The offset passed to midi_synth_load_patch() can be essentially arbitrary. If it's greater than the header length, this will result in a copy_from_user(dst, src, negative_val). While this will just return -EFAULT on x86, on other architectures this may cause memory corruption. Additionally, the length field of the sysex_info structure may not be initialized prior to its use. Finally, a signed comparison may result in an unintentionally large loop. On suggestion by Takashi Iwai, version two removes the offset argument from the load_patch callbacks entirely, which also resolves similar issues in opl3. Compile tested only. v3 adjusts comments and hopefully gets copy offsets right. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai --- sound/oss/dev_table.h | 2 +- sound/oss/midi_synth.c | 30 +++++++++++++----------------- sound/oss/midi_synth.h | 2 +- sound/oss/opl3.c | 8 ++------ sound/oss/sequencer.c | 2 +- 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h index b7617bee6388..0199a317c5a9 100644 --- a/sound/oss/dev_table.h +++ b/sound/oss/dev_table.h @@ -271,7 +271,7 @@ struct synth_operations void (*reset) (int dev); void (*hw_control) (int dev, unsigned char *event); int (*load_patch) (int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag); + int count, int pmgr_flag); void (*aftertouch) (int dev, int voice, int pressure); void (*controller) (int dev, int voice, int ctrl_num, int value); void (*panning) (int dev, int voice, int value); diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c index 3c09374ea5bf..2292c230d7e6 100644 --- a/sound/oss/midi_synth.c +++ b/sound/oss/midi_synth.c @@ -476,7 +476,7 @@ EXPORT_SYMBOL(midi_synth_hw_control); int midi_synth_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) + int count, int pmgr_flag) { int orig_dev = synth_devs[dev]->midi_dev; @@ -491,33 +491,29 @@ midi_synth_load_patch(int dev, int format, const char __user *addr, if (!prefix_cmd(orig_dev, 0xf0)) return 0; + /* Invalid patch format */ if (format != SYSEX_PATCH) - { -/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ return -EINVAL; - } + + /* Patch header too short */ if (count < hdr_size) - { -/* printk("MIDI Error: Patch header too short\n");*/ return -EINVAL; - } + count -= hdr_size; /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. + * Copy the header from user space */ - if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) + if (copy_from_user(&sysex, addr, hdr_size)) return -EFAULT; - - if (count < sysex.len) - { -/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ + + /* Sysex record too short */ + if ((unsigned)count < (unsigned)sysex.len) sysex.len = count; - } - left = sysex.len; - src_offs = 0; + + left = sysex.len; + src_offs = 0; for (i = 0; i < left && !signal_pending(current); i++) { diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h index 6bc9d00bc77c..b64ddd6c4abc 100644 --- a/sound/oss/midi_synth.h +++ b/sound/oss/midi_synth.h @@ -8,7 +8,7 @@ int midi_synth_open (int dev, int mode); void midi_synth_close (int dev); void midi_synth_hw_control (int dev, unsigned char *event); int midi_synth_load_patch (int dev, int format, const char __user * addr, - int offs, int count, int pmgr_flag); + int count, int pmgr_flag); void midi_synth_panning (int dev, int channel, int pressure); void midi_synth_aftertouch (int dev, int channel, int pressure); void midi_synth_controller (int dev, int channel, int ctrl_num, int value); diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index 938c48c43585..cbf957424d5c 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -820,7 +820,7 @@ static void opl3_hw_control(int dev, unsigned char *event) } static int opl3_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) + int count, int pmgr_flag) { struct sbi_instrument ins; @@ -830,11 +830,7 @@ static int opl3_load_patch(int dev, int format, const char __user *addr, return -EINVAL; } - /* - * What the fuck is going on here? We leave junk in the beginning - * of ins and then check the field pretty close to that beginning? - */ - if(copy_from_user(&((char *) &ins)[offs], addr + offs, sizeof(ins) - offs)) + if (copy_from_user(&ins, addr, sizeof(ins))) return -EFAULT; if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c index 5ea1098ac427..30bcfe470f83 100644 --- a/sound/oss/sequencer.c +++ b/sound/oss/sequencer.c @@ -241,7 +241,7 @@ int sequencer_write(int dev, struct file *file, const char __user *buf, int coun return -ENXIO; fmt = (*(short *) &event_rec[0]) & 0xffff; - err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); + err = synth_devs[dev]->load_patch(dev, fmt, buf + p, c, 0); if (err < 0) return err; From 4d00135a680727f6c3be78f8befaac009030e4df Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Wed, 23 Mar 2011 11:42:57 -0400 Subject: [PATCH 02/11] sound/oss/opl3: validate voice and channel indexes User-controllable indexes for voice and channel values may cause reading and writing beyond the bounds of their respective arrays, leading to potentially exploitable memory corruption. Validate these indexes. Signed-off-by: Dan Rosenberg Cc: stable@kernel.org Signed-off-by: Takashi Iwai --- sound/oss/opl3.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index cbf957424d5c..407cd677950b 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -845,6 +845,10 @@ static int opl3_load_patch(int dev, int format, const char __user *addr, static void opl3_panning(int dev, int voice, int value) { + + if (voice < 0 || voice >= devc->nr_voice) + return; + devc->voc[voice].panning = value; } @@ -1062,8 +1066,15 @@ static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info static void opl3_setup_voice(int dev, int voice, int chn) { - struct channel_info *info = - &synth_devs[dev]->chn_info[chn]; + struct channel_info *info; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + if (chn < 0 || chn > 15) + return; + + info = &synth_devs[dev]->chn_info[chn]; opl3_set_instr(dev, voice, info->pgm_num); From e19869204fca4ae28b6e4d8f5e20849e9f7b18bd Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Thu, 17 Feb 2011 14:26:51 +0100 Subject: [PATCH 03/11] ALSA: usb-audio: add Cakewalk UM-1G support Add a quirk for the Cakewalk UM-1G USB MIDI interface in "advanced driver" mode. (It already works in standard mode.) Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c0dcfca9b5b5..196c75328561 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1567,6 +1567,19 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x0104), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "Roland", */ + /* .product_name = "UM-1G", */ + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, { /* has ID 0x0110 when not in Advanced Driver mode */ USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f), From cb6f4b55f5907528d8a1a927b850c9eb04d4ef90 Mon Sep 17 00:00:00 2001 From: "Keith A. Milner" Date: Mon, 21 Mar 2011 20:15:08 +0000 Subject: [PATCH 04/11] ALSA: usb-audio - Support for Boss JS-8 Jam Station Signed-off-by: Keith A. Milner Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 196c75328561..c66d3f64dcf8 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1580,6 +1580,33 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* Boss JS-8 Jam Station */ + USB_DEVICE(0x0582, 0x0109), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "BOSS", */ + /* .product_name = "JS-8", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, { /* has ID 0x0110 when not in Advanced Driver mode */ USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f), From 20b67dddcc5f29d3d0c900225d85e0ac655bc69d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Mar 2011 22:54:32 +0100 Subject: [PATCH 05/11] ALSA: hda - Fix SPDIF out regression on ALC889 The commit 5a8cfb4e8ae317d283f84122ed20faa069c5e0c4 ALSA: hda - Use ALC_INIT_DEFAULT for really default initialization changed to use the default initialization method for ALC889, but this caused a regression on SPDIF output on some machines. This seems due to the COEF setup included in the default init procedure. For making SPDIF working again, the COEF-setup has to be avoided for the id 0889. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=24342 Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5d582de91c19..0ef0035fe99f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1290,7 +1290,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case 0x10ec0883: case 0x10ec0885: case 0x10ec0887: - case 0x10ec0889: + /*case 0x10ec0889:*/ /* this causes an SPDIF problem */ alc889_coef_init(codec); break; case 0x10ec0888: From 3674f19dabd15f9541079a588149a370d888f4e6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 25 Mar 2011 17:51:54 +1100 Subject: [PATCH 06/11] ALSA: vmalloc buffers should use normal mmap It's a big no-no to use pgprot_noncached() when mmap'ing such buffers into userspace since they are mapped cachable in kernel space. This can cause all sort of interesting things ranging from to garbled sound to lockups on various architectures. I've observed that usb-audio is broken on powerpc 4xx for example because of that. Also remove the now unused snd_pcm_lib_mmap_noncached(). It's an arch business to know when to use uncached mappings, there's already hacks for MIPS inside snd_pcm_default_mmap() and other archs are supposed to use dma_mmap_coherent(). (See my separate patch that adds dma_mmap_coherent() to powerpc) Signed-off-by: Benjamin Herrenschmidt CC: Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 4 +--- sound/core/pcm_native.c | 9 --------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 430a9cc045e2..e1bad1130616 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1031,9 +1031,7 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s #define snd_pcm_lib_mmap_iomem NULL #endif -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, - struct vm_area_struct *area); -#define snd_pcm_lib_mmap_vmalloc snd_pcm_lib_mmap_noncached +#define snd_pcm_lib_mmap_vmalloc NULL static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ae42b6509ce4..fe5c8036beba 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3201,15 +3201,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ -/* mmap callback with pgprot_noncached */ -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - area->vm_page_prot = pgprot_noncached(area->vm_page_prot); - return snd_pcm_default_mmap(substream, area); -} -EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached); - /* * mmap DMA buffer */ From 677cd904aba939bc4cfdc3c1eada8ec46582127e Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 7 Feb 2011 15:19:34 +0100 Subject: [PATCH 07/11] ALSA: HDA: New AD1984A model for Dell Precision R5500 For codec AD1984A, add a new model to support Dell Precision R5500 or the microphone jack won't work correctly. BugLink: http://bugs.launchpad.net/bugs/741516 Tested-by: Kent Baxley Cc: stable@kernel.org Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 734c6ee55d8a..2942d2a9ea10 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -4255,6 +4255,84 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec) return 0; } +/* + * Precision R5500 + * 0x12 - HP/line-out + * 0x13 - speaker (mono) + * 0x15 - mic-in + */ + +static struct hda_verb ad1984a_precision_verbs[] = { + /* Unmute main output path */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */ + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Select mic as input */ + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */ + /* Configure as mic */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* HP unmute */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* turn on EAPD */ + {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + /* unsolicited event for pin-sense */ + {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_precision_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + { } /* end */ +}; + + +/* mute internal speaker if HP is plugged */ +static void ad1984a_precision_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_jack_detect(codec, 0x12); + snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + + +/* unsolicited event for HP jack sensing */ +static void ad1984a_precision_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1984a_precision_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1984a_precision_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1984a_precision_automute(codec); + return 0; +} + + /* * HP Touchsmart * port-A (0x11) - front hp-out @@ -4384,6 +4462,7 @@ enum { AD1884A_MOBILE, AD1884A_THINKPAD, AD1984A_TOUCHSMART, + AD1984A_PRECISION, AD1884A_MODELS }; @@ -4393,9 +4472,11 @@ static const char * const ad1884a_models[AD1884A_MODELS] = { [AD1884A_MOBILE] = "mobile", [AD1884A_THINKPAD] = "thinkpad", [AD1984A_TOUCHSMART] = "touchsmart", + [AD1984A_PRECISION] = "precision", }; static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION), SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), @@ -4489,6 +4570,14 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; codec->patch_ops.init = ad1984a_thinkpad_init; break; + case AD1984A_PRECISION: + spec->mixers[0] = ad1984a_precision_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1984a_precision_verbs; + spec->multiout.dig_out_nid = 0; + codec->patch_ops.unsol_event = ad1984a_precision_unsol_event; + codec->patch_ops.init = ad1984a_precision_init; + break; case AD1984A_TOUCHSMART: spec->mixers[0] = ad1984a_touchsmart_mixers; spec->init_verbs[0] = ad1984a_touchsmart_verbs; From 7bf76c33e9a1ecb2a15f1a066d4e032b5d0922a7 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Fri, 25 Mar 2011 15:25:46 +1300 Subject: [PATCH 08/11] ALSA: asihpi - Support single-rate no-SRC cards Cards without settable local samplerate and without SRC still must have a valid samplerate. This fixed rate is determined by reading the current rate for the card. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 0ac1f98d91a1..22606e3b08f4 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -381,13 +381,13 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, "No local sampleclock, err %d\n", err); } - for (idx = 0; idx < 100; idx++) { - if (hpi_sample_clock_query_local_rate( - h_control, idx, &sample_rate)) { - if (!idx) - snd_printk(KERN_ERR - "Local rate query failed\n"); - + for (idx = -1; idx < 100; idx++) { + if (idx == -1) { + if (hpi_sample_clock_get_sample_rate(h_control, + &sample_rate)) + continue; + } else if (hpi_sample_clock_query_local_rate(h_control, + idx, &sample_rate)) { break; } @@ -440,8 +440,6 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, } } - /* printk(KERN_INFO "Supported rates %X %d %d\n", - rates, rate_min, rate_max); */ pcmhw->rates = rates; pcmhw->rate_min = rate_min; pcmhw->rate_max = rate_max; From 26aebef420f8036213419b8a46e3a07db51439cd Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Fri, 25 Mar 2011 15:25:47 +1300 Subject: [PATCH 09/11] ALSA: asihpi - Improve non-busmaster adapter operation Make playback silence callback a no-op, card automatically outputs silence when written data runs out. Increasing update interval and thus minimum period avoids xrun on startup or because of timer jitter. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 22606e3b08f4..c90d77ad681b 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1012,6 +1012,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, card->update_interval_frames); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, card->update_interval_frames * 2, UINT_MAX); @@ -1054,7 +1055,7 @@ static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream, hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream, runtime->dma_area, len, &dpcm->format)); - dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len; + dpcm->pcm_buf_host_rw_ofs += len; return 0; } @@ -1064,16 +1065,11 @@ static int snd_card_asihpi_playback_silence(struct snd_pcm_substream * snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - unsigned int len; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - - len = frames_to_bytes(runtime, count); - VPRINTK1(KERN_INFO "playback silence %u bytes\n", len); - - memset(runtime->dma_area, 0, len); - hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream, - runtime->dma_area, len, &dpcm->format)); + /* Usually writes silence to DMA buffer, which should be overwritten + by real audio later. Our fifos cannot be overwritten, and are not + free-running DMAs. Silence is output on fifo underflow. + This callback is still required to allow the copy callback to be used. + */ return 0; } @@ -2885,6 +2881,9 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, if (err) asihpi->update_interval_frames = 512; + if (!asihpi->support_mmap) + asihpi->update_interval_frames *= 2; + hpi_handle_error(hpi_instream_open(asihpi->adapter_index, 0, &h_stream)); From b2e65c8e9133218eb28c30e79ddd3d66d4666ba0 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Fri, 25 Mar 2011 15:25:48 +1300 Subject: [PATCH 10/11] ALSA: asihpi - Update verbose debug print macros Replace local VPRINTK1 with snd_printdd. Create local snd_printddd instead of VPRINTK2 for most verbose debug. In most cases let snd_printk supply default level for messages. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 100 ++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 53 deletions(-) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index c90d77ad681b..f53a31e939c1 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -22,21 +22,6 @@ * for any purpose including commercial applications. */ -/* >0: print Hw params, timer vars. >1: print stream write/copy sizes */ -#define REALLY_VERBOSE_LOGGING 0 - -#if REALLY_VERBOSE_LOGGING -#define VPRINTK1 snd_printd -#else -#define VPRINTK1(...) -#endif - -#if REALLY_VERBOSE_LOGGING > 1 -#define VPRINTK2 snd_printd -#else -#define VPRINTK2(...) -#endif - #include "hpi_internal.h" #include "hpimsginit.h" #include "hpioctl.h" @@ -57,11 +42,25 @@ #include #include - MODULE_LICENSE("GPL"); MODULE_AUTHOR("AudioScience inc. "); MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx"); +#if defined CONFIG_SND_DEBUG_VERBOSE +/** + * snd_printddd - very verbose debug printk + * @format: format string + * + * Works like snd_printk() for debugging purposes. + * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. + * Must set snd module debug parameter to 3 to enable at runtime. + */ +#define snd_printddd(format, args...) \ + __snd_printk(3, __FILE__, __LINE__, format, ##args) +#else +#define snd_printddd(format, args...) do { } while (0) +#endif + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; @@ -289,7 +288,6 @@ static u16 handle_error(u16 err, int line, char *filename) #define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__) /***************************** GENERAL PCM ****************/ -#if REALLY_VERBOSE_LOGGING static void print_hwparams(struct snd_pcm_hw_params *p) { snd_printd("HWPARAMS \n"); @@ -304,9 +302,6 @@ static void print_hwparams(struct snd_pcm_hw_params *p) snd_printd("periods %d \n", params_periods(p)); snd_printd("buffer_size %d \n", params_buffer_size(p)); } -#else -#define print_hwparams(x) -#endif static snd_pcm_format_t hpi_to_alsa_formats[] = { -1, /* INVALID */ @@ -464,7 +459,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, if (err) return err; - VPRINTK1(KERN_INFO "format %d, %d chans, %d_hz\n", + snd_printdd("format %d, %d chans, %d_hz\n", format, params_channels(params), params_rate(params)); @@ -487,13 +482,12 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, err = hpi_stream_host_buffer_attach(dpcm->h_stream, params_buffer_bytes(params), runtime->dma_addr); if (err == 0) { - VPRINTK1(KERN_INFO + snd_printdd( "stream_host_buffer_attach succeeded %u %lu\n", params_buffer_bytes(params), (unsigned long)runtime->dma_addr); } else { - snd_printd(KERN_INFO - "stream_host_buffer_attach error %d\n", + snd_printd("stream_host_buffer_attach error %d\n", err); return -ENOMEM; } @@ -502,7 +496,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, &dpcm->hpi_buffer_attached, NULL, NULL, NULL); - VPRINTK1(KERN_INFO "stream_host_buffer_attach status 0x%x\n", + snd_printdd("stream_host_buffer_attach status 0x%x\n", dpcm->hpi_buffer_attached); } bytes_per_sec = params_rate(params) * params_channels(params); @@ -515,7 +509,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, dpcm->bytes_per_sec = bytes_per_sec; dpcm->buffer_bytes = params_buffer_bytes(params); dpcm->period_bytes = params_period_bytes(params); - VPRINTK1(KERN_INFO "buffer_bytes=%d, period_bytes=%d, bps=%d\n", + snd_printdd("buffer_bytes=%d, period_bytes=%d, bps=%d\n", dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec); return 0; @@ -571,7 +565,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, struct snd_pcm_substream *s; u16 e; - VPRINTK1(KERN_INFO "%c%d trigger\n", + snd_printdd("%c%d trigger\n", SCHR(substream->stream), substream->number); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -595,7 +589,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, * data?? */ unsigned int preload = ds->period_bytes * 1; - VPRINTK2(KERN_INFO "%d preload x%x\n", s->number, preload); + snd_printddd("%d preload x%x\n", s->number, preload); hpi_handle_error(hpi_outstream_write_buf( ds->h_stream, &runtime->dma_area[0], @@ -605,7 +599,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } if (card->support_grouping) { - VPRINTK1(KERN_INFO "\t%c%d group\n", + snd_printdd("\t%c%d group\n", SCHR(s->stream), s->number); e = hpi_stream_group_add( @@ -620,7 +614,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } else break; } - VPRINTK1(KERN_INFO "start\n"); + snd_printdd("start\n"); /* start the master stream */ snd_card_asihpi_pcm_timer_start(substream); if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || @@ -642,14 +636,14 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, s->runtime->status->state = SNDRV_PCM_STATE_SETUP; if (card->support_grouping) { - VPRINTK1(KERN_INFO "\t%c%d group\n", + snd_printdd("\t%c%d group\n", SCHR(s->stream), s->number); snd_pcm_trigger_done(s, substream); } else break; } - VPRINTK1(KERN_INFO "stop\n"); + snd_printdd("stop\n"); /* _prepare and _hwparams reset the stream */ hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); @@ -662,12 +656,12 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - VPRINTK1(KERN_INFO "pause release\n"); + snd_printdd("pause release\n"); hpi_handle_error(hpi_stream_start(dpcm->h_stream)); snd_card_asihpi_pcm_timer_start(substream); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - VPRINTK1(KERN_INFO "pause\n"); + snd_printdd("pause\n"); snd_card_asihpi_pcm_timer_stop(substream); hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); break; @@ -739,7 +733,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) u16 state; u32 buffer_size, bytes_avail, samples_played, on_card_bytes; - VPRINTK1(KERN_INFO "%c%d snd_card_asihpi_timer_function\n", + snd_printdd("%c%d snd_card_asihpi_timer_function\n", SCHR(substream->stream), substream->number); /* find minimum newdata and buffer pos in group */ @@ -768,10 +762,10 @@ static void snd_card_asihpi_timer_function(unsigned long data) if ((bytes_avail == 0) && (on_card_bytes < ds->pcm_buf_host_rw_ofs)) { hpi_handle_error(hpi_stream_start(ds->h_stream)); - VPRINTK1(KERN_INFO "P%d start\n", s->number); + snd_printdd("P%d start\n", s->number); } } else if (state == HPI_STATE_DRAINED) { - VPRINTK1(KERN_WARNING "P%d drained\n", + snd_printd(KERN_WARNING "P%d drained\n", s->number); /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); continue; */ @@ -792,13 +786,13 @@ static void snd_card_asihpi_timer_function(unsigned long data) newdata); } - VPRINTK1(KERN_INFO "PB timer hw_ptr x%04lX, appl_ptr x%04lX\n", + snd_printdd("hw_ptr x%04lX, appl_ptr x%04lX\n", (unsigned long)frames_to_bytes(runtime, runtime->status->hw_ptr), (unsigned long)frames_to_bytes(runtime, runtime->control->appl_ptr)); - VPRINTK1(KERN_INFO "%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X," + snd_printdd("%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X," " aux=x%04X space=x%04X\n", loops, SCHR(s->stream), s->number, state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail, @@ -820,7 +814,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) next_jiffies = max(next_jiffies, 1U); dpcm->timer.expires = jiffies + next_jiffies; - VPRINTK1(KERN_INFO "jif %d buf pos x%04X newdata x%04X xfer x%04X\n", + snd_printdd("jif %d buf pos x%04X newdata x%04X xfer x%04X\n", next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); snd_pcm_group_for_each_entry(s, substream) { @@ -835,7 +829,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) if (xfercount && (on_card_bytes <= ds->period_bytes)) { if (card->support_mmap) { if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - VPRINTK2(KERN_INFO "P%d write x%04x\n", + snd_printddd("P%d write x%04x\n", s->number, ds->period_bytes); hpi_handle_error( @@ -846,7 +840,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) xfercount, &ds->format)); } else { - VPRINTK2(KERN_INFO "C%d read x%04x\n", + snd_printddd("C%d read x%04x\n", s->number, xfercount); hpi_handle_error( @@ -869,7 +863,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { - /* snd_printd(KERN_INFO "Playback ioctl %d\n", cmd); */ + snd_printdd(KERN_INFO "Playback ioctl %d\n", cmd); return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -879,7 +873,7 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - VPRINTK1(KERN_INFO "playback prepare %d\n", substream->number); + snd_printdd("playback prepare %d\n", substream->number); hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); dpcm->pcm_buf_host_rw_ofs = 0; @@ -896,7 +890,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) snd_pcm_uframes_t ptr; ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); - /* VPRINTK2(KERN_INFO "playback_pointer=x%04lx\n", (unsigned long)ptr); */ + snd_printddd("playback_pointer=x%04lx\n", (unsigned long)ptr); return ptr; } @@ -1018,7 +1012,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); - VPRINTK1(KERN_INFO "playback open\n"); + snd_printdd("playback open\n"); return 0; } @@ -1029,7 +1023,7 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) struct snd_card_asihpi_pcm *dpcm = runtime->private_data; hpi_handle_error(hpi_outstream_close(dpcm->h_stream)); - VPRINTK1(KERN_INFO "playback close\n"); + snd_printdd("playback close\n"); return 0; } @@ -1049,7 +1043,7 @@ static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream, if (copy_from_user(runtime->dma_area, src, len)) return -EFAULT; - VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n", + snd_printddd("playback copy%d %u bytes\n", substream->number, len); hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream, @@ -1104,7 +1098,7 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - VPRINTK2(KERN_INFO "capture pointer %d=%d\n", + snd_printddd("capture pointer %d=%d\n", substream->number, dpcm->pcm_buf_dma_ofs); /* NOTE Unlike playback can't use actual samples_played for the capture position, because those samples aren't yet in @@ -1129,7 +1123,7 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) dpcm->pcm_buf_dma_ofs = 0; dpcm->pcm_buf_elapsed_dma_ofs = 0; - VPRINTK1("Capture Prepare %d\n", substream->number); + snd_printdd("Capture Prepare %d\n", substream->number); return 0; } @@ -1192,7 +1186,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) if (dpcm == NULL) return -ENOMEM; - VPRINTK1("hpi_instream_open adapter %d stream %d\n", + snd_printdd("capture open adapter %d stream %d\n", card->adapter_index, substream->number); err = hpi_handle_error( @@ -1262,7 +1256,7 @@ static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream, len = frames_to_bytes(runtime, count); - VPRINTK2(KERN_INFO "capture copy%d %d bytes\n", substream->number, len); + snd_printddd("capture copy%d %d bytes\n", substream->number, len); hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream, runtime->dma_area, len)); @@ -2906,7 +2900,6 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->support_mrx ); - err = snd_card_asihpi_pcm_new(asihpi, 0, pcm_substreams); if (err < 0) { snd_printk(KERN_ERR "pcm_new failed\n"); @@ -2941,6 +2934,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, sprintf(card->longname, "%s %i", card->shortname, asihpi->adapter_index); err = snd_card_register(card); + if (!err) { hpi_card->snd_card_asihpi = card; dev++; From a45e3d6b13e97506b616980c0f122c3389bcefa4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Mar 2011 09:50:15 +0100 Subject: [PATCH 11/11] ALSA: Fix yet another race in disconnection This patch fixes a race between snd_card_file_remove() and snd_card_disconnect(). When the card is added to shutdown_files list in snd_card_disconnect(), but it's freed in snd_card_file_remove() at the same time, the shutdown_files list gets corrupted. The list member must be freed in snd_card_file_remove() as well. Reported-and-tested-by: Russ Dill Cc: Signed-off-by: Takashi Iwai --- sound/core/init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/init.c b/sound/core/init.c index 3e65da21a08c..a0080aa45ae9 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -848,6 +848,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file) return -ENOMEM; mfile->file = file; mfile->disconnected_f_op = NULL; + INIT_LIST_HEAD(&mfile->shutdown_list); spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); @@ -883,6 +884,9 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) list_for_each_entry(mfile, &card->files_list, list) { if (mfile->file == file) { list_del(&mfile->list); + spin_lock(&shutdown_lock); + list_del(&mfile->shutdown_list); + spin_unlock(&shutdown_lock); if (mfile->disconnected_f_op) fops_put(mfile->disconnected_f_op); found = mfile;