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:
Peter Maydell 2022-03-04 22:13:35 +00:00
commit 5c8463886d
27 changed files with 602 additions and 366 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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,
};

View File

@ -18,3 +18,4 @@ guest hardware that is specific to QEMU.
acpi_mem_hotplug
acpi_pci_hotplug
acpi_nvdimm
sev-guest-firmware

View 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

View File

@ -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);
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -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"

View File

@ -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 },

View File

@ -66,8 +66,10 @@ void qemu_clipboard_update(QemuClipboardInfo *info)
notifier_list_notify(&clipboard_notifiers, &notify);
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)

View File

@ -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)];

View File

@ -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:

View File

@ -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;
}