instead of using the mixer ioctl()s on the dsp fd, open a seperate fd for the mixer. This allows us the keep the mixer fd open while closing the dsp device while suspending.

git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/lennart@1893 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2007-09-23 21:03:24 +00:00
parent fc00eaf1d4
commit 77ed60ce4c
3 changed files with 292 additions and 194 deletions

View file

@ -36,8 +36,6 @@
* *
*/ */
/* TODO: handle restoring of volume after suspend */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif #endif
@ -115,14 +113,14 @@ struct userdata {
int use_getospace, use_getispace; int use_getospace, use_getispace;
int use_getodelay; int use_getodelay;
int use_pcm_volume;
int use_input_volume;
int sink_suspended, source_suspended; int sink_suspended, source_suspended;
int fd; int fd;
int mode; int mode;
int mixer_fd;
int mixer_devmask;
int nfrags, frag_size; int nfrags, frag_size;
int use_mmap; int use_mmap;
@ -583,6 +581,11 @@ static int unsuspend(struct userdata *u) {
pollfd->events = 0; pollfd->events = 0;
pollfd->revents = 0; pollfd->revents = 0;
if (u->sink)
pa_sink_get_volume(u->sink);
if (u->source)
pa_source_get_volume(u->source);
pa_log_info("Resumed successfully..."); pa_log_info("Resumed successfully...");
return 0; return 0;
@ -659,31 +662,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break; break;
case PA_SINK_MESSAGE_SET_VOLUME:
if (u->use_pcm_volume && u->fd >= 0) {
if (pa_oss_set_pcm_volume(u->fd, &u->sink->sample_spec, ((pa_cvolume*) data)) < 0) {
pa_log_info("Device doesn't support setting mixer settings: %s", pa_cstrerror(errno));
u->use_pcm_volume = 0;
} else
return 0;
}
break;
case PA_SINK_MESSAGE_GET_VOLUME:
if (u->use_pcm_volume && u->fd >= 0) {
if (pa_oss_get_pcm_volume(u->fd, &u->sink->sample_spec, ((pa_cvolume*) data)) < 0) {
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
u->use_pcm_volume = 0;
} else
return 0;
}
break;
} }
ret = pa_sink_process_msg(o, code, data, offset, chunk); ret = pa_sink_process_msg(o, code, data, offset, chunk);
@ -757,31 +735,6 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
} }
break; break;
case PA_SOURCE_MESSAGE_SET_VOLUME:
if (u->use_input_volume && u->fd >= 0) {
if (pa_oss_set_input_volume(u->fd, &u->source->sample_spec, ((pa_cvolume*) data)) < 0) {
pa_log_info("Device doesn't support setting mixer settings: %s", pa_cstrerror(errno));
u->use_input_volume = 0;
} else
return 0;
}
break;
case PA_SOURCE_MESSAGE_GET_VOLUME:
if (u->use_input_volume && u->fd >= 0) {
if (pa_oss_get_input_volume(u->fd, &u->source->sample_spec, ((pa_cvolume*) data)) < 0) {
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
u->use_input_volume = 0;
} else
return 0;
}
break;
} }
ret = pa_source_process_msg(o, code, data, offset, chunk); ret = pa_source_process_msg(o, code, data, offset, chunk);
@ -792,6 +745,86 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return ret; return ret;
} }
static int sink_get_volume(pa_sink *s) {
struct userdata *u;
int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0)
return r;
if (u->mixer_devmask & SOUND_MASK_PCM)
if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0)
return r;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
return -1;
}
static int sink_set_volume(pa_sink *s) {
struct userdata *u;
int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0)
return r;
if (u->mixer_devmask & SOUND_MASK_PCM)
if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0)
return r;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
return -1;
}
static int source_get_volume(pa_source *s) {
struct userdata *u;
int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0)
return r;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0)
return r;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
return -1;
}
static int source_set_volume(pa_source *s) {
struct userdata *u;
int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0)
return r;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0)
return r;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
return -1;
}
static void thread_func(void *userdata) { static void thread_func(void *userdata) {
struct userdata *u = userdata; struct userdata *u = userdata;
int write_type = 0, read_type = 0; int write_type = 0, read_type = 0;
@ -1135,9 +1168,9 @@ int pa__init(pa_module*m) {
u->module = m; u->module = m;
m->userdata = u; m->userdata = u;
u->fd = fd; u->fd = fd;
u->mixer_fd = -1;
u->use_getospace = u->use_getispace = 1; u->use_getospace = u->use_getispace = 1;
u->use_getodelay = 1; u->use_getodelay = 1;
u->use_input_volume = u->use_pcm_volume = 1;
u->mode = mode; u->mode = mode;
u->frame_size = pa_frame_size(&ss); u->frame_size = pa_frame_size(&ss);
u->device_name = pa_xstrdup(dev); u->device_name = pa_xstrdup(dev);
@ -1210,7 +1243,7 @@ int pa__init(pa_module*m) {
hwdesc[0] ? ")" : "", hwdesc[0] ? ")" : "",
use_mmap ? " via DMA" : "")); use_mmap ? " via DMA" : ""));
pa_xfree(t); pa_xfree(t);
u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_CAN_SUSPEND|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL; u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_CAN_SUSPEND|PA_SOURCE_LATENCY;
u->source->refresh_volume = 1; u->source->refresh_volume = 1;
if (use_mmap) if (use_mmap)
@ -1265,13 +1298,44 @@ int pa__init(pa_module*m) {
hwdesc[0] ? ")" : "", hwdesc[0] ? ")" : "",
use_mmap ? " via DMA" : "")); use_mmap ? " via DMA" : ""));
pa_xfree(t); pa_xfree(t);
u->sink->flags = PA_SINK_HARDWARE|PA_SINK_CAN_SUSPEND|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL; u->sink->flags = PA_SINK_HARDWARE|PA_SINK_CAN_SUSPEND|PA_SINK_LATENCY;
u->sink->refresh_volume = 1; u->sink->refresh_volume = 1;
if (use_mmap) if (use_mmap)
u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags); u->out_mmap_memblocks = pa_xnew0(pa_memblock*, u->out_nfrags);
} }
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
int do_close = 1;
u->mixer_devmask = 0;
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
else {
if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) {
pa_log_debug("Found hardware mixer track for playback.");
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
u->sink->get_volume = sink_get_volume;
u->sink->set_volume = sink_set_volume;
do_close = 0;
}
if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) {
pa_log_debug("Found hardware mixer track for recording.");
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
u->source->get_volume = source_get_volume;
u->source->set_volume = source_set_volume;
do_close = 0;
}
}
if (do_close) {
pa_close(u->mixer_fd);
u->mixer_fd = -1;
}
}
go_on: go_on:
pa_assert(u->source || u->sink); pa_assert(u->source || u->sink);
@ -1284,10 +1348,10 @@ go_on:
} }
/* Read mixer settings */ /* Read mixer settings */
if (u->source) if (u->sink && u->sink->get_volume)
pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL); sink_get_volume(u->sink);
if (u->sink) if (u->source && u->source->get_volume)
pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL); source_get_volume(u->source);
if (u->sink) if (u->sink)
pa_sink_put(u->sink); pa_sink_put(u->sink);
@ -1372,6 +1436,9 @@ void pa__done(pa_module*m) {
if (u->fd >= 0) if (u->fd >= 0)
pa_close(u->fd); pa_close(u->fd);
if (u->mixer_fd >= 0)
pa_close(u->mixer_fd);
pa_xfree(u->device_name); pa_xfree(u->device_name);
pa_xfree(u); pa_xfree(u);

View file

@ -36,6 +36,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h> #include <pulsecore/core-error.h>
#include <pulsecore/core-util.h> #include <pulsecore/core-util.h>
#include <pulsecore/log.h> #include <pulsecore/log.h>
@ -248,7 +249,7 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
return 0; return 0;
} }
static int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) { int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX]; char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol; unsigned vol;
@ -259,16 +260,18 @@ static int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvo
if (ioctl(fd, mixer, &vol) < 0) if (ioctl(fd, mixer, &vol) < 0)
return -1; return -1;
pa_cvolume_reset(volume, ss->channels);
volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100; volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100;
if ((volume->channels = ss->channels) >= 2) if (volume->channels >= 2)
volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100; volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;
pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume)); pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
return 0; return 0;
} }
static int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) { int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
char cv[PA_CVOLUME_SNPRINT_MAX]; char cv[PA_CVOLUME_SNPRINT_MAX];
unsigned vol; unsigned vol;
pa_volume_t l, r; pa_volume_t l, r;
@ -289,40 +292,38 @@ static int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const
return 0; return 0;
} }
int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) { static int get_device_number(const char *dev) {
return pa_oss_get_volume(fd, SOUND_MIXER_READ_PCM, ss, volume); char buf[PATH_MAX];
} const char *p, *e;
int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) { if (readlink(dev, buf, sizeof(buf)) < 0) {
return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_PCM, ss, volume); if (errno != EINVAL && errno != ENOLINK)
} return -1;
int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume) { p = dev;
return pa_oss_get_volume(fd, SOUND_MIXER_READ_IGAIN, ss, volume); } else
} p = buf;
int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume) { if ((e = strrchr(p, '/')))
return pa_oss_set_volume(fd, SOUND_MIXER_WRITE_IGAIN, ss, volume); p = e+1;
if (p == 0)
return 0;
p = strchr(p, 0) -1;
if (*p >= '0' && *p <= '9')
return *p - '0';
return -1;
} }
int pa_oss_get_hw_description(const char *dev, char *name, size_t l) { int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
FILE *f; FILE *f;
const char *e = NULL;
int n, r = -1; int n, r = -1;
int b = 0; int b = 0;
if (strncmp(dev, "/dev/dsp", 8) == 0) if ((n = get_device_number(dev)) < 0)
e = dev+8;
else if (strncmp(dev, "/dev/adsp", 9) == 0)
e = dev+9;
else
return -1;
if (*e == 0)
n = 0;
else if (*e >= '0' && *e <= '9' && *(e+1) == 0)
n = *e - '0';
else
return -1; return -1;
if (!(f = fopen("/dev/sndstat", "r")) && if (!(f = fopen("/dev/sndstat", "r")) &&
@ -373,3 +374,34 @@ int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
fclose(f); fclose(f);
return r; return r;
} }
static int open_mixer(const char *mixer) {
int fd;
if ((fd = open(mixer, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0)
return fd;
return -1;
}
int pa_oss_open_mixer_for_device(const char *device) {
int n;
char *fn;
int fd;
if ((n = get_device_number(device)) < 0)
return -1;
if (n == 0)
if ((fd = open_mixer("/dev/mixer")) >= 0)
return fd;
fn = pa_sprintf_malloc("/dev/mixer%i", n);
fd = open_mixer(fn);
pa_xfree(fn);
if (fd < 0)
pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
return fd;
}

View file

@ -33,12 +33,11 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss);
int pa_oss_set_fragments(int fd, int frags, int frag_size); int pa_oss_set_fragments(int fd, int frags, int frag_size);
int pa_oss_get_pcm_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume); int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume);
int pa_oss_set_pcm_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume); int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_get_input_volume(int fd, const pa_sample_spec *ss, pa_cvolume *volume);
int pa_oss_set_input_volume(int fd, const pa_sample_spec *ss, const pa_cvolume *volume);
int pa_oss_get_hw_description(const char *dev, char *name, size_t l); int pa_oss_get_hw_description(const char *dev, char *name, size_t l);
int pa_oss_open_mixer_for_device(const char *device);
#endif #endif