Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Aug 2006 00:21:33 GMT
From:      Ryan Beasley <ryanb@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 104633 for review
Message-ID:  <200608210021.k7L0LXqo090267@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=104633

Change 104633 by ryanb@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@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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200608210021.k7L0LXqo090267>