PERFORCE change 104633 for review
Ryan Beasley
ryanb at FreeBSD.org
Mon Aug 21 00:21:35 UTC 2006
http://perforce.freebsd.org/chv.cgi?CH=104633
Change 104633 by ryanb at ryanb_yuki on 2006/08/21 00:20:40
Integrate from user branch
Affected files ...
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel_if.m#3 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/dsp.c#12 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/mixer.c#8 integrate
.. //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/mixer.h#7 integrate
Differences ...
==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/channel_if.m#3 (text+ko) ====
@@ -189,14 +189,13 @@
* (e.g., application wants direct DMA access after setting a channel to run
* "uncooked").
*
- * @todo Once 4Front docs for SNDCTL_AUDIOINFO are back, double check to
- * make sure that such tables are truly static. I'm wondering if it's
- * possible for a driver's supported rates to change depending on the
- * card's operating mode.
+ * The parameter @c rates is a double pointer which will be reset to
+ * point to an array of supported sample rates. The number of elements
+ * in the array is returned to the caller.
*
* @param obj standard kobj object (usually @c channel->methods)
* @param data driver-specific data (usually @c channel->devinfo)
- * @param rates if successful, will point to static array of supported rates
+ * @param rates rate array pointer
*
* @return Number of rates in the array
*/
==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/dsp.c#12 (text+ko) ====
@@ -451,6 +451,20 @@
return EBADF;
}
+ /*
+ * Certain ioctls may be made on any type of device (audio, mixer,
+ * and MIDI).
+ */
+ if (IOCGROUP(cmd) == 'X') {
+ switch(cmd) {
+ case SNDCTL_MIXERINFO:
+ return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
+ break;
+ default:
+ return EINVAL;
+ }
+ }
+
getchns(i_dev, &rdch, &wrch, 0);
kill = 0;
@@ -1090,7 +1104,6 @@
if (d->mixer_dev != NULL)
ret = mixer_ioctl(d->mixer_dev, xcmd, arg, -1, td);
else
- /** @todo verify that this is the correct error */
ret = ENOTSUP;
break;
@@ -1100,8 +1113,7 @@
if (d->mixer_dev != NULL)
ret = mixer_ioctl(d->mixer_dev, cmd, arg, -1, td);
else
- /** @todo verify error correctness */
- ret = EINVAL;
+ ret = ENOTSUP;
break;
/*
@@ -1199,10 +1211,10 @@
* from the 4Front drivers. However, I don't expect this to be
* much of a problem.
*
- * @todo In a test where this ioctl is called immediately after write
+ * @note In a test where @c CURRENT_OPTR is called immediately after write
* returns, this driver is about 32K samples behind whereas
- * 4Front's is about 8K samples behind. Must determine source
- * of discrepancy.
+ * 4Front's is about 8K samples behind. Should determine source
+ * of discrepancy, even if only out of curiosity.
*
* @todo Actually test SNDCTL_DSP_CURRENT_IPTR.
*/
@@ -1566,7 +1578,7 @@
* the sound subsystem scans for an available audio channel (or channels, if
* opened in read+write) and then assigns them to the si_drv[12] private
* data fields. As a result, any information returned linking a channel to
- * a specific character device isn't really accurate.
+ * a specific character device isn't necessarily accurate.
*
* @note
* Calling threads must not hold any snddev_info or pcm_channel locks.
@@ -1578,10 +1590,6 @@
* @retval EINVAL ai->dev specifies an invalid device
*
* @todo Verify correctness of Doxygen tags. ;)
- * @todo Ask about device and channel locking; with D_NEEDGIANT, am I safe
- * for now?
- * @todo Discuss rectifying the channel/cdev relationship. OSS will return
- * EBUSY if applications attempt to open a DSP device more than once.
*/
int
dsp_oss_audioinfo(struct cdev *i_dev, oss_audioinfo *ai)
@@ -1591,9 +1599,14 @@
struct pcm_channel *ch;
struct snddev_info *d;
struct cdev *t_cdev;
- int i, nchan, ret, *rates;
+ uint32_t fmts;
+ int i, nchan, ret, *rates, minch, maxch;
- /* If probing handling device, make sure it's a DSP device. */
+ /*
+ * If probing the device that received the ioctl, make sure it's a
+ * DSP device. (Users may use this ioctl with /dev/mixer and
+ * /dev/midi.)
+ */
if ((ai->dev == -1) && (i_dev->si_devsw != &dsp_cdevsw))
return EINVAL;
@@ -1602,7 +1615,10 @@
nchan = 0;
ret = 0;
- /* Search for the requested audio device. */
+ /*
+ * Search for the requested audio device (channel). Start by
+ * iterating over pcm devices.
+ */
for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
d = devclass_get_softc(pcm_devclass, i);
if (d == NULL)
@@ -1618,8 +1634,8 @@
mtx_assert(ch->lock, MA_NOTOWNED);
CHN_LOCK(ch);
if (ai->dev == -1) {
- if ((ch == i_dev->si_drv1) ||
- (ch == i_dev->si_drv2)) {
+ if ((ch == i_dev->si_drv1) || /* record ch */
+ (ch == i_dev->si_drv2)) { /* playback ch */
t_cdev = i_dev;
goto dspfound;
}
@@ -1678,15 +1694,46 @@
*/
ai->pid = ch->pid;
+ /*
+ * These flags stolen from SNDCTL_DSP_GETCAPS handler. Note, however,
+ * that a single channel operates in only one direction, so
+ * DSP_CAP_DUPLEX is out.
+ */
/**
- * @todo @c caps - Should take the SNDCTL_DSP_GETCAPS route.
- * Question: Since no drivers actually use the caps
- * field of pcmchan_caps, why not store DSP_CAP_*
- * values there?
- * @todo @c iformats - chn_getformats includes sw emulated formats,
- * but cmd is supposed to ask hardware directly
- * @todo @c oformats - same as iformats
+ * @todo @c SNDCTL_AUDIOINFO::caps - Make drivers keep these in
+ * pcmchan::caps?
+ */
+ ai->caps = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER;
+
+ /*
+ * Collect formats supported @b natively by the device. Also
+ * determine min/max channels. (I.e., mono, stereo, or both?)
*
+ * If any channel is stereo, maxch = 2;
+ * if all channels are stereo, minch = 2, too;
+ * if any channel is mono, minch = 1;
+ * and if all channels are mono, maxch = 1.
+ */
+ minch = 0;
+ maxch = 0;
+ fmts = 0;
+ for (i = 0; caps->fmtlist[i]; i++) {
+ fmts |= caps->fmtlist[i];
+ if (caps->fmtlist[i] & AFMT_STEREO) {
+ minch = (minch == 0) ? 2 : minch;
+ maxch = 2;
+ } else {
+ minch = 1;
+ maxch = (maxch == 0) ? 1 : maxch;
+ }
+ }
+
+ if (ch->direction == PCMDIR_PLAY)
+ ai->oformats = fmts;
+ else
+ ai->iformats = fmts;
+
+ /**
* @note
* @c magic - OSSv4 docs: "Reserved for internal use by OSS."
*
@@ -1696,16 +1743,11 @@
* Applications should normally not use this field for any
* purpose."
*/
- if (ch->direction == PCMDIR_PLAY)
- ai->iformats = chn_getformats(ch);
- else
- ai->oformats = chn_getformats(ch);
-
ai->card_number = -1;
/**
* @todo @c song_name - depends first on SNDCTL_[GS]ETSONG
* @todo @c label - depends on SNDCTL_[GS]ETLABEL
- * @todo @c port_number - device unit number?
+ * @todo @c port_number - routing information?
*/
ai->port_number = -1;
ai->mixer_dev = (d->mixer_dev != NULL) ? PCMUNIT(d->mixer_dev) : -1;
@@ -1720,9 +1762,6 @@
* @note
* @c flags - OSSv4 docs: "Reserved for future use."
*
- * @todo @c min_channels, @c max_channels - A little more research
- * is required.
- *
* @note
* @c binding - OSSv4 docs: "Reserved for future use."
*
@@ -1732,7 +1771,12 @@
ai->min_rate = caps->minspeed;
ai->max_rate = caps->maxspeed;
+ ai->min_channels = minch;
+ ai->max_channels = maxch;
+
ai->nrates = chn_getrates(ch, &rates);
+ if (ai->nrates > MAX_SAMPLE_RATES)
+ ai->nrates = MAX_SAMPLE_RATES;
for (i = 0; i < ai->nrates; i++)
ai->rates[i] = rates[i];
@@ -1758,6 +1802,10 @@
* sync group is created. Otherwise, wrch and rdch (if set) are added to
* the group specified.
*
+ * @todo As far as memory allocation, should we assume that things are
+ * okay and allocate with M_WAITOK before acquiring channel locks,
+ * freeing later if not?
+ *
* @param wrch output channel associated w/ device (if any)
* @param rdch input channel associated w/ device (if any)
* @param group Sync group parameters
@@ -1780,18 +1828,14 @@
PCM_SG_LOCK();
/*
- * - If device's channels are already mapped to a group, unmap them.
- * - Verify that mode matches device properites.
- * - Bail if PCM_ENABLE_OUTPUT && wrch == NULL.
- * - Bail if PCM_ENABLE_INPUT && rdch == NULL.
- * - If group->id != 0, lookup group.
- * - Bail if group not found.
- * - If group->id == 0, create a new group.
* - Insert channel(s) into group's member list.
* - Set CHN_F_NOTRIGGER on channel(s).
* - Stop channel(s).
*/
+ /*
+ * If device's channels are already mapped to a group, unmap them.
+ */
if (wrch) {
CHN_LOCK(wrch);
chn_syncdestroy(wrch);
@@ -1802,6 +1846,11 @@
chn_syncdestroy(rdch);
}
+ /*
+ * Verify that mode matches character device properites.
+ * - Bail if PCM_ENABLE_OUTPUT && wrch == NULL.
+ * - Bail if PCM_ENABLE_INPUT && rdch == NULL.
+ */
if (((wrch == NULL) && (group->mode & PCM_ENABLE_OUTPUT)) ||
((rdch == NULL) && (group->mode & PCM_ENABLE_INPUT))) {
ret = EINVAL;
@@ -1831,9 +1880,14 @@
ret = EINVAL;
}
+ /* Couldn't create or find a syncgroup. Fail. */
if (sg == NULL)
goto bad;
+ /*
+ * Allocate a syncmember, assign it and a channel together, and
+ * insert into syncgroup.
+ */
if (group->mode & PCM_ENABLE_INPUT) {
smrd = (struct pcmchan_syncmember *)malloc(sizeof(*smrd), M_DEVBUF, M_NOWAIT);
if (smrd == NULL) {
==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/mixer.c#8 (text+ko) ====
@@ -50,6 +50,12 @@
char name[MIXER_NAMELEN];
struct mtx *lock;
oss_mixer_enuminfo enuminfo;
+ /**
+ * Counter is incremented when applications change any of this
+ * mixer's controls. A change in value indicates that persistent
+ * mixer applications should update their displays.
+ */
+ int modify_counter;
};
static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
@@ -630,6 +636,11 @@
return EBADF;
}
+ if (cmd == SNDCTL_MIXERINFO) {
+ snd_mtxunlock(m->lock);
+ return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
+ }
+
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
if (j == SOUND_MIXER_RECSRC)
ret = mixer_setrecsrc(m, *arg_i);
@@ -724,3 +735,155 @@
SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
#endif
+
+/**
+ * @brief Handler for SNDCTL_MIXERINFO
+ *
+ * This function searches for a mixer based on the numeric ID stored
+ * in oss_miserinfo::dev. If set to -1, then information about the
+ * current mixer handling the request is provided. Note, however, that
+ * this ioctl may be made with any sound device (audio, mixer, midi).
+ *
+ * @note Caller must not hold any PCM device, channel, or mixer locks.
+ *
+ * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for
+ * more information.
+ *
+ * @param i_dev character device on which the ioctl arrived
+ * @param arg user argument (oss_mixerinfo *)
+ *
+ * @retval EINVAL oss_mixerinfo::dev specified a bad value
+ * @retval 0 success
+ */
+int
+mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
+{
+ struct snddev_info *d;
+ struct snd_mixer *m;
+ struct cdev *t_cdev;
+ int nmix, ret, pcmunit, i;
+
+ /*
+ * If probing the device handling the ioctl, make sure it's a mixer
+ * device. (This ioctl is valid on audio, mixer, and midi devices.)
+ */
+ if ((mi->dev == -1) && (i_dev->si_devsw != &mixer_cdevsw))
+ return EINVAL;
+
+ m = NULL;
+ t_cdev = NULL;
+ nmix = 0;
+ ret = 0;
+ pcmunit = -1; /* pcmX */
+
+ /*
+ * There's a 1:1 relationship between mixers and PCM devices, so
+ * begin by iterating over PCM devices and search for our mixer.
+ */
+ for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
+ d = devclass_get_softc(pcm_devclass, i);
+ if (d == NULL)
+ continue;
+
+ /* See the note in function docblock. */
+ mtx_assert(d->lock, MA_NOTOWNED);
+ pcm_inprog(d, 1);
+ pcm_lock(d);
+
+ if (d->mixer_dev != NULL) {
+ if (((mi->dev == -1) && (d->mixer_dev == i_dev)) || (mi->dev == nmix)) {
+ t_cdev = d->mixer_dev;
+ pcmunit = i;
+ break;
+ }
+ ++nmix;
+ }
+
+ pcm_unlock(d);
+ pcm_inprog(d, -1);
+ }
+
+ /*
+ * If t_cdev is NULL, then search was exhausted and device wasn't
+ * found. No locks are held, so just return.
+ */
+ if (t_cdev == NULL)
+ return EINVAL;
+
+ m = t_cdev->si_drv1;
+ mtx_lock(m->lock);
+
+ /*
+ * At this point, the following synchronization stuff has happened:
+ * - a specific PCM device is locked and its "in progress
+ * operations" counter has been incremented, so be sure to unlock
+ * and decrement when exiting;
+ * - a specific mixer device has been locked, so be sure to unlock
+ * when existing.
+ */
+
+ bzero((void *)mi, sizeof(*mi));
+
+ mi->dev = nmix;
+ snprintf(mi->id, sizeof(mi->id), "mixer%d", dev2unit(t_cdev));
+ strlcpy(mi->name, m->name, sizeof(mi->name));
+ mi->modify_counter = m->modify_counter;
+ mi->card_number = pcmunit;
+ /*
+ * Currently, FreeBSD assumes 1:1 relationship between a pcm and
+ * mixer devices, so this is hardcoded to 0.
+ */
+ mi->port_number = 0;
+
+ /**
+ * @todo Fill in @sa oss_mixerinfo::mixerhandle.
+ * @note From 4Front: "mixerhandle is an arbitrary string that
+ * identifies the mixer better than the device number
+ * (mixerinfo.dev). Device numbers may change depending on
+ * the order the drivers are loaded. However the handle
+ * should remain the same provided that the sound card is
+ * not moved to another PCI slot."
+ */
+
+ /**
+ * @note
+ * @sa oss_mixerinfo::magic is a reserved field.
+ *
+ * @par
+ * From 4Front: "magic is usually 0. However some devices may have
+ * dedicated setup utilities and the magic field may contain an
+ * unique driver specific value (managed by [4Front])."
+ */
+
+ mi->enabled = device_is_attached(m->dev) ? 1 : 0;
+ /**
+ * The only flag for @sa oss_mixerinfo::caps is currently
+ * MIXER_CAP_VIRTUAL, which I'm not sure we really worry about.
+ */
+ /**
+ * Mixer extensions currently aren't supported, so leave
+ * @sa oss_mixerinfo::nrext blank for now.
+ */
+ /**
+ * @todo Fill in @sa oss_mixerinfo::priority (requires touching
+ * drivers?)
+ * @note The priority field is for mixer applets to determine which
+ * mixer should be the default, with 0 being least preferred and 10
+ * being most preferred. From 4Front: "OSS drivers like ICH use
+ * higher values (10) because such chips are known to be used only
+ * on motherboards. Drivers for high end pro devices use 0 because
+ * they will never be the default mixer. Other devices use values 1
+ * to 9 depending on the estimated probability of being the default
+ * device.
+ *
+ * XXX Described by Hannu at 4Front, but not found in soundcard.h.
+ strlcpy(mi->devnode, t_cdev->si_name, sizeof(mi->devnode));
+ mi->legacy_device = pcmunit;
+ */
+
+ mtx_unlock(m->lock);
+ pcm_unlock(d);
+ pcm_inprog(d, -1);
+
+ return ret;
+}
==== //depot/projects/soc2006/rbeasley_sound/sys/dev/sound/pcm/mixer.h#7 (text+ko) ====
@@ -30,6 +30,7 @@
int mixer_uninit(device_t dev);
int mixer_reinit(device_t dev);
int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td);
+int mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi);
int mixer_hwvol_init(device_t dev);
void mixer_hwvol_mute(device_t dev);
More information about the p4-projects
mailing list