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>