Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108595199
D26884.id78981.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D26884.id78981.diff
View Options
Index: sys/dev/sound/pcm/sndstat.c
===================================================================
--- sys/dev/sound/pcm/sndstat.c
+++ sys/dev/sound/pcm/sndstat.c
@@ -31,13 +31,27 @@
#include "opt_snd.h"
#endif
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/nv.h>
+#include <sys/dnv.h>
+#include <sys/sx.h>
+#ifdef COMPAT_FREEBSD32
+#include <sys/sysent.h>
+#endif
+
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/pcm.h>
#include <dev/sound/version.h>
-#include <sys/sx.h>
+
SND_DECLARE_FILE("$FreeBSD$");
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev/"
+#endif
+
#define SS_TYPE_MODULE 0
#define SS_TYPE_PCM 1
#define SS_TYPE_MIDI 2
@@ -47,12 +61,14 @@
static void sndstat_close(void *);
static d_read_t sndstat_read;
static d_write_t sndstat_write;
+static d_ioctl_t sndstat_ioctl;
static struct cdevsw sndstat_cdevsw = {
.d_version = D_VERSION,
.d_open = sndstat_open,
.d_read = sndstat_read,
.d_write = sndstat_write,
+ .d_ioctl = sndstat_ioctl,
.d_name = "sndstat",
.d_flags = D_TRACKCLOSE,
};
@@ -65,11 +81,31 @@
int type, unit;
};
+struct sndstat_userdev {
+ TAILQ_ENTRY(sndstat_userdev) link;
+ char *nameunit;
+ char *devnode;
+ char *desc;
+ unsigned int pchan;
+ unsigned int rchan;
+ uint32_t pminrate;
+ uint32_t pmaxrate;
+ uint32_t rminrate;
+ uint32_t rmaxrate;
+ uint32_t pfmts;
+ uint32_t rfmts;
+};
+
struct sndstat_file {
TAILQ_ENTRY(sndstat_file) entry;
struct sbuf sbuf;
+ struct sx lock;
+ void *devs_nvlbuf; /* (l) */
+ size_t devs_nbytes; /* (l) */
+ TAILQ_HEAD(, sndstat_userdev) userdev_list; /* (l) */
int out_offset;
int in_offset;
+ int fflags;
};
static struct sx sndstat_lock;
@@ -84,6 +120,8 @@
int snd_verbose = 0;
static int sndstat_prepare(struct sndstat_file *);
+static struct sndstat_userdev *
+sndstat_line2userdev(struct sndstat_file *, const char *, int);
static int
sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS)
@@ -112,12 +150,16 @@
pf = malloc(sizeof(*pf), M_DEVBUF, M_WAITOK | M_ZERO);
- SNDSTAT_LOCK();
if (sbuf_new(&pf->sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
- SNDSTAT_UNLOCK();
free(pf, M_DEVBUF);
return (ENOMEM);
}
+
+ pf->fflags = flags;
+ TAILQ_INIT(&pf->userdev_list);
+ sx_init(&pf->lock, "sndstat_file");
+
+ SNDSTAT_LOCK();
TAILQ_INSERT_TAIL(&sndstat_filelist, pf, entry);
SNDSTAT_UNLOCK();
@@ -126,6 +168,27 @@
return (0);
}
+/*
+ * Should only be called either when:
+ * * Closing
+ * * pf->lock held
+ */
+static void
+sndstat_remove_all_userdevs(struct sndstat_file *pf)
+{
+ struct sndstat_userdev *ud, *tud;
+
+ KASSERT(
+ sx_xlocked(&pf->lock), ("%s: Called without pf->lock", __func__));
+ TAILQ_FOREACH_SAFE(ud, &pf->userdev_list, link, tud) {
+ TAILQ_REMOVE(&pf->userdev_list, ud, link);
+ free(ud->desc, M_DEVBUF);
+ free(ud->devnode, M_DEVBUF);
+ free(ud->nameunit, M_DEVBUF);
+ free(ud, M_DEVBUF);
+ }
+}
+
static void
sndstat_close(void *sndstat_file)
{
@@ -136,6 +199,12 @@
TAILQ_REMOVE(&sndstat_filelist, pf, entry);
SNDSTAT_UNLOCK();
+ free(pf->devs_nvlbuf, M_NVLIST);
+ sx_xlock(&pf->lock);
+ sndstat_remove_all_userdevs(pf);
+ sx_xunlock(&pf->lock);
+ sx_destroy(&pf->lock);
+
free(pf, M_DEVBUF);
}
@@ -203,7 +272,10 @@
err = EINVAL;
} else {
/* only remember the last write - allows for updates */
- sbuf_clear(&pf->sbuf);
+ sx_xlock(&pf->lock);
+ sndstat_remove_all_userdevs(pf);
+ sx_xunlock(&pf->lock);
+
while (1) {
len = sizeof(temp);
if (len > buf->uio_resid)
@@ -221,15 +293,621 @@
}
}
sbuf_finish(&pf->sbuf);
- if (err == 0)
+
+ if (err == 0) {
+ char *line, *str;
+
+ str = sbuf_data(&pf->sbuf);
+ while ((line = strsep(&str, "\n")) != NULL) {
+ struct sndstat_userdev *ud;
+
+ ud = sndstat_line2userdev(pf, line, strlen(line));
+ if (ud == NULL)
+ continue;
+
+ sx_xlock(&pf->lock);
+ TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
+ sx_xunlock(&pf->lock);
+ }
+
pf->out_offset = sbuf_len(&pf->sbuf);
- else
+ } else
pf->out_offset = 0;
+
+ sbuf_clear(&pf->sbuf);
}
SNDSTAT_UNLOCK();
return (err);
}
+static void
+sndstat_get_caps(struct snddev_info *d, bool play, uint32_t *min_rate,
+ uint32_t *max_rate, uint32_t *fmts)
+{
+ struct pcm_channel *c;
+ int dir;
+
+ dir = play ? PCMDIR_PLAY : PCMDIR_REC;
+ *min_rate = UINT32_MAX;
+ *max_rate = 0;
+ *fmts = 0;
+
+ if (play && d->pvchancount > 0) {
+ *min_rate = *max_rate = d->pvchanrate;
+ *fmts = d->pvchanformat;
+ return;
+ } else if (!play && d->rvchancount > 0) {
+ *min_rate = *max_rate = d->rvchanrate;
+ *fmts = d->rvchanformat;
+ return;
+ }
+
+ CHN_FOREACH(c, d, channels.pcm) {
+ struct pcmchan_caps *caps;
+
+ if (c->direction != dir || (c->flags & CHN_F_VIRTUAL) != 0)
+ continue;
+
+ CHN_LOCK(c);
+ caps = chn_getcaps(c);
+ *min_rate = caps->minspeed;
+ *max_rate = caps->maxspeed;
+ *fmts = chn_getformats(c);
+ CHN_UNLOCK(c);
+ }
+}
+
+/*
+ * Should only be called with the following locks held:
+ * * sndstat_lock
+ */
+static int
+sndstat_create_devs_nvlist(nvlist_t **nvlp)
+{
+ int err;
+ nvlist_t *nvl;
+ struct sndstat_entry *ent;
+ struct sndstat_file *pf;
+
+ nvl = nvlist_create(0);
+ if (nvl == NULL)
+ return (ENOMEM);
+
+ TAILQ_FOREACH (ent, &sndstat_devlist, link) {
+ struct snddev_info *d;
+ nvlist_t *di;
+
+ if (ent->dev == NULL)
+ continue;
+ d = device_get_softc(ent->dev);
+ if (!PCM_REGISTERED(d))
+ continue;
+
+ di = nvlist_create(0);
+ if (di == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+
+ nvlist_add_bool(di, SNDSTAT_LABEL_FROM_USER, false);
+ nvlist_add_number(di, SNDSTAT_LABEL_UNIT,
+ device_get_unit(
+ ent->dev)); // XXX: I want signed integer here
+ nvlist_add_stringf(di, SNDSTAT_LABEL_NAMEUNIT, "%s",
+ device_get_nameunit(ent->dev));
+ nvlist_add_stringf(di, SNDSTAT_LABEL_DEVNODE, _PATH_DEV "dsp%d",
+ device_get_unit(ent->dev));
+ nvlist_add_string(
+ di, SNDSTAT_LABEL_DESC, device_get_desc(ent->dev));
+ PCM_ACQUIRE_QUICK(d);
+ nvlist_add_number(di, SNDSTAT_LABEL_PCHAN, d->playcount);
+ nvlist_add_number(di, SNDSTAT_LABEL_RCHAN, d->reccount);
+ nvlist_add_number(di, SNDSTAT_LABEL_PVCHAN, d->pvchancount);
+ nvlist_add_number(di, SNDSTAT_LABEL_RVCHAN, d->rvchancount);
+ if (d->playcount > 0) {
+ uint32_t max_rate, min_rate, fmts;
+
+ sndstat_get_caps(d, true, &min_rate, &max_rate, &fmts);
+ nvlist_add_number(di, SNDSTAT_LABEL_PMINRATE, min_rate);
+ nvlist_add_number(di, SNDSTAT_LABEL_PMAXRATE, max_rate);
+ nvlist_add_number(di, SNDSTAT_LABEL_PFMTS, fmts);
+ }
+ if (d->reccount > 0) {
+ uint32_t max_rate, min_rate, fmts;
+
+ sndstat_get_caps(d, false, &min_rate, &max_rate, &fmts);
+ nvlist_add_number(di, SNDSTAT_LABEL_RMINRATE, min_rate);
+ nvlist_add_number(di, SNDSTAT_LABEL_RMAXRATE, max_rate);
+ nvlist_add_number(di, SNDSTAT_LABEL_RFMTS, fmts);
+ }
+ nvlist_add_bool(
+ di, SNDSTAT_LABEL_BITPERFECT, d->flags & SD_F_BITPERFECT);
+ PCM_RELEASE_QUICK(d);
+
+ nvlist_append_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, di);
+ nvlist_destroy(di);
+ err = nvlist_error(nvl);
+ if (err != 0)
+ goto done;
+ }
+
+ TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
+ nvlist_t *di;
+ struct sndstat_userdev *ud;
+
+ sx_xlock(&pf->lock);
+
+ TAILQ_FOREACH(ud, &pf->userdev_list, link) {
+ di = nvlist_create(0);
+ if (di == NULL) {
+ err = ENOMEM;
+ sx_xunlock(&pf->lock);
+ goto done;
+ }
+
+ nvlist_add_bool(di, SNDSTAT_LABEL_FROM_USER, true);
+ nvlist_add_number(di, SNDSTAT_LABEL_PCHAN, ud->pchan);
+ nvlist_add_number(di, SNDSTAT_LABEL_RCHAN, ud->rchan);
+ nvlist_add_string(di, SNDSTAT_LABEL_NAMEUNIT, ud->nameunit);
+ if (ud->devnode[0] != '/')
+ nvlist_add_stringf(di, SNDSTAT_LABEL_DEVNODE,
+ _PATH_DEV "%s", ud->devnode);
+ else
+ nvlist_add_string(di, SNDSTAT_LABEL_DEVNODE,
+ ud->devnode);
+ nvlist_add_string(di, SNDSTAT_LABEL_DESC, ud->desc);
+ if (ud->pchan != 0) {
+ nvlist_add_number(
+ di, SNDSTAT_LABEL_PMINRATE, ud->pminrate);
+ nvlist_add_number(
+ di, SNDSTAT_LABEL_PMAXRATE, ud->pmaxrate);
+ nvlist_add_number(
+ di, SNDSTAT_LABEL_PFMTS, ud->pfmts);
+ }
+ if (ud->rchan != 0) {
+ nvlist_add_number(
+ di, SNDSTAT_LABEL_RMINRATE, ud->rminrate);
+ nvlist_add_number(
+ di, SNDSTAT_LABEL_RMAXRATE, ud->rmaxrate);
+ nvlist_add_number(
+ di, SNDSTAT_LABEL_RFMTS, ud->rfmts);
+ }
+
+ nvlist_append_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, di);
+ nvlist_destroy(di);
+ err = nvlist_error(nvl);
+ if (err != 0) {
+ sx_xunlock(&pf->lock);
+ goto done;
+ }
+ }
+
+ sx_xunlock(&pf->lock);
+ }
+
+ *nvlp = nvl;
+
+done:
+ if (err != 0)
+ nvlist_destroy(nvl);
+ return (err);
+}
+
+static int
+sndstat_refresh_devs(struct sndstat_file *pf)
+{
+ int err;
+ nvlist_t *nvl;
+ void *nvlbuf;
+ size_t nbytes;
+
+ SNDSTAT_LOCK();
+ err = sndstat_create_devs_nvlist(&nvl);
+ if (err) {
+ SNDSTAT_UNLOCK();
+ return (err);
+ }
+ SNDSTAT_UNLOCK();
+
+ sx_xlock(&pf->lock);
+ nvlbuf = nvlist_pack(nvl, &nbytes);
+ if (nvlbuf == NULL) {
+ sx_xunlock(&pf->lock);
+ nvlist_destroy(nvl);
+ return (ENOMEM);
+ }
+
+ free(pf->devs_nvlbuf, M_NVLIST);
+ pf->devs_nvlbuf = nvlbuf;
+ pf->devs_nbytes = nbytes;
+ sx_unlock(&pf->lock);
+
+ nvlist_destroy(nvl);
+ return (0);
+}
+
+static int
+sndstat_get_devs(struct sndstat_file *pf, caddr_t data)
+{
+ int err;
+ struct sndstat_nvlbuf_arg *arg = (struct sndstat_nvlbuf_arg *)data;
+
+ SNDSTAT_LOCK();
+ sx_xlock(&pf->lock);
+
+ if (pf->devs_nvlbuf == NULL) {
+ nvlist_t *nvl;
+ void *nvlbuf;
+ size_t nbytes;
+ int err;
+
+ sx_xunlock(&pf->lock);
+
+ err = sndstat_create_devs_nvlist(&nvl);
+ if (err) {
+ SNDSTAT_UNLOCK();
+ return (err);
+ }
+
+ sx_xlock(&pf->lock);
+
+ nvlbuf = nvlist_pack(nvl, &nbytes);
+ err = nvlist_error(nvl);
+ nvlist_destroy(nvl);
+ if (nvlbuf == NULL || err != 0) {
+ SNDSTAT_UNLOCK();
+ sx_xunlock(&pf->lock);
+ if (err == 0)
+ return (ENOMEM);
+ return (err);
+ }
+
+ free(pf->devs_nvlbuf, M_NVLIST);
+ pf->devs_nvlbuf = nvlbuf;
+ pf->devs_nbytes = nbytes;
+ }
+
+ SNDSTAT_UNLOCK();
+
+ if (!arg->nbytes) {
+ arg->nbytes = pf->devs_nbytes;
+ err = 0;
+ goto done;
+ }
+ if (arg->nbytes < pf->devs_nbytes) {
+ arg->nbytes = 0;
+ err = 0;
+ goto done;
+ }
+
+ err = copyout(pf->devs_nvlbuf, arg->buf, pf->devs_nbytes);
+ if (err)
+ goto done;
+
+ arg->nbytes = pf->devs_nbytes;
+
+ free(pf->devs_nvlbuf, M_NVLIST);
+ pf->devs_nvlbuf = NULL;
+ pf->devs_nbytes = 0;
+
+done:
+ sx_unlock(&pf->lock);
+ return (err);
+}
+
+static int
+sndstat_unpack_user_nvlbuf(const void *unvlbuf, size_t nbytes, nvlist_t **nvl)
+{
+ void *nvlbuf;
+ int err;
+
+ nvlbuf = malloc(nbytes, M_DEVBUF, M_WAITOK);
+ err = copyin(unvlbuf, nvlbuf, nbytes);
+ if (err != 0) {
+ free(nvlbuf, M_DEVBUF);
+ return (err);
+ }
+ *nvl = nvlist_unpack(nvlbuf, nbytes, 0);
+ free(nvlbuf, M_DEVBUF);
+ if (nvl == NULL) {
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static const char *
+sndstat_strip_path_dev(const char *str)
+{
+ if (strncmp(str, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
+ str += sizeof(_PATH_DEV) - 1;
+ return (str);
+}
+
+static char *
+sndstat_normalize_path(const char *str)
+{
+ const char *p = str;
+ size_t buflen, slen;
+ char *buf;
+
+ buflen = strlen(str) + 1;
+ buf = (char *)malloc(buflen, M_DEVBUF, M_WAITOK|M_ZERO);
+
+ if (p[0] == '/') {
+ buf[0] = '/';
+ p++;
+ }
+
+ do {
+ const char *match;
+
+ match = strchrnul(p, '/');
+ strncat(buf, p, match - p);
+ if (match[0] == '/' && match != p)
+ strcat(buf, "/");
+ p = match + 1;
+ } while (p < str + buflen);
+
+ p = sndstat_strip_path_dev(buf);
+ slen = strlen(p);
+ memmove(buf, p, slen + 1);
+
+ return (buf);
+}
+
+static bool
+sndstat_dsp_nvlist_is_sane(const nvlist_t *nvlist)
+{
+ if (!(nvlist_exists_string(nvlist, SNDSTAT_LABEL_DEVNODE) &&
+ nvlist_exists_string(nvlist, SNDSTAT_LABEL_DESC) &&
+ nvlist_exists_number(nvlist, SNDSTAT_LABEL_PCHAN) &&
+ nvlist_exists_number(nvlist, SNDSTAT_LABEL_RCHAN)))
+ return (false);
+
+ return (true);
+
+}
+
+static int
+sndstat_add_user_devs(struct sndstat_file *pf, caddr_t data)
+{
+ int err;
+ nvlist_t *nvl = NULL;
+ const nvlist_t * const *dsps;
+ size_t i, ndsps;
+ struct sndstat_nvlbuf_arg *arg = (struct sndstat_nvlbuf_arg *)data;
+
+ if ((pf->fflags & FWRITE) == 0) {
+ err = EPERM;
+ goto done;
+ }
+
+ err = sndstat_unpack_user_nvlbuf(arg->buf, arg->nbytes, &nvl);
+ if (err != 0)
+ goto done;
+
+ if (!nvlist_exists_nvlist_array(nvl, SNDSTAT_LABEL_DSPS)) {
+ err = EINVAL;
+ goto done;
+ }
+ dsps = nvlist_get_nvlist_array(nvl, SNDSTAT_LABEL_DSPS, &ndsps);
+ for (i = 0; i < ndsps; i++) {
+ if (!sndstat_dsp_nvlist_is_sane(dsps[i])) {
+ err = EINVAL;
+ goto done;
+ }
+ }
+ sx_xlock(&pf->lock);
+ for (i = 0; i < ndsps; i++) {
+ const char *nameunit, *devnode, *desc;
+ unsigned int pchan, rchan;
+ uint32_t pminrate = 0, pmaxrate = 0;
+ uint32_t rminrate = 0, rmaxrate = 0;
+ uint32_t pfmts = 0, rfmts = 0;
+ struct sndstat_userdev *ud;
+
+ devnode = nvlist_get_string(dsps[i], SNDSTAT_LABEL_DEVNODE);
+ if (nvlist_exists_string(dsps[i], SNDSTAT_LABEL_NAMEUNIT))
+ nameunit = nvlist_get_string(dsps[i], SNDSTAT_LABEL_NAMEUNIT);
+ else
+ nameunit = devnode;
+ desc = nvlist_get_string(dsps[i], SNDSTAT_LABEL_DESC);
+ pchan = nvlist_get_number(dsps[i], SNDSTAT_LABEL_PCHAN);
+ rchan = nvlist_get_number(dsps[i], SNDSTAT_LABEL_RCHAN);
+ if (pchan != 0) {
+ pminrate = dnvlist_get_number(
+ dsps[i], SNDSTAT_LABEL_PMINRATE, 48000);
+ pmaxrate = dnvlist_get_number(
+ dsps[i], SNDSTAT_LABEL_PMAXRATE, 48000);
+ pfmts = dnvlist_get_number(dsps[i], SNDSTAT_LABEL_PFMTS,
+ SND_FORMAT(AFMT_S16_LE, 2, 0));
+ }
+ if (rchan != 0) {
+ rminrate = dnvlist_get_number(
+ dsps[i], SNDSTAT_LABEL_RMINRATE, 48000);
+ rmaxrate = dnvlist_get_number(
+ dsps[i], SNDSTAT_LABEL_RMAXRATE, 48000);
+ rfmts = dnvlist_get_number(dsps[i], SNDSTAT_LABEL_RFMTS,
+ SND_FORMAT(AFMT_S16_LE, 2, 0));
+ }
+
+ ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK);
+ ud->devnode = sndstat_normalize_path(devnode);
+ ud->nameunit = strdup(nameunit, M_DEVBUF);
+ ud->desc = strdup(desc, M_DEVBUF);
+ ud->pchan = pchan;
+ ud->rchan = rchan;
+ ud->pminrate = pminrate;
+ ud->pmaxrate = pmaxrate;
+ ud->rminrate = rminrate;
+ ud->rmaxrate = rmaxrate;
+ ud->pfmts = 0;
+ ud->rfmts = 0;
+ TAILQ_INSERT_TAIL(&pf->userdev_list, ud, link);
+ }
+ sx_unlock(&pf->lock);
+
+done:
+ nvlist_destroy(nvl);
+ return (err);
+}
+
+static int
+sndstat_flush_user_devs(struct sndstat_file *pf)
+{
+ if ((pf->fflags & FWRITE) == 0)
+ return (EPERM);
+
+ sx_xlock(&pf->lock);
+ sndstat_remove_all_userdevs(pf);
+ sx_xunlock(&pf->lock);
+
+ return (0);
+}
+
+#ifdef COMPAT_FREEBSD32
+static int
+compat_sndstat_get_devs32(struct sndstat_file *pf, caddr_t data)
+{
+ struct sndstat_nvlbuf_arg32 *arg32 = (struct sndstat_nvlbuf_arg32 *)data;
+ struct sndstat_nvlbuf_arg arg;
+ int err;
+
+ arg.buf = (void *)(uintptr_t)arg32->buf;
+ arg.nbytes = arg32->nbytes;
+
+ err = sndstat_get_devs(pf, (caddr_t)&arg);
+ if (err == 0) {
+ arg32->buf = (uint32_t)(uintptr_t)arg.buf;
+ arg32->nbytes = arg.nbytes;
+ }
+
+ return (err);
+}
+
+static int
+compat_sndstat_add_user_devs32(struct sndstat_file *pf, caddr_t data)
+{
+ struct sndstat_nvlbuf_arg32 *arg32 = (struct sndstat_nvlbuf_arg32 *)data;
+ struct sndstat_nvlbuf_arg arg;
+ int err;
+
+ arg.buf = (void *)(uintptr_t)arg32->buf;
+ arg.nbytes = arg32->nbytes;
+
+ err = sndstat_add_user_devs(pf, (caddr_t)&arg);
+ if (err == 0) {
+ arg32->buf = (uint32_t)(uintptr_t)arg.buf;
+ arg32->nbytes = arg.nbytes;
+ }
+
+ return (err);
+}
+#endif
+
+static int
+sndstat_ioctl(
+ struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
+{
+ int err;
+ struct sndstat_file *pf;
+
+ err = devfs_get_cdevpriv((void **)&pf);
+ if (err != 0)
+ return (err);
+
+ switch (cmd) {
+ case SNDSTAT_GET_DEVS:
+ err = sndstat_get_devs(pf, data);
+ break;
+#ifdef COMPAT_FREEBSD32
+ case SNDSTAT_GET_DEVS32:
+ if (!SV_CURPROC_FLAG(SV_ILP32)) {
+ err = ENODEV;
+ break;
+ }
+ err = compat_sndstat_get_devs32(pf, data);
+ break;
+#endif
+ case SNDSTAT_ADD_USER_DEVS:
+ err = sndstat_add_user_devs(pf, data);
+ break;
+#ifdef COMPAT_FREEBSD32
+ case SNDSTAT_ADD_USER_DEVS32:
+ if (!SV_CURPROC_FLAG(SV_ILP32)) {
+ err = ENODEV;
+ break;
+ }
+ err = compat_sndstat_add_user_devs32(pf, data);
+ break;
+#endif
+ case SNDSTAT_REFRESH_DEVS:
+ err = sndstat_refresh_devs(pf);
+ break;
+ case SNDSTAT_FLUSH_USER_DEVS:
+ err = sndstat_flush_user_devs(pf);
+ break;
+ default:
+ err = ENODEV;
+ }
+
+ return (err);
+}
+
+static struct sndstat_userdev *
+sndstat_line2userdev(struct sndstat_file *pf, const char *line, int n)
+{
+ struct sndstat_userdev *ud;
+ const char dirpath[] = _PATH_DEV;
+ const char *e, *m;
+
+ ud = malloc(sizeof(*ud), M_DEVBUF, M_WAITOK|M_ZERO);
+
+ e = strchr(line, ':');
+ if (e == NULL)
+ goto fail;
+ ud->nameunit = strndup(line, e - line, M_DEVBUF);
+ ud->devnode = (char *)malloc(
+ sizeof(dirpath) + e - line, M_DEVBUF, M_WAITOK | M_ZERO);
+ strlcat(ud->devnode, dirpath, sizeof(dirpath) + e - line);
+ strlcat(ud->devnode, ud->nameunit, sizeof(dirpath) + e - line);
+ line = e + 1;
+
+ e = strchr(line, '<');
+ if (e == NULL)
+ goto fail;
+ line = e + 1;
+ e = strrchr(line, '>');
+ if (e == NULL)
+ goto fail;
+ ud->desc = strndup(line, e - line, M_DEVBUF);
+ line = e + 1;
+
+ e = strchr(line, '(');
+ if (e == NULL)
+ goto fail;
+ line = e + 1;
+ e = strrchr(line, ')');
+ if (e == NULL)
+ goto fail;
+ m = strstr(line, "play");
+ if (m != NULL && m < e)
+ ud->pchan = 1;
+ m = strstr(line, "rec");
+ if (m != NULL && m < e)
+ ud->rchan = 1;
+
+ return (ud);
+
+fail:
+ free(ud->nameunit, M_DEVBUF);
+ free(ud->devnode, M_DEVBUF);
+ free(ud->desc, M_DEVBUF);
+ free(ud, M_DEVBUF);
+ return (NULL);
+}
+
/************************************************************************/
int
@@ -379,14 +1057,26 @@
/* append any input from userspace */
k = 0;
TAILQ_FOREACH(pf, &sndstat_filelist, entry) {
+ struct sndstat_userdev *ud;
+
if (pf == pf_self)
continue;
- if (pf->out_offset == 0)
+ sx_xlock(&pf->lock);
+ if (TAILQ_EMPTY(&pf->userdev_list)) {
+ sx_unlock(&pf->lock);
continue;
+ }
if (!k++)
sbuf_printf(s, "Installed devices from userspace:\n");
- sbuf_bcat(s, sbuf_data(&pf->sbuf),
- sbuf_len(&pf->sbuf));
+ TAILQ_FOREACH(ud, &pf->userdev_list, link) {
+ const char *caps = (ud->pchan && ud->rchan) ?
+ "play/rec" :
+ (ud->pchan ? "play" : (ud->rchan ? "rec" : ""));
+ sbuf_printf(s, "%s: <%s>", ud->nameunit, ud->desc);
+ sbuf_printf(s, " (%s)", caps);
+ sbuf_printf(s, "\n");
+ }
+ sx_unlock(&pf->lock);
}
if (k == 0)
sbuf_printf(s, "No devices installed from userspace.\n");
Index: sys/dev/sound/pcm/sound.h
===================================================================
--- sys/dev/sound/pcm/sound.h
+++ sys/dev/sound/pcm/sound.h
@@ -64,6 +64,7 @@
#include <sys/poll.h>
#include <sys/sbuf.h>
#include <sys/soundcard.h>
+#include <sys/sndstat.h>
#include <sys/sysctl.h>
#include <sys/kobj.h>
#include <vm/vm.h>
Index: sys/sys/sndstat.h
===================================================================
--- /dev/null
+++ sys/sys/sndstat.h
@@ -0,0 +1,88 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 The FreeBSD Foundation
+ *
+ * This software was developed by Ka Ho Ng
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_SNDSTAT_H_
+#define _SYS_SNDSTAT_H_
+
+#include <sys/types.h>
+#ifndef _IOWR
+#include <sys/ioccom.h>
+#endif /* !_IOWR */
+
+struct sndstat_nvlbuf_arg {
+ size_t nbytes; /* [IN/OUT] buffer size/number of bytes filled */
+ void *buf; /* [OUT] buffer holding a packed nvlist */
+};
+
+#define SNDSTAT_LABEL_DSPS "dsps"
+#define SNDSTAT_LABEL_FROM_USER "from_user"
+#define SNDSTAT_LABEL_UNIT "unit"
+#define SNDSTAT_LABEL_PCHAN "pchan"
+#define SNDSTAT_LABEL_RCHAN "rchan"
+#define SNDSTAT_LABEL_PVCHAN "pvchan"
+#define SNDSTAT_LABEL_RVCHAN "rvchan"
+#define SNDSTAT_LABEL_PMINRATE "pminrate"
+#define SNDSTAT_LABEL_PMAXRATE "pmaxrate"
+#define SNDSTAT_LABEL_RMINRATE "rminrate"
+#define SNDSTAT_LABEL_RMAXRATE "rmaxrate"
+#define SNDSTAT_LABEL_PFMTS "pfmts"
+#define SNDSTAT_LABEL_RFMTS "rfmts"
+#define SNDSTAT_LABEL_BITPERFECT "bitperfect"
+#define SNDSTAT_LABEL_NAMEUNIT "nameunit"
+#define SNDSTAT_LABEL_DEVNODE "devnode"
+#define SNDSTAT_LABEL_DESC "desc"
+
+#define SNDSTAT_DEVS_VER_MAJOR 1
+#define SNDSTAT_DEVS_VER_MINOR 0
+
+#define SNDSTAT_REFRESH_DEVS _IO('D', 100)
+#define SNDSTAT_GET_DEVS _IOWR('D', 101, struct sndstat_nvlbuf_arg)
+#define SNDSTAT_ADD_USER_DEVS _IOWR('D', 102, struct sndstat_nvlbuf_arg)
+#define SNDSTAT_FLUSH_USER_DEVS _IO('D', 103)
+
+#ifdef _KERNEL
+#ifdef COMPAT_FREEBSD32
+
+struct sndstat_nvlbuf_arg32 {
+ uint32_t nbytes;
+ uint32_t buf;
+};
+
+#define SNDSTAT_GET_DEVS32 \
+ _IOC_NEWTYPE(SNDSTAT_GET_DEVS, struct sndstat_nvlbuf_arg32)
+#define SNDSTAT_ADD_USER_DEVS32 \
+ _IOC_NEWTYPE(SNDSTAT_ADD_USER_DEVS, struct sndstat_nvlbuf_arg32)
+
+#endif
+#endif
+
+#endif /* !_SYS_SNDSTAT_H_ */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 27, 6:24 PM (5 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16204250
Default Alt Text
D26884.id78981.diff (22 KB)
Attached To
Mode
D26884: Implement sndstat nvlist-based enumeration ioctls.
Attached
Detach File
Event Timeline
Log In to Comment