mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-22 00:04:58 +00:00
Fix a panic when kldloading a sound driver. Do this by replacing the
link-list of dev_t's with named variables. Remove used code. Approved by: tanimura (mentor)
This commit is contained in:
parent
28466ae036
commit
5ee30e277a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=124740
|
@ -41,7 +41,7 @@ static d_ioctl_t dsp_ioctl;
|
|||
static d_poll_t dsp_poll;
|
||||
static d_mmap_t dsp_mmap;
|
||||
|
||||
static struct cdevsw dsp_cdevsw = {
|
||||
struct cdevsw dsp_cdevsw = {
|
||||
.d_open = dsp_open,
|
||||
.d_close = dsp_close,
|
||||
.d_read = dsp_read,
|
||||
|
@ -1044,101 +1044,23 @@ dsp_mmap(dev_t i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_register(int unit, int channel)
|
||||
{
|
||||
dev_t dt;
|
||||
int r;
|
||||
|
||||
dt = make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel);
|
||||
r = pcm_regdevt(dt, unit, SND_DEV_DSP, channel);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dt = make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel);
|
||||
r = pcm_regdevt(dt, unit, SND_DEV_DSP16, channel);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
dt = make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel);
|
||||
r = pcm_regdevt(dt, unit, SND_DEV_AUDIO, channel);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_registerrec(int unit, int channel)
|
||||
{
|
||||
dev_t dt;
|
||||
int r;
|
||||
|
||||
dt = make_dev(&dsp_cdevsw, PCMMKMINOR(unit, SND_DEV_DSPREC, channel),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel);
|
||||
|
||||
r = pcm_regdevt(dt, unit, SND_DEV_DSPREC, channel);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_unregister(int unit, int channel)
|
||||
{
|
||||
dev_t pdev;
|
||||
int r;
|
||||
|
||||
pdev = pcm_getdevt(unit, SND_DEV_DSP, channel);
|
||||
if (pdev == NULL)
|
||||
return ENOENT;
|
||||
destroy_dev(pdev);
|
||||
r = pcm_unregdevt(unit, SND_DEV_DSP, channel);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
pdev = pcm_getdevt(unit, SND_DEV_DSP16, channel);
|
||||
if (pdev == NULL)
|
||||
return ENOENT;
|
||||
destroy_dev(pdev);
|
||||
r = pcm_unregdevt(unit, SND_DEV_DSP16, channel);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
pdev = pcm_getdevt(unit, SND_DEV_AUDIO, channel);
|
||||
if (pdev == NULL)
|
||||
return ENOENT;
|
||||
destroy_dev(pdev);
|
||||
r = pcm_unregdevt(unit, SND_DEV_AUDIO, channel);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dsp_unregisterrec(int unit, int channel)
|
||||
{
|
||||
dev_t pdev;
|
||||
int r;
|
||||
|
||||
pdev = pcm_getdevt(unit, SND_DEV_DSPREC, channel);
|
||||
if (pdev == NULL)
|
||||
return ENOENT;
|
||||
destroy_dev(pdev);
|
||||
r = pcm_unregdevt(unit, SND_DEV_DSPREC, channel);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef USING_DEVFS
|
||||
|
||||
/*
|
||||
* Clone logic is this:
|
||||
* x E X = {dsp, dspW, audio}
|
||||
* x -> x${sysctl("hw.snd.unit")}
|
||||
* xN->
|
||||
* for i N = 1 to channels of device N
|
||||
* if xN.i isn't busy, return its dev_t
|
||||
*/
|
||||
static void
|
||||
dsp_clone(void *arg, char *name, int namelen, dev_t *dev)
|
||||
{
|
||||
dev_t pdev;
|
||||
int i, cont, unit, devtype;
|
||||
struct snddev_info *pcm_dev;
|
||||
struct snddev_channel *pcm_chan;
|
||||
int i, unit, devtype;
|
||||
int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO};
|
||||
char *devnames[3] = {"dsp", "dspW", "audio"};
|
||||
|
||||
|
@ -1161,16 +1083,27 @@ dsp_clone(void *arg, char *name, int namelen, dev_t *dev)
|
|||
if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass))
|
||||
return;
|
||||
|
||||
cont = 1;
|
||||
for (i = 0; cont; i++) {
|
||||
pdev = pcm_getdevt(unit, devtype, i);
|
||||
if (pdev->si_flags & SI_NAMED) {
|
||||
if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
|
||||
*dev = pdev;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
cont = 0;
|
||||
pcm_dev = devclass_get_softc(pcm_devclass, unit);
|
||||
|
||||
SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) {
|
||||
|
||||
switch(devtype) {
|
||||
case SND_DEV_DSP:
|
||||
pdev = pcm_chan->dsp_devt;
|
||||
break;
|
||||
case SND_DEV_DSP16:
|
||||
pdev = pcm_chan->dspW_devt;
|
||||
break;
|
||||
case SND_DEV_AUDIO:
|
||||
pdev = pcm_chan->audio_devt;
|
||||
break;
|
||||
default:
|
||||
panic("Unknown devtype %d", devtype);
|
||||
}
|
||||
|
||||
if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) {
|
||||
*dev = pdev;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,4 @@
|
|||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
int dsp_register(int unit, int channel);
|
||||
int dsp_registerrec(int unit, int channel);
|
||||
int dsp_unregister(int unit, int channel);
|
||||
int dsp_unregisterrec(int unit, int channel);
|
||||
extern struct cdevsw dsp_cdevsw;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <dev/sound/pcm/sound.h>
|
||||
#include <dev/sound/pcm/vchan.h>
|
||||
#include <dev/sound/pcm/dsp.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "feeder_if.h"
|
||||
|
@ -413,11 +414,20 @@ pcm_chn_destroy(struct pcm_channel *ch)
|
|||
}
|
||||
|
||||
int
|
||||
pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
|
||||
pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
|
||||
{
|
||||
struct snddev_channel *sce, *tmp, *after;
|
||||
int unit = device_get_unit(d->dev);
|
||||
int x = -1;
|
||||
int device = device_get_unit(d->dev);
|
||||
|
||||
/*
|
||||
* Note it's confusing nomenclature.
|
||||
* dev_t
|
||||
* device -> pcm_device
|
||||
* unit -> pcm_channel
|
||||
* channel -> snddev_channel
|
||||
* device_t
|
||||
* unit -> pcm_device
|
||||
*/
|
||||
|
||||
sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
if (!sce) {
|
||||
|
@ -426,6 +436,7 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
|
|||
|
||||
snd_mtxlock(d->lock);
|
||||
sce->channel = ch;
|
||||
sce->chan_num= d->devcount++;
|
||||
if (SLIST_EMPTY(&d->channels)) {
|
||||
SLIST_INSERT_HEAD(&d->channels, sce, link);
|
||||
} else {
|
||||
|
@ -435,24 +446,35 @@ pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
|
|||
}
|
||||
SLIST_INSERT_AFTER(after, sce, link);
|
||||
}
|
||||
if (mkdev)
|
||||
x = d->devcount++;
|
||||
snd_mtxunlock(d->lock);
|
||||
sce->dsp_devt= make_dev(&dsp_cdevsw,
|
||||
PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
|
||||
device, sce->chan_num);
|
||||
|
||||
if (mkdev) {
|
||||
dsp_register(unit, x);
|
||||
if (ch->direction == PCMDIR_REC)
|
||||
dsp_registerrec(unit, ch->num);
|
||||
}
|
||||
sce->dspW_devt= make_dev(&dsp_cdevsw,
|
||||
PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
|
||||
UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
|
||||
device, sce->chan_num);
|
||||
|
||||
sce->audio_devt= make_dev(&dsp_cdevsw,
|
||||
PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num),
|
||||
UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
|
||||
device, sce->chan_num);
|
||||
|
||||
if (ch->direction == PCMDIR_REC)
|
||||
sce->dspr_devt = make_dev(&dsp_cdevsw,
|
||||
PCMMKMINOR(device, SND_DEV_DSPREC,
|
||||
sce->chan_num), UID_ROOT, GID_WHEEL,
|
||||
0666, "dspr%d.%d", device, sce->chan_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
|
||||
pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
|
||||
{
|
||||
struct snddev_channel *sce;
|
||||
int unit = device_get_unit(d->dev);
|
||||
#if 0
|
||||
int ourlock;
|
||||
|
||||
|
@ -474,11 +496,6 @@ pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
|
|||
return EINVAL;
|
||||
gotit:
|
||||
SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
|
||||
if (rmdev) {
|
||||
dsp_unregister(unit, --d->devcount);
|
||||
if (ch->direction == PCMDIR_REC)
|
||||
dsp_unregisterrec(unit, ch->num);
|
||||
}
|
||||
|
||||
if (ch->direction == PCMDIR_REC)
|
||||
d->reccount--;
|
||||
|
@ -509,7 +526,7 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
|
|||
return ENODEV;
|
||||
}
|
||||
|
||||
err = pcm_chn_add(d, ch, 1);
|
||||
err = pcm_chn_add(d, ch);
|
||||
if (err) {
|
||||
device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
|
||||
snd_mtxunlock(d->lock);
|
||||
|
@ -541,7 +558,7 @@ pcm_killchan(device_t dev)
|
|||
sce = SLIST_FIRST(&d->channels);
|
||||
ch = sce->channel;
|
||||
|
||||
error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children));
|
||||
error = pcm_chn_remove(d, sce->channel);
|
||||
if (error)
|
||||
return (error);
|
||||
return (pcm_chn_destroy(ch));
|
||||
|
@ -685,6 +702,8 @@ pcm_unregister(device_t dev)
|
|||
snd_mtxunlock(d->lock);
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
ch = sce->channel;
|
||||
if (ch->refcount > 0) {
|
||||
|
@ -693,6 +712,15 @@ pcm_unregister(device_t dev)
|
|||
return EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
SLIST_FOREACH(sce, &d->channels, link) {
|
||||
destroy_dev(sce->dsp_devt);
|
||||
destroy_dev(sce->dspW_devt);
|
||||
destroy_dev(sce->audio_devt);
|
||||
if (sce->dspr_devt)
|
||||
destroy_dev(sce->dspr_devt);
|
||||
}
|
||||
|
||||
if (mixer_uninit(dev)) {
|
||||
device_printf(dev, "unregister: mixer busy\n");
|
||||
snd_mtxunlock(d->lock);
|
||||
|
@ -715,82 +743,6 @@ pcm_unregister(device_t dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pcm_regdevt(dev_t dev, unsigned unit, unsigned type, unsigned channel)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snddev_devt *dt;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, unit);
|
||||
KASSERT((d != NULL), ("bad d"));
|
||||
KASSERT((dev != NULL), ("bad dev"));
|
||||
|
||||
dt = malloc(sizeof(*dt), M_DEVBUF, M_ZERO | M_WAITOK);
|
||||
if (dt == NULL)
|
||||
return ENOMEM;
|
||||
dt->dev = dev;
|
||||
dt->type = type;
|
||||
dt->channel = channel;
|
||||
|
||||
snd_mtxlock(d->lock);
|
||||
SLIST_INSERT_HEAD(&d->devs, dt, link);
|
||||
snd_mtxunlock(d->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_t
|
||||
pcm_getdevt(unsigned unit, unsigned type, unsigned channel)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snddev_devt *dt;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, unit);
|
||||
KASSERT((d != NULL), ("bad d"));
|
||||
|
||||
#if 0
|
||||
snd_mtxlock(d->lock);
|
||||
#endif
|
||||
SLIST_FOREACH(dt, &d->devs, link) {
|
||||
if ((dt->type == type) && (dt->channel == channel))
|
||||
return dt->dev;
|
||||
}
|
||||
#if 0
|
||||
snd_mtxunlock(d->lock);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
pcm_unregdevt(unsigned unit, unsigned type, unsigned channel)
|
||||
{
|
||||
struct snddev_info *d;
|
||||
struct snddev_devt *dt;
|
||||
|
||||
d = devclass_get_softc(pcm_devclass, unit);
|
||||
KASSERT((d != NULL), ("bad d"));
|
||||
|
||||
#if 0
|
||||
snd_mtxlock(d->lock);
|
||||
#endif
|
||||
SLIST_FOREACH(dt, &d->devs, link) {
|
||||
if ((dt->type == type) && (dt->channel == channel)) {
|
||||
SLIST_REMOVE(&d->devs, dt, snddev_devt, link);
|
||||
free(dt, M_DEVBUF);
|
||||
#if 0
|
||||
snd_mtxunlock(d->lock);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
snd_mtxunlock(d->lock);
|
||||
#endif
|
||||
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static int
|
||||
|
|
|
@ -98,7 +98,6 @@ struct snd_mixer;
|
|||
#include <dev/sound/pcm/channel.h>
|
||||
#include <dev/sound/pcm/feeder.h>
|
||||
#include <dev/sound/pcm/mixer.h>
|
||||
#include <dev/sound/pcm/dsp.h>
|
||||
|
||||
#define PCM_SOFTC_SIZE 512
|
||||
|
||||
|
@ -222,8 +221,8 @@ int pcm_inprog(struct snddev_info *d, int delta);
|
|||
|
||||
struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo);
|
||||
int pcm_chn_destroy(struct pcm_channel *ch);
|
||||
int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev);
|
||||
int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev);
|
||||
int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch);
|
||||
int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch);
|
||||
|
||||
int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo);
|
||||
unsigned int pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max);
|
||||
|
@ -234,9 +233,6 @@ u_int32_t pcm_getflags(device_t dev);
|
|||
void pcm_setflags(device_t dev, u_int32_t val);
|
||||
void *pcm_getdevinfo(device_t dev);
|
||||
|
||||
int pcm_regdevt(dev_t dev, unsigned unit, unsigned type, unsigned channel);
|
||||
dev_t pcm_getdevt(unsigned unit, unsigned type, unsigned channel);
|
||||
int pcm_unregdevt(unsigned unit, unsigned type, unsigned channel);
|
||||
|
||||
int snd_setup_intr(device_t dev, struct resource *res, int flags,
|
||||
driver_intr_t hand, void *param, void **cookiep);
|
||||
|
@ -286,18 +282,15 @@ int sndstat_busy(void);
|
|||
struct snddev_channel {
|
||||
SLIST_ENTRY(snddev_channel) link;
|
||||
struct pcm_channel *channel;
|
||||
};
|
||||
|
||||
struct snddev_devt {
|
||||
SLIST_ENTRY(snddev_devt) link;
|
||||
dev_t dev;
|
||||
unsigned channel;
|
||||
unsigned type;
|
||||
int chan_num;
|
||||
dev_t dsp_devt;
|
||||
dev_t dspW_devt;
|
||||
dev_t audio_devt;
|
||||
dev_t dspr_devt;
|
||||
};
|
||||
|
||||
struct snddev_info {
|
||||
SLIST_HEAD(, snddev_channel) channels;
|
||||
SLIST_HEAD(, snddev_devt) devs;
|
||||
struct pcm_channel *fakechan;
|
||||
unsigned devcount, playcount, reccount, vchancount;
|
||||
unsigned flags;
|
||||
|
|
|
@ -260,7 +260,10 @@ vchan_create(struct pcm_channel *parent)
|
|||
CHN_UNLOCK(parent);
|
||||
|
||||
/* add us to our grandparent's channel list */
|
||||
err = pcm_chn_add(d, child, !first);
|
||||
/*
|
||||
* XXX maybe we shouldn't always add the dev_t
|
||||
*/
|
||||
err = pcm_chn_add(d, child);
|
||||
if (err) {
|
||||
pcm_chn_destroy(child);
|
||||
free(pce, M_DEVBUF);
|
||||
|
@ -313,7 +316,7 @@ vchan_destroy(struct pcm_channel *c)
|
|||
parent->flags &= ~CHN_F_BUSY;
|
||||
|
||||
/* remove us from our grandparent's channel list */
|
||||
err = pcm_chn_remove(d, c, !last);
|
||||
err = pcm_chn_remove(d, c);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
Loading…
Reference in a new issue