usb: fixes for ohci, xhci, mtp and redirect
audio: latency fixes ui: opengl and cocoa fixes firmware: ovmf tabel aprser fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmIiH8cACgkQTLbY7tPo cTjgJQ/8DaMEICUApnDgXjoogg80gTc+5eyfXnn3YzSv6dWJV/beVQMZaQZ0NY7U QYFEhmaboda6Kh246EC633CfS4YR06oPHUkt+XqtwKXen/gnr684yj63lZvJDiB3 uRnwMJe4F4OSyubtBrCHPG40NoXML9h5/VmJOdLBToD6bkZEkpxxasN2IhxtmXJi 3fuHK1At9t9pgcntrzZYrSD887k01MwmKIr8Xl5/Ysr+yZ+xrV9gt8STvjgx3L3K z4pzk6s+yC/pps+t2XO7cewVp7z06ko84UHlSxq+zqn9obzCR2o+OSnrusFZi+8K a8QuXYmMtxC6IhwxSrhBxRjQESiMiA53drRb4bfLWBXooskbiHsRHiKBWqE5b7m2 9UMONsewRJ3K4frGOlGtBpj9jIJ5KIQ3z/7xodGBybwuZWVvzPiwVypm7cSWrEzK LcxImkwKGxG3wtt+xGGlmQXuOJ/VGIiJYCMyFSCGbXRrisUkCIEcGjixpIA5NhEj iLOlev1NaRrhWP/h9uw40NNmxqiHhX4OG5VXtPkTpWf2kE2EIoiseUgTb+2Px9Mt wCxuXTDsCyf67mSa9BCOwABqKBdx+N6HHs/Ol4AnqLFVXLsQ1C85BNpgycm3duhJ aLO6dIH3jBVH2dcEpQJAM5zeqeBgRrM3tSjm2pX9mLp3rCNH7ac= =ms76 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/kraxel-20220304-pull-request' into staging usb: fixes for ohci, xhci, mtp and redirect audio: latency fixes ui: opengl and cocoa fixes firmware: ovmf tabel aprser fixes # gpg: Signature made Fri 04 Mar 2022 14:18:47 GMT # gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/kraxel-20220304-pull-request: (35 commits) hw/display/vmware_vga: replace fprintf calls with trace events edid: Fix clock of Detailed Timing Descriptor softmmu/qdev-monitor: Add virtio-gpu-gl aliases ui/cocoa: Add Services menu ui/clipboard: fix use-after-free regression ui: do not create a surface when resizing a GL scanout ui/console: fix texture leak when calling surface_gl_create_texture() ui/console: fix crash when using gl context with non-gl listeners docs: Add spec of OVMF GUIDed table for SEV guests hw/i386: Replace magic number with field length calculation hw/i386: Improve bounds checking in OVMF table parsing coreaudio: Notify error in coreaudio_init_out hw/usb/redirect.c: Stop using qemu_oom_check() sdlaudio: fix samples vs. frames mix-up paaudio: fix samples vs. frames mix-up ossaudio: reduce effective playback buffer size dsoundaudio: reduce effective playback buffer size paaudio: reduce effective playback buffer size audio: restore mixing-engine playback buffer size Revert "audio: fix wavcapture segfault" ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5c8463886d
@ -916,6 +916,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
|||||||
.init_out = alsa_init_out,
|
.init_out = alsa_init_out,
|
||||||
.fini_out = alsa_fini_out,
|
.fini_out = alsa_fini_out,
|
||||||
.write = alsa_write,
|
.write = alsa_write,
|
||||||
|
.buffer_get_free = audio_generic_buffer_get_free,
|
||||||
.run_buffer_out = audio_generic_run_buffer_out,
|
.run_buffer_out = audio_generic_run_buffer_out,
|
||||||
.enable_out = alsa_enable_out,
|
.enable_out = alsa_enable_out,
|
||||||
|
|
||||||
|
194
audio/audio.c
194
audio/audio.c
@ -548,65 +548,45 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
|
|||||||
return live;
|
return live;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
|
static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
|
||||||
{
|
{
|
||||||
size_t clipped = 0;
|
size_t conv = 0;
|
||||||
size_t pos = hw->mix_buf->pos;
|
STSampleBuffer *conv_buf = hw->conv_buf;
|
||||||
|
|
||||||
while (len) {
|
while (samples) {
|
||||||
st_sample *src = hw->mix_buf->samples + pos;
|
uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);
|
||||||
uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
|
size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);
|
||||||
size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
|
|
||||||
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
|
|
||||||
|
|
||||||
hw->clip(dst, src, samples_to_clip);
|
hw->conv(conv_buf->samples + conv_buf->pos, src, proc);
|
||||||
|
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
|
||||||
pos = (pos + samples_to_clip) % hw->mix_buf->size;
|
samples -= proc;
|
||||||
len -= samples_to_clip;
|
conv += proc;
|
||||||
clipped += samples_to_clip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return conv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Soft voice (capture)
|
* Soft voice (capture)
|
||||||
*/
|
*/
|
||||||
static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
|
|
||||||
{
|
|
||||||
HWVoiceIn *hw = sw->hw;
|
|
||||||
ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
|
|
||||||
ssize_t rpos;
|
|
||||||
|
|
||||||
if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) {
|
|
||||||
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
rpos = hw->conv_buf->pos - live;
|
|
||||||
if (rpos >= 0) {
|
|
||||||
return rpos;
|
|
||||||
} else {
|
|
||||||
return hw->conv_buf->size + rpos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||||
{
|
{
|
||||||
HWVoiceIn *hw = sw->hw;
|
HWVoiceIn *hw = sw->hw;
|
||||||
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
|
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
|
||||||
struct st_sample *src, *dst = sw->buf;
|
struct st_sample *src, *dst = sw->buf;
|
||||||
|
|
||||||
rpos = audio_pcm_sw_get_rpos_in(sw) % hw->conv_buf->size;
|
|
||||||
|
|
||||||
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
|
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||||
|
if (!live) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||||
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
|
||||||
|
|
||||||
samples = size / sw->info.bytes_per_frame;
|
samples = size / sw->info.bytes_per_frame;
|
||||||
if (!live) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
swlim = (live * sw->ratio) >> 32;
|
swlim = (live * sw->ratio) >> 32;
|
||||||
swlim = MIN (swlim, samples);
|
swlim = MIN (swlim, samples);
|
||||||
@ -632,7 +612,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
|||||||
total += isamp;
|
total += isamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hw->pcm_ops && !hw->pcm_ops->volume_in) {
|
if (!hw->pcm_ops->volume_in) {
|
||||||
mixeng_volume (sw->buf, ret, &sw->vol);
|
mixeng_volume (sw->buf, ret, &sw->vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,12 +663,38 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)
|
||||||
|
{
|
||||||
|
return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :
|
||||||
|
INT_MAX) / hw->info.bytes_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
|
||||||
|
{
|
||||||
|
size_t clipped = 0;
|
||||||
|
size_t pos = hw->mix_buf->pos;
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
st_sample *src = hw->mix_buf->samples + pos;
|
||||||
|
uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
|
||||||
|
size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
|
||||||
|
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
|
||||||
|
|
||||||
|
hw->clip(dst, src, samples_to_clip);
|
||||||
|
|
||||||
|
pos = (pos + samples_to_clip) % hw->mix_buf->size;
|
||||||
|
len -= samples_to_clip;
|
||||||
|
clipped += samples_to_clip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Soft voice (playback)
|
* Soft voice (playback)
|
||||||
*/
|
*/
|
||||||
static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||||
{
|
{
|
||||||
size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
|
size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, blck;
|
||||||
|
size_t hw_free;
|
||||||
size_t ret = 0, pos = 0, total = 0;
|
size_t ret = 0, pos = 0, total = 0;
|
||||||
|
|
||||||
if (!sw) {
|
if (!sw) {
|
||||||
@ -711,27 +717,28 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
|
wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
|
||||||
samples = size / sw->info.bytes_per_frame;
|
|
||||||
|
|
||||||
dead = hwsamples - live;
|
dead = hwsamples - live;
|
||||||
swlim = ((int64_t) dead << 32) / sw->ratio;
|
hw_free = audio_pcm_hw_get_free(sw->hw);
|
||||||
swlim = MIN (swlim, samples);
|
hw_free = hw_free > live ? hw_free - live : 0;
|
||||||
if (swlim) {
|
samples = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio;
|
||||||
sw->conv (sw->buf, buf, swlim);
|
samples = MIN(samples, size / sw->info.bytes_per_frame);
|
||||||
|
if (samples) {
|
||||||
|
sw->conv(sw->buf, buf, samples);
|
||||||
|
|
||||||
if (sw->hw->pcm_ops && !sw->hw->pcm_ops->volume_out) {
|
if (!sw->hw->pcm_ops->volume_out) {
|
||||||
mixeng_volume (sw->buf, swlim, &sw->vol);
|
mixeng_volume(sw->buf, samples, &sw->vol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (swlim) {
|
while (samples) {
|
||||||
dead = hwsamples - live;
|
dead = hwsamples - live;
|
||||||
left = hwsamples - wpos;
|
left = hwsamples - wpos;
|
||||||
blck = MIN (dead, left);
|
blck = MIN (dead, left);
|
||||||
if (!blck) {
|
if (!blck) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
isamp = swlim;
|
isamp = samples;
|
||||||
osamp = blck;
|
osamp = blck;
|
||||||
st_rate_flow_mix (
|
st_rate_flow_mix (
|
||||||
sw->rate,
|
sw->rate,
|
||||||
@ -741,7 +748,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
|||||||
&osamp
|
&osamp
|
||||||
);
|
);
|
||||||
ret += isamp;
|
ret += isamp;
|
||||||
swlim -= isamp;
|
samples -= isamp;
|
||||||
pos += isamp;
|
pos += isamp;
|
||||||
live += osamp;
|
live += osamp;
|
||||||
wpos = (wpos + osamp) % hwsamples;
|
wpos = (wpos + osamp) % hwsamples;
|
||||||
@ -1003,6 +1010,11 @@ static size_t audio_get_avail (SWVoiceIn *sw)
|
|||||||
return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame;
|
return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t audio_sw_bytes_free(SWVoiceOut *sw, size_t free)
|
||||||
|
{
|
||||||
|
return (((int64_t)free << 32) / sw->ratio) * sw->info.bytes_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t audio_get_free(SWVoiceOut *sw)
|
static size_t audio_get_free(SWVoiceOut *sw)
|
||||||
{
|
{
|
||||||
size_t live, dead;
|
size_t live, dead;
|
||||||
@ -1022,13 +1034,11 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
|||||||
dead = sw->hw->mix_buf->size - live;
|
dead = sw->hw->mix_buf->size - live;
|
||||||
|
|
||||||
#ifdef DEBUG_OUT
|
#ifdef DEBUG_OUT
|
||||||
dolog ("%s: get_free live %zu dead %zu ret %" PRId64 "\n",
|
dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n",
|
||||||
SW_NAME (sw),
|
SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead));
|
||||||
live, dead, (((int64_t) dead << 32) / sw->ratio) *
|
|
||||||
sw->info.bytes_per_frame);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return (((int64_t) dead << 32) / sw->ratio) * sw->info.bytes_per_frame;
|
return dead;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
|
static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
|
||||||
@ -1132,9 +1142,27 @@ static void audio_run_out (AudioState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
|
while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
|
||||||
size_t played, live, prev_rpos, free;
|
size_t played, live, prev_rpos;
|
||||||
|
size_t hw_free = audio_pcm_hw_get_free(hw);
|
||||||
int nb_live;
|
int nb_live;
|
||||||
|
|
||||||
|
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
|
||||||
|
if (sw->active) {
|
||||||
|
size_t sw_free = audio_get_free(sw);
|
||||||
|
size_t free;
|
||||||
|
|
||||||
|
if (hw_free > sw->total_hw_samples_mixed) {
|
||||||
|
free = audio_sw_bytes_free(sw,
|
||||||
|
MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
|
||||||
|
} else {
|
||||||
|
free = 0;
|
||||||
|
}
|
||||||
|
if (free > 0) {
|
||||||
|
sw->callback.fn(sw->callback.opaque, free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
live = audio_pcm_hw_get_live_out (hw, &nb_live);
|
live = audio_pcm_hw_get_live_out (hw, &nb_live);
|
||||||
if (!nb_live) {
|
if (!nb_live) {
|
||||||
live = 0;
|
live = 0;
|
||||||
@ -1163,14 +1191,6 @@ static void audio_run_out (AudioState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!live) {
|
if (!live) {
|
||||||
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
|
|
||||||
if (sw->active) {
|
|
||||||
free = audio_get_free (sw);
|
|
||||||
if (free > 0) {
|
|
||||||
sw->callback.fn (sw->callback.opaque, free);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hw->pcm_ops->run_buffer_out) {
|
if (hw->pcm_ops->run_buffer_out) {
|
||||||
hw->pcm_ops->run_buffer_out(hw);
|
hw->pcm_ops->run_buffer_out(hw);
|
||||||
}
|
}
|
||||||
@ -1211,13 +1231,6 @@ static void audio_run_out (AudioState *s)
|
|||||||
if (!sw->total_hw_samples_mixed) {
|
if (!sw->total_hw_samples_mixed) {
|
||||||
sw->empty = 1;
|
sw->empty = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sw->active) {
|
|
||||||
free = audio_get_free (sw);
|
|
||||||
if (free > 0) {
|
|
||||||
sw->callback.fn (sw->callback.opaque, free);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1225,7 +1238,6 @@ static void audio_run_out (AudioState *s)
|
|||||||
static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
|
static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
|
||||||
{
|
{
|
||||||
size_t conv = 0;
|
size_t conv = 0;
|
||||||
STSampleBuffer *conv_buf = hw->conv_buf;
|
|
||||||
|
|
||||||
if (hw->pcm_ops->run_buffer_in) {
|
if (hw->pcm_ops->run_buffer_in) {
|
||||||
hw->pcm_ops->run_buffer_in(hw);
|
hw->pcm_ops->run_buffer_in(hw);
|
||||||
@ -1241,11 +1253,7 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
proc = MIN(size / hw->info.bytes_per_frame,
|
proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);
|
||||||
conv_buf->size - conv_buf->pos);
|
|
||||||
|
|
||||||
hw->conv(conv_buf->samples + conv_buf->pos, buf, proc);
|
|
||||||
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
|
|
||||||
|
|
||||||
samples -= proc;
|
samples -= proc;
|
||||||
conv += proc;
|
conv += proc;
|
||||||
@ -1394,12 +1402,10 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw)
|
|||||||
|
|
||||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
||||||
{
|
{
|
||||||
ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
|
size_t start;
|
||||||
|
|
||||||
if (start < 0) {
|
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
|
||||||
start += hw->size_emul;
|
assert(start < hw->size_emul);
|
||||||
}
|
|
||||||
assert(start >= 0 && start < hw->size_emul);
|
|
||||||
|
|
||||||
*size = MIN(*size, hw->pending_emul);
|
*size = MIN(*size, hw->pending_emul);
|
||||||
*size = MIN(*size, hw->size_emul - start);
|
*size = MIN(*size, hw->size_emul - start);
|
||||||
@ -1412,16 +1418,22 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
|
|||||||
hw->pending_emul -= size;
|
hw->pending_emul -= size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t audio_generic_buffer_get_free(HWVoiceOut *hw)
|
||||||
|
{
|
||||||
|
if (hw->buf_emul) {
|
||||||
|
return hw->size_emul - hw->pending_emul;
|
||||||
|
} else {
|
||||||
|
return hw->samples * hw->info.bytes_per_frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void audio_generic_run_buffer_out(HWVoiceOut *hw)
|
void audio_generic_run_buffer_out(HWVoiceOut *hw)
|
||||||
{
|
{
|
||||||
while (hw->pending_emul) {
|
while (hw->pending_emul) {
|
||||||
size_t write_len, written;
|
size_t write_len, written, start;
|
||||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
|
||||||
|
|
||||||
if (start < 0) {
|
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
|
||||||
start += hw->size_emul;
|
assert(start < hw->size_emul);
|
||||||
}
|
|
||||||
assert(start >= 0 && start < hw->size_emul);
|
|
||||||
|
|
||||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||||
|
|
||||||
@ -1462,6 +1474,12 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
|
|||||||
{
|
{
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
|
|
||||||
|
if (hw->pcm_ops->buffer_get_free) {
|
||||||
|
size_t free = hw->pcm_ops->buffer_get_free(hw);
|
||||||
|
|
||||||
|
size = MIN(size, free);
|
||||||
|
}
|
||||||
|
|
||||||
while (total < size) {
|
while (total < size) {
|
||||||
size_t dst_size = size - total;
|
size_t dst_size = size - total;
|
||||||
size_t copy_size, proc;
|
size_t copy_size, proc;
|
||||||
@ -1821,6 +1839,7 @@ void AUD_remove_card (QEMUSoundCard *card)
|
|||||||
g_free (card->name);
|
g_free (card->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct audio_pcm_ops capture_pcm_ops;
|
||||||
|
|
||||||
CaptureVoiceOut *AUD_add_capture(
|
CaptureVoiceOut *AUD_add_capture(
|
||||||
AudioState *s,
|
AudioState *s,
|
||||||
@ -1866,6 +1885,7 @@ CaptureVoiceOut *AUD_add_capture(
|
|||||||
|
|
||||||
hw = &cap->hw;
|
hw = &cap->hw;
|
||||||
hw->s = s;
|
hw->s = s;
|
||||||
|
hw->pcm_ops = &capture_pcm_ops;
|
||||||
QLIST_INIT (&hw->sw_head);
|
QLIST_INIT (&hw->sw_head);
|
||||||
QLIST_INIT (&cap->cb_head);
|
QLIST_INIT (&cap->cb_head);
|
||||||
|
|
||||||
|
@ -161,10 +161,14 @@ struct audio_pcm_ops {
|
|||||||
void (*fini_out)(HWVoiceOut *hw);
|
void (*fini_out)(HWVoiceOut *hw);
|
||||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||||
void (*run_buffer_out)(HWVoiceOut *hw);
|
void (*run_buffer_out)(HWVoiceOut *hw);
|
||||||
|
/*
|
||||||
|
* Get the free output buffer size. This is an upper limit. The size
|
||||||
|
* returned by function get_buffer_out may be smaller.
|
||||||
|
*/
|
||||||
|
size_t (*buffer_get_free)(HWVoiceOut *hw);
|
||||||
/*
|
/*
|
||||||
* get a buffer that after later can be passed to put_buffer_out; optional
|
* get a buffer that after later can be passed to put_buffer_out; optional
|
||||||
* returns the buffer, and writes it's size to size (in bytes)
|
* returns the buffer, and writes it's size to size (in bytes)
|
||||||
* this is unrelated to the above buffer_size_out function
|
|
||||||
*/
|
*/
|
||||||
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
|
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
|
||||||
/*
|
/*
|
||||||
@ -190,6 +194,7 @@ void audio_generic_run_buffer_in(HWVoiceIn *hw);
|
|||||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||||
void audio_generic_run_buffer_out(HWVoiceOut *hw);
|
void audio_generic_run_buffer_out(HWVoiceOut *hw);
|
||||||
|
size_t audio_generic_buffer_get_free(HWVoiceOut *hw);
|
||||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
||||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
||||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
||||||
@ -266,6 +271,19 @@ static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
|
|||||||
return (dst >= src) ? (dst - src) : (len - src + dst);
|
return (dst >= src) ? (dst - src) : (len - src + dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* audio_ring_posb() - returns new position in ringbuffer in backward
|
||||||
|
* direction at given distance
|
||||||
|
*
|
||||||
|
* @pos: current position in ringbuffer
|
||||||
|
* @dist: distance in ringbuffer to walk in reverse direction
|
||||||
|
* @len: size of ringbuffer
|
||||||
|
*/
|
||||||
|
static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
|
||||||
|
{
|
||||||
|
return pos >= dist ? pos - dist : len - dist + pos;
|
||||||
|
}
|
||||||
|
|
||||||
#define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__)
|
#define dolog(fmt, ...) AUD_log(AUDIO_CAP, fmt, ## __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -283,6 +283,7 @@ static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
|||||||
coreaudio_buf_unlock(core, "coreaudio_" #name); \
|
coreaudio_buf_unlock(core, "coreaudio_" #name); \
|
||||||
return ret; \
|
return ret; \
|
||||||
}
|
}
|
||||||
|
COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
|
||||||
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||||
(hw, size))
|
(hw, size))
|
||||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
|
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||||
@ -333,12 +334,10 @@ static OSStatus audioDeviceIOProc(
|
|||||||
|
|
||||||
len = frameCount * hw->info.bytes_per_frame;
|
len = frameCount * hw->info.bytes_per_frame;
|
||||||
while (len) {
|
while (len) {
|
||||||
size_t write_len;
|
size_t write_len, start;
|
||||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
|
||||||
if (start < 0) {
|
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
|
||||||
start += hw->size_emul;
|
assert(start < hw->size_emul);
|
||||||
}
|
|
||||||
assert(start >= 0 && start < hw->size_emul);
|
|
||||||
|
|
||||||
write_len = MIN(MIN(hw->pending_emul, len),
|
write_len = MIN(MIN(hw->pending_emul, len),
|
||||||
hw->size_emul - start);
|
hw->size_emul - start);
|
||||||
@ -604,6 +603,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||||||
coreaudio_playback_logerr(status,
|
coreaudio_playback_logerr(status,
|
||||||
"Could not remove voice property change listener\n");
|
"Could not remove voice property change listener\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -654,6 +655,8 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
|
|||||||
.fini_out = coreaudio_fini_out,
|
.fini_out = coreaudio_fini_out,
|
||||||
/* wrapper for audio_generic_write */
|
/* wrapper for audio_generic_write */
|
||||||
.write = coreaudio_write,
|
.write = coreaudio_write,
|
||||||
|
/* wrapper for audio_generic_buffer_get_free */
|
||||||
|
.buffer_get_free = coreaudio_buffer_get_free,
|
||||||
/* wrapper for audio_generic_get_buffer_out */
|
/* wrapper for audio_generic_get_buffer_out */
|
||||||
.get_buffer_out = coreaudio_get_buffer_out,
|
.get_buffer_out = coreaudio_get_buffer_out,
|
||||||
/* wrapper for audio_generic_put_buffer_out */
|
/* wrapper for audio_generic_put_buffer_out */
|
||||||
|
@ -427,22 +427,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
static size_t dsound_buffer_get_free(HWVoiceOut *hw)
|
||||||
{
|
{
|
||||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
DWORD ppos, wpos, act_size;
|
DWORD ppos, wpos;
|
||||||
size_t req_size;
|
|
||||||
int err;
|
|
||||||
void *ret;
|
|
||||||
|
|
||||||
hr = IDirectSoundBuffer_GetCurrentPosition(
|
hr = IDirectSoundBuffer_GetCurrentPosition(
|
||||||
dsb, &ppos, ds->first_time ? &wpos : NULL);
|
dsb, &ppos, ds->first_time ? &wpos : NULL);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
dsound_logerr(hr, "Could not get playback buffer position\n");
|
dsound_logerr(hr, "Could not get playback buffer position\n");
|
||||||
*size = 0;
|
return 0;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds->first_time) {
|
if (ds->first_time) {
|
||||||
@ -450,13 +446,20 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
|||||||
ds->first_time = false;
|
ds->first_time = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
||||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
}
|
||||||
|
|
||||||
if (req_size == 0) {
|
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||||
*size = 0;
|
{
|
||||||
return NULL;
|
DSoundVoiceOut *ds = (DSoundVoiceOut *)hw;
|
||||||
}
|
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||||
|
DWORD act_size;
|
||||||
|
size_t req_size;
|
||||||
|
int err;
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
req_size = MIN(*size, hw->size_emul - hw->pos_emul);
|
||||||
|
assert(req_size > 0);
|
||||||
|
|
||||||
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||||
&act_size, NULL, false, ds->s);
|
&act_size, NULL, false, ds->s);
|
||||||
@ -699,6 +702,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
|
|||||||
.init_out = dsound_init_out,
|
.init_out = dsound_init_out,
|
||||||
.fini_out = dsound_fini_out,
|
.fini_out = dsound_fini_out,
|
||||||
.write = audio_generic_write,
|
.write = audio_generic_write,
|
||||||
|
.buffer_get_free = dsound_buffer_get_free,
|
||||||
.get_buffer_out = dsound_get_buffer_out,
|
.get_buffer_out = dsound_get_buffer_out,
|
||||||
.put_buffer_out = dsound_put_buffer_out,
|
.put_buffer_out = dsound_put_buffer_out,
|
||||||
.enable_out = dsound_enable_out,
|
.enable_out = dsound_enable_out,
|
||||||
|
@ -483,8 +483,8 @@ static int qjack_client_init(QJackClient *c)
|
|||||||
c->buffersize = 512;
|
c->buffersize = 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a 2 period buffer */
|
/* create a 3 period buffer */
|
||||||
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2);
|
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 3);
|
||||||
|
|
||||||
qjack_client_connect_ports(c);
|
qjack_client_connect_ports(c);
|
||||||
c->state = QJACK_STATE_RUNNING;
|
c->state = QJACK_STATE_RUNNING;
|
||||||
@ -652,6 +652,7 @@ static struct audio_pcm_ops jack_pcm_ops = {
|
|||||||
.init_out = qjack_init_out,
|
.init_out = qjack_init_out,
|
||||||
.fini_out = qjack_fini_out,
|
.fini_out = qjack_fini_out,
|
||||||
.write = qjack_write,
|
.write = qjack_write,
|
||||||
|
.buffer_get_free = audio_generic_buffer_get_free,
|
||||||
.run_buffer_out = audio_generic_run_buffer_out,
|
.run_buffer_out = audio_generic_run_buffer_out,
|
||||||
.enable_out = qjack_enable_out,
|
.enable_out = qjack_enable_out,
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = {
|
|||||||
.init_out = no_init_out,
|
.init_out = no_init_out,
|
||||||
.fini_out = no_fini_out,
|
.fini_out = no_fini_out,
|
||||||
.write = no_write,
|
.write = no_write,
|
||||||
|
.buffer_get_free = audio_generic_buffer_get_free,
|
||||||
.run_buffer_out = audio_generic_run_buffer_out,
|
.run_buffer_out = audio_generic_run_buffer_out,
|
||||||
.enable_out = no_enable_out,
|
.enable_out = no_enable_out,
|
||||||
|
|
||||||
|
@ -389,11 +389,23 @@ static void oss_run_buffer_out(HWVoiceOut *hw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t oss_buffer_get_free(HWVoiceOut *hw)
|
||||||
|
{
|
||||||
|
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
|
||||||
|
|
||||||
|
if (oss->mmapped) {
|
||||||
|
return oss_get_available_bytes(oss);
|
||||||
|
} else {
|
||||||
|
return audio_generic_buffer_get_free(hw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||||
{
|
{
|
||||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
|
||||||
|
|
||||||
if (oss->mmapped) {
|
if (oss->mmapped) {
|
||||||
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
|
*size = hw->size_emul - hw->pos_emul;
|
||||||
return hw->buf_emul + hw->pos_emul;
|
return hw->buf_emul + hw->pos_emul;
|
||||||
} else {
|
} else {
|
||||||
return audio_generic_get_buffer_out(hw, size);
|
return audio_generic_get_buffer_out(hw, size);
|
||||||
@ -750,6 +762,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
|
|||||||
.init_out = oss_init_out,
|
.init_out = oss_init_out,
|
||||||
.fini_out = oss_fini_out,
|
.fini_out = oss_fini_out,
|
||||||
.write = oss_write,
|
.write = oss_write,
|
||||||
|
.buffer_get_free = oss_buffer_get_free,
|
||||||
.run_buffer_out = oss_run_buffer_out,
|
.run_buffer_out = oss_run_buffer_out,
|
||||||
.get_buffer_out = oss_get_buffer_out,
|
.get_buffer_out = oss_get_buffer_out,
|
||||||
.put_buffer_out = oss_put_buffer_out,
|
.put_buffer_out = oss_put_buffer_out,
|
||||||
|
@ -201,13 +201,11 @@ unlock_and_fail:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
static size_t qpa_buffer_get_free(HWVoiceOut *hw)
|
||||||
{
|
{
|
||||||
PAVoiceOut *p = (PAVoiceOut *) hw;
|
PAVoiceOut *p = (PAVoiceOut *)hw;
|
||||||
PAConnection *c = p->g->conn;
|
PAConnection *c = p->g->conn;
|
||||||
void *ret;
|
|
||||||
size_t l;
|
size_t l;
|
||||||
int r;
|
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(c->mainloop);
|
pa_threaded_mainloop_lock(c->mainloop);
|
||||||
|
|
||||||
@ -216,7 +214,6 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
|||||||
if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
|
if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
|
||||||
/* wait for stream to become ready */
|
/* wait for stream to become ready */
|
||||||
l = 0;
|
l = 0;
|
||||||
ret = NULL;
|
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,16 +221,33 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
|||||||
CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
|
CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
|
||||||
"pa_stream_writable_size failed\n");
|
"pa_stream_writable_size failed\n");
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
pa_threaded_mainloop_unlock(c->mainloop);
|
||||||
|
return l;
|
||||||
|
|
||||||
|
unlock_and_fail:
|
||||||
|
pa_threaded_mainloop_unlock(c->mainloop);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||||
|
{
|
||||||
|
PAVoiceOut *p = (PAVoiceOut *)hw;
|
||||||
|
PAConnection *c = p->g->conn;
|
||||||
|
void *ret;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
pa_threaded_mainloop_lock(c->mainloop);
|
||||||
|
|
||||||
|
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
|
||||||
|
"pa_threaded_mainloop_lock failed\n");
|
||||||
|
|
||||||
*size = -1;
|
*size = -1;
|
||||||
r = pa_stream_begin_write(p->stream, &ret, size);
|
r = pa_stream_begin_write(p->stream, &ret, size);
|
||||||
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
|
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
|
||||||
"pa_stream_begin_write failed\n");
|
"pa_stream_begin_write failed\n");
|
||||||
|
|
||||||
unlock:
|
|
||||||
pa_threaded_mainloop_unlock(c->mainloop);
|
pa_threaded_mainloop_unlock(c->mainloop);
|
||||||
if (*size > l) {
|
|
||||||
*size = l;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
unlock_and_fail:
|
unlock_and_fail:
|
||||||
@ -535,11 +549,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||||||
}
|
}
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
/*
|
/* hw->samples counts in frames */
|
||||||
* This is wrong. hw->samples counts in frames. hw->samples will be
|
hw->samples = audio_buffer_frames(
|
||||||
* number of channels times larger than expected.
|
|
||||||
*/
|
|
||||||
hw->samples = audio_buffer_samples(
|
|
||||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
|
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -587,11 +598,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &obt_as);
|
audio_pcm_init_info (&hw->info, &obt_as);
|
||||||
/*
|
/* hw->samples counts in frames */
|
||||||
* This is wrong. hw->samples counts in frames. hw->samples will be
|
hw->samples = audio_buffer_frames(
|
||||||
* number of channels times larger than expected.
|
|
||||||
*/
|
|
||||||
hw->samples = audio_buffer_samples(
|
|
||||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
|
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -744,7 +752,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
|
|||||||
{
|
{
|
||||||
if (!pdo->has_latency) {
|
if (!pdo->has_latency) {
|
||||||
pdo->has_latency = true;
|
pdo->has_latency = true;
|
||||||
pdo->latency = 15000;
|
pdo->latency = 46440;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -901,6 +909,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
|
|||||||
.init_out = qpa_init_out,
|
.init_out = qpa_init_out,
|
||||||
.fini_out = qpa_fini_out,
|
.fini_out = qpa_fini_out,
|
||||||
.write = qpa_write,
|
.write = qpa_write,
|
||||||
|
.buffer_get_free = qpa_buffer_get_free,
|
||||||
.get_buffer_out = qpa_get_buffer_out,
|
.get_buffer_out = qpa_get_buffer_out,
|
||||||
.put_buffer_out = qpa_put_buffer_out,
|
.put_buffer_out = qpa_put_buffer_out,
|
||||||
.volume_out = qpa_volume_out,
|
.volume_out = qpa_volume_out,
|
||||||
|
@ -224,12 +224,11 @@ static void sdl_callback_out(void *opaque, Uint8 *buf, int len)
|
|||||||
/* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */
|
/* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */
|
||||||
|
|
||||||
while (hw->pending_emul && len) {
|
while (hw->pending_emul && len) {
|
||||||
size_t write_len;
|
size_t write_len, start;
|
||||||
ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
|
|
||||||
if (start < 0) {
|
start = audio_ring_posb(hw->pos_emul, hw->pending_emul,
|
||||||
start += hw->size_emul;
|
hw->size_emul);
|
||||||
}
|
assert(start < hw->size_emul);
|
||||||
assert(start >= 0 && start < hw->size_emul);
|
|
||||||
|
|
||||||
write_len = MIN(MIN(hw->pending_emul, len),
|
write_len = MIN(MIN(hw->pending_emul, len),
|
||||||
hw->size_emul - start);
|
hw->size_emul - start);
|
||||||
@ -310,6 +309,7 @@ static void sdl_callback_in(void *opaque, Uint8 *buf, int len)
|
|||||||
SDL_UnlockAudioDevice(sdl->devid); \
|
SDL_UnlockAudioDevice(sdl->devid); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw), Out)
|
||||||
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||||
(hw, size), Out)
|
(hw, size), Out)
|
||||||
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
|
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||||
@ -347,11 +347,8 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.format = aud_to_sdlfmt (as->fmt);
|
req.format = aud_to_sdlfmt (as->fmt);
|
||||||
req.channels = as->nchannels;
|
req.channels = as->nchannels;
|
||||||
/*
|
/* SDL samples are QEMU frames */
|
||||||
* This is wrong. SDL samples are QEMU frames. The buffer size will be
|
req.samples = audio_buffer_frames(
|
||||||
* the requested buffer size multiplied by the number of channels.
|
|
||||||
*/
|
|
||||||
req.samples = audio_buffer_samples(
|
|
||||||
qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
|
qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
|
||||||
req.callback = sdl_callback_out;
|
req.callback = sdl_callback_out;
|
||||||
req.userdata = sdl;
|
req.userdata = sdl;
|
||||||
@ -472,6 +469,8 @@ static struct audio_pcm_ops sdl_pcm_ops = {
|
|||||||
.fini_out = sdl_fini_out,
|
.fini_out = sdl_fini_out,
|
||||||
/* wrapper for audio_generic_write */
|
/* wrapper for audio_generic_write */
|
||||||
.write = sdl_write,
|
.write = sdl_write,
|
||||||
|
/* wrapper for audio_generic_buffer_get_free */
|
||||||
|
.buffer_get_free = sdl_buffer_get_free,
|
||||||
/* wrapper for audio_generic_get_buffer_out */
|
/* wrapper for audio_generic_get_buffer_out */
|
||||||
.get_buffer_out = sdl_get_buffer_out,
|
.get_buffer_out = sdl_get_buffer_out,
|
||||||
/* wrapper for audio_generic_put_buffer_out */
|
/* wrapper for audio_generic_put_buffer_out */
|
||||||
|
@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
|
|||||||
.init_out = wav_init_out,
|
.init_out = wav_init_out,
|
||||||
.fini_out = wav_fini_out,
|
.fini_out = wav_fini_out,
|
||||||
.write = wav_write_out,
|
.write = wav_write_out,
|
||||||
|
.buffer_get_free = audio_generic_buffer_get_free,
|
||||||
.run_buffer_out = audio_generic_run_buffer_out,
|
.run_buffer_out = audio_generic_run_buffer_out,
|
||||||
.enable_out = wav_enable_out,
|
.enable_out = wav_enable_out,
|
||||||
};
|
};
|
||||||
|
@ -18,3 +18,4 @@ guest hardware that is specific to QEMU.
|
|||||||
acpi_mem_hotplug
|
acpi_mem_hotplug
|
||||||
acpi_pci_hotplug
|
acpi_pci_hotplug
|
||||||
acpi_nvdimm
|
acpi_nvdimm
|
||||||
|
sev-guest-firmware
|
||||||
|
125
docs/specs/sev-guest-firmware.rst
Normal file
125
docs/specs/sev-guest-firmware.rst
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
====================================================
|
||||||
|
QEMU/Guest Firmware Interface for AMD SEV and SEV-ES
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
The guest firmware image (OVMF) may contain some configuration entries
|
||||||
|
which are used by QEMU before the guest launches. These are listed in a
|
||||||
|
GUIDed table at a known location in the firmware image. QEMU parses
|
||||||
|
this table when it loads the firmware image into memory, and then QEMU
|
||||||
|
reads individual entries when their values are needed.
|
||||||
|
|
||||||
|
Though nothing in the table structure is SEV-specific, currently all the
|
||||||
|
entries in the table are related to SEV and SEV-ES features.
|
||||||
|
|
||||||
|
|
||||||
|
Table parsing in QEMU
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The table is parsed from the footer: first the presence of the table
|
||||||
|
footer GUID (96b582de-1fb2-45f7-baea-a366c55a082d) at 0xffffffd0 is
|
||||||
|
verified. If that is found, two bytes at 0xffffffce are the entire
|
||||||
|
table length.
|
||||||
|
|
||||||
|
Then the table is scanned backwards looking for the specific entry GUID.
|
||||||
|
|
||||||
|
QEMU files related to parsing and scanning the OVMF table:
|
||||||
|
- ``hw/i386/pc_sysfw_ovmf.c``
|
||||||
|
|
||||||
|
The edk2 firmware code that constructs this structure is in the
|
||||||
|
`OVMF Reset Vector file`_.
|
||||||
|
|
||||||
|
|
||||||
|
Table memory layout
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| GPA | Length | Description |
|
||||||
|
+============+========+=========================================+
|
||||||
|
| 0xffffff80 | 4 | Zero padding |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffff84 | 4 | SEV hashes table base address |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffff88 | 4 | SEV hashes table size (=0x400) |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffff8c | 2 | SEV hashes table entry length (=0x1a) |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffff8e | 16 | SEV hashes table GUID: |
|
||||||
|
| | | 7255371f-3a3b-4b04-927b-1da6efa8d454 |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffff9e | 4 | SEV secret block base address |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffa2 | 4 | SEV secret block size (=0xc00) |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffa6 | 2 | SEV secret block entry length (=0x1a) |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffa8 | 16 | SEV secret block GUID: |
|
||||||
|
| | | 4c2eb361-7d9b-4cc3-8081-127c90d3d294 |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffb8 | 4 | SEV-ES AP reset RIP |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffbc | 2 | SEV-ES reset block entry length (=0x16) |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffbe | 16 | SEV-ES reset block entry GUID: |
|
||||||
|
| | | 00f771de-1a7e-4fcb-890e-68c77e2fb44e |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffce | 2 | Length of entire table including table |
|
||||||
|
| | | footer GUID and length (=0x72) |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffd0 | 16 | OVMF GUIDed table footer GUID: |
|
||||||
|
| | | 96b582de-1fb2-45f7-baea-a366c55a082d |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffe0 | 8 | Application processor entry point code |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xffffffe8 | 8 | "\0\0\0\0VTF\0" |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
| 0xfffffff0 | 16 | Reset vector code |
|
||||||
|
+------------+--------+-----------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
Table entries description
|
||||||
|
=========================
|
||||||
|
|
||||||
|
SEV-ES reset block
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Entry GUID: 00f771de-1a7e-4fcb-890e-68c77e2fb44e
|
||||||
|
|
||||||
|
For the initial boot of an AP under SEV-ES, the "reset" RIP must be
|
||||||
|
programmed to the RAM area defined by this entry. The entry's format
|
||||||
|
is:
|
||||||
|
|
||||||
|
* IP value [0:15]
|
||||||
|
* CS segment base [31:16]
|
||||||
|
|
||||||
|
A hypervisor reads the CS segment base and IP value. The CS segment
|
||||||
|
base value represents the high order 16-bits of the CS segment base, so
|
||||||
|
the hypervisor must left shift the value of the CS segment base by 16
|
||||||
|
bits to form the full CS segment base for the CS segment register. It
|
||||||
|
would then program the EIP register with the IP value as read.
|
||||||
|
|
||||||
|
|
||||||
|
SEV secret block
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Entry GUID: 4c2eb361-7d9b-4cc3-8081-127c90d3d294
|
||||||
|
|
||||||
|
This describes the guest RAM area where the hypervisor should inject the
|
||||||
|
Guest Owner secret (using SEV_LAUNCH_SECRET).
|
||||||
|
|
||||||
|
|
||||||
|
SEV hashes table
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Entry GUID: 7255371f-3a3b-4b04-927b-1da6efa8d454
|
||||||
|
|
||||||
|
This describes the guest RAM area where the hypervisor should install a
|
||||||
|
table describing the hashes of certain firmware configuration device
|
||||||
|
files that would otherwise be passed in unchecked. The current use is
|
||||||
|
for the kernel, initrd and command line values, but others may be added.
|
||||||
|
|
||||||
|
|
||||||
|
.. _OVMF Reset Vector file:
|
||||||
|
https://github.com/tianocore/edk2/blob/master/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm
|
@ -255,33 +255,31 @@ static void edid_desc_dummy(uint8_t *desc)
|
|||||||
edid_desc_type(desc, 0x10);
|
edid_desc_type(desc, 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void edid_desc_timing(uint8_t *desc, uint32_t refresh_rate,
|
static void edid_desc_timing(uint8_t *desc, const Timings *timings,
|
||||||
uint32_t xres, uint32_t yres,
|
uint32_t xres, uint32_t yres,
|
||||||
uint32_t xmm, uint32_t ymm)
|
uint32_t xmm, uint32_t ymm)
|
||||||
{
|
{
|
||||||
Timings timings;
|
stw_le_p(desc, timings->clock);
|
||||||
generate_timings(&timings, refresh_rate, xres, yres);
|
|
||||||
stl_le_p(desc, timings.clock);
|
|
||||||
|
|
||||||
desc[2] = xres & 0xff;
|
desc[2] = xres & 0xff;
|
||||||
desc[3] = timings.xblank & 0xff;
|
desc[3] = timings->xblank & 0xff;
|
||||||
desc[4] = (((xres & 0xf00) >> 4) |
|
desc[4] = (((xres & 0xf00) >> 4) |
|
||||||
((timings.xblank & 0xf00) >> 8));
|
((timings->xblank & 0xf00) >> 8));
|
||||||
|
|
||||||
desc[5] = yres & 0xff;
|
desc[5] = yres & 0xff;
|
||||||
desc[6] = timings.yblank & 0xff;
|
desc[6] = timings->yblank & 0xff;
|
||||||
desc[7] = (((yres & 0xf00) >> 4) |
|
desc[7] = (((yres & 0xf00) >> 4) |
|
||||||
((timings.yblank & 0xf00) >> 8));
|
((timings->yblank & 0xf00) >> 8));
|
||||||
|
|
||||||
desc[8] = timings.xfront & 0xff;
|
desc[8] = timings->xfront & 0xff;
|
||||||
desc[9] = timings.xsync & 0xff;
|
desc[9] = timings->xsync & 0xff;
|
||||||
|
|
||||||
desc[10] = (((timings.yfront & 0x00f) << 4) |
|
desc[10] = (((timings->yfront & 0x00f) << 4) |
|
||||||
((timings.ysync & 0x00f) << 0));
|
((timings->ysync & 0x00f) << 0));
|
||||||
desc[11] = (((timings.xfront & 0x300) >> 2) |
|
desc[11] = (((timings->xfront & 0x300) >> 2) |
|
||||||
((timings.xsync & 0x300) >> 4) |
|
((timings->xsync & 0x300) >> 4) |
|
||||||
((timings.yfront & 0x030) >> 2) |
|
((timings->yfront & 0x030) >> 2) |
|
||||||
((timings.ysync & 0x030) >> 4));
|
((timings->ysync & 0x030) >> 4));
|
||||||
|
|
||||||
desc[12] = xmm & 0xff;
|
desc[12] = xmm & 0xff;
|
||||||
desc[13] = ymm & 0xff;
|
desc[13] = ymm & 0xff;
|
||||||
@ -348,13 +346,10 @@ static void init_displayid(uint8_t *did)
|
|||||||
edid_checksum(did + 1, did[2] + 4);
|
edid_checksum(did + 1, did[2] + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate,
|
static void qemu_displayid_generate(uint8_t *did, const Timings *timings,
|
||||||
uint32_t xres, uint32_t yres,
|
uint32_t xres, uint32_t yres,
|
||||||
uint32_t xmm, uint32_t ymm)
|
uint32_t xmm, uint32_t ymm)
|
||||||
{
|
{
|
||||||
Timings timings;
|
|
||||||
generate_timings(&timings, refresh_rate, xres, yres);
|
|
||||||
|
|
||||||
did[0] = 0x70; /* display id extension */
|
did[0] = 0x70; /* display id extension */
|
||||||
did[1] = 0x13; /* version 1.3 */
|
did[1] = 0x13; /* version 1.3 */
|
||||||
did[2] = 23; /* length */
|
did[2] = 23; /* length */
|
||||||
@ -364,21 +359,21 @@ static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate,
|
|||||||
did[6] = 0x00; /* revision */
|
did[6] = 0x00; /* revision */
|
||||||
did[7] = 0x14; /* block length */
|
did[7] = 0x14; /* block length */
|
||||||
|
|
||||||
did[8] = timings.clock & 0xff;
|
did[8] = timings->clock & 0xff;
|
||||||
did[9] = (timings.clock & 0xff00) >> 8;
|
did[9] = (timings->clock & 0xff00) >> 8;
|
||||||
did[10] = (timings.clock & 0xff0000) >> 16;
|
did[10] = (timings->clock & 0xff0000) >> 16;
|
||||||
|
|
||||||
did[11] = 0x88; /* leave aspect ratio undefined */
|
did[11] = 0x88; /* leave aspect ratio undefined */
|
||||||
|
|
||||||
stw_le_p(did + 12, 0xffff & (xres - 1));
|
stw_le_p(did + 12, 0xffff & (xres - 1));
|
||||||
stw_le_p(did + 14, 0xffff & (timings.xblank - 1));
|
stw_le_p(did + 14, 0xffff & (timings->xblank - 1));
|
||||||
stw_le_p(did + 16, 0xffff & (timings.xfront - 1));
|
stw_le_p(did + 16, 0xffff & (timings->xfront - 1));
|
||||||
stw_le_p(did + 18, 0xffff & (timings.xsync - 1));
|
stw_le_p(did + 18, 0xffff & (timings->xsync - 1));
|
||||||
|
|
||||||
stw_le_p(did + 20, 0xffff & (yres - 1));
|
stw_le_p(did + 20, 0xffff & (yres - 1));
|
||||||
stw_le_p(did + 22, 0xffff & (timings.yblank - 1));
|
stw_le_p(did + 22, 0xffff & (timings->yblank - 1));
|
||||||
stw_le_p(did + 24, 0xffff & (timings.yfront - 1));
|
stw_le_p(did + 24, 0xffff & (timings->yfront - 1));
|
||||||
stw_le_p(did + 26, 0xffff & (timings.ysync - 1));
|
stw_le_p(did + 26, 0xffff & (timings->ysync - 1));
|
||||||
|
|
||||||
edid_checksum(did + 1, did[2] + 4);
|
edid_checksum(did + 1, did[2] + 4);
|
||||||
}
|
}
|
||||||
@ -386,6 +381,7 @@ static void qemu_displayid_generate(uint8_t *did, uint32_t refresh_rate,
|
|||||||
void qemu_edid_generate(uint8_t *edid, size_t size,
|
void qemu_edid_generate(uint8_t *edid, size_t size,
|
||||||
qemu_edid_info *info)
|
qemu_edid_info *info)
|
||||||
{
|
{
|
||||||
|
Timings timings;
|
||||||
uint8_t *desc = edid + 54;
|
uint8_t *desc = edid + 54;
|
||||||
uint8_t *xtra3 = NULL;
|
uint8_t *xtra3 = NULL;
|
||||||
uint8_t *dta = NULL;
|
uint8_t *dta = NULL;
|
||||||
@ -409,9 +405,6 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||||||
if (!info->prefy) {
|
if (!info->prefy) {
|
||||||
info->prefy = 800;
|
info->prefy = 800;
|
||||||
}
|
}
|
||||||
if (info->prefx >= 4096 || info->prefy >= 4096) {
|
|
||||||
large_screen = 1;
|
|
||||||
}
|
|
||||||
if (info->width_mm && info->height_mm) {
|
if (info->width_mm && info->height_mm) {
|
||||||
width_mm = info->width_mm;
|
width_mm = info->width_mm;
|
||||||
height_mm = info->height_mm;
|
height_mm = info->height_mm;
|
||||||
@ -421,6 +414,11 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||||||
height_mm = qemu_edid_dpi_to_mm(dpi, info->prefy);
|
height_mm = qemu_edid_dpi_to_mm(dpi, info->prefy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_timings(&timings, refresh_rate, info->prefx, info->prefy);
|
||||||
|
if (info->prefx >= 4096 || info->prefy >= 4096 || timings.clock >= 65536) {
|
||||||
|
large_screen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* =============== extensions =============== */
|
/* =============== extensions =============== */
|
||||||
|
|
||||||
if (size >= 256) {
|
if (size >= 256) {
|
||||||
@ -501,7 +499,7 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||||||
|
|
||||||
if (!large_screen) {
|
if (!large_screen) {
|
||||||
/* The DTD section has only 12 bits to store the resolution */
|
/* The DTD section has only 12 bits to store the resolution */
|
||||||
edid_desc_timing(desc, refresh_rate, info->prefx, info->prefy,
|
edid_desc_timing(desc, &timings, info->prefx, info->prefy,
|
||||||
width_mm, height_mm);
|
width_mm, height_mm);
|
||||||
desc = edid_desc_next(edid, dta, desc);
|
desc = edid_desc_next(edid, dta, desc);
|
||||||
}
|
}
|
||||||
@ -536,7 +534,7 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||||||
/* =============== display id extensions =============== */
|
/* =============== display id extensions =============== */
|
||||||
|
|
||||||
if (did && large_screen) {
|
if (did && large_screen) {
|
||||||
qemu_displayid_generate(did, refresh_rate, info->prefx, info->prefy,
|
qemu_displayid_generate(did, &timings, info->prefx, info->prefy,
|
||||||
width_mm, height_mm);
|
width_mm, height_mm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,9 @@ vmware_palette_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
|
|||||||
vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
|
vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
|
||||||
vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
|
vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
|
||||||
vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
|
vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
|
||||||
|
vmware_verify_rect_less_than_zero(const char *name, const char *param, int x) "%s: %s was < 0 (%d)"
|
||||||
|
vmware_verify_rect_greater_than_bound(const char *name, const char *param, int bound, int x) "%s: %s was > %d (%d)"
|
||||||
|
vmware_verify_rect_surface_bound_exceeded(const char *name, const char *component, int bound, const char *param1, int value1, const char *param2, int value2) "%s: %s > %d (%s: %d, %s: %d)"
|
||||||
|
|
||||||
# virtio-gpu-base.c
|
# virtio-gpu-base.c
|
||||||
virtio_gpu_features(bool virgl) "virgl %d"
|
virtio_gpu_features(bool virgl) "virgl %d"
|
||||||
|
@ -297,46 +297,52 @@ static inline bool vmsvga_verify_rect(DisplaySurface *surface,
|
|||||||
int x, int y, int w, int h)
|
int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
if (x < 0) {
|
if (x < 0) {
|
||||||
fprintf(stderr, "%s: x was < 0 (%d)\n", name, x);
|
trace_vmware_verify_rect_less_than_zero(name, "x", x);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (x > SVGA_MAX_WIDTH) {
|
if (x > SVGA_MAX_WIDTH) {
|
||||||
fprintf(stderr, "%s: x was > %d (%d)\n", name, SVGA_MAX_WIDTH, x);
|
trace_vmware_verify_rect_greater_than_bound(name, "x", SVGA_MAX_WIDTH,
|
||||||
|
x);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (w < 0) {
|
if (w < 0) {
|
||||||
fprintf(stderr, "%s: w was < 0 (%d)\n", name, w);
|
trace_vmware_verify_rect_less_than_zero(name, "w", w);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (w > SVGA_MAX_WIDTH) {
|
if (w > SVGA_MAX_WIDTH) {
|
||||||
fprintf(stderr, "%s: w was > %d (%d)\n", name, SVGA_MAX_WIDTH, w);
|
trace_vmware_verify_rect_greater_than_bound(name, "w", SVGA_MAX_WIDTH,
|
||||||
|
w);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (x + w > surface_width(surface)) {
|
if (x + w > surface_width(surface)) {
|
||||||
fprintf(stderr, "%s: width was > %d (x: %d, w: %d)\n",
|
trace_vmware_verify_rect_surface_bound_exceeded(name, "width",
|
||||||
name, surface_width(surface), x, w);
|
surface_width(surface),
|
||||||
|
"x", x, "w", w);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y < 0) {
|
if (y < 0) {
|
||||||
fprintf(stderr, "%s: y was < 0 (%d)\n", name, y);
|
trace_vmware_verify_rect_less_than_zero(name, "y", y);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (y > SVGA_MAX_HEIGHT) {
|
if (y > SVGA_MAX_HEIGHT) {
|
||||||
fprintf(stderr, "%s: y was > %d (%d)\n", name, SVGA_MAX_HEIGHT, y);
|
trace_vmware_verify_rect_greater_than_bound(name, "y", SVGA_MAX_HEIGHT,
|
||||||
|
y);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (h < 0) {
|
if (h < 0) {
|
||||||
fprintf(stderr, "%s: h was < 0 (%d)\n", name, h);
|
trace_vmware_verify_rect_less_than_zero(name, "h", h);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (h > SVGA_MAX_HEIGHT) {
|
if (h > SVGA_MAX_HEIGHT) {
|
||||||
fprintf(stderr, "%s: h was > %d (%d)\n", name, SVGA_MAX_HEIGHT, h);
|
trace_vmware_verify_rect_greater_than_bound(name, "y", SVGA_MAX_HEIGHT,
|
||||||
|
y);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (y + h > surface_height(surface)) {
|
if (y + h > surface_height(surface)) {
|
||||||
fprintf(stderr, "%s: update height > %d (y: %d, h: %d)\n",
|
trace_vmware_verify_rect_surface_bound_exceeded(name, "height",
|
||||||
name, surface_height(surface), y, h);
|
surface_height(surface),
|
||||||
|
"y", y, "h", h);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "hw/i386/pc.h"
|
#include "hw/i386/pc.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
#define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d"
|
#define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d"
|
||||||
|
|
||||||
|
static const int bytes_after_table_footer = 32;
|
||||||
|
|
||||||
static bool ovmf_flash_parsed;
|
static bool ovmf_flash_parsed;
|
||||||
static uint8_t *ovmf_table;
|
static uint8_t *ovmf_table;
|
||||||
static int ovmf_table_len;
|
static int ovmf_table_len;
|
||||||
@ -52,12 +55,13 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* if this is OVMF there will be a table footer
|
* if this is OVMF there will be a table footer
|
||||||
* guid 48 bytes before the end of the flash file. If it's
|
* guid 48 bytes before the end of the flash file
|
||||||
* not found, silently abort the flash parsing.
|
* (= 32 bytes after the table + 16 bytes the GUID itself).
|
||||||
|
* If it's not found, silently abort the flash parsing.
|
||||||
*/
|
*/
|
||||||
qemu_uuid_parse(OVMF_TABLE_FOOTER_GUID, &guid);
|
qemu_uuid_parse(OVMF_TABLE_FOOTER_GUID, &guid);
|
||||||
guid = qemu_uuid_bswap(guid); /* guids are LE */
|
guid = qemu_uuid_bswap(guid); /* guids are LE */
|
||||||
ptr = flash_ptr + flash_size - 48;
|
ptr = flash_ptr + flash_size - (bytes_after_table_footer + sizeof(guid));
|
||||||
if (!qemu_uuid_is_equal((QemuUUID *)ptr, &guid)) {
|
if (!qemu_uuid_is_equal((QemuUUID *)ptr, &guid)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,7 +70,13 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size)
|
|||||||
ptr -= sizeof(uint16_t);
|
ptr -= sizeof(uint16_t);
|
||||||
tot_len = le16_to_cpu(*(uint16_t *)ptr) - sizeof(guid) - sizeof(uint16_t);
|
tot_len = le16_to_cpu(*(uint16_t *)ptr) - sizeof(guid) - sizeof(uint16_t);
|
||||||
|
|
||||||
if (tot_len <= 0) {
|
if (tot_len < 0 || tot_len > (ptr - flash_ptr)) {
|
||||||
|
error_report("OVMF table has invalid size %d", tot_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tot_len == 0) {
|
||||||
|
/* no entries in the OVMF table */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1607,7 +1607,7 @@ static void usb_mtp_write_data(MTPState *s, uint32_t handle)
|
|||||||
usb_mtp_object_lookup(s, s->dataset.parent_handle);
|
usb_mtp_object_lookup(s, s->dataset.parent_handle);
|
||||||
char *path = NULL;
|
char *path = NULL;
|
||||||
uint64_t rc;
|
uint64_t rc;
|
||||||
mode_t mask = 0644;
|
mode_t mask = 0755;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
assert(d != NULL);
|
assert(d != NULL);
|
||||||
@ -1635,7 +1635,7 @@ static void usb_mtp_write_data(MTPState *s, uint32_t handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
d->fd = open(path, O_CREAT | O_WRONLY |
|
d->fd = open(path, O_CREAT | O_WRONLY |
|
||||||
O_CLOEXEC | O_NOFOLLOW, mask);
|
O_CLOEXEC | O_NOFOLLOW, mask & 0666);
|
||||||
if (d->fd == -1) {
|
if (d->fd == -1) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -58,8 +58,6 @@ struct ohci_hcca {
|
|||||||
#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head)
|
#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head)
|
||||||
#define ED_WBACK_SIZE 4
|
#define ED_WBACK_SIZE 4
|
||||||
|
|
||||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
|
|
||||||
|
|
||||||
/* Bitfields for the first word of an Endpoint Desciptor. */
|
/* Bitfields for the first word of an Endpoint Desciptor. */
|
||||||
#define OHCI_ED_FA_SHIFT 0
|
#define OHCI_ED_FA_SHIFT 0
|
||||||
#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)
|
#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)
|
||||||
@ -261,92 +259,6 @@ static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
|
|||||||
ohci_intr_update(ohci);
|
ohci_intr_update(ohci);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach or detach a device on a root hub port. */
|
|
||||||
static void ohci_attach(USBPort *port1)
|
|
||||||
{
|
|
||||||
OHCIState *s = port1->opaque;
|
|
||||||
OHCIPort *port = &s->rhport[port1->index];
|
|
||||||
uint32_t old_state = port->ctrl;
|
|
||||||
|
|
||||||
/* set connect status */
|
|
||||||
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
|
|
||||||
|
|
||||||
/* update speed */
|
|
||||||
if (port->port.dev->speed == USB_SPEED_LOW) {
|
|
||||||
port->ctrl |= OHCI_PORT_LSDA;
|
|
||||||
} else {
|
|
||||||
port->ctrl &= ~OHCI_PORT_LSDA;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* notify of remote-wakeup */
|
|
||||||
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
|
||||||
ohci_set_interrupt(s, OHCI_INTR_RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
trace_usb_ohci_port_attach(port1->index);
|
|
||||||
|
|
||||||
if (old_state != port->ctrl) {
|
|
||||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ohci_detach(USBPort *port1)
|
|
||||||
{
|
|
||||||
OHCIState *s = port1->opaque;
|
|
||||||
OHCIPort *port = &s->rhport[port1->index];
|
|
||||||
uint32_t old_state = port->ctrl;
|
|
||||||
|
|
||||||
ohci_async_cancel_device(s, port1->dev);
|
|
||||||
|
|
||||||
/* set connect status */
|
|
||||||
if (port->ctrl & OHCI_PORT_CCS) {
|
|
||||||
port->ctrl &= ~OHCI_PORT_CCS;
|
|
||||||
port->ctrl |= OHCI_PORT_CSC;
|
|
||||||
}
|
|
||||||
/* disable port */
|
|
||||||
if (port->ctrl & OHCI_PORT_PES) {
|
|
||||||
port->ctrl &= ~OHCI_PORT_PES;
|
|
||||||
port->ctrl |= OHCI_PORT_PESC;
|
|
||||||
}
|
|
||||||
trace_usb_ohci_port_detach(port1->index);
|
|
||||||
|
|
||||||
if (old_state != port->ctrl) {
|
|
||||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ohci_wakeup(USBPort *port1)
|
|
||||||
{
|
|
||||||
OHCIState *s = port1->opaque;
|
|
||||||
OHCIPort *port = &s->rhport[port1->index];
|
|
||||||
uint32_t intr = 0;
|
|
||||||
if (port->ctrl & OHCI_PORT_PSS) {
|
|
||||||
trace_usb_ohci_port_wakeup(port1->index);
|
|
||||||
port->ctrl |= OHCI_PORT_PSSC;
|
|
||||||
port->ctrl &= ~OHCI_PORT_PSS;
|
|
||||||
intr = OHCI_INTR_RHSC;
|
|
||||||
}
|
|
||||||
/* Note that the controller can be suspended even if this port is not */
|
|
||||||
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
|
||||||
trace_usb_ohci_remote_wakeup(s->name);
|
|
||||||
/* This is the one state transition the controller can do by itself */
|
|
||||||
s->ctl &= ~OHCI_CTL_HCFS;
|
|
||||||
s->ctl |= OHCI_USB_RESUME;
|
|
||||||
/* In suspend mode only ResumeDetected is possible, not RHSC:
|
|
||||||
* see the OHCI spec 5.1.2.3.
|
|
||||||
*/
|
|
||||||
intr = OHCI_INTR_RD;
|
|
||||||
}
|
|
||||||
ohci_set_interrupt(s, intr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ohci_child_detach(USBPort *port1, USBDevice *child)
|
|
||||||
{
|
|
||||||
OHCIState *s = port1->opaque;
|
|
||||||
|
|
||||||
ohci_async_cancel_device(s, child);
|
|
||||||
}
|
|
||||||
|
|
||||||
static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr)
|
static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr)
|
||||||
{
|
{
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
@ -369,6 +281,10 @@ void ohci_stop_endpoints(OHCIState *ohci)
|
|||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
if (ohci->async_td) {
|
||||||
|
usb_cancel_packet(&ohci->usb_packet);
|
||||||
|
ohci->async_td = 0;
|
||||||
|
}
|
||||||
for (i = 0; i < ohci->num_ports; i++) {
|
for (i = 0; i < ohci->num_ports; i++) {
|
||||||
dev = ohci->rhport[i].port.dev;
|
dev = ohci->rhport[i].port.dev;
|
||||||
if (dev && dev->attached) {
|
if (dev && dev->attached) {
|
||||||
@ -398,10 +314,6 @@ static void ohci_roothub_reset(OHCIState *ohci)
|
|||||||
usb_port_reset(&port->port);
|
usb_port_reset(&port->port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ohci->async_td) {
|
|
||||||
usb_cancel_packet(&ohci->usb_packet);
|
|
||||||
ohci->async_td = 0;
|
|
||||||
}
|
|
||||||
ohci_stop_endpoints(ohci);
|
ohci_stop_endpoints(ohci);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,21 +546,9 @@ static int ohci_copy_iso_td(OHCIState *ohci,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ohci_process_lists(OHCIState *ohci, int completion);
|
|
||||||
|
|
||||||
static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
|
|
||||||
{
|
|
||||||
OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
|
|
||||||
|
|
||||||
trace_usb_ohci_async_complete();
|
|
||||||
ohci->async_complete = true;
|
|
||||||
ohci_process_lists(ohci, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
|
#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
|
||||||
|
|
||||||
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||||
int completion)
|
|
||||||
{
|
{
|
||||||
int dir;
|
int dir;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
@ -658,6 +558,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||||||
int i;
|
int i;
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
USBEndpoint *ep;
|
USBEndpoint *ep;
|
||||||
|
USBPacket *pkt;
|
||||||
|
uint8_t buf[8192];
|
||||||
|
bool int_req;
|
||||||
struct ohci_iso_td iso_td;
|
struct ohci_iso_td iso_td;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
uint16_t starting_frame;
|
uint16_t starting_frame;
|
||||||
@ -792,40 +695,42 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||||||
} else {
|
} else {
|
||||||
len = end_addr - start_addr + 1;
|
len = end_addr - start_addr + 1;
|
||||||
}
|
}
|
||||||
if (len > sizeof(ohci->usb_buf)) {
|
if (len > sizeof(buf)) {
|
||||||
len = sizeof(ohci->usb_buf);
|
len = sizeof(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len && dir != OHCI_TD_DIR_IN) {
|
if (len && dir != OHCI_TD_DIR_IN) {
|
||||||
if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len,
|
if (ohci_copy_iso_td(ohci, start_addr, end_addr, buf, len,
|
||||||
DMA_DIRECTION_TO_DEVICE)) {
|
DMA_DIRECTION_TO_DEVICE)) {
|
||||||
ohci_die(ohci);
|
ohci_die(ohci);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!completion) {
|
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||||
bool int_req = relative_frame_number == frame_count &&
|
if (dev == NULL) {
|
||||||
OHCI_BM(iso_td.flags, TD_DI) == 0;
|
trace_usb_ohci_td_dev_error();
|
||||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
return 1;
|
||||||
if (dev == NULL) {
|
|
||||||
trace_usb_ohci_td_dev_error();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
|
||||||
usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req);
|
|
||||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
|
||||||
usb_handle_packet(dev, &ohci->usb_packet);
|
|
||||||
if (ohci->usb_packet.status == USB_RET_ASYNC) {
|
|
||||||
usb_device_flush_ep_queue(dev, ep);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (ohci->usb_packet.status == USB_RET_SUCCESS) {
|
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||||
ret = ohci->usb_packet.actual_length;
|
pkt = g_new0(USBPacket, 1);
|
||||||
|
usb_packet_init(pkt);
|
||||||
|
int_req = relative_frame_number == frame_count &&
|
||||||
|
OHCI_BM(iso_td.flags, TD_DI) == 0;
|
||||||
|
usb_packet_setup(pkt, pid, ep, 0, addr, false, int_req);
|
||||||
|
usb_packet_addbuf(pkt, buf, len);
|
||||||
|
usb_handle_packet(dev, pkt);
|
||||||
|
if (pkt->status == USB_RET_ASYNC) {
|
||||||
|
usb_device_flush_ep_queue(dev, ep);
|
||||||
|
g_free(pkt);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (pkt->status == USB_RET_SUCCESS) {
|
||||||
|
ret = pkt->actual_length;
|
||||||
} else {
|
} else {
|
||||||
ret = ohci->usb_packet.status;
|
ret = pkt->status;
|
||||||
}
|
}
|
||||||
|
g_free(pkt);
|
||||||
|
|
||||||
trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr,
|
trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr,
|
||||||
str, len, ret);
|
str, len, ret);
|
||||||
@ -833,7 +738,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||||||
/* Writeback */
|
/* Writeback */
|
||||||
if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
|
if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
|
||||||
/* IN transfer succeeded */
|
/* IN transfer succeeded */
|
||||||
if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret,
|
if (ohci_copy_iso_td(ohci, start_addr, end_addr, buf, ret,
|
||||||
DMA_DIRECTION_FROM_DEVICE)) {
|
DMA_DIRECTION_FROM_DEVICE)) {
|
||||||
ohci_die(ohci);
|
ohci_die(ohci);
|
||||||
return 1;
|
return 1;
|
||||||
@ -1033,21 +938,21 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||||||
ohci->async_td = 0;
|
ohci->async_td = 0;
|
||||||
ohci->async_complete = false;
|
ohci->async_complete = false;
|
||||||
} else {
|
} else {
|
||||||
if (ohci->async_td) {
|
|
||||||
/* ??? The hardware should allow one active packet per
|
|
||||||
endpoint. We only allow one active packet per controller.
|
|
||||||
This should be sufficient as long as devices respond in a
|
|
||||||
timely manner.
|
|
||||||
*/
|
|
||||||
trace_usb_ohci_td_too_many_pending();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||||
if (dev == NULL) {
|
if (dev == NULL) {
|
||||||
trace_usb_ohci_td_dev_error();
|
trace_usb_ohci_td_dev_error();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||||
|
if (ohci->async_td) {
|
||||||
|
/* ??? The hardware should allow one active packet per
|
||||||
|
endpoint. We only allow one active packet per controller.
|
||||||
|
This should be sufficient as long as devices respond in a
|
||||||
|
timely manner.
|
||||||
|
*/
|
||||||
|
trace_usb_ohci_td_too_many_pending(ep->nr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r,
|
usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r,
|
||||||
OHCI_BM(td.flags, TD_DI) == 0);
|
OHCI_BM(td.flags, TD_DI) == 0);
|
||||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
|
||||||
@ -1156,7 +1061,7 @@ exit_no_retire:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Service an endpoint list. Returns nonzero if active TD were found. */
|
/* Service an endpoint list. Returns nonzero if active TD were found. */
|
||||||
static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
|
static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
|
||||||
{
|
{
|
||||||
struct ohci_ed ed;
|
struct ohci_ed ed;
|
||||||
uint32_t next_ed;
|
uint32_t next_ed;
|
||||||
@ -1207,8 +1112,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
|
|||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
/* Handle isochronous endpoints */
|
/* Handle isochronous endpoints */
|
||||||
if (ohci_service_iso_td(ohci, &ed, completion))
|
if (ohci_service_iso_td(ohci, &ed)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1235,20 +1141,20 @@ static void ohci_sof(OHCIState *ohci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Process Control and Bulk lists. */
|
/* Process Control and Bulk lists. */
|
||||||
static void ohci_process_lists(OHCIState *ohci, int completion)
|
static void ohci_process_lists(OHCIState *ohci)
|
||||||
{
|
{
|
||||||
if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
|
if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
|
||||||
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
|
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
|
||||||
trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur);
|
trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur);
|
||||||
}
|
}
|
||||||
if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
|
if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) {
|
||||||
ohci->ctrl_cur = 0;
|
ohci->ctrl_cur = 0;
|
||||||
ohci->status &= ~OHCI_STATUS_CLF;
|
ohci->status &= ~OHCI_STATUS_CLF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
|
if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
|
||||||
if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
|
if (!ohci_service_ed_list(ohci, ohci->bulk_head)) {
|
||||||
ohci->bulk_cur = 0;
|
ohci->bulk_cur = 0;
|
||||||
ohci->status &= ~OHCI_STATUS_BLF;
|
ohci->status &= ~OHCI_STATUS_BLF;
|
||||||
}
|
}
|
||||||
@ -1272,19 +1178,15 @@ static void ohci_frame_boundary(void *opaque)
|
|||||||
int n;
|
int n;
|
||||||
|
|
||||||
n = ohci->frame_number & 0x1f;
|
n = ohci->frame_number & 0x1f;
|
||||||
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
|
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cancel all pending packets if either of the lists has been disabled. */
|
/* Cancel all pending packets if either of the lists has been disabled. */
|
||||||
if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
|
if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
|
||||||
if (ohci->async_td) {
|
|
||||||
usb_cancel_packet(&ohci->usb_packet);
|
|
||||||
ohci->async_td = 0;
|
|
||||||
}
|
|
||||||
ohci_stop_endpoints(ohci);
|
ohci_stop_endpoints(ohci);
|
||||||
}
|
}
|
||||||
ohci->old_ctl = ohci->ctl;
|
ohci->old_ctl = ohci->ctl;
|
||||||
ohci_process_lists(ohci, 0);
|
ohci_process_lists(ohci);
|
||||||
|
|
||||||
/* Stop if UnrecoverableError happened or ohci_sof will crash */
|
/* Stop if UnrecoverableError happened or ohci_sof will crash */
|
||||||
if (ohci->intr_status & OHCI_INTR_UE) {
|
if (ohci->intr_status & OHCI_INTR_UE) {
|
||||||
@ -1793,8 +1695,45 @@ static void ohci_mem_write(void *opaque,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
|
static const MemoryRegionOps ohci_mem_ops = {
|
||||||
|
.read = ohci_mem_read,
|
||||||
|
.write = ohci_mem_write,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* USBPortOps */
|
||||||
|
static void ohci_attach(USBPort *port1)
|
||||||
{
|
{
|
||||||
|
OHCIState *s = port1->opaque;
|
||||||
|
OHCIPort *port = &s->rhport[port1->index];
|
||||||
|
uint32_t old_state = port->ctrl;
|
||||||
|
|
||||||
|
/* set connect status */
|
||||||
|
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
|
||||||
|
|
||||||
|
/* update speed */
|
||||||
|
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||||
|
port->ctrl |= OHCI_PORT_LSDA;
|
||||||
|
} else {
|
||||||
|
port->ctrl &= ~OHCI_PORT_LSDA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notify of remote-wakeup */
|
||||||
|
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
||||||
|
ohci_set_interrupt(s, OHCI_INTR_RD);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_usb_ohci_port_attach(port1->index);
|
||||||
|
|
||||||
|
if (old_state != port->ctrl) {
|
||||||
|
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ohci_child_detach(USBPort *port1, USBDevice *dev)
|
||||||
|
{
|
||||||
|
OHCIState *ohci = port1->opaque;
|
||||||
|
|
||||||
if (ohci->async_td &&
|
if (ohci->async_td &&
|
||||||
usb_packet_is_inflight(&ohci->usb_packet) &&
|
usb_packet_is_inflight(&ohci->usb_packet) &&
|
||||||
ohci->usb_packet.ep->dev == dev) {
|
ohci->usb_packet.ep->dev == dev) {
|
||||||
@ -1803,11 +1742,65 @@ static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps ohci_mem_ops = {
|
static void ohci_detach(USBPort *port1)
|
||||||
.read = ohci_mem_read,
|
{
|
||||||
.write = ohci_mem_write,
|
OHCIState *s = port1->opaque;
|
||||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
OHCIPort *port = &s->rhport[port1->index];
|
||||||
};
|
uint32_t old_state = port->ctrl;
|
||||||
|
|
||||||
|
ohci_child_detach(port1, port1->dev);
|
||||||
|
|
||||||
|
/* set connect status */
|
||||||
|
if (port->ctrl & OHCI_PORT_CCS) {
|
||||||
|
port->ctrl &= ~OHCI_PORT_CCS;
|
||||||
|
port->ctrl |= OHCI_PORT_CSC;
|
||||||
|
}
|
||||||
|
/* disable port */
|
||||||
|
if (port->ctrl & OHCI_PORT_PES) {
|
||||||
|
port->ctrl &= ~OHCI_PORT_PES;
|
||||||
|
port->ctrl |= OHCI_PORT_PESC;
|
||||||
|
}
|
||||||
|
trace_usb_ohci_port_detach(port1->index);
|
||||||
|
|
||||||
|
if (old_state != port->ctrl) {
|
||||||
|
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ohci_wakeup(USBPort *port1)
|
||||||
|
{
|
||||||
|
OHCIState *s = port1->opaque;
|
||||||
|
OHCIPort *port = &s->rhport[port1->index];
|
||||||
|
uint32_t intr = 0;
|
||||||
|
if (port->ctrl & OHCI_PORT_PSS) {
|
||||||
|
trace_usb_ohci_port_wakeup(port1->index);
|
||||||
|
port->ctrl |= OHCI_PORT_PSSC;
|
||||||
|
port->ctrl &= ~OHCI_PORT_PSS;
|
||||||
|
intr = OHCI_INTR_RHSC;
|
||||||
|
}
|
||||||
|
/* Note that the controller can be suspended even if this port is not */
|
||||||
|
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
||||||
|
trace_usb_ohci_remote_wakeup(s->name);
|
||||||
|
/* This is the one state transition the controller can do by itself */
|
||||||
|
s->ctl &= ~OHCI_CTL_HCFS;
|
||||||
|
s->ctl |= OHCI_USB_RESUME;
|
||||||
|
/*
|
||||||
|
* In suspend mode only ResumeDetected is possible, not RHSC:
|
||||||
|
* see the OHCI spec 5.1.2.3.
|
||||||
|
*/
|
||||||
|
intr = OHCI_INTR_RD;
|
||||||
|
}
|
||||||
|
ohci_set_interrupt(s, intr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||||
|
{
|
||||||
|
OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
|
||||||
|
|
||||||
|
trace_usb_ohci_async_complete();
|
||||||
|
ohci->async_complete = true;
|
||||||
|
ohci_process_lists(ohci);
|
||||||
|
}
|
||||||
|
|
||||||
static USBPortOps ohci_port_ops = {
|
static USBPortOps ohci_port_ops = {
|
||||||
.attach = ohci_attach,
|
.attach = ohci_attach,
|
||||||
|
@ -2523,7 +2523,7 @@ static void xhci_process_commands(XHCIState *xhci)
|
|||||||
case CR_VENDOR_NEC_FIRMWARE_REVISION:
|
case CR_VENDOR_NEC_FIRMWARE_REVISION:
|
||||||
if (xhci->nec_quirks) {
|
if (xhci->nec_quirks) {
|
||||||
event.type = 48; /* NEC reply */
|
event.type = 48; /* NEC reply */
|
||||||
event.length = 0x3025;
|
event.length = 0x3034;
|
||||||
} else {
|
} else {
|
||||||
event.ccode = CC_TRB_ERROR;
|
event.ccode = CC_TRB_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1239,7 +1239,11 @@ static void usbredir_create_parser(USBRedirDevice *dev)
|
|||||||
|
|
||||||
DPRINTF("creating usbredirparser\n");
|
DPRINTF("creating usbredirparser\n");
|
||||||
|
|
||||||
dev->parser = qemu_oom_check(usbredirparser_create());
|
dev->parser = usbredirparser_create();
|
||||||
|
if (!dev->parser) {
|
||||||
|
error_report("usbredirparser_create() failed");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
dev->parser->priv = dev;
|
dev->parser->priv = dev;
|
||||||
dev->parser->log_func = usbredir_log;
|
dev->parser->log_func = usbredir_log;
|
||||||
dev->parser->read_func = usbredir_read;
|
dev->parser->read_func = usbredir_read;
|
||||||
@ -2239,7 +2243,10 @@ static int usbredir_put_parser(QEMUFile *f, void *priv, size_t unused,
|
|||||||
}
|
}
|
||||||
|
|
||||||
usbredirparser_serialize(dev->parser, &data, &len);
|
usbredirparser_serialize(dev->parser, &data, &len);
|
||||||
qemu_oom_check(data);
|
if (!data) {
|
||||||
|
error_report("usbredirparser_serialize failed");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
qemu_put_be32(f, len);
|
qemu_put_be32(f, len);
|
||||||
qemu_put_buffer(f, data, len);
|
qemu_put_buffer(f, data, len);
|
||||||
@ -2330,7 +2337,11 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused,
|
|||||||
bufp->len = qemu_get_be32(f);
|
bufp->len = qemu_get_be32(f);
|
||||||
bufp->status = qemu_get_be32(f);
|
bufp->status = qemu_get_be32(f);
|
||||||
bufp->offset = 0;
|
bufp->offset = 0;
|
||||||
bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */
|
bufp->data = malloc(bufp->len); /* regular malloc! */
|
||||||
|
if (!bufp->data) {
|
||||||
|
error_report("usbredir_get_bufpq: out of memory");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
bufp->free_on_destroy = bufp->data;
|
bufp->free_on_destroy = bufp->data;
|
||||||
qemu_get_buffer(f, bufp->data, bufp->len);
|
qemu_get_buffer(f, bufp->data, bufp->len);
|
||||||
QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
|
QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
|
||||||
|
@ -51,7 +51,7 @@ usb_ohci_td_skip_async(void) ""
|
|||||||
usb_ohci_td_pkt_hdr(uint32_t addr, int64_t pktlen, int64_t len, const char *s, int flag_r, uint32_t cbp, uint32_t be) " TD @ 0x%.8x %" PRId64 " of %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x"
|
usb_ohci_td_pkt_hdr(uint32_t addr, int64_t pktlen, int64_t len, const char *s, int flag_r, uint32_t cbp, uint32_t be) " TD @ 0x%.8x %" PRId64 " of %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x"
|
||||||
usb_ohci_td_pkt_short(const char *dir, const char *buf) "%s data: %s"
|
usb_ohci_td_pkt_short(const char *dir, const char *buf) "%s data: %s"
|
||||||
usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s"
|
usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s"
|
||||||
usb_ohci_td_too_many_pending(void) ""
|
usb_ohci_td_too_many_pending(int ep) "ep=%d"
|
||||||
usb_ohci_td_packet_status(int status) "status=%d"
|
usb_ohci_td_packet_status(int status) "status=%d"
|
||||||
usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x"
|
usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x"
|
||||||
usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x"
|
usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x"
|
||||||
|
@ -83,6 +83,8 @@ static const QDevAlias qdev_alias_table[] = {
|
|||||||
{ "virtio-gpu-device", "virtio-gpu", QEMU_ARCH_VIRTIO_MMIO },
|
{ "virtio-gpu-device", "virtio-gpu", QEMU_ARCH_VIRTIO_MMIO },
|
||||||
{ "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_VIRTIO_CCW },
|
{ "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_VIRTIO_CCW },
|
||||||
{ "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_VIRTIO_PCI },
|
{ "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_VIRTIO_PCI },
|
||||||
|
{ "virtio-gpu-gl-device", "virtio-gpu-gl", QEMU_ARCH_VIRTIO_MMIO },
|
||||||
|
{ "virtio-gpu-gl-pci", "virtio-gpu-gl", QEMU_ARCH_VIRTIO_PCI },
|
||||||
{ "virtio-input-host-device", "virtio-input-host", QEMU_ARCH_VIRTIO_MMIO },
|
{ "virtio-input-host-device", "virtio-input-host", QEMU_ARCH_VIRTIO_MMIO },
|
||||||
{ "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW },
|
{ "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW },
|
||||||
{ "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_VIRTIO_PCI },
|
{ "virtio-input-host-pci", "virtio-input-host", QEMU_ARCH_VIRTIO_PCI },
|
||||||
|
@ -66,8 +66,10 @@ void qemu_clipboard_update(QemuClipboardInfo *info)
|
|||||||
|
|
||||||
notifier_list_notify(&clipboard_notifiers, ¬ify);
|
notifier_list_notify(&clipboard_notifiers, ¬ify);
|
||||||
|
|
||||||
qemu_clipboard_info_unref(cbinfo[info->selection]);
|
if (cbinfo[info->selection] != info) {
|
||||||
cbinfo[info->selection] = qemu_clipboard_info_ref(info);
|
qemu_clipboard_info_unref(cbinfo[info->selection]);
|
||||||
|
cbinfo[info->selection] = qemu_clipboard_info_ref(info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection)
|
QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection)
|
||||||
|
@ -1611,11 +1611,15 @@ static void create_initial_menus(void)
|
|||||||
NSMenuItem *menuItem;
|
NSMenuItem *menuItem;
|
||||||
|
|
||||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||||
|
[NSApp setServicesMenu:[[NSMenu alloc] initWithTitle:@"Services"]];
|
||||||
|
|
||||||
// Application menu
|
// Application menu
|
||||||
menu = [[NSMenu alloc] initWithTitle:@""];
|
menu = [[NSMenu alloc] initWithTitle:@""];
|
||||||
[menu addItemWithTitle:@"About QEMU" action:@selector(do_about_menu_item:) keyEquivalent:@""]; // About QEMU
|
[menu addItemWithTitle:@"About QEMU" action:@selector(do_about_menu_item:) keyEquivalent:@""]; // About QEMU
|
||||||
[menu addItem:[NSMenuItem separatorItem]]; //Separator
|
[menu addItem:[NSMenuItem separatorItem]]; //Separator
|
||||||
|
menuItem = [menu addItemWithTitle:@"Services" action:nil keyEquivalent:@""];
|
||||||
|
[menuItem setSubmenu:[NSApp servicesMenu]];
|
||||||
|
[menu addItem:[NSMenuItem separatorItem]];
|
||||||
[menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
|
[menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
|
||||||
menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
|
menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
|
||||||
[menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
|
[menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
|
||||||
|
@ -49,6 +49,10 @@ void surface_gl_create_texture(QemuGLShader *gls,
|
|||||||
assert(gls);
|
assert(gls);
|
||||||
assert(QEMU_IS_ALIGNED(surface_stride(surface), surface_bytes_per_pixel(surface)));
|
assert(QEMU_IS_ALIGNED(surface_stride(surface), surface_bytes_per_pixel(surface)));
|
||||||
|
|
||||||
|
if (surface->texture) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (surface->format) {
|
switch (surface->format) {
|
||||||
case PIXMAN_BE_b8g8r8x8:
|
case PIXMAN_BE_b8g8r8x8:
|
||||||
case PIXMAN_BE_b8g8r8a8:
|
case PIXMAN_BE_b8g8r8a8:
|
||||||
|
29
ui/console.c
29
ui/console.c
@ -1860,7 +1860,9 @@ void dpy_gl_scanout_disable(QemuConsole *con)
|
|||||||
con->scanout.kind = SCANOUT_NONE;
|
con->scanout.kind = SCANOUT_NONE;
|
||||||
}
|
}
|
||||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||||
dcl->ops->dpy_gl_scanout_disable(dcl);
|
if (dcl->ops->dpy_gl_scanout_disable) {
|
||||||
|
dcl->ops->dpy_gl_scanout_disable(dcl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1881,10 +1883,12 @@ void dpy_gl_scanout_texture(QemuConsole *con,
|
|||||||
x, y, width, height
|
x, y, width, height
|
||||||
};
|
};
|
||||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||||
dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
|
if (dcl->ops->dpy_gl_scanout_texture) {
|
||||||
backing_y_0_top,
|
dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
|
||||||
backing_width, backing_height,
|
backing_y_0_top,
|
||||||
x, y, width, height);
|
backing_width, backing_height,
|
||||||
|
x, y, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1897,7 +1901,9 @@ void dpy_gl_scanout_dmabuf(QemuConsole *con,
|
|||||||
con->scanout.kind = SCANOUT_DMABUF;
|
con->scanout.kind = SCANOUT_DMABUF;
|
||||||
con->scanout.dmabuf = dmabuf;
|
con->scanout.dmabuf = dmabuf;
|
||||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||||
dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
|
if (dcl->ops->dpy_gl_scanout_dmabuf) {
|
||||||
|
dcl->ops->dpy_gl_scanout_dmabuf(dcl, dmabuf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1951,7 +1957,9 @@ void dpy_gl_update(QemuConsole *con,
|
|||||||
|
|
||||||
graphic_hw_gl_block(con, true);
|
graphic_hw_gl_block(con, true);
|
||||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||||
dcl->ops->dpy_gl_update(dcl, x, y, w, h);
|
if (dcl->ops->dpy_gl_update) {
|
||||||
|
dcl->ops->dpy_gl_update(dcl, x, y, w, h);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
graphic_hw_gl_block(con, false);
|
graphic_hw_gl_block(con, false);
|
||||||
}
|
}
|
||||||
@ -2392,13 +2400,12 @@ static void vc_chr_open(Chardev *chr,
|
|||||||
|
|
||||||
void qemu_console_resize(QemuConsole *s, int width, int height)
|
void qemu_console_resize(QemuConsole *s, int width, int height)
|
||||||
{
|
{
|
||||||
DisplaySurface *surface = qemu_console_surface(s);
|
DisplaySurface *surface;
|
||||||
|
|
||||||
assert(s->console_type == GRAPHIC_CONSOLE);
|
assert(s->console_type == GRAPHIC_CONSOLE);
|
||||||
|
|
||||||
if (surface && (surface->flags & QEMU_ALLOCATED_FLAG) &&
|
if (qemu_console_get_width(s, -1) == width &&
|
||||||
pixman_image_get_width(surface->image) == width &&
|
qemu_console_get_height(s, -1) == height) {
|
||||||
pixman_image_get_height(surface->image) == height) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user