#include <sys/types.h>
#include <sys/list.h>
#include <sys/sysmacros.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/atomic.h>
#include "audio_impl.h"
static void
audio_control_freenames(audio_ctrl_t *ctrl)
{
int indx;
if (ctrl->ctrl_name != NULL)
strfree((char *)ctrl->ctrl_name);
ctrl->ctrl_name = NULL;
for (indx = 0; indx < 64; indx++) {
if (ctrl->ctrl_enum[indx] != NULL) {
strfree((char *)ctrl->ctrl_enum[indx]);
ctrl->ctrl_enum[indx] = NULL;
}
}
}
audio_ctrl_t *
audio_dev_add_control(audio_dev_t *d, audio_ctrl_desc_t *desc,
audio_ctrl_rd_t read_fn, audio_ctrl_wr_t write_fn, void *arg)
{
audio_ctrl_t *ctrl;
audio_ctrl_desc_t *new_desc;
char scratch[16];
const char *name;
ASSERT(d);
ASSERT(desc);
if ((name = desc->acd_name) == NULL) {
return (NULL);
}
ctrl = auclnt_find_control(d, name);
if (ctrl == NULL) {
ctrl = kmem_zalloc(sizeof (*ctrl), KM_SLEEP);
} else {
switch (desc->acd_type) {
case AUDIO_CTRL_TYPE_BOOLEAN:
case AUDIO_CTRL_TYPE_STEREO:
case AUDIO_CTRL_TYPE_MONO:
case AUDIO_CTRL_TYPE_METER:
case AUDIO_CTRL_TYPE_ENUM:
break;
default:
audio_dev_warn(d, "bad control type %d for %s "
"not replaced", desc->acd_type, desc->acd_name);
return (NULL);
}
mutex_enter(&d->d_ctrl_lock);
list_remove(&d->d_controls, ctrl);
mutex_exit(&d->d_ctrl_lock);
audio_control_freenames(ctrl);
ctrl->ctrl_read_fn = NULL;
ctrl->ctrl_write_fn = NULL;
ctrl->ctrl_arg = NULL;
ctrl->ctrl_dev = NULL;
}
new_desc = &ctrl->ctrl_des;
new_desc->acd_type = desc->acd_type;
new_desc->acd_flags = desc->acd_flags;
new_desc->acd_maxvalue = desc->acd_maxvalue;
new_desc->acd_minvalue = desc->acd_minvalue;
new_desc->acd_name = strdup(name);
switch (desc->acd_type) {
case AUDIO_CTRL_TYPE_BOOLEAN:
case AUDIO_CTRL_TYPE_STEREO:
case AUDIO_CTRL_TYPE_MONO:
case AUDIO_CTRL_TYPE_METER:
break;
case AUDIO_CTRL_TYPE_ENUM:
for (int bit = 0; bit < 64; bit++) {
if (((1U << bit) & desc->acd_maxvalue) == 0)
continue;
name = desc->acd_enum[bit];
if (name == NULL) {
(void) snprintf(scratch, sizeof (scratch),
"bit%d", bit);
name = scratch;
}
new_desc->acd_enum[bit] = strdup(name);
}
break;
default:
audio_dev_warn(d, "bad control type %d for %s",
desc->acd_type, desc->acd_name);
goto ctrl_fail;
}
ctrl->ctrl_dev = d;
if (new_desc->acd_flags & AUDIO_CTRL_FLAG_READABLE) {
ASSERT(read_fn);
ctrl->ctrl_read_fn = read_fn;
ctrl->ctrl_arg = arg;
}
if (new_desc->acd_flags & AUDIO_CTRL_FLAG_WRITEABLE) {
ASSERT(write_fn);
ctrl->ctrl_write_fn = write_fn;
ctrl->ctrl_arg = arg;
}
mutex_enter(&d->d_ctrl_lock);
list_insert_tail(&d->d_controls, ctrl);
mutex_exit(&d->d_ctrl_lock);
return (ctrl);
ctrl_fail:
if (ctrl) {
audio_control_freenames(ctrl);
kmem_free(ctrl, sizeof (*ctrl));
}
return (NULL);
}
void
audio_dev_del_control(audio_ctrl_t *ctrl)
{
audio_dev_t *d;
ASSERT(ctrl);
d = ctrl->ctrl_dev;
ASSERT(d);
mutex_enter(&d->d_ctrl_lock);
list_remove(&d->d_controls, ctrl);
mutex_exit(&d->d_ctrl_lock);
audio_control_freenames(ctrl);
kmem_free(ctrl, sizeof (*ctrl));
}
void
audio_dev_add_soft_volume(audio_dev_t *d)
{
audio_ctrl_desc_t desc;
bzero(&desc, sizeof (desc));
if (d->d_pcmvol_ctrl == NULL) {
desc.acd_name = AUDIO_CTRL_ID_VOLUME;
desc.acd_type = AUDIO_CTRL_TYPE_MONO;
desc.acd_minvalue = 0;
desc.acd_maxvalue = 100;
desc.acd_flags = AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY |
AUDIO_CTRL_FLAG_PCMVOL;
d->d_pcmvol_ctrl = audio_dev_add_control(d, &desc,
auimpl_get_pcmvol, auimpl_set_pcmvol, d);
d->d_pcmvol = 75;
}
}
void
audio_dev_update_controls(audio_dev_t *d)
{
atomic_inc_uint(&d->d_serial);
}
int
audio_control_read(audio_ctrl_t *ctrl, uint64_t *value)
{
audio_dev_t *d = ctrl->ctrl_dev;
uint64_t my_value;
int ret;
ASSERT(value);
mutex_enter(&d->d_ctrl_lock);
while (d->d_suspended) {
cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
}
if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) {
mutex_exit(&d->d_ctrl_lock);
return (ENXIO);
}
ASSERT(ctrl->ctrl_read_fn);
ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value);
mutex_exit(&d->d_ctrl_lock);
if (ret == 0) {
*value = my_value;
}
return (ret);
}
int
audio_control_write(audio_ctrl_t *ctrl, uint64_t value)
{
int ret;
audio_dev_t *d = ctrl->ctrl_dev;
mutex_enter(&d->d_ctrl_lock);
while (d->d_suspended) {
cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock);
}
if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) {
mutex_exit(&d->d_ctrl_lock);
return (ENXIO);
}
ASSERT(ctrl->ctrl_write_fn);
ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value);
if (ret == 0) {
ctrl->ctrl_saved = value;
ctrl->ctrl_saved_ok = B_TRUE;
}
mutex_exit(&d->d_ctrl_lock);
if (ret == 0) {
audio_dev_update_controls(d);
}
return (ret);
}
int
auimpl_save_controls(audio_dev_t *d)
{
audio_ctrl_t *ctrl;
list_t *l;
int ret;
ASSERT(mutex_owned(&d->d_ctrl_lock));
l = &d->d_controls;
for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
if ((!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) ||
(!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE))) {
continue;
}
ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &ctrl->ctrl_saved);
if (ret != 0) {
audio_dev_warn(d,
"Unable to save value of control %s",
ctrl->ctrl_name);
return (ret);
} else {
ctrl->ctrl_saved_ok = B_TRUE;
}
}
return (0);
}
int
auimpl_restore_controls(audio_dev_t *d)
{
audio_ctrl_t *ctrl;
list_t *l;
int ret;
int rv = 0;
ASSERT(mutex_owned(&d->d_ctrl_lock));
l = &d->d_controls;
for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) {
if (!ctrl->ctrl_saved_ok) {
continue;
}
ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, ctrl->ctrl_saved);
if (ret != 0) {
audio_dev_warn(d,
"Unable to restore value of control %s",
ctrl->ctrl_name);
rv = ret;
}
}
return (rv);
}