--- /dev/null
+diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
+index b27a3e7..86e8ce8 100644
+--- a/audio/alsaaudio.c
++++ b/audio/alsaaudio.c
+@@ -57,10 +57,13 @@ static struct {
+ int buffer_size_out_overridden;
+ int period_size_out_overridden;
+ int verbose;
++
++ const char *volume_control;
+ } conf = {
+ .buffer_size_out = 1024,
+ .pcm_name_out = "default",
+ .pcm_name_in = "default",
++ .volume_control = "Master",
+ };
+
+ struct alsa_params_req {
+@@ -290,6 +293,25 @@ static int alsa_open (int in, struct alsa_params_req *req,
+ return -1;
+ }
+
++ /* Close and then open again: volume control seems to only work
++ * after the device has been closed once. */
++ err = snd_pcm_close(handle);
++ if (err < 0) {
++ alsa_logerr2 (err, typ, "Failed to close `%s':\n", pcm_name);
++ return -1;
++ }
++
++ err = snd_pcm_open (
++ &handle,
++ pcm_name,
++ in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
++ SND_PCM_NONBLOCK
++ );
++ if (err < 0) {
++ alsa_logerr2 (err, typ, "Failed to re-open `%s':\n", pcm_name);
++ return -1;
++ }
++
+ err = snd_pcm_hw_params_any (handle, hw_params);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
+@@ -887,6 +909,73 @@ static void alsa_audio_fini (void *opaque)
+ (void) opaque;
+ }
+
++void alsa_volume(int rvol, int lvol, int mute)
++{
++ static snd_mixer_t *handle = NULL;
++ static const char *card = "default";
++ snd_mixer_elem_t *elem;
++ snd_mixer_selem_id_t *sid;
++ int err, chn, volume;
++
++ snd_mixer_selem_id_alloca(&sid);
++
++ if (handle == NULL) {
++ if ((err = snd_mixer_open(&handle, 0)) < 0) {
++ alsa_logerr(err, "Mixer %s open error: %s\n", card,
++ snd_strerror(err));
++ return;
++ }
++ if ((err = snd_mixer_attach(handle, card)) < 0) {
++ alsa_logerr(err, "Mixer attach %s error: %s", card,
++ snd_strerror(err));
++ snd_mixer_close(handle);
++ handle = NULL;
++ return;
++ }
++ if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
++ alsa_logerr(err, "Mixer register error: %s", snd_strerror(err));
++ snd_mixer_close(handle);
++ handle = NULL;
++ return;
++ }
++ err = snd_mixer_load(handle);
++ if (err < 0) {
++ alsa_logerr(err, "Mixer %s load error: %s", card, snd_strerror(err));
++ snd_mixer_close(handle);
++ handle = NULL;
++ return;
++ }
++ }
++ snd_mixer_selem_id_set_index(sid, 0);
++ snd_mixer_selem_id_set_name(sid, conf.volume_control);
++ elem = snd_mixer_find_selem(handle, sid);
++ if (!elem) {
++ alsa_logerr(ENOENT, "Unable to find simple control '%s',%i\n",
++ snd_mixer_selem_id_get_name(sid),
++ snd_mixer_selem_id_get_index(sid));
++ snd_mixer_close(handle);
++ handle = NULL;
++ return;
++ }
++
++ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
++ if (!snd_mixer_selem_has_playback_channel(elem, chn))
++ continue;
++ if (snd_mixer_selem_has_playback_switch(elem))
++ err = snd_mixer_selem_set_playback_switch(elem, chn, mute);
++ else if (mute)
++ rvol = lvol = 0;
++ volume = (chn == 1) ? rvol : lvol;
++ err = snd_mixer_selem_set_playback_volume(elem, chn, volume);
++ if (err < 0) {
++ alsa_logerr(err, "Unable to set volume for channel %d\n", chn);
++ snd_mixer_close(handle);
++ handle = NULL;
++ return;
++ }
++ }
++}
++
+ static struct audio_option alsa_options[] = {
+ {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
+ "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+@@ -918,6 +1007,9 @@ static struct audio_option alsa_options[] = {
+ {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
+ "Behave in a more verbose way", NULL, 0},
+
++ {"VOL_CTRL", AUD_OPT_STR, &conf.volume_control,
++ "Volume control voice name", NULL, 0},
++
+ {NULL, 0, NULL, NULL, NULL, 0}
+ };
+
+diff --git a/audio/audio.h b/audio/audio.h
+index 4aaeb96..b09cce5 100644
+--- a/audio/audio.h
++++ b/audio/audio.h
+@@ -173,4 +173,6 @@ uint32_t lsbindex (uint32_t u);
+ int wav_start_capture (CaptureState *s, const char *path, int freq,
+ int bits, int nchannels);
+
++void alsa_volume(int, int, int);
++
+ #endif /* audio.h */
+diff --git a/hw/ac97.c b/hw/ac97.c
+index ade2719..5ec3044 100644
+--- a/hw/ac97.c
++++ b/hw/ac97.c
+@@ -172,7 +172,8 @@ enum {
+ #ifdef DEBUG_AC97
+ #define dolog(...) AUD_log ("ac97", __VA_ARGS__)
+ #else
+-#define dolog(...)
++extern int term_printf(const char *, ...);
++#define dolog(...) term_printf(__VA_ARGS__)
+ #endif
+
+ typedef struct PCIAC97LinkState {
+@@ -527,6 +528,22 @@ static void record_select (AC97LinkState *s, uint32_t val)
+ ls = aud_to_ac97_record_source (als);
+ mixer_store (s, AC97_Record_Select, rs | (ls << 8));
+ }
++#else
++static void set_volume (AC97LinkState *s, int index,
++ /* audmixerctl_t mt, */ uint32_t val)
++{
++ int mute = (val >> MUTE_SHIFT) & 1;
++ uint8_t rvol = VOL_MASK - (val & VOL_MASK);
++ uint8_t lvol = VOL_MASK - ((val >> 8) & VOL_MASK);
++
++ rvol = 255 * rvol / VOL_MASK;
++ lvol = 255 * lvol / VOL_MASK;
++
++ if (index == AC97_Master_Volume_Mute)
++ alsa_volume(rvol, lvol, mute);
++
++ mixer_store (s, index, val);
++}
+ #endif
+
+ static void mixer_reset (AC97LinkState *s)
+@@ -568,6 +585,8 @@ static void mixer_reset (AC97LinkState *s)
+ set_volume (s, AC97_Master_Volume_Mute, AUD_MIXER_VOLUME , 0x8000);
+ set_volume (s, AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM , 0x8808);
+ set_volume (s, AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN, 0x8808);
++#else
++ set_volume (s, AC97_Master_Volume_Mute, /* AUD_MIXER_VOLUME , */ 0x8000);
+ #endif
+ reset_voices (s, active);
+ }
+@@ -632,10 +651,10 @@ static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
+ val |= mixer_load (s, index) & 0xf;
+ mixer_store (s, index, val);
+ break;
+-#ifdef USE_MIXER
+ case AC97_Master_Volume_Mute:
+- set_volume (s, index, AUD_MIXER_VOLUME, val);
++ set_volume (s, index, /* AUD_MIXER_VOLUME, */ val);
+ break;
++#ifdef USE_MIXER
+ case AC97_PCM_Out_Volume_Mute:
+ set_volume (s, index, AUD_MIXER_PCM, val);
+ break;
+diff --git a/monitor.c b/monitor.c
+index c8fcab7..94f13f6 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -1583,6 +1583,8 @@ static const term_cmd_t term_cmds[] = {
+ "target", "request VM to change it's memory allocation (in MB)" },
+ { "set_link", "ss", do_set_link,
+ "name [up|down]", "change the link status of a network adapter" },
++ { "volume", "iii", alsa_volume, "volume",
++ "set alsa volume (right, left, mute)" },
+ { NULL, NULL, },
+ };
+