--- a/sys/dev/sound/pci/fm801.c +++ b/sys/dev/sound/pci/fm801.c @@ -614,11 +614,6 @@ fm801_init(fm801); - codec = AC97_CREATE(dev, fm801, fm801_ac97); - if (codec == NULL) goto oops; - - if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops; - fm801->irqid = 0; fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid, RF_ACTIVE | RF_SHAREABLE); @@ -638,12 +633,33 @@ goto oops; } +#define FM801_MAXPLAYCH 1 + if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; + + /* + * Only publish the mixer once no failure path remains: mixer_init() + * creates the 0666 /dev/mixerN cdev that dispatches into codec/fm801 + * (mixer.c make_dev + si_drv1), so a later goto oops would leave a + * live cdev over ac97_destroy()ed/kfree()d objects (DF-4156 family). + */ + codec = AC97_CREATE(dev, fm801, fm801_ac97); + if (codec == NULL) goto oops; + + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { + /* + * mixer_init() failed before the mixer cdev was published; + * destroy the codec inline so oops: never frees an object a + * live /dev/mixerN still points at. + */ + ac97_destroy(codec); + codec = NULL; + goto oops; + } + ksnprintf(status, 64, "at %s 0x%lx irq %ld %s", (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801)); -#define FM801_MAXPLAYCH 1 - if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801); pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801); pcm_setstatus(dev, status); @@ -655,9 +671,10 @@ oops: if (codec) ac97_destroy(codec); - if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); + /* tear the handler down before unmapping its MMIO window */ if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih); if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); + if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat); kfree(fm801, M_DEVBUF); return ENXIO;