qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio
at master 322 lines 9.2 kB view raw
1/* 2 * Copyright (C) 2010 Red Hat, Inc. 3 * 4 * maintained by Gerd Hoffmann <kraxel@redhat.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 or 9 * (at your option) version 3 of the License. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "qemu/osdep.h" 21#include "qemu/host-utils.h" 22#include "qemu/module.h" 23#include "qemu/error-report.h" 24#include "qemu/timer.h" 25#include "ui/qemu-spice.h" 26 27#define AUDIO_CAP "spice" 28#include "audio.h" 29#include "audio_int.h" 30 31#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 32#define LINE_OUT_SAMPLES (480 * 4) 33#else 34#define LINE_OUT_SAMPLES (256 * 4) 35#endif 36 37#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 38#define LINE_IN_SAMPLES (480 * 4) 39#else 40#define LINE_IN_SAMPLES (256 * 4) 41#endif 42 43typedef struct SpiceVoiceOut { 44 HWVoiceOut hw; 45 SpicePlaybackInstance sin; 46 RateCtl rate; 47 int active; 48 uint32_t *frame; 49 uint32_t fpos; 50 uint32_t fsize; 51} SpiceVoiceOut; 52 53typedef struct SpiceVoiceIn { 54 HWVoiceIn hw; 55 SpiceRecordInstance sin; 56 RateCtl rate; 57 int active; 58} SpiceVoiceIn; 59 60static const SpicePlaybackInterface playback_sif = { 61 .base.type = SPICE_INTERFACE_PLAYBACK, 62 .base.description = "playback", 63 .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR, 64 .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR, 65}; 66 67static const SpiceRecordInterface record_sif = { 68 .base.type = SPICE_INTERFACE_RECORD, 69 .base.description = "record", 70 .base.major_version = SPICE_INTERFACE_RECORD_MAJOR, 71 .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, 72}; 73 74static void *spice_audio_init(Audiodev *dev) 75{ 76 if (!using_spice) { 77 return NULL; 78 } 79 return &spice_audio_init; 80} 81 82static void spice_audio_fini (void *opaque) 83{ 84 /* nothing */ 85} 86 87/* playback */ 88 89static int line_out_init(HWVoiceOut *hw, struct audsettings *as, 90 void *drv_opaque) 91{ 92 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); 93 struct audsettings settings; 94 95#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 96 settings.freq = spice_server_get_best_playback_rate(NULL); 97#else 98 settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; 99#endif 100 settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; 101 settings.fmt = AUDIO_FORMAT_S16; 102 settings.endianness = AUDIO_HOST_ENDIANNESS; 103 104 audio_pcm_init_info (&hw->info, &settings); 105 hw->samples = LINE_OUT_SAMPLES; 106 out->active = 0; 107 108 out->sin.base.sif = &playback_sif.base; 109 qemu_spice_add_interface (&out->sin.base); 110#if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 111 spice_server_set_playback_rate(&out->sin, settings.freq); 112#endif 113 return 0; 114} 115 116static void line_out_fini (HWVoiceOut *hw) 117{ 118 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); 119 120 spice_server_remove_interface (&out->sin.base); 121} 122 123static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) 124{ 125 SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); 126 127 if (!out->frame) { 128 spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize); 129 out->fpos = 0; 130 } 131 132 if (out->frame) { 133 *size = audio_rate_get_bytes( 134 &hw->info, &out->rate, 135 (out->fsize - out->fpos) * hw->info.bytes_per_frame); 136 } else { 137 audio_rate_start(&out->rate); 138 } 139 return out->frame + out->fpos; 140} 141 142static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size) 143{ 144 SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); 145 146 assert(buf == out->frame + out->fpos && out->fpos <= out->fsize); 147 out->fpos += size >> 2; 148 149 if (out->fpos == out->fsize) { /* buffer full */ 150 spice_server_playback_put_samples(&out->sin, out->frame); 151 out->frame = NULL; 152 } 153 154 return size; 155} 156 157static void line_out_enable(HWVoiceOut *hw, bool enable) 158{ 159 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); 160 161 if (enable) { 162 if (out->active) { 163 return; 164 } 165 out->active = 1; 166 audio_rate_start(&out->rate); 167 spice_server_playback_start (&out->sin); 168 } else { 169 if (!out->active) { 170 return; 171 } 172 out->active = 0; 173 if (out->frame) { 174 memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2); 175 spice_server_playback_put_samples (&out->sin, out->frame); 176 out->frame = NULL; 177 } 178 spice_server_playback_stop (&out->sin); 179 } 180} 181 182#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2)) 183static void line_out_volume(HWVoiceOut *hw, Volume *vol) 184{ 185 SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); 186 uint16_t svol[2]; 187 188 assert(vol->channels == 2); 189 svol[0] = vol->vol[0] * 257; 190 svol[1] = vol->vol[1] * 257; 191 spice_server_playback_set_volume(&out->sin, 2, svol); 192 spice_server_playback_set_mute(&out->sin, vol->mute); 193} 194#endif 195 196/* record */ 197 198static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 199{ 200 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); 201 struct audsettings settings; 202 203#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 204 settings.freq = spice_server_get_best_record_rate(NULL); 205#else 206 settings.freq = SPICE_INTERFACE_RECORD_FREQ; 207#endif 208 settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; 209 settings.fmt = AUDIO_FORMAT_S16; 210 settings.endianness = AUDIO_HOST_ENDIANNESS; 211 212 audio_pcm_init_info (&hw->info, &settings); 213 hw->samples = LINE_IN_SAMPLES; 214 in->active = 0; 215 216 in->sin.base.sif = &record_sif.base; 217 qemu_spice_add_interface (&in->sin.base); 218#if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 219 spice_server_set_record_rate(&in->sin, settings.freq); 220#endif 221 return 0; 222} 223 224static void line_in_fini (HWVoiceIn *hw) 225{ 226 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); 227 228 spice_server_remove_interface (&in->sin.base); 229} 230 231static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len) 232{ 233 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); 234 uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2; 235 size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read); 236 237 /* XXX: do we need this? */ 238 if (ready == 0) { 239 memset(buf, 0, to_read << 2); 240 ready = to_read; 241 } 242 243 return ready << 2; 244} 245 246static void line_in_enable(HWVoiceIn *hw, bool enable) 247{ 248 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); 249 250 if (enable) { 251 if (in->active) { 252 return; 253 } 254 in->active = 1; 255 audio_rate_start(&in->rate); 256 spice_server_record_start (&in->sin); 257 } else { 258 if (!in->active) { 259 return; 260 } 261 in->active = 0; 262 spice_server_record_stop (&in->sin); 263 } 264} 265 266#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) 267static void line_in_volume(HWVoiceIn *hw, Volume *vol) 268{ 269 SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw); 270 uint16_t svol[2]; 271 272 assert(vol->channels == 2); 273 svol[0] = vol->vol[0] * 257; 274 svol[1] = vol->vol[1] * 257; 275 spice_server_record_set_volume(&in->sin, 2, svol); 276 spice_server_record_set_mute(&in->sin, vol->mute); 277} 278#endif 279 280static struct audio_pcm_ops audio_callbacks = { 281 .init_out = line_out_init, 282 .fini_out = line_out_fini, 283 .write = audio_generic_write, 284 .get_buffer_out = line_out_get_buffer, 285 .put_buffer_out = line_out_put_buffer, 286 .enable_out = line_out_enable, 287#if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \ 288 (SPICE_INTERFACE_PLAYBACK_MINOR >= 2) 289 .volume_out = line_out_volume, 290#endif 291 292 .init_in = line_in_init, 293 .fini_in = line_in_fini, 294 .read = line_in_read, 295 .enable_in = line_in_enable, 296#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) 297 .volume_in = line_in_volume, 298#endif 299}; 300 301static struct audio_driver spice_audio_driver = { 302 .name = "spice", 303 .descr = "spice audio driver", 304 .init = spice_audio_init, 305 .fini = spice_audio_fini, 306 .pcm_ops = &audio_callbacks, 307 .max_voices_out = 1, 308 .max_voices_in = 1, 309 .voice_size_out = sizeof (SpiceVoiceOut), 310 .voice_size_in = sizeof (SpiceVoiceIn), 311}; 312 313void qemu_spice_audio_init (void) 314{ 315 spice_audio_driver.can_be_default = 1; 316} 317 318static void register_audio_spice(void) 319{ 320 audio_driver_register(&spice_audio_driver); 321} 322type_init(register_audio_spice);