mirror of https://github.com/xemu-project/xemu.git
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
audio
alsaaudio.caudio.caudio_int.hcoreaudio.cdsoundaudio.cjackaudio.cnoaudio.cossaudio.cpaaudio.csdlaudio.cwavaudio.c
docs/specs
hw
display
i386
usb
softmmu
ui
|
@ -916,6 +916,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
|||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
.write = alsa_write,
|
||||
.buffer_get_free = audio_generic_buffer_get_free,
|
||||
.run_buffer_out = audio_generic_run_buffer_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;
|
||||
}
|
||||
|
||||
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 pos = hw->mix_buf->pos;
|
||||
size_t conv = 0;
|
||||
STSampleBuffer *conv_buf = hw->conv_buf;
|
||||
|
||||
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);
|
||||
while (samples) {
|
||||
uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);
|
||||
size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);
|
||||
|
||||
hw->clip(dst, src, samples_to_clip);
|
||||
|
||||
pos = (pos + samples_to_clip) % hw->mix_buf->size;
|
||||
len -= samples_to_clip;
|
||||
clipped += samples_to_clip;
|
||||
hw->conv(conv_buf->samples + conv_buf->pos, src, proc);
|
||||
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
|
||||
samples -= proc;
|
||||
conv += proc;
|
||||
}
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
HWVoiceIn *hw = sw->hw;
|
||||
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
|
||||
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;
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
|
||||
|
||||
samples = size / sw->info.bytes_per_frame;
|
||||
if (!live) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
swlim = (live * sw->ratio) >> 32;
|
||||
swlim = MIN (swlim, samples);
|
||||
|
@ -632,7 +612,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
|||
total += isamp;
|
||||
}
|
||||
|
||||
if (hw->pcm_ops && !hw->pcm_ops->volume_in) {
|
||||
if (!hw->pcm_ops->volume_in) {
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
*/
|
||||
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;
|
||||
|
||||
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;
|
||||
samples = size / sw->info.bytes_per_frame;
|
||||
|
||||
dead = hwsamples - live;
|
||||
swlim = ((int64_t) dead << 32) / sw->ratio;
|
||||
swlim = MIN (swlim, samples);
|
||||
if (swlim) {
|
||||
sw->conv (sw->buf, buf, swlim);
|
||||
hw_free = audio_pcm_hw_get_free(sw->hw);
|
||||
hw_free = hw_free > live ? hw_free - live : 0;
|
||||
samples = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio;
|
||||
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) {
|
||||
mixeng_volume (sw->buf, swlim, &sw->vol);
|
||||
if (!sw->hw->pcm_ops->volume_out) {
|
||||
mixeng_volume(sw->buf, samples, &sw->vol);
|
||||
}
|
||||
}
|
||||
|
||||
while (swlim) {
|
||||
while (samples) {
|
||||
dead = hwsamples - live;
|
||||
left = hwsamples - wpos;
|
||||
blck = MIN (dead, left);
|
||||
if (!blck) {
|
||||
break;
|
||||
}
|
||||
isamp = swlim;
|
||||
isamp = samples;
|
||||
osamp = blck;
|
||||
st_rate_flow_mix (
|
||||
sw->rate,
|
||||
|
@ -741,7 +748,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
|||
&osamp
|
||||
);
|
||||
ret += isamp;
|
||||
swlim -= isamp;
|
||||
samples -= isamp;
|
||||
pos += isamp;
|
||||
live += osamp;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
size_t live, dead;
|
||||
|
@ -1022,13 +1034,11 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
|||
dead = sw->hw->mix_buf->size - live;
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
dolog ("%s: get_free live %zu dead %zu ret %" PRId64 "\n",
|
||||
SW_NAME (sw),
|
||||
live, dead, (((int64_t) dead << 32) / sw->ratio) *
|
||||
sw->info.bytes_per_frame);
|
||||
dolog("%s: get_free live %zu dead %zu sw_bytes %zu\n",
|
||||
SW_NAME(sw), live, dead, audio_sw_bytes_free(sw, dead));
|
||||
#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,
|
||||
|
@ -1132,9 +1142,27 @@ static void audio_run_out (AudioState *s)
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
if (!nb_live) {
|
||||
live = 0;
|
||||
|
@ -1163,14 +1191,6 @@ static void audio_run_out (AudioState *s)
|
|||
}
|
||||
|
||||
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) {
|
||||
hw->pcm_ops->run_buffer_out(hw);
|
||||
}
|
||||
|
@ -1211,13 +1231,6 @@ static void audio_run_out (AudioState *s)
|
|||
if (!sw->total_hw_samples_mixed) {
|
||||
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)
|
||||
{
|
||||
size_t conv = 0;
|
||||
STSampleBuffer *conv_buf = hw->conv_buf;
|
||||
|
||||
if (hw->pcm_ops->run_buffer_in) {
|
||||
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;
|
||||
}
|
||||
|
||||
proc = MIN(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;
|
||||
proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);
|
||||
|
||||
samples -= 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)
|
||||
{
|
||||
ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
|
||||
size_t start;
|
||||
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
|
||||
assert(start < hw->size_emul);
|
||||
|
||||
*size = MIN(*size, hw->pending_emul);
|
||||
*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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
size_t write_len, written, start;
|
||||
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
|
||||
assert(start < hw->size_emul);
|
||||
|
||||
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;
|
||||
|
||||
if (hw->pcm_ops->buffer_get_free) {
|
||||
size_t free = hw->pcm_ops->buffer_get_free(hw);
|
||||
|
||||
size = MIN(size, free);
|
||||
}
|
||||
|
||||
while (total < size) {
|
||||
size_t dst_size = size - total;
|
||||
size_t copy_size, proc;
|
||||
|
@ -1821,6 +1839,7 @@ void AUD_remove_card (QEMUSoundCard *card)
|
|||
g_free (card->name);
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops capture_pcm_ops;
|
||||
|
||||
CaptureVoiceOut *AUD_add_capture(
|
||||
AudioState *s,
|
||||
|
@ -1866,6 +1885,7 @@ CaptureVoiceOut *AUD_add_capture(
|
|||
|
||||
hw = &cap->hw;
|
||||
hw->s = s;
|
||||
hw->pcm_ops = &capture_pcm_ops;
|
||||
QLIST_INIT (&hw->sw_head);
|
||||
QLIST_INIT (&cap->cb_head);
|
||||
|
||||
|
|
|
@ -161,10 +161,14 @@ struct audio_pcm_ops {
|
|||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||
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
|
||||
* 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);
|
||||
/*
|
||||
|
@ -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_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||
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);
|
||||
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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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__)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -283,6 +283,7 @@ static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
|||
coreaudio_buf_unlock(core, "coreaudio_" #name); \
|
||||
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),
|
||||
(hw, size))
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
|
@ -333,12 +334,10 @@ static OSStatus audioDeviceIOProc(
|
|||
|
||||
len = frameCount * hw->info.bytes_per_frame;
|
||||
while (len) {
|
||||
size_t write_len;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
size_t write_len, start;
|
||||
|
||||
start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
|
||||
assert(start < hw->size_emul);
|
||||
|
||||
write_len = MIN(MIN(hw->pending_emul, len),
|
||||
hw->size_emul - start);
|
||||
|
@ -604,6 +603,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
|||
coreaudio_playback_logerr(status,
|
||||
"Could not remove voice property change listener\n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -654,6 +655,8 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
|
|||
.fini_out = coreaudio_fini_out,
|
||||
/* wrapper for audio_generic_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 */
|
||||
.get_buffer_out = coreaudio_get_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;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
HRESULT hr;
|
||||
DWORD ppos, wpos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
DWORD ppos, wpos;
|
||||
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition(
|
||||
dsb, &ppos, ds->first_time ? &wpos : NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get playback buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ds->first_time) {
|
||||
|
@ -450,13 +446,20 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
|||
ds->first_time = false;
|
||||
}
|
||||
|
||||
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
||||
return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
||||
}
|
||||
|
||||
if (req_size == 0) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
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,
|
||||
&act_size, NULL, false, ds->s);
|
||||
|
@ -699,6 +702,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
|
|||
.init_out = dsound_init_out,
|
||||
.fini_out = dsound_fini_out,
|
||||
.write = audio_generic_write,
|
||||
.buffer_get_free = dsound_buffer_get_free,
|
||||
.get_buffer_out = dsound_get_buffer_out,
|
||||
.put_buffer_out = dsound_put_buffer_out,
|
||||
.enable_out = dsound_enable_out,
|
||||
|
|
|
@ -483,8 +483,8 @@ static int qjack_client_init(QJackClient *c)
|
|||
c->buffersize = 512;
|
||||
}
|
||||
|
||||
/* create a 2 period buffer */
|
||||
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2);
|
||||
/* create a 3 period buffer */
|
||||
qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 3);
|
||||
|
||||
qjack_client_connect_ports(c);
|
||||
c->state = QJACK_STATE_RUNNING;
|
||||
|
@ -652,6 +652,7 @@ static struct audio_pcm_ops jack_pcm_ops = {
|
|||
.init_out = qjack_init_out,
|
||||
.fini_out = qjack_fini_out,
|
||||
.write = qjack_write,
|
||||
.buffer_get_free = audio_generic_buffer_get_free,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = qjack_enable_out,
|
||||
|
||||
|
|
|
@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = {
|
|||
.init_out = no_init_out,
|
||||
.fini_out = no_fini_out,
|
||||
.write = no_write,
|
||||
.buffer_get_free = audio_generic_buffer_get_free,
|
||||
.run_buffer_out = audio_generic_run_buffer_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)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
|
||||
|
||||
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;
|
||||
} else {
|
||||
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,
|
||||
.fini_out = oss_fini_out,
|
||||
.write = oss_write,
|
||||
.buffer_get_free = oss_buffer_get_free,
|
||||
.run_buffer_out = oss_run_buffer_out,
|
||||
.get_buffer_out = oss_get_buffer_out,
|
||||
.put_buffer_out = oss_put_buffer_out,
|
||||
|
|
|
@ -201,13 +201,11 @@ unlock_and_fail:
|
|||
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;
|
||||
void *ret;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
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) {
|
||||
/* wait for stream to become ready */
|
||||
l = 0;
|
||||
ret = NULL;
|
||||
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,
|
||||
"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;
|
||||
r = pa_stream_begin_write(p->stream, &ret, size);
|
||||
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
|
||||
"pa_stream_begin_write failed\n");
|
||||
|
||||
unlock:
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
if (*size > l) {
|
||||
*size = l;
|
||||
}
|
||||
return ret;
|
||||
|
||||
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);
|
||||
/*
|
||||
* This is wrong. hw->samples counts in frames. hw->samples will be
|
||||
* number of channels times larger than expected.
|
||||
*/
|
||||
hw->samples = audio_buffer_samples(
|
||||
/* hw->samples counts in frames */
|
||||
hw->samples = audio_buffer_frames(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
|
||||
|
||||
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);
|
||||
/*
|
||||
* This is wrong. hw->samples counts in frames. hw->samples will be
|
||||
* number of channels times larger than expected.
|
||||
*/
|
||||
hw->samples = audio_buffer_samples(
|
||||
/* hw->samples counts in frames */
|
||||
hw->samples = audio_buffer_frames(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
|
||||
|
||||
return 0;
|
||||
|
@ -744,7 +752,7 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
|
|||
{
|
||||
if (!pdo->has_latency) {
|
||||
pdo->has_latency = true;
|
||||
pdo->latency = 15000;
|
||||
pdo->latency = 46440;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -901,6 +909,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
|
|||
.init_out = qpa_init_out,
|
||||
.fini_out = qpa_fini_out,
|
||||
.write = qpa_write,
|
||||
.buffer_get_free = qpa_buffer_get_free,
|
||||
.get_buffer_out = qpa_get_buffer_out,
|
||||
.put_buffer_out = qpa_put_buffer_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); */
|
||||
|
||||
while (hw->pending_emul && len) {
|
||||
size_t write_len;
|
||||
ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
size_t write_len, start;
|
||||
|
||||
start = audio_ring_posb(hw->pos_emul, hw->pending_emul,
|
||||
hw->size_emul);
|
||||
assert(start < hw->size_emul);
|
||||
|
||||
write_len = MIN(MIN(hw->pending_emul, len),
|
||||
hw->size_emul - start);
|
||||
|
@ -310,6 +309,7 @@ static void sdl_callback_in(void *opaque, Uint8 *buf, int len)
|
|||
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),
|
||||
(hw, size), Out)
|
||||
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.format = aud_to_sdlfmt (as->fmt);
|
||||
req.channels = as->nchannels;
|
||||
/*
|
||||
* This is wrong. SDL samples are QEMU frames. The buffer size will be
|
||||
* the requested buffer size multiplied by the number of channels.
|
||||
*/
|
||||
req.samples = audio_buffer_samples(
|
||||
/* SDL samples are QEMU frames */
|
||||
req.samples = audio_buffer_frames(
|
||||
qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
|
||||
req.callback = sdl_callback_out;
|
||||
req.userdata = sdl;
|
||||
|
@ -472,6 +469,8 @@ static struct audio_pcm_ops sdl_pcm_ops = {
|
|||
.fini_out = sdl_fini_out,
|
||||
/* wrapper for audio_generic_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 */
|
||||
.get_buffer_out = sdl_get_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,
|
||||
.fini_out = wav_fini_out,
|
||||
.write = wav_write_out,
|
||||
.buffer_get_free = audio_generic_buffer_get_free,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = wav_enable_out,
|
||||
};
|
||||
|
|
|
@ -18,3 +18,4 @@ guest hardware that is specific to QEMU.
|
|||
acpi_mem_hotplug
|
||||
acpi_pci_hotplug
|
||||
acpi_nvdimm
|
||||
sev-guest-firmware
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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 xmm, uint32_t ymm)
|
||||
{
|
||||
Timings timings;
|
||||
generate_timings(&timings, refresh_rate, xres, yres);
|
||||
stl_le_p(desc, timings.clock);
|
||||
stw_le_p(desc, timings->clock);
|
||||
|
||||
desc[2] = xres & 0xff;
|
||||
desc[3] = timings.xblank & 0xff;
|
||||
desc[3] = timings->xblank & 0xff;
|
||||
desc[4] = (((xres & 0xf00) >> 4) |
|
||||
((timings.xblank & 0xf00) >> 8));
|
||||
((timings->xblank & 0xf00) >> 8));
|
||||
|
||||
desc[5] = yres & 0xff;
|
||||
desc[6] = timings.yblank & 0xff;
|
||||
desc[6] = timings->yblank & 0xff;
|
||||
desc[7] = (((yres & 0xf00) >> 4) |
|
||||
((timings.yblank & 0xf00) >> 8));
|
||||
((timings->yblank & 0xf00) >> 8));
|
||||
|
||||
desc[8] = timings.xfront & 0xff;
|
||||
desc[9] = timings.xsync & 0xff;
|
||||
desc[8] = timings->xfront & 0xff;
|
||||
desc[9] = timings->xsync & 0xff;
|
||||
|
||||
desc[10] = (((timings.yfront & 0x00f) << 4) |
|
||||
((timings.ysync & 0x00f) << 0));
|
||||
desc[11] = (((timings.xfront & 0x300) >> 2) |
|
||||
((timings.xsync & 0x300) >> 4) |
|
||||
((timings.yfront & 0x030) >> 2) |
|
||||
((timings.ysync & 0x030) >> 4));
|
||||
desc[10] = (((timings->yfront & 0x00f) << 4) |
|
||||
((timings->ysync & 0x00f) << 0));
|
||||
desc[11] = (((timings->xfront & 0x300) >> 2) |
|
||||
((timings->xsync & 0x300) >> 4) |
|
||||
((timings->yfront & 0x030) >> 2) |
|
||||
((timings->ysync & 0x030) >> 4));
|
||||
|
||||
desc[12] = xmm & 0xff;
|
||||
desc[13] = ymm & 0xff;
|
||||
|
@ -348,13 +346,10 @@ static void init_displayid(uint8_t *did)
|
|||
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 xmm, uint32_t ymm)
|
||||
{
|
||||
Timings timings;
|
||||
generate_timings(&timings, refresh_rate, xres, yres);
|
||||
|
||||
did[0] = 0x70; /* display id extension */
|
||||
did[1] = 0x13; /* version 1.3 */
|
||||
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[7] = 0x14; /* block length */
|
||||
|
||||
did[8] = timings.clock & 0xff;
|
||||
did[9] = (timings.clock & 0xff00) >> 8;
|
||||
did[10] = (timings.clock & 0xff0000) >> 16;
|
||||
did[8] = timings->clock & 0xff;
|
||||
did[9] = (timings->clock & 0xff00) >> 8;
|
||||
did[10] = (timings->clock & 0xff0000) >> 16;
|
||||
|
||||
did[11] = 0x88; /* leave aspect ratio undefined */
|
||||
|
||||
stw_le_p(did + 12, 0xffff & (xres - 1));
|
||||
stw_le_p(did + 14, 0xffff & (timings.xblank - 1));
|
||||
stw_le_p(did + 16, 0xffff & (timings.xfront - 1));
|
||||
stw_le_p(did + 18, 0xffff & (timings.xsync - 1));
|
||||
stw_le_p(did + 14, 0xffff & (timings->xblank - 1));
|
||||
stw_le_p(did + 16, 0xffff & (timings->xfront - 1));
|
||||
stw_le_p(did + 18, 0xffff & (timings->xsync - 1));
|
||||
|
||||
stw_le_p(did + 20, 0xffff & (yres - 1));
|
||||
stw_le_p(did + 22, 0xffff & (timings.yblank - 1));
|
||||
stw_le_p(did + 24, 0xffff & (timings.yfront - 1));
|
||||
stw_le_p(did + 26, 0xffff & (timings.ysync - 1));
|
||||
stw_le_p(did + 22, 0xffff & (timings->yblank - 1));
|
||||
stw_le_p(did + 24, 0xffff & (timings->yfront - 1));
|
||||
stw_le_p(did + 26, 0xffff & (timings->ysync - 1));
|
||||
|
||||
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,
|
||||
qemu_edid_info *info)
|
||||
{
|
||||
Timings timings;
|
||||
uint8_t *desc = edid + 54;
|
||||
uint8_t *xtra3 = NULL;
|
||||
uint8_t *dta = NULL;
|
||||
|
@ -409,9 +405,6 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||
if (!info->prefy) {
|
||||
info->prefy = 800;
|
||||
}
|
||||
if (info->prefx >= 4096 || info->prefy >= 4096) {
|
||||
large_screen = 1;
|
||||
}
|
||||
if (info->width_mm && info->height_mm) {
|
||||
width_mm = info->width_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);
|
||||
}
|
||||
|
||||
generate_timings(&timings, refresh_rate, info->prefx, info->prefy);
|
||||
if (info->prefx >= 4096 || info->prefy >= 4096 || timings.clock >= 65536) {
|
||||
large_screen = 1;
|
||||
}
|
||||
|
||||
/* =============== extensions =============== */
|
||||
|
||||
if (size >= 256) {
|
||||
|
@ -501,7 +499,7 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||
|
||||
if (!large_screen) {
|
||||
/* 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);
|
||||
desc = edid_desc_next(edid, dta, desc);
|
||||
}
|
||||
|
@ -536,7 +534,7 @@ void qemu_edid_generate(uint8_t *edid, size_t size,
|
|||
/* =============== display id extensions =============== */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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_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_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_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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (x + w > surface_width(surface)) {
|
||||
fprintf(stderr, "%s: width was > %d (x: %d, w: %d)\n",
|
||||
name, surface_width(surface), x, w);
|
||||
trace_vmware_verify_rect_surface_bound_exceeded(name, "width",
|
||||
surface_width(surface),
|
||||
"x", x, "w", w);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (y + h > surface_height(surface)) {
|
||||
fprintf(stderr, "%s: update height > %d (y: %d, h: %d)\n",
|
||||
name, surface_height(surface), y, h);
|
||||
trace_vmware_verify_rect_surface_bound_exceeded(name, "height",
|
||||
surface_height(surface),
|
||||
"y", y, "h", h);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,14 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d"
|
||||
|
||||
static const int bytes_after_table_footer = 32;
|
||||
|
||||
static bool ovmf_flash_parsed;
|
||||
static uint8_t *ovmf_table;
|
||||
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
|
||||
* guid 48 bytes before the end of the flash file. If it's
|
||||
* not found, silently abort the flash parsing.
|
||||
* guid 48 bytes before the end of the flash file
|
||||
* (= 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);
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
|
@ -66,7 +70,13 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size)
|
|||
ptr -= 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1607,7 +1607,7 @@ static void usb_mtp_write_data(MTPState *s, uint32_t handle)
|
|||
usb_mtp_object_lookup(s, s->dataset.parent_handle);
|
||||
char *path = NULL;
|
||||
uint64_t rc;
|
||||
mode_t mask = 0644;
|
||||
mode_t mask = 0755;
|
||||
int ret = 0;
|
||||
|
||||
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 |
|
||||
O_CLOEXEC | O_NOFOLLOW, mask);
|
||||
O_CLOEXEC | O_NOFOLLOW, mask & 0666);
|
||||
if (d->fd == -1) {
|
||||
ret = 1;
|
||||
goto done;
|
||||
|
|
|
@ -58,8 +58,6 @@ struct ohci_hcca {
|
|||
#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head)
|
||||
#define ED_WBACK_SIZE 4
|
||||
|
||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
|
||||
|
||||
/* Bitfields for the first word of an Endpoint Desciptor. */
|
||||
#define OHCI_ED_FA_SHIFT 0
|
||||
#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);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
USBDevice *dev;
|
||||
|
@ -369,6 +281,10 @@ void ohci_stop_endpoints(OHCIState *ohci)
|
|||
USBDevice *dev;
|
||||
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++) {
|
||||
dev = ohci->rhport[i].port.dev;
|
||||
if (dev && dev->attached) {
|
||||
|
@ -398,10 +314,6 @@ static void ohci_roothub_reset(OHCIState *ohci)
|
|||
usb_port_reset(&port->port);
|
||||
}
|
||||
}
|
||||
if (ohci->async_td) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
ohci_stop_endpoints(ohci);
|
||||
}
|
||||
|
||||
|
@ -634,21 +546,9 @@ static int ohci_copy_iso_td(OHCIState *ohci,
|
|||
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)))
|
||||
|
||||
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
||||
int completion)
|
||||
static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
{
|
||||
int dir;
|
||||
size_t len = 0;
|
||||
|
@ -658,6 +558,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
int i;
|
||||
USBDevice *dev;
|
||||
USBEndpoint *ep;
|
||||
USBPacket *pkt;
|
||||
uint8_t buf[8192];
|
||||
bool int_req;
|
||||
struct ohci_iso_td iso_td;
|
||||
uint32_t addr;
|
||||
uint16_t starting_frame;
|
||||
|
@ -792,40 +695,42 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
} else {
|
||||
len = end_addr - start_addr + 1;
|
||||
}
|
||||
if (len > sizeof(ohci->usb_buf)) {
|
||||
len = sizeof(ohci->usb_buf);
|
||||
if (len > sizeof(buf)) {
|
||||
len = sizeof(buf);
|
||||
}
|
||||
|
||||
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)) {
|
||||
ohci_die(ohci);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!completion) {
|
||||
bool int_req = relative_frame_number == frame_count &&
|
||||
OHCI_BM(iso_td.flags, TD_DI) == 0;
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
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;
|
||||
}
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
if (dev == NULL) {
|
||||
trace_usb_ohci_td_dev_error();
|
||||
return 1;
|
||||
}
|
||||
if (ohci->usb_packet.status == USB_RET_SUCCESS) {
|
||||
ret = ohci->usb_packet.actual_length;
|
||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||
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 {
|
||||
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,
|
||||
str, len, ret);
|
||||
|
@ -833,7 +738,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
/* Writeback */
|
||||
if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
|
||||
/* 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)) {
|
||||
ohci_die(ohci);
|
||||
return 1;
|
||||
|
@ -1033,21 +938,21 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
ohci->async_td = 0;
|
||||
ohci->async_complete = false;
|
||||
} 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));
|
||||
if (dev == NULL) {
|
||||
trace_usb_ohci_td_dev_error();
|
||||
return 1;
|
||||
}
|
||||
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,
|
||||
OHCI_BM(td.flags, TD_DI) == 0);
|
||||
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. */
|
||||
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;
|
||||
uint32_t next_ed;
|
||||
|
@ -1207,8 +1112,9 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
|
|||
break;
|
||||
} else {
|
||||
/* Handle isochronous endpoints */
|
||||
if (ohci_service_iso_td(ohci, &ed, completion))
|
||||
if (ohci_service_iso_td(ohci, &ed)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1235,20 +1141,20 @@ static void ohci_sof(OHCIState *ohci)
|
|||
}
|
||||
|
||||
/* 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->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
|
||||
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->status &= ~OHCI_STATUS_CLF;
|
||||
}
|
||||
}
|
||||
|
||||
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->status &= ~OHCI_STATUS_BLF;
|
||||
}
|
||||
|
@ -1272,19 +1178,15 @@ static void ohci_frame_boundary(void *opaque)
|
|||
int n;
|
||||
|
||||
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. */
|
||||
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->old_ctl = ohci->ctl;
|
||||
ohci_process_lists(ohci, 0);
|
||||
ohci_process_lists(ohci);
|
||||
|
||||
/* Stop if UnrecoverableError happened or ohci_sof will crash */
|
||||
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 &&
|
||||
usb_packet_is_inflight(&ohci->usb_packet) &&
|
||||
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 = {
|
||||
.read = ohci_mem_read,
|
||||
.write = ohci_mem_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
static void ohci_detach(USBPort *port1)
|
||||
{
|
||||
OHCIState *s = port1->opaque;
|
||||
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 = {
|
||||
.attach = ohci_attach,
|
||||
|
|
|
@ -2523,7 +2523,7 @@ static void xhci_process_commands(XHCIState *xhci)
|
|||
case CR_VENDOR_NEC_FIRMWARE_REVISION:
|
||||
if (xhci->nec_quirks) {
|
||||
event.type = 48; /* NEC reply */
|
||||
event.length = 0x3025;
|
||||
event.length = 0x3034;
|
||||
} else {
|
||||
event.ccode = CC_TRB_ERROR;
|
||||
}
|
||||
|
|
|
@ -1239,7 +1239,11 @@ static void usbredir_create_parser(USBRedirDevice *dev)
|
|||
|
||||
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->log_func = usbredir_log;
|
||||
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);
|
||||
qemu_oom_check(data);
|
||||
if (!data) {
|
||||
error_report("usbredirparser_serialize failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
qemu_put_be32(f, 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->status = qemu_get_be32(f);
|
||||
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;
|
||||
qemu_get_buffer(f, bufp->data, bufp->len);
|
||||
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_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_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_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"
|
||||
|
|
|
@ -83,6 +83,8 @@ static const QDevAlias qdev_alias_table[] = {
|
|||
{ "virtio-gpu-device", "virtio-gpu", QEMU_ARCH_VIRTIO_MMIO },
|
||||
{ "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_VIRTIO_CCW },
|
||||
{ "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-ccw", "virtio-input-host", QEMU_ARCH_VIRTIO_CCW },
|
||||
{ "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);
|
||||
|
||||
qemu_clipboard_info_unref(cbinfo[info->selection]);
|
||||
cbinfo[info->selection] = qemu_clipboard_info_ref(info);
|
||||
if (cbinfo[info->selection] != info) {
|
||||
qemu_clipboard_info_unref(cbinfo[info->selection]);
|
||||
cbinfo[info->selection] = qemu_clipboard_info_ref(info);
|
||||
}
|
||||
}
|
||||
|
||||
QemuClipboardInfo *qemu_clipboard_info(QemuClipboardSelection selection)
|
||||
|
|
|
@ -1611,11 +1611,15 @@ static void create_initial_menus(void)
|
|||
NSMenuItem *menuItem;
|
||||
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
[NSApp setServicesMenu:[[NSMenu alloc] initWithTitle:@"Services"]];
|
||||
|
||||
// Application menu
|
||||
menu = [[NSMenu alloc] initWithTitle:@""];
|
||||
[menu addItemWithTitle:@"About QEMU" action:@selector(do_about_menu_item:) keyEquivalent:@""]; // About QEMU
|
||||
[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
|
||||
menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
|
||||
[menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
|
||||
|
|
|
@ -49,6 +49,10 @@ void surface_gl_create_texture(QemuGLShader *gls,
|
|||
assert(gls);
|
||||
assert(QEMU_IS_ALIGNED(surface_stride(surface), surface_bytes_per_pixel(surface)));
|
||||
|
||||
if (surface->texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (surface->format) {
|
||||
case PIXMAN_BE_b8g8r8x8:
|
||||
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;
|
||||
}
|
||||
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
|
||||
};
|
||||
QLIST_FOREACH(dcl, &s->listeners, next) {
|
||||
dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
|
||||
backing_y_0_top,
|
||||
backing_width, backing_height,
|
||||
x, y, width, height);
|
||||
if (dcl->ops->dpy_gl_scanout_texture) {
|
||||
dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
|
||||
backing_y_0_top,
|
||||
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.dmabuf = dmabuf;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
@ -2392,13 +2400,12 @@ static void vc_chr_open(Chardev *chr,
|
|||
|
||||
void qemu_console_resize(QemuConsole *s, int width, int height)
|
||||
{
|
||||
DisplaySurface *surface = qemu_console_surface(s);
|
||||
DisplaySurface *surface;
|
||||
|
||||
assert(s->console_type == GRAPHIC_CONSOLE);
|
||||
|
||||
if (surface && (surface->flags & QEMU_ALLOCATED_FLAG) &&
|
||||
pixman_image_get_width(surface->image) == width &&
|
||||
pixman_image_get_height(surface->image) == height) {
|
||||
if (qemu_console_get_width(s, -1) == width &&
|
||||
qemu_console_get_height(s, -1) == height) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue