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:
Mathew Kanner 2004-01-20 03:58:57 +00:00
parent 28466ae036
commit 5ee30e277a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=124740
5 changed files with 94 additions and 216 deletions

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;