mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 11:39:53 +00:00
usb: make usbnet work with xhci.
audio: add sndio backend. misc bugfixes for console, xhci, audio, ati-vga and virtio-gpu. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmMyse8ACgkQTLbY7tPo cTiLrRAAltoyd++jsmhg2wXuJsfekfec3kOro7T+eSznDWfBRvm7VxJ+gswYBYga HbEkHjII0yPbOP9WDMhhHx33g2nYdbhDLPKXHdK8MjHTTPxtYP7XmsWkEVpuuzTx WqeYvGSmUri6QOUz7fd07IhiBT1aQvUQ/vWQ6OhyRVPy41bR8kIbGx3iV0JDxWvz n3xUZALGLz3QAM0lXRzXPYT9JB/RqdbpMM35HNTpN9/xaZmgFWsyuQXSSm61pTtb PS+lILDPjgZeYsfsZRyhZaSZrp2f6WOGm1ZdtSM0rvmRKezOzYnG8fm4fqZQLYSj nrAqUs38sKaM71a3QbpXhDjbv4cpj0K3iSNLmlUq4pgvPiMgwPlgSwwCGlkNDaRo IA1KON1pMH2A5vvtXEUt5RTkbXxHAAKPdpl5sS6kgbs7dgoKDqzaIPFQELam259Z 9nbMBqz/d6gm2CFT5ogrY0q511IC5hWtsmbQZkOZeBd5SvhvyJ59DIabFDcw05fG ixZVapewXYtzFUde2lb8X5qyneUVeGY5D2OJ2uUykHgR2Qz4d3CjXlhnRkLIkMcd Uu6N1LTkjyuuB86BoTSZxk0iz94OvmyDiXpqwmRaCGcdnTOTj0dKrbRrtHdC2vCo cBpUAIdyJvDJSm0X8ZWvvv1sMJCAJ7lofFf/P/jUKlacC2ipgXQ= =QBLK -----END PGP SIGNATURE----- Merge tag 'kraxel-20220927-pull-request' of https://gitlab.com/kraxel/qemu into staging usb: make usbnet work with xhci. audio: add sndio backend. misc bugfixes for console, xhci, audio, ati-vga and virtio-gpu. # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEEoDKM/7k6F6eZAf59TLbY7tPocTgFAmMyse8ACgkQTLbY7tPo # cTiLrRAAltoyd++jsmhg2wXuJsfekfec3kOro7T+eSznDWfBRvm7VxJ+gswYBYga # HbEkHjII0yPbOP9WDMhhHx33g2nYdbhDLPKXHdK8MjHTTPxtYP7XmsWkEVpuuzTx # WqeYvGSmUri6QOUz7fd07IhiBT1aQvUQ/vWQ6OhyRVPy41bR8kIbGx3iV0JDxWvz # n3xUZALGLz3QAM0lXRzXPYT9JB/RqdbpMM35HNTpN9/xaZmgFWsyuQXSSm61pTtb # PS+lILDPjgZeYsfsZRyhZaSZrp2f6WOGm1ZdtSM0rvmRKezOzYnG8fm4fqZQLYSj # nrAqUs38sKaM71a3QbpXhDjbv4cpj0K3iSNLmlUq4pgvPiMgwPlgSwwCGlkNDaRo # IA1KON1pMH2A5vvtXEUt5RTkbXxHAAKPdpl5sS6kgbs7dgoKDqzaIPFQELam259Z # 9nbMBqz/d6gm2CFT5ogrY0q511IC5hWtsmbQZkOZeBd5SvhvyJ59DIabFDcw05fG # ixZVapewXYtzFUde2lb8X5qyneUVeGY5D2OJ2uUykHgR2Qz4d3CjXlhnRkLIkMcd # Uu6N1LTkjyuuB86BoTSZxk0iz94OvmyDiXpqwmRaCGcdnTOTj0dKrbRrtHdC2vCo # cBpUAIdyJvDJSm0X8ZWvvv1sMJCAJ7lofFf/P/jUKlacC2ipgXQ= # =QBLK # -----END PGP SIGNATURE----- # gpg: Signature made Tue 27 Sep 2022 04:18:55 EDT # 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 * tag 'kraxel-20220927-pull-request' of https://gitlab.com/kraxel/qemu: (24 commits) virtio-gpu: update scanout if there is any area covered by the rect hw/display/ati_2d: Fix buffer overflow in ati_2d_blt (CVE-2021-3638) audio: remove abort() in audio_bug() Revert "audio: Log context for audio bug" audio: Add sndio backend usbnet: Report link-up via interrupt endpoint in CDC-ECM mode usbnet: Detect short packets as sent by the xHCI controller usbnet: Accept mandatory USB_CDC_SET_ETHERNET_PACKET_FILTER request usbnet: Add missing usb_wakeup() call in usbnet_receive() hcd-xhci: drop operation with secondary stream arrays enabled usb/msd: add usb_msd_fatal_error() and fix guest-triggerable assert usb/msd: move usb_msd_packet_complete() hcd-ohci: Drop ohci_service_iso_td() if ed->head & OHCI_DPTR_MASK is zero hw/usb/hcd-xhci: Check whether DMA accesses fail ui/console: fix three double frees in png_save() ui/vdagent: fix serial reset of guest agent ui/clipboard: reset the serial state on reset ui/vdagent: always reset the clipboard serial on caps ui/clipboard: fix serial priority ui: add some vdagent related traces ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
c48c9c6b33
@ -2438,6 +2438,7 @@ X: audio/jackaudio.c
|
||||
X: audio/ossaudio.c
|
||||
X: audio/paaudio.c
|
||||
X: audio/sdlaudio.c
|
||||
X: audio/sndioaudio.c
|
||||
X: audio/spiceaudio.c
|
||||
F: qapi/audio.json
|
||||
|
||||
@ -2482,6 +2483,12 @@ R: Thomas Huth <huth@tuxfamily.org>
|
||||
S: Odd Fixes
|
||||
F: audio/sdlaudio.c
|
||||
|
||||
Sndio Audio backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
R: Alexandre Ratchov <alex@caoua.org>
|
||||
S: Odd Fixes
|
||||
F: audio/sndioaudio.c
|
||||
|
||||
Block layer core
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Hanna Reitz <hreitz@redhat.com>
|
||||
|
@ -138,7 +138,7 @@ static inline int audio_bits_to_index (int bits)
|
||||
default:
|
||||
audio_bug ("bits_to_index", 1);
|
||||
AUD_log (NULL, "invalid bits %d\n", bits);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size)
|
||||
AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
|
||||
funcname);
|
||||
AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len);
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_malloc0 (len);
|
||||
@ -543,7 +543,7 @@ static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
|
||||
size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
|
||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
}
|
||||
@ -581,7 +581,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||
}
|
||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpos = audio_ring_posb(hw->conv_buf->pos, live, hw->conv_buf->size);
|
||||
@ -656,7 +656,7 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
|
||||
|
||||
if (audio_bug(__func__, live > hw->mix_buf->size)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
}
|
||||
@ -706,7 +706,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
live = sw->total_hw_samples_mixed;
|
||||
if (audio_bug(__func__, live > hwsamples)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (live == hwsamples) {
|
||||
@ -998,7 +998,7 @@ static size_t audio_get_avail (SWVoiceIn *sw)
|
||||
if (audio_bug(__func__, live > sw->hw->conv_buf->size)) {
|
||||
dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live,
|
||||
sw->hw->conv_buf->size);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ldebug (
|
||||
@ -1028,7 +1028,7 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
||||
if (audio_bug(__func__, live > sw->hw->mix_buf->size)) {
|
||||
dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live,
|
||||
sw->hw->mix_buf->size);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
dead = sw->hw->mix_buf->size - live;
|
||||
@ -1170,7 +1170,7 @@ static void audio_run_out (AudioState *s)
|
||||
|
||||
if (audio_bug(__func__, live > hw->mix_buf->size)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
|
||||
abort();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hw->pending_disable && !nb_live) {
|
||||
@ -1203,7 +1203,7 @@ static void audio_run_out (AudioState *s)
|
||||
if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) {
|
||||
dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n",
|
||||
hw->mix_buf->pos, hw->mix_buf->size, played);
|
||||
abort();
|
||||
hw->mix_buf->pos = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
@ -1223,7 +1223,7 @@ static void audio_run_out (AudioState *s)
|
||||
if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) {
|
||||
dolog("played=%zu sw->total_hw_samples_mixed=%zu\n",
|
||||
played, sw->total_hw_samples_mixed);
|
||||
abort();
|
||||
played = sw->total_hw_samples_mixed;
|
||||
}
|
||||
|
||||
sw->total_hw_samples_mixed -= played;
|
||||
@ -1346,7 +1346,7 @@ static void audio_run_capture (AudioState *s)
|
||||
if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) {
|
||||
dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n",
|
||||
captured, sw->total_hw_samples_mixed);
|
||||
abort();
|
||||
captured = sw->total_hw_samples_mixed;
|
||||
}
|
||||
|
||||
sw->total_hw_samples_mixed -= captured;
|
||||
@ -2030,6 +2030,7 @@ void audio_create_pdos(Audiodev *dev)
|
||||
CASE(OSS, oss, Oss);
|
||||
CASE(PA, pa, Pa);
|
||||
CASE(SDL, sdl, Sdl);
|
||||
CASE(SNDIO, sndio, );
|
||||
CASE(SPICE, spice, );
|
||||
CASE(WAV, wav, );
|
||||
|
||||
|
@ -59,13 +59,12 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
||||
if (audio_bug(__func__, !voice_size && max_voices)) {
|
||||
dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
|
||||
drv->name, max_voices);
|
||||
abort();
|
||||
glue (s->nb_hw_voices_, TYPE) = 0;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, voice_size && !max_voices)) {
|
||||
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
|
||||
drv->name, voice_size);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +81,6 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
|
||||
size_t samples = hw->samples;
|
||||
if (audio_bug(__func__, samples == 0)) {
|
||||
dolog("Attempted to allocate empty buffer\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
|
||||
@ -254,12 +252,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
|
||||
if (audio_bug(__func__, !drv)) {
|
||||
dolog ("No host audio driver\n");
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, !drv->pcm_ops)) {
|
||||
dolog ("Host audio driver without pcm_ops\n");
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE));
|
||||
@ -277,13 +275,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
QLIST_INIT (&hw->cap_head);
|
||||
#endif
|
||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) {
|
||||
g_free(hw);
|
||||
return NULL;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, hw->samples <= 0)) {
|
||||
dolog("hw->samples=%zd\n", hw->samples);
|
||||
abort();
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (hw->info.is_float) {
|
||||
@ -312,6 +309,12 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
audio_attach_capture (hw);
|
||||
#endif
|
||||
return hw;
|
||||
|
||||
err1:
|
||||
glue (hw->pcm_ops->fini_, TYPE) (hw);
|
||||
err0:
|
||||
g_free (hw);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
@ -336,6 +339,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
|
||||
case AUDIODEV_DRIVER_SDL:
|
||||
return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE);
|
||||
case AUDIODEV_DRIVER_SNDIO:
|
||||
return dev->u.sndio.TYPE;
|
||||
case AUDIODEV_DRIVER_SPICE:
|
||||
return dev->u.spice.TYPE;
|
||||
case AUDIODEV_DRIVER_WAV:
|
||||
@ -432,7 +437,7 @@ void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
|
||||
if (sw) {
|
||||
if (audio_bug(__func__, !card)) {
|
||||
dolog ("card=%p\n", card);
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
glue (audio_close_, TYPE) (sw);
|
||||
@ -454,7 +459,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||
card, name, callback_fn, as);
|
||||
abort();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s = card->state;
|
||||
@ -465,12 +470,12 @@ SW *glue (AUD_open_, TYPE) (
|
||||
|
||||
if (audio_bug(__func__, audio_validate_settings(as))) {
|
||||
audio_print_settings (as);
|
||||
abort();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, !s->drv)) {
|
||||
dolog ("Can not open `%s' (no host audio driver)\n", name);
|
||||
abort();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sw && audio_pcm_info_eq (&sw->info, as)) {
|
||||
|
@ -17,6 +17,7 @@ foreach m : [
|
||||
['pa', pulse, files('paaudio.c')],
|
||||
['sdl', sdl, files('sdlaudio.c')],
|
||||
['jack', jack, files('jackaudio.c')],
|
||||
['sndio', sndio, files('sndioaudio.c')],
|
||||
['spice', spice, files('spiceaudio.c')]
|
||||
]
|
||||
if m[1].found()
|
||||
|
565
audio/sndioaudio.c
Normal file
565
audio/sndioaudio.c
Normal file
@ -0,0 +1,565 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2019 Alexandre Ratchov <alex@caoua.org>
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO :
|
||||
*
|
||||
* Use a single device and open it in full-duplex rather than
|
||||
* opening it twice (once for playback once for recording).
|
||||
*
|
||||
* This is the only way to ensure that playback doesn't drift with respect
|
||||
* to recording, which is what guest systems expect.
|
||||
*/
|
||||
|
||||
#include <poll.h>
|
||||
#include <sndio.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "audio.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define AUDIO_CAP "sndio"
|
||||
#include "audio_int.h"
|
||||
|
||||
/* default latency in microseconds if no option is set */
|
||||
#define SNDIO_LATENCY_US 50000
|
||||
|
||||
typedef struct SndioVoice {
|
||||
union {
|
||||
HWVoiceOut out;
|
||||
HWVoiceIn in;
|
||||
} hw;
|
||||
struct sio_par par;
|
||||
struct sio_hdl *hdl;
|
||||
struct pollfd *pfds;
|
||||
struct pollindex {
|
||||
struct SndioVoice *self;
|
||||
int index;
|
||||
} *pindexes;
|
||||
unsigned char *buf;
|
||||
size_t buf_size;
|
||||
size_t sndio_pos;
|
||||
size_t qemu_pos;
|
||||
unsigned int mode;
|
||||
unsigned int nfds;
|
||||
bool enabled;
|
||||
} SndioVoice;
|
||||
|
||||
typedef struct SndioConf {
|
||||
const char *devname;
|
||||
unsigned int latency;
|
||||
} SndioConf;
|
||||
|
||||
/* needed for forward reference */
|
||||
static void sndio_poll_in(void *arg);
|
||||
static void sndio_poll_out(void *arg);
|
||||
|
||||
/*
|
||||
* stop polling descriptors
|
||||
*/
|
||||
static void sndio_poll_clear(SndioVoice *self)
|
||||
{
|
||||
struct pollfd *pfd;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self->nfds; i++) {
|
||||
pfd = &self->pfds[i];
|
||||
qemu_set_fd_handler(pfd->fd, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
self->nfds = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* write data to the device until it blocks or
|
||||
* all of our buffered data is written
|
||||
*/
|
||||
static void sndio_write(SndioVoice *self)
|
||||
{
|
||||
size_t todo, n;
|
||||
|
||||
todo = self->qemu_pos - self->sndio_pos;
|
||||
|
||||
/*
|
||||
* transfer data to device, until it blocks
|
||||
*/
|
||||
while (todo > 0) {
|
||||
n = sio_write(self->hdl, self->buf + self->sndio_pos, todo);
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
self->sndio_pos += n;
|
||||
todo -= n;
|
||||
}
|
||||
|
||||
if (self->sndio_pos == self->buf_size) {
|
||||
/*
|
||||
* we complete the block
|
||||
*/
|
||||
self->sndio_pos = 0;
|
||||
self->qemu_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* read data from the device until it blocks or
|
||||
* there no room any longer
|
||||
*/
|
||||
static void sndio_read(SndioVoice *self)
|
||||
{
|
||||
size_t todo, n;
|
||||
|
||||
todo = self->buf_size - self->sndio_pos;
|
||||
|
||||
/*
|
||||
* transfer data from the device, until it blocks
|
||||
*/
|
||||
while (todo > 0) {
|
||||
n = sio_read(self->hdl, self->buf + self->sndio_pos, todo);
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
self->sndio_pos += n;
|
||||
todo -= n;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set handlers for all descriptors libsndio needs to
|
||||
* poll
|
||||
*/
|
||||
static void sndio_poll_wait(SndioVoice *self)
|
||||
{
|
||||
struct pollfd *pfd;
|
||||
int events, i;
|
||||
|
||||
events = 0;
|
||||
if (self->mode == SIO_PLAY) {
|
||||
if (self->sndio_pos < self->qemu_pos) {
|
||||
events |= POLLOUT;
|
||||
}
|
||||
} else {
|
||||
if (self->sndio_pos < self->buf_size) {
|
||||
events |= POLLIN;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fill the given array of descriptors with the events sndio
|
||||
* wants, they are different from our 'event' variable because
|
||||
* sndio may use descriptors internally.
|
||||
*/
|
||||
self->nfds = sio_pollfd(self->hdl, self->pfds, events);
|
||||
|
||||
for (i = 0; i < self->nfds; i++) {
|
||||
pfd = &self->pfds[i];
|
||||
if (pfd->fd < 0) {
|
||||
continue;
|
||||
}
|
||||
qemu_set_fd_handler(pfd->fd,
|
||||
(pfd->events & POLLIN) ? sndio_poll_in : NULL,
|
||||
(pfd->events & POLLOUT) ? sndio_poll_out : NULL,
|
||||
&self->pindexes[i]);
|
||||
pfd->revents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-back called when one of the descriptors
|
||||
* became readable or writable
|
||||
*/
|
||||
static void sndio_poll_event(SndioVoice *self, int index, int event)
|
||||
{
|
||||
int revents;
|
||||
|
||||
/*
|
||||
* ensure we're not called twice this cycle
|
||||
*/
|
||||
sndio_poll_clear(self);
|
||||
|
||||
/*
|
||||
* make self->pfds[] look as we're returning from poll syscal,
|
||||
* this is how sio_revents expects events to be.
|
||||
*/
|
||||
self->pfds[index].revents = event;
|
||||
|
||||
/*
|
||||
* tell sndio to handle events and return whether we can read or
|
||||
* write without blocking.
|
||||
*/
|
||||
revents = sio_revents(self->hdl, self->pfds);
|
||||
if (self->mode == SIO_PLAY) {
|
||||
if (revents & POLLOUT) {
|
||||
sndio_write(self);
|
||||
}
|
||||
|
||||
if (self->qemu_pos < self->buf_size) {
|
||||
audio_run(self->hw.out.s, "sndio_out");
|
||||
}
|
||||
} else {
|
||||
if (revents & POLLIN) {
|
||||
sndio_read(self);
|
||||
}
|
||||
|
||||
if (self->qemu_pos < self->sndio_pos) {
|
||||
audio_run(self->hw.in.s, "sndio_in");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* audio_run() may have changed state
|
||||
*/
|
||||
if (self->enabled) {
|
||||
sndio_poll_wait(self);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* return the upper limit of the amount of free play buffer space
|
||||
*/
|
||||
static size_t sndio_buffer_get_free(HWVoiceOut *hw)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
return self->buf_size - self->qemu_pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* return a buffer where data to play can be stored,
|
||||
* its size is stored in the location pointed by the size argument.
|
||||
*/
|
||||
static void *sndio_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
*size = self->buf_size - self->qemu_pos;
|
||||
return self->buf + self->qemu_pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* put back to sndio back-end a buffer returned by sndio_get_buffer_out()
|
||||
*/
|
||||
static size_t sndio_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
self->qemu_pos += size;
|
||||
sndio_poll_wait(self);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* return a buffer from where recorded data is available,
|
||||
* its size is stored in the location pointed by the size argument.
|
||||
* it may not exceed the initial value of "*size".
|
||||
*/
|
||||
static void *sndio_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
size_t todo, max_todo;
|
||||
|
||||
/*
|
||||
* unlike the get_buffer_out() method, get_buffer_in()
|
||||
* must return a buffer of at most the given size, see audio.c
|
||||
*/
|
||||
max_todo = *size;
|
||||
|
||||
todo = self->sndio_pos - self->qemu_pos;
|
||||
if (todo > max_todo) {
|
||||
todo = max_todo;
|
||||
}
|
||||
|
||||
*size = todo;
|
||||
return self->buf + self->qemu_pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* discard the given amount of recorded data
|
||||
*/
|
||||
static void sndio_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
self->qemu_pos += size;
|
||||
if (self->qemu_pos == self->buf_size) {
|
||||
self->qemu_pos = 0;
|
||||
self->sndio_pos = 0;
|
||||
}
|
||||
sndio_poll_wait(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-back called when one of our descriptors becomes writable
|
||||
*/
|
||||
static void sndio_poll_out(void *arg)
|
||||
{
|
||||
struct pollindex *pindex = (struct pollindex *) arg;
|
||||
|
||||
sndio_poll_event(pindex->self, pindex->index, POLLOUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-back called when one of our descriptors becomes readable
|
||||
*/
|
||||
static void sndio_poll_in(void *arg)
|
||||
{
|
||||
struct pollindex *pindex = (struct pollindex *) arg;
|
||||
|
||||
sndio_poll_event(pindex->self, pindex->index, POLLIN);
|
||||
}
|
||||
|
||||
static void sndio_fini(SndioVoice *self)
|
||||
{
|
||||
if (self->hdl) {
|
||||
sio_close(self->hdl);
|
||||
self->hdl = NULL;
|
||||
}
|
||||
|
||||
g_free(self->pfds);
|
||||
g_free(self->pindexes);
|
||||
g_free(self->buf);
|
||||
}
|
||||
|
||||
static int sndio_init(SndioVoice *self,
|
||||
struct audsettings *as, int mode, Audiodev *dev)
|
||||
{
|
||||
AudiodevSndioOptions *opts = &dev->u.sndio;
|
||||
unsigned long long latency;
|
||||
const char *dev_name;
|
||||
struct sio_par req;
|
||||
unsigned int nch;
|
||||
int i, nfds;
|
||||
|
||||
dev_name = opts->has_dev ? opts->dev : SIO_DEVANY;
|
||||
latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US;
|
||||
|
||||
/* open the device in non-blocking mode */
|
||||
self->hdl = sio_open(dev_name, mode, 1);
|
||||
if (self->hdl == NULL) {
|
||||
dolog("failed to open device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->mode = mode;
|
||||
|
||||
sio_initpar(&req);
|
||||
|
||||
switch (as->fmt) {
|
||||
case AUDIO_FORMAT_S8:
|
||||
req.bits = 8;
|
||||
req.sig = 1;
|
||||
break;
|
||||
case AUDIO_FORMAT_U8:
|
||||
req.bits = 8;
|
||||
req.sig = 0;
|
||||
break;
|
||||
case AUDIO_FORMAT_S16:
|
||||
req.bits = 16;
|
||||
req.sig = 1;
|
||||
break;
|
||||
case AUDIO_FORMAT_U16:
|
||||
req.bits = 16;
|
||||
req.sig = 0;
|
||||
break;
|
||||
case AUDIO_FORMAT_S32:
|
||||
req.bits = 32;
|
||||
req.sig = 1;
|
||||
break;
|
||||
case AUDIO_FORMAT_U32:
|
||||
req.bits = 32;
|
||||
req.sig = 0;
|
||||
break;
|
||||
default:
|
||||
dolog("unknown audio sample format\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (req.bits > 8) {
|
||||
req.le = as->endianness ? 0 : 1;
|
||||
}
|
||||
|
||||
req.rate = as->freq;
|
||||
if (mode == SIO_PLAY) {
|
||||
req.pchan = as->nchannels;
|
||||
} else {
|
||||
req.rchan = as->nchannels;
|
||||
}
|
||||
|
||||
/* set on-device buffer size */
|
||||
req.appbufsz = req.rate * latency / 1000000;
|
||||
|
||||
if (!sio_setpar(self->hdl, &req)) {
|
||||
dolog("failed set audio params\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!sio_getpar(self->hdl, &self->par)) {
|
||||
dolog("failed get audio params\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nch = (mode == SIO_PLAY) ? self->par.pchan : self->par.rchan;
|
||||
|
||||
/*
|
||||
* With the default setup, sndio supports any combination of parameters
|
||||
* so these checks are mostly to catch configuration errors.
|
||||
*/
|
||||
if (self->par.bits != req.bits || self->par.bps != req.bits / 8 ||
|
||||
self->par.sig != req.sig || (req.bits > 8 && self->par.le != req.le) ||
|
||||
self->par.rate != as->freq || nch != as->nchannels) {
|
||||
dolog("unsupported audio params\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* we use one block as buffer size; this is how
|
||||
* transfers get well aligned
|
||||
*/
|
||||
self->buf_size = self->par.round * self->par.bps * nch;
|
||||
|
||||
self->buf = g_malloc(self->buf_size);
|
||||
if (self->buf == NULL) {
|
||||
dolog("failed to allocate audio buffer\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nfds = sio_nfds(self->hdl);
|
||||
|
||||
self->pfds = g_malloc_n(nfds, sizeof(struct pollfd));
|
||||
if (self->pfds == NULL) {
|
||||
dolog("failed to allocate pollfd structures\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
self->pindexes = g_malloc_n(nfds, sizeof(struct pollindex));
|
||||
if (self->pindexes == NULL) {
|
||||
dolog("failed to allocate pollindex structures\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
self->pindexes[i].self = self;
|
||||
self->pindexes[i].index = i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
sndio_fini(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void sndio_enable(SndioVoice *self, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
sio_start(self->hdl);
|
||||
self->enabled = true;
|
||||
sndio_poll_wait(self);
|
||||
} else {
|
||||
self->enabled = false;
|
||||
sndio_poll_clear(self);
|
||||
sio_stop(self->hdl);
|
||||
}
|
||||
}
|
||||
|
||||
static void sndio_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
sndio_enable(self, enable);
|
||||
}
|
||||
|
||||
static void sndio_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
sndio_enable(self, enable);
|
||||
}
|
||||
|
||||
static int sndio_init_out(HWVoiceOut *hw, struct audsettings *as, void *opaque)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
if (sndio_init(self, as, SIO_PLAY, opaque) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio_pcm_init_info(&hw->info, as);
|
||||
hw->samples = self->par.round;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sndio_init_in(HWVoiceIn *hw, struct audsettings *as, void *opaque)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
if (sndio_init(self, as, SIO_REC, opaque) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio_pcm_init_info(&hw->info, as);
|
||||
hw->samples = self->par.round;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sndio_fini_out(HWVoiceOut *hw)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
sndio_fini(self);
|
||||
}
|
||||
|
||||
static void sndio_fini_in(HWVoiceIn *hw)
|
||||
{
|
||||
SndioVoice *self = (SndioVoice *) hw;
|
||||
|
||||
sndio_fini(self);
|
||||
}
|
||||
|
||||
static void *sndio_audio_init(Audiodev *dev)
|
||||
{
|
||||
assert(dev->driver == AUDIODEV_DRIVER_SNDIO);
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void sndio_audio_fini(void *opaque)
|
||||
{
|
||||
}
|
||||
|
||||
static struct audio_pcm_ops sndio_pcm_ops = {
|
||||
.init_out = sndio_init_out,
|
||||
.fini_out = sndio_fini_out,
|
||||
.enable_out = sndio_enable_out,
|
||||
.write = audio_generic_write,
|
||||
.buffer_get_free = sndio_buffer_get_free,
|
||||
.get_buffer_out = sndio_get_buffer_out,
|
||||
.put_buffer_out = sndio_put_buffer_out,
|
||||
.init_in = sndio_init_in,
|
||||
.fini_in = sndio_fini_in,
|
||||
.read = audio_generic_read,
|
||||
.enable_in = sndio_enable_in,
|
||||
.get_buffer_in = sndio_get_buffer_in,
|
||||
.put_buffer_in = sndio_put_buffer_in,
|
||||
};
|
||||
|
||||
static struct audio_driver sndio_audio_driver = {
|
||||
.name = "sndio",
|
||||
.descr = "sndio https://sndio.org",
|
||||
.init = sndio_audio_init,
|
||||
.fini = sndio_audio_fini,
|
||||
.pcm_ops = &sndio_pcm_ops,
|
||||
.can_be_default = 1,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof(SndioVoice),
|
||||
.voice_size_in = sizeof(SndioVoice)
|
||||
};
|
||||
|
||||
static void register_audio_sndio(void)
|
||||
{
|
||||
audio_driver_register(&sndio_audio_driver);
|
||||
}
|
||||
|
||||
type_init(register_audio_sndio);
|
@ -287,8 +287,8 @@ select the fuzz target. Then, the qtest client is initialized. If the target
|
||||
requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized.
|
||||
Then the QGraph is walked and the QEMU cmd_line is determined and saved.
|
||||
|
||||
After this, the ``vl.c:qemu_main`` is called to set up the guest. There are
|
||||
target-specific hooks that can be called before and after qemu_main, for
|
||||
After this, the ``vl.c:main`` is called to set up the guest. There are
|
||||
target-specific hooks that can be called before and after main, for
|
||||
additional setup(e.g. PCI setup, or VM snapshotting).
|
||||
|
||||
``LLVMFuzzerTestOneInput``: Uses qtest/qos functions to act based on the fuzz
|
||||
|
@ -84,7 +84,7 @@ void ati_2d_blt(ATIVGAState *s)
|
||||
DPRINTF("%d %d %d, %d %d %d, (%d,%d) -> (%d,%d) %dx%d %c %c\n",
|
||||
s->regs.src_offset, s->regs.dst_offset, s->regs.default_offset,
|
||||
s->regs.src_pitch, s->regs.dst_pitch, s->regs.default_pitch,
|
||||
s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y,
|
||||
s->regs.src_x, s->regs.src_y, dst_x, dst_y,
|
||||
s->regs.dst_width, s->regs.dst_height,
|
||||
(s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ? '>' : '<'),
|
||||
(s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ? 'v' : '^'));
|
||||
@ -180,11 +180,11 @@ void ati_2d_blt(ATIVGAState *s)
|
||||
dst_stride /= sizeof(uint32_t);
|
||||
DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n",
|
||||
dst_bits, dst_stride, bpp,
|
||||
s->regs.dst_x, s->regs.dst_y,
|
||||
dst_x, dst_y,
|
||||
s->regs.dst_width, s->regs.dst_height,
|
||||
filler);
|
||||
pixman_fill((uint32_t *)dst_bits, dst_stride, bpp,
|
||||
s->regs.dst_x, s->regs.dst_y,
|
||||
dst_x, dst_y,
|
||||
s->regs.dst_width, s->regs.dst_height,
|
||||
filler);
|
||||
if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
|
||||
|
@ -515,9 +515,10 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
|
||||
for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
|
||||
scanout = &g->parent_obj.scanout[i];
|
||||
if (scanout->resource_id == res->resource_id &&
|
||||
rf.r.x >= scanout->x && rf.r.y >= scanout->y &&
|
||||
rf.r.x + rf.r.width <= scanout->x + scanout->width &&
|
||||
rf.r.y + rf.r.height <= scanout->y + scanout->height &&
|
||||
rf.r.x < scanout->x + scanout->width &&
|
||||
rf.r.x + rf.r.width >= scanout->x &&
|
||||
rf.r.y < scanout->y + scanout->height &&
|
||||
rf.r.y + rf.r.height >= scanout->y &&
|
||||
console_has_gl(scanout->con)) {
|
||||
dpy_gl_update(scanout->con, 0, 0, scanout->width,
|
||||
scanout->height);
|
||||
|
@ -91,6 +91,8 @@ enum usbstring_idx {
|
||||
#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43
|
||||
#define USB_CDC_GET_ETHERNET_STATISTIC 0x44
|
||||
|
||||
#define USB_CDC_NETWORK_CONNECTION 0x00
|
||||
|
||||
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
|
||||
#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
|
||||
|
||||
@ -640,6 +642,8 @@ struct USBNetState {
|
||||
uint16_t filter;
|
||||
uint32_t vendorid;
|
||||
|
||||
uint16_t connection;
|
||||
|
||||
unsigned int out_ptr;
|
||||
uint8_t out_buf[2048];
|
||||
|
||||
@ -647,6 +651,7 @@ struct USBNetState {
|
||||
uint8_t in_buf[2048];
|
||||
|
||||
USBEndpoint *intr;
|
||||
USBEndpoint *bulk_in;
|
||||
|
||||
char usbstring_mac[13];
|
||||
NICState *nic;
|
||||
@ -1121,6 +1126,12 @@ static void usb_net_handle_control(USBDevice *dev, USBPacket *p,
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ClassInterfaceOutRequest | USB_CDC_SET_ETHERNET_PACKET_FILTER:
|
||||
if (is_rndis(s)) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fail:
|
||||
fprintf(stderr, "usbnet: failed control transaction: "
|
||||
@ -1133,18 +1144,28 @@ static void usb_net_handle_control(USBDevice *dev, USBPacket *p,
|
||||
|
||||
static void usb_net_handle_statusin(USBNetState *s, USBPacket *p)
|
||||
{
|
||||
le32 buf[2];
|
||||
le32 rbuf[2];
|
||||
uint16_t ebuf[4];
|
||||
|
||||
if (p->iov.size < 8) {
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
buf[0] = cpu_to_le32(1);
|
||||
buf[1] = cpu_to_le32(0);
|
||||
usb_packet_copy(p, buf, 8);
|
||||
if (!s->rndis_resp.tqh_first) {
|
||||
p->status = USB_RET_NAK;
|
||||
if (is_rndis(s)) {
|
||||
rbuf[0] = cpu_to_le32(1);
|
||||
rbuf[1] = cpu_to_le32(0);
|
||||
usb_packet_copy(p, rbuf, 8);
|
||||
if (!s->rndis_resp.tqh_first) {
|
||||
p->status = USB_RET_NAK;
|
||||
}
|
||||
} else {
|
||||
ebuf[0] =
|
||||
cpu_to_be16(ClassInterfaceRequest | USB_CDC_NETWORK_CONNECTION);
|
||||
ebuf[1] = cpu_to_le16(s->connection);
|
||||
ebuf[2] = cpu_to_le16(1);
|
||||
ebuf[3] = cpu_to_le16(0);
|
||||
usb_packet_copy(p, ebuf, 8);
|
||||
}
|
||||
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
@ -1204,7 +1225,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
|
||||
s->out_ptr += sz;
|
||||
|
||||
if (!is_rndis(s)) {
|
||||
if (p->iov.size < 64) {
|
||||
if (p->iov.size % 64 || p->iov.size == 0) {
|
||||
qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr);
|
||||
s->out_ptr = 0;
|
||||
}
|
||||
@ -1317,6 +1338,7 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
|
||||
memcpy(in_buf, buf, size);
|
||||
s->in_len = total_size;
|
||||
s->in_ptr = 0;
|
||||
usb_wakeup(s->bulk_in, 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -1358,7 +1380,9 @@ static void usb_net_realize(USBDevice *dev, Error **errp)
|
||||
s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */;
|
||||
s->filter = 0;
|
||||
s->vendorid = 0x1234;
|
||||
s->connection = 1; /* Connected */
|
||||
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||
s->bulk_in = usb_ep_get(dev, USB_TOKEN_IN, 2);
|
||||
|
||||
qemu_macaddr_default_if_unset(&s->conf.macaddr);
|
||||
s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
|
||||
|
@ -177,6 +177,37 @@ static const USBDesc desc = {
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
static void usb_msd_packet_complete(MSDState *s)
|
||||
{
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
/*
|
||||
* Set s->packet to NULL before calling usb_packet_complete
|
||||
* because another request may be issued before
|
||||
* usb_packet_complete returns.
|
||||
*/
|
||||
trace_usb_msd_packet_complete();
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(&s->dev, p);
|
||||
}
|
||||
|
||||
static void usb_msd_fatal_error(MSDState *s)
|
||||
{
|
||||
trace_usb_msd_fatal_error();
|
||||
|
||||
if (s->packet) {
|
||||
s->packet->status = USB_RET_STALL;
|
||||
usb_msd_packet_complete(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guest messed up up device state with illegal requests. Go
|
||||
* ignore any requests until the guests resets the device (and
|
||||
* brings it into a known state that way).
|
||||
*/
|
||||
s->needs_reset = true;
|
||||
}
|
||||
|
||||
static void usb_msd_copy_data(MSDState *s, USBPacket *p)
|
||||
{
|
||||
uint32_t len;
|
||||
@ -208,24 +239,16 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
|
||||
memset(&s->csw, 0, sizeof(s->csw));
|
||||
}
|
||||
|
||||
static void usb_msd_packet_complete(MSDState *s)
|
||||
{
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
/* Set s->packet to NULL before calling usb_packet_complete
|
||||
because another request may be issued before
|
||||
usb_packet_complete returns. */
|
||||
trace_usb_msd_packet_complete();
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(&s->dev, p);
|
||||
}
|
||||
|
||||
void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
|
||||
if ((s->mode == USB_MSDM_DATAOUT) != (req->cmd.mode == SCSI_XFER_TO_DEV)) {
|
||||
usb_msd_fatal_error(s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->scsi_len = len;
|
||||
s->scsi_off = 0;
|
||||
if (p) {
|
||||
@ -315,6 +338,8 @@ void usb_msd_handle_reset(USBDevice *dev)
|
||||
|
||||
memset(&s->csw, 0, sizeof(s->csw));
|
||||
s->mode = USB_MSDM_CBW;
|
||||
|
||||
s->needs_reset = false;
|
||||
}
|
||||
|
||||
static void usb_msd_handle_control(USBDevice *dev, USBPacket *p,
|
||||
@ -380,6 +405,11 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
SCSIDevice *scsi_dev;
|
||||
uint32_t len;
|
||||
|
||||
if (s->needs_reset) {
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
if (devep != 2)
|
||||
|
@ -571,6 +571,11 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
|
||||
addr = ed->head & OHCI_DPTR_MASK;
|
||||
|
||||
if (addr == 0) {
|
||||
ohci_die(ohci);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ohci_read_iso_td(ohci, addr, &iso_td)) {
|
||||
trace_usb_ohci_iso_td_read_failed(addr);
|
||||
ohci_die(ohci);
|
||||
@ -859,6 +864,11 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
int completion;
|
||||
|
||||
addr = ed->head & OHCI_DPTR_MASK;
|
||||
if (addr == 0) {
|
||||
ohci_die(ohci);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* See if this TD has already been submitted to the device. */
|
||||
completion = (addr == ohci->async_td);
|
||||
if (completion && !ohci->async_complete) {
|
||||
|
@ -463,6 +463,12 @@ static void xhci_mfwrap_timer(void *opaque)
|
||||
xhci_mfwrap_update(xhci);
|
||||
}
|
||||
|
||||
static void xhci_die(XHCIState *xhci)
|
||||
{
|
||||
xhci->usbsts |= USBSTS_HCE;
|
||||
DPRINTF("xhci: asserted controller error\n");
|
||||
}
|
||||
|
||||
static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high)
|
||||
{
|
||||
if (sizeof(dma_addr_t) == 4) {
|
||||
@ -488,7 +494,14 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr,
|
||||
|
||||
assert((len % sizeof(uint32_t)) == 0);
|
||||
|
||||
dma_memory_read(xhci->as, addr, buf, len, MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_memory_read(xhci->as, addr, buf, len,
|
||||
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
|
||||
__func__);
|
||||
memset(buf, 0xff, len);
|
||||
xhci_die(xhci);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
|
||||
buf[i] = le32_to_cpu(buf[i]);
|
||||
@ -496,7 +509,7 @@ static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr,
|
||||
}
|
||||
|
||||
static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
|
||||
uint32_t *buf, size_t len)
|
||||
const uint32_t *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
uint32_t tmp[5];
|
||||
@ -508,7 +521,13 @@ static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
|
||||
for (i = 0; i < n; i++) {
|
||||
tmp[i] = cpu_to_le32(buf[i]);
|
||||
}
|
||||
dma_memory_write(xhci->as, addr, tmp, len, MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_memory_write(xhci->as, addr, tmp, len,
|
||||
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
|
||||
__func__);
|
||||
xhci_die(xhci);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
|
||||
@ -593,12 +612,6 @@ static inline int xhci_running(XHCIState *xhci)
|
||||
return !(xhci->usbsts & USBSTS_HCH);
|
||||
}
|
||||
|
||||
static void xhci_die(XHCIState *xhci)
|
||||
{
|
||||
xhci->usbsts |= USBSTS_HCE;
|
||||
DPRINTF("xhci: asserted controller error\n");
|
||||
}
|
||||
|
||||
static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
{
|
||||
XHCIInterrupter *intr = &xhci->intr[v];
|
||||
@ -619,7 +632,12 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
ev_trb.status, ev_trb.control);
|
||||
|
||||
addr = intr->er_start + TRB_SIZE*intr->er_ep_idx;
|
||||
dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE, MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_memory_write(xhci->as, addr, &ev_trb, TRB_SIZE,
|
||||
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
|
||||
__func__);
|
||||
xhci_die(xhci);
|
||||
}
|
||||
|
||||
intr->er_ep_idx++;
|
||||
if (intr->er_ep_idx >= intr->er_size) {
|
||||
@ -680,8 +698,12 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb,
|
||||
|
||||
while (1) {
|
||||
TRBType type;
|
||||
dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE,
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_memory_read(xhci->as, ring->dequeue, trb, TRB_SIZE,
|
||||
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
trb->addr = ring->dequeue;
|
||||
trb->ccs = ring->ccs;
|
||||
le64_to_cpus(&trb->parameter);
|
||||
@ -798,8 +820,14 @@ static void xhci_er_reset(XHCIState *xhci, int v)
|
||||
xhci_die(xhci);
|
||||
return;
|
||||
}
|
||||
dma_memory_read(xhci->as, erstba, &seg, sizeof(seg),
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_memory_read(xhci->as, erstba, &seg, sizeof(seg),
|
||||
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory access failed!\n",
|
||||
__func__);
|
||||
xhci_die(xhci);
|
||||
return;
|
||||
}
|
||||
|
||||
le32_to_cpus(&seg.addr_low);
|
||||
le32_to_cpus(&seg.addr_high);
|
||||
le32_to_cpus(&seg.size);
|
||||
@ -992,7 +1020,9 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx,
|
||||
}
|
||||
sctx = epctx->pstreams + streamid;
|
||||
} else {
|
||||
FIXME("secondary streams not implemented yet");
|
||||
fprintf(stderr, "xhci: FIXME: secondary streams not implemented yet");
|
||||
*cc_error = CC_INVALID_STREAM_TYPE_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sctx->sct == -1) {
|
||||
@ -2415,8 +2445,12 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
|
||||
/* TODO: actually implement real values here */
|
||||
bw_ctx[0] = 0;
|
||||
memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
|
||||
dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx),
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx),
|
||||
MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA memory write failed!\n",
|
||||
__func__);
|
||||
return CC_TRB_ERROR;
|
||||
}
|
||||
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
|
@ -263,6 +263,7 @@ usb_msd_packet_complete(void) ""
|
||||
usb_msd_cmd_submit(unsigned lun, unsigned tag, unsigned flags, unsigned len, unsigned data_len) "lun %u, tag 0x%x, flags 0x%08x, len %d, data-len %d"
|
||||
usb_msd_cmd_complete(unsigned status, unsigned tag) "status %d, tag 0x%x"
|
||||
usb_msd_cmd_cancel(unsigned tag) "tag 0x%x"
|
||||
usb_msd_fatal_error(void) ""
|
||||
|
||||
# dev-uas.c
|
||||
usb_uas_reset(int addr) "dev %d"
|
||||
|
@ -40,6 +40,7 @@ struct MSDState {
|
||||
bool removable;
|
||||
bool commandlog;
|
||||
SCSIDevice *scsi_dev;
|
||||
bool needs_reset;
|
||||
};
|
||||
|
||||
typedef struct MSDState MSDState;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef QEMU_MAIN_H
|
||||
#define QEMU_MAIN_H
|
||||
|
||||
int qemu_main(int argc, char **argv, char **envp);
|
||||
int qemu_default_main(void);
|
||||
extern int (*qemu_main)(void);
|
||||
|
||||
#endif /* QEMU_MAIN_H */
|
||||
|
@ -284,23 +284,10 @@ bool qemu_in_main_thread(void);
|
||||
* Please refer to include/block/block-global-state.h for more
|
||||
* information about GS API.
|
||||
*/
|
||||
#ifdef CONFIG_COCOA
|
||||
/*
|
||||
* When using the Cocoa UI, addRemovableDevicesMenuItems() is called from
|
||||
* a thread different from the QEMU main thread and can not take the BQL,
|
||||
* triggering this assertions in the block layer (commit 0439c5a462).
|
||||
* As the Cocoa fix is not trivial, disable this assertion for the v7.0.0
|
||||
* release (when using Cocoa); we will restore it immediately after the
|
||||
* release.
|
||||
* This issue is tracked as https://gitlab.com/qemu-project/qemu/-/issues/926
|
||||
*/
|
||||
#define GLOBAL_STATE_CODE()
|
||||
#else
|
||||
#define GLOBAL_STATE_CODE() \
|
||||
do { \
|
||||
assert(qemu_in_main_thread()); \
|
||||
} while (0)
|
||||
#endif /* CONFIG_COCOA */
|
||||
|
||||
/*
|
||||
* Mark and check that the function is part of the I/O API.
|
||||
|
@ -102,7 +102,7 @@ void qemu_boot_set(const char *boot_order, Error **errp);
|
||||
|
||||
bool defaults_enabled(void);
|
||||
|
||||
void qemu_init(int argc, char **argv, char **envp);
|
||||
void qemu_init(int argc, char **argv);
|
||||
int qemu_main_loop(void);
|
||||
void qemu_cleanup(void);
|
||||
|
||||
|
@ -70,6 +70,7 @@ void hmp_mouse_set(Monitor *mon, const QDict *qdict);
|
||||
/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
|
||||
constants) */
|
||||
#define QEMU_KEY_ESC1(c) ((c) | 0xe100)
|
||||
#define QEMU_KEY_TAB 0x0009
|
||||
#define QEMU_KEY_BACKSPACE 0x007f
|
||||
#define QEMU_KEY_UP QEMU_KEY_ESC1('A')
|
||||
#define QEMU_KEY_DOWN QEMU_KEY_ESC1('B')
|
||||
|
19
meson.build
19
meson.build
@ -589,12 +589,6 @@ endif
|
||||
|
||||
cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'],
|
||||
required: get_option('cocoa'))
|
||||
if cocoa.found() and get_option('sdl').enabled()
|
||||
error('Cocoa and SDL cannot be enabled at the same time')
|
||||
endif
|
||||
if cocoa.found() and get_option('gtk').enabled()
|
||||
error('Cocoa and GTK+ cannot be enabled at the same time')
|
||||
endif
|
||||
|
||||
vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet'))
|
||||
if vmnet.found() and not cc.has_header_symbol('vmnet/vmnet.h',
|
||||
@ -681,6 +675,11 @@ if not get_option('jack').auto() or have_system
|
||||
jack = dependency('jack', required: get_option('jack'),
|
||||
method: 'pkg-config', kwargs: static_kwargs)
|
||||
endif
|
||||
sndio = not_found
|
||||
if not get_option('sndio').auto() or have_system
|
||||
sndio = dependency('sndio', required: get_option('sndio'),
|
||||
method: 'pkg-config', kwargs: static_kwargs)
|
||||
endif
|
||||
|
||||
spice_protocol = not_found
|
||||
if not get_option('spice_protocol').auto() or have_system
|
||||
@ -921,7 +920,7 @@ if not get_option('brlapi').auto() or have_system
|
||||
endif
|
||||
|
||||
sdl = not_found
|
||||
if not get_option('sdl').auto() or (have_system and not cocoa.found())
|
||||
if not get_option('sdl').auto() or have_system
|
||||
sdl = dependency('sdl2', required: get_option('sdl'), kwargs: static_kwargs)
|
||||
sdl_image = not_found
|
||||
endif
|
||||
@ -1187,7 +1186,7 @@ endif
|
||||
gtk = not_found
|
||||
gtkx11 = not_found
|
||||
vte = not_found
|
||||
if not get_option('gtk').auto() or (have_system and not cocoa.found())
|
||||
if not get_option('gtk').auto() or have_system
|
||||
gtk = dependency('gtk+-3.0', version: '>=3.22.0',
|
||||
method: 'pkg-config',
|
||||
required: get_option('gtk'),
|
||||
@ -1597,6 +1596,7 @@ if have_system
|
||||
'oss': oss.found(),
|
||||
'pa': pulse.found(),
|
||||
'sdl': sdl.found(),
|
||||
'sndio': sndio.found(),
|
||||
}
|
||||
foreach k, v: audio_drivers_available
|
||||
config_host_data.set('CONFIG_AUDIO_' + k.to_upper(), v)
|
||||
@ -1604,7 +1604,7 @@ if have_system
|
||||
|
||||
# Default to native drivers first, OSS second, SDL third
|
||||
audio_drivers_priority = \
|
||||
[ 'pa', 'coreaudio', 'dsound', 'oss' ] + \
|
||||
[ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \
|
||||
(targetos == 'linux' ? [] : [ 'sdl' ])
|
||||
audio_drivers_default = []
|
||||
foreach k: audio_drivers_priority
|
||||
@ -3928,6 +3928,7 @@ if vnc.found()
|
||||
endif
|
||||
if targetos not in ['darwin', 'haiku', 'windows']
|
||||
summary_info += {'OSS support': oss}
|
||||
summary_info += {'sndio support': sndio}
|
||||
elif targetos == 'darwin'
|
||||
summary_info += {'CoreAudio support': coreaudio}
|
||||
elif targetos == 'windows'
|
||||
|
@ -21,7 +21,7 @@ option('tls_priority', type : 'string', value : 'NORMAL',
|
||||
option('default_devices', type : 'boolean', value : true,
|
||||
description: 'Include a default selection of devices in emulators')
|
||||
option('audio_drv_list', type: 'array', value: ['default'],
|
||||
choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', 'pa', 'sdl'],
|
||||
choices: ['alsa', 'coreaudio', 'default', 'dsound', 'jack', 'oss', 'pa', 'sdl', 'sndio'],
|
||||
description: 'Set audio driver list')
|
||||
option('block_drv_rw_whitelist', type : 'string', value : '',
|
||||
description: 'set block driver read-write whitelist (by default affects only QEMU, not tools like qemu-img)')
|
||||
@ -240,6 +240,8 @@ option('oss', type: 'feature', value: 'auto',
|
||||
description: 'OSS sound support')
|
||||
option('pa', type: 'feature', value: 'auto',
|
||||
description: 'PulseAudio sound support')
|
||||
option('sndio', type: 'feature', value: 'auto',
|
||||
description: 'sndio sound support')
|
||||
|
||||
option('vhost_kernel', type: 'feature', value: 'auto',
|
||||
description: 'vhost kernel backend support')
|
||||
|
@ -106,6 +106,28 @@
|
||||
'*out': 'AudiodevAlsaPerDirectionOptions',
|
||||
'*threshold': 'uint32' } }
|
||||
|
||||
##
|
||||
# @AudiodevSndioOptions:
|
||||
#
|
||||
# Options of the sndio audio backend.
|
||||
#
|
||||
# @in: options of the capture stream
|
||||
#
|
||||
# @out: options of the playback stream
|
||||
#
|
||||
# @dev: the name of the sndio device to use (default 'default')
|
||||
#
|
||||
# @latency: play buffer size (in microseconds)
|
||||
#
|
||||
# Since: 7.2
|
||||
##
|
||||
{ 'struct': 'AudiodevSndioOptions',
|
||||
'data': {
|
||||
'*in': 'AudiodevPerDirectionOptions',
|
||||
'*out': 'AudiodevPerDirectionOptions',
|
||||
'*dev': 'str',
|
||||
'*latency': 'uint32'} }
|
||||
|
||||
##
|
||||
# @AudiodevCoreaudioPerDirectionOptions:
|
||||
#
|
||||
@ -387,7 +409,7 @@
|
||||
##
|
||||
{ 'enum': 'AudiodevDriver',
|
||||
'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa',
|
||||
'sdl', 'spice', 'wav' ] }
|
||||
'sdl', 'sndio', 'spice', 'wav' ] }
|
||||
|
||||
##
|
||||
# @Audiodev:
|
||||
@ -418,5 +440,6 @@
|
||||
'oss': 'AudiodevOssOptions',
|
||||
'pa': 'AudiodevPaOptions',
|
||||
'sdl': 'AudiodevSdlOptions',
|
||||
'sndio': 'AudiodevSndioOptions',
|
||||
'spice': 'AudiodevGenericOptions',
|
||||
'wav': 'AudiodevWavOptions' } }
|
||||
|
@ -769,6 +769,9 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
|
||||
"-audiodev sdl,id=id[,prop[=value][,...]]\n"
|
||||
" in|out.buffer-count= number of buffers\n"
|
||||
#endif
|
||||
#ifdef CONFIG_AUDIO_SNDIO
|
||||
"-audiodev sndio,id=id[,prop[=value][,...]]\n"
|
||||
#endif
|
||||
#ifdef CONFIG_SPICE
|
||||
"-audiodev spice,id=id[,prop[=value][,...]]\n"
|
||||
#endif
|
||||
@ -935,6 +938,19 @@ SRST
|
||||
``in|out.buffer-count=count``
|
||||
Sets the count of the buffers.
|
||||
|
||||
``-audiodev sndio,id=id[,prop[=value][,...]]``
|
||||
Creates a backend using SNDIO. This backend is available on
|
||||
OpenBSD and most other Unix-like systems.
|
||||
|
||||
Sndio specific options are:
|
||||
|
||||
``in|out.dev=device``
|
||||
Specify the sndio device to use for input and/or output. Default
|
||||
is ``default``.
|
||||
|
||||
``in|out.latency=usecs``
|
||||
Sets the desired period length in microseconds.
|
||||
|
||||
``-audiodev spice,id=id[,prop[=value][,...]]``
|
||||
Creates a backend that sends audio through SPICE. This backend
|
||||
requires ``-spice`` and automatically selected in that case, so
|
||||
|
@ -1,7 +1,7 @@
|
||||
# This file is generated by meson-buildoptions.py, do not edit!
|
||||
meson_options_help() {
|
||||
printf "%s\n" ' --audio-drv-list=CHOICES Set audio driver list [default] (choices:'
|
||||
printf "%s\n" ' alsa/coreaudio/default/dsound/jack/oss/pa/sdl)'
|
||||
printf "%s\n" ' --audio-drv-list=CHOICES Set audio driver list [default] (choices: alsa/co'
|
||||
printf "%s\n" ' reaudio/default/dsound/jack/oss/pa/sdl/sndio)'
|
||||
printf "%s\n" ' --block-drv-ro-whitelist=VALUE'
|
||||
printf "%s\n" ' set block driver read-only whitelist (by default'
|
||||
printf "%s\n" ' affects only QEMU, not tools like qemu-img)'
|
||||
@ -144,6 +144,7 @@ meson_options_help() {
|
||||
printf "%s\n" ' slirp-smbd use smbd (at path --smbd=*) in slirp networking'
|
||||
printf "%s\n" ' smartcard CA smartcard emulation support'
|
||||
printf "%s\n" ' snappy snappy compression support'
|
||||
printf "%s\n" ' sndio sndio sound support'
|
||||
printf "%s\n" ' sparse sparse checker'
|
||||
printf "%s\n" ' spice Spice server support'
|
||||
printf "%s\n" ' spice-protocol Spice protocol support'
|
||||
@ -393,6 +394,8 @@ _meson_option_parse() {
|
||||
--disable-smartcard) printf "%s" -Dsmartcard=disabled ;;
|
||||
--enable-snappy) printf "%s" -Dsnappy=enabled ;;
|
||||
--disable-snappy) printf "%s" -Dsnappy=disabled ;;
|
||||
--enable-sndio) printf "%s" -Dsndio=enabled ;;
|
||||
--disable-sndio) printf "%s" -Dsndio=disabled ;;
|
||||
--enable-sparse) printf "%s" -Dsparse=enabled ;;
|
||||
--disable-sparse) printf "%s" -Dsparse=disabled ;;
|
||||
--sphinx-build=*) quote_sh "-Dsphinx_build=$2" ;;
|
||||
|
@ -30,20 +30,20 @@
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
int qemu_main(int argc, char **argv, char **envp)
|
||||
int qemu_default_main(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
qemu_init(argc, argv, envp);
|
||||
status = qemu_main_loop();
|
||||
qemu_cleanup();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_COCOA
|
||||
int (*qemu_main)(void) = qemu_default_main;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return qemu_main(argc, argv, NULL);
|
||||
qemu_init(argc, argv);
|
||||
return qemu_main();
|
||||
}
|
||||
#endif
|
||||
|
@ -2605,7 +2605,7 @@ void qmp_x_exit_preconfig(Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_init(int argc, char **argv, char **envp)
|
||||
void qemu_init(int argc, char **argv)
|
||||
{
|
||||
QemuOpts *opts;
|
||||
QemuOpts *icount_opts = NULL, *accel_opts = NULL;
|
||||
|
@ -218,7 +218,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp)
|
||||
g_free(pretty_cmd_line);
|
||||
}
|
||||
|
||||
qemu_init(result.we_wordc, result.we_wordv, NULL);
|
||||
qemu_init(result.we_wordc, result.we_wordv);
|
||||
|
||||
/* re-enable the rcu atfork, which was previously disabled in qemu_init */
|
||||
rcu_enable_atfork();
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "ui/clipboard.h"
|
||||
#include "trace.h"
|
||||
|
||||
static NotifierList clipboard_notifiers =
|
||||
NOTIFIER_LIST_INITIALIZER(clipboard_notifiers);
|
||||
@ -43,17 +44,23 @@ void qemu_clipboard_peer_release(QemuClipboardPeer *peer,
|
||||
|
||||
bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
if (!info->has_serial ||
|
||||
!cbinfo[info->selection] ||
|
||||
!cbinfo[info->selection]->has_serial) {
|
||||
trace_clipboard_check_serial(-1, -1, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (client) {
|
||||
return cbinfo[info->selection]->serial >= info->serial;
|
||||
ok = info->serial >= cbinfo[info->selection]->serial;
|
||||
} else {
|
||||
return cbinfo[info->selection]->serial > info->serial;
|
||||
ok = info->serial > cbinfo[info->selection]->serial;
|
||||
}
|
||||
|
||||
trace_clipboard_check_serial(cbinfo[info->selection]->serial, info->serial, ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void qemu_clipboard_update(QemuClipboardInfo *info)
|
||||
@ -132,7 +139,14 @@ void qemu_clipboard_request(QemuClipboardInfo *info,
|
||||
void qemu_clipboard_reset_serial(void)
|
||||
{
|
||||
QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL };
|
||||
int i;
|
||||
|
||||
for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) {
|
||||
QemuClipboardInfo *info = qemu_clipboard_info(i);
|
||||
if (info) {
|
||||
info->serial = 0;
|
||||
}
|
||||
}
|
||||
notifier_list_notify(&clipboard_notifiers, ¬ify);
|
||||
}
|
||||
|
||||
|
144
ui/cocoa.m
144
ui/cocoa.m
@ -100,13 +100,9 @@ static int cursor_hide = 1;
|
||||
static int left_command_key_enabled = 1;
|
||||
static bool swap_opt_cmd;
|
||||
|
||||
static int gArgc;
|
||||
static char **gArgv;
|
||||
static bool stretch_video;
|
||||
static NSTextField *pauseLabel;
|
||||
|
||||
static QemuSemaphore display_init_sem;
|
||||
static QemuSemaphore app_started_sem;
|
||||
static bool allow_events;
|
||||
|
||||
static NSInteger cbchangecount = -1;
|
||||
@ -597,7 +593,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
|
||||
/*
|
||||
* Don't try to tell QEMU about UI information in the application
|
||||
* startup phase -- we haven't yet registered dcl with the QEMU UI
|
||||
* layer, and also trying to take the iothread lock would deadlock.
|
||||
* layer.
|
||||
* When cocoa_display_init() does register the dcl, the UI layer
|
||||
* will call cocoa_switch(), which will call updateUIInfo, so
|
||||
* we don't lose any information here.
|
||||
@ -790,16 +786,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
|
||||
|
||||
- (bool) handleEvent:(NSEvent *)event
|
||||
{
|
||||
if(!allow_events) {
|
||||
/*
|
||||
* Just let OSX have all events that arrive before
|
||||
* applicationDidFinishLaunching.
|
||||
* This avoids a deadlock on the iothread lock, which cocoa_display_init()
|
||||
* will not drop until after the app_started_sem is posted. (In theory
|
||||
* there should not be any such events, but OSX Catalina now emits some.)
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
return bool_with_iothread_lock(^{
|
||||
return [self handleEventLocked:event];
|
||||
});
|
||||
@ -1287,8 +1273,6 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
|
||||
{
|
||||
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
|
||||
allow_events = true;
|
||||
/* Tell cocoa_display_init to proceed */
|
||||
qemu_sem_post(&app_started_sem);
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
@ -1919,92 +1903,45 @@ static void cocoa_clipboard_request(QemuClipboardInfo *info,
|
||||
/*
|
||||
* The startup process for the OSX/Cocoa UI is complicated, because
|
||||
* OSX insists that the UI runs on the initial main thread, and so we
|
||||
* need to start a second thread which runs the vl.c qemu_main():
|
||||
*
|
||||
* Initial thread: 2nd thread:
|
||||
* need to start a second thread which runs the qemu_default_main():
|
||||
* in main():
|
||||
* create qemu-main thread
|
||||
* wait on display_init semaphore
|
||||
* call qemu_main()
|
||||
* ...
|
||||
* in cocoa_display_init():
|
||||
* post the display_init semaphore
|
||||
* wait on app_started semaphore
|
||||
* create application, menus, etc
|
||||
* enter OSX run loop
|
||||
* in applicationDidFinishLaunching:
|
||||
* post app_started semaphore
|
||||
* tell main thread to fullscreen if needed
|
||||
* [...]
|
||||
* run qemu main-loop
|
||||
*
|
||||
* We do this in two stages so that we don't do the creation of the
|
||||
* GUI application menus and so on for command line options like --help
|
||||
* where we want to just print text to stdout and exit immediately.
|
||||
* in cocoa_display_init():
|
||||
* assign cocoa_main to qemu_main
|
||||
* create application, menus, etc
|
||||
* in cocoa_main():
|
||||
* create qemu-main thread
|
||||
* enter OSX run loop
|
||||
*/
|
||||
|
||||
static void *call_qemu_main(void *opaque)
|
||||
{
|
||||
int status;
|
||||
|
||||
COCOA_DEBUG("Second thread: calling qemu_main()\n");
|
||||
status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
|
||||
COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
|
||||
COCOA_DEBUG("Second thread: calling qemu_default_main()\n");
|
||||
qemu_mutex_lock_iothread();
|
||||
status = qemu_default_main();
|
||||
qemu_mutex_unlock_iothread();
|
||||
COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n");
|
||||
[cbowner release];
|
||||
exit(status);
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
static int cocoa_main()
|
||||
{
|
||||
QemuThread thread;
|
||||
|
||||
COCOA_DEBUG("Entered main()\n");
|
||||
gArgc = argc;
|
||||
gArgv = argv;
|
||||
|
||||
qemu_sem_init(&display_init_sem, 0);
|
||||
qemu_sem_init(&app_started_sem, 0);
|
||||
COCOA_DEBUG("Entered %s()\n", __func__);
|
||||
|
||||
qemu_mutex_unlock_iothread();
|
||||
qemu_thread_create(&thread, "qemu_main", call_qemu_main,
|
||||
NULL, QEMU_THREAD_DETACHED);
|
||||
|
||||
COCOA_DEBUG("Main thread: waiting for display_init_sem\n");
|
||||
qemu_sem_wait(&display_init_sem);
|
||||
COCOA_DEBUG("Main thread: initializing app\n");
|
||||
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
// Pull this console process up to being a fully-fledged graphical
|
||||
// app with a menubar and Dock icon
|
||||
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
||||
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
||||
|
||||
[QemuApplication sharedApplication];
|
||||
|
||||
create_initial_menus();
|
||||
|
||||
/*
|
||||
* Create the menu entries which depend on QEMU state (for consoles
|
||||
* and removeable devices). These make calls back into QEMU functions,
|
||||
* which is OK because at this point we know that the second thread
|
||||
* holds the iothread lock and is synchronously waiting for us to
|
||||
* finish.
|
||||
*/
|
||||
add_console_menu_entries();
|
||||
addRemovableDevicesMenuItems();
|
||||
|
||||
// Create an Application controller
|
||||
QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
|
||||
[NSApp setDelegate:appController];
|
||||
|
||||
// Start the main event loop
|
||||
COCOA_DEBUG("Main thread: entering OSX run loop\n");
|
||||
[NSApp run];
|
||||
COCOA_DEBUG("Main thread: left OSX run loop, exiting\n");
|
||||
COCOA_DEBUG("Main thread: left OSX run loop, which should never happen\n");
|
||||
|
||||
[appController release];
|
||||
[pool release];
|
||||
|
||||
return 0;
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
@ -2083,25 +2020,42 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
|
||||
|
||||
static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
||||
{
|
||||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
|
||||
|
||||
/* Tell main thread to go ahead and create the app and enter the run loop */
|
||||
qemu_sem_post(&display_init_sem);
|
||||
qemu_sem_wait(&app_started_sem);
|
||||
COCOA_DEBUG("cocoa_display_init: app start completed\n");
|
||||
qemu_main = cocoa_main;
|
||||
|
||||
// Pull this console process up to being a fully-fledged graphical
|
||||
// app with a menubar and Dock icon
|
||||
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
||||
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
||||
|
||||
[QemuApplication sharedApplication];
|
||||
|
||||
create_initial_menus();
|
||||
|
||||
/*
|
||||
* Create the menu entries which depend on QEMU state (for consoles
|
||||
* and removeable devices). These make calls back into QEMU functions,
|
||||
* which is OK because at this point we know that the second thread
|
||||
* holds the iothread lock and is synchronously waiting for us to
|
||||
* finish.
|
||||
*/
|
||||
add_console_menu_entries();
|
||||
addRemovableDevicesMenuItems();
|
||||
|
||||
// Create an Application controller
|
||||
QemuCocoaAppController *controller = [[QemuCocoaAppController alloc] init];
|
||||
[NSApp setDelegate:controller];
|
||||
|
||||
QemuCocoaAppController *controller = (QemuCocoaAppController *)[[NSApplication sharedApplication] delegate];
|
||||
/* if fullscreen mode is to be used */
|
||||
if (opts->has_full_screen && opts->full_screen) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[NSApp activateIgnoringOtherApps: YES];
|
||||
[controller toggleFullScreen: nil];
|
||||
});
|
||||
[NSApp activateIgnoringOtherApps: YES];
|
||||
[controller toggleFullScreen: nil];
|
||||
}
|
||||
if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[controller setFullGrab: nil];
|
||||
});
|
||||
[controller setFullGrab: nil];
|
||||
}
|
||||
|
||||
if (opts->has_show_cursor && opts->show_cursor) {
|
||||
@ -2121,6 +2075,8 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
||||
qemu_event_init(&cbevent, false);
|
||||
cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
|
||||
qemu_clipboard_peer_register(&cbpeer);
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
static QemuDisplay qemu_display_cocoa = {
|
||||
|
@ -304,8 +304,8 @@ static bool png_save(int fd, pixman_image_t *image, Error **errp)
|
||||
{
|
||||
int width = pixman_image_get_width(image);
|
||||
int height = pixman_image_get_height(image);
|
||||
g_autofree png_struct *png_ptr = NULL;
|
||||
g_autofree png_info *info_ptr = NULL;
|
||||
png_struct *png_ptr;
|
||||
png_info *info_ptr;
|
||||
g_autoptr(pixman_image_t) linebuf =
|
||||
qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width);
|
||||
uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf);
|
||||
@ -346,7 +346,6 @@ static bool png_save(int fd, pixman_image_t *image, Error **errp)
|
||||
qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
|
||||
png_write_row(png_ptr, buf);
|
||||
}
|
||||
qemu_pixman_image_unref(linebuf);
|
||||
|
||||
png_write_end(png_ptr, NULL);
|
||||
|
||||
@ -1368,6 +1367,7 @@ static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
|
||||
[Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
|
||||
[Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
|
||||
[Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
|
||||
[Q_KEY_CODE_TAB] = QEMU_KEY_TAB,
|
||||
[Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
|
||||
};
|
||||
|
||||
|
@ -127,15 +127,20 @@ xkeymap_vendor(const char *name) "vendor '%s'"
|
||||
xkeymap_keycodes(const char *name) "keycodes '%s'"
|
||||
xkeymap_keymap(const char *name) "keymap '%s'"
|
||||
|
||||
# clipboard.c
|
||||
clipboard_check_serial(int cur, int recv, bool ok) "cur:%d recv:%d %d"
|
||||
|
||||
# vdagent.c
|
||||
vdagent_open(void) ""
|
||||
vdagent_close(void) ""
|
||||
vdagent_disconnect(void) ""
|
||||
vdagent_send(const char *name) "msg %s"
|
||||
vdagent_send_empty_clipboard(void) ""
|
||||
vdagent_recv_chunk(uint32_t size) "size %d"
|
||||
vdagent_recv_msg(const char *name, uint32_t size) "msg %s, size %d"
|
||||
vdagent_peer_cap(const char *name) "cap %s"
|
||||
vdagent_cb_grab_selection(const char *name) "selection %s"
|
||||
vdagent_cb_grab_discard(const char *name, int cur, int recv) "selection %s, cur:%d recv:%d"
|
||||
vdagent_cb_grab_type(const char *name) "type %s"
|
||||
vdagent_cb_serial_discard(uint32_t current, uint32_t received) "current=%u, received=%u"
|
||||
|
||||
|
13
ui/vdagent.c
13
ui/vdagent.c
@ -471,7 +471,7 @@ static void vdagent_clipboard_reset_serial(VDAgentChardev *vd)
|
||||
|
||||
/* reopen the agent connection to reset the serial state */
|
||||
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
/* OPENED again after the guest disconnected, see set_fe_open */
|
||||
}
|
||||
|
||||
static void vdagent_clipboard_notify(Notifier *notifier, void *data)
|
||||
@ -533,6 +533,8 @@ static void vdagent_clipboard_recv_grab(VDAgentChardev *vd, uint8_t s, uint32_t
|
||||
info->has_serial = true;
|
||||
info->serial = *(uint32_t *)data;
|
||||
if (info->serial < vd->last_serial[s]) {
|
||||
trace_vdagent_cb_grab_discard(GET_NAME(sel_name, s),
|
||||
vd->last_serial[s], info->serial);
|
||||
/* discard lower-ordering guest grab */
|
||||
return;
|
||||
}
|
||||
@ -717,8 +719,10 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg)
|
||||
if (have_mouse(vd) && vd->mouse_hs) {
|
||||
qemu_input_handler_activate(vd->mouse_hs);
|
||||
}
|
||||
|
||||
memset(vd->last_serial, 0, sizeof(vd->last_serial));
|
||||
|
||||
if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) {
|
||||
memset(vd->last_serial, 0, sizeof(vd->last_serial));
|
||||
vd->cbpeer.name = "vdagent";
|
||||
vd->cbpeer.notifier.notify = vdagent_clipboard_notify;
|
||||
vd->cbpeer.request = vdagent_clipboard_request;
|
||||
@ -853,6 +857,8 @@ static void vdagent_chr_accept_input(Chardev *chr)
|
||||
|
||||
static void vdagent_disconnect(VDAgentChardev *vd)
|
||||
{
|
||||
trace_vdagent_disconnect();
|
||||
|
||||
buffer_reset(&vd->outbuf);
|
||||
vdagent_reset_bufs(vd);
|
||||
vd->caps = 0;
|
||||
@ -869,6 +875,9 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
|
||||
{
|
||||
if (!fe_open) {
|
||||
trace_vdagent_close();
|
||||
/* To reset_serial, we CLOSED our side. Make sure the other end knows we
|
||||
* are ready again. */
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user