Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d95d7d802c | |||
| fbb7ef56d5 | |||
| 6dd35fd81e | |||
| 2762955f72 | |||
| 73ad33ef7b | |||
| 06ac27f683 | |||
| 65eb1e6b4c | |||
| 81ebb07c56 | |||
| d1f52a1d70 | |||
| 191e1f0acd | |||
| 49dd6d0d33 | |||
| f2dcc6cec2 | |||
| 4045a85ad1 | |||
| 765b37da3f | |||
| 9a644c4b4d | |||
| 5706db1deb | |||
| 307119e7d9 | |||
| 3cec7cc22f | |||
| 14382605da | |||
| 0bac111167 | |||
| 0a2df857a7 | |||
| 9faffeb777 | |||
| fafa4d508b | |||
| 5ff1547b75 | |||
| 73da023209 | |||
| 773495364f | |||
| f4d248bdc3 | |||
| 1e354528bd | |||
| 6b5166f8a8 | |||
| b027a538c6 | |||
| be93f21627 | |||
| 6484e42247 | |||
| 82e1cc4bf9 | |||
| a90a7425cf | |||
| 6e99c631f1 | |||
| e8dd1d9c39 | |||
| 95b1416ae9 | |||
| 0bc12c4f7e | |||
| 4cb618abc1 | |||
| a4ef02fd9b | |||
| d218b28d28 | |||
| 91b4d29f4e | |||
| 1d565b21e1 | |||
| 60eb27fe49 | |||
| d0f44a55fa | |||
| a2368e01c9 | |||
| 3408694525 | |||
| 5ed9a259c1 | |||
| 563807520f | |||
| e42fd944f0 | |||
| 91c45a38f2 | |||
| 6773f9b687 | |||
| 28b027d5b6 | |||
| 5204ea79ea | |||
| e117f52636 | |||
| cd0d45c401 | |||
| 284b731a6a | |||
| b435f3f3d1 | |||
| 4fa3dd17dc | |||
| a97270ad5d | |||
| 73d9a7961a | |||
| f68945d42b | |||
| 37fb569c01 | |||
| ce39bfc918 | |||
| 1a8f46f8d6 | |||
| 7c1e52ba6f | |||
| bca7856ae8 | |||
| e3807054e2 | |||
| f796baa1b3 | |||
| b3af1bc9d2 | |||
| 5cd8cadae8 | |||
| 0163a2e025 | |||
| e45a1ebfc6 | |||
| 5b61d57521 | |||
| 5ee6926582 | |||
| 7205c9ec52 | |||
| 76cc7b587f | |||
| c3049a56d6 | |||
| 56e93d26b8 | |||
| d8e3b729cf | |||
| afa25c4bb5 | |||
| 0b70743d4f | |||
| 169b71331e | |||
| 4ebc736e99 | |||
| c96d9286a6 | |||
| 39e16a5b70 | |||
| 060ab76356 | |||
| bd8f1ebce4 | |||
| 409b52bfe1 | |||
| 1670735dd7 | |||
| 89ae0ff9b7 | |||
| 104655a5c8 | |||
| 3df5de64f0 | |||
| 84689cbb97 | |||
| f2f62c4db2 | |||
| d791d60f1c | |||
| 95c357bc46 | |||
| dc6e3e1e1a | |||
| b421f3f52a | |||
| dd8205130b | |||
| a3d586f704 | |||
| 9b1d21c53b | |||
| ce9782f40a | |||
| adc370a48f | |||
| 3b4afc9e75 | |||
| be3a8c53b4 | |||
| 71c199c81d | |||
| 7c979afd11 | |||
| eab9944c78 | |||
| 5a9259a0b5 | |||
| c6e484707f | |||
| 0e12e61ff9 | |||
| 21549a4642 | |||
| 83a84878da | |||
| 448777c411 | |||
| fe704809b9 | |||
| 0c63237a90 | |||
| db58c063e1 | |||
| 87108bb26c | |||
| 710e2d90da | |||
| f958c8aa13 | |||
| ffaa050371 | |||
| b6ce27a593 | |||
| cc52ea90f8 | |||
| fc004905c5 | |||
| 54790d71e4 | |||
| a3cc2e8159 | |||
| 1141ce2190 | |||
| 588255ad50 | |||
| 40de55affd | |||
| fbdc6892dd | |||
| 975acc0ae6 | |||
| 4e93a68eb3 | |||
| 23c5e39775 | |||
| 8aca0d7586 | |||
| e266d42149 | |||
| 54c720d49d | |||
| adfb743c90 | |||
| b8f059081d | |||
| dfb8e184db | |||
| c17bef3360 | |||
| 9a2ba82302 | |||
| b150613200 | |||
| df91055db5 | |||
| bb9d17f831 | |||
| b6a3cddb22 | |||
| 0b352fd680 | |||
| 6c0196d702 | |||
| f5a5628cf0 | |||
| ab223c9518 | |||
| 3c185597c8 | |||
| 3974c9d8cc | |||
| eed8a8f572 | |||
| e015fe008a | |||
| 9f7c594c00 | |||
| 24bfa207ef | |||
| 5efed5a172 | |||
| 81b2b81062 | |||
| 0eb973f915 | |||
| 0f9b214139 | |||
| 023e314856 | |||
| 48779e5018 | |||
| 1edd34b638 | |||
| 1ceaefbd0d | |||
| 7a8d15d770 | |||
| 08d49df0db | |||
| 38559979bf | |||
| 0ea2730bef |
@@ -770,7 +770,6 @@ F: hw/net/rocker/
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
M: Vassili Karpov (malc) <av1474@comtv.ru>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: audio/
|
||||
@@ -1014,8 +1013,6 @@ M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Maintained
|
||||
F: include/migration/
|
||||
F: migration/
|
||||
F: savevm.c
|
||||
F: arch_init.c
|
||||
F: scripts/vmstate-static-checker.py
|
||||
F: tests/vmstate-static-checker-data/
|
||||
|
||||
|
||||
+2
-1
@@ -132,9 +132,10 @@ obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
|
||||
obj-y += qtest.o bootdevice.o
|
||||
obj-y += hw/
|
||||
obj-$(CONFIG_KVM) += kvm-all.o
|
||||
obj-y += memory.o savevm.o cputlb.o
|
||||
obj-y += memory.o cputlb.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += dump.o
|
||||
obj-y += migration/ram.o migration/savevm.o
|
||||
LIBS := $(libs_softmmu) $(LIBS)
|
||||
|
||||
# xen support
|
||||
|
||||
-1611
File diff suppressed because it is too large
Load Diff
@@ -5,13 +5,9 @@ common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
common-obj-$(CONFIG_COREAUDIO) += coreaudio.o
|
||||
common-obj-$(CONFIG_ALSA) += alsaaudio.o
|
||||
common-obj-$(CONFIG_DSOUND) += dsoundaudio.o
|
||||
common-obj-$(CONFIG_FMOD) += fmodaudio.o
|
||||
common-obj-$(CONFIG_ESD) += esdaudio.o
|
||||
common-obj-$(CONFIG_PA) += paaudio.o
|
||||
common-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
|
||||
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
|
||||
common-obj-y += wavcapture.o
|
||||
|
||||
$(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
|
||||
sdlaudio.o-cflags := $(SDL_CFLAGS)
|
||||
|
||||
+83
-112
@@ -25,6 +25,7 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "audio.h"
|
||||
#include "trace.h"
|
||||
|
||||
#if QEMU_GNUC_PREREQ(4, 3)
|
||||
#pragma GCC diagnostic ignored "-Waddress"
|
||||
@@ -33,9 +34,28 @@
|
||||
#define AUDIO_CAP "alsa"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct ALSAConf {
|
||||
int size_in_usec_in;
|
||||
int size_in_usec_out;
|
||||
const char *pcm_name_in;
|
||||
const char *pcm_name_out;
|
||||
unsigned int buffer_size_in;
|
||||
unsigned int period_size_in;
|
||||
unsigned int buffer_size_out;
|
||||
unsigned int period_size_out;
|
||||
unsigned int threshold;
|
||||
|
||||
int buffer_size_in_overridden;
|
||||
int period_size_in_overridden;
|
||||
|
||||
int buffer_size_out_overridden;
|
||||
int period_size_out_overridden;
|
||||
} ALSAConf;
|
||||
|
||||
struct pollhlp {
|
||||
snd_pcm_t *handle;
|
||||
struct pollfd *pfds;
|
||||
ALSAConf *conf;
|
||||
int count;
|
||||
int mask;
|
||||
};
|
||||
@@ -56,30 +76,6 @@ typedef struct ALSAVoiceIn {
|
||||
struct pollhlp pollhlp;
|
||||
} ALSAVoiceIn;
|
||||
|
||||
static struct {
|
||||
int size_in_usec_in;
|
||||
int size_in_usec_out;
|
||||
const char *pcm_name_in;
|
||||
const char *pcm_name_out;
|
||||
unsigned int buffer_size_in;
|
||||
unsigned int period_size_in;
|
||||
unsigned int buffer_size_out;
|
||||
unsigned int period_size_out;
|
||||
unsigned int threshold;
|
||||
|
||||
int buffer_size_in_overridden;
|
||||
int period_size_in_overridden;
|
||||
|
||||
int buffer_size_out_overridden;
|
||||
int period_size_out_overridden;
|
||||
int verbose;
|
||||
} conf = {
|
||||
.buffer_size_out = 4096,
|
||||
.period_size_out = 1024,
|
||||
.pcm_name_out = "default",
|
||||
.pcm_name_in = "default",
|
||||
};
|
||||
|
||||
struct alsa_params_req {
|
||||
int freq;
|
||||
snd_pcm_format_t fmt;
|
||||
@@ -205,9 +201,7 @@ static void alsa_poll_handler (void *opaque)
|
||||
}
|
||||
|
||||
if (!(revents & hlp->mask)) {
|
||||
if (conf.verbose) {
|
||||
dolog ("revents = %d\n", revents);
|
||||
}
|
||||
trace_alsa_revents(revents);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -266,31 +260,14 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
if (pfds[i].events & POLLIN) {
|
||||
err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler,
|
||||
NULL, hlp);
|
||||
qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp);
|
||||
}
|
||||
if (pfds[i].events & POLLOUT) {
|
||||
if (conf.verbose) {
|
||||
dolog ("POLLOUT %d %d\n", i, pfds[i].fd);
|
||||
}
|
||||
err = qemu_set_fd_handler (pfds[i].fd, NULL,
|
||||
alsa_poll_handler, hlp);
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Set handler events=%#x index=%d fd=%d err=%d\n",
|
||||
pfds[i].events, i, pfds[i].fd, err);
|
||||
trace_alsa_pollout(i, pfds[i].fd);
|
||||
qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp);
|
||||
}
|
||||
trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err);
|
||||
|
||||
if (err) {
|
||||
dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n",
|
||||
pfds[i].events, i, pfds[i].fd, err);
|
||||
|
||||
while (i--) {
|
||||
qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
|
||||
}
|
||||
g_free (pfds);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
hlp->pfds = pfds;
|
||||
hlp->count = count;
|
||||
@@ -476,14 +453,15 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
||||
}
|
||||
|
||||
static int alsa_open (int in, struct alsa_params_req *req,
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep)
|
||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||
ALSAConf *conf)
|
||||
{
|
||||
snd_pcm_t *handle;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
int err;
|
||||
int size_in_usec;
|
||||
unsigned int freq, nchannels;
|
||||
const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
|
||||
const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
|
||||
snd_pcm_uframes_t obt_buffer_size;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
snd_pcm_format_t obtfmt;
|
||||
@@ -522,7 +500,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
|
||||
if (err < 0 && conf.verbose) {
|
||||
if (err < 0) {
|
||||
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
|
||||
}
|
||||
|
||||
@@ -654,7 +632,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!in && conf.threshold) {
|
||||
if (!in && conf->threshold) {
|
||||
snd_pcm_uframes_t threshold;
|
||||
int bytes_per_sec;
|
||||
|
||||
@@ -676,7 +654,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||
break;
|
||||
}
|
||||
|
||||
threshold = (conf.threshold * bytes_per_sec) / 1000;
|
||||
threshold = (conf->threshold * bytes_per_sec) / 1000;
|
||||
alsa_set_threshold (handle, threshold);
|
||||
}
|
||||
|
||||
@@ -686,10 +664,9 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||
|
||||
*handlep = handle;
|
||||
|
||||
if (conf.verbose &&
|
||||
(obtfmt != req->fmt ||
|
||||
if (obtfmt != req->fmt ||
|
||||
obt->nchannels != req->nchannels ||
|
||||
obt->freq != req->freq)) {
|
||||
obt->freq != req->freq) {
|
||||
dolog ("Audio parameters for %s\n", typ);
|
||||
alsa_dump_info (req, obt, obtfmt);
|
||||
}
|
||||
@@ -743,9 +720,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to write %d frames (wrote zero)\n", len);
|
||||
}
|
||||
trace_alsa_wrote_zero(len);
|
||||
return;
|
||||
|
||||
case -EPIPE:
|
||||
@@ -754,9 +729,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
|
||||
len);
|
||||
return;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from playback xrun\n");
|
||||
}
|
||||
trace_alsa_xrun_out();
|
||||
continue;
|
||||
|
||||
case -ESTRPIPE:
|
||||
@@ -767,9 +740,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
|
||||
len);
|
||||
return;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Resuming suspended output stream\n");
|
||||
}
|
||||
trace_alsa_resume_out();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
@@ -819,25 +790,27 @@ static void alsa_fini_out (HWVoiceOut *hw)
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
struct alsa_params_req req;
|
||||
struct alsa_params_obt obt;
|
||||
snd_pcm_t *handle;
|
||||
struct audsettings obt_as;
|
||||
ALSAConf *conf = drv_opaque;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf.period_size_out;
|
||||
req.buffer_size = conf.buffer_size_out;
|
||||
req.size_in_usec = conf.size_in_usec_out;
|
||||
req.period_size = conf->period_size_out;
|
||||
req.buffer_size = conf->buffer_size_out;
|
||||
req.size_in_usec = conf->size_in_usec_out;
|
||||
req.override_mask =
|
||||
(conf.period_size_out_overridden ? 1 : 0) |
|
||||
(conf.buffer_size_out_overridden ? 2 : 0);
|
||||
(conf->period_size_out_overridden ? 1 : 0) |
|
||||
(conf->buffer_size_out_overridden ? 2 : 0);
|
||||
|
||||
if (alsa_open (0, &req, &obt, &handle)) {
|
||||
if (alsa_open (0, &req, &obt, &handle, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -858,6 +831,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
alsa->pollhlp.conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -928,25 +902,26 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
struct alsa_params_req req;
|
||||
struct alsa_params_obt obt;
|
||||
snd_pcm_t *handle;
|
||||
struct audsettings obt_as;
|
||||
ALSAConf *conf = drv_opaque;
|
||||
|
||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.period_size = conf.period_size_in;
|
||||
req.buffer_size = conf.buffer_size_in;
|
||||
req.size_in_usec = conf.size_in_usec_in;
|
||||
req.period_size = conf->period_size_in;
|
||||
req.buffer_size = conf->buffer_size_in;
|
||||
req.size_in_usec = conf->size_in_usec_in;
|
||||
req.override_mask =
|
||||
(conf.period_size_in_overridden ? 1 : 0) |
|
||||
(conf.buffer_size_in_overridden ? 2 : 0);
|
||||
(conf->period_size_in_overridden ? 1 : 0) |
|
||||
(conf->buffer_size_in_overridden ? 2 : 0);
|
||||
|
||||
if (alsa_open (1, &req, &obt, &handle)) {
|
||||
if (alsa_open (1, &req, &obt, &handle, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -967,6 +942,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
}
|
||||
|
||||
alsa->handle = handle;
|
||||
alsa->pollhlp.conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1022,14 +998,10 @@ static int alsa_run_in (HWVoiceIn *hw)
|
||||
dolog ("Failed to resume suspended input stream\n");
|
||||
return 0;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Resuming suspended input stream\n");
|
||||
}
|
||||
trace_alsa_resume_in();
|
||||
break;
|
||||
default:
|
||||
if (conf.verbose) {
|
||||
dolog ("No frames available and ALSA state is %d\n", state);
|
||||
}
|
||||
trace_alsa_no_frames(state);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1064,9 +1036,7 @@ static int alsa_run_in (HWVoiceIn *hw)
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
if (conf.verbose) {
|
||||
dolog ("Failed to read %ld frames (read zero)\n", len);
|
||||
}
|
||||
trace_alsa_read_zero(len);
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
@@ -1074,9 +1044,7 @@ static int alsa_run_in (HWVoiceIn *hw)
|
||||
alsa_logerr (nread, "Failed to read %ld frames\n", len);
|
||||
goto exit;
|
||||
}
|
||||
if (conf.verbose) {
|
||||
dolog ("Recovering from capture xrun\n");
|
||||
}
|
||||
trace_alsa_xrun_in();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
@@ -1148,82 +1116,85 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ALSAConf glob_conf = {
|
||||
.buffer_size_out = 4096,
|
||||
.period_size_out = 1024,
|
||||
.pcm_name_out = "default",
|
||||
.pcm_name_in = "default",
|
||||
};
|
||||
|
||||
static void *alsa_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
ALSAConf *conf = g_malloc(sizeof(ALSAConf));
|
||||
*conf = glob_conf;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void alsa_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option alsa_options[] = {
|
||||
{
|
||||
.name = "DAC_SIZE_IN_USEC",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.size_in_usec_out,
|
||||
.valp = &glob_conf.size_in_usec_out,
|
||||
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_PERIOD_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.period_size_out,
|
||||
.valp = &glob_conf.period_size_out,
|
||||
.descr = "DAC period size (0 to go with system default)",
|
||||
.overriddenp = &conf.period_size_out_overridden
|
||||
.overriddenp = &glob_conf.period_size_out_overridden
|
||||
},
|
||||
{
|
||||
.name = "DAC_BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.buffer_size_out,
|
||||
.valp = &glob_conf.buffer_size_out,
|
||||
.descr = "DAC buffer size (0 to go with system default)",
|
||||
.overriddenp = &conf.buffer_size_out_overridden
|
||||
.overriddenp = &glob_conf.buffer_size_out_overridden
|
||||
},
|
||||
{
|
||||
.name = "ADC_SIZE_IN_USEC",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.size_in_usec_in,
|
||||
.valp = &glob_conf.size_in_usec_in,
|
||||
.descr =
|
||||
"ADC period/buffer size in microseconds (otherwise in frames)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_PERIOD_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.period_size_in,
|
||||
.valp = &glob_conf.period_size_in,
|
||||
.descr = "ADC period size (0 to go with system default)",
|
||||
.overriddenp = &conf.period_size_in_overridden
|
||||
.overriddenp = &glob_conf.period_size_in_overridden
|
||||
},
|
||||
{
|
||||
.name = "ADC_BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.buffer_size_in,
|
||||
.valp = &glob_conf.buffer_size_in,
|
||||
.descr = "ADC buffer size (0 to go with system default)",
|
||||
.overriddenp = &conf.buffer_size_in_overridden
|
||||
.overriddenp = &glob_conf.buffer_size_in_overridden
|
||||
},
|
||||
{
|
||||
.name = "THRESHOLD",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.threshold,
|
||||
.valp = &glob_conf.threshold,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.pcm_name_out,
|
||||
.valp = &glob_conf.pcm_name_out,
|
||||
.descr = "DAC device name (for instance dmix)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.pcm_name_in,
|
||||
.valp = &glob_conf.pcm_name_in,
|
||||
.descr = "ADC device name"
|
||||
},
|
||||
{
|
||||
.name = "VERBOSE",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.verbose,
|
||||
.descr = "Behave in a more verbose way"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
|
||||
+3
-32
@@ -30,7 +30,6 @@
|
||||
#define AUDIO_CAP "audio"
|
||||
#include "audio_int.h"
|
||||
|
||||
/* #define DEBUG_PLIVE */
|
||||
/* #define DEBUG_LIVE */
|
||||
/* #define DEBUG_OUT */
|
||||
/* #define DEBUG_CAPTURE */
|
||||
@@ -66,8 +65,6 @@ static struct {
|
||||
int hertz;
|
||||
int64_t ticks;
|
||||
} period;
|
||||
int plive;
|
||||
int log_to_monitor;
|
||||
int try_poll_in;
|
||||
int try_poll_out;
|
||||
} conf = {
|
||||
@@ -96,8 +93,6 @@ static struct {
|
||||
},
|
||||
|
||||
.period = { .hertz = 100 },
|
||||
.plive = 0,
|
||||
.log_to_monitor = 0,
|
||||
.try_poll_in = 1,
|
||||
.try_poll_out = 1,
|
||||
};
|
||||
@@ -331,20 +326,11 @@ static const char *audio_get_conf_str (const char *key,
|
||||
|
||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
|
||||
{
|
||||
if (conf.log_to_monitor) {
|
||||
if (cap) {
|
||||
monitor_printf(default_mon, "%s: ", cap);
|
||||
}
|
||||
|
||||
monitor_vprintf(default_mon, fmt, ap);
|
||||
if (cap) {
|
||||
fprintf(stderr, "%s: ", cap);
|
||||
}
|
||||
else {
|
||||
if (cap) {
|
||||
fprintf (stderr, "%s: ", cap);
|
||||
}
|
||||
|
||||
vfprintf (stderr, fmt, ap);
|
||||
}
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
|
||||
void AUD_log (const char *cap, const char *fmt, ...)
|
||||
@@ -1454,9 +1440,6 @@ static void audio_run_out (AudioState *s)
|
||||
while (sw) {
|
||||
sw1 = sw->entries.le_next;
|
||||
if (!sw->active && !sw->callback.fn) {
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Finishing with old voice\n");
|
||||
#endif
|
||||
audio_close_out (sw);
|
||||
}
|
||||
sw = sw1;
|
||||
@@ -1648,18 +1631,6 @@ static struct audio_option audio_options[] = {
|
||||
.valp = &conf.period.hertz,
|
||||
.descr = "Timer period in HZ (0 - use lowest possible)"
|
||||
},
|
||||
{
|
||||
.name = "PLIVE",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.plive,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "LOG_TO_MONITOR",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.log_to_monitor,
|
||||
.descr = "Print logging messages to monitor instead of stderr"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
|
||||
+2
-5
@@ -156,13 +156,13 @@ struct audio_driver {
|
||||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, struct audsettings *as);
|
||||
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
int (*run_out) (HWVoiceOut *hw, int live);
|
||||
int (*write) (SWVoiceOut *sw, void *buf, int size);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
|
||||
int (*init_in) (HWVoiceIn *hw, struct audsettings *as);
|
||||
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
int (*run_in) (HWVoiceIn *hw);
|
||||
int (*read) (SWVoiceIn *sw, void *buf, int size);
|
||||
@@ -206,14 +206,11 @@ extern struct audio_driver no_audio_driver;
|
||||
extern struct audio_driver oss_audio_driver;
|
||||
extern struct audio_driver sdl_audio_driver;
|
||||
extern struct audio_driver wav_audio_driver;
|
||||
extern struct audio_driver fmod_audio_driver;
|
||||
extern struct audio_driver alsa_audio_driver;
|
||||
extern struct audio_driver coreaudio_audio_driver;
|
||||
extern struct audio_driver dsound_audio_driver;
|
||||
extern struct audio_driver esd_audio_driver;
|
||||
extern struct audio_driver pa_audio_driver;
|
||||
extern struct audio_driver spice_audio_driver;
|
||||
extern struct audio_driver winwave_audio_driver;
|
||||
extern const struct mixeng_volume nominal_volume;
|
||||
|
||||
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
|
||||
|
||||
+1
-42
@@ -262,7 +262,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
#ifdef DAC
|
||||
QLIST_INIT (&hw->cap_head);
|
||||
#endif
|
||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
|
||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) {
|
||||
goto err0;
|
||||
}
|
||||
|
||||
@@ -398,10 +398,6 @@ SW *glue (AUD_open_, TYPE) (
|
||||
)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
#ifdef DAC
|
||||
int live = 0;
|
||||
SW *old_sw = NULL;
|
||||
#endif
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||
@@ -426,29 +422,6 @@ SW *glue (AUD_open_, TYPE) (
|
||||
return sw;
|
||||
}
|
||||
|
||||
#ifdef DAC
|
||||
if (conf.plive && sw && (!sw->active && !sw->empty)) {
|
||||
live = sw->total_hw_samples_mixed;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
|
||||
dolog ("Old %s freq %d, bits %d, channels %d\n",
|
||||
SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
|
||||
dolog ("New %s freq %d, bits %d, channels %d\n",
|
||||
name,
|
||||
as->freq,
|
||||
(as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8,
|
||||
as->nchannels);
|
||||
#endif
|
||||
|
||||
if (live) {
|
||||
old_sw = sw;
|
||||
old_sw->callback.fn = NULL;
|
||||
sw = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
sw = NULL;
|
||||
@@ -481,20 +454,6 @@ SW *glue (AUD_open_, TYPE) (
|
||||
sw->callback.fn = callback_fn;
|
||||
sw->callback.opaque = callback_opaque;
|
||||
|
||||
#ifdef DAC
|
||||
if (live) {
|
||||
int mixed =
|
||||
(live << old_sw->info.shift)
|
||||
* old_sw->info.bytes_per_second
|
||||
/ sw->info.bytes_per_second;
|
||||
|
||||
#ifdef DEBUG_PLIVE
|
||||
dolog ("Silence will be mixed %d\n", mixed);
|
||||
#endif
|
||||
sw->total_hw_samples_mixed += mixed;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
dolog ("%s\n", name);
|
||||
audio_pcm_print_info ("hw", &sw->hw->info);
|
||||
|
||||
+26
-20
@@ -32,20 +32,16 @@
|
||||
#define AUDIO_CAP "coreaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
struct {
|
||||
static int isAtexit;
|
||||
|
||||
typedef struct {
|
||||
int buffer_frames;
|
||||
int nbuffers;
|
||||
int isAtexit;
|
||||
} conf = {
|
||||
.buffer_frames = 512,
|
||||
.nbuffers = 4,
|
||||
.isAtexit = 0
|
||||
};
|
||||
} CoreaudioConf;
|
||||
|
||||
typedef struct coreaudioVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
pthread_mutex_t mutex;
|
||||
int isAtexit;
|
||||
AudioDeviceID outputDeviceID;
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
@@ -161,7 +157,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
|
||||
|
||||
static void coreaudio_atexit (void)
|
||||
{
|
||||
conf.isAtexit = 1;
|
||||
isAtexit = 1;
|
||||
}
|
||||
|
||||
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
@@ -287,7 +283,8 @@ static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
@@ -295,6 +292,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
int err;
|
||||
const char *typ = "playback";
|
||||
AudioValueRange frameRange;
|
||||
CoreaudioConf *conf = drv_opaque;
|
||||
|
||||
/* create mutex */
|
||||
err = pthread_mutex_init(&core->mutex, NULL);
|
||||
@@ -336,16 +334,16 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frameRange.mMinimum > conf.buffer_frames) {
|
||||
if (frameRange.mMinimum > conf->buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
||||
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
||||
}
|
||||
else if (frameRange.mMaximum < conf.buffer_frames) {
|
||||
else if (frameRange.mMaximum < conf->buffer_frames) {
|
||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
||||
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
||||
}
|
||||
else {
|
||||
core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
|
||||
core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
|
||||
}
|
||||
|
||||
/* set Buffer Frame Size */
|
||||
@@ -379,7 +377,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
"Could not get device buffer frame size\n");
|
||||
return -1;
|
||||
}
|
||||
hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
@@ -443,7 +441,7 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
int err;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (!conf.isAtexit) {
|
||||
if (!isAtexit) {
|
||||
/* stop playback */
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
@@ -486,7 +484,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
|
||||
case VOICE_DISABLE:
|
||||
/* stop playback */
|
||||
if (!conf.isAtexit) {
|
||||
if (!isAtexit) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
@@ -499,28 +497,36 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CoreaudioConf glob_conf = {
|
||||
.buffer_frames = 512,
|
||||
.nbuffers = 4,
|
||||
};
|
||||
|
||||
static void *coreaudio_audio_init (void)
|
||||
{
|
||||
CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
|
||||
*conf = glob_conf;
|
||||
|
||||
atexit(coreaudio_atexit);
|
||||
return &coreaudio_audio_init;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void coreaudio_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option coreaudio_options[] = {
|
||||
{
|
||||
.name = "BUFFER_SIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.buffer_frames,
|
||||
.valp = &glob_conf.buffer_frames,
|
||||
.descr = "Size of the buffer in frames"
|
||||
},
|
||||
{
|
||||
.name = "BUFFER_COUNT",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.nbuffers,
|
||||
.valp = &glob_conf.nbuffers,
|
||||
.descr = "Number of buffers"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
|
||||
+18
-33
@@ -67,11 +67,11 @@ static int glue (dsound_lock_, TYPE) (
|
||||
LPVOID *p2p,
|
||||
DWORD *blen1p,
|
||||
DWORD *blen2p,
|
||||
int entire
|
||||
int entire,
|
||||
dsound *s
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
int i;
|
||||
LPVOID p1 = NULL, p2 = NULL;
|
||||
DWORD blen1 = 0, blen2 = 0;
|
||||
DWORD flag;
|
||||
@@ -81,37 +81,18 @@ static int glue (dsound_lock_, TYPE) (
|
||||
#else
|
||||
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
|
||||
#endif
|
||||
for (i = 0; i < conf.lock_retries; ++i) {
|
||||
hr = glue (IFACE, _Lock) (
|
||||
buf,
|
||||
pos,
|
||||
len,
|
||||
&p1,
|
||||
&blen1,
|
||||
&p2,
|
||||
&blen2,
|
||||
flag
|
||||
);
|
||||
hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
|
||||
|
||||
if (FAILED (hr)) {
|
||||
if (FAILED (hr)) {
|
||||
#ifndef DSBTYPE_IN
|
||||
if (hr == DSERR_BUFFERLOST) {
|
||||
if (glue (dsound_restore_, TYPE) (buf)) {
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
continue;
|
||||
if (hr == DSERR_BUFFERLOST) {
|
||||
if (glue (dsound_restore_, TYPE) (buf, s)) {
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
}
|
||||
#endif
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == conf.lock_retries) {
|
||||
dolog ("%d attempts to lock " NAME " failed\n", i);
|
||||
#endif
|
||||
dsound_logerr (hr, "Could not lock " NAME "\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -174,16 +155,19 @@ static void dsound_fini_out (HWVoiceOut *hw)
|
||||
}
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
#else
|
||||
static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
#endif
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
dsound *s = &glob_dsound;
|
||||
dsound *s = drv_opaque;
|
||||
WAVEFORMATEX wfx;
|
||||
struct audsettings obt_as;
|
||||
DSoundConf *conf = &s->conf;
|
||||
#ifdef DSBTYPE_IN
|
||||
const char *typ = "ADC";
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
@@ -210,7 +194,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
bd.dwSize = sizeof (bd);
|
||||
bd.lpwfxFormat = &wfx;
|
||||
#ifdef DSBTYPE_IN
|
||||
bd.dwBufferBytes = conf.bufsize_in;
|
||||
bd.dwBufferBytes = conf->bufsize_in;
|
||||
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
||||
s->dsound_capture,
|
||||
&bd,
|
||||
@@ -219,7 +203,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
);
|
||||
#else
|
||||
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||
bd.dwBufferBytes = conf.bufsize_out;
|
||||
bd.dwBufferBytes = conf->bufsize_out;
|
||||
hr = IDirectSound_CreateSoundBuffer (
|
||||
s->dsound,
|
||||
&bd,
|
||||
@@ -269,6 +253,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
);
|
||||
}
|
||||
hw->samples = bc.dwBufferBytes >> hw->info.shift;
|
||||
ds->s = s;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("caps %ld, desc %ld\n",
|
||||
|
||||
+56
-182
@@ -41,42 +41,25 @@
|
||||
|
||||
/* #define DEBUG_DSOUND */
|
||||
|
||||
static struct {
|
||||
int lock_retries;
|
||||
int restore_retries;
|
||||
int getstatus_retries;
|
||||
int set_primary;
|
||||
typedef struct {
|
||||
int bufsize_in;
|
||||
int bufsize_out;
|
||||
struct audsettings settings;
|
||||
int latency_millis;
|
||||
} conf = {
|
||||
.lock_retries = 1,
|
||||
.restore_retries = 1,
|
||||
.getstatus_retries = 1,
|
||||
.set_primary = 0,
|
||||
.bufsize_in = 16384,
|
||||
.bufsize_out = 16384,
|
||||
.settings.freq = 44100,
|
||||
.settings.nchannels = 2,
|
||||
.settings.fmt = AUD_FMT_S16,
|
||||
.latency_millis = 10
|
||||
};
|
||||
} DSoundConf;
|
||||
|
||||
typedef struct {
|
||||
LPDIRECTSOUND dsound;
|
||||
LPDIRECTSOUNDCAPTURE dsound_capture;
|
||||
LPDIRECTSOUNDBUFFER dsound_primary_buffer;
|
||||
struct audsettings settings;
|
||||
DSoundConf conf;
|
||||
} dsound;
|
||||
|
||||
static dsound glob_dsound;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
LPDIRECTSOUNDBUFFER dsound_buffer;
|
||||
DWORD old_pos;
|
||||
int first_time;
|
||||
dsound *s;
|
||||
#ifdef DEBUG_DSOUND
|
||||
DWORD old_ppos;
|
||||
DWORD played;
|
||||
@@ -88,6 +71,7 @@ typedef struct {
|
||||
HWVoiceIn hw;
|
||||
int first_time;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
|
||||
dsound *s;
|
||||
} DSoundVoiceIn;
|
||||
|
||||
static void dsound_log_hresult (HRESULT hr)
|
||||
@@ -281,29 +265,17 @@ static void print_wave_format (WAVEFORMATEX *wfx)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
|
||||
static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
|
||||
{
|
||||
HRESULT hr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < conf.restore_retries; ++i) {
|
||||
hr = IDirectSoundBuffer_Restore (dsb);
|
||||
hr = IDirectSoundBuffer_Restore (dsb);
|
||||
|
||||
switch (hr) {
|
||||
case DS_OK:
|
||||
return 0;
|
||||
|
||||
case DSERR_BUFFERLOST:
|
||||
continue;
|
||||
|
||||
default:
|
||||
dsound_logerr (hr, "Could not restore playback buffer\n");
|
||||
return -1;
|
||||
}
|
||||
if (hr != DS_OK) {
|
||||
dsound_logerr (hr, "Could not restore playback buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dolog ("%d attempts to restore playback buffer failed\n", i);
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "dsound_template.h"
|
||||
@@ -311,25 +283,20 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
|
||||
#include "dsound_template.h"
|
||||
#undef DSBTYPE_IN
|
||||
|
||||
static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
|
||||
static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
|
||||
dsound *s)
|
||||
{
|
||||
HRESULT hr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < conf.getstatus_retries; ++i) {
|
||||
hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get playback buffer status\n");
|
||||
return -1;
|
||||
}
|
||||
hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get playback buffer status\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*statusp & DSERR_BUFFERLOST) {
|
||||
if (dsound_restore_out (dsb)) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
if (*statusp & DSERR_BUFFERLOST) {
|
||||
dsound_restore_out(dsb, s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -376,7 +343,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
|
||||
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||
dsound *s)
|
||||
{
|
||||
int err;
|
||||
LPVOID p1, p2;
|
||||
@@ -389,7 +357,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
|
||||
hw->samples << hw->info.shift,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2,
|
||||
1
|
||||
1,
|
||||
s
|
||||
);
|
||||
if (err) {
|
||||
return;
|
||||
@@ -415,25 +384,9 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
|
||||
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
|
||||
}
|
||||
|
||||
static void dsound_close (dsound *s)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (s->dsound_primary_buffer) {
|
||||
hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not release primary buffer\n");
|
||||
}
|
||||
s->dsound_primary_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dsound_open (dsound *s)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
WAVEFORMATEX wfx;
|
||||
DSBUFFERDESC dsbd;
|
||||
HWND hwnd;
|
||||
|
||||
hwnd = GetForegroundWindow ();
|
||||
@@ -449,63 +402,7 @@ static int dsound_open (dsound *s)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!conf.set_primary) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = waveformat_from_audio_settings (&wfx, &conf.settings);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset (&dsbd, 0, sizeof (dsbd));
|
||||
dsbd.dwSize = sizeof (dsbd);
|
||||
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
dsbd.dwBufferBytes = 0;
|
||||
dsbd.lpwfxFormat = NULL;
|
||||
|
||||
hr = IDirectSound_CreateSoundBuffer (
|
||||
s->dsound,
|
||||
&dsbd,
|
||||
&s->dsound_primary_buffer,
|
||||
NULL
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not create primary playback buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not set primary playback buffer format\n");
|
||||
}
|
||||
|
||||
hr = IDirectSoundBuffer_GetFormat (
|
||||
s->dsound_primary_buffer,
|
||||
&wfx,
|
||||
sizeof (wfx),
|
||||
NULL
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get primary playback buffer format\n");
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("Primary\n");
|
||||
print_wave_format (&wfx);
|
||||
#endif
|
||||
|
||||
err = waveformat_to_audio_settings (&wfx, &s->settings);
|
||||
if (err) {
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail0:
|
||||
dsound_close (s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
@@ -514,6 +411,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
DWORD status;
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
dsound *s = ds->s;
|
||||
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to control voice without a buffer\n");
|
||||
@@ -522,7 +420,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (dsound_get_status_out (dsb, &status)) {
|
||||
if (dsound_get_status_out (dsb, &status, s)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -531,7 +429,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dsound_clear_sample (hw, dsb);
|
||||
dsound_clear_sample (hw, dsb, s);
|
||||
|
||||
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
|
||||
if (FAILED (hr)) {
|
||||
@@ -541,7 +439,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (dsound_get_status_out (dsb, &status)) {
|
||||
if (dsound_get_status_out (dsb, &status, s)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -578,6 +476,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||
DWORD wpos, ppos, old_pos;
|
||||
LPVOID p1, p2;
|
||||
int bufsize;
|
||||
dsound *s = ds->s;
|
||||
DSoundConf *conf = &s->conf;
|
||||
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to run empty with playback buffer\n");
|
||||
@@ -600,14 +500,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||
len = live << hwshift;
|
||||
|
||||
if (ds->first_time) {
|
||||
if (conf.latency_millis) {
|
||||
if (conf->latency_millis) {
|
||||
DWORD cur_blat;
|
||||
|
||||
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
||||
ds->first_time = 0;
|
||||
old_pos = wpos;
|
||||
old_pos +=
|
||||
millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
|
||||
millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
|
||||
old_pos %= bufsize;
|
||||
old_pos &= ~hw->info.align;
|
||||
}
|
||||
@@ -663,7 +563,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||
len,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2,
|
||||
0
|
||||
0,
|
||||
s
|
||||
);
|
||||
if (err) {
|
||||
return 0;
|
||||
@@ -766,6 +667,7 @@ static int dsound_run_in (HWVoiceIn *hw)
|
||||
DWORD cpos, rpos;
|
||||
LPVOID p1, p2;
|
||||
int hwshift;
|
||||
dsound *s = ds->s;
|
||||
|
||||
if (!dscb) {
|
||||
dolog ("Attempt to run without capture buffer\n");
|
||||
@@ -820,7 +722,8 @@ static int dsound_run_in (HWVoiceIn *hw)
|
||||
&p2,
|
||||
&blen1,
|
||||
&blen2,
|
||||
0
|
||||
0,
|
||||
s
|
||||
);
|
||||
if (err) {
|
||||
return 0;
|
||||
@@ -843,12 +746,19 @@ static int dsound_run_in (HWVoiceIn *hw)
|
||||
return decr;
|
||||
}
|
||||
|
||||
static DSoundConf glob_conf = {
|
||||
.bufsize_in = 16384,
|
||||
.bufsize_out = 16384,
|
||||
.latency_millis = 10
|
||||
};
|
||||
|
||||
static void dsound_audio_fini (void *opaque)
|
||||
{
|
||||
HRESULT hr;
|
||||
dsound *s = opaque;
|
||||
|
||||
if (!s->dsound) {
|
||||
g_free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -859,6 +769,7 @@ static void dsound_audio_fini (void *opaque)
|
||||
s->dsound = NULL;
|
||||
|
||||
if (!s->dsound_capture) {
|
||||
g_free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -867,17 +778,21 @@ static void dsound_audio_fini (void *opaque)
|
||||
dsound_logerr (hr, "Could not release DirectSoundCapture\n");
|
||||
}
|
||||
s->dsound_capture = NULL;
|
||||
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void *dsound_audio_init (void)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
dsound *s = &glob_dsound;
|
||||
dsound *s = g_malloc0(sizeof(dsound));
|
||||
|
||||
s->conf = glob_conf;
|
||||
hr = CoInitialize (NULL);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not initialize COM\n");
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -890,6 +805,7 @@ static void *dsound_audio_init (void)
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not create DirectSound instance\n");
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -901,7 +817,7 @@ static void *dsound_audio_init (void)
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not release DirectSound\n");
|
||||
}
|
||||
s->dsound = NULL;
|
||||
g_free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -938,64 +854,22 @@ static void *dsound_audio_init (void)
|
||||
}
|
||||
|
||||
static struct audio_option dsound_options[] = {
|
||||
{
|
||||
.name = "LOCK_RETRIES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.lock_retries,
|
||||
.descr = "Number of times to attempt locking the buffer"
|
||||
},
|
||||
{
|
||||
.name = "RESTOURE_RETRIES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.restore_retries,
|
||||
.descr = "Number of times to attempt restoring the buffer"
|
||||
},
|
||||
{
|
||||
.name = "GETSTATUS_RETRIES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.getstatus_retries,
|
||||
.descr = "Number of times to attempt getting status of the buffer"
|
||||
},
|
||||
{
|
||||
.name = "SET_PRIMARY",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.set_primary,
|
||||
.descr = "Set the parameters of primary buffer"
|
||||
},
|
||||
{
|
||||
.name = "LATENCY_MILLIS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.latency_millis,
|
||||
.valp = &glob_conf.latency_millis,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "PRIMARY_FREQ",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.settings.freq,
|
||||
.descr = "Primary buffer frequency"
|
||||
},
|
||||
{
|
||||
.name = "PRIMARY_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.settings.nchannels,
|
||||
.descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "PRIMARY_FMT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &conf.settings.fmt,
|
||||
.descr = "Primary buffer format"
|
||||
},
|
||||
{
|
||||
.name = "BUFSIZE_OUT",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.bufsize_out,
|
||||
.valp = &glob_conf.bufsize_out,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{
|
||||
.name = "BUFSIZE_IN",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.bufsize_in,
|
||||
.valp = &glob_conf.bufsize_in,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
|
||||
@@ -1,557 +0,0 @@
|
||||
/*
|
||||
* QEMU ESD audio driver
|
||||
*
|
||||
* Copyright (c) 2006 Frederick Reeve (brushed up by malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <esd.h>
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "esd"
|
||||
#include "audio_int.h"
|
||||
#include "audio_pt_int.h"
|
||||
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
int done;
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
struct audio_pt pt;
|
||||
} ESDVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceIn hw;
|
||||
int done;
|
||||
int dead;
|
||||
int incr;
|
||||
int wpos;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
struct audio_pt pt;
|
||||
} ESDVoiceIn;
|
||||
|
||||
static struct {
|
||||
int samples;
|
||||
int divisor;
|
||||
char *dac_host;
|
||||
char *adc_host;
|
||||
} conf = {
|
||||
.samples = 1024,
|
||||
.divisor = 2,
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
/* playback */
|
||||
static void *qesd_thread_out (void *arg)
|
||||
{
|
||||
ESDVoiceOut *esd = arg;
|
||||
HWVoiceOut *hw = &esd->hw;
|
||||
int threshold;
|
||||
|
||||
threshold = conf.divisor ? hw->samples / conf.divisor : 0;
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int decr, to_mix, rpos;
|
||||
|
||||
for (;;) {
|
||||
if (esd->done) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (esd->live > threshold) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
decr = to_mix = esd->live;
|
||||
rpos = hw->rpos;
|
||||
|
||||
if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (to_mix) {
|
||||
ssize_t written;
|
||||
int chunk = audio_MIN (to_mix, hw->samples - rpos);
|
||||
struct st_sample *src = hw->mix_buf + rpos;
|
||||
|
||||
hw->clip (esd->pcm_buf, src, chunk);
|
||||
|
||||
again:
|
||||
written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
|
||||
if (written == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
goto again;
|
||||
}
|
||||
qesd_logerr (errno, "write failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (written != chunk << hw->info.shift) {
|
||||
int wsamples = written >> hw->info.shift;
|
||||
int wbytes = wsamples << hw->info.shift;
|
||||
if (wbytes != written) {
|
||||
dolog ("warning: Misaligned write %d (requested %zd), "
|
||||
"alignment %d\n",
|
||||
wbytes, written, hw->info.align + 1);
|
||||
}
|
||||
to_mix -= wsamples;
|
||||
rpos = (rpos + wsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
|
||||
rpos = (rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
}
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esd->rpos = rpos;
|
||||
esd->live -= decr;
|
||||
esd->decr += decr;
|
||||
}
|
||||
|
||||
exit:
|
||||
audio_pt_unlock (&esd->pt, AUDIO_FUNC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qesd_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
int decr;
|
||||
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = audio_MIN (live, esd->decr);
|
||||
esd->decr -= decr;
|
||||
esd->live = live - decr;
|
||||
hw->rpos = esd->rpos;
|
||||
if (esd->live > 0) {
|
||||
audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
|
||||
}
|
||||
else {
|
||||
audio_pt_unlock (&esd->pt, AUDIO_FUNC);
|
||||
}
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int qesd_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
{
|
||||
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
|
||||
struct audsettings obt_as = *as;
|
||||
int esdfmt = ESD_STREAM | ESD_PLAY;
|
||||
|
||||
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
|
||||
switch (as->fmt) {
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
esdfmt |= ESD_BITS8;
|
||||
obt_as.fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
dolog ("Will use 16 instead of 32 bit samples\n");
|
||||
/* fall through */
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
deffmt:
|
||||
esdfmt |= ESD_BITS16;
|
||||
obt_as.fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
|
||||
goto deffmt;
|
||||
|
||||
}
|
||||
obt_as.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
|
||||
hw->samples = conf.samples;
|
||||
esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!esd->pcm_buf) {
|
||||
dolog ("Could not allocate buffer (%d bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
return -1;
|
||||
}
|
||||
|
||||
esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
|
||||
if (esd->fd < 0) {
|
||||
qesd_logerr (errno, "esd_play_stream failed\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
if (close (esd->fd)) {
|
||||
qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
|
||||
AUDIO_FUNC, esd->fd);
|
||||
}
|
||||
esd->fd = -1;
|
||||
|
||||
fail1:
|
||||
g_free (esd->pcm_buf);
|
||||
esd->pcm_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void qesd_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
void *ret;
|
||||
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
|
||||
|
||||
audio_pt_lock (&esd->pt, AUDIO_FUNC);
|
||||
esd->done = 1;
|
||||
audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
|
||||
audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
|
||||
|
||||
if (esd->fd >= 0) {
|
||||
if (close (esd->fd)) {
|
||||
qesd_logerr (errno, "failed to close esd socket\n");
|
||||
}
|
||||
esd->fd = -1;
|
||||
}
|
||||
|
||||
audio_pt_fini (&esd->pt, AUDIO_FUNC);
|
||||
|
||||
g_free (esd->pcm_buf);
|
||||
esd->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* capture */
|
||||
static void *qesd_thread_in (void *arg)
|
||||
{
|
||||
ESDVoiceIn *esd = arg;
|
||||
HWVoiceIn *hw = &esd->hw;
|
||||
int threshold;
|
||||
|
||||
threshold = conf.divisor ? hw->samples / conf.divisor : 0;
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int incr, to_grab, wpos;
|
||||
|
||||
for (;;) {
|
||||
if (esd->done) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (esd->dead > threshold) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
incr = to_grab = esd->dead;
|
||||
wpos = hw->wpos;
|
||||
|
||||
if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (to_grab) {
|
||||
ssize_t nread;
|
||||
int chunk = audio_MIN (to_grab, hw->samples - wpos);
|
||||
void *buf = advance (esd->pcm_buf, wpos);
|
||||
|
||||
again:
|
||||
nread = read (esd->fd, buf, chunk << hw->info.shift);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
goto again;
|
||||
}
|
||||
qesd_logerr (errno, "read failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nread != chunk << hw->info.shift) {
|
||||
int rsamples = nread >> hw->info.shift;
|
||||
int rbytes = rsamples << hw->info.shift;
|
||||
if (rbytes != nread) {
|
||||
dolog ("warning: Misaligned write %d (requested %zd), "
|
||||
"alignment %d\n",
|
||||
rbytes, nread, hw->info.align + 1);
|
||||
}
|
||||
to_grab -= rsamples;
|
||||
wpos = (wpos + rsamples) % hw->samples;
|
||||
break;
|
||||
}
|
||||
|
||||
hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift);
|
||||
wpos = (wpos + chunk) % hw->samples;
|
||||
to_grab -= chunk;
|
||||
}
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esd->wpos = wpos;
|
||||
esd->dead -= incr;
|
||||
esd->incr += incr;
|
||||
}
|
||||
|
||||
exit:
|
||||
audio_pt_unlock (&esd->pt, AUDIO_FUNC);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qesd_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
int live, incr, dead;
|
||||
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
|
||||
|
||||
if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
incr = audio_MIN (dead, esd->incr);
|
||||
esd->incr -= incr;
|
||||
esd->dead = dead - incr;
|
||||
hw->wpos = esd->wpos;
|
||||
if (esd->dead > 0) {
|
||||
audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
|
||||
}
|
||||
else {
|
||||
audio_pt_unlock (&esd->pt, AUDIO_FUNC);
|
||||
}
|
||||
return incr;
|
||||
}
|
||||
|
||||
static int qesd_read (SWVoiceIn *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, len);
|
||||
}
|
||||
|
||||
static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
{
|
||||
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
|
||||
struct audsettings obt_as = *as;
|
||||
int esdfmt = ESD_STREAM | ESD_RECORD;
|
||||
|
||||
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
|
||||
switch (as->fmt) {
|
||||
case AUD_FMT_S8:
|
||||
case AUD_FMT_U8:
|
||||
esdfmt |= ESD_BITS8;
|
||||
obt_as.fmt = AUD_FMT_U8;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
case AUD_FMT_U16:
|
||||
esdfmt |= ESD_BITS16;
|
||||
obt_as.fmt = AUD_FMT_S16;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S32:
|
||||
case AUD_FMT_U32:
|
||||
dolog ("Will use 16 instead of 32 bit samples\n");
|
||||
esdfmt |= ESD_BITS16;
|
||||
obt_as.fmt = AUD_FMT_S16;
|
||||
break;
|
||||
}
|
||||
obt_as.endianness = AUDIO_HOST_ENDIANNESS;
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
|
||||
hw->samples = conf.samples;
|
||||
esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
if (!esd->pcm_buf) {
|
||||
dolog ("Could not allocate buffer (%d bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
return -1;
|
||||
}
|
||||
|
||||
esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
|
||||
if (esd->fd < 0) {
|
||||
qesd_logerr (errno, "esd_record_stream failed\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
if (close (esd->fd)) {
|
||||
qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
|
||||
AUDIO_FUNC, esd->fd);
|
||||
}
|
||||
esd->fd = -1;
|
||||
|
||||
fail1:
|
||||
g_free (esd->pcm_buf);
|
||||
esd->pcm_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void qesd_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
void *ret;
|
||||
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
|
||||
|
||||
audio_pt_lock (&esd->pt, AUDIO_FUNC);
|
||||
esd->done = 1;
|
||||
audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
|
||||
audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
|
||||
|
||||
if (esd->fd >= 0) {
|
||||
if (close (esd->fd)) {
|
||||
qesd_logerr (errno, "failed to close esd socket\n");
|
||||
}
|
||||
esd->fd = -1;
|
||||
}
|
||||
|
||||
audio_pt_fini (&esd->pt, AUDIO_FUNC);
|
||||
|
||||
g_free (esd->pcm_buf);
|
||||
esd->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* common */
|
||||
static void *qesd_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void qesd_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
ldebug ("esd_fini");
|
||||
}
|
||||
|
||||
struct audio_option qesd_options[] = {
|
||||
{
|
||||
.name = "SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.samples,
|
||||
.descr = "buffer size in samples"
|
||||
},
|
||||
{
|
||||
.name = "DIVISOR",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.divisor,
|
||||
.descr = "threshold divisor"
|
||||
},
|
||||
{
|
||||
.name = "DAC_HOST",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.dac_host,
|
||||
.descr = "playback host"
|
||||
},
|
||||
{
|
||||
.name = "ADC_HOST",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.adc_host,
|
||||
.descr = "capture host"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops qesd_pcm_ops = {
|
||||
.init_out = qesd_init_out,
|
||||
.fini_out = qesd_fini_out,
|
||||
.run_out = qesd_run_out,
|
||||
.write = qesd_write,
|
||||
.ctl_out = qesd_ctl_out,
|
||||
|
||||
.init_in = qesd_init_in,
|
||||
.fini_in = qesd_fini_in,
|
||||
.run_in = qesd_run_in,
|
||||
.read = qesd_read,
|
||||
.ctl_in = qesd_ctl_in,
|
||||
};
|
||||
|
||||
struct audio_driver esd_audio_driver = {
|
||||
.name = "esd",
|
||||
.descr = "http://en.wikipedia.org/wiki/Esound",
|
||||
.options = qesd_options,
|
||||
.init = qesd_audio_init,
|
||||
.fini = qesd_audio_fini,
|
||||
.pcm_ops = &qesd_pcm_ops,
|
||||
.can_be_default = 0,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof (ESDVoiceOut),
|
||||
.voice_size_in = sizeof (ESDVoiceIn)
|
||||
};
|
||||
@@ -1,685 +0,0 @@
|
||||
/*
|
||||
* QEMU FMOD audio driver
|
||||
*
|
||||
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <fmod.h>
|
||||
#include <fmod_errors.h>
|
||||
#include "qemu-common.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "fmod"
|
||||
#include "audio_int.h"
|
||||
|
||||
typedef struct FMODVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
unsigned int old_pos;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
int channel;
|
||||
} FMODVoiceOut;
|
||||
|
||||
typedef struct FMODVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
FSOUND_SAMPLE *fmod_sample;
|
||||
} FMODVoiceIn;
|
||||
|
||||
static struct {
|
||||
const char *drvname;
|
||||
int nb_samples;
|
||||
int freq;
|
||||
int nb_channels;
|
||||
int bufsize;
|
||||
int broken_adc;
|
||||
} conf = {
|
||||
.nb_samples = 2048 * 2,
|
||||
.freq = 44100,
|
||||
.nb_channels = 2,
|
||||
};
|
||||
|
||||
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n",
|
||||
FMOD_ErrorString (FSOUND_GetError ()));
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
|
||||
const char *typ,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (AUDIO_CAP, "Reason: %s\n",
|
||||
FMOD_ErrorString (FSOUND_GetError ()));
|
||||
}
|
||||
|
||||
static int fmod_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static void fmod_clear_sample (FMODVoiceOut *fmd)
|
||||
{
|
||||
HWVoiceOut *hw = &fmd->hw;
|
||||
int status;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
fmd->fmod_sample,
|
||||
0,
|
||||
hw->samples << hw->info.shift,
|
||||
&p1,
|
||||
&p2,
|
||||
&len1,
|
||||
&len2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
|
||||
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
||||
len1, len2, hw->info.align + 1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((len1 + len2) - (hw->samples << hw->info.shift)) {
|
||||
dolog ("Lock returned incomplete length %d, %d\n",
|
||||
len1 + len2, hw->samples << hw->info.shift);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
|
||||
|
||||
fail:
|
||||
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len;
|
||||
int src_len2 = 0;
|
||||
int pos = hw->rpos + dst_len;
|
||||
struct st_sample *src1 = hw->mix_buf + hw->rpos;
|
||||
struct st_sample *src2 = NULL;
|
||||
|
||||
if (pos > hw->samples) {
|
||||
src_len1 = hw->samples - hw->rpos;
|
||||
src2 = hw->mix_buf;
|
||||
src_len2 = dst_len - src_len1;
|
||||
pos = src_len2;
|
||||
}
|
||||
|
||||
if (src_len1) {
|
||||
hw->clip (dst, src1, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
dst = advance (dst, src_len1 << hw->info.shift);
|
||||
hw->clip (dst, src2, src_len2);
|
||||
}
|
||||
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
|
||||
unsigned int blen1, unsigned int blen2)
|
||||
{
|
||||
int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to unlock sample\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_lock_sample (
|
||||
FSOUND_SAMPLE *sample,
|
||||
struct audio_pcm_info *info,
|
||||
int pos,
|
||||
int len,
|
||||
void **p1,
|
||||
void **p2,
|
||||
unsigned int *blen1,
|
||||
unsigned int *blen2
|
||||
)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = FSOUND_Sample_Lock (
|
||||
sample,
|
||||
pos << info->shift,
|
||||
len << info->shift,
|
||||
p1,
|
||||
p2,
|
||||
blen1,
|
||||
blen2
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to lock sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((*blen1 & info->align) || (*blen2 & info->align)) {
|
||||
dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
||||
*blen1, *blen2, info->align + 1);
|
||||
|
||||
fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
|
||||
|
||||
*p1 = NULL - 1;
|
||||
*p2 = NULL - 1;
|
||||
*blen1 = ~0U;
|
||||
*blen2 = ~0U;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*p1 && *blen1) {
|
||||
dolog ("warning: !p1 && blen1=%d\n", *blen1);
|
||||
*blen1 = 0;
|
||||
}
|
||||
|
||||
if (!p2 && *blen2) {
|
||||
dolog ("warning: !p2 && blen2=%d\n", *blen2);
|
||||
*blen2 = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
int decr;
|
||||
void *p1 = 0, *p2 = 0;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1 = 0, len2 = 0;
|
||||
|
||||
if (!hw->pending_disable) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = live;
|
||||
|
||||
if (fmd->channel >= 0) {
|
||||
int len = decr;
|
||||
int old_pos = fmd->old_pos;
|
||||
int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
||||
|
||||
if (ppos == old_pos || !ppos) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
||||
len = ppos - old_pos;
|
||||
}
|
||||
else {
|
||||
if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
||||
len = hw->samples - old_pos + ppos;
|
||||
}
|
||||
}
|
||||
decr = len;
|
||||
|
||||
if (audio_bug (AUDIO_FUNC, decr < 0)) {
|
||||
dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
|
||||
decr, live, ppos, old_pos, len);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
||||
fmd->old_pos, decr,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hw->info.shift;
|
||||
len2 = blen2 >> hw->info.shift;
|
||||
ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && len1) {
|
||||
fmod_write_sample (hw, p1, len1);
|
||||
}
|
||||
|
||||
if (p2 && len2) {
|
||||
fmod_write_sample (hw, p2, len2);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
|
||||
fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
|
||||
{
|
||||
int mode = FSOUND_LOOP_NORMAL;
|
||||
|
||||
switch (fmt) {
|
||||
case AUD_FMT_S8:
|
||||
mode |= FSOUND_SIGNED | FSOUND_8BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_U8:
|
||||
mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_S16:
|
||||
mode |= FSOUND_SIGNED | FSOUND_16BITS;
|
||||
break;
|
||||
|
||||
case AUD_FMT_U16:
|
||||
mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
||||
#ifdef DEBUG_FMOD
|
||||
abort ();
|
||||
#endif
|
||||
mode |= FSOUND_8BITS;
|
||||
}
|
||||
mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void fmod_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
fmd->fmod_sample = 0;
|
||||
|
||||
if (fmd->channel >= 0) {
|
||||
FSOUND_StopSound (fmd->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
{
|
||||
int mode, channel;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
struct audsettings obt_as = *as;
|
||||
|
||||
mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
|
||||
if (channel < 0) {
|
||||
fmod_logerr2 ("DAC", "Failed to start playing sound\n");
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
return -1;
|
||||
}
|
||||
fmd->channel = channel;
|
||||
|
||||
/* FMOD always operates on little endian frames? */
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = conf.nb_samples;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
fmod_clear_sample (fmd);
|
||||
status = FSOUND_SetPaused (fmd->channel, 0);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_SetPaused (fmd->channel, 1);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
{
|
||||
int mode;
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
struct audsettings obt_as = *as;
|
||||
|
||||
if (conf.broken_adc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
|
||||
fmd->fmod_sample = FSOUND_Sample_Alloc (
|
||||
FSOUND_FREE, /* index */
|
||||
conf.nb_samples, /* length */
|
||||
mode, /* mode */
|
||||
as->freq, /* freq */
|
||||
255, /* volume */
|
||||
128, /* pan */
|
||||
255 /* priority */
|
||||
);
|
||||
|
||||
if (!fmd->fmod_sample) {
|
||||
fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FMOD always operates on little endian frames? */
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = conf.nb_samples;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
if (fmd->fmod_sample) {
|
||||
FSOUND_Record_Stop ();
|
||||
FSOUND_Sample_Free (fmd->fmod_sample);
|
||||
fmd->fmod_sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int fmod_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int live, dead, new_pos, len;
|
||||
unsigned int blen1 = 0, blen2 = 0;
|
||||
unsigned int len1, len2;
|
||||
unsigned int decr;
|
||||
void *p1, *p2;
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_pos = FSOUND_Record_GetPosition ();
|
||||
if (new_pos < 0) {
|
||||
fmod_logerr ("Could not get recording position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
len = audio_MIN (len, dead);
|
||||
|
||||
if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
||||
hw->wpos, len,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && blen1) {
|
||||
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
|
||||
}
|
||||
if (p2 && len2) {
|
||||
hw->conv (hw->conv_buf, p2, len2);
|
||||
}
|
||||
|
||||
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
|
||||
hw->wpos = (hw->wpos + decr) % hw->samples;
|
||||
return decr;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int type;
|
||||
} drvtab[] = {
|
||||
{ .name = "none", .type = FSOUND_OUTPUT_NOSOUND },
|
||||
#ifdef _WIN32
|
||||
{ .name = "winmm", .type = FSOUND_OUTPUT_WINMM },
|
||||
{ .name = "dsound", .type = FSOUND_OUTPUT_DSOUND },
|
||||
{ .name = "a3d", .type = FSOUND_OUTPUT_A3D },
|
||||
{ .name = "asio", .type = FSOUND_OUTPUT_ASIO },
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
{ .name = "oss", .type = FSOUND_OUTPUT_OSS },
|
||||
{ .name = "alsa", .type = FSOUND_OUTPUT_ALSA },
|
||||
{ .name = "esd", .type = FSOUND_OUTPUT_ESD },
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
{ .name = "mac", .type = FSOUND_OUTPUT_MAC },
|
||||
#endif
|
||||
#if 0
|
||||
{ .name = "xbox", .type = FSOUND_OUTPUT_XBOX },
|
||||
{ .name = "ps2", .type = FSOUND_OUTPUT_PS2 },
|
||||
{ .name = "gcube", .type = FSOUND_OUTPUT_GC },
|
||||
#endif
|
||||
{ .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
|
||||
};
|
||||
|
||||
static void *fmod_audio_init (void)
|
||||
{
|
||||
size_t i;
|
||||
double ver;
|
||||
int status;
|
||||
int output_type = -1;
|
||||
const char *drv = conf.drvname;
|
||||
|
||||
ver = FSOUND_GetVersion ();
|
||||
if (ver < FMOD_VERSION) {
|
||||
dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (ver < 3.75) {
|
||||
dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
|
||||
"ADC will be disabled.\n");
|
||||
conf.broken_adc = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (drv) {
|
||||
int found = 0;
|
||||
for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
|
||||
if (!strcmp (drv, drvtab[i].name)) {
|
||||
output_type = drvtab[i].type;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
dolog ("Unknown FMOD driver `%s'\n", drv);
|
||||
dolog ("Valid drivers:\n");
|
||||
for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
|
||||
dolog (" %s\n", drvtab[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output_type != -1) {
|
||||
status = FSOUND_SetOutput (output_type);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.bufsize) {
|
||||
status = FSOUND_SetBufferSize (conf.bufsize);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
||||
if (!status) {
|
||||
fmod_logerr ("FSOUND_Init failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static int fmod_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
int status;
|
||||
FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to start recording\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
status = FSOUND_Record_Stop ();
|
||||
if (!status) {
|
||||
fmod_logerr ("Failed to stop recording\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fmod_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
FSOUND_Close ();
|
||||
}
|
||||
|
||||
static struct audio_option fmod_options[] = {
|
||||
{
|
||||
.name = "DRV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.drvname,
|
||||
.descr = "FMOD driver"
|
||||
},
|
||||
{
|
||||
.name = "FREQ",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.freq,
|
||||
.descr = "Default frequency"
|
||||
},
|
||||
{
|
||||
.name = "SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.nb_samples,
|
||||
.descr = "Buffer size in samples"
|
||||
},
|
||||
{
|
||||
.name = "CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.nb_channels,
|
||||
.descr = "Number of default channels (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "BUFSIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.bufsize,
|
||||
.descr = "(undocumented)"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops fmod_pcm_ops = {
|
||||
.init_out = fmod_init_out,
|
||||
.fini_out = fmod_fini_out,
|
||||
.run_out = fmod_run_out,
|
||||
.write = fmod_write,
|
||||
.ctl_out = fmod_ctl_out,
|
||||
|
||||
.init_in = fmod_init_in,
|
||||
.fini_in = fmod_fini_in,
|
||||
.run_in = fmod_run_in,
|
||||
.read = fmod_read,
|
||||
.ctl_in = fmod_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver fmod_audio_driver = {
|
||||
.name = "fmod",
|
||||
.descr = "FMOD 3.xx http://www.fmod.org",
|
||||
.options = fmod_options,
|
||||
.init = fmod_audio_init,
|
||||
.fini = fmod_audio_fini,
|
||||
.pcm_ops = &fmod_pcm_ops,
|
||||
.can_be_default = 1,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof (FMODVoiceOut),
|
||||
.voice_size_in = sizeof (FMODVoiceIn)
|
||||
};
|
||||
+2
-2
@@ -63,7 +63,7 @@ static int no_write (SWVoiceOut *sw, void *buf, int len)
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int no_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
@@ -82,7 +82,7 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int no_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
|
||||
+69
-71
@@ -30,6 +30,7 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "audio.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define AUDIO_CAP "oss"
|
||||
#include "audio_int.h"
|
||||
@@ -38,6 +39,16 @@
|
||||
#define USE_DSP_POLICY
|
||||
#endif
|
||||
|
||||
typedef struct OSSConf {
|
||||
int try_mmap;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
const char *devpath_out;
|
||||
const char *devpath_in;
|
||||
int exclusive;
|
||||
int policy;
|
||||
} OSSConf;
|
||||
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
@@ -47,6 +58,7 @@ typedef struct OSSVoiceOut {
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int pending;
|
||||
OSSConf *conf;
|
||||
} OSSVoiceOut;
|
||||
|
||||
typedef struct OSSVoiceIn {
|
||||
@@ -55,28 +67,9 @@ typedef struct OSSVoiceIn {
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
OSSConf *conf;
|
||||
} OSSVoiceIn;
|
||||
|
||||
static struct {
|
||||
int try_mmap;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
const char *devpath_out;
|
||||
const char *devpath_in;
|
||||
int debug;
|
||||
int exclusive;
|
||||
int policy;
|
||||
} conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.devpath_out = "/dev/dsp",
|
||||
.devpath_in = "/dev/dsp",
|
||||
.debug = 0,
|
||||
.exclusive = 0,
|
||||
.policy = 5
|
||||
};
|
||||
|
||||
struct oss_params {
|
||||
int freq;
|
||||
audfmt_e fmt;
|
||||
@@ -138,18 +131,18 @@ static void oss_helper_poll_in (void *opaque)
|
||||
audio_run ("oss_poll_in");
|
||||
}
|
||||
|
||||
static int oss_poll_out (HWVoiceOut *hw)
|
||||
static void oss_poll_out (HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
|
||||
return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
|
||||
qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
|
||||
}
|
||||
|
||||
static int oss_poll_in (HWVoiceIn *hw)
|
||||
static void oss_poll_in (HWVoiceIn *hw)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
|
||||
qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
|
||||
}
|
||||
|
||||
static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||
@@ -272,18 +265,18 @@ static int oss_get_version (int fd, int *version, const char *typ)
|
||||
#endif
|
||||
|
||||
static int oss_open (int in, struct oss_params *req,
|
||||
struct oss_params *obt, int *pfd)
|
||||
struct oss_params *obt, int *pfd, OSSConf* conf)
|
||||
{
|
||||
int fd;
|
||||
int oflags = conf.exclusive ? O_EXCL : 0;
|
||||
int oflags = conf->exclusive ? O_EXCL : 0;
|
||||
audio_buf_info abinfo;
|
||||
int fmt, freq, nchannels;
|
||||
int setfragment = 1;
|
||||
const char *dspname = in ? conf.devpath_in : conf.devpath_out;
|
||||
const char *dspname = in ? conf->devpath_in : conf->devpath_out;
|
||||
const char *typ = in ? "ADC" : "DAC";
|
||||
|
||||
/* Kludge needed to have working mmap on Linux */
|
||||
oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
|
||||
oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
|
||||
|
||||
fd = open (dspname, oflags | O_NONBLOCK);
|
||||
if (-1 == fd) {
|
||||
@@ -317,20 +310,18 @@ static int oss_open (int in, struct oss_params *req,
|
||||
}
|
||||
|
||||
#ifdef USE_DSP_POLICY
|
||||
if (conf.policy >= 0) {
|
||||
if (conf->policy >= 0) {
|
||||
int version;
|
||||
|
||||
if (!oss_get_version (fd, &version, typ)) {
|
||||
if (conf.debug) {
|
||||
dolog ("OSS version = %#x\n", version);
|
||||
}
|
||||
trace_oss_version(version);
|
||||
|
||||
if (version >= 0x040000) {
|
||||
int policy = conf.policy;
|
||||
int policy = conf->policy;
|
||||
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
|
||||
oss_logerr2 (errno, typ,
|
||||
"Failed to set timing policy to %d\n",
|
||||
conf.policy);
|
||||
conf->policy);
|
||||
goto err;
|
||||
}
|
||||
setfragment = 0;
|
||||
@@ -458,19 +449,12 @@ static int oss_run_out (HWVoiceOut *hw, int live)
|
||||
}
|
||||
|
||||
if (abinfo.bytes > bufsize) {
|
||||
if (conf.debug) {
|
||||
dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
|
||||
"please report your OS/audio hw to av1474@comtv.ru\n",
|
||||
abinfo.bytes, bufsize);
|
||||
}
|
||||
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
|
||||
abinfo.bytes = bufsize;
|
||||
}
|
||||
|
||||
if (abinfo.bytes < 0) {
|
||||
if (conf.debug) {
|
||||
dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
|
||||
abinfo.bytes, bufsize);
|
||||
}
|
||||
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -510,7 +494,8 @@ static void oss_fini_out (HWVoiceOut *hw)
|
||||
}
|
||||
}
|
||||
|
||||
static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
struct oss_params req, obt;
|
||||
@@ -519,16 +504,17 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
struct audsettings obt_as;
|
||||
OSSConf *conf = drv_opaque;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
req.fragsize = conf->fragsize;
|
||||
req.nfrags = conf->nfrags;
|
||||
|
||||
if (oss_open (0, &req, &obt, &fd)) {
|
||||
if (oss_open (0, &req, &obt, &fd, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -555,7 +541,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (conf.try_mmap) {
|
||||
if (conf->try_mmap) {
|
||||
oss->pcm_buf = mmap (
|
||||
NULL,
|
||||
hw->samples << hw->info.shift,
|
||||
@@ -615,6 +601,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -634,7 +621,8 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
va_end (ap);
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode && oss_poll_out (hw)) {
|
||||
if (poll_mode) {
|
||||
oss_poll_out (hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
@@ -676,7 +664,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
struct oss_params req, obt;
|
||||
@@ -685,15 +673,16 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
int fd;
|
||||
audfmt_e effective_fmt;
|
||||
struct audsettings obt_as;
|
||||
OSSConf *conf = drv_opaque;
|
||||
|
||||
oss->fd = -1;
|
||||
|
||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||
req.freq = as->freq;
|
||||
req.nchannels = as->nchannels;
|
||||
req.fragsize = conf.fragsize;
|
||||
req.nfrags = conf.nfrags;
|
||||
if (oss_open (1, &req, &obt, &fd)) {
|
||||
req.fragsize = conf->fragsize;
|
||||
req.nfrags = conf->nfrags;
|
||||
if (oss_open (1, &req, &obt, &fd, conf)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -727,6 +716,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->conf = conf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -828,7 +818,8 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
if (poll_mode && oss_poll_in (hw)) {
|
||||
if (poll_mode) {
|
||||
oss_poll_in (hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
@@ -845,71 +836,78 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static OSSConf glob_conf = {
|
||||
.try_mmap = 0,
|
||||
.nfrags = 4,
|
||||
.fragsize = 4096,
|
||||
.devpath_out = "/dev/dsp",
|
||||
.devpath_in = "/dev/dsp",
|
||||
.exclusive = 0,
|
||||
.policy = 5
|
||||
};
|
||||
|
||||
static void *oss_audio_init (void)
|
||||
{
|
||||
if (access(conf.devpath_in, R_OK | W_OK) < 0 ||
|
||||
access(conf.devpath_out, R_OK | W_OK) < 0) {
|
||||
OSSConf *conf = g_malloc(sizeof(OSSConf));
|
||||
*conf = glob_conf;
|
||||
|
||||
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
|
||||
access(conf->devpath_out, R_OK | W_OK) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return &conf;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void oss_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option oss_options[] = {
|
||||
{
|
||||
.name = "FRAGSIZE",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fragsize,
|
||||
.valp = &glob_conf.fragsize,
|
||||
.descr = "Fragment size in bytes"
|
||||
},
|
||||
{
|
||||
.name = "NFRAGS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.nfrags,
|
||||
.valp = &glob_conf.nfrags,
|
||||
.descr = "Number of fragments"
|
||||
},
|
||||
{
|
||||
.name = "MMAP",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.try_mmap,
|
||||
.valp = &glob_conf.try_mmap,
|
||||
.descr = "Try using memory mapped access"
|
||||
},
|
||||
{
|
||||
.name = "DAC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.devpath_out,
|
||||
.valp = &glob_conf.devpath_out,
|
||||
.descr = "Path to DAC device"
|
||||
},
|
||||
{
|
||||
.name = "ADC_DEV",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.devpath_in,
|
||||
.valp = &glob_conf.devpath_in,
|
||||
.descr = "Path to ADC device"
|
||||
},
|
||||
{
|
||||
.name = "EXCLUSIVE",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.exclusive,
|
||||
.valp = &glob_conf.exclusive,
|
||||
.descr = "Open device in exclusive mode (vmix wont work)"
|
||||
},
|
||||
#ifdef USE_DSP_POLICY
|
||||
{
|
||||
.name = "POLICY",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.policy,
|
||||
.valp = &glob_conf.policy,
|
||||
.descr = "Set the timing policy of the device, -1 to use fragment mode",
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "DEBUG",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.debug,
|
||||
.descr = "Turn on some debugging messages"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
|
||||
+58
-47
@@ -8,6 +8,19 @@
|
||||
#include "audio_int.h"
|
||||
#include "audio_pt_int.h"
|
||||
|
||||
typedef struct {
|
||||
int samples;
|
||||
char *server;
|
||||
char *sink;
|
||||
char *source;
|
||||
} PAConf;
|
||||
|
||||
typedef struct {
|
||||
PAConf conf;
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
} paaudio;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
int done;
|
||||
@@ -17,6 +30,7 @@ typedef struct {
|
||||
pa_stream *stream;
|
||||
void *pcm_buf;
|
||||
struct audio_pt pt;
|
||||
paaudio *g;
|
||||
} PAVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
@@ -30,20 +44,10 @@ typedef struct {
|
||||
struct audio_pt pt;
|
||||
const void *read_data;
|
||||
size_t read_index, read_length;
|
||||
paaudio *g;
|
||||
} PAVoiceIn;
|
||||
|
||||
typedef struct {
|
||||
int samples;
|
||||
char *server;
|
||||
char *sink;
|
||||
char *source;
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
} paaudio;
|
||||
|
||||
static paaudio glob_paaudio = {
|
||||
.samples = 4096,
|
||||
};
|
||||
static void qpa_audio_fini(void *opaque);
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
|
||||
{
|
||||
@@ -106,7 +110,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
|
||||
|
||||
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = p->g;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
@@ -160,7 +164,7 @@ unlock_and_fail:
|
||||
|
||||
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = p->g;
|
||||
|
||||
pa_threaded_mainloop_lock (g->mainloop);
|
||||
|
||||
@@ -222,7 +226,7 @@ static void *qpa_thread_out (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
|
||||
decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
|
||||
rpos = pa->rpos;
|
||||
|
||||
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
||||
@@ -314,7 +318,7 @@ static void *qpa_thread_in (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
|
||||
incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
|
||||
wpos = pa->wpos;
|
||||
|
||||
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
||||
@@ -430,7 +434,7 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
|
||||
|
||||
static void context_state_cb (pa_context *c, void *userdata)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = userdata;
|
||||
|
||||
switch (pa_context_get_state(c)) {
|
||||
case PA_CONTEXT_READY:
|
||||
@@ -449,7 +453,7 @@ static void context_state_cb (pa_context *c, void *userdata)
|
||||
|
||||
static void stream_state_cb (pa_stream *s, void * userdata)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = userdata;
|
||||
|
||||
switch (pa_stream_get_state (s)) {
|
||||
|
||||
@@ -467,23 +471,21 @@ static void stream_state_cb (pa_stream *s, void * userdata)
|
||||
|
||||
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = userdata;
|
||||
|
||||
pa_threaded_mainloop_signal (g->mainloop, 0);
|
||||
}
|
||||
|
||||
static pa_stream *qpa_simple_new (
|
||||
const char *server,
|
||||
paaudio *g,
|
||||
const char *name,
|
||||
pa_stream_direction_t dir,
|
||||
const char *dev,
|
||||
const char *stream_name,
|
||||
const pa_sample_spec *ss,
|
||||
const pa_channel_map *map,
|
||||
const pa_buffer_attr *attr,
|
||||
int *rerror)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
int r;
|
||||
pa_stream *stream;
|
||||
|
||||
@@ -534,13 +536,15 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
int error;
|
||||
static pa_sample_spec ss;
|
||||
static pa_buffer_attr ba;
|
||||
pa_sample_spec ss;
|
||||
pa_buffer_attr ba;
|
||||
struct audsettings obt_as = *as;
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
paaudio *g = pa->g = drv_opaque;
|
||||
|
||||
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||
ss.channels = as->nchannels;
|
||||
@@ -558,11 +562,10 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
glob_paaudio.server,
|
||||
g,
|
||||
"qemu",
|
||||
PA_STREAM_PLAYBACK,
|
||||
glob_paaudio.sink,
|
||||
"pcm.playback",
|
||||
g->conf.sink,
|
||||
&ss,
|
||||
NULL, /* channel map */
|
||||
&ba, /* buffering attributes */
|
||||
@@ -574,7 +577,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = glob_paaudio.samples;
|
||||
hw->samples = g->conf.samples;
|
||||
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
pa->rpos = hw->rpos;
|
||||
if (!pa->pcm_buf) {
|
||||
@@ -601,12 +604,13 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
int error;
|
||||
static pa_sample_spec ss;
|
||||
pa_sample_spec ss;
|
||||
struct audsettings obt_as = *as;
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
paaudio *g = pa->g = drv_opaque;
|
||||
|
||||
ss.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||
ss.channels = as->nchannels;
|
||||
@@ -615,11 +619,10 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
|
||||
|
||||
pa->stream = qpa_simple_new (
|
||||
glob_paaudio.server,
|
||||
g,
|
||||
"qemu",
|
||||
PA_STREAM_RECORD,
|
||||
glob_paaudio.source,
|
||||
"pcm.capture",
|
||||
g->conf.source,
|
||||
&ss,
|
||||
NULL, /* channel map */
|
||||
NULL, /* buffering attributes */
|
||||
@@ -631,7 +634,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = glob_paaudio.samples;
|
||||
hw->samples = g->conf.samples;
|
||||
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
||||
pa->wpos = hw->wpos;
|
||||
if (!pa->pcm_buf) {
|
||||
@@ -703,7 +706,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
pa_operation *op;
|
||||
pa_cvolume v;
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = pa->g;
|
||||
|
||||
#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
|
||||
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
|
||||
@@ -755,7 +758,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
pa_operation *op;
|
||||
pa_cvolume v;
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = pa->g;
|
||||
|
||||
#ifdef PA_CHECK_VERSION
|
||||
pa_cvolume_init (&v);
|
||||
@@ -805,23 +808,31 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
}
|
||||
|
||||
/* common */
|
||||
static PAConf glob_conf = {
|
||||
.samples = 4096,
|
||||
};
|
||||
|
||||
static void *qpa_audio_init (void)
|
||||
{
|
||||
paaudio *g = &glob_paaudio;
|
||||
paaudio *g = g_malloc(sizeof(paaudio));
|
||||
g->conf = glob_conf;
|
||||
g->mainloop = NULL;
|
||||
g->context = NULL;
|
||||
|
||||
g->mainloop = pa_threaded_mainloop_new ();
|
||||
if (!g->mainloop) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
|
||||
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
|
||||
g->conf.server);
|
||||
if (!g->context) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback (g->context, context_state_cb, g);
|
||||
|
||||
if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
|
||||
if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
|
||||
qpa_logerr (pa_context_errno (g->context),
|
||||
"pa_context_connect() failed\n");
|
||||
goto fail;
|
||||
@@ -854,12 +865,13 @@ static void *qpa_audio_init (void)
|
||||
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
|
||||
return &glob_paaudio;
|
||||
return g;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock (g->mainloop);
|
||||
fail:
|
||||
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
|
||||
qpa_audio_fini(g);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -874,39 +886,38 @@ static void qpa_audio_fini (void *opaque)
|
||||
if (g->context) {
|
||||
pa_context_disconnect (g->context);
|
||||
pa_context_unref (g->context);
|
||||
g->context = NULL;
|
||||
}
|
||||
|
||||
if (g->mainloop) {
|
||||
pa_threaded_mainloop_free (g->mainloop);
|
||||
}
|
||||
|
||||
g->mainloop = NULL;
|
||||
g_free(g);
|
||||
}
|
||||
|
||||
struct audio_option qpa_options[] = {
|
||||
{
|
||||
.name = "SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &glob_paaudio.samples,
|
||||
.valp = &glob_conf.samples,
|
||||
.descr = "buffer size in samples"
|
||||
},
|
||||
{
|
||||
.name = "SERVER",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_paaudio.server,
|
||||
.valp = &glob_conf.server,
|
||||
.descr = "server address"
|
||||
},
|
||||
{
|
||||
.name = "SINK",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_paaudio.sink,
|
||||
.valp = &glob_conf.sink,
|
||||
.descr = "sink device name"
|
||||
},
|
||||
{
|
||||
.name = "SOURCE",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &glob_paaudio.source,
|
||||
.valp = &glob_conf.source,
|
||||
.descr = "source device name"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
|
||||
+9
-1
@@ -55,6 +55,7 @@ static struct SDLAudioState {
|
||||
SDL_mutex *mutex;
|
||||
SDL_sem *sem;
|
||||
int initialized;
|
||||
bool driver_created;
|
||||
} glob_sdl;
|
||||
typedef struct SDLAudioState SDLAudioState;
|
||||
|
||||
@@ -332,7 +333,8 @@ static void sdl_fini_out (HWVoiceOut *hw)
|
||||
sdl_close (&glob_sdl);
|
||||
}
|
||||
|
||||
static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
@@ -392,6 +394,10 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void *sdl_audio_init (void)
|
||||
{
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
if (s->driver_created) {
|
||||
sdl_logerr("Can't create multiple sdl backends\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
|
||||
sdl_logerr ("SDL failed to initialize audio subsystem\n");
|
||||
@@ -413,6 +419,7 @@ static void *sdl_audio_init (void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->driver_created = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -423,6 +430,7 @@ static void sdl_audio_fini (void *opaque)
|
||||
SDL_DestroySemaphore (s->sem);
|
||||
SDL_DestroyMutex (s->mutex);
|
||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||
s->driver_created = false;
|
||||
}
|
||||
|
||||
static struct audio_option sdl_options[] = {
|
||||
|
||||
+3
-2
@@ -115,7 +115,8 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
|
||||
|
||||
/* playback */
|
||||
|
||||
static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
struct audsettings settings;
|
||||
@@ -243,7 +244,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
|
||||
|
||||
/* record */
|
||||
|
||||
static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
|
||||
static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
struct audsettings settings;
|
||||
|
||||
+23
-19
@@ -36,15 +36,10 @@ typedef struct WAVVoiceOut {
|
||||
int total_samples;
|
||||
} WAVVoiceOut;
|
||||
|
||||
static struct {
|
||||
typedef struct {
|
||||
struct audsettings settings;
|
||||
const char *wav_path;
|
||||
} conf = {
|
||||
.settings.freq = 44100,
|
||||
.settings.nchannels = 2,
|
||||
.settings.fmt = AUD_FMT_S16,
|
||||
.wav_path = "qemu.wav"
|
||||
};
|
||||
} WAVConf;
|
||||
|
||||
static int wav_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
@@ -105,7 +100,8 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
|
||||
}
|
||||
}
|
||||
|
||||
static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
int bits16 = 0, stereo = 0;
|
||||
@@ -115,9 +111,8 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
struct audsettings wav_as = conf.settings;
|
||||
|
||||
(void) as;
|
||||
WAVConf *conf = drv_opaque;
|
||||
struct audsettings wav_as = conf->settings;
|
||||
|
||||
stereo = wav_as.nchannels == 2;
|
||||
switch (wav_as.fmt) {
|
||||
@@ -155,10 +150,10 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
||||
|
||||
wav->f = fopen (conf.wav_path, "wb");
|
||||
wav->f = fopen (conf->wav_path, "wb");
|
||||
if (!wav->f) {
|
||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
conf.wav_path, strerror (errno));
|
||||
conf->wav_path, strerror (errno));
|
||||
g_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
return -1;
|
||||
@@ -226,40 +221,49 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static WAVConf glob_conf = {
|
||||
.settings.freq = 44100,
|
||||
.settings.nchannels = 2,
|
||||
.settings.fmt = AUD_FMT_S16,
|
||||
.wav_path = "qemu.wav"
|
||||
};
|
||||
|
||||
static void *wav_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
WAVConf *conf = g_malloc(sizeof(WAVConf));
|
||||
*conf = glob_conf;
|
||||
return conf;
|
||||
}
|
||||
|
||||
static void wav_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
ldebug ("wav_fini");
|
||||
g_free(opaque);
|
||||
}
|
||||
|
||||
static struct audio_option wav_options[] = {
|
||||
{
|
||||
.name = "FREQUENCY",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.settings.freq,
|
||||
.valp = &glob_conf.settings.freq,
|
||||
.descr = "Frequency"
|
||||
},
|
||||
{
|
||||
.name = "FORMAT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &conf.settings.fmt,
|
||||
.valp = &glob_conf.settings.fmt,
|
||||
.descr = "Format"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.settings.nchannels,
|
||||
.valp = &glob_conf.settings.nchannels,
|
||||
.descr = "Number of channels (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "PATH",
|
||||
.tag = AUD_OPT_STR,
|
||||
.valp = &conf.wav_path,
|
||||
.valp = &glob_conf.wav_path,
|
||||
.descr = "Path to wave file"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
|
||||
@@ -1,717 +0,0 @@
|
||||
/* public domain */
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "winwave"
|
||||
#include "audio_int.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include "audio_win_int.h"
|
||||
|
||||
static struct {
|
||||
int dac_headers;
|
||||
int dac_samples;
|
||||
int adc_headers;
|
||||
int adc_samples;
|
||||
} conf = {
|
||||
.dac_headers = 4,
|
||||
.dac_samples = 1024,
|
||||
.adc_headers = 4,
|
||||
.adc_samples = 1024
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
HWAVEOUT hwo;
|
||||
WAVEHDR *hdrs;
|
||||
HANDLE event;
|
||||
void *pcm_buf;
|
||||
int avail;
|
||||
int pending;
|
||||
int curhdr;
|
||||
int paused;
|
||||
CRITICAL_SECTION crit_sect;
|
||||
} WaveVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceIn hw;
|
||||
HWAVEIN hwi;
|
||||
WAVEHDR *hdrs;
|
||||
HANDLE event;
|
||||
void *pcm_buf;
|
||||
int curhdr;
|
||||
int paused;
|
||||
int rpos;
|
||||
int avail;
|
||||
CRITICAL_SECTION crit_sect;
|
||||
} WaveVoiceIn;
|
||||
|
||||
static void winwave_log_mmresult (MMRESULT mr)
|
||||
{
|
||||
const char *str = "BUG";
|
||||
|
||||
switch (mr) {
|
||||
case MMSYSERR_NOERROR:
|
||||
str = "Success";
|
||||
break;
|
||||
|
||||
case MMSYSERR_INVALHANDLE:
|
||||
str = "Specified device handle is invalid";
|
||||
break;
|
||||
|
||||
case MMSYSERR_BADDEVICEID:
|
||||
str = "Specified device id is out of range";
|
||||
break;
|
||||
|
||||
case MMSYSERR_NODRIVER:
|
||||
str = "No device driver is present";
|
||||
break;
|
||||
|
||||
case MMSYSERR_NOMEM:
|
||||
str = "Unable to allocate or lock memory";
|
||||
break;
|
||||
|
||||
case WAVERR_SYNC:
|
||||
str = "Device is synchronous but waveOutOpen was called "
|
||||
"without using the WINWAVE_ALLOWSYNC flag";
|
||||
break;
|
||||
|
||||
case WAVERR_UNPREPARED:
|
||||
str = "The data block pointed to by the pwh parameter "
|
||||
"hasn't been prepared";
|
||||
break;
|
||||
|
||||
case WAVERR_STILLPLAYING:
|
||||
str = "There are still buffers in the queue";
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
|
||||
return;
|
||||
}
|
||||
|
||||
dolog ("Reason: %s\n", str);
|
||||
}
|
||||
|
||||
static void GCC_FMT_ATTR (2, 3) winwave_logerr (
|
||||
MMRESULT mr,
|
||||
const char *fmt,
|
||||
...
|
||||
)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (AUDIO_CAP, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (NULL, " failed\n");
|
||||
winwave_log_mmresult (mr);
|
||||
}
|
||||
|
||||
static void winwave_anal_close_out (WaveVoiceOut *wave)
|
||||
{
|
||||
MMRESULT mr;
|
||||
|
||||
mr = waveOutClose (wave->hwo);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutClose");
|
||||
}
|
||||
wave->hwo = NULL;
|
||||
}
|
||||
|
||||
static void CALLBACK winwave_callback_out (
|
||||
HWAVEOUT hwo,
|
||||
UINT msg,
|
||||
DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1,
|
||||
DWORD_PTR dwParam2
|
||||
)
|
||||
{
|
||||
WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
|
||||
|
||||
switch (msg) {
|
||||
case WOM_DONE:
|
||||
{
|
||||
WAVEHDR *h = (WAVEHDR *) dwParam1;
|
||||
if (!h->dwUser) {
|
||||
h->dwUser = 1;
|
||||
EnterCriticalSection (&wave->crit_sect);
|
||||
{
|
||||
wave->avail += conf.dac_samples;
|
||||
}
|
||||
LeaveCriticalSection (&wave->crit_sect);
|
||||
if (wave->hw.poll_mode) {
|
||||
if (!SetEvent (wave->event)) {
|
||||
dolog ("DAC SetEvent failed %lx\n", GetLastError ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WOM_CLOSE:
|
||||
case WOM_OPEN:
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("unknown wave out callback msg %x\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
MMRESULT mr;
|
||||
WAVEFORMATEX wfx;
|
||||
WaveVoiceOut *wave;
|
||||
|
||||
wave = (WaveVoiceOut *) hw;
|
||||
|
||||
InitializeCriticalSection (&wave->crit_sect);
|
||||
|
||||
err = waveformat_from_audio_settings (&wfx, as);
|
||||
if (err) {
|
||||
goto err0;
|
||||
}
|
||||
|
||||
mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
|
||||
(DWORD_PTR) winwave_callback_out,
|
||||
(DWORD_PTR) wave, CALLBACK_FUNCTION);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutOpen");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
|
||||
sizeof (*wave->hdrs));
|
||||
if (!wave->hdrs) {
|
||||
goto err2;
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = conf.dac_samples * conf.dac_headers;
|
||||
wave->avail = hw->samples;
|
||||
|
||||
wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
|
||||
conf.dac_headers << hw->info.shift);
|
||||
if (!wave->pcm_buf) {
|
||||
goto err3;
|
||||
}
|
||||
|
||||
for (i = 0; i < conf.dac_headers; ++i) {
|
||||
WAVEHDR *h = &wave->hdrs[i];
|
||||
|
||||
h->dwUser = 0;
|
||||
h->dwBufferLength = conf.dac_samples << hw->info.shift;
|
||||
h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
|
||||
h->dwFlags = 0;
|
||||
|
||||
mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
|
||||
goto err4;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
g_free (wave->pcm_buf);
|
||||
err3:
|
||||
g_free (wave->hdrs);
|
||||
err2:
|
||||
winwave_anal_close_out (wave);
|
||||
err1:
|
||||
err0:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int winwave_write (SWVoiceOut *sw, void *buf, int len)
|
||||
{
|
||||
return audio_pcm_sw_write (sw, buf, len);
|
||||
}
|
||||
|
||||
static int winwave_run_out (HWVoiceOut *hw, int live)
|
||||
{
|
||||
WaveVoiceOut *wave = (WaveVoiceOut *) hw;
|
||||
int decr;
|
||||
int doreset;
|
||||
|
||||
EnterCriticalSection (&wave->crit_sect);
|
||||
{
|
||||
decr = audio_MIN (live, wave->avail);
|
||||
decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
|
||||
wave->pending += decr;
|
||||
wave->avail -= decr;
|
||||
}
|
||||
LeaveCriticalSection (&wave->crit_sect);
|
||||
|
||||
doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
|
||||
if (doreset && !ResetEvent (wave->event)) {
|
||||
dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
|
||||
}
|
||||
|
||||
while (wave->pending >= conf.dac_samples) {
|
||||
MMRESULT mr;
|
||||
WAVEHDR *h = &wave->hdrs[wave->curhdr];
|
||||
|
||||
h->dwUser = 0;
|
||||
mr = waveOutWrite (wave->hwo, h, sizeof (*h));
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
|
||||
break;
|
||||
}
|
||||
|
||||
wave->pending -= conf.dac_samples;
|
||||
wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
|
||||
}
|
||||
|
||||
return decr;
|
||||
}
|
||||
|
||||
static void winwave_poll (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
audio_run ("winwave_poll");
|
||||
}
|
||||
|
||||
static void winwave_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
int i;
|
||||
MMRESULT mr;
|
||||
WaveVoiceOut *wave = (WaveVoiceOut *) hw;
|
||||
|
||||
mr = waveOutReset (wave->hwo);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutReset");
|
||||
}
|
||||
|
||||
for (i = 0; i < conf.dac_headers; ++i) {
|
||||
mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
|
||||
sizeof (wave->hdrs[i]));
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
|
||||
}
|
||||
}
|
||||
|
||||
winwave_anal_close_out (wave);
|
||||
|
||||
if (wave->event) {
|
||||
qemu_del_wait_object (wave->event, winwave_poll, wave);
|
||||
if (!CloseHandle (wave->event)) {
|
||||
dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
|
||||
}
|
||||
wave->event = NULL;
|
||||
}
|
||||
|
||||
g_free (wave->pcm_buf);
|
||||
wave->pcm_buf = NULL;
|
||||
|
||||
g_free (wave->hdrs);
|
||||
wave->hdrs = NULL;
|
||||
}
|
||||
|
||||
static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
{
|
||||
MMRESULT mr;
|
||||
WaveVoiceOut *wave = (WaveVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
if (poll_mode && !wave->event) {
|
||||
wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
|
||||
if (!wave->event) {
|
||||
dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
|
||||
GetLastError ());
|
||||
}
|
||||
}
|
||||
|
||||
if (wave->event) {
|
||||
int ret;
|
||||
|
||||
ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
|
||||
hw->poll_mode = (ret == 0);
|
||||
}
|
||||
else {
|
||||
hw->poll_mode = 0;
|
||||
}
|
||||
wave->paused = 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (!wave->paused) {
|
||||
mr = waveOutReset (wave->hwo);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveOutReset");
|
||||
}
|
||||
else {
|
||||
wave->paused = 1;
|
||||
}
|
||||
}
|
||||
if (wave->event) {
|
||||
qemu_del_wait_object (wave->event, winwave_poll, wave);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void winwave_anal_close_in (WaveVoiceIn *wave)
|
||||
{
|
||||
MMRESULT mr;
|
||||
|
||||
mr = waveInClose (wave->hwi);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInClose");
|
||||
}
|
||||
wave->hwi = NULL;
|
||||
}
|
||||
|
||||
static void CALLBACK winwave_callback_in (
|
||||
HWAVEIN *hwi,
|
||||
UINT msg,
|
||||
DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1,
|
||||
DWORD_PTR dwParam2
|
||||
)
|
||||
{
|
||||
WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
|
||||
|
||||
switch (msg) {
|
||||
case WIM_DATA:
|
||||
{
|
||||
WAVEHDR *h = (WAVEHDR *) dwParam1;
|
||||
if (!h->dwUser) {
|
||||
h->dwUser = 1;
|
||||
EnterCriticalSection (&wave->crit_sect);
|
||||
{
|
||||
wave->avail += conf.adc_samples;
|
||||
}
|
||||
LeaveCriticalSection (&wave->crit_sect);
|
||||
if (wave->hw.poll_mode) {
|
||||
if (!SetEvent (wave->event)) {
|
||||
dolog ("ADC SetEvent failed %lx\n", GetLastError ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WIM_CLOSE:
|
||||
case WIM_OPEN:
|
||||
break;
|
||||
|
||||
default:
|
||||
dolog ("unknown wave in callback msg %x\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
|
||||
{
|
||||
int doreset;
|
||||
|
||||
doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
|
||||
if (doreset && !ResetEvent (wave->event)) {
|
||||
dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
|
||||
}
|
||||
|
||||
while (samples >= conf.adc_samples) {
|
||||
MMRESULT mr;
|
||||
WAVEHDR *h = &wave->hdrs[wave->curhdr];
|
||||
|
||||
h->dwUser = 0;
|
||||
mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
|
||||
}
|
||||
wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
|
||||
samples -= conf.adc_samples;
|
||||
}
|
||||
}
|
||||
|
||||
static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
MMRESULT mr;
|
||||
WAVEFORMATEX wfx;
|
||||
WaveVoiceIn *wave;
|
||||
|
||||
wave = (WaveVoiceIn *) hw;
|
||||
|
||||
InitializeCriticalSection (&wave->crit_sect);
|
||||
|
||||
err = waveformat_from_audio_settings (&wfx, as);
|
||||
if (err) {
|
||||
goto err0;
|
||||
}
|
||||
|
||||
mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
|
||||
(DWORD_PTR) winwave_callback_in,
|
||||
(DWORD_PTR) wave, CALLBACK_FUNCTION);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInOpen");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
|
||||
sizeof (*wave->hdrs));
|
||||
if (!wave->hdrs) {
|
||||
goto err2;
|
||||
}
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = conf.adc_samples * conf.adc_headers;
|
||||
wave->avail = 0;
|
||||
|
||||
wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
|
||||
conf.adc_headers << hw->info.shift);
|
||||
if (!wave->pcm_buf) {
|
||||
goto err3;
|
||||
}
|
||||
|
||||
for (i = 0; i < conf.adc_headers; ++i) {
|
||||
WAVEHDR *h = &wave->hdrs[i];
|
||||
|
||||
h->dwUser = 0;
|
||||
h->dwBufferLength = conf.adc_samples << hw->info.shift;
|
||||
h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
|
||||
h->dwFlags = 0;
|
||||
|
||||
mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
|
||||
goto err4;
|
||||
}
|
||||
}
|
||||
|
||||
wave->paused = 1;
|
||||
winwave_add_buffers (wave, hw->samples);
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
g_free (wave->pcm_buf);
|
||||
err3:
|
||||
g_free (wave->hdrs);
|
||||
err2:
|
||||
winwave_anal_close_in (wave);
|
||||
err1:
|
||||
err0:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void winwave_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
int i;
|
||||
MMRESULT mr;
|
||||
WaveVoiceIn *wave = (WaveVoiceIn *) hw;
|
||||
|
||||
mr = waveInReset (wave->hwi);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInReset");
|
||||
}
|
||||
|
||||
for (i = 0; i < conf.adc_headers; ++i) {
|
||||
mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
|
||||
sizeof (wave->hdrs[i]));
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
|
||||
}
|
||||
}
|
||||
|
||||
winwave_anal_close_in (wave);
|
||||
|
||||
if (wave->event) {
|
||||
qemu_del_wait_object (wave->event, winwave_poll, wave);
|
||||
if (!CloseHandle (wave->event)) {
|
||||
dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
|
||||
}
|
||||
wave->event = NULL;
|
||||
}
|
||||
|
||||
g_free (wave->pcm_buf);
|
||||
wave->pcm_buf = NULL;
|
||||
|
||||
g_free (wave->hdrs);
|
||||
wave->hdrs = NULL;
|
||||
}
|
||||
|
||||
static int winwave_run_in (HWVoiceIn *hw)
|
||||
{
|
||||
WaveVoiceIn *wave = (WaveVoiceIn *) hw;
|
||||
int live = audio_pcm_hw_get_live_in (hw);
|
||||
int dead = hw->samples - live;
|
||||
int decr, ret;
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EnterCriticalSection (&wave->crit_sect);
|
||||
{
|
||||
decr = audio_MIN (dead, wave->avail);
|
||||
wave->avail -= decr;
|
||||
}
|
||||
LeaveCriticalSection (&wave->crit_sect);
|
||||
|
||||
ret = decr;
|
||||
while (decr) {
|
||||
int left = hw->samples - hw->wpos;
|
||||
int conv = audio_MIN (left, decr);
|
||||
hw->conv (hw->conv_buf + hw->wpos,
|
||||
advance (wave->pcm_buf, wave->rpos << hw->info.shift),
|
||||
conv);
|
||||
|
||||
wave->rpos = (wave->rpos + conv) % hw->samples;
|
||||
hw->wpos = (hw->wpos + conv) % hw->samples;
|
||||
decr -= conv;
|
||||
}
|
||||
|
||||
winwave_add_buffers (wave, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int winwave_read (SWVoiceIn *sw, void *buf, int size)
|
||||
{
|
||||
return audio_pcm_sw_read (sw, buf, size);
|
||||
}
|
||||
|
||||
static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
{
|
||||
MMRESULT mr;
|
||||
WaveVoiceIn *wave = (WaveVoiceIn *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
va_list ap;
|
||||
int poll_mode;
|
||||
|
||||
va_start (ap, cmd);
|
||||
poll_mode = va_arg (ap, int);
|
||||
va_end (ap);
|
||||
|
||||
if (poll_mode && !wave->event) {
|
||||
wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
|
||||
if (!wave->event) {
|
||||
dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
|
||||
GetLastError ());
|
||||
}
|
||||
}
|
||||
|
||||
if (wave->event) {
|
||||
int ret;
|
||||
|
||||
ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
|
||||
hw->poll_mode = (ret == 0);
|
||||
}
|
||||
else {
|
||||
hw->poll_mode = 0;
|
||||
}
|
||||
if (wave->paused) {
|
||||
mr = waveInStart (wave->hwi);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInStart");
|
||||
}
|
||||
wave->paused = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (!wave->paused) {
|
||||
mr = waveInStop (wave->hwi);
|
||||
if (mr != MMSYSERR_NOERROR) {
|
||||
winwave_logerr (mr, "waveInStop");
|
||||
}
|
||||
else {
|
||||
wave->paused = 1;
|
||||
}
|
||||
}
|
||||
if (wave->event) {
|
||||
qemu_del_wait_object (wave->event, winwave_poll, wave);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *winwave_audio_init (void)
|
||||
{
|
||||
return &conf;
|
||||
}
|
||||
|
||||
static void winwave_audio_fini (void *opaque)
|
||||
{
|
||||
(void) opaque;
|
||||
}
|
||||
|
||||
static struct audio_option winwave_options[] = {
|
||||
{
|
||||
.name = "DAC_HEADERS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.dac_headers,
|
||||
.descr = "DAC number of headers",
|
||||
},
|
||||
{
|
||||
.name = "DAC_SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.dac_samples,
|
||||
.descr = "DAC number of samples per header",
|
||||
},
|
||||
{
|
||||
.name = "ADC_HEADERS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.adc_headers,
|
||||
.descr = "ADC number of headers",
|
||||
},
|
||||
{
|
||||
.name = "ADC_SAMPLES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.adc_samples,
|
||||
.descr = "ADC number of samples per header",
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static struct audio_pcm_ops winwave_pcm_ops = {
|
||||
.init_out = winwave_init_out,
|
||||
.fini_out = winwave_fini_out,
|
||||
.run_out = winwave_run_out,
|
||||
.write = winwave_write,
|
||||
.ctl_out = winwave_ctl_out,
|
||||
.init_in = winwave_init_in,
|
||||
.fini_in = winwave_fini_in,
|
||||
.run_in = winwave_run_in,
|
||||
.read = winwave_read,
|
||||
.ctl_in = winwave_ctl_in
|
||||
};
|
||||
|
||||
struct audio_driver winwave_audio_driver = {
|
||||
.name = "winwave",
|
||||
.descr = "Windows Waveform Audio http://msdn.microsoft.com",
|
||||
.options = winwave_options,
|
||||
.init = winwave_audio_init,
|
||||
.fini = winwave_audio_fini,
|
||||
.pcm_ops = &winwave_pcm_ops,
|
||||
.can_be_default = 1,
|
||||
.max_voices_out = INT_MAX,
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof (WaveVoiceOut),
|
||||
.voice_size_in = sizeof (WaveVoiceIn)
|
||||
};
|
||||
+2
-2
@@ -43,7 +43,7 @@ void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
|
||||
|
||||
server_fd = socket_listen(addr, errp);
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL);
|
||||
qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ void qmp_nbd_server_stop(Error **errp)
|
||||
}
|
||||
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
|
||||
qemu_set_fd_handler(server_fd, NULL, NULL, NULL);
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
}
|
||||
|
||||
@@ -285,8 +285,6 @@ sysconfdir="\${prefix}/etc"
|
||||
local_statedir="\${prefix}/var"
|
||||
confsuffix="/qemu"
|
||||
slirp="yes"
|
||||
fmod_lib=""
|
||||
fmod_inc=""
|
||||
oss_lib=""
|
||||
bsd="no"
|
||||
linux="no"
|
||||
@@ -437,6 +435,14 @@ EOF
|
||||
compile_object
|
||||
}
|
||||
|
||||
check_include() {
|
||||
cat > $TMPC <<EOF
|
||||
#include <$1>
|
||||
int main(void) { return 0; }
|
||||
EOF
|
||||
compile_object
|
||||
}
|
||||
|
||||
write_c_skeleton() {
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) { return 0; }
|
||||
@@ -564,24 +570,28 @@ case $targetos in
|
||||
CYGWIN*)
|
||||
mingw32="yes"
|
||||
QEMU_CFLAGS="-mno-cygwin $QEMU_CFLAGS"
|
||||
audio_possible_drivers="winwave sdl"
|
||||
audio_drv_list="winwave"
|
||||
audio_possible_drivers="sdl"
|
||||
audio_drv_list="sdl"
|
||||
;;
|
||||
MINGW32*)
|
||||
mingw32="yes"
|
||||
audio_possible_drivers="winwave dsound sdl fmod"
|
||||
audio_drv_list="winwave"
|
||||
audio_possible_drivers="dsound sdl"
|
||||
if check_include dsound.h; then
|
||||
audio_drv_list="dsound"
|
||||
else
|
||||
audio_drv_list=""
|
||||
fi
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
bsd="yes"
|
||||
audio_drv_list="oss"
|
||||
audio_possible_drivers="oss sdl esd pa"
|
||||
audio_possible_drivers="oss sdl pa"
|
||||
;;
|
||||
FreeBSD)
|
||||
bsd="yes"
|
||||
make="${MAKE-gmake}"
|
||||
audio_drv_list="oss"
|
||||
audio_possible_drivers="oss sdl esd pa"
|
||||
audio_possible_drivers="oss sdl pa"
|
||||
# needed for kinfo_getvmmap(3) in libutil.h
|
||||
LIBS="-lutil $LIBS"
|
||||
netmap="" # enable netmap autodetect
|
||||
@@ -591,14 +601,14 @@ DragonFly)
|
||||
bsd="yes"
|
||||
make="${MAKE-gmake}"
|
||||
audio_drv_list="oss"
|
||||
audio_possible_drivers="oss sdl esd pa"
|
||||
audio_possible_drivers="oss sdl pa"
|
||||
HOST_VARIANT_DIR="dragonfly"
|
||||
;;
|
||||
NetBSD)
|
||||
bsd="yes"
|
||||
make="${MAKE-gmake}"
|
||||
audio_drv_list="oss"
|
||||
audio_possible_drivers="oss sdl esd"
|
||||
audio_possible_drivers="oss sdl"
|
||||
oss_lib="-lossaudio"
|
||||
HOST_VARIANT_DIR="netbsd"
|
||||
;;
|
||||
@@ -606,7 +616,7 @@ OpenBSD)
|
||||
bsd="yes"
|
||||
make="${MAKE-gmake}"
|
||||
audio_drv_list="sdl"
|
||||
audio_possible_drivers="sdl esd"
|
||||
audio_possible_drivers="sdl"
|
||||
HOST_VARIANT_DIR="openbsd"
|
||||
;;
|
||||
Darwin)
|
||||
@@ -619,7 +629,7 @@ Darwin)
|
||||
fi
|
||||
cocoa="yes"
|
||||
audio_drv_list="coreaudio"
|
||||
audio_possible_drivers="coreaudio sdl fmod"
|
||||
audio_possible_drivers="coreaudio sdl"
|
||||
LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS"
|
||||
libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu"
|
||||
# Disable attempts to use ObjectiveC features in os/object.h since they
|
||||
@@ -674,15 +684,12 @@ Haiku)
|
||||
;;
|
||||
*)
|
||||
audio_drv_list="oss"
|
||||
audio_possible_drivers="oss alsa sdl esd pa"
|
||||
audio_possible_drivers="oss alsa sdl pa"
|
||||
linux="yes"
|
||||
linux_user="yes"
|
||||
kvm="yes"
|
||||
vhost_net="yes"
|
||||
vhost_scsi="yes"
|
||||
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "x32" ] ; then
|
||||
audio_possible_drivers="$audio_possible_drivers fmod"
|
||||
fi
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES"
|
||||
;;
|
||||
esac
|
||||
@@ -847,10 +854,6 @@ for opt do
|
||||
;;
|
||||
--enable-vnc) vnc="yes"
|
||||
;;
|
||||
--fmod-lib=*) fmod_lib="$optarg"
|
||||
;;
|
||||
--fmod-inc=*) fmod_inc="$optarg"
|
||||
;;
|
||||
--oss-lib=*) oss_lib="$optarg"
|
||||
;;
|
||||
--audio-drv-list=*) audio_drv_list="$optarg"
|
||||
@@ -1349,8 +1352,6 @@ Advanced options (experts only):
|
||||
--disable-guest-base disable GUEST_BASE support
|
||||
--enable-pie build Position Independent Executables
|
||||
--disable-pie do not build Position Independent Executables
|
||||
--fmod-lib path to FMOD library
|
||||
--fmod-inc path to FMOD includes
|
||||
--oss-lib path to OSS library
|
||||
--cpu=CPU Build for host CPU [$cpu]
|
||||
--disable-uuid disable uuid support
|
||||
@@ -2621,21 +2622,6 @@ for drv in $audio_drv_list; do
|
||||
libs_softmmu="-lasound $libs_softmmu"
|
||||
;;
|
||||
|
||||
fmod)
|
||||
if test -z $fmod_lib || test -z $fmod_inc; then
|
||||
error_exit "You must specify path to FMOD library and headers" \
|
||||
"Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
|
||||
fi
|
||||
audio_drv_probe $drv fmod.h $fmod_lib "return FSOUND_GetVersion();" "-I $fmod_inc"
|
||||
libs_softmmu="$fmod_lib $libs_softmmu"
|
||||
;;
|
||||
|
||||
esd)
|
||||
audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);'
|
||||
libs_softmmu="-lesd $libs_softmmu"
|
||||
audio_pt_int="yes"
|
||||
;;
|
||||
|
||||
pa)
|
||||
audio_drv_probe $drv pulse/mainloop.h "-lpulse" \
|
||||
"pa_mainloop *m = 0; pa_mainloop_free (m); return 0;"
|
||||
@@ -2660,11 +2646,6 @@ for drv in $audio_drv_list; do
|
||||
# XXX: Probes for CoreAudio, DirectSound, SDL(?)
|
||||
;;
|
||||
|
||||
winwave)
|
||||
libs_softmmu="-lwinmm $libs_softmmu"
|
||||
audio_win_int="yes"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "$audio_possible_drivers" | grep -q "\<$drv\>" || {
|
||||
error_exit "Unknown driver '$drv' selected" \
|
||||
@@ -4629,9 +4610,6 @@ echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
|
||||
for drv in $audio_drv_list; do
|
||||
def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'`
|
||||
echo "$def=y" >> $config_host_mak
|
||||
if test "$drv" = "fmod"; then
|
||||
echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak
|
||||
fi
|
||||
done
|
||||
if test "$audio_pt_int" = "yes" ; then
|
||||
echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
|
||||
|
||||
@@ -480,6 +480,7 @@ static const VMStateDescription icount_vmstate_timers = {
|
||||
.name = "timer/icount",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = icount_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(qemu_icount_bias, TimersState),
|
||||
VMSTATE_INT64(qemu_icount, TimersState),
|
||||
@@ -497,13 +498,9 @@ static const VMStateDescription vmstate_timers = {
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &icount_vmstate_timers,
|
||||
.needed = icount_state_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&icount_vmstate_timers,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,14 +24,9 @@ CONFIG_PIIX4=y
|
||||
CONFIG_IDE_ISA=y
|
||||
CONFIG_IDE_PIIX=y
|
||||
CONFIG_NE2000_ISA=y
|
||||
CONFIG_RC4030=y
|
||||
CONFIG_DP8393X=y
|
||||
CONFIG_DS1225Y=y
|
||||
CONFIG_MIPSNET=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_G364FB=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_JAZZ_LED=y
|
||||
CONFIG_MC146818RTC=y
|
||||
CONFIG_ISA_TESTDEV=y
|
||||
CONFIG_EMPTY_SLOT=y
|
||||
|
||||
@@ -29,6 +29,7 @@ CONFIG_DP8393X=y
|
||||
CONFIG_DS1225Y=y
|
||||
CONFIG_MIPSNET=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_JAZZ=y
|
||||
CONFIG_G364FB=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_JAZZ_LED=y
|
||||
|
||||
@@ -31,6 +31,7 @@ CONFIG_DS1225Y=y
|
||||
CONFIG_MIPSNET=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_FULONG=y
|
||||
CONFIG_JAZZ=y
|
||||
CONFIG_G364FB=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_JAZZ_LED=y
|
||||
|
||||
@@ -24,14 +24,9 @@ CONFIG_PIIX4=y
|
||||
CONFIG_IDE_ISA=y
|
||||
CONFIG_IDE_PIIX=y
|
||||
CONFIG_NE2000_ISA=y
|
||||
CONFIG_RC4030=y
|
||||
CONFIG_DP8393X=y
|
||||
CONFIG_DS1225Y=y
|
||||
CONFIG_MIPSNET=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_G364FB=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_JAZZ_LED=y
|
||||
CONFIG_MC146818RTC=y
|
||||
CONFIG_ISA_TESTDEV=y
|
||||
CONFIG_EMPTY_SLOT=y
|
||||
|
||||
@@ -2238,6 +2238,8 @@ const struct mips_opcode mips_builtin_opcodes[] =
|
||||
{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
|
||||
{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
|
||||
{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
|
||||
{"mfhc0", "t,G,H", 0x40400000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I33},
|
||||
{"mthc0", "t,G,H", 0x40c00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I33},
|
||||
{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 },
|
||||
{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
|
||||
{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
|
||||
@@ -2407,6 +2409,7 @@ const struct mips_opcode mips_builtin_opcodes[] =
|
||||
{"emt", "", 0x41600be1, 0xffffffff, TRAP, 0, MT32 },
|
||||
{"emt", "t", 0x41600be1, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
|
||||
{"eret", "", 0x42000018, 0xffffffff, 0, 0, I3|I32 },
|
||||
{"eretnc", "", 0x42000058, 0xffffffff, 0, 0, I33},
|
||||
{"evpe", "", 0x41600021, 0xffffffff, TRAP, 0, MT32 },
|
||||
{"evpe", "t", 0x41600021, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
|
||||
{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, 0, I33 },
|
||||
|
||||
+4
-7
@@ -257,6 +257,7 @@ const VMStateDescription vmstate_ide_drive_pio_state = {
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = ide_drive_pio_pre_save,
|
||||
.post_load = ide_drive_pio_post_load,
|
||||
.needed = ide_drive_pio_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(req_nb_sectors, IDEState),
|
||||
VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
|
||||
@@ -279,13 +280,9 @@ const VMStateDescription vmstate_ide_drive = {
|
||||
.... several fields ....
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_ide_drive_pio_state,
|
||||
.needed = ide_drive_pio_state_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_ide_drive_pio_state,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -203,3 +203,24 @@ completes fully overwriting the item's data.
|
||||
|
||||
NOTE: This function is deprecated, and will be completely removed
|
||||
starting with QEMU v2.4.
|
||||
|
||||
== Externally Provided Items ==
|
||||
|
||||
As of v2.4, "file" fw_cfg items (i.e., items with selector keys above
|
||||
FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file
|
||||
directory structure) may be inserted via the QEMU command line, using
|
||||
the following syntax:
|
||||
|
||||
-fw_cfg [name=]<item_name>,file=<path>
|
||||
|
||||
where <item_name> is the fw_cfg item name, and <path> is the location
|
||||
on the host file system of a file containing the data to be inserted.
|
||||
|
||||
NOTE: Users *SHOULD* choose item names beginning with the prefix "opt/"
|
||||
when using the "-fw_cfg" command line option, to avoid conflicting with
|
||||
item names used internally by QEMU. For instance:
|
||||
|
||||
-fw_cfg name=opt/my_item_name,file=./my_blob.bin
|
||||
|
||||
Similarly, QEMU developers *SHOULD NOT* use item names prefixed with
|
||||
"opt/" when inserting items programmatically, e.g. via fw_cfg_add_file().
|
||||
|
||||
@@ -420,6 +420,7 @@ Other properties for front-panel ports are available via DMA CMD descriptors:
|
||||
LEARNING 1 MAC address learning on port
|
||||
1 = enabled
|
||||
0 = disabled
|
||||
PHYS_NAME <var> Physical port name (string)
|
||||
|
||||
Set PORT_SETTINGS descriptor:
|
||||
|
||||
|
||||
@@ -454,6 +454,7 @@ static const VMStateDescription vmstate_cpu_common_exception_index = {
|
||||
.name = "cpu_common/exception_index",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = cpu_common_exception_index_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(exception_index, CPUState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -471,13 +472,9 @@ const VMStateDescription vmstate_cpu_common = {
|
||||
VMSTATE_UINT32(interrupt_request, CPUState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_cpu_common_exception_index,
|
||||
.needed = cpu_common_exception_index_needed,
|
||||
} , {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_cpu_common_exception_index,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3348,14 +3345,20 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr)
|
||||
return res;
|
||||
}
|
||||
|
||||
void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
|
||||
int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
|
||||
{
|
||||
RAMBlock *block;
|
||||
int ret = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
||||
func(block->host, block->offset, block->used_length, opaque);
|
||||
ret = func(block->idstr, block->host, block->offset,
|
||||
block->used_length, opaque);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1798,6 +1798,30 @@ STEXI
|
||||
show available trace events and their state
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@item rocker @var{name}
|
||||
@findex rocker
|
||||
Show Rocker(s)
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@item rocker_ports @var{name}
|
||||
@findex rocker_ports
|
||||
Show Rocker ports
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@item rocker_of_dpa_flows @var{name} [@var{tbl_id}]
|
||||
@findex rocker_of_dpa_flows
|
||||
Show Rocker OF-DPA flow tables
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@item rocker_of_dpa_groups @var{name} [@var{type}]
|
||||
@findex rocker_of_dpa_groups
|
||||
Show Rocker OF-DPA groups
|
||||
ETEXI
|
||||
|
||||
STEXI
|
||||
@end table
|
||||
ETEXI
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "hmp.h"
|
||||
#include "net/net.h"
|
||||
#include "net/eth.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/option.h"
|
||||
@@ -1999,3 +2000,305 @@ void hmp_qom_set(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
void hmp_rocker(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
RockerSwitch *rocker;
|
||||
Error *errp = NULL;
|
||||
|
||||
rocker = qmp_query_rocker(name, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_printf(mon, "name: %s\n", rocker->name);
|
||||
monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id);
|
||||
monitor_printf(mon, "ports: %d\n", rocker->ports);
|
||||
|
||||
qapi_free_RockerSwitch(rocker);
|
||||
}
|
||||
|
||||
void hmp_rocker_ports(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
RockerPortList *list, *port;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
Error *errp = NULL;
|
||||
|
||||
list = qmp_query_rocker_ports(name, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_printf(mon, " ena/ speed/ auto\n");
|
||||
monitor_printf(mon, " port link duplex neg?\n");
|
||||
|
||||
for (port = list; port; port = port->next) {
|
||||
monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n",
|
||||
port->value->name,
|
||||
port->value->enabled ? port->value->link_up ?
|
||||
"up" : "down" : "!ena",
|
||||
port->value->speed == 10000 ? "10G" : "??",
|
||||
port->value->duplex ? "FD" : "HD",
|
||||
port->value->autoneg ? "Yes" : "No");
|
||||
}
|
||||
|
||||
qapi_free_RockerPortList(list);
|
||||
}
|
||||
|
||||
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
RockerOfDpaFlowList *list, *info;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1);
|
||||
Error *errp = NULL;
|
||||
|
||||
list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_printf(mon, "prio tbl hits key(mask) --> actions\n");
|
||||
|
||||
for (info = list; info; info = info->next) {
|
||||
RockerOfDpaFlow *flow = info->value;
|
||||
RockerOfDpaFlowKey *key = flow->key;
|
||||
RockerOfDpaFlowMask *mask = flow->mask;
|
||||
RockerOfDpaFlowAction *action = flow->action;
|
||||
|
||||
if (flow->hits) {
|
||||
monitor_printf(mon, "%-4d %-3d %-4" PRIu64,
|
||||
key->priority, key->tbl_id, flow->hits);
|
||||
} else {
|
||||
monitor_printf(mon, "%-4d %-3d ",
|
||||
key->priority, key->tbl_id);
|
||||
}
|
||||
|
||||
if (key->has_in_pport) {
|
||||
monitor_printf(mon, " pport %d", key->in_pport);
|
||||
if (mask->has_in_pport) {
|
||||
monitor_printf(mon, "(0x%x)", mask->in_pport);
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_vlan_id) {
|
||||
monitor_printf(mon, " vlan %d",
|
||||
key->vlan_id & VLAN_VID_MASK);
|
||||
if (mask->has_vlan_id) {
|
||||
monitor_printf(mon, "(0x%x)", mask->vlan_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_tunnel_id) {
|
||||
monitor_printf(mon, " tunnel %d", key->tunnel_id);
|
||||
if (mask->has_tunnel_id) {
|
||||
monitor_printf(mon, "(0x%x)", mask->tunnel_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_eth_type) {
|
||||
switch (key->eth_type) {
|
||||
case 0x0806:
|
||||
monitor_printf(mon, " ARP");
|
||||
break;
|
||||
case 0x0800:
|
||||
monitor_printf(mon, " IP");
|
||||
break;
|
||||
case 0x86dd:
|
||||
monitor_printf(mon, " IPv6");
|
||||
break;
|
||||
case 0x8809:
|
||||
monitor_printf(mon, " LACP");
|
||||
break;
|
||||
case 0x88cc:
|
||||
monitor_printf(mon, " LLDP");
|
||||
break;
|
||||
default:
|
||||
monitor_printf(mon, " eth type 0x%04x", key->eth_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_eth_src) {
|
||||
if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) &&
|
||||
(mask->has_eth_src) &&
|
||||
(strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) {
|
||||
monitor_printf(mon, " src <any mcast/bcast>");
|
||||
} else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) &&
|
||||
(mask->has_eth_src) &&
|
||||
(strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) {
|
||||
monitor_printf(mon, " src <any ucast>");
|
||||
} else {
|
||||
monitor_printf(mon, " src %s", key->eth_src);
|
||||
if (mask->has_eth_src) {
|
||||
monitor_printf(mon, "(%s)", mask->eth_src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_eth_dst) {
|
||||
if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) &&
|
||||
(mask->has_eth_dst) &&
|
||||
(strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) {
|
||||
monitor_printf(mon, " dst <any mcast/bcast>");
|
||||
} else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) &&
|
||||
(mask->has_eth_dst) &&
|
||||
(strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) {
|
||||
monitor_printf(mon, " dst <any ucast>");
|
||||
} else {
|
||||
monitor_printf(mon, " dst %s", key->eth_dst);
|
||||
if (mask->has_eth_dst) {
|
||||
monitor_printf(mon, "(%s)", mask->eth_dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_ip_proto) {
|
||||
monitor_printf(mon, " proto %d", key->ip_proto);
|
||||
if (mask->has_ip_proto) {
|
||||
monitor_printf(mon, "(0x%x)", mask->ip_proto);
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_ip_tos) {
|
||||
monitor_printf(mon, " TOS %d", key->ip_tos);
|
||||
if (mask->has_ip_tos) {
|
||||
monitor_printf(mon, "(0x%x)", mask->ip_tos);
|
||||
}
|
||||
}
|
||||
|
||||
if (key->has_ip_dst) {
|
||||
monitor_printf(mon, " dst %s", key->ip_dst);
|
||||
}
|
||||
|
||||
if (action->has_goto_tbl || action->has_group_id ||
|
||||
action->has_new_vlan_id) {
|
||||
monitor_printf(mon, " -->");
|
||||
}
|
||||
|
||||
if (action->has_new_vlan_id) {
|
||||
monitor_printf(mon, " apply new vlan %d",
|
||||
ntohs(action->new_vlan_id));
|
||||
}
|
||||
|
||||
if (action->has_group_id) {
|
||||
monitor_printf(mon, " write group 0x%08x", action->group_id);
|
||||
}
|
||||
|
||||
if (action->has_goto_tbl) {
|
||||
monitor_printf(mon, " goto tbl %d", action->goto_tbl);
|
||||
}
|
||||
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
qapi_free_RockerOfDpaFlowList(list);
|
||||
}
|
||||
|
||||
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
RockerOfDpaGroupList *list, *g;
|
||||
const char *name = qdict_get_str(qdict, "name");
|
||||
uint8_t type = qdict_get_try_int(qdict, "type", 9);
|
||||
Error *errp = NULL;
|
||||
bool set = false;
|
||||
|
||||
list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &errp);
|
||||
if (errp != NULL) {
|
||||
hmp_handle_error(mon, &errp);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor_printf(mon, "id (decode) --> buckets\n");
|
||||
|
||||
for (g = list; g; g = g->next) {
|
||||
RockerOfDpaGroup *group = g->value;
|
||||
|
||||
monitor_printf(mon, "0x%08x", group->id);
|
||||
|
||||
monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" :
|
||||
group->type == 1 ? "L2 rewrite" :
|
||||
group->type == 2 ? "L3 unicast" :
|
||||
group->type == 3 ? "L2 multicast" :
|
||||
group->type == 4 ? "L2 flood" :
|
||||
group->type == 5 ? "L3 interface" :
|
||||
group->type == 6 ? "L3 multicast" :
|
||||
group->type == 7 ? "L3 ECMP" :
|
||||
group->type == 8 ? "L2 overlay" :
|
||||
"unknown");
|
||||
|
||||
if (group->has_vlan_id) {
|
||||
monitor_printf(mon, " vlan %d", group->vlan_id);
|
||||
}
|
||||
|
||||
if (group->has_pport) {
|
||||
monitor_printf(mon, " pport %d", group->pport);
|
||||
}
|
||||
|
||||
if (group->has_index) {
|
||||
monitor_printf(mon, " index %d", group->index);
|
||||
}
|
||||
|
||||
monitor_printf(mon, ") -->");
|
||||
|
||||
if (group->has_set_vlan_id && group->set_vlan_id) {
|
||||
set = true;
|
||||
monitor_printf(mon, " set vlan %d",
|
||||
group->set_vlan_id & VLAN_VID_MASK);
|
||||
}
|
||||
|
||||
if (group->has_set_eth_src) {
|
||||
if (!set) {
|
||||
set = true;
|
||||
monitor_printf(mon, " set");
|
||||
}
|
||||
monitor_printf(mon, " src %s", group->set_eth_src);
|
||||
}
|
||||
|
||||
if (group->has_set_eth_dst) {
|
||||
if (!set) {
|
||||
set = true;
|
||||
monitor_printf(mon, " set");
|
||||
}
|
||||
monitor_printf(mon, " dst %s", group->set_eth_dst);
|
||||
}
|
||||
|
||||
set = false;
|
||||
|
||||
if (group->has_ttl_check && group->ttl_check) {
|
||||
monitor_printf(mon, " check TTL");
|
||||
}
|
||||
|
||||
if (group->has_group_id && group->group_id) {
|
||||
monitor_printf(mon, " group id 0x%08x", group->group_id);
|
||||
}
|
||||
|
||||
if (group->has_pop_vlan && group->pop_vlan) {
|
||||
monitor_printf(mon, " pop vlan");
|
||||
}
|
||||
|
||||
if (group->has_out_pport) {
|
||||
monitor_printf(mon, " out pport %d", group->out_pport);
|
||||
}
|
||||
|
||||
if (group->has_group_ids) {
|
||||
struct uint32List *id;
|
||||
|
||||
monitor_printf(mon, " groups [");
|
||||
for (id = group->group_ids; id; id = id->next) {
|
||||
monitor_printf(mon, "0x%08x", id->value);
|
||||
if (id->next) {
|
||||
monitor_printf(mon, ",");
|
||||
}
|
||||
}
|
||||
monitor_printf(mon, "]");
|
||||
}
|
||||
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
qapi_free_RockerOfDpaGroupList(list);
|
||||
}
|
||||
|
||||
@@ -124,5 +124,9 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args,
|
||||
const char *str);
|
||||
void delvm_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||
void loadvm_completion(ReadLineState *rs, int nb_args, const char *str);
|
||||
void hmp_rocker(Monitor *mon, const QDict *qdict);
|
||||
void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
|
||||
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
|
||||
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -140,7 +140,8 @@ out:
|
||||
/* virtio-9p device */
|
||||
|
||||
static Property virtio_9p_properties[] = {
|
||||
DEFINE_VIRTIO_9P_PROPERTIES(V9fsState, fsconf),
|
||||
DEFINE_PROP_STRING("mount_tag", V9fsState, fsconf.tag),
|
||||
DEFINE_PROP_STRING("fsdev", V9fsState, fsconf.fsdev_id),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -391,8 +391,4 @@ extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
|
||||
#define VIRTIO_9P(obj) \
|
||||
OBJECT_CHECK(V9fsState, (obj), TYPE_VIRTIO_9P)
|
||||
|
||||
#define DEFINE_VIRTIO_9P_PROPERTIES(_state, _field) \
|
||||
DEFINE_PROP_STRING("mount_tag", _state, _field.tag), \
|
||||
DEFINE_PROP_STRING("fsdev", _state, _field.fsdev_id)
|
||||
|
||||
#endif
|
||||
|
||||
+4
-6
@@ -152,6 +152,7 @@ static const VMStateDescription vmstate_memhp_state = {
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.needed = vmstate_test_use_memhp,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -175,12 +176,9 @@ const VMStateDescription vmstate_ich9_pm = {
|
||||
VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_memhp_state,
|
||||
.needed = vmstate_test_use_memhp,
|
||||
},
|
||||
VMSTATE_END_OF_LIST()
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_memhp_state,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-6
@@ -260,6 +260,7 @@ static const VMStateDescription vmstate_memhp_state = {
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.minimum_version_id_old = 1,
|
||||
.needed = vmstate_test_use_memhp,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -298,12 +299,9 @@ static const VMStateDescription vmstate_acpi = {
|
||||
vmstate_test_use_acpi_pci_hotplug),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_memhp_state,
|
||||
.needed = vmstate_test_use_memhp,
|
||||
},
|
||||
VMSTATE_END_OF_LIST()
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_memhp_state,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+16
-26
@@ -699,6 +699,7 @@ static const VMStateDescription vmstate_fdrive_media_changed = {
|
||||
.name = "fdrive/media_changed",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = fdrive_media_changed_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(media_changed, FDrive),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -716,6 +717,7 @@ static const VMStateDescription vmstate_fdrive_media_rate = {
|
||||
.name = "fdrive/media_rate",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = fdrive_media_rate_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(media_rate, FDrive),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -733,6 +735,7 @@ static const VMStateDescription vmstate_fdrive_perpendicular = {
|
||||
.name = "fdrive/perpendicular",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = fdrive_perpendicular_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(perpendicular, FDrive),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -756,19 +759,11 @@ static const VMStateDescription vmstate_fdrive = {
|
||||
VMSTATE_UINT8(sect, FDrive),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_fdrive_media_changed,
|
||||
.needed = &fdrive_media_changed_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_fdrive_media_rate,
|
||||
.needed = &fdrive_media_rate_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_fdrive_perpendicular,
|
||||
.needed = &fdrive_perpendicular_needed,
|
||||
} , {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_fdrive_media_changed,
|
||||
&vmstate_fdrive_media_rate,
|
||||
&vmstate_fdrive_perpendicular,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -833,6 +828,7 @@ static const VMStateDescription vmstate_fdc_reset_sensei = {
|
||||
.name = "fdc/reset_sensei",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = fdc_reset_sensei_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(reset_sensei, FDCtrl),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -850,6 +846,7 @@ static const VMStateDescription vmstate_fdc_result_timer = {
|
||||
.name = "fdc/result_timer",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = fdc_result_timer_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_TIMER_PTR(result_timer, FDCtrl),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -867,6 +864,7 @@ static const VMStateDescription vmstate_fdc_phase = {
|
||||
.name = "fdc/phase",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = fdc_phase_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(phase, FDCtrl),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -911,19 +909,11 @@ static const VMStateDescription vmstate_fdc = {
|
||||
vmstate_fdrive, FDrive),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_fdc_reset_sensei,
|
||||
.needed = fdc_reset_sensei_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_fdc_result_timer,
|
||||
.needed = fdc_result_timer_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_fdc_phase,
|
||||
.needed = fdc_phase_needed,
|
||||
} , {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_fdc_reset_sensei,
|
||||
&vmstate_fdc_result_timer,
|
||||
&vmstate_fdc_phase,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+16
-25
@@ -662,6 +662,7 @@ static const VMStateDescription vmstate_serial_thr_ipending = {
|
||||
.name = "serial/thr_ipending",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = serial_thr_ipending_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(thr_ipending, SerialState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -678,6 +679,7 @@ static const VMStateDescription vmstate_serial_tsr = {
|
||||
.name = "serial/tsr",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = serial_tsr_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(tsr_retry, SerialState),
|
||||
VMSTATE_UINT8(thr, SerialState),
|
||||
@@ -697,6 +699,7 @@ static const VMStateDescription vmstate_serial_recv_fifo = {
|
||||
.name = "serial/recv_fifo",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = serial_recv_fifo_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -713,6 +716,7 @@ static const VMStateDescription vmstate_serial_xmit_fifo = {
|
||||
.name = "serial/xmit_fifo",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = serial_xmit_fifo_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -729,6 +733,7 @@ static const VMStateDescription vmstate_serial_fifo_timeout_timer = {
|
||||
.name = "serial/fifo_timeout_timer",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = serial_fifo_timeout_timer_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -745,6 +750,7 @@ static const VMStateDescription vmstate_serial_timeout_ipending = {
|
||||
.name = "serial/timeout_ipending",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = serial_timeout_ipending_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(timeout_ipending, SerialState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -760,6 +766,7 @@ static bool serial_poll_needed(void *opaque)
|
||||
static const VMStateDescription vmstate_serial_poll = {
|
||||
.name = "serial/poll",
|
||||
.version_id = 1,
|
||||
.needed = serial_poll_needed,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(poll_msl, SerialState),
|
||||
@@ -788,31 +795,15 @@ const VMStateDescription vmstate_serial = {
|
||||
VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_serial_thr_ipending,
|
||||
.needed = &serial_thr_ipending_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_serial_tsr,
|
||||
.needed = &serial_tsr_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_serial_recv_fifo,
|
||||
.needed = &serial_recv_fifo_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_serial_xmit_fifo,
|
||||
.needed = &serial_xmit_fifo_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_serial_fifo_timeout_timer,
|
||||
.needed = &serial_fifo_timeout_timer_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_serial_timeout_ipending,
|
||||
.needed = &serial_timeout_ipending_needed,
|
||||
} , {
|
||||
.vmsd = &vmstate_serial_poll,
|
||||
.needed = &serial_poll_needed,
|
||||
} , {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_serial_thr_ipending,
|
||||
&vmstate_serial_tsr,
|
||||
&vmstate_serial_recv_fifo,
|
||||
&vmstate_serial_xmit_fifo,
|
||||
&vmstate_serial_fifo_timeout_timer,
|
||||
&vmstate_serial_timeout_ipending,
|
||||
&vmstate_serial_poll,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1083,7 +1083,8 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
static Property virtio_serial_properties[] = {
|
||||
DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerial, serial),
|
||||
DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports,
|
||||
31),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
+4
-7
@@ -2220,6 +2220,7 @@ static VMStateDescription qxl_vmstate_monitors_config = {
|
||||
.name = "qxl/monitors-config",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = qxl_monitors_config_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -2253,13 +2254,9 @@ static VMStateDescription qxl_vmstate = {
|
||||
VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &qxl_vmstate_monitors_config,
|
||||
.needed = qxl_monitors_config_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&qxl_vmstate_monitors_config,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-7
@@ -2035,6 +2035,7 @@ static const VMStateDescription vmstate_vga_endian = {
|
||||
.name = "vga.endian",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = vga_endian_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(big_endian_fb, VGACommonState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -2078,13 +2079,9 @@ const VMStateDescription vmstate_vga_common = {
|
||||
VMSTATE_UINT32(vbe_bank_mask, VGACommonState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_vga_endian,
|
||||
.needed = vga_endian_state_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_vga_endian,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+242
-226
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* QEMU JAZZ RC4030 chipset
|
||||
*
|
||||
* Copyright (c) 2007-2009 Herve Poussineau
|
||||
* Copyright (c) 2007-2013 Hervé Poussineau
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -24,29 +24,16 @@
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/mips/mips.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
/********************************************************/
|
||||
/* debug rc4030 */
|
||||
|
||||
//#define DEBUG_RC4030
|
||||
//#define DEBUG_RC4030_DMA
|
||||
|
||||
#ifdef DEBUG_RC4030
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0)
|
||||
static const char* irq_names[] = { "parallel", "floppy", "sound", "video",
|
||||
"network", "scsi", "keyboard", "mouse", "serial0", "serial1" };
|
||||
#else
|
||||
#define DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define RC4030_ERROR(fmt, ...) \
|
||||
do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
|
||||
#include "exec/address-spaces.h"
|
||||
#include "trace.h"
|
||||
|
||||
/********************************************************/
|
||||
/* rc4030 emulation */
|
||||
|
||||
#define MAX_TL_ENTRIES 512
|
||||
|
||||
typedef struct dma_pagetable_entry {
|
||||
int32_t frame;
|
||||
int32_t owner;
|
||||
@@ -63,8 +50,14 @@ typedef struct dma_pagetable_entry {
|
||||
#define DMA_FLAG_MEM_INTR 0x0200
|
||||
#define DMA_FLAG_ADDR_INTR 0x0400
|
||||
|
||||
#define TYPE_RC4030 "rc4030"
|
||||
#define RC4030(obj) \
|
||||
OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030)
|
||||
|
||||
typedef struct rc4030State
|
||||
{
|
||||
SysBusDevice parent;
|
||||
|
||||
uint32_t config; /* 0x0000: RC4030 config register */
|
||||
uint32_t revision; /* 0x0008: RC4030 Revision register */
|
||||
uint32_t invalid_address_register; /* 0x0010: Invalid Address register */
|
||||
@@ -83,7 +76,7 @@ typedef struct rc4030State
|
||||
uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */
|
||||
|
||||
uint32_t nmi_interrupt; /* 0x0200: interrupt source */
|
||||
uint32_t offset210;
|
||||
uint32_t memory_refresh_rate; /* 0x0210: memory refresh rate */
|
||||
uint32_t nvram_protect; /* 0x0220: NV ram protect register */
|
||||
uint32_t rem_speed[16];
|
||||
uint32_t imr_jazz; /* Local bus int enable mask */
|
||||
@@ -96,6 +89,16 @@ typedef struct rc4030State
|
||||
qemu_irq timer_irq;
|
||||
qemu_irq jazz_bus_irq;
|
||||
|
||||
/* biggest translation table */
|
||||
MemoryRegion dma_tt;
|
||||
/* translation table memory region alias, added to system RAM */
|
||||
MemoryRegion dma_tt_alias;
|
||||
/* whole DMA memory region, root of DMA address space */
|
||||
MemoryRegion dma_mr;
|
||||
/* translation table entry aliases, added to DMA memory region */
|
||||
MemoryRegion dma_mrs[MAX_TL_ENTRIES];
|
||||
AddressSpace dma_as;
|
||||
|
||||
MemoryRegion iomem_chipset;
|
||||
MemoryRegion iomem_jazzio;
|
||||
} rc4030State;
|
||||
@@ -112,7 +115,7 @@ static void set_next_tick(rc4030State *s)
|
||||
}
|
||||
|
||||
/* called for accesses to rc4030 */
|
||||
static uint32_t rc4030_readl(void *opaque, hwaddr addr)
|
||||
static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
uint32_t val;
|
||||
@@ -220,9 +223,9 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr)
|
||||
case 0x0208:
|
||||
val = 0;
|
||||
break;
|
||||
/* Offset 0x0210 */
|
||||
/* Memory refresh rate */
|
||||
case 0x0210:
|
||||
val = s->offset210;
|
||||
val = s->memory_refresh_rate;
|
||||
break;
|
||||
/* NV ram protect register */
|
||||
case 0x0220:
|
||||
@@ -238,39 +241,117 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr)
|
||||
val = 7; /* FIXME: should be read from EISA controller */
|
||||
break;
|
||||
default:
|
||||
RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"rc4030: invalid read at 0x%x", (int)addr);
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((addr & ~3) != 0x230) {
|
||||
DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr);
|
||||
trace_rc4030_read(addr, val);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t rc4030_readw(void *opaque, hwaddr addr)
|
||||
static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame)
|
||||
{
|
||||
uint32_t v = rc4030_readl(opaque, addr & ~0x3);
|
||||
if (addr & 0x2)
|
||||
return v >> 16;
|
||||
else
|
||||
return v & 0xffff;
|
||||
if (index < MAX_TL_ENTRIES) {
|
||||
memory_region_set_enabled(&s->dma_mrs[index], false);
|
||||
}
|
||||
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index >= MAX_TL_ENTRIES) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"rc4030: trying to use too high "
|
||||
"translation table entry %d (max allowed=%d)",
|
||||
index, MAX_TL_ENTRIES);
|
||||
return;
|
||||
}
|
||||
memory_region_set_alias_offset(&s->dma_mrs[index], frame);
|
||||
memory_region_set_enabled(&s->dma_mrs[index], true);
|
||||
}
|
||||
|
||||
static uint32_t rc4030_readb(void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t v = rc4030_readl(opaque, addr & ~0x3);
|
||||
return (v >> (8 * (addr & 0x3))) & 0xff;
|
||||
}
|
||||
|
||||
static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
|
||||
static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned int size)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
|
||||
/* write memory */
|
||||
memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size);
|
||||
|
||||
/* update dma address space (only if frame field has been written) */
|
||||
if (addr % sizeof(dma_pagetable_entry) == 0) {
|
||||
int index = addr / sizeof(dma_pagetable_entry);
|
||||
memory_region_transaction_begin();
|
||||
rc4030_dma_as_update_one(s, index, (uint32_t)data);
|
||||
memory_region_transaction_commit();
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps rc4030_dma_tt_ops = {
|
||||
.write = rc4030_dma_tt_write,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
};
|
||||
|
||||
static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base,
|
||||
uint32_t new_tl_limit)
|
||||
{
|
||||
int entries, i;
|
||||
dma_pagetable_entry *dma_tl_contents;
|
||||
|
||||
if (s->dma_tl_limit) {
|
||||
/* write old dma tl table to physical memory */
|
||||
memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias);
|
||||
cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff,
|
||||
memory_region_get_ram_ptr(&s->dma_tt),
|
||||
memory_region_size(&s->dma_tt_alias));
|
||||
}
|
||||
object_unparent(OBJECT(&s->dma_tt_alias));
|
||||
|
||||
s->dma_tl_base = new_tl_base;
|
||||
s->dma_tl_limit = new_tl_limit;
|
||||
new_tl_base &= 0x7fffffff;
|
||||
|
||||
if (s->dma_tl_limit) {
|
||||
uint64_t dma_tt_size;
|
||||
if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) {
|
||||
dma_tt_size = s->dma_tl_limit;
|
||||
} else {
|
||||
dma_tt_size = memory_region_size(&s->dma_tt);
|
||||
}
|
||||
memory_region_init_alias(&s->dma_tt_alias, OBJECT(s),
|
||||
"dma-table-alias",
|
||||
&s->dma_tt, 0, dma_tt_size);
|
||||
dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt);
|
||||
cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size);
|
||||
|
||||
memory_region_transaction_begin();
|
||||
entries = dma_tt_size / sizeof(dma_pagetable_entry);
|
||||
for (i = 0; i < entries; i++) {
|
||||
rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame);
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(), new_tl_base,
|
||||
&s->dma_tt_alias);
|
||||
memory_region_transaction_commit();
|
||||
} else {
|
||||
memory_region_init(&s->dma_tt_alias, OBJECT(s),
|
||||
"dma-table-alias", 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned int size)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
uint32_t val = data;
|
||||
addr &= 0x3fff;
|
||||
|
||||
DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr);
|
||||
trace_rc4030_write(addr, val);
|
||||
|
||||
switch (addr & ~0x3) {
|
||||
/* Global config register */
|
||||
@@ -279,11 +360,11 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
|
||||
break;
|
||||
/* DMA transl. table base */
|
||||
case 0x0018:
|
||||
s->dma_tl_base = val;
|
||||
rc4030_dma_tt_update(s, val, s->dma_tl_limit);
|
||||
break;
|
||||
/* DMA transl. table limit */
|
||||
case 0x0020:
|
||||
s->dma_tl_limit = val;
|
||||
rc4030_dma_tt_update(s, s->dma_tl_base, val);
|
||||
break;
|
||||
/* DMA transl. table invalidated */
|
||||
case 0x0028:
|
||||
@@ -371,9 +452,9 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
|
||||
s->dma_regs[entry][idx] = val;
|
||||
}
|
||||
break;
|
||||
/* Offset 0x0210 */
|
||||
/* Memory refresh rate */
|
||||
case 0x0210:
|
||||
s->offset210 = val;
|
||||
s->memory_refresh_rate = val;
|
||||
break;
|
||||
/* Interval timer reload */
|
||||
case 0x0228:
|
||||
@@ -385,48 +466,18 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
|
||||
case 0x0238:
|
||||
break;
|
||||
default:
|
||||
RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"rc4030: invalid write of 0x%02x at 0x%x",
|
||||
val, (int)addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc4030_writew(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
|
||||
|
||||
if (addr & 0x2)
|
||||
val = (val << 16) | (old_val & 0x0000ffff);
|
||||
else
|
||||
val = val | (old_val & 0xffff0000);
|
||||
rc4030_writel(opaque, addr & ~0x3, val);
|
||||
}
|
||||
|
||||
static void rc4030_writeb(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
|
||||
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
val = val | (old_val & 0xffffff00);
|
||||
break;
|
||||
case 1:
|
||||
val = (val << 8) | (old_val & 0xffff00ff);
|
||||
break;
|
||||
case 2:
|
||||
val = (val << 16) | (old_val & 0xff00ffff);
|
||||
break;
|
||||
case 3:
|
||||
val = (val << 24) | (old_val & 0x00ffffff);
|
||||
break;
|
||||
}
|
||||
rc4030_writel(opaque, addr & ~0x3, val);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps rc4030_ops = {
|
||||
.old_mmio = {
|
||||
.read = { rc4030_readb, rc4030_readw, rc4030_readl, },
|
||||
.write = { rc4030_writeb, rc4030_writew, rc4030_writel, },
|
||||
},
|
||||
.read = rc4030_read,
|
||||
.write = rc4030_write,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
@@ -436,22 +487,6 @@ static void update_jazz_irq(rc4030State *s)
|
||||
|
||||
pending = s->isr_jazz & s->imr_jazz;
|
||||
|
||||
#ifdef DEBUG_RC4030
|
||||
if (s->isr_jazz != 0) {
|
||||
uint32_t irq = 0;
|
||||
DPRINTF("pending irqs:");
|
||||
for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) {
|
||||
if (s->isr_jazz & (1 << irq)) {
|
||||
printf(" %s", irq_names[irq]);
|
||||
if (!(s->imr_jazz & (1 << irq))) {
|
||||
printf("(ignored)");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pending != 0)
|
||||
qemu_irq_raise(s->jazz_bus_irq);
|
||||
else
|
||||
@@ -479,7 +514,7 @@ static void rc4030_periodic_timer(void *opaque)
|
||||
qemu_irq_raise(s->timer_irq);
|
||||
}
|
||||
|
||||
static uint32_t jazzio_readw(void *opaque, hwaddr addr)
|
||||
static uint64_t jazzio_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
uint32_t val;
|
||||
@@ -494,7 +529,6 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr)
|
||||
irq = 0;
|
||||
while (pending) {
|
||||
if (pending & 1) {
|
||||
DPRINTF("returning irq %s\n", irq_names[irq]);
|
||||
val = (irq + 1) << 2;
|
||||
break;
|
||||
}
|
||||
@@ -508,36 +542,25 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr)
|
||||
val = s->imr_jazz;
|
||||
break;
|
||||
default:
|
||||
RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"rc4030/jazzio: invalid read at 0x%x", (int)addr);
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr);
|
||||
trace_jazzio_read(addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t jazzio_readb(void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t v;
|
||||
v = jazzio_readw(opaque, addr & ~0x1);
|
||||
return (v >> (8 * (addr & 0x1))) & 0xff;
|
||||
}
|
||||
|
||||
static uint32_t jazzio_readl(void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t v;
|
||||
v = jazzio_readw(opaque, addr);
|
||||
v |= jazzio_readw(opaque, addr + 2) << 16;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
|
||||
static void jazzio_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned int size)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
uint32_t val = data;
|
||||
addr &= 0xfff;
|
||||
|
||||
DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr);
|
||||
trace_jazzio_write(addr, val);
|
||||
|
||||
switch (addr) {
|
||||
/* Local bus int enable mask */
|
||||
@@ -546,43 +569,24 @@ static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
|
||||
update_jazz_irq(s);
|
||||
break;
|
||||
default:
|
||||
RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"rc4030/jazzio: invalid write of 0x%02x at 0x%x",
|
||||
val, (int)addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void jazzio_writeb(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
uint32_t old_val = jazzio_readw(opaque, addr & ~0x1);
|
||||
|
||||
switch (addr & 1) {
|
||||
case 0:
|
||||
val = val | (old_val & 0xff00);
|
||||
break;
|
||||
case 1:
|
||||
val = (val << 8) | (old_val & 0x00ff);
|
||||
break;
|
||||
}
|
||||
jazzio_writew(opaque, addr & ~0x1, val);
|
||||
}
|
||||
|
||||
static void jazzio_writel(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
jazzio_writew(opaque, addr, val & 0xffff);
|
||||
jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps jazzio_ops = {
|
||||
.old_mmio = {
|
||||
.read = { jazzio_readb, jazzio_readw, jazzio_readl, },
|
||||
.write = { jazzio_writeb, jazzio_writew, jazzio_writel, },
|
||||
},
|
||||
.read = jazzio_read,
|
||||
.write = jazzio_write,
|
||||
.impl.min_access_size = 2,
|
||||
.impl.max_access_size = 2,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void rc4030_reset(void *opaque)
|
||||
static void rc4030_reset(DeviceState *dev)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
rc4030State *s = RC4030(dev);
|
||||
int i;
|
||||
|
||||
s->config = 0x410; /* some boards seem to accept 0x104 too */
|
||||
@@ -590,14 +594,14 @@ static void rc4030_reset(void *opaque)
|
||||
s->invalid_address_register = 0;
|
||||
|
||||
memset(s->dma_regs, 0, sizeof(s->dma_regs));
|
||||
s->dma_tl_base = s->dma_tl_limit = 0;
|
||||
rc4030_dma_tt_update(s, 0, 0);
|
||||
|
||||
s->remote_failed_address = s->memory_failed_address = 0;
|
||||
s->cache_maint = 0;
|
||||
s->cache_ptag = s->cache_ltag = 0;
|
||||
s->cache_bmask = 0;
|
||||
|
||||
s->offset210 = 0x18186;
|
||||
s->memory_refresh_rate = 0x18186;
|
||||
s->nvram_protect = 7;
|
||||
for (i = 0; i < 15; i++)
|
||||
s->rem_speed[i] = 7;
|
||||
@@ -631,7 +635,7 @@ static int rc4030_load(QEMUFile *f, void *opaque, int version_id)
|
||||
s->cache_ptag = qemu_get_be32(f);
|
||||
s->cache_ltag = qemu_get_be32(f);
|
||||
s->cache_bmask = qemu_get_be32(f);
|
||||
s->offset210 = qemu_get_be32(f);
|
||||
s->memory_refresh_rate = qemu_get_be32(f);
|
||||
s->nvram_protect = qemu_get_be32(f);
|
||||
for (i = 0; i < 15; i++)
|
||||
s->rem_speed[i] = qemu_get_be32(f);
|
||||
@@ -663,7 +667,7 @@ static void rc4030_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32(f, s->cache_ptag);
|
||||
qemu_put_be32(f, s->cache_ltag);
|
||||
qemu_put_be32(f, s->cache_bmask);
|
||||
qemu_put_be32(f, s->offset210);
|
||||
qemu_put_be32(f, s->memory_refresh_rate);
|
||||
qemu_put_be32(f, s->nvram_protect);
|
||||
for (i = 0; i < 15; i++)
|
||||
qemu_put_be32(f, s->rem_speed[i]);
|
||||
@@ -672,44 +676,6 @@ static void rc4030_save(QEMUFile *f, void *opaque)
|
||||
qemu_put_be32(f, s->itr);
|
||||
}
|
||||
|
||||
void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
hwaddr entry_addr;
|
||||
hwaddr phys_addr;
|
||||
dma_pagetable_entry entry;
|
||||
int index;
|
||||
int ncpy, i;
|
||||
|
||||
i = 0;
|
||||
for (;;) {
|
||||
if (i == len) {
|
||||
break;
|
||||
}
|
||||
|
||||
ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1));
|
||||
if (ncpy > len - i)
|
||||
ncpy = len - i;
|
||||
|
||||
/* Get DMA translation table entry */
|
||||
index = addr / DMA_PAGESIZE;
|
||||
if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
|
||||
break;
|
||||
}
|
||||
entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
|
||||
/* XXX: not sure. should we really use only lowest bits? */
|
||||
entry_addr &= 0x7fffffff;
|
||||
cpu_physical_memory_read(entry_addr, &entry, sizeof(entry));
|
||||
|
||||
/* Read/write data at right place */
|
||||
phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1));
|
||||
cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
|
||||
|
||||
i += ncpy;
|
||||
addr += ncpy;
|
||||
}
|
||||
}
|
||||
|
||||
static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
|
||||
{
|
||||
rc4030State *s = opaque;
|
||||
@@ -733,32 +699,11 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri
|
||||
dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
|
||||
|
||||
/* Read/write data at right place */
|
||||
rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write);
|
||||
address_space_rw(&s->dma_as, dma_addr, MEMTXATTRS_UNSPECIFIED,
|
||||
buf, len, is_write);
|
||||
|
||||
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
|
||||
s->dma_regs[n][DMA_REG_COUNT] -= len;
|
||||
|
||||
#ifdef DEBUG_RC4030_DMA
|
||||
{
|
||||
int i, j;
|
||||
printf("rc4030 dma: Copying %d bytes %s host %p\n",
|
||||
len, is_write ? "from" : "to", buf);
|
||||
for (i = 0; i < len; i += 16) {
|
||||
int n = 16;
|
||||
if (n > len - i) {
|
||||
n = len - i;
|
||||
}
|
||||
for (j = 0; j < n; j++)
|
||||
printf("%02x ", buf[i + j]);
|
||||
while (j++ < 16)
|
||||
printf(" ");
|
||||
printf("| ");
|
||||
for (j = 0; j < n; j++)
|
||||
printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct rc4030DMAState {
|
||||
@@ -795,31 +740,102 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
|
||||
return s;
|
||||
}
|
||||
|
||||
void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
|
||||
qemu_irq **irqs, rc4030_dma **dmas,
|
||||
MemoryRegion *sysmem)
|
||||
static void rc4030_initfn(Object *obj)
|
||||
{
|
||||
rc4030State *s;
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
rc4030State *s = RC4030(obj);
|
||||
SysBusDevice *sysbus = SYS_BUS_DEVICE(obj);
|
||||
|
||||
s = g_malloc0(sizeof(rc4030State));
|
||||
qdev_init_gpio_in(dev, rc4030_irq_jazz_request, 16);
|
||||
|
||||
*irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16);
|
||||
*dmas = rc4030_allocate_dmas(s, 4);
|
||||
sysbus_init_irq(sysbus, &s->timer_irq);
|
||||
sysbus_init_irq(sysbus, &s->jazz_bus_irq);
|
||||
|
||||
s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rc4030_periodic_timer, s);
|
||||
s->timer_irq = timer;
|
||||
s->jazz_bus_irq = jazz_bus;
|
||||
|
||||
qemu_register_reset(rc4030_reset, s);
|
||||
register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s);
|
||||
rc4030_reset(s);
|
||||
|
||||
sysbus_init_mmio(sysbus, &s->iomem_chipset);
|
||||
sysbus_init_mmio(sysbus, &s->iomem_jazzio);
|
||||
}
|
||||
|
||||
static void rc4030_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
rc4030State *s = RC4030(dev);
|
||||
Object *o = OBJECT(dev);
|
||||
int i;
|
||||
|
||||
s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
rc4030_periodic_timer, s);
|
||||
|
||||
memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s,
|
||||
"rc4030.chipset", 0x300);
|
||||
memory_region_add_subregion(sysmem, 0x80000000, &s->iomem_chipset);
|
||||
memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s,
|
||||
"rc4030.jazzio", 0x00001000);
|
||||
memory_region_add_subregion(sysmem, 0xf0000000, &s->iomem_jazzio);
|
||||
|
||||
return s;
|
||||
memory_region_init_rom_device(&s->dma_tt, o,
|
||||
&rc4030_dma_tt_ops, s, "dma-table",
|
||||
MAX_TL_ENTRIES * sizeof(dma_pagetable_entry),
|
||||
NULL);
|
||||
memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0);
|
||||
memory_region_init(&s->dma_mr, o, "dma", INT32_MAX);
|
||||
for (i = 0; i < MAX_TL_ENTRIES; ++i) {
|
||||
memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias",
|
||||
get_system_memory(), 0, DMA_PAGESIZE);
|
||||
memory_region_set_enabled(&s->dma_mrs[i], false);
|
||||
memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE,
|
||||
&s->dma_mrs[i]);
|
||||
}
|
||||
address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma");
|
||||
}
|
||||
|
||||
static void rc4030_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
rc4030State *s = RC4030(dev);
|
||||
int i;
|
||||
|
||||
timer_free(s->periodic_timer);
|
||||
|
||||
address_space_destroy(&s->dma_as);
|
||||
object_unparent(OBJECT(&s->dma_tt));
|
||||
object_unparent(OBJECT(&s->dma_tt_alias));
|
||||
object_unparent(OBJECT(&s->dma_mr));
|
||||
for (i = 0; i < MAX_TL_ENTRIES; ++i) {
|
||||
memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]);
|
||||
object_unparent(OBJECT(&s->dma_mrs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void rc4030_class_init(ObjectClass *klass, void *class_data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = rc4030_realize;
|
||||
dc->unrealize = rc4030_unrealize;
|
||||
dc->reset = rc4030_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo rc4030_info = {
|
||||
.name = TYPE_RC4030,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(rc4030State),
|
||||
.instance_init = rc4030_initfn,
|
||||
.class_init = rc4030_class_init,
|
||||
};
|
||||
|
||||
static void rc4030_register_types(void)
|
||||
{
|
||||
type_register_static(&rc4030_info);
|
||||
}
|
||||
|
||||
type_init(rc4030_register_types)
|
||||
|
||||
DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_RC4030);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
*dmas = rc4030_allocate_dmas(dev, 4);
|
||||
*dma_mr = &RC4030(dev)->dma_mr;
|
||||
return dev;
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ static Aml *build_crs(PCIHostState *host,
|
||||
* Work-around for old bioses
|
||||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base || range_base > range_limit) {
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
@@ -854,7 +854,7 @@ static Aml *build_crs(PCIHostState *host,
|
||||
* Work-around for old bioses
|
||||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base || range_base > range_limit) {
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
@@ -865,7 +865,7 @@ static Aml *build_crs(PCIHostState *host,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
}
|
||||
}
|
||||
|
||||
range_base =
|
||||
pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
@@ -876,7 +876,7 @@ static Aml *build_crs(PCIHostState *host,
|
||||
* Work-around for old bioses
|
||||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base || range_base > range_limit) {
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
@@ -945,9 +945,8 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
|
||||
scope = aml_scope("\\_SB");
|
||||
dev = aml_device("PC%.02X", bus_num);
|
||||
aml_append(dev,
|
||||
aml_name_decl("_UID", aml_string("PC%.02X", bus_num)));
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A03")));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(bus_num)));
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
|
||||
aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num)));
|
||||
|
||||
if (numa_node != NUMA_NODE_UNASSIGNED) {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#ifdef CONFIG_XEN
|
||||
# include <xen/hvm/hvm_info_table.h>
|
||||
#endif
|
||||
#include "migration/migration.h"
|
||||
|
||||
#define MAX_IDE_BUS 2
|
||||
|
||||
@@ -305,6 +306,7 @@ static void pc_init1(MachineState *machine)
|
||||
|
||||
static void pc_compat_2_3(MachineState *machine)
|
||||
{
|
||||
savevm_skip_section_footers();
|
||||
}
|
||||
|
||||
static void pc_compat_2_2(MachineState *machine)
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "hw/usb.h"
|
||||
#include "hw/cpu/icc_bus.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/migration.h"
|
||||
|
||||
/* ICH9 AHCI has 6 ports */
|
||||
#define MAX_SATA_PORTS 6
|
||||
@@ -289,6 +290,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
|
||||
static void pc_compat_2_3(MachineState *machine)
|
||||
{
|
||||
savevm_skip_section_footers();
|
||||
}
|
||||
|
||||
static void pc_compat_2_2(MachineState *machine)
|
||||
|
||||
+12
-20
@@ -2561,6 +2561,7 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = {
|
||||
.name ="ide_drive/atapi/gesn_state",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = ide_atapi_gesn_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(events.new_media, IDEState),
|
||||
VMSTATE_BOOL(events.eject_request, IDEState),
|
||||
@@ -2572,6 +2573,7 @@ static const VMStateDescription vmstate_ide_tray_state = {
|
||||
.name = "ide_drive/tray_state",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = ide_tray_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(tray_open, IDEState),
|
||||
VMSTATE_BOOL(tray_locked, IDEState),
|
||||
@@ -2585,6 +2587,7 @@ static const VMStateDescription vmstate_ide_drive_pio_state = {
|
||||
.minimum_version_id = 1,
|
||||
.pre_save = ide_drive_pio_pre_save,
|
||||
.post_load = ide_drive_pio_post_load,
|
||||
.needed = ide_drive_pio_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(req_nb_sectors, IDEState),
|
||||
VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
|
||||
@@ -2626,19 +2629,11 @@ const VMStateDescription vmstate_ide_drive = {
|
||||
VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_ide_drive_pio_state,
|
||||
.needed = ide_drive_pio_state_needed,
|
||||
}, {
|
||||
.vmsd = &vmstate_ide_tray_state,
|
||||
.needed = ide_tray_state_needed,
|
||||
}, {
|
||||
.vmsd = &vmstate_ide_atapi_gesn_state,
|
||||
.needed = ide_atapi_gesn_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_ide_drive_pio_state,
|
||||
&vmstate_ide_tray_state,
|
||||
&vmstate_ide_atapi_gesn_state,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2646,6 +2641,7 @@ static const VMStateDescription vmstate_ide_error_status = {
|
||||
.name ="ide_bus/error",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 1,
|
||||
.needed = ide_error_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(error_status, IDEBus),
|
||||
VMSTATE_INT64_V(retry_sector_num, IDEBus, 2),
|
||||
@@ -2664,13 +2660,9 @@ const VMStateDescription vmstate_ide_bus = {
|
||||
VMSTATE_UINT8(unit, IDEBus),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_ide_error_status,
|
||||
.needed = ide_error_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_ide_error_status,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+6
-10
@@ -350,6 +350,7 @@ static const VMStateDescription vmstate_bmdma_current = {
|
||||
.name = "ide bmdma_current",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = ide_bmdma_current_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(cur_addr, BMDMAState),
|
||||
VMSTATE_UINT32(cur_prd_last, BMDMAState),
|
||||
@@ -363,6 +364,7 @@ static const VMStateDescription vmstate_bmdma_status = {
|
||||
.name ="ide bmdma/status",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = ide_bmdma_status_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(status, BMDMAState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -383,16 +385,10 @@ static const VMStateDescription vmstate_bmdma = {
|
||||
VMSTATE_UINT8(migration_retry_unit, BMDMAState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_bmdma_current,
|
||||
.needed = ide_bmdma_current_needed,
|
||||
}, {
|
||||
.vmsd = &vmstate_bmdma_status,
|
||||
.needed = ide_bmdma_status_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_bmdma_current,
|
||||
&vmstate_bmdma_status,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+15
-17
@@ -391,23 +391,24 @@ static int kbd_outport_post_load(void *opaque, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_kbd_outport = {
|
||||
.name = "pckbd_outport",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = kbd_outport_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(outport, KBDState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static bool kbd_outport_needed(void *opaque)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
return s->outport != kbd_outport_default(s);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_kbd_outport = {
|
||||
.name = "pckbd_outport",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = kbd_outport_post_load,
|
||||
.needed = kbd_outport_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(outport, KBDState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static int kbd_post_load(void *opaque, int version_id)
|
||||
{
|
||||
KBDState *s = opaque;
|
||||
@@ -430,12 +431,9 @@ static const VMStateDescription vmstate_kbd = {
|
||||
VMSTATE_UINT8(pending, KBDState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_kbd_outport,
|
||||
.needed = kbd_outport_needed,
|
||||
},
|
||||
VMSTATE_END_OF_LIST()
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_kbd_outport,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-7
@@ -677,6 +677,7 @@ static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 2,
|
||||
.post_load = ps2_kbd_ledstate_post_load,
|
||||
.needed = ps2_keyboard_ledstate_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(ledstate, PS2KbdState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -717,13 +718,9 @@ static const VMStateDescription vmstate_ps2_keyboard = {
|
||||
VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_ps2_keyboard_ledstate,
|
||||
.needed = ps2_keyboard_ledstate_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_ps2_keyboard_ledstate,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -369,6 +369,7 @@ static const VMStateDescription vmstate_apic_common_sipi = {
|
||||
.name = "apic_sipi",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = apic_common_sipi_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT32(sipi_vector, APICCommonState),
|
||||
VMSTATE_INT32(wait_for_sipi, APICCommonState),
|
||||
@@ -408,12 +409,9 @@ static const VMStateDescription vmstate_apic_common = {
|
||||
APICCommonState), /* open-coded timer state */
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_apic_common_sipi,
|
||||
.needed = apic_common_sipi_needed,
|
||||
},
|
||||
VMSTATE_END_OF_LIST()
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_apic_common_sipi,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-6
@@ -650,6 +650,7 @@ static const VMStateDescription vmstate_ich9_rst_cnt = {
|
||||
.name = "ICH9LPC/rst_cnt",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = ich9_rst_cnt_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(rst_cnt, ICH9LPCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -669,12 +670,9 @@ static const VMStateDescription vmstate_ich9_lpc = {
|
||||
VMSTATE_UINT32(sci_level, ICH9LPCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_ich9_rst_cnt,
|
||||
.needed = ich9_rst_cnt_needed
|
||||
},
|
||||
{ 0 }
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_ich9_rst_cnt,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
|
||||
obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
|
||||
obj-y += addr.o cputimer.o mips_int.o
|
||||
obj-$(CONFIG_JAZZ) += mips_jazz.o
|
||||
obj-$(CONFIG_FULONG) += mips_fulong2e.o
|
||||
obj-y += gt64xxx_pci.o
|
||||
|
||||
+33
-20
@@ -135,16 +135,16 @@ static void mips_jazz_init(MachineState *machine,
|
||||
MIPSCPU *cpu;
|
||||
CPUClass *cc;
|
||||
CPUMIPSState *env;
|
||||
qemu_irq *rc4030, *i8259;
|
||||
qemu_irq *i8259;
|
||||
rc4030_dma *dmas;
|
||||
void* rc4030_opaque;
|
||||
MemoryRegion *rc4030_dma_mr;
|
||||
MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *isa_io = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *rtc = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *i8042 = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *dma_dummy = g_new(MemoryRegion, 1);
|
||||
NICInfo *nd;
|
||||
DeviceState *dev;
|
||||
DeviceState *dev, *rc4030;
|
||||
SysBusDevice *sysbus;
|
||||
ISABus *isa_bus;
|
||||
ISADevice *pit;
|
||||
@@ -157,12 +157,7 @@ static void mips_jazz_init(MachineState *machine,
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
#ifdef TARGET_MIPS64
|
||||
cpu_model = "R4000";
|
||||
#else
|
||||
/* FIXME: All wrong, this maybe should be R3000 for the older JAZZs. */
|
||||
cpu_model = "24Kf";
|
||||
#endif
|
||||
}
|
||||
cpu = cpu_mips_init(cpu_model);
|
||||
if (cpu == NULL) {
|
||||
@@ -218,8 +213,14 @@ static void mips_jazz_init(MachineState *machine,
|
||||
cpu_mips_clock_init(env);
|
||||
|
||||
/* Chipset */
|
||||
rc4030_opaque = rc4030_init(env->irq[6], env->irq[3], &rc4030, &dmas,
|
||||
address_space);
|
||||
rc4030 = rc4030_init(&dmas, &rc4030_dma_mr);
|
||||
sysbus = SYS_BUS_DEVICE(rc4030);
|
||||
sysbus_connect_irq(sysbus, 0, env->irq[6]);
|
||||
sysbus_connect_irq(sysbus, 1, env->irq[3]);
|
||||
memory_region_add_subregion(address_space, 0x80000000,
|
||||
sysbus_mmio_get_region(sysbus, 0));
|
||||
memory_region_add_subregion(address_space, 0xf0000000,
|
||||
sysbus_mmio_get_region(sysbus, 1));
|
||||
memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000);
|
||||
memory_region_add_subregion(address_space, 0x8000d000, dma_dummy);
|
||||
|
||||
@@ -246,7 +247,7 @@ static void mips_jazz_init(MachineState *machine,
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(sysbus, 0, 0x60080000);
|
||||
sysbus_mmio_map(sysbus, 1, 0x40000000);
|
||||
sysbus_connect_irq(sysbus, 0, rc4030[3]);
|
||||
sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 3));
|
||||
{
|
||||
/* Simple ROM, so user doesn't have to provide one */
|
||||
MemoryRegion *rom_mr = g_new(MemoryRegion, 1);
|
||||
@@ -272,8 +273,17 @@ static void mips_jazz_init(MachineState *machine,
|
||||
if (!nd->model)
|
||||
nd->model = g_strdup("dp83932");
|
||||
if (strcmp(nd->model, "dp83932") == 0) {
|
||||
dp83932_init(nd, 0x80001000, 2, get_system_memory(), rc4030[4],
|
||||
rc4030_opaque, rc4030_dma_memory_rw);
|
||||
qemu_check_nic_model(nd, "dp83932");
|
||||
|
||||
dev = qdev_create(NULL, "dp8393x");
|
||||
qdev_set_nic_properties(dev, nd);
|
||||
qdev_prop_set_uint8(dev, "it_shift", 2);
|
||||
qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr);
|
||||
qdev_init_nofail(dev);
|
||||
sysbus = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(sysbus, 0, 0x80001000);
|
||||
sysbus_mmio_map(sysbus, 1, 0x8000b000);
|
||||
sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4));
|
||||
break;
|
||||
} else if (is_help_option(nd->model)) {
|
||||
fprintf(stderr, "qemu: Supported NICs: dp83932\n");
|
||||
@@ -287,7 +297,7 @@ static void mips_jazz_init(MachineState *machine,
|
||||
/* SCSI adapter */
|
||||
esp_init(0x80002000, 0,
|
||||
rc4030_dma_read, rc4030_dma_write, dmas[0],
|
||||
rc4030[5], &esp_reset, &dma_enable);
|
||||
qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
|
||||
|
||||
/* Floppy */
|
||||
if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {
|
||||
@@ -297,7 +307,7 @@ static void mips_jazz_init(MachineState *machine,
|
||||
for (n = 0; n < MAX_FD; n++) {
|
||||
fds[n] = drive_get(IF_FLOPPY, 0, n);
|
||||
}
|
||||
fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds);
|
||||
fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds);
|
||||
|
||||
/* Real time clock */
|
||||
rtc_init(isa_bus, 1980, NULL);
|
||||
@@ -305,23 +315,26 @@ static void mips_jazz_init(MachineState *machine,
|
||||
memory_region_add_subregion(address_space, 0x80004000, rtc);
|
||||
|
||||
/* Keyboard (i8042) */
|
||||
i8042_mm_init(rc4030[6], rc4030[7], i8042, 0x1000, 0x1);
|
||||
i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7),
|
||||
i8042, 0x1000, 0x1);
|
||||
memory_region_add_subregion(address_space, 0x80005000, i8042);
|
||||
|
||||
/* Serial ports */
|
||||
if (serial_hds[0]) {
|
||||
serial_mm_init(address_space, 0x80006000, 0, rc4030[8], 8000000/16,
|
||||
serial_mm_init(address_space, 0x80006000, 0,
|
||||
qdev_get_gpio_in(rc4030, 8), 8000000/16,
|
||||
serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
||||
}
|
||||
if (serial_hds[1]) {
|
||||
serial_mm_init(address_space, 0x80007000, 0, rc4030[9], 8000000/16,
|
||||
serial_mm_init(address_space, 0x80007000, 0,
|
||||
qdev_get_gpio_in(rc4030, 9), 8000000/16,
|
||||
serial_hds[1], DEVICE_NATIVE_ENDIAN);
|
||||
}
|
||||
|
||||
/* Parallel port */
|
||||
if (parallel_hds[0])
|
||||
parallel_mm_init(address_space, 0x80008000, 0, rc4030[0],
|
||||
parallel_hds[0]);
|
||||
parallel_mm_init(address_space, 0x80008000, 0,
|
||||
qdev_get_gpio_in(rc4030, 0), parallel_hds[0]);
|
||||
|
||||
/* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ typedef struct {
|
||||
static ISADevice *pit;
|
||||
|
||||
static struct _loaderparams {
|
||||
int ram_size;
|
||||
int ram_size, ram_low_size;
|
||||
const char *kernel_filename;
|
||||
const char *kernel_cmdline;
|
||||
const char *initrd_filename;
|
||||
@@ -641,8 +641,8 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
|
||||
stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */
|
||||
stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
|
||||
stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
|
||||
stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(ram_size) */
|
||||
stl_p(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(ram_size) */
|
||||
stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); /* lui a3, high(ram_low_size) */
|
||||
stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* ori a3, a3, low(ram_low_size) */
|
||||
|
||||
/* Load BAR registers as done by YAMON */
|
||||
stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */
|
||||
@@ -851,8 +851,10 @@ static int64_t load_kernel (void)
|
||||
}
|
||||
|
||||
prom_set(prom_buf, prom_index++, "memsize");
|
||||
prom_set(prom_buf, prom_index++, "%i",
|
||||
MIN(loaderparams.ram_size, 256 << 20));
|
||||
prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_low_size);
|
||||
|
||||
prom_set(prom_buf, prom_index++, "ememsize");
|
||||
prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_size);
|
||||
|
||||
prom_set(prom_buf, prom_index++, "modetty0");
|
||||
prom_set(prom_buf, prom_index++, "38400n8r");
|
||||
@@ -1054,7 +1056,8 @@ void mips_malta_init(MachineState *machine)
|
||||
}
|
||||
|
||||
/* Write a small bootloader to the flash location. */
|
||||
loaderparams.ram_size = ram_low_size;
|
||||
loaderparams.ram_size = ram_size;
|
||||
loaderparams.ram_low_size = ram_low_size;
|
||||
loaderparams.kernel_filename = kernel_filename;
|
||||
loaderparams.kernel_cmdline = kernel_cmdline;
|
||||
loaderparams.initrd_filename = initrd_filename;
|
||||
|
||||
@@ -39,3 +39,4 @@ obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
|
||||
common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
|
||||
rocker/rocker_desc.o rocker/rocker_world.o \
|
||||
rocker/rocker_of_dpa.o
|
||||
obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
|
||||
|
||||
+187
-188
@@ -17,20 +17,15 @@
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/devices.h"
|
||||
#include "net/net.h"
|
||||
#include "hw/mips/mips.h"
|
||||
#include "qemu/timer.h"
|
||||
#include <zlib.h>
|
||||
|
||||
//#define DEBUG_SONIC
|
||||
|
||||
/* Calculate CRCs properly on Rx packets */
|
||||
#define SONIC_CALCULATE_RXCRC
|
||||
|
||||
#if defined(SONIC_CALCULATE_RXCRC)
|
||||
/* For crc32 */
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#define SONIC_PROM_SIZE 0x1000
|
||||
|
||||
#ifdef DEBUG_SONIC
|
||||
#define DPRINTF(fmt, ...) \
|
||||
@@ -145,9 +140,14 @@ do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
|
||||
#define SONIC_ISR_PINT 0x0800
|
||||
#define SONIC_ISR_LCD 0x1000
|
||||
|
||||
#define TYPE_DP8393X "dp8393x"
|
||||
#define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
|
||||
|
||||
typedef struct dp8393xState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/* Hardware */
|
||||
int it_shift;
|
||||
uint8_t it_shift;
|
||||
qemu_irq irq;
|
||||
#ifdef DEBUG_SONIC
|
||||
int irq_level;
|
||||
@@ -156,8 +156,8 @@ typedef struct dp8393xState {
|
||||
int64_t wt_last_update;
|
||||
NICConf conf;
|
||||
NICState *nic;
|
||||
MemoryRegion *address_space;
|
||||
MemoryRegion mmio;
|
||||
MemoryRegion prom;
|
||||
|
||||
/* Registers */
|
||||
uint8_t cam[16][6];
|
||||
@@ -168,8 +168,8 @@ typedef struct dp8393xState {
|
||||
int loopback_packet;
|
||||
|
||||
/* Memory access */
|
||||
void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
|
||||
void* mem_opaque;
|
||||
void *dma_mr;
|
||||
AddressSpace as;
|
||||
} dp8393xState;
|
||||
|
||||
static void dp8393x_update_irq(dp8393xState *s)
|
||||
@@ -190,7 +190,7 @@ static void dp8393x_update_irq(dp8393xState *s)
|
||||
qemu_set_irq(s->irq, level);
|
||||
}
|
||||
|
||||
static void do_load_cam(dp8393xState *s)
|
||||
static void dp8393x_do_load_cam(dp8393xState *s)
|
||||
{
|
||||
uint16_t data[8];
|
||||
int width, size;
|
||||
@@ -201,9 +201,9 @@ static void do_load_cam(dp8393xState *s)
|
||||
|
||||
while (s->regs[SONIC_CDC] & 0x1f) {
|
||||
/* Fill current entry */
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
s->cam[index][0] = data[1 * width] & 0xff;
|
||||
s->cam[index][1] = data[1 * width] >> 8;
|
||||
s->cam[index][2] = data[2 * width] & 0xff;
|
||||
@@ -220,9 +220,9 @@ static void do_load_cam(dp8393xState *s)
|
||||
}
|
||||
|
||||
/* Read CAM enable */
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
s->regs[SONIC_CE] = data[0 * width];
|
||||
DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
|
||||
|
||||
@@ -232,7 +232,7 @@ static void do_load_cam(dp8393xState *s)
|
||||
dp8393x_update_irq(s);
|
||||
}
|
||||
|
||||
static void do_read_rra(dp8393xState *s)
|
||||
static void dp8393x_do_read_rra(dp8393xState *s)
|
||||
{
|
||||
uint16_t data[8];
|
||||
int width, size;
|
||||
@@ -240,9 +240,9 @@ static void do_read_rra(dp8393xState *s)
|
||||
/* Read memory */
|
||||
width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
|
||||
size = sizeof(uint16_t) * 4 * width;
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
|
||||
/* Update SONIC registers */
|
||||
s->regs[SONIC_CRBA0] = data[0 * width];
|
||||
@@ -272,7 +272,7 @@ static void do_read_rra(dp8393xState *s)
|
||||
s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
|
||||
}
|
||||
|
||||
static void do_software_reset(dp8393xState *s)
|
||||
static void dp8393x_do_software_reset(dp8393xState *s)
|
||||
{
|
||||
timer_del(s->watchdog);
|
||||
|
||||
@@ -280,7 +280,7 @@ static void do_software_reset(dp8393xState *s)
|
||||
s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
|
||||
}
|
||||
|
||||
static void set_next_tick(dp8393xState *s)
|
||||
static void dp8393x_set_next_tick(dp8393xState *s)
|
||||
{
|
||||
uint32_t ticks;
|
||||
int64_t delay;
|
||||
@@ -296,7 +296,7 @@ static void set_next_tick(dp8393xState *s)
|
||||
timer_mod(s->watchdog, s->wt_last_update + delay);
|
||||
}
|
||||
|
||||
static void update_wt_regs(dp8393xState *s)
|
||||
static void dp8393x_update_wt_regs(dp8393xState *s)
|
||||
{
|
||||
int64_t elapsed;
|
||||
uint32_t val;
|
||||
@@ -311,33 +311,33 @@ static void update_wt_regs(dp8393xState *s)
|
||||
val -= elapsed / 5000000;
|
||||
s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
|
||||
s->regs[SONIC_WT0] = (val >> 0) & 0xffff;
|
||||
set_next_tick(s);
|
||||
dp8393x_set_next_tick(s);
|
||||
|
||||
}
|
||||
|
||||
static void do_start_timer(dp8393xState *s)
|
||||
static void dp8393x_do_start_timer(dp8393xState *s)
|
||||
{
|
||||
s->regs[SONIC_CR] &= ~SONIC_CR_STP;
|
||||
set_next_tick(s);
|
||||
dp8393x_set_next_tick(s);
|
||||
}
|
||||
|
||||
static void do_stop_timer(dp8393xState *s)
|
||||
static void dp8393x_do_stop_timer(dp8393xState *s)
|
||||
{
|
||||
s->regs[SONIC_CR] &= ~SONIC_CR_ST;
|
||||
update_wt_regs(s);
|
||||
dp8393x_update_wt_regs(s);
|
||||
}
|
||||
|
||||
static void do_receiver_enable(dp8393xState *s)
|
||||
static void dp8393x_do_receiver_enable(dp8393xState *s)
|
||||
{
|
||||
s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
|
||||
}
|
||||
|
||||
static void do_receiver_disable(dp8393xState *s)
|
||||
static void dp8393x_do_receiver_disable(dp8393xState *s)
|
||||
{
|
||||
s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
|
||||
}
|
||||
|
||||
static void do_transmit_packets(dp8393xState *s)
|
||||
static void dp8393x_do_transmit_packets(dp8393xState *s)
|
||||
{
|
||||
NetClientState *nc = qemu_get_queue(s->nic);
|
||||
uint16_t data[12];
|
||||
@@ -353,9 +353,9 @@ static void do_transmit_packets(dp8393xState *s)
|
||||
(s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
|
||||
size = sizeof(uint16_t) * 6 * width;
|
||||
s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
tx_len = 0;
|
||||
|
||||
/* Update registers */
|
||||
@@ -379,18 +379,18 @@ static void do_transmit_packets(dp8393xState *s)
|
||||
if (tx_len + len > sizeof(s->tx_buffer)) {
|
||||
len = sizeof(s->tx_buffer) - tx_len;
|
||||
}
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
(s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
|
||||
&s->tx_buffer[tx_len], len, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
|
||||
tx_len += len;
|
||||
|
||||
i++;
|
||||
if (i != s->regs[SONIC_TFC]) {
|
||||
/* Read next fragment details */
|
||||
size = sizeof(uint16_t) * 3 * width;
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
s->regs[SONIC_TSA0] = data[0 * width];
|
||||
s->regs[SONIC_TSA1] = data[1 * width];
|
||||
s->regs[SONIC_TFS] = data[2 * width];
|
||||
@@ -422,16 +422,16 @@ static void do_transmit_packets(dp8393xState *s)
|
||||
/* Write status */
|
||||
data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
|
||||
size = sizeof(uint16_t) * width;
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
(s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
|
||||
(uint8_t *)data, size, 1);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
|
||||
|
||||
if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
|
||||
/* Read footer of packet */
|
||||
size = sizeof(uint16_t) * width;
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
|
||||
if (data[0 * width] & 0x1) {
|
||||
/* EOL detected */
|
||||
@@ -446,12 +446,12 @@ static void do_transmit_packets(dp8393xState *s)
|
||||
dp8393x_update_irq(s);
|
||||
}
|
||||
|
||||
static void do_halt_transmission(dp8393xState *s)
|
||||
static void dp8393x_do_halt_transmission(dp8393xState *s)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
static void do_command(dp8393xState *s, uint16_t command)
|
||||
static void dp8393x_do_command(dp8393xState *s, uint16_t command)
|
||||
{
|
||||
if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
|
||||
s->regs[SONIC_CR] &= ~SONIC_CR_RST;
|
||||
@@ -461,34 +461,36 @@ static void do_command(dp8393xState *s, uint16_t command)
|
||||
s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
|
||||
|
||||
if (command & SONIC_CR_HTX)
|
||||
do_halt_transmission(s);
|
||||
dp8393x_do_halt_transmission(s);
|
||||
if (command & SONIC_CR_TXP)
|
||||
do_transmit_packets(s);
|
||||
dp8393x_do_transmit_packets(s);
|
||||
if (command & SONIC_CR_RXDIS)
|
||||
do_receiver_disable(s);
|
||||
dp8393x_do_receiver_disable(s);
|
||||
if (command & SONIC_CR_RXEN)
|
||||
do_receiver_enable(s);
|
||||
dp8393x_do_receiver_enable(s);
|
||||
if (command & SONIC_CR_STP)
|
||||
do_stop_timer(s);
|
||||
dp8393x_do_stop_timer(s);
|
||||
if (command & SONIC_CR_ST)
|
||||
do_start_timer(s);
|
||||
dp8393x_do_start_timer(s);
|
||||
if (command & SONIC_CR_RST)
|
||||
do_software_reset(s);
|
||||
dp8393x_do_software_reset(s);
|
||||
if (command & SONIC_CR_RRRA)
|
||||
do_read_rra(s);
|
||||
dp8393x_do_read_rra(s);
|
||||
if (command & SONIC_CR_LCAM)
|
||||
do_load_cam(s);
|
||||
dp8393x_do_load_cam(s);
|
||||
}
|
||||
|
||||
static uint16_t read_register(dp8393xState *s, int reg)
|
||||
static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
dp8393xState *s = opaque;
|
||||
int reg = addr >> s->it_shift;
|
||||
uint16_t val = 0;
|
||||
|
||||
switch (reg) {
|
||||
/* Update data before reading it */
|
||||
case SONIC_WT0:
|
||||
case SONIC_WT1:
|
||||
update_wt_regs(s);
|
||||
dp8393x_update_wt_regs(s);
|
||||
val = s->regs[reg];
|
||||
break;
|
||||
/* Accept read to some registers only when in reset mode */
|
||||
@@ -510,14 +512,18 @@ static uint16_t read_register(dp8393xState *s, int reg)
|
||||
return val;
|
||||
}
|
||||
|
||||
static void write_register(dp8393xState *s, int reg, uint16_t val)
|
||||
static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned int size)
|
||||
{
|
||||
DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
|
||||
dp8393xState *s = opaque;
|
||||
int reg = addr >> s->it_shift;
|
||||
|
||||
DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
|
||||
|
||||
switch (reg) {
|
||||
/* Command register */
|
||||
case SONIC_CR:
|
||||
do_command(s, val);
|
||||
dp8393x_do_command(s, data);
|
||||
break;
|
||||
/* Prevent write to read-only registers */
|
||||
case SONIC_CAP2:
|
||||
@@ -530,37 +536,37 @@ static void write_register(dp8393xState *s, int reg, uint16_t val)
|
||||
/* Accept write to some registers only when in reset mode */
|
||||
case SONIC_DCR:
|
||||
if (s->regs[SONIC_CR] & SONIC_CR_RST) {
|
||||
s->regs[reg] = val & 0xbfff;
|
||||
s->regs[reg] = data & 0xbfff;
|
||||
} else {
|
||||
DPRINTF("writing to DCR invalid\n");
|
||||
}
|
||||
break;
|
||||
case SONIC_DCR2:
|
||||
if (s->regs[SONIC_CR] & SONIC_CR_RST) {
|
||||
s->regs[reg] = val & 0xf017;
|
||||
s->regs[reg] = data & 0xf017;
|
||||
} else {
|
||||
DPRINTF("writing to DCR2 invalid\n");
|
||||
}
|
||||
break;
|
||||
/* 12 lower bytes are Read Only */
|
||||
case SONIC_TCR:
|
||||
s->regs[reg] = val & 0xf000;
|
||||
s->regs[reg] = data & 0xf000;
|
||||
break;
|
||||
/* 9 lower bytes are Read Only */
|
||||
case SONIC_RCR:
|
||||
s->regs[reg] = val & 0xffe0;
|
||||
s->regs[reg] = data & 0xffe0;
|
||||
break;
|
||||
/* Ignore most significant bit */
|
||||
case SONIC_IMR:
|
||||
s->regs[reg] = val & 0x7fff;
|
||||
s->regs[reg] = data & 0x7fff;
|
||||
dp8393x_update_irq(s);
|
||||
break;
|
||||
/* Clear bits by writing 1 to them */
|
||||
case SONIC_ISR:
|
||||
val &= s->regs[reg];
|
||||
s->regs[reg] &= ~val;
|
||||
if (val & SONIC_ISR_RBE) {
|
||||
do_read_rra(s);
|
||||
data &= s->regs[reg];
|
||||
s->regs[reg] &= ~data;
|
||||
if (data & SONIC_ISR_RBE) {
|
||||
dp8393x_do_read_rra(s);
|
||||
}
|
||||
dp8393x_update_irq(s);
|
||||
break;
|
||||
@@ -569,24 +575,32 @@ static void write_register(dp8393xState *s, int reg, uint16_t val)
|
||||
case SONIC_REA:
|
||||
case SONIC_RRP:
|
||||
case SONIC_RWP:
|
||||
s->regs[reg] = val & 0xfffe;
|
||||
s->regs[reg] = data & 0xfffe;
|
||||
break;
|
||||
/* Invert written value for some registers */
|
||||
case SONIC_CRCT:
|
||||
case SONIC_FAET:
|
||||
case SONIC_MPT:
|
||||
s->regs[reg] = val ^ 0xffff;
|
||||
s->regs[reg] = data ^ 0xffff;
|
||||
break;
|
||||
/* All other registers have no special contrainst */
|
||||
default:
|
||||
s->regs[reg] = val;
|
||||
s->regs[reg] = data;
|
||||
}
|
||||
|
||||
if (reg == SONIC_WT0 || reg == SONIC_WT1) {
|
||||
set_next_tick(s);
|
||||
dp8393x_set_next_tick(s);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps dp8393x_ops = {
|
||||
.read = dp8393x_read,
|
||||
.write = dp8393x_write,
|
||||
.impl.min_access_size = 2,
|
||||
.impl.max_access_size = 2,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void dp8393x_watchdog(void *opaque)
|
||||
{
|
||||
dp8393xState *s = opaque;
|
||||
@@ -597,84 +611,14 @@ static void dp8393x_watchdog(void *opaque)
|
||||
|
||||
s->regs[SONIC_WT1] = 0xffff;
|
||||
s->regs[SONIC_WT0] = 0xffff;
|
||||
set_next_tick(s);
|
||||
dp8393x_set_next_tick(s);
|
||||
|
||||
/* Signal underflow */
|
||||
s->regs[SONIC_ISR] |= SONIC_ISR_TC;
|
||||
dp8393x_update_irq(s);
|
||||
}
|
||||
|
||||
static uint32_t dp8393x_readw(void *opaque, hwaddr addr)
|
||||
{
|
||||
dp8393xState *s = opaque;
|
||||
int reg;
|
||||
|
||||
if ((addr & ((1 << s->it_shift) - 1)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
reg = addr >> s->it_shift;
|
||||
return read_register(s, reg);
|
||||
}
|
||||
|
||||
static uint32_t dp8393x_readb(void *opaque, hwaddr addr)
|
||||
{
|
||||
uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
|
||||
return (v >> (8 * (addr & 0x1))) & 0xff;
|
||||
}
|
||||
|
||||
static uint32_t dp8393x_readl(void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t v;
|
||||
v = dp8393x_readw(opaque, addr);
|
||||
v |= dp8393x_readw(opaque, addr + 2) << 16;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
dp8393xState *s = opaque;
|
||||
int reg;
|
||||
|
||||
if ((addr & ((1 << s->it_shift) - 1)) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
reg = addr >> s->it_shift;
|
||||
|
||||
write_register(s, reg, (uint16_t)val);
|
||||
}
|
||||
|
||||
static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
|
||||
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
val = val | (old_val & 0xff00);
|
||||
break;
|
||||
case 1:
|
||||
val = (val << 8) | (old_val & 0x00ff);
|
||||
break;
|
||||
}
|
||||
dp8393x_writew(opaque, addr & ~0x1, val);
|
||||
}
|
||||
|
||||
static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val)
|
||||
{
|
||||
dp8393x_writew(opaque, addr, val & 0xffff);
|
||||
dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps dp8393x_ops = {
|
||||
.old_mmio = {
|
||||
.read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, },
|
||||
.write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, },
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int nic_can_receive(NetClientState *nc)
|
||||
static int dp8393x_can_receive(NetClientState *nc)
|
||||
{
|
||||
dp8393xState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
@@ -685,7 +629,8 @@ static int nic_can_receive(NetClientState *nc)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
|
||||
static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
|
||||
int size)
|
||||
{
|
||||
static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
int i;
|
||||
@@ -723,7 +668,8 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
|
||||
static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
|
||||
size_t size)
|
||||
{
|
||||
dp8393xState *s = qemu_get_nic_opaque(nc);
|
||||
uint16_t data[10];
|
||||
@@ -737,7 +683,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
|
||||
s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
|
||||
SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
|
||||
|
||||
packet_type = receive_filter(s, buf, size);
|
||||
packet_type = dp8393x_receive_filter(s, buf, size);
|
||||
if (packet_type < 0) {
|
||||
DPRINTF("packet not for netcard\n");
|
||||
return -1;
|
||||
@@ -750,7 +696,8 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
|
||||
/* Are we still in resource exhaustion? */
|
||||
size = sizeof(uint16_t) * 1 * width;
|
||||
address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
|
||||
s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
|
||||
address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
|
||||
(uint8_t *)data, size, 0);
|
||||
if (data[0 * width] & 0x1) {
|
||||
/* Still EOL ; stop reception */
|
||||
return -1;
|
||||
@@ -764,18 +711,16 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
|
||||
s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
|
||||
|
||||
/* Calculate the ethernet checksum */
|
||||
#ifdef SONIC_CALCULATE_RXCRC
|
||||
checksum = cpu_to_le32(crc32(0, buf, rx_len));
|
||||
#else
|
||||
checksum = 0;
|
||||
#endif
|
||||
|
||||
/* Put packet into RBA */
|
||||
DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
|
||||
address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
|
||||
s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
|
||||
address_space_rw(&s->as, address,
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
|
||||
address += rx_len;
|
||||
s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
|
||||
address_space_rw(&s->as, address,
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
|
||||
rx_len += 4;
|
||||
s->regs[SONIC_CRBA1] = address >> 16;
|
||||
s->regs[SONIC_CRBA0] = address & 0xffff;
|
||||
@@ -803,29 +748,30 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
|
||||
data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
|
||||
data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
|
||||
size = sizeof(uint16_t) * 5 * width;
|
||||
s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
|
||||
address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA],
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
|
||||
|
||||
/* Move to next descriptor */
|
||||
size = sizeof(uint16_t) * width;
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
|
||||
(uint8_t *)data, size, 0);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
|
||||
s->regs[SONIC_LLFA] = data[0 * width];
|
||||
if (s->regs[SONIC_LLFA] & 0x1) {
|
||||
/* EOL detected */
|
||||
s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
|
||||
} else {
|
||||
data[0 * width] = 0; /* in_use */
|
||||
s->memory_rw(s->mem_opaque,
|
||||
address_space_rw(&s->as,
|
||||
((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
|
||||
(uint8_t *)data, size, 1);
|
||||
MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
|
||||
s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
|
||||
s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
|
||||
s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
|
||||
|
||||
if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
|
||||
/* Read next RRA */
|
||||
do_read_rra(s);
|
||||
dp8393x_do_read_rra(s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,11 +781,12 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static void nic_reset(void *opaque)
|
||||
static void dp8393x_reset(DeviceState *dev)
|
||||
{
|
||||
dp8393xState *s = opaque;
|
||||
dp8393xState *s = DP8393X(dev);
|
||||
timer_del(s->watchdog);
|
||||
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
|
||||
s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
|
||||
s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
|
||||
@@ -862,39 +809,91 @@ static void nic_reset(void *opaque)
|
||||
static NetClientInfo net_dp83932_info = {
|
||||
.type = NET_CLIENT_OPTIONS_KIND_NIC,
|
||||
.size = sizeof(NICState),
|
||||
.can_receive = nic_can_receive,
|
||||
.receive = nic_receive,
|
||||
.can_receive = dp8393x_can_receive,
|
||||
.receive = dp8393x_receive,
|
||||
};
|
||||
|
||||
void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
|
||||
MemoryRegion *address_space,
|
||||
qemu_irq irq, void* mem_opaque,
|
||||
void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write))
|
||||
static void dp8393x_instance_init(Object *obj)
|
||||
{
|
||||
dp8393xState *s;
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
dp8393xState *s = DP8393X(obj);
|
||||
|
||||
qemu_check_nic_model(nd, "dp83932");
|
||||
sysbus_init_mmio(sbd, &s->mmio);
|
||||
sysbus_init_mmio(sbd, &s->prom);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
s = g_malloc0(sizeof(dp8393xState));
|
||||
static void dp8393x_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
dp8393xState *s = DP8393X(dev);
|
||||
int i, checksum;
|
||||
uint8_t *prom;
|
||||
|
||||
address_space_init(&s->as, s->dma_mr, "dp8393x");
|
||||
memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
|
||||
"dp8393x-regs", 0x40 << s->it_shift);
|
||||
|
||||
s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
|
||||
|
||||
s->address_space = address_space;
|
||||
s->mem_opaque = mem_opaque;
|
||||
s->memory_rw = memory_rw;
|
||||
s->it_shift = it_shift;
|
||||
s->irq = irq;
|
||||
s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
|
||||
s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
|
||||
|
||||
s->conf.macaddr = nd->macaddr;
|
||||
s->conf.peers.ncs[0] = nd->netdev;
|
||||
|
||||
s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
|
||||
|
||||
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
|
||||
qemu_register_reset(nic_reset, s);
|
||||
nic_reset(s);
|
||||
|
||||
memory_region_init_io(&s->mmio, NULL, &dp8393x_ops, s,
|
||||
"dp8393x", 0x40 << it_shift);
|
||||
memory_region_add_subregion(address_space, base, &s->mmio);
|
||||
memory_region_init_rom_device(&s->prom, OBJECT(dev), NULL, NULL,
|
||||
"dp8393x-prom", SONIC_PROM_SIZE, NULL);
|
||||
prom = memory_region_get_ram_ptr(&s->prom);
|
||||
checksum = 0;
|
||||
for (i = 0; i < 6; i++) {
|
||||
prom[i] = s->conf.macaddr.a[i];
|
||||
checksum += prom[i];
|
||||
if (checksum > 0xff) {
|
||||
checksum = (checksum + 1) & 0xff;
|
||||
}
|
||||
}
|
||||
prom[7] = 0xff - checksum;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_dp8393x = {
|
||||
.name = "dp8393x",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
|
||||
VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property dp8393x_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(dp8393xState, conf),
|
||||
DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
|
||||
DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void dp8393x_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||
dc->realize = dp8393x_realize;
|
||||
dc->reset = dp8393x_reset;
|
||||
dc->vmsd = &vmstate_dp8393x;
|
||||
dc->props = dp8393x_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo dp8393x_info = {
|
||||
.name = TYPE_DP8393X,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(dp8393xState),
|
||||
.instance_init = dp8393x_instance_init,
|
||||
.class_init = dp8393x_class_init,
|
||||
};
|
||||
|
||||
static void dp8393x_register_types(void)
|
||||
{
|
||||
type_register_static(&dp8393x_info);
|
||||
}
|
||||
|
||||
type_init(dp8393x_register_types)
|
||||
|
||||
+4
-7
@@ -1370,6 +1370,7 @@ static const VMStateDescription vmstate_e1000_mit_state = {
|
||||
.name = "e1000/mit_state",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = e1000_mit_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(mac_reg[RDTR], E1000State),
|
||||
VMSTATE_UINT32(mac_reg[RADV], E1000State),
|
||||
@@ -1457,13 +1458,9 @@ static const VMStateDescription vmstate_e1000 = {
|
||||
VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_e1000_mit_state,
|
||||
.needed = e1000_mit_state_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_e1000_mit_state,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1241,6 +1241,14 @@ static void pcnet_transmit(PCNetState *s)
|
||||
}
|
||||
|
||||
bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
|
||||
|
||||
/* if multi-tmd packet outsizes s->buffer then skip it silently.
|
||||
Note: this is not what real hw does */
|
||||
if (s->xmit_pos + bcnt > sizeof(s->buffer)) {
|
||||
s->xmit_pos = -1;
|
||||
goto txdone;
|
||||
}
|
||||
|
||||
s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
|
||||
s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
|
||||
s->xmit_pos += bcnt;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* QMP Target options - Commands handled based on a target config
|
||||
* versus a host config
|
||||
*
|
||||
* Copyright (c) 2015 David Ahern <dsahern@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
||||
RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
|
||||
{
|
||||
error_set(errp, QERR_FEATURE_DISABLED, "rocker");
|
||||
return NULL;
|
||||
};
|
||||
|
||||
RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
|
||||
{
|
||||
error_set(errp, QERR_FEATURE_DISABLED, "rocker");
|
||||
return NULL;
|
||||
};
|
||||
|
||||
RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
|
||||
bool has_tbl_id,
|
||||
uint32_t tbl_id,
|
||||
Error **errp)
|
||||
{
|
||||
error_set(errp, QERR_FEATURE_DISABLED, "rocker");
|
||||
return NULL;
|
||||
};
|
||||
|
||||
RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
|
||||
bool has_type,
|
||||
uint8_t type,
|
||||
Error **errp)
|
||||
{
|
||||
error_set(errp, QERR_FEATURE_DISABLED, "rocker");
|
||||
return NULL;
|
||||
};
|
||||
+67
-1
@@ -94,6 +94,51 @@ World *rocker_get_world(Rocker *r, enum rocker_world_type type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
|
||||
{
|
||||
RockerSwitch *rocker = g_malloc0(sizeof(*rocker));
|
||||
Rocker *r;
|
||||
|
||||
r = rocker_find(name);
|
||||
if (!r) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"rocker %s not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rocker->name = g_strdup(r->name);
|
||||
rocker->id = r->switch_id;
|
||||
rocker->ports = r->fp_ports;
|
||||
|
||||
return rocker;
|
||||
}
|
||||
|
||||
RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
|
||||
{
|
||||
RockerPortList *list = NULL;
|
||||
Rocker *r;
|
||||
int i;
|
||||
|
||||
r = rocker_find(name);
|
||||
if (!r) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"rocker %s not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = r->fp_ports - 1; i >= 0; i--) {
|
||||
RockerPortList *info = g_malloc0(sizeof(*info));
|
||||
info->value = g_malloc0(sizeof(*info->value));
|
||||
struct fp_port *port = r->fp_port[i];
|
||||
|
||||
fp_port_get_info(port, info);
|
||||
info->next = list;
|
||||
list = info;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
uint32_t rocker_fp_ports(Rocker *r)
|
||||
{
|
||||
return r->fp_ports;
|
||||
@@ -238,6 +283,7 @@ static int cmd_get_port_settings(Rocker *r,
|
||||
uint8_t duplex;
|
||||
uint8_t autoneg;
|
||||
uint8_t learning;
|
||||
char *phys_name;
|
||||
MACAddr macaddr;
|
||||
enum rocker_world_type mode;
|
||||
size_t tlv_size;
|
||||
@@ -265,6 +311,7 @@ static int cmd_get_port_settings(Rocker *r,
|
||||
fp_port_get_macaddr(fp_port, &macaddr);
|
||||
mode = world_type(fp_port_get_world(fp_port));
|
||||
learning = fp_port_get_learning(fp_port);
|
||||
phys_name = fp_port_get_name(fp_port);
|
||||
|
||||
tlv_size = rocker_tlv_total_size(0) + /* nest */
|
||||
rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */
|
||||
@@ -273,7 +320,8 @@ static int cmd_get_port_settings(Rocker *r,
|
||||
rocker_tlv_total_size(sizeof(uint8_t)) + /* autoneg */
|
||||
rocker_tlv_total_size(sizeof(macaddr.a)) + /* macaddr */
|
||||
rocker_tlv_total_size(sizeof(uint8_t)) + /* mode */
|
||||
rocker_tlv_total_size(sizeof(uint8_t)); /* learning */
|
||||
rocker_tlv_total_size(sizeof(uint8_t)) + /* learning */
|
||||
rocker_tlv_total_size(strlen(phys_name));
|
||||
|
||||
if (tlv_size > desc_buf_size(info)) {
|
||||
return -ROCKER_EMSGSIZE;
|
||||
@@ -290,6 +338,8 @@ static int cmd_get_port_settings(Rocker *r,
|
||||
rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode);
|
||||
rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
|
||||
learning);
|
||||
rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME,
|
||||
strlen(phys_name), phys_name);
|
||||
rocker_tlv_nest_end(buf, &pos, nest);
|
||||
|
||||
return desc_set_buf(info, tlv_size);
|
||||
@@ -1277,6 +1327,22 @@ static int pci_rocker_init(PCIDevice *dev)
|
||||
goto err_duplicate;
|
||||
}
|
||||
|
||||
/* Rocker name is passed in port name requests to OS with the intention
|
||||
* that the name is used in interface names. Limit the length of the
|
||||
* rocker name to avoid naming problems in the OS. Also, adding the
|
||||
* port number as p# and unganged breakout b#, where # is at most 2
|
||||
* digits, so leave room for it too (-1 for string terminator, -3 for
|
||||
* p# and -3 for b#)
|
||||
*/
|
||||
#define ROCKER_IFNAMSIZ 16
|
||||
#define MAX_ROCKER_NAME_LEN (ROCKER_IFNAMSIZ - 1 - 3 - 3)
|
||||
if (strlen(r->name) > MAX_ROCKER_NAME_LEN) {
|
||||
fprintf(stderr,
|
||||
"rocker: name too long; please shorten to at most %d chars\n",
|
||||
MAX_ROCKER_NAME_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) {
|
||||
memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt));
|
||||
r->fp_start_macaddr.a[4] += (sw_index++);
|
||||
|
||||
@@ -41,11 +41,26 @@ struct fp_port {
|
||||
NICConf conf;
|
||||
};
|
||||
|
||||
char *fp_port_get_name(FpPort *port)
|
||||
{
|
||||
return port->name;
|
||||
}
|
||||
|
||||
bool fp_port_get_link_up(FpPort *port)
|
||||
{
|
||||
return !qemu_get_queue(port->nic)->link_down;
|
||||
}
|
||||
|
||||
void fp_port_get_info(FpPort *port, RockerPortList *info)
|
||||
{
|
||||
info->value->name = g_strdup(port->name);
|
||||
info->value->enabled = port->enabled;
|
||||
info->value->link_up = fp_port_get_link_up(port);
|
||||
info->value->speed = port->speed;
|
||||
info->value->duplex = port->duplex;
|
||||
info->value->autoneg = port->autoneg;
|
||||
}
|
||||
|
||||
void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
|
||||
{
|
||||
memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
|
||||
@@ -173,8 +188,19 @@ bool fp_port_enabled(FpPort *port)
|
||||
return port->enabled;
|
||||
}
|
||||
|
||||
static void fp_port_set_link(FpPort *port, bool up)
|
||||
{
|
||||
NetClientState *nc = qemu_get_queue(port->nic);
|
||||
|
||||
if (up == nc->link_down) {
|
||||
nc->link_down = !up;
|
||||
nc->info->link_status_changed(nc);
|
||||
}
|
||||
}
|
||||
|
||||
void fp_port_enable(FpPort *port)
|
||||
{
|
||||
fp_port_set_link(port, true);
|
||||
port->enabled = true;
|
||||
DPRINTF("port %d enabled\n", port->index);
|
||||
}
|
||||
@@ -182,6 +208,7 @@ void fp_port_enable(FpPort *port)
|
||||
void fp_port_disable(FpPort *port)
|
||||
{
|
||||
port->enabled = false;
|
||||
fp_port_set_link(port, false);
|
||||
DPRINTF("port %d disabled\n", port->index);
|
||||
}
|
||||
|
||||
@@ -201,7 +228,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name,
|
||||
|
||||
/* front-panel switch port names are 1-based */
|
||||
|
||||
port->name = g_strdup_printf("%s.%d", sw_name, port->pport);
|
||||
port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
|
||||
|
||||
memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
|
||||
port->conf.macaddr.a[5] += index;
|
||||
|
||||
@@ -26,7 +26,9 @@ typedef struct fp_port FpPort;
|
||||
|
||||
int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt);
|
||||
|
||||
char *fp_port_get_name(FpPort *port);
|
||||
bool fp_port_get_link_up(FpPort *port);
|
||||
void fp_port_get_info(FpPort *port, RockerPortList *info);
|
||||
void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr);
|
||||
void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr);
|
||||
uint8_t fp_port_get_learning(FpPort *port);
|
||||
|
||||
@@ -179,6 +179,7 @@ enum {
|
||||
ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */
|
||||
ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */
|
||||
ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, /* u8 */
|
||||
ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, /* binary */
|
||||
|
||||
__ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
|
||||
ROCKER_TLV_CMD_PORT_SETTINGS_MAX = __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1,
|
||||
|
||||
@@ -2302,6 +2302,318 @@ static void of_dpa_uninit(World *world)
|
||||
g_hash_table_destroy(of_dpa->flow_tbl);
|
||||
}
|
||||
|
||||
struct of_dpa_flow_fill_context {
|
||||
RockerOfDpaFlowList *list;
|
||||
uint32_t tbl_id;
|
||||
};
|
||||
|
||||
static void of_dpa_flow_fill(void *cookie, void *value, void *user_data)
|
||||
{
|
||||
struct of_dpa_flow *flow = value;
|
||||
struct of_dpa_flow_key *key = &flow->key;
|
||||
struct of_dpa_flow_key *mask = &flow->mask;
|
||||
struct of_dpa_flow_fill_context *flow_context = user_data;
|
||||
RockerOfDpaFlowList *new;
|
||||
RockerOfDpaFlow *nflow;
|
||||
RockerOfDpaFlowKey *nkey;
|
||||
RockerOfDpaFlowMask *nmask;
|
||||
RockerOfDpaFlowAction *naction;
|
||||
|
||||
if (flow_context->tbl_id != -1 &&
|
||||
flow_context->tbl_id != key->tbl_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
new = g_malloc0(sizeof(*new));
|
||||
nflow = new->value = g_malloc0(sizeof(*nflow));
|
||||
nkey = nflow->key = g_malloc0(sizeof(*nkey));
|
||||
nmask = nflow->mask = g_malloc0(sizeof(*nmask));
|
||||
naction = nflow->action = g_malloc0(sizeof(*naction));
|
||||
|
||||
nflow->cookie = flow->cookie;
|
||||
nflow->hits = flow->stats.hits;
|
||||
nkey->priority = flow->priority;
|
||||
nkey->tbl_id = key->tbl_id;
|
||||
|
||||
if (key->in_pport || mask->in_pport) {
|
||||
nkey->has_in_pport = true;
|
||||
nkey->in_pport = key->in_pport;
|
||||
}
|
||||
|
||||
if (nkey->has_in_pport && mask->in_pport != 0xffffffff) {
|
||||
nmask->has_in_pport = true;
|
||||
nmask->in_pport = mask->in_pport;
|
||||
}
|
||||
|
||||
if (key->eth.vlan_id || mask->eth.vlan_id) {
|
||||
nkey->has_vlan_id = true;
|
||||
nkey->vlan_id = ntohs(key->eth.vlan_id);
|
||||
}
|
||||
|
||||
if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) {
|
||||
nmask->has_vlan_id = true;
|
||||
nmask->vlan_id = ntohs(mask->eth.vlan_id);
|
||||
}
|
||||
|
||||
if (key->tunnel_id || mask->tunnel_id) {
|
||||
nkey->has_tunnel_id = true;
|
||||
nkey->tunnel_id = key->tunnel_id;
|
||||
}
|
||||
|
||||
if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) {
|
||||
nmask->has_tunnel_id = true;
|
||||
nmask->tunnel_id = mask->tunnel_id;
|
||||
}
|
||||
|
||||
if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) ||
|
||||
memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) {
|
||||
nkey->has_eth_src = true;
|
||||
nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a);
|
||||
}
|
||||
|
||||
if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) {
|
||||
nmask->has_eth_src = true;
|
||||
nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a);
|
||||
}
|
||||
|
||||
if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) ||
|
||||
memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) {
|
||||
nkey->has_eth_dst = true;
|
||||
nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a);
|
||||
}
|
||||
|
||||
if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) {
|
||||
nmask->has_eth_dst = true;
|
||||
nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a);
|
||||
}
|
||||
|
||||
if (key->eth.type) {
|
||||
|
||||
nkey->has_eth_type = true;
|
||||
nkey->eth_type = ntohs(key->eth.type);
|
||||
|
||||
switch (ntohs(key->eth.type)) {
|
||||
case 0x0800:
|
||||
case 0x86dd:
|
||||
if (key->ip.proto || mask->ip.proto) {
|
||||
nkey->has_ip_proto = true;
|
||||
nkey->ip_proto = key->ip.proto;
|
||||
}
|
||||
if (nkey->has_ip_proto && mask->ip.proto != 0xff) {
|
||||
nmask->has_ip_proto = true;
|
||||
nmask->ip_proto = mask->ip.proto;
|
||||
}
|
||||
if (key->ip.tos || mask->ip.tos) {
|
||||
nkey->has_ip_tos = true;
|
||||
nkey->ip_tos = key->ip.tos;
|
||||
}
|
||||
if (nkey->has_ip_tos && mask->ip.tos != 0xff) {
|
||||
nmask->has_ip_tos = true;
|
||||
nmask->ip_tos = mask->ip.tos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ntohs(key->eth.type)) {
|
||||
case 0x0800:
|
||||
if (key->ipv4.addr.dst || mask->ipv4.addr.dst) {
|
||||
char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst);
|
||||
int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst);
|
||||
nkey->has_ip_dst = true;
|
||||
nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flow->action.goto_tbl) {
|
||||
naction->has_goto_tbl = true;
|
||||
naction->goto_tbl = flow->action.goto_tbl;
|
||||
}
|
||||
|
||||
if (flow->action.write.group_id) {
|
||||
naction->has_group_id = true;
|
||||
naction->group_id = flow->action.write.group_id;
|
||||
}
|
||||
|
||||
if (flow->action.apply.new_vlan_id) {
|
||||
naction->has_new_vlan_id = true;
|
||||
naction->new_vlan_id = flow->action.apply.new_vlan_id;
|
||||
}
|
||||
|
||||
new->next = flow_context->list;
|
||||
flow_context->list = new;
|
||||
}
|
||||
|
||||
RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
|
||||
bool has_tbl_id,
|
||||
uint32_t tbl_id,
|
||||
Error **errp)
|
||||
{
|
||||
struct rocker *r;
|
||||
struct world *w;
|
||||
struct of_dpa *of_dpa;
|
||||
struct of_dpa_flow_fill_context fill_context = {
|
||||
.list = NULL,
|
||||
.tbl_id = tbl_id,
|
||||
};
|
||||
|
||||
r = rocker_find(name);
|
||||
if (!r) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"rocker %s not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
|
||||
if (!w) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"rocker %s doesn't have OF-DPA world", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
of_dpa = world_private(w);
|
||||
|
||||
g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context);
|
||||
|
||||
return fill_context.list;
|
||||
}
|
||||
|
||||
struct of_dpa_group_fill_context {
|
||||
RockerOfDpaGroupList *list;
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
static void of_dpa_group_fill(void *key, void *value, void *user_data)
|
||||
{
|
||||
struct of_dpa_group *group = value;
|
||||
struct of_dpa_group_fill_context *flow_context = user_data;
|
||||
RockerOfDpaGroupList *new;
|
||||
RockerOfDpaGroup *ngroup;
|
||||
struct uint32List *id;
|
||||
int i;
|
||||
|
||||
if (flow_context->type != 9 &&
|
||||
flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
new = g_malloc0(sizeof(*new));
|
||||
ngroup = new->value = g_malloc0(sizeof(*ngroup));
|
||||
|
||||
ngroup->id = group->id;
|
||||
|
||||
ngroup->type = ROCKER_GROUP_TYPE_GET(group->id);
|
||||
|
||||
switch (ngroup->type) {
|
||||
case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
|
||||
ngroup->has_vlan_id = true;
|
||||
ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
|
||||
ngroup->has_pport = true;
|
||||
ngroup->pport = ROCKER_GROUP_PORT_GET(group->id);
|
||||
ngroup->has_out_pport = true;
|
||||
ngroup->out_pport = group->l2_interface.out_pport;
|
||||
ngroup->has_pop_vlan = true;
|
||||
ngroup->pop_vlan = group->l2_interface.pop_vlan;
|
||||
break;
|
||||
case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
|
||||
ngroup->has_index = true;
|
||||
ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
|
||||
ngroup->has_group_id = true;
|
||||
ngroup->group_id = group->l2_rewrite.group_id;
|
||||
if (group->l2_rewrite.vlan_id) {
|
||||
ngroup->has_set_vlan_id = true;
|
||||
ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id);
|
||||
}
|
||||
break;
|
||||
if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) {
|
||||
ngroup->has_set_eth_src = true;
|
||||
ngroup->set_eth_src =
|
||||
qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a);
|
||||
}
|
||||
if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) {
|
||||
ngroup->has_set_eth_dst = true;
|
||||
ngroup->set_eth_dst =
|
||||
qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a);
|
||||
}
|
||||
case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
|
||||
case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
|
||||
ngroup->has_vlan_id = true;
|
||||
ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
|
||||
ngroup->has_index = true;
|
||||
ngroup->index = ROCKER_GROUP_INDEX_GET(group->id);
|
||||
for (i = 0; i < group->l2_flood.group_count; i++) {
|
||||
ngroup->has_group_ids = true;
|
||||
id = g_malloc0(sizeof(*id));
|
||||
id->value = group->l2_flood.group_ids[i];
|
||||
id->next = ngroup->group_ids;
|
||||
ngroup->group_ids = id;
|
||||
}
|
||||
break;
|
||||
case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
|
||||
ngroup->has_index = true;
|
||||
ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
|
||||
ngroup->has_group_id = true;
|
||||
ngroup->group_id = group->l3_unicast.group_id;
|
||||
if (group->l3_unicast.vlan_id) {
|
||||
ngroup->has_set_vlan_id = true;
|
||||
ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id);
|
||||
}
|
||||
if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) {
|
||||
ngroup->has_set_eth_src = true;
|
||||
ngroup->set_eth_src =
|
||||
qemu_mac_strdup_printf(group->l3_unicast.src_mac.a);
|
||||
}
|
||||
if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) {
|
||||
ngroup->has_set_eth_dst = true;
|
||||
ngroup->set_eth_dst =
|
||||
qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a);
|
||||
}
|
||||
if (group->l3_unicast.ttl_check) {
|
||||
ngroup->has_ttl_check = true;
|
||||
ngroup->ttl_check = group->l3_unicast.ttl_check;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
new->next = flow_context->list;
|
||||
flow_context->list = new;
|
||||
}
|
||||
|
||||
RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
|
||||
bool has_type,
|
||||
uint8_t type,
|
||||
Error **errp)
|
||||
{
|
||||
struct rocker *r;
|
||||
struct world *w;
|
||||
struct of_dpa *of_dpa;
|
||||
struct of_dpa_group_fill_context fill_context = {
|
||||
.list = NULL,
|
||||
.type = type,
|
||||
};
|
||||
|
||||
r = rocker_find(name);
|
||||
if (!r) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"rocker %s not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
|
||||
if (!w) {
|
||||
error_set(errp, ERROR_CLASS_GENERIC_ERROR,
|
||||
"rocker %s doesn't have OF-DPA world", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
of_dpa = world_private(w);
|
||||
|
||||
g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context);
|
||||
|
||||
return fill_context.list;
|
||||
}
|
||||
|
||||
static WorldOps of_dpa_ops = {
|
||||
.init = of_dpa_init,
|
||||
.uninit = of_dpa_uninit,
|
||||
|
||||
+4
-7
@@ -3240,6 +3240,7 @@ static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
|
||||
.name = "rtl8139/hotplug_ready",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = rtl8139_hotplug_ready_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
@@ -3335,13 +3336,9 @@ static const VMStateDescription vmstate_rtl8139 = {
|
||||
VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_rtl8139_hotplug_ready,
|
||||
.needed = rtl8139_hotplug_ready_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_rtl8139_hotplug_ready,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+8
-6
@@ -52,6 +52,7 @@ static const int kernel_feature_bits[] = {
|
||||
VIRTIO_RING_F_INDIRECT_DESC,
|
||||
VIRTIO_RING_F_EVENT_IDX,
|
||||
VIRTIO_NET_F_MRG_RXBUF,
|
||||
VIRTIO_F_VERSION_1,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
||||
@@ -62,6 +63,7 @@ static const int user_feature_bits[] = {
|
||||
VIRTIO_RING_F_EVENT_IDX,
|
||||
|
||||
VIRTIO_F_ANY_LAYOUT,
|
||||
VIRTIO_F_VERSION_1,
|
||||
VIRTIO_NET_F_CSUM,
|
||||
VIRTIO_NET_F_GUEST_CSUM,
|
||||
VIRTIO_NET_F_GSO,
|
||||
@@ -107,13 +109,13 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
|
||||
return feature_bits;
|
||||
}
|
||||
|
||||
unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
|
||||
uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
|
||||
{
|
||||
return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net),
|
||||
features);
|
||||
}
|
||||
|
||||
void vhost_net_ack_features(struct vhost_net *net, unsigned features)
|
||||
void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
|
||||
{
|
||||
net->dev.acked_features = net->dev.backend_features;
|
||||
vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features);
|
||||
@@ -147,7 +149,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
|
||||
goto fail;
|
||||
}
|
||||
net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend)
|
||||
? 0 : (1 << VHOST_NET_F_VIRTIO_NET_HDR);
|
||||
? 0 : (1ULL << VHOST_NET_F_VIRTIO_NET_HDR);
|
||||
net->backend = r;
|
||||
} else {
|
||||
net->dev.backend_features = 0;
|
||||
@@ -167,7 +169,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
|
||||
if (backend_kernel) {
|
||||
if (!qemu_has_vnet_hdr_len(options->net_backend,
|
||||
sizeof(struct virtio_net_hdr_mrg_rxbuf))) {
|
||||
net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
|
||||
net->dev.features &= ~(1ULL << VIRTIO_NET_F_MRG_RXBUF);
|
||||
}
|
||||
if (~net->dev.features & net->dev.backend_features) {
|
||||
fprintf(stderr, "vhost lacks feature mask %" PRIu64
|
||||
@@ -431,11 +433,11 @@ void vhost_net_cleanup(struct vhost_net *net)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
|
||||
uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
|
||||
{
|
||||
return features;
|
||||
}
|
||||
void vhost_net_ack_features(struct vhost_net *net, unsigned features)
|
||||
void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+59
-8
@@ -87,6 +87,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
|
||||
memcpy(&netcfg, config, n->config_size);
|
||||
|
||||
if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
|
||||
!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) &&
|
||||
memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
|
||||
memcpy(n->mac, netcfg.mac, ETH_ALEN);
|
||||
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
|
||||
@@ -366,15 +367,21 @@ static int peer_has_ufo(VirtIONet *n)
|
||||
return n->has_ufo;
|
||||
}
|
||||
|
||||
static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs)
|
||||
static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
|
||||
int version_1)
|
||||
{
|
||||
int i;
|
||||
NetClientState *nc;
|
||||
|
||||
n->mergeable_rx_bufs = mergeable_rx_bufs;
|
||||
|
||||
n->guest_hdr_len = n->mergeable_rx_bufs ?
|
||||
sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
|
||||
if (version_1) {
|
||||
n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
|
||||
} else {
|
||||
n->guest_hdr_len = n->mergeable_rx_bufs ?
|
||||
sizeof(struct virtio_net_hdr_mrg_rxbuf) :
|
||||
sizeof(struct virtio_net_hdr);
|
||||
}
|
||||
|
||||
for (i = 0; i < n->max_queues; i++) {
|
||||
nc = qemu_get_subqueue(n->nic, i);
|
||||
@@ -463,6 +470,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features)
|
||||
}
|
||||
|
||||
if (!get_vhost_net(nc->peer)) {
|
||||
virtio_add_feature(&features, VIRTIO_F_VERSION_1);
|
||||
return features;
|
||||
}
|
||||
return vhost_net_get_features(get_vhost_net(nc->peer), features);
|
||||
@@ -521,7 +529,9 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
|
||||
|
||||
virtio_net_set_mrg_rx_bufs(n,
|
||||
__virtio_has_feature(features,
|
||||
VIRTIO_NET_F_MRG_RXBUF));
|
||||
VIRTIO_NET_F_MRG_RXBUF),
|
||||
__virtio_has_feature(features,
|
||||
VIRTIO_F_VERSION_1));
|
||||
|
||||
if (n->has_vnet_hdr) {
|
||||
n->curr_guest_offloads =
|
||||
@@ -1374,7 +1384,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
|
||||
qemu_get_buffer(f, n->mac, ETH_ALEN);
|
||||
n->vqs[0].tx_waiting = qemu_get_be32(f);
|
||||
|
||||
virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f));
|
||||
virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f),
|
||||
virtio_has_feature(vdev, VIRTIO_F_VERSION_1));
|
||||
|
||||
if (version_id >= 3)
|
||||
n->status = qemu_get_be16(f);
|
||||
@@ -1626,7 +1637,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
n->vqs[0].tx_waiting = 0;
|
||||
n->tx_burst = n->net_conf.txburst;
|
||||
virtio_net_set_mrg_rx_bufs(n, 0);
|
||||
virtio_net_set_mrg_rx_bufs(n, 0, 0);
|
||||
n->promisc = 1; /* for compatibility */
|
||||
|
||||
n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
|
||||
@@ -1696,10 +1707,50 @@ static void virtio_net_instance_init(Object *obj)
|
||||
}
|
||||
|
||||
static Property virtio_net_properties[] = {
|
||||
DEFINE_VIRTIO_NET_FEATURES(VirtIONet, host_features),
|
||||
DEFINE_PROP_BIT("any_layout", VirtIONet, host_features,
|
||||
VIRTIO_F_ANY_LAYOUT, true),
|
||||
DEFINE_PROP_BIT("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true),
|
||||
DEFINE_PROP_BIT("guest_csum", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_CSUM, true),
|
||||
DEFINE_PROP_BIT("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
|
||||
DEFINE_PROP_BIT("guest_tso4", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_TSO4, true),
|
||||
DEFINE_PROP_BIT("guest_tso6", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_TSO6, true),
|
||||
DEFINE_PROP_BIT("guest_ecn", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_ECN, true),
|
||||
DEFINE_PROP_BIT("guest_ufo", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_UFO, true),
|
||||
DEFINE_PROP_BIT("guest_announce", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_ANNOUNCE, true),
|
||||
DEFINE_PROP_BIT("host_tso4", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_TSO4, true),
|
||||
DEFINE_PROP_BIT("host_tso6", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_TSO6, true),
|
||||
DEFINE_PROP_BIT("host_ecn", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_ECN, true),
|
||||
DEFINE_PROP_BIT("host_ufo", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_UFO, true),
|
||||
DEFINE_PROP_BIT("mrg_rxbuf", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_MRG_RXBUF, true),
|
||||
DEFINE_PROP_BIT("status", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_STATUS, true),
|
||||
DEFINE_PROP_BIT("ctrl_vq", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_VQ, true),
|
||||
DEFINE_PROP_BIT("ctrl_rx", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_RX, true),
|
||||
DEFINE_PROP_BIT("ctrl_vlan", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_VLAN, true),
|
||||
DEFINE_PROP_BIT("ctrl_rx_extra", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_RX_EXTRA, true),
|
||||
DEFINE_PROP_BIT("ctrl_mac_addr", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_MAC_ADDR, true),
|
||||
DEFINE_PROP_BIT("ctrl_guest_offloads", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
|
||||
DEFINE_PROP_BIT("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
|
||||
DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
|
||||
DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
|
||||
TX_TIMER_INTERVAL),
|
||||
TX_TIMER_INTERVAL),
|
||||
DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
|
||||
DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
|
||||
+4
-8
@@ -2226,6 +2226,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = {
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_load = vmxnet3_mcast_list_pre_load,
|
||||
.needed = vmxnet3_mc_list_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0,
|
||||
mcast_list_buff_size),
|
||||
@@ -2470,14 +2471,9 @@ static const VMStateDescription vmstate_vmxnet3 = {
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmxstate_vmxnet3_mcast_list,
|
||||
.needed = vmxnet3_mc_list_needed
|
||||
},
|
||||
{
|
||||
/* empty element. */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmxstate_vmxnet3_mcast_list,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+18
-37
@@ -46,7 +46,6 @@ typedef struct FWCfgEntry {
|
||||
uint32_t len;
|
||||
uint8_t *data;
|
||||
void *callback_opaque;
|
||||
FWCfgCallback callback;
|
||||
FWCfgReadCallback read_callback;
|
||||
} FWCfgEntry;
|
||||
|
||||
@@ -232,19 +231,7 @@ static void fw_cfg_reboot(FWCfgState *s)
|
||||
|
||||
static void fw_cfg_write(FWCfgState *s, uint8_t value)
|
||||
{
|
||||
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
|
||||
FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
|
||||
|
||||
trace_fw_cfg_write(s, value);
|
||||
|
||||
if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
|
||||
s->cur_offset < e->len) {
|
||||
e->data[s->cur_offset++] = value;
|
||||
if (s->cur_offset == e->len) {
|
||||
e->callback(e->callback_opaque, e->data);
|
||||
s->cur_offset = 0;
|
||||
}
|
||||
}
|
||||
/* nothing, write support removed in QEMU v2.4+ */
|
||||
}
|
||||
|
||||
static int fw_cfg_select(FWCfgState *s, uint16_t key)
|
||||
@@ -436,6 +423,7 @@ static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key,
|
||||
key &= FW_CFG_ENTRY_MASK;
|
||||
|
||||
assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
|
||||
assert(s->entries[arch][key].data == NULL); /* avoid key conflict */
|
||||
|
||||
s->entries[arch][key].data = data;
|
||||
s->entries[arch][key].len = (uint32_t)len;
|
||||
@@ -458,7 +446,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key,
|
||||
s->entries[arch][key].data = data;
|
||||
s->entries[arch][key].len = len;
|
||||
s->entries[arch][key].callback_opaque = NULL;
|
||||
s->entries[arch][key].callback = NULL;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
@@ -484,6 +471,16 @@ void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
|
||||
fw_cfg_add_bytes(s, key, copy, sizeof(value));
|
||||
}
|
||||
|
||||
void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value)
|
||||
{
|
||||
uint16_t *copy, *old;
|
||||
|
||||
copy = g_malloc(sizeof(value));
|
||||
*copy = cpu_to_le16(value);
|
||||
old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
|
||||
g_free(old);
|
||||
}
|
||||
|
||||
void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
|
||||
{
|
||||
uint32_t *copy;
|
||||
@@ -502,23 +499,6 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
|
||||
fw_cfg_add_bytes(s, key, copy, sizeof(value));
|
||||
}
|
||||
|
||||
void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
|
||||
void *callback_opaque, void *data, size_t len)
|
||||
{
|
||||
int arch = !!(key & FW_CFG_ARCH_LOCAL);
|
||||
|
||||
assert(key & FW_CFG_WRITE_CHANNEL);
|
||||
|
||||
key &= FW_CFG_ENTRY_MASK;
|
||||
|
||||
assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
|
||||
|
||||
s->entries[arch][key].data = data;
|
||||
s->entries[arch][key].len = (uint32_t)len;
|
||||
s->entries[arch][key].callback_opaque = callback_opaque;
|
||||
s->entries[arch][key].callback = callback;
|
||||
}
|
||||
|
||||
void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
|
||||
FWCfgReadCallback callback, void *callback_opaque,
|
||||
void *data, size_t len)
|
||||
@@ -535,18 +515,19 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
|
||||
index = be32_to_cpu(s->files->count);
|
||||
assert(index < FW_CFG_FILE_SLOTS);
|
||||
|
||||
fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index,
|
||||
callback, callback_opaque, data, len);
|
||||
|
||||
pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
|
||||
filename);
|
||||
for (i = 0; i < index; i++) {
|
||||
if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
|
||||
trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
|
||||
return;
|
||||
error_report("duplicate fw_cfg file name: %s",
|
||||
s->files->f[index].name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index,
|
||||
callback, callback_opaque, data, len);
|
||||
|
||||
s->files->f[index].size = cpu_to_be32(len);
|
||||
s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
|
||||
trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
|
||||
|
||||
+4
-6
@@ -582,6 +582,7 @@ static const VMStateDescription vmstate_piix3_rcr = {
|
||||
.name = "PIIX3/rcr",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = piix3_rcr_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(rcr, PIIX3State),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -600,12 +601,9 @@ static const VMStateDescription vmstate_piix3 = {
|
||||
PIIX_NUM_PIRQS, 3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_piix3_rcr,
|
||||
.needed = piix3_rcr_needed,
|
||||
},
|
||||
{ 0 }
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_piix3_rcr,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ static const MemoryRegionOps unin_ops = {
|
||||
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
|
||||
Error **errp)
|
||||
{
|
||||
fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
}
|
||||
|
||||
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
|
||||
Error **errp)
|
||||
{
|
||||
fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
}
|
||||
|
||||
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
|
||||
|
||||
+12
-8
@@ -498,15 +498,19 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
virtio_ccw_stop_ioeventfd(dev);
|
||||
}
|
||||
virtio_set_status(vdev, status);
|
||||
if (vdev->status == 0) {
|
||||
virtio_reset(vdev);
|
||||
if (virtio_set_status(vdev, status) == 0) {
|
||||
if (vdev->status == 0) {
|
||||
virtio_reset(vdev);
|
||||
}
|
||||
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||
virtio_ccw_start_ioeventfd(dev);
|
||||
}
|
||||
sch->curr_status.scsw.count = ccw.count - sizeof(status);
|
||||
ret = 0;
|
||||
} else {
|
||||
/* Trigger a command reject. */
|
||||
ret = -ENOSYS;
|
||||
}
|
||||
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||
virtio_ccw_start_ioeventfd(dev);
|
||||
}
|
||||
sch->curr_status.scsw.count = ccw.count - sizeof(status);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
case CCW_CMD_SET_IND:
|
||||
|
||||
+4
-7
@@ -1968,6 +1968,7 @@ static const VMStateDescription vmstate_scsi_sense_state = {
|
||||
.name = "SCSIDevice/sense",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = scsi_sense_state_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice,
|
||||
SCSI_SENSE_BUF_SIZE_OLD,
|
||||
@@ -1998,13 +1999,9 @@ const VMStateDescription vmstate_scsi_device = {
|
||||
},
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_scsi_sense_state,
|
||||
.needed = scsi_sense_state_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_scsi_sense_state,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -294,7 +294,14 @@ static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus,
|
||||
}
|
||||
|
||||
static Property vhost_scsi_properties[] = {
|
||||
DEFINE_VHOST_SCSI_PROPERTIES(VHostSCSI, parent_obj.conf),
|
||||
DEFINE_PROP_STRING("vhostfd", VHostSCSI, parent_obj.conf.vhostfd),
|
||||
DEFINE_PROP_STRING("wwpn", VHostSCSI, parent_obj.conf.wwpn),
|
||||
DEFINE_PROP_UINT32("boot_tpgt", VHostSCSI, parent_obj.conf.boot_tpgt, 0),
|
||||
DEFINE_PROP_UINT32("num_queues", VHostSCSI, parent_obj.conf.num_queues, 1),
|
||||
DEFINE_PROP_UINT32("max_sectors", VHostSCSI, parent_obj.conf.max_sectors,
|
||||
0xFFFF),
|
||||
DEFINE_PROP_UINT32("cmd_per_lun", VHostSCSI, parent_obj.conf.cmd_per_lun,
|
||||
128),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
+11
-2
@@ -948,8 +948,17 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
static Property virtio_scsi_properties[] = {
|
||||
DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSI, parent_obj.conf),
|
||||
DEFINE_VIRTIO_SCSI_FEATURES(VirtIOSCSI, host_features),
|
||||
DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1),
|
||||
DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors,
|
||||
0xFFFF),
|
||||
DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSI, parent_obj.conf.cmd_per_lun,
|
||||
128),
|
||||
DEFINE_PROP_BIT("any_layout", VirtIOSCSI, host_features,
|
||||
VIRTIO_F_ANY_LAYOUT, true),
|
||||
DEFINE_PROP_BIT("hotplug", VirtIOSCSI, host_features,
|
||||
VIRTIO_SCSI_F_HOTPLUG, true),
|
||||
DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features,
|
||||
VIRTIO_SCSI_F_CHANGE, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
+6
-6
@@ -127,7 +127,7 @@ static void r2d_fpga_irq_set(void *opaque, int n, int level)
|
||||
update_irl(fpga);
|
||||
}
|
||||
|
||||
static uint32_t r2d_fpga_read(void *opaque, hwaddr addr)
|
||||
static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
r2d_fpga_t *s = opaque;
|
||||
|
||||
@@ -146,7 +146,7 @@ static uint32_t r2d_fpga_read(void *opaque, hwaddr addr)
|
||||
}
|
||||
|
||||
static void
|
||||
r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value)
|
||||
r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size)
|
||||
{
|
||||
r2d_fpga_t *s = opaque;
|
||||
|
||||
@@ -170,10 +170,10 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value)
|
||||
}
|
||||
|
||||
static const MemoryRegionOps r2d_fpga_ops = {
|
||||
.old_mmio = {
|
||||
.read = { r2d_fpga_read, r2d_fpga_read, NULL, },
|
||||
.write = { r2d_fpga_write, r2d_fpga_write, NULL, },
|
||||
},
|
||||
.read = r2d_fpga_read,
|
||||
.write = r2d_fpga_write,
|
||||
.impl.min_access_size = 2,
|
||||
.impl.max_access_size = 2,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ void DMA_register_channel (int nchan,
|
||||
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
|
||||
Error **errp)
|
||||
{
|
||||
fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
}
|
||||
|
||||
static void nvram_init(Nvram *nvram, uint8_t *macaddr,
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ void DMA_register_channel (int nchan,
|
||||
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
|
||||
Error **errp)
|
||||
{
|
||||
fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
|
||||
}
|
||||
|
||||
static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size,
|
||||
|
||||
+4
-7
@@ -283,6 +283,7 @@ static const VMStateDescription vmstate_hpet_rtc_irq_level = {
|
||||
.name = "hpet/rtc_irq_level",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = hpet_rtc_irq_level_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(rtc_irq_level, HPETState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -322,13 +323,9 @@ static const VMStateDescription vmstate_hpet = {
|
||||
vmstate_hpet_timer, HPETTimer),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_hpet_rtc_irq_level,
|
||||
.needed = hpet_rtc_irq_level_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_hpet_rtc_irq_level,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+14
-17
@@ -733,22 +733,23 @@ static int rtc_post_load(void *opaque, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = {
|
||||
.name = "mc146818rtc/irq_reinject_on_ack_count",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static bool rtc_irq_reinject_on_ack_count_needed(void *opaque)
|
||||
{
|
||||
RTCState *s = (RTCState *)opaque;
|
||||
return s->irq_reinject_on_ack_count != 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = {
|
||||
.name = "mc146818rtc/irq_reinject_on_ack_count",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = rtc_irq_reinject_on_ack_count_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_rtc = {
|
||||
.name = "mc146818rtc",
|
||||
.version_id = 3,
|
||||
@@ -770,13 +771,9 @@ static const VMStateDescription vmstate_rtc = {
|
||||
VMSTATE_UINT64_V(next_alarm_time, RTCState, 3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_rtc_irq_reinject_on_ack_count,
|
||||
.needed = rtc_irq_reinject_on_ack_count_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_rtc_irq_reinject_on_ack_count,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+4
-7
@@ -2034,6 +2034,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = {
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.pre_load = ohci_eof_timer_pre_load,
|
||||
.needed = ohci_eof_timer_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_TIMER_PTR(eof_timer, OHCIState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -2081,13 +2082,9 @@ static const VMStateDescription vmstate_ohci_state = {
|
||||
VMSTATE_BOOL(async_complete, OHCIState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection []) {
|
||||
{
|
||||
.vmsd = &vmstate_ohci_eof_timer,
|
||||
.needed = ohci_eof_timer_needed,
|
||||
} , {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_ohci_eof_timer,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+19
-23
@@ -2257,16 +2257,6 @@ static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
|
||||
|
||||
|
||||
/* For endp_data migration */
|
||||
static const VMStateDescription usbredir_bulk_receiving_vmstate = {
|
||||
.name = "usb-redir-ep/bulk-receiving",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(bulk_receiving_started, struct endp_data),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static bool usbredir_bulk_receiving_needed(void *priv)
|
||||
{
|
||||
struct endp_data *endp = priv;
|
||||
@@ -2274,12 +2264,13 @@ static bool usbredir_bulk_receiving_needed(void *priv)
|
||||
return endp->bulk_receiving_started;
|
||||
}
|
||||
|
||||
static const VMStateDescription usbredir_stream_vmstate = {
|
||||
.name = "usb-redir-ep/stream-state",
|
||||
static const VMStateDescription usbredir_bulk_receiving_vmstate = {
|
||||
.name = "usb-redir-ep/bulk-receiving",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = usbredir_bulk_receiving_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(max_streams, struct endp_data),
|
||||
VMSTATE_UINT8(bulk_receiving_started, struct endp_data),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
@@ -2291,6 +2282,17 @@ static bool usbredir_stream_needed(void *priv)
|
||||
return endp->max_streams;
|
||||
}
|
||||
|
||||
static const VMStateDescription usbredir_stream_vmstate = {
|
||||
.name = "usb-redir-ep/stream-state",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = usbredir_stream_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(max_streams, struct endp_data),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription usbredir_ep_vmstate = {
|
||||
.name = "usb-redir-ep",
|
||||
.version_id = 1,
|
||||
@@ -2318,16 +2320,10 @@ static const VMStateDescription usbredir_ep_vmstate = {
|
||||
VMSTATE_INT32(bufpq_target_size, struct endp_data),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &usbredir_bulk_receiving_vmstate,
|
||||
.needed = usbredir_bulk_receiving_needed,
|
||||
}, {
|
||||
.vmsd = &usbredir_stream_vmstate,
|
||||
.needed = usbredir_stream_needed,
|
||||
}, {
|
||||
/* empty */
|
||||
}
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&usbredir_bulk_receiving_vmstate,
|
||||
&usbredir_stream_vmstate,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
ifeq ($(CONFIG_LINUX), y)
|
||||
obj-$(CONFIG_SOFTMMU) += common.o
|
||||
obj-$(CONFIG_PCI) += pci.o
|
||||
obj-$(CONFIG_SOFTMMU) += platform.o
|
||||
obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* calxeda xgmac VFIO device
|
||||
*
|
||||
* Copyright Linaro Limited, 2014
|
||||
*
|
||||
* Authors:
|
||||
* Eric Auger <eric.auger@linaro.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/vfio/vfio-calxeda-xgmac.h"
|
||||
|
||||
static void calxeda_xgmac_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
|
||||
VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev);
|
||||
|
||||
vdev->compat = g_strdup("calxeda,hb-xgmac");
|
||||
|
||||
k->parent_realize(dev, errp);
|
||||
}
|
||||
|
||||
static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = {
|
||||
.name = TYPE_VFIO_CALXEDA_XGMAC,
|
||||
.unmigratable = 1,
|
||||
};
|
||||
|
||||
static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VFIOCalxedaXgmacDeviceClass *vcxc =
|
||||
VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass);
|
||||
vcxc->parent_realize = dc->realize;
|
||||
dc->realize = calxeda_xgmac_realize;
|
||||
dc->desc = "VFIO Calxeda XGMAC";
|
||||
dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate;
|
||||
}
|
||||
|
||||
static const TypeInfo vfio_calxeda_xgmac_dev_info = {
|
||||
.name = TYPE_VFIO_CALXEDA_XGMAC,
|
||||
.parent = TYPE_VFIO_PLATFORM,
|
||||
.instance_size = sizeof(VFIOCalxedaXgmacDevice),
|
||||
.class_init = vfio_calxeda_xgmac_class_init,
|
||||
.class_size = sizeof(VFIOCalxedaXgmacDeviceClass),
|
||||
};
|
||||
|
||||
static void register_calxeda_xgmac_dev_type(void)
|
||||
{
|
||||
type_register_static(&vfio_calxeda_xgmac_dev_info);
|
||||
}
|
||||
|
||||
type_init(register_calxeda_xgmac_dev_type)
|
||||
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* vfio based device assignment support - platform devices
|
||||
*
|
||||
* Copyright Linaro Limited, 2014
|
||||
*
|
||||
* Authors:
|
||||
* Kim Phillips <kim.phillips@linaro.org>
|
||||
* Eric Auger <eric.auger@linaro.org>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
* Based on vfio based PCI device assignment support:
|
||||
* Copyright Red Hat, Inc. 2012
|
||||
*/
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "hw/vfio/vfio-platform.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/range.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/memory.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "trace.h"
|
||||
#include "hw/platform-bus.h"
|
||||
|
||||
/*
|
||||
* Functions used whatever the injection method
|
||||
*/
|
||||
|
||||
/**
|
||||
* vfio_init_intp - allocate, initialize the IRQ struct pointer
|
||||
* and add it into the list of IRQs
|
||||
* @vbasedev: the VFIO device handle
|
||||
* @info: irq info struct retrieved from VFIO driver
|
||||
*/
|
||||
static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
|
||||
struct vfio_irq_info info)
|
||||
{
|
||||
int ret;
|
||||
VFIOPlatformDevice *vdev =
|
||||
container_of(vbasedev, VFIOPlatformDevice, vbasedev);
|
||||
SysBusDevice *sbdev = SYS_BUS_DEVICE(vdev);
|
||||
VFIOINTp *intp;
|
||||
|
||||
intp = g_malloc0(sizeof(*intp));
|
||||
intp->vdev = vdev;
|
||||
intp->pin = info.index;
|
||||
intp->flags = info.flags;
|
||||
intp->state = VFIO_IRQ_INACTIVE;
|
||||
|
||||
sysbus_init_irq(sbdev, &intp->qemuirq);
|
||||
|
||||
/* Get an eventfd for trigger */
|
||||
ret = event_notifier_init(&intp->interrupt, 0);
|
||||
if (ret) {
|
||||
g_free(intp);
|
||||
error_report("vfio: Error: trigger event_notifier_init failed ");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD(&vdev->intp_list, intp, next);
|
||||
return intp;
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_set_trigger_eventfd - set VFIO eventfd handling
|
||||
*
|
||||
* @intp: IRQ struct handle
|
||||
* @handler: handler to be called on eventfd signaling
|
||||
*
|
||||
* Setup VFIO signaling and attach an optional user-side handler
|
||||
* to the eventfd
|
||||
*/
|
||||
static int vfio_set_trigger_eventfd(VFIOINTp *intp,
|
||||
eventfd_user_side_handler_t handler)
|
||||
{
|
||||
VFIODevice *vbasedev = &intp->vdev->vbasedev;
|
||||
struct vfio_irq_set *irq_set;
|
||||
int argsz, ret;
|
||||
int32_t *pfd;
|
||||
|
||||
argsz = sizeof(*irq_set) + sizeof(*pfd);
|
||||
irq_set = g_malloc0(argsz);
|
||||
irq_set->argsz = argsz;
|
||||
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irq_set->index = intp->pin;
|
||||
irq_set->start = 0;
|
||||
irq_set->count = 1;
|
||||
pfd = (int32_t *)&irq_set->data;
|
||||
*pfd = event_notifier_get_fd(&intp->interrupt);
|
||||
qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp);
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
||||
g_free(irq_set);
|
||||
if (ret < 0) {
|
||||
error_report("vfio: Failed to set trigger eventfd: %m");
|
||||
qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions only used when eventfds are handled on user-side
|
||||
* ie. without irqfd
|
||||
*/
|
||||
|
||||
/**
|
||||
* vfio_mmap_set_enabled - enable/disable the fast path mode
|
||||
* @vdev: the VFIO platform device
|
||||
* @enabled: the target mmap state
|
||||
*
|
||||
* enabled = true ~ fast path = MMIO region is mmaped (no KVM TRAP);
|
||||
* enabled = false ~ slow path = MMIO region is trapped and region callbacks
|
||||
* are called; slow path enables to trap the device IRQ status register reset
|
||||
*/
|
||||
|
||||
static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled)
|
||||
{
|
||||
int i;
|
||||
|
||||
trace_vfio_platform_mmap_set_enabled(enabled);
|
||||
|
||||
for (i = 0; i < vdev->vbasedev.num_regions; i++) {
|
||||
VFIORegion *region = vdev->regions[i];
|
||||
|
||||
memory_region_set_enabled(®ion->mmap_mem, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_intp_mmap_enable - timer function, restores the fast path
|
||||
* if there is no more active IRQ
|
||||
* @opaque: actually points to the VFIO platform device
|
||||
*
|
||||
* Called on mmap timer timout, this function checks whether the
|
||||
* IRQ is still active and if not, restores the fast path.
|
||||
* by construction a single eventfd is handled at a time.
|
||||
* if the IRQ is still active, the timer is re-programmed.
|
||||
*/
|
||||
static void vfio_intp_mmap_enable(void *opaque)
|
||||
{
|
||||
VFIOINTp *tmp;
|
||||
VFIOPlatformDevice *vdev = (VFIOPlatformDevice *)opaque;
|
||||
|
||||
qemu_mutex_lock(&vdev->intp_mutex);
|
||||
QLIST_FOREACH(tmp, &vdev->intp_list, next) {
|
||||
if (tmp->state == VFIO_IRQ_ACTIVE) {
|
||||
trace_vfio_platform_intp_mmap_enable(tmp->pin);
|
||||
/* re-program the timer to check active status later */
|
||||
timer_mod(vdev->mmap_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
|
||||
vdev->mmap_timeout);
|
||||
qemu_mutex_unlock(&vdev->intp_mutex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
vfio_mmap_set_enabled(vdev, true);
|
||||
qemu_mutex_unlock(&vdev->intp_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_intp_inject_pending_lockheld - Injects a pending IRQ
|
||||
* @opaque: opaque pointer, in practice the VFIOINTp handle
|
||||
*
|
||||
* The function is called on a previous IRQ completion, from
|
||||
* vfio_platform_eoi, while the intp_mutex is locked.
|
||||
* Also in such situation, the slow path already is set and
|
||||
* the mmap timer was already programmed.
|
||||
*/
|
||||
static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp)
|
||||
{
|
||||
trace_vfio_platform_intp_inject_pending_lockheld(intp->pin,
|
||||
event_notifier_get_fd(&intp->interrupt));
|
||||
|
||||
intp->state = VFIO_IRQ_ACTIVE;
|
||||
|
||||
/* trigger the virtual IRQ */
|
||||
qemu_set_irq(intp->qemuirq, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_intp_interrupt - The user-side eventfd handler
|
||||
* @opaque: opaque pointer which in practice is the VFIOINTp handle
|
||||
*
|
||||
* the function is entered in event handler context:
|
||||
* the vIRQ is injected into the guest if there is no other active
|
||||
* or pending IRQ.
|
||||
*/
|
||||
static void vfio_intp_interrupt(VFIOINTp *intp)
|
||||
{
|
||||
int ret;
|
||||
VFIOINTp *tmp;
|
||||
VFIOPlatformDevice *vdev = intp->vdev;
|
||||
bool delay_handling = false;
|
||||
|
||||
qemu_mutex_lock(&vdev->intp_mutex);
|
||||
if (intp->state == VFIO_IRQ_INACTIVE) {
|
||||
QLIST_FOREACH(tmp, &vdev->intp_list, next) {
|
||||
if (tmp->state == VFIO_IRQ_ACTIVE ||
|
||||
tmp->state == VFIO_IRQ_PENDING) {
|
||||
delay_handling = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (delay_handling) {
|
||||
/*
|
||||
* the new IRQ gets a pending status and is pushed in
|
||||
* the pending queue
|
||||
*/
|
||||
intp->state = VFIO_IRQ_PENDING;
|
||||
trace_vfio_intp_interrupt_set_pending(intp->pin);
|
||||
QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue,
|
||||
intp, pqnext);
|
||||
ret = event_notifier_test_and_clear(&intp->interrupt);
|
||||
qemu_mutex_unlock(&vdev->intp_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_vfio_platform_intp_interrupt(intp->pin,
|
||||
event_notifier_get_fd(&intp->interrupt));
|
||||
|
||||
ret = event_notifier_test_and_clear(&intp->interrupt);
|
||||
if (!ret) {
|
||||
error_report("Error when clearing fd=%d (ret = %d)\n",
|
||||
event_notifier_get_fd(&intp->interrupt), ret);
|
||||
}
|
||||
|
||||
intp->state = VFIO_IRQ_ACTIVE;
|
||||
|
||||
/* sets slow path */
|
||||
vfio_mmap_set_enabled(vdev, false);
|
||||
|
||||
/* trigger the virtual IRQ */
|
||||
qemu_set_irq(intp->qemuirq, 1);
|
||||
|
||||
/*
|
||||
* Schedule the mmap timer which will restore fastpath when no IRQ
|
||||
* is active anymore
|
||||
*/
|
||||
if (vdev->mmap_timeout) {
|
||||
timer_mod(vdev->mmap_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
|
||||
vdev->mmap_timeout);
|
||||
}
|
||||
qemu_mutex_unlock(&vdev->intp_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_platform_eoi - IRQ completion routine
|
||||
* @vbasedev: the VFIO device handle
|
||||
*
|
||||
* De-asserts the active virtual IRQ and unmasks the physical IRQ
|
||||
* (effective for level sensitive IRQ auto-masked by the VFIO driver).
|
||||
* Then it handles next pending IRQ if any.
|
||||
* eoi function is called on the first access to any MMIO region
|
||||
* after an IRQ was triggered, trapped since slow path was set.
|
||||
* It is assumed this access corresponds to the IRQ status
|
||||
* register reset. With such a mechanism, a single IRQ can be
|
||||
* handled at a time since there is no way to know which IRQ
|
||||
* was completed by the guest (we would need additional details
|
||||
* about the IRQ status register mask).
|
||||
*/
|
||||
static void vfio_platform_eoi(VFIODevice *vbasedev)
|
||||
{
|
||||
VFIOINTp *intp;
|
||||
VFIOPlatformDevice *vdev =
|
||||
container_of(vbasedev, VFIOPlatformDevice, vbasedev);
|
||||
|
||||
qemu_mutex_lock(&vdev->intp_mutex);
|
||||
QLIST_FOREACH(intp, &vdev->intp_list, next) {
|
||||
if (intp->state == VFIO_IRQ_ACTIVE) {
|
||||
trace_vfio_platform_eoi(intp->pin,
|
||||
event_notifier_get_fd(&intp->interrupt));
|
||||
intp->state = VFIO_IRQ_INACTIVE;
|
||||
|
||||
/* deassert the virtual IRQ */
|
||||
qemu_set_irq(intp->qemuirq, 0);
|
||||
|
||||
if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
|
||||
/* unmasks the physical level-sensitive IRQ */
|
||||
vfio_unmask_single_irqindex(vbasedev, intp->pin);
|
||||
}
|
||||
|
||||
/* a single IRQ can be active at a time */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* in case there are pending IRQs, handle the first one */
|
||||
if (!QSIMPLEQ_EMPTY(&vdev->pending_intp_queue)) {
|
||||
intp = QSIMPLEQ_FIRST(&vdev->pending_intp_queue);
|
||||
vfio_intp_inject_pending_lockheld(intp);
|
||||
QSIMPLEQ_REMOVE_HEAD(&vdev->pending_intp_queue, pqnext);
|
||||
}
|
||||
qemu_mutex_unlock(&vdev->intp_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_start_eventfd_injection - starts the virtual IRQ injection using
|
||||
* user-side handled eventfds
|
||||
* @intp: the IRQ struct pointer
|
||||
*/
|
||||
|
||||
static int vfio_start_eventfd_injection(VFIOINTp *intp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
|
||||
if (ret) {
|
||||
error_report("vfio: Error: Failed to pass IRQ fd to the driver: %m");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VFIO skeleton */
|
||||
|
||||
static void vfio_platform_compute_needs_reset(VFIODevice *vbasedev)
|
||||
{
|
||||
vbasedev->needs_reset = true;
|
||||
}
|
||||
|
||||
/* not implemented yet */
|
||||
static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_populate_device - Allocate and populate MMIO region
|
||||
* and IRQ structs according to driver returned information
|
||||
* @vbasedev: the VFIO device handle
|
||||
*
|
||||
*/
|
||||
static int vfio_populate_device(VFIODevice *vbasedev)
|
||||
{
|
||||
VFIOINTp *intp, *tmp;
|
||||
int i, ret = -1;
|
||||
VFIOPlatformDevice *vdev =
|
||||
container_of(vbasedev, VFIOPlatformDevice, vbasedev);
|
||||
|
||||
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
|
||||
error_report("vfio: Um, this isn't a platform device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions);
|
||||
|
||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||
struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
|
||||
VFIORegion *ptr;
|
||||
|
||||
vdev->regions[i] = g_malloc0(sizeof(VFIORegion));
|
||||
ptr = vdev->regions[i];
|
||||
reg_info.index = i;
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info);
|
||||
if (ret) {
|
||||
error_report("vfio: Error getting region %d info: %m", i);
|
||||
goto reg_error;
|
||||
}
|
||||
ptr->flags = reg_info.flags;
|
||||
ptr->size = reg_info.size;
|
||||
ptr->fd_offset = reg_info.offset;
|
||||
ptr->nr = i;
|
||||
ptr->vbasedev = vbasedev;
|
||||
|
||||
trace_vfio_platform_populate_regions(ptr->nr,
|
||||
(unsigned long)ptr->flags,
|
||||
(unsigned long)ptr->size,
|
||||
ptr->vbasedev->fd,
|
||||
(unsigned long)ptr->fd_offset);
|
||||
}
|
||||
|
||||
vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
|
||||
vfio_intp_mmap_enable, vdev);
|
||||
|
||||
QSIMPLEQ_INIT(&vdev->pending_intp_queue);
|
||||
|
||||
for (i = 0; i < vbasedev->num_irqs; i++) {
|
||||
struct vfio_irq_info irq = { .argsz = sizeof(irq) };
|
||||
|
||||
irq.index = i;
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
|
||||
if (ret) {
|
||||
error_printf("vfio: error getting device %s irq info",
|
||||
vbasedev->name);
|
||||
goto irq_err;
|
||||
} else {
|
||||
trace_vfio_platform_populate_interrupts(irq.index,
|
||||
irq.count,
|
||||
irq.flags);
|
||||
intp = vfio_init_intp(vbasedev, irq);
|
||||
if (!intp) {
|
||||
error_report("vfio: Error installing IRQ %d up", i);
|
||||
goto irq_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
irq_err:
|
||||
timer_del(vdev->mmap_timer);
|
||||
QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) {
|
||||
QLIST_REMOVE(intp, next);
|
||||
g_free(intp);
|
||||
}
|
||||
reg_error:
|
||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||
g_free(vdev->regions[i]);
|
||||
}
|
||||
g_free(vdev->regions);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* specialized functions for VFIO Platform devices */
|
||||
static VFIODeviceOps vfio_platform_ops = {
|
||||
.vfio_compute_needs_reset = vfio_platform_compute_needs_reset,
|
||||
.vfio_hot_reset_multi = vfio_platform_hot_reset_multi,
|
||||
.vfio_eoi = vfio_platform_eoi,
|
||||
};
|
||||
|
||||
/**
|
||||
* vfio_base_device_init - perform preliminary VFIO setup
|
||||
* @vbasedev: the VFIO device handle
|
||||
*
|
||||
* Implement the VFIO command sequence that allows to discover
|
||||
* assigned device resources: group extraction, device
|
||||
* fd retrieval, resource query.
|
||||
* Precondition: the device name must be initialized
|
||||
*/
|
||||
static int vfio_base_device_init(VFIODevice *vbasedev)
|
||||
{
|
||||
VFIOGroup *group;
|
||||
VFIODevice *vbasedev_iter;
|
||||
char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name;
|
||||
ssize_t len;
|
||||
struct stat st;
|
||||
int groupid;
|
||||
int ret;
|
||||
|
||||
/* name must be set prior to the call */
|
||||
if (!vbasedev->name || strchr(vbasedev->name, '/')) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check that the host device exists */
|
||||
g_snprintf(path, sizeof(path), "/sys/bus/platform/devices/%s/",
|
||||
vbasedev->name);
|
||||
|
||||
if (stat(path, &st) < 0) {
|
||||
error_report("vfio: error: no such host device: %s", path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
g_strlcat(path, "iommu_group", sizeof(path));
|
||||
len = readlink(path, iommu_group_path, sizeof(iommu_group_path));
|
||||
if (len < 0 || len >= sizeof(iommu_group_path)) {
|
||||
error_report("vfio: error no iommu_group for device");
|
||||
return len < 0 ? -errno : -ENAMETOOLONG;
|
||||
}
|
||||
|
||||
iommu_group_path[len] = 0;
|
||||
group_name = basename(iommu_group_path);
|
||||
|
||||
if (sscanf(group_name, "%d", &groupid) != 1) {
|
||||
error_report("vfio: error reading %s: %m", path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
trace_vfio_platform_base_device_init(vbasedev->name, groupid);
|
||||
|
||||
group = vfio_get_group(groupid, &address_space_memory);
|
||||
if (!group) {
|
||||
error_report("vfio: failed to get group %d", groupid);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
g_snprintf(path, sizeof(path), "%s", vbasedev->name);
|
||||
|
||||
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
||||
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
|
||||
error_report("vfio: error: device %s is already attached", path);
|
||||
vfio_put_group(group);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
ret = vfio_get_device(group, path, vbasedev);
|
||||
if (ret) {
|
||||
error_report("vfio: failed to get device %s", path);
|
||||
vfio_put_group(group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vfio_populate_device(vbasedev);
|
||||
if (ret) {
|
||||
error_report("vfio: failed to populate device %s", path);
|
||||
vfio_put_group(group);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_map_region - initialize the 2 memory regions for a given
|
||||
* MMIO region index
|
||||
* @vdev: the VFIO platform device handle
|
||||
* @nr: the index of the region
|
||||
*
|
||||
* Init the top memory region and the mmapped memory region beneath
|
||||
* VFIOPlatformDevice is used since VFIODevice is not a QOM Object
|
||||
* and could not be passed to memory region functions
|
||||
*/
|
||||
static void vfio_map_region(VFIOPlatformDevice *vdev, int nr)
|
||||
{
|
||||
VFIORegion *region = vdev->regions[nr];
|
||||
uint64_t size = region->size;
|
||||
char name[64];
|
||||
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_snprintf(name, sizeof(name), "VFIO %s region %d",
|
||||
vdev->vbasedev.name, nr);
|
||||
|
||||
/* A "slow" read/write mapping underlies all regions */
|
||||
memory_region_init_io(®ion->mem, OBJECT(vdev), &vfio_region_ops,
|
||||
region, name, size);
|
||||
|
||||
g_strlcat(name, " mmap", sizeof(name));
|
||||
|
||||
if (vfio_mmap_region(OBJECT(vdev), region, ®ion->mem,
|
||||
®ion->mmap_mem, ®ion->mmap, size, 0, name)) {
|
||||
error_report("%s unsupported. Performance may be slow", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vfio_platform_realize - the device realize function
|
||||
* @dev: device state pointer
|
||||
* @errp: error
|
||||
*
|
||||
* initialize the device, its memory regions and IRQ structures
|
||||
* IRQ are started separately
|
||||
*/
|
||||
static void vfio_platform_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
|
||||
SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
|
||||
VFIODevice *vbasedev = &vdev->vbasedev;
|
||||
VFIOINTp *intp;
|
||||
int i, ret;
|
||||
|
||||
vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM;
|
||||
vbasedev->ops = &vfio_platform_ops;
|
||||
|
||||
trace_vfio_platform_realize(vbasedev->name, vdev->compat);
|
||||
|
||||
ret = vfio_base_device_init(vbasedev);
|
||||
if (ret) {
|
||||
error_setg(errp, "vfio: vfio_base_device_init failed for %s",
|
||||
vbasedev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||
vfio_map_region(vdev, i);
|
||||
sysbus_init_mmio(sbdev, &vdev->regions[i]->mem);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(intp, &vdev->intp_list, next) {
|
||||
vfio_start_eventfd_injection(intp);
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vfio_platform_vmstate = {
|
||||
.name = TYPE_VFIO_PLATFORM,
|
||||
.unmigratable = 1,
|
||||
};
|
||||
|
||||
static Property vfio_platform_dev_properties[] = {
|
||||
DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name),
|
||||
DEFINE_PROP_BOOL("x-mmap", VFIOPlatformDevice, vbasedev.allow_mmap, true),
|
||||
DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
|
||||
mmap_timeout, 1100),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void vfio_platform_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = vfio_platform_realize;
|
||||
dc->props = vfio_platform_dev_properties;
|
||||
dc->vmsd = &vfio_platform_vmstate;
|
||||
dc->desc = "VFIO-based platform device assignment";
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo vfio_platform_dev_info = {
|
||||
.name = TYPE_VFIO_PLATFORM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(VFIOPlatformDevice),
|
||||
.class_init = vfio_platform_class_init,
|
||||
.class_size = sizeof(VFIOPlatformDeviceClass),
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void register_vfio_platform_dev_type(void)
|
||||
{
|
||||
type_register_static(&vfio_platform_dev_info);
|
||||
}
|
||||
|
||||
type_init(register_vfio_platform_dev_type)
|
||||
+26
-21
@@ -157,15 +157,18 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
|
||||
}
|
||||
|
||||
|
||||
static int get_desc(Vring *vring, VirtQueueElement *elem,
|
||||
static int get_desc(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem,
|
||||
struct vring_desc *desc)
|
||||
{
|
||||
unsigned *num;
|
||||
struct iovec *iov;
|
||||
hwaddr *addr;
|
||||
MemoryRegion *mr;
|
||||
int is_write = virtio_tswap16(vdev, desc->flags) & VRING_DESC_F_WRITE;
|
||||
uint32_t len = virtio_tswap32(vdev, desc->len);
|
||||
uint64_t desc_addr = virtio_tswap64(vdev, desc->addr);
|
||||
|
||||
if (desc->flags & VRING_DESC_F_WRITE) {
|
||||
if (is_write) {
|
||||
num = &elem->in_num;
|
||||
iov = &elem->in_sg[*num];
|
||||
addr = &elem->in_addr[*num];
|
||||
@@ -189,18 +192,17 @@ static int get_desc(Vring *vring, VirtQueueElement *elem,
|
||||
}
|
||||
|
||||
/* TODO handle non-contiguous memory across region boundaries */
|
||||
iov->iov_base = vring_map(&mr, desc->addr, desc->len,
|
||||
desc->flags & VRING_DESC_F_WRITE);
|
||||
iov->iov_base = vring_map(&mr, desc_addr, len, is_write);
|
||||
if (!iov->iov_base) {
|
||||
error_report("Failed to map descriptor addr %#" PRIx64 " len %u",
|
||||
(uint64_t)desc->addr, desc->len);
|
||||
(uint64_t)desc_addr, len);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* The MemoryRegion is looked up again and unref'ed later, leave the
|
||||
* ref in place. */
|
||||
iov->iov_len = desc->len;
|
||||
*addr = desc->addr;
|
||||
iov->iov_len = len;
|
||||
*addr = desc_addr;
|
||||
*num += 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -222,21 +224,23 @@ static int get_indirect(VirtIODevice *vdev, Vring *vring,
|
||||
struct vring_desc desc;
|
||||
unsigned int i = 0, count, found = 0;
|
||||
int ret;
|
||||
uint32_t len = virtio_tswap32(vdev, indirect->len);
|
||||
uint64_t addr = virtio_tswap64(vdev, indirect->addr);
|
||||
|
||||
/* Sanity check */
|
||||
if (unlikely(indirect->len % sizeof(desc))) {
|
||||
if (unlikely(len % sizeof(desc))) {
|
||||
error_report("Invalid length in indirect descriptor: "
|
||||
"len %#x not multiple of %#zx",
|
||||
indirect->len, sizeof(desc));
|
||||
len, sizeof(desc));
|
||||
vring->broken = true;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
count = indirect->len / sizeof(desc);
|
||||
count = len / sizeof(desc);
|
||||
/* Buffers are chained via a 16 bit next field, so
|
||||
* we can have at most 2^16 of these. */
|
||||
if (unlikely(count > USHRT_MAX + 1)) {
|
||||
error_report("Indirect buffer length too big: %d", indirect->len);
|
||||
error_report("Indirect buffer length too big: %d", len);
|
||||
vring->broken = true;
|
||||
return -EFAULT;
|
||||
}
|
||||
@@ -247,12 +251,12 @@ static int get_indirect(VirtIODevice *vdev, Vring *vring,
|
||||
|
||||
/* Translate indirect descriptor */
|
||||
desc_ptr = vring_map(&mr,
|
||||
indirect->addr + found * sizeof(desc),
|
||||
addr + found * sizeof(desc),
|
||||
sizeof(desc), false);
|
||||
if (!desc_ptr) {
|
||||
error_report("Failed to map indirect descriptor "
|
||||
"addr %#" PRIx64 " len %zu",
|
||||
(uint64_t)indirect->addr + found * sizeof(desc),
|
||||
(uint64_t)addr + found * sizeof(desc),
|
||||
sizeof(desc));
|
||||
vring->broken = true;
|
||||
return -EFAULT;
|
||||
@@ -270,19 +274,20 @@ static int get_indirect(VirtIODevice *vdev, Vring *vring,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) {
|
||||
if (unlikely(virtio_tswap16(vdev, desc.flags)
|
||||
& VRING_DESC_F_INDIRECT)) {
|
||||
error_report("Nested indirect descriptor");
|
||||
vring->broken = true;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ret = get_desc(vring, elem, &desc);
|
||||
ret = get_desc(vdev, vring, elem, &desc);
|
||||
if (ret < 0) {
|
||||
vring->broken |= (ret == -EFAULT);
|
||||
return ret;
|
||||
}
|
||||
i = desc.next;
|
||||
} while (desc.flags & VRING_DESC_F_NEXT);
|
||||
i = virtio_tswap16(vdev, desc.next);
|
||||
} while (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_NEXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -383,7 +388,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
|
||||
/* Ensure descriptor is loaded before accessing fields */
|
||||
barrier();
|
||||
|
||||
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
||||
if (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_INDIRECT) {
|
||||
ret = get_indirect(vdev, vring, elem, &desc);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
@@ -391,13 +396,13 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = get_desc(vring, elem, &desc);
|
||||
ret = get_desc(vdev, vring, elem, &desc);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = desc.next;
|
||||
} while (desc.flags & VRING_DESC_F_NEXT);
|
||||
i = virtio_tswap16(vdev, desc.next);
|
||||
} while (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_NEXT);
|
||||
|
||||
/* On success, increment avail index. */
|
||||
vring->last_avail_idx++;
|
||||
|
||||
+8
-10
@@ -591,7 +591,7 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log)
|
||||
uint64_t features = dev->acked_features;
|
||||
int r;
|
||||
if (enable_log) {
|
||||
features |= 0x1 << VHOST_F_LOG_ALL;
|
||||
features |= 0x1ULL << VHOST_F_LOG_ALL;
|
||||
}
|
||||
r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features);
|
||||
return r < 0 ? -errno : 0;
|
||||
@@ -902,7 +902,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
|
||||
.priority = 10
|
||||
};
|
||||
hdev->migration_blocker = NULL;
|
||||
if (!(hdev->features & (0x1 << VHOST_F_LOG_ALL))) {
|
||||
if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
|
||||
error_setg(&hdev->migration_blocker,
|
||||
"Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
|
||||
migrate_add_blocker(hdev->migration_blocker);
|
||||
@@ -1045,12 +1045,12 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
|
||||
assert(r >= 0);
|
||||
}
|
||||
|
||||
unsigned vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
|
||||
unsigned features)
|
||||
uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
|
||||
uint64_t features)
|
||||
{
|
||||
const int *bit = feature_bits;
|
||||
while (*bit != VHOST_INVALID_FEATURE_BIT) {
|
||||
unsigned bit_mask = (1 << *bit);
|
||||
uint64_t bit_mask = (1ULL << *bit);
|
||||
if (!(hdev->features & bit_mask)) {
|
||||
features &= ~bit_mask;
|
||||
}
|
||||
@@ -1060,11 +1060,11 @@ unsigned vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
|
||||
}
|
||||
|
||||
void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
|
||||
unsigned features)
|
||||
uint64_t features)
|
||||
{
|
||||
const int *bit = feature_bits;
|
||||
while (*bit != VHOST_INVALID_FEATURE_BIT) {
|
||||
unsigned bit_mask = (1 << *bit);
|
||||
uint64_t bit_mask = (1ULL << *bit);
|
||||
if (features & bit_mask) {
|
||||
hdev->acked_features |= bit_mask;
|
||||
}
|
||||
@@ -1114,9 +1114,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||
|
||||
return 0;
|
||||
fail_log:
|
||||
if (hdev->log_size) {
|
||||
vhost_log_put(hdev, false);
|
||||
}
|
||||
vhost_log_put(hdev, false);
|
||||
fail_vq:
|
||||
while (--i >= 0) {
|
||||
vhost_virtqueue_stop(hdev,
|
||||
|
||||
@@ -312,7 +312,7 @@ static void virtio_balloon_set_config(VirtIODevice *vdev,
|
||||
|
||||
static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f)
|
||||
{
|
||||
f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
|
||||
virtio_add_feature(&f, VIRTIO_BALLOON_F_STATS_VQ);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
@@ -333,8 +333,11 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
case VIRTIO_MMIO_QUEUENUM:
|
||||
DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE);
|
||||
virtio_queue_set_num(vdev, vdev->queue_sel, value);
|
||||
/* Note: only call this function for legacy devices */
|
||||
virtio_queue_update_rings(vdev, vdev->queue_sel);
|
||||
break;
|
||||
case VIRTIO_MMIO_QUEUEALIGN:
|
||||
/* Note: this is only valid for legacy devices */
|
||||
virtio_queue_set_align(vdev, vdev->queue_sel, value);
|
||||
break;
|
||||
case VIRTIO_MMIO_QUEUEPFN:
|
||||
|
||||
+592
-20
@@ -24,6 +24,7 @@
|
||||
#include "hw/virtio/virtio-serial.h"
|
||||
#include "hw/virtio/virtio-scsi.h"
|
||||
#include "hw/virtio/virtio-balloon.h"
|
||||
#include "hw/virtio/virtio-input.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/pci/msi.h"
|
||||
@@ -38,6 +39,8 @@
|
||||
|
||||
#define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev))
|
||||
|
||||
#undef VIRTIO_PCI_CONFIG
|
||||
|
||||
/* The remaining space is defined by each driver as the per-driver
|
||||
* configuration space */
|
||||
#define VIRTIO_PCI_CONFIG_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_enabled(dev))
|
||||
@@ -133,12 +136,21 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define QEMU_VIRTIO_PCI_QUEUE_MEM_MULT 0x1000
|
||||
|
||||
static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
|
||||
int n, bool assign, bool set_handler)
|
||||
{
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
|
||||
bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
|
||||
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
|
||||
MemoryRegion *modern_mr = &proxy->notify.mr;
|
||||
MemoryRegion *legacy_mr = &proxy->bar;
|
||||
hwaddr modern_addr = QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
|
||||
virtio_get_queue_index(vq);
|
||||
hwaddr legacy_addr = VIRTIO_PCI_QUEUE_NOTIFY;
|
||||
int r = 0;
|
||||
|
||||
if (assign) {
|
||||
@@ -149,11 +161,23 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
|
||||
return r;
|
||||
}
|
||||
virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
|
||||
memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
|
||||
true, n, notifier);
|
||||
if (modern) {
|
||||
memory_region_add_eventfd(modern_mr, modern_addr, 2,
|
||||
true, n, notifier);
|
||||
}
|
||||
if (legacy) {
|
||||
memory_region_add_eventfd(legacy_mr, legacy_addr, 2,
|
||||
true, n, notifier);
|
||||
}
|
||||
} else {
|
||||
memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
|
||||
true, n, notifier);
|
||||
if (modern) {
|
||||
memory_region_del_eventfd(modern_mr, modern_addr, 2,
|
||||
true, n, notifier);
|
||||
}
|
||||
if (legacy) {
|
||||
memory_region_del_eventfd(legacy_mr, legacy_addr, 2,
|
||||
true, n, notifier);
|
||||
}
|
||||
virtio_queue_set_host_notifier_fd_handler(vq, false, false);
|
||||
event_notifier_cleanup(notifier);
|
||||
}
|
||||
@@ -918,11 +942,359 @@ static int virtio_pci_query_nvectors(DeviceState *d)
|
||||
return proxy->nvectors;
|
||||
}
|
||||
|
||||
static void virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy,
|
||||
struct virtio_pci_cap *cap)
|
||||
{
|
||||
PCIDevice *dev = &proxy->pci_dev;
|
||||
int offset;
|
||||
|
||||
offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, 0, cap->cap_len);
|
||||
assert(offset > 0);
|
||||
|
||||
assert(cap->cap_len >= sizeof *cap);
|
||||
memcpy(dev->config + offset + PCI_CAP_FLAGS, &cap->cap_len,
|
||||
cap->cap_len - PCI_CAP_FLAGS);
|
||||
}
|
||||
|
||||
static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = opaque;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||
uint32_t val = 0;
|
||||
int i;
|
||||
|
||||
switch (addr) {
|
||||
case VIRTIO_PCI_COMMON_DFSELECT:
|
||||
val = proxy->dfselect;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_DF:
|
||||
if (proxy->dfselect <= 1) {
|
||||
val = vdev->host_features >> (32 * proxy->dfselect);
|
||||
}
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_GFSELECT:
|
||||
val = proxy->gfselect;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_GF:
|
||||
if (proxy->gfselect <= ARRAY_SIZE(proxy->guest_features)) {
|
||||
val = proxy->guest_features[proxy->gfselect];
|
||||
}
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_MSIX:
|
||||
val = vdev->config_vector;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_NUMQ:
|
||||
for (i = 0; i < VIRTIO_QUEUE_MAX; ++i) {
|
||||
if (virtio_queue_get_num(vdev, i)) {
|
||||
val = i + 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_STATUS:
|
||||
val = vdev->status;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_CFGGENERATION:
|
||||
val = vdev->generation;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_SELECT:
|
||||
val = vdev->queue_sel;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_SIZE:
|
||||
val = virtio_queue_get_num(vdev, vdev->queue_sel);
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_MSIX:
|
||||
val = virtio_queue_vector(vdev, vdev->queue_sel);
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_ENABLE:
|
||||
val = proxy->vqs[vdev->queue_sel].enabled;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_NOFF:
|
||||
/* Simply map queues in order */
|
||||
val = vdev->queue_sel;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_DESCLO:
|
||||
val = proxy->vqs[vdev->queue_sel].desc[0];
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_DESCHI:
|
||||
val = proxy->vqs[vdev->queue_sel].desc[1];
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_AVAILLO:
|
||||
val = proxy->vqs[vdev->queue_sel].avail[0];
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_AVAILHI:
|
||||
val = proxy->vqs[vdev->queue_sel].avail[1];
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_USEDLO:
|
||||
val = proxy->vqs[vdev->queue_sel].used[0];
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_USEDHI:
|
||||
val = proxy->vqs[vdev->queue_sel].used[1];
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void virtio_pci_common_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = opaque;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||
|
||||
switch (addr) {
|
||||
case VIRTIO_PCI_COMMON_DFSELECT:
|
||||
proxy->dfselect = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_GFSELECT:
|
||||
proxy->gfselect = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_GF:
|
||||
if (proxy->gfselect <= ARRAY_SIZE(proxy->guest_features)) {
|
||||
proxy->guest_features[proxy->gfselect] = val;
|
||||
virtio_set_features(vdev,
|
||||
(((uint64_t)proxy->guest_features[1]) << 32) |
|
||||
proxy->guest_features[0]);
|
||||
}
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_MSIX:
|
||||
msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
|
||||
/* Make it possible for guest to discover an error took place. */
|
||||
if (msix_vector_use(&proxy->pci_dev, val) < 0) {
|
||||
val = VIRTIO_NO_VECTOR;
|
||||
}
|
||||
vdev->config_vector = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_STATUS:
|
||||
if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
virtio_pci_stop_ioeventfd(proxy);
|
||||
}
|
||||
|
||||
virtio_set_status(vdev, val & 0xFF);
|
||||
|
||||
if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||
virtio_pci_start_ioeventfd(proxy);
|
||||
}
|
||||
|
||||
if (vdev->status == 0) {
|
||||
virtio_reset(vdev);
|
||||
msix_unuse_all_vectors(&proxy->pci_dev);
|
||||
}
|
||||
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_SELECT:
|
||||
if (val < VIRTIO_QUEUE_MAX) {
|
||||
vdev->queue_sel = val;
|
||||
}
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_SIZE:
|
||||
proxy->vqs[vdev->queue_sel].num = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_MSIX:
|
||||
msix_vector_unuse(&proxy->pci_dev,
|
||||
virtio_queue_vector(vdev, vdev->queue_sel));
|
||||
/* Make it possible for guest to discover an error took place. */
|
||||
if (msix_vector_use(&proxy->pci_dev, val) < 0) {
|
||||
val = VIRTIO_NO_VECTOR;
|
||||
}
|
||||
virtio_queue_set_vector(vdev, vdev->queue_sel, val);
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_ENABLE:
|
||||
/* TODO: need a way to put num back on reset. */
|
||||
virtio_queue_set_num(vdev, vdev->queue_sel,
|
||||
proxy->vqs[vdev->queue_sel].num);
|
||||
virtio_queue_set_rings(vdev, vdev->queue_sel,
|
||||
((uint64_t)proxy->vqs[vdev->queue_sel].desc[1]) << 32 |
|
||||
proxy->vqs[vdev->queue_sel].desc[0],
|
||||
((uint64_t)proxy->vqs[vdev->queue_sel].avail[1]) << 32 |
|
||||
proxy->vqs[vdev->queue_sel].avail[0],
|
||||
((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 |
|
||||
proxy->vqs[vdev->queue_sel].used[0]);
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_DESCLO:
|
||||
proxy->vqs[vdev->queue_sel].desc[0] = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_DESCHI:
|
||||
proxy->vqs[vdev->queue_sel].desc[1] = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_AVAILLO:
|
||||
proxy->vqs[vdev->queue_sel].avail[0] = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_AVAILHI:
|
||||
proxy->vqs[vdev->queue_sel].avail[1] = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_USEDLO:
|
||||
proxy->vqs[vdev->queue_sel].used[0] = val;
|
||||
break;
|
||||
case VIRTIO_PCI_COMMON_Q_USEDHI:
|
||||
proxy->vqs[vdev->queue_sel].used[1] = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t virtio_pci_notify_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void virtio_pci_notify_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
VirtIODevice *vdev = opaque;
|
||||
unsigned queue = addr / QEMU_VIRTIO_PCI_QUEUE_MEM_MULT;
|
||||
|
||||
if (queue < VIRTIO_QUEUE_MAX) {
|
||||
virtio_queue_notify(vdev, queue);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = opaque;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||
uint64_t val = vdev->isr;
|
||||
|
||||
vdev->isr = 0;
|
||||
pci_irq_deassert(&proxy->pci_dev);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void virtio_pci_isr_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
static uint64_t virtio_pci_device_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
VirtIODevice *vdev = opaque;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
val = virtio_config_modern_readb(vdev, addr);
|
||||
break;
|
||||
case 2:
|
||||
val = virtio_config_modern_readw(vdev, addr);
|
||||
break;
|
||||
case 4:
|
||||
val = virtio_config_modern_readl(vdev, addr);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void virtio_pci_device_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
VirtIODevice *vdev = opaque;
|
||||
switch (size) {
|
||||
case 1:
|
||||
virtio_config_modern_writeb(vdev, addr, val);
|
||||
break;
|
||||
case 2:
|
||||
virtio_config_modern_writew(vdev, addr, val);
|
||||
break;
|
||||
case 4:
|
||||
virtio_config_modern_writel(vdev, addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy)
|
||||
{
|
||||
static const MemoryRegionOps common_ops = {
|
||||
.read = virtio_pci_common_read,
|
||||
.write = virtio_pci_common_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
static const MemoryRegionOps isr_ops = {
|
||||
.read = virtio_pci_isr_read,
|
||||
.write = virtio_pci_isr_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
static const MemoryRegionOps device_ops = {
|
||||
.read = virtio_pci_device_read,
|
||||
.write = virtio_pci_device_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
static const MemoryRegionOps notify_ops = {
|
||||
.read = virtio_pci_notify_read,
|
||||
.write = virtio_pci_notify_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
memory_region_init_io(&proxy->common.mr, OBJECT(proxy),
|
||||
&common_ops,
|
||||
proxy,
|
||||
"virtio-pci-common",
|
||||
proxy->common.size);
|
||||
|
||||
memory_region_init_io(&proxy->isr.mr, OBJECT(proxy),
|
||||
&isr_ops,
|
||||
proxy,
|
||||
"virtio-pci-isr",
|
||||
proxy->isr.size);
|
||||
|
||||
memory_region_init_io(&proxy->device.mr, OBJECT(proxy),
|
||||
&device_ops,
|
||||
virtio_bus_get_device(&proxy->bus),
|
||||
"virtio-pci-device",
|
||||
proxy->device.size);
|
||||
|
||||
memory_region_init_io(&proxy->notify.mr, OBJECT(proxy),
|
||||
¬ify_ops,
|
||||
virtio_bus_get_device(&proxy->bus),
|
||||
"virtio-pci-notify",
|
||||
proxy->notify.size);
|
||||
}
|
||||
|
||||
static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
|
||||
VirtIOPCIRegion *region,
|
||||
struct virtio_pci_cap *cap)
|
||||
{
|
||||
memory_region_add_subregion(&proxy->modern_bar,
|
||||
region->offset,
|
||||
®ion->mr);
|
||||
|
||||
cap->cfg_type = region->type;
|
||||
cap->bar = proxy->modern_mem_bar;
|
||||
cap->offset = cpu_to_le32(region->offset);
|
||||
cap->length = cpu_to_le32(region->size);
|
||||
virtio_pci_add_mem_cap(proxy, cap);
|
||||
}
|
||||
|
||||
/* This is called by virtio-bus just after the device is plugged. */
|
||||
static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||
{
|
||||
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
|
||||
VirtioBusState *bus = &proxy->bus;
|
||||
bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
|
||||
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
|
||||
uint8_t *config;
|
||||
uint32_t size;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||
@@ -931,13 +1303,51 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||
if (proxy->class_code) {
|
||||
pci_config_set_class(config, proxy->class_code);
|
||||
}
|
||||
pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
|
||||
pci_get_word(config + PCI_VENDOR_ID));
|
||||
pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
|
||||
|
||||
if (legacy) {
|
||||
/* legacy and transitional */
|
||||
pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
|
||||
pci_get_word(config + PCI_VENDOR_ID));
|
||||
pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
|
||||
} else {
|
||||
/* pure virtio-1.0 */
|
||||
pci_set_word(config + PCI_VENDOR_ID,
|
||||
PCI_VENDOR_ID_REDHAT_QUMRANET);
|
||||
pci_set_word(config + PCI_DEVICE_ID,
|
||||
0x1040 + virtio_bus_get_vdev_id(bus));
|
||||
pci_config_set_revision(config, 1);
|
||||
}
|
||||
config[PCI_INTERRUPT_PIN] = 1;
|
||||
|
||||
|
||||
if (modern) {
|
||||
struct virtio_pci_cap cap = {
|
||||
.cap_len = sizeof cap,
|
||||
};
|
||||
struct virtio_pci_notify_cap notify = {
|
||||
.cap.cap_len = sizeof notify,
|
||||
.notify_off_multiplier =
|
||||
cpu_to_le32(QEMU_VIRTIO_PCI_QUEUE_MEM_MULT),
|
||||
};
|
||||
|
||||
/* TODO: add io access for speed */
|
||||
|
||||
virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
|
||||
virtio_pci_modern_regions_init(proxy);
|
||||
virtio_pci_modern_region_map(proxy, &proxy->common, &cap);
|
||||
virtio_pci_modern_region_map(proxy, &proxy->isr, &cap);
|
||||
virtio_pci_modern_region_map(proxy, &proxy->device, &cap);
|
||||
virtio_pci_modern_region_map(proxy, &proxy->notify, ¬ify.cap);
|
||||
pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_PREFETCH |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
&proxy->modern_bar);
|
||||
}
|
||||
|
||||
if (proxy->nvectors &&
|
||||
msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) {
|
||||
msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
|
||||
proxy->msix_bar)) {
|
||||
error_report("unable to init msix vectors to %" PRIu32,
|
||||
proxy->nvectors);
|
||||
proxy->nvectors = 0;
|
||||
@@ -945,16 +1355,20 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||
|
||||
proxy->pci_dev.config_write = virtio_write_config;
|
||||
|
||||
size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
|
||||
+ virtio_bus_get_vdev_config_len(bus);
|
||||
if (size & (size - 1)) {
|
||||
size = 1 << qemu_fls(size);
|
||||
}
|
||||
if (legacy) {
|
||||
size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
|
||||
+ virtio_bus_get_vdev_config_len(bus);
|
||||
if (size & (size - 1)) {
|
||||
size = 1 << qemu_fls(size);
|
||||
}
|
||||
|
||||
memory_region_init_io(&proxy->bar, OBJECT(proxy), &virtio_pci_config_ops,
|
||||
proxy, "virtio-pci", size);
|
||||
pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&proxy->bar);
|
||||
memory_region_init_io(&proxy->bar, OBJECT(proxy),
|
||||
&virtio_pci_config_ops,
|
||||
proxy, "virtio-pci", size);
|
||||
|
||||
pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar,
|
||||
PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar);
|
||||
}
|
||||
|
||||
if (!kvm_has_many_ioeventfds()) {
|
||||
proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
|
||||
@@ -972,12 +1386,47 @@ static void virtio_pci_device_unplugged(DeviceState *d)
|
||||
|
||||
static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
{
|
||||
VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
|
||||
VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
|
||||
VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
|
||||
|
||||
virtio_pci_bus_new(&dev->bus, sizeof(dev->bus), dev);
|
||||
/*
|
||||
* virtio pci bar layout used by default.
|
||||
* subclasses can re-arrange things if needed.
|
||||
*
|
||||
* region 0 -- virtio legacy io bar
|
||||
* region 1 -- msi-x bar
|
||||
* region 4+5 -- virtio modern memory (64bit) bar
|
||||
*
|
||||
*/
|
||||
proxy->legacy_io_bar = 0;
|
||||
proxy->msix_bar = 1;
|
||||
proxy->modern_mem_bar = 4;
|
||||
|
||||
proxy->common.offset = 0x0;
|
||||
proxy->common.size = 0x1000;
|
||||
proxy->common.type = VIRTIO_PCI_CAP_COMMON_CFG;
|
||||
|
||||
proxy->isr.offset = 0x1000;
|
||||
proxy->isr.size = 0x1000;
|
||||
proxy->isr.type = VIRTIO_PCI_CAP_ISR_CFG;
|
||||
|
||||
proxy->device.offset = 0x2000;
|
||||
proxy->device.size = 0x1000;
|
||||
proxy->device.type = VIRTIO_PCI_CAP_DEVICE_CFG;
|
||||
|
||||
proxy->notify.offset = 0x3000;
|
||||
proxy->notify.size =
|
||||
QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * VIRTIO_QUEUE_MAX;
|
||||
proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
|
||||
|
||||
/* subclasses can enforce modern, so do this unconditionally */
|
||||
memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci",
|
||||
2 * QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
|
||||
VIRTIO_QUEUE_MAX);
|
||||
|
||||
virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
|
||||
if (k->realize) {
|
||||
k->realize(dev, errp);
|
||||
k->realize(proxy, errp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,6 +1447,10 @@ static void virtio_pci_reset(DeviceState *qdev)
|
||||
static Property virtio_pci_properties[] = {
|
||||
DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags,
|
||||
VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false),
|
||||
DEFINE_PROP_BIT("disable-legacy", VirtIOPCIProxy, flags,
|
||||
VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false),
|
||||
DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags,
|
||||
VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@@ -1445,6 +1898,120 @@ static const TypeInfo virtio_rng_pci_info = {
|
||||
.class_init = virtio_rng_pci_class_init,
|
||||
};
|
||||
|
||||
/* virtio-input-pci */
|
||||
|
||||
static Property virtio_input_hid_pci_properties[] = {
|
||||
DEFINE_VIRTIO_INPUT_PROPERTIES(VirtIOInputPCI, vdev.input),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
|
||||
{
|
||||
VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
|
||||
DeviceState *vdev = DEVICE(&vinput->vdev);
|
||||
|
||||
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
|
||||
/* force virtio-1.0 */
|
||||
vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
|
||||
vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
|
||||
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
|
||||
}
|
||||
|
||||
static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
|
||||
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = virtio_input_pci_realize;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
|
||||
pcidev_k->class_id = PCI_CLASS_INPUT_OTHER;
|
||||
}
|
||||
|
||||
static void virtio_input_hid_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = virtio_input_hid_pci_properties;
|
||||
}
|
||||
|
||||
static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD;
|
||||
}
|
||||
|
||||
static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass,
|
||||
void *data)
|
||||
{
|
||||
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE;
|
||||
}
|
||||
|
||||
static void virtio_keyboard_initfn(Object *obj)
|
||||
{
|
||||
VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_KEYBOARD);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
static void virtio_mouse_initfn(Object *obj)
|
||||
{
|
||||
VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_MOUSE);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
static void virtio_tablet_initfn(Object *obj)
|
||||
{
|
||||
VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_TABLET);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_input_pci_info = {
|
||||
.name = TYPE_VIRTIO_INPUT_PCI,
|
||||
.parent = TYPE_VIRTIO_PCI,
|
||||
.instance_size = sizeof(VirtIOInputPCI),
|
||||
.class_init = virtio_input_pci_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static const TypeInfo virtio_input_hid_pci_info = {
|
||||
.name = TYPE_VIRTIO_INPUT_HID_PCI,
|
||||
.parent = TYPE_VIRTIO_INPUT_PCI,
|
||||
.instance_size = sizeof(VirtIOInputHIDPCI),
|
||||
.class_init = virtio_input_hid_pci_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static const TypeInfo virtio_keyboard_pci_info = {
|
||||
.name = TYPE_VIRTIO_KEYBOARD_PCI,
|
||||
.parent = TYPE_VIRTIO_INPUT_HID_PCI,
|
||||
.class_init = virtio_input_hid_kbd_pci_class_init,
|
||||
.instance_size = sizeof(VirtIOInputHIDPCI),
|
||||
.instance_init = virtio_keyboard_initfn,
|
||||
};
|
||||
|
||||
static const TypeInfo virtio_mouse_pci_info = {
|
||||
.name = TYPE_VIRTIO_MOUSE_PCI,
|
||||
.parent = TYPE_VIRTIO_INPUT_HID_PCI,
|
||||
.class_init = virtio_input_hid_mouse_pci_class_init,
|
||||
.instance_size = sizeof(VirtIOInputHIDPCI),
|
||||
.instance_init = virtio_mouse_initfn,
|
||||
};
|
||||
|
||||
static const TypeInfo virtio_tablet_pci_info = {
|
||||
.name = TYPE_VIRTIO_TABLET_PCI,
|
||||
.parent = TYPE_VIRTIO_INPUT_HID_PCI,
|
||||
.instance_size = sizeof(VirtIOInputHIDPCI),
|
||||
.instance_init = virtio_tablet_initfn,
|
||||
};
|
||||
|
||||
/* virtio-pci-bus */
|
||||
|
||||
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
|
||||
@@ -1486,6 +2053,11 @@ static const TypeInfo virtio_pci_bus_info = {
|
||||
static void virtio_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&virtio_rng_pci_info);
|
||||
type_register_static(&virtio_input_pci_info);
|
||||
type_register_static(&virtio_input_hid_pci_info);
|
||||
type_register_static(&virtio_keyboard_pci_info);
|
||||
type_register_static(&virtio_mouse_pci_info);
|
||||
type_register_static(&virtio_tablet_pci_info);
|
||||
type_register_static(&virtio_pci_bus_info);
|
||||
type_register_static(&virtio_pci_info);
|
||||
#ifdef CONFIG_VIRTFS
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "hw/virtio/virtio-balloon.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "hw/virtio/virtio-9p.h"
|
||||
#include "hw/virtio/virtio-input.h"
|
||||
#ifdef CONFIG_VIRTFS
|
||||
#include "hw/9pfs/virtio-9p.h"
|
||||
#endif
|
||||
@@ -39,6 +40,8 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
|
||||
typedef struct VirtIONetPCI VirtIONetPCI;
|
||||
typedef struct VHostSCSIPCI VHostSCSIPCI;
|
||||
typedef struct VirtIORngPCI VirtIORngPCI;
|
||||
typedef struct VirtIOInputPCI VirtIOInputPCI;
|
||||
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
|
||||
|
||||
/* virtio-pci-bus */
|
||||
|
||||
@@ -63,6 +66,12 @@ typedef struct VirtioBusClass VirtioPCIBusClass;
|
||||
#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1
|
||||
#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT)
|
||||
|
||||
/* virtio version flags */
|
||||
#define VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT 2
|
||||
#define VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT 3
|
||||
#define VIRTIO_PCI_FLAG_DISABLE_LEGACY (1 << VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT)
|
||||
#define VIRTIO_PCI_FLAG_DISABLE_MODERN (1 << VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT)
|
||||
|
||||
typedef struct {
|
||||
MSIMessage msg;
|
||||
int virq;
|
||||
@@ -85,12 +94,38 @@ typedef struct VirtioPCIClass {
|
||||
void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp);
|
||||
} VirtioPCIClass;
|
||||
|
||||
typedef struct VirtIOPCIRegion {
|
||||
MemoryRegion mr;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t type;
|
||||
} VirtIOPCIRegion;
|
||||
|
||||
struct VirtIOPCIProxy {
|
||||
PCIDevice pci_dev;
|
||||
MemoryRegion bar;
|
||||
VirtIOPCIRegion common;
|
||||
VirtIOPCIRegion isr;
|
||||
VirtIOPCIRegion device;
|
||||
VirtIOPCIRegion notify;
|
||||
MemoryRegion modern_bar;
|
||||
uint32_t legacy_io_bar;
|
||||
uint32_t msix_bar;
|
||||
uint32_t modern_mem_bar;
|
||||
uint32_t flags;
|
||||
uint32_t class_code;
|
||||
uint32_t nvectors;
|
||||
uint32_t dfselect;
|
||||
uint32_t gfselect;
|
||||
uint32_t guest_features[2];
|
||||
struct {
|
||||
uint16_t num;
|
||||
bool enabled;
|
||||
uint32_t desc[2];
|
||||
uint32_t avail[2];
|
||||
uint32_t used[2];
|
||||
} vqs[VIRTIO_QUEUE_MAX];
|
||||
|
||||
bool ioeventfd_disabled;
|
||||
bool ioeventfd_started;
|
||||
VirtIOIRQFD *vector_irqfd;
|
||||
@@ -202,6 +237,30 @@ struct VirtIORngPCI {
|
||||
VirtIORNG vdev;
|
||||
};
|
||||
|
||||
/*
|
||||
* virtio-input-pci: This extends VirtioPCIProxy.
|
||||
*/
|
||||
#define TYPE_VIRTIO_INPUT_PCI "virtio-input-pci"
|
||||
#define VIRTIO_INPUT_PCI(obj) \
|
||||
OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI)
|
||||
|
||||
struct VirtIOInputPCI {
|
||||
VirtIOPCIProxy parent_obj;
|
||||
VirtIOInput vdev;
|
||||
};
|
||||
|
||||
#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci"
|
||||
#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci"
|
||||
#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci"
|
||||
#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci"
|
||||
#define VIRTIO_INPUT_HID_PCI(obj) \
|
||||
OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI)
|
||||
|
||||
struct VirtIOInputHIDPCI {
|
||||
VirtIOPCIProxy parent_obj;
|
||||
VirtIOInputHID vdev;
|
||||
};
|
||||
|
||||
/* Virtio ABI version, if we increment this, we break the guest driver. */
|
||||
#define VIRTIO_PCI_ABI_VERSION 0
|
||||
|
||||
|
||||
@@ -219,7 +219,13 @@ static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
static Property virtio_rng_properties[] = {
|
||||
DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORNG, conf),
|
||||
/* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If
|
||||
* you have an entropy source capable of generating more entropy than this
|
||||
* and you can pass it through via virtio-rng, then hats off to you. Until
|
||||
* then, this is unlimited for all practical purposes.
|
||||
*/
|
||||
DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX),
|
||||
DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
+180
-34
@@ -69,7 +69,6 @@ typedef struct VRing
|
||||
struct VirtQueue
|
||||
{
|
||||
VRing vring;
|
||||
hwaddr pa;
|
||||
uint16_t last_avail_idx;
|
||||
/* Last used index value we have signalled on */
|
||||
uint16_t signalled_used;
|
||||
@@ -93,15 +92,18 @@ struct VirtQueue
|
||||
};
|
||||
|
||||
/* virt queue functions */
|
||||
static void virtqueue_init(VirtQueue *vq)
|
||||
void virtio_queue_update_rings(VirtIODevice *vdev, int n)
|
||||
{
|
||||
hwaddr pa = vq->pa;
|
||||
VRing *vring = &vdev->vq[n].vring;
|
||||
|
||||
vq->vring.desc = pa;
|
||||
vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
|
||||
vq->vring.used = vring_align(vq->vring.avail +
|
||||
offsetof(VRingAvail, ring[vq->vring.num]),
|
||||
vq->vring.align);
|
||||
if (!vring->desc) {
|
||||
/* not yet setup -> nothing to do */
|
||||
return;
|
||||
}
|
||||
vring->avail = vring->desc + vring->num * sizeof(VRingDesc);
|
||||
vring->used = vring_align(vring->avail +
|
||||
offsetof(VRingAvail, ring[vring->num]),
|
||||
vring->align);
|
||||
}
|
||||
|
||||
static inline uint64_t vring_desc_addr(VirtIODevice *vdev, hwaddr desc_pa,
|
||||
@@ -542,15 +544,37 @@ void virtio_update_irq(VirtIODevice *vdev)
|
||||
virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
|
||||
}
|
||||
|
||||
void virtio_set_status(VirtIODevice *vdev, uint8_t val)
|
||||
static int virtio_validate_features(VirtIODevice *vdev)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
|
||||
if (k->validate_features) {
|
||||
return k->validate_features(vdev);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int virtio_set_status(VirtIODevice *vdev, uint8_t val)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
trace_virtio_set_status(vdev, val);
|
||||
|
||||
if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
|
||||
if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) &&
|
||||
val & VIRTIO_CONFIG_S_FEATURES_OK) {
|
||||
int ret = virtio_validate_features(vdev);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k->set_status) {
|
||||
k->set_status(vdev, val);
|
||||
}
|
||||
vdev->status = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool target_words_bigendian(void);
|
||||
@@ -605,7 +629,6 @@ void virtio_reset(void *opaque)
|
||||
vdev->vq[i].vring.avail = 0;
|
||||
vdev->vq[i].vring.used = 0;
|
||||
vdev->vq[i].last_avail_idx = 0;
|
||||
vdev->vq[i].pa = 0;
|
||||
virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR);
|
||||
vdev->vq[i].signalled_used = 0;
|
||||
vdev->vq[i].signalled_used_valid = false;
|
||||
@@ -706,15 +729,119 @@ void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t virtio_config_modern_readb(VirtIODevice *vdev, uint32_t addr)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
uint8_t val;
|
||||
|
||||
if (addr + sizeof(val) > vdev->config_len) {
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
|
||||
k->get_config(vdev, vdev->config);
|
||||
|
||||
val = ldub_p(vdev->config + addr);
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t virtio_config_modern_readw(VirtIODevice *vdev, uint32_t addr)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
uint16_t val;
|
||||
|
||||
if (addr + sizeof(val) > vdev->config_len) {
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
|
||||
k->get_config(vdev, vdev->config);
|
||||
|
||||
val = lduw_le_p(vdev->config + addr);
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t virtio_config_modern_readl(VirtIODevice *vdev, uint32_t addr)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
uint32_t val;
|
||||
|
||||
if (addr + sizeof(val) > vdev->config_len) {
|
||||
return (uint32_t)-1;
|
||||
}
|
||||
|
||||
k->get_config(vdev, vdev->config);
|
||||
|
||||
val = ldl_le_p(vdev->config + addr);
|
||||
return val;
|
||||
}
|
||||
|
||||
void virtio_config_modern_writeb(VirtIODevice *vdev,
|
||||
uint32_t addr, uint32_t data)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
uint8_t val = data;
|
||||
|
||||
if (addr + sizeof(val) > vdev->config_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
stb_p(vdev->config + addr, val);
|
||||
|
||||
if (k->set_config) {
|
||||
k->set_config(vdev, vdev->config);
|
||||
}
|
||||
}
|
||||
|
||||
void virtio_config_modern_writew(VirtIODevice *vdev,
|
||||
uint32_t addr, uint32_t data)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
uint16_t val = data;
|
||||
|
||||
if (addr + sizeof(val) > vdev->config_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
stw_le_p(vdev->config + addr, val);
|
||||
|
||||
if (k->set_config) {
|
||||
k->set_config(vdev, vdev->config);
|
||||
}
|
||||
}
|
||||
|
||||
void virtio_config_modern_writel(VirtIODevice *vdev,
|
||||
uint32_t addr, uint32_t data)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
uint32_t val = data;
|
||||
|
||||
if (addr + sizeof(val) > vdev->config_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
stl_le_p(vdev->config + addr, val);
|
||||
|
||||
if (k->set_config) {
|
||||
k->set_config(vdev, vdev->config);
|
||||
}
|
||||
}
|
||||
|
||||
void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr)
|
||||
{
|
||||
vdev->vq[n].pa = addr;
|
||||
virtqueue_init(&vdev->vq[n]);
|
||||
vdev->vq[n].vring.desc = addr;
|
||||
virtio_queue_update_rings(vdev, n);
|
||||
}
|
||||
|
||||
hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n)
|
||||
{
|
||||
return vdev->vq[n].pa;
|
||||
return vdev->vq[n].vring.desc;
|
||||
}
|
||||
|
||||
void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
|
||||
hwaddr avail, hwaddr used)
|
||||
{
|
||||
vdev->vq[n].vring.desc = desc;
|
||||
vdev->vq[n].vring.avail = avail;
|
||||
vdev->vq[n].vring.used = used;
|
||||
}
|
||||
|
||||
void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
|
||||
@@ -728,7 +855,6 @@ void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
|
||||
return;
|
||||
}
|
||||
vdev->vq[n].vring.num = num;
|
||||
virtqueue_init(&vdev->vq[n]);
|
||||
}
|
||||
|
||||
VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector)
|
||||
@@ -771,6 +897,11 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
|
||||
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
|
||||
/* virtio-1 compliant devices cannot change the alignment */
|
||||
if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
|
||||
error_report("tried to modify queue alignment for virtio-1 device");
|
||||
return;
|
||||
}
|
||||
/* Check that the transport told us it was going to do this
|
||||
* (so a buggy transport will immediately assert rather than
|
||||
* silently failing to migrate this state)
|
||||
@@ -778,7 +909,7 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
|
||||
assert(k->has_variable_vring_alignment);
|
||||
|
||||
vdev->vq[n].vring.align = align;
|
||||
virtqueue_init(&vdev->vq[n]);
|
||||
virtio_queue_update_rings(vdev, n);
|
||||
}
|
||||
|
||||
void virtio_queue_notify_vq(VirtQueue *vq)
|
||||
@@ -895,6 +1026,7 @@ void virtio_notify_config(VirtIODevice *vdev)
|
||||
return;
|
||||
|
||||
vdev->isr |= 0x03;
|
||||
vdev->generation++;
|
||||
virtio_notify_vector(vdev, vdev->config_vector);
|
||||
}
|
||||
|
||||
@@ -903,7 +1035,11 @@ static bool virtio_device_endian_needed(void *opaque)
|
||||
VirtIODevice *vdev = opaque;
|
||||
|
||||
assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN);
|
||||
return vdev->device_endian != virtio_default_endian();
|
||||
if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
|
||||
return vdev->device_endian != virtio_default_endian();
|
||||
}
|
||||
/* Devices conforming to VIRTIO 1.0 or later are always LE. */
|
||||
return vdev->device_endian != VIRTIO_DEVICE_ENDIAN_LITTLE;
|
||||
}
|
||||
|
||||
static bool virtio_64bit_features_needed(void *opaque)
|
||||
@@ -917,6 +1053,7 @@ static const VMStateDescription vmstate_virtio_device_endian = {
|
||||
.name = "virtio/device_endian",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = &virtio_device_endian_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(device_endian, VirtIODevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -927,6 +1064,7 @@ static const VMStateDescription vmstate_virtio_64bit_features = {
|
||||
.name = "virtio/64bit_features",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = &virtio_64bit_features_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(guest_features, VirtIODevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
@@ -941,16 +1079,10 @@ static const VMStateDescription vmstate_virtio = {
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (VMStateSubsection[]) {
|
||||
{
|
||||
.vmsd = &vmstate_virtio_device_endian,
|
||||
.needed = &virtio_device_endian_needed
|
||||
},
|
||||
{
|
||||
.vmsd = &vmstate_virtio_64bit_features,
|
||||
.needed = &virtio_64bit_features_needed
|
||||
},
|
||||
{ 0 }
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_virtio_device_endian,
|
||||
&vmstate_virtio_64bit_features,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -988,7 +1120,8 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
|
||||
if (k->has_variable_vring_alignment) {
|
||||
qemu_put_be32(f, vdev->vq[i].vring.align);
|
||||
}
|
||||
qemu_put_be64(f, vdev->vq[i].pa);
|
||||
/* XXX virtio-1 devices */
|
||||
qemu_put_be64(f, vdev->vq[i].vring.desc);
|
||||
qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
|
||||
if (k->save_queue) {
|
||||
k->save_queue(qbus->parent, i, f);
|
||||
@@ -1003,7 +1136,7 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
|
||||
vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
|
||||
}
|
||||
|
||||
int virtio_set_features(VirtIODevice *vdev, uint64_t val)
|
||||
static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val)
|
||||
{
|
||||
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||
bool bad = (val & ~(vdev->host_features)) != 0;
|
||||
@@ -1016,6 +1149,18 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
|
||||
return bad ? -1 : 0;
|
||||
}
|
||||
|
||||
int virtio_set_features(VirtIODevice *vdev, uint64_t val)
|
||||
{
|
||||
/*
|
||||
* The driver must not attempt to set features after feature negotiation
|
||||
* has finished.
|
||||
*/
|
||||
if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return virtio_set_features_nocheck(vdev, val);
|
||||
}
|
||||
|
||||
int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
|
||||
{
|
||||
int i, ret;
|
||||
@@ -1072,13 +1217,14 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
|
||||
if (k->has_variable_vring_alignment) {
|
||||
vdev->vq[i].vring.align = qemu_get_be32(f);
|
||||
}
|
||||
vdev->vq[i].pa = qemu_get_be64(f);
|
||||
vdev->vq[i].vring.desc = qemu_get_be64(f);
|
||||
qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
|
||||
vdev->vq[i].signalled_used_valid = false;
|
||||
vdev->vq[i].notification = true;
|
||||
|
||||
if (vdev->vq[i].pa) {
|
||||
virtqueue_init(&vdev->vq[i]);
|
||||
if (vdev->vq[i].vring.desc) {
|
||||
/* XXX virtio-1 devices */
|
||||
virtio_queue_update_rings(vdev, i);
|
||||
} else if (vdev->vq[i].last_avail_idx) {
|
||||
error_report("VQ %d address 0x0 "
|
||||
"inconsistent with Host index 0x%x",
|
||||
@@ -1118,14 +1264,14 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
|
||||
* host_features.
|
||||
*/
|
||||
uint64_t features64 = vdev->guest_features;
|
||||
if (virtio_set_features(vdev, features64) < 0) {
|
||||
if (virtio_set_features_nocheck(vdev, features64) < 0) {
|
||||
error_report("Features 0x%" PRIx64 " unsupported. "
|
||||
"Allowed features: 0x%" PRIx64,
|
||||
features64, vdev->host_features);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (virtio_set_features(vdev, features) < 0) {
|
||||
if (virtio_set_features_nocheck(vdev, features) < 0) {
|
||||
error_report("Features 0x%x unsupported. "
|
||||
"Allowed features: 0x%" PRIx64,
|
||||
features, vdev->host_features);
|
||||
@@ -1134,7 +1280,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (vdev->vq[i].pa) {
|
||||
if (vdev->vq[i].vring.desc) {
|
||||
uint16_t nheads;
|
||||
nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
|
||||
/* Check it isn't doing strange things with descriptor numbers. */
|
||||
|
||||
@@ -714,9 +714,7 @@ int xen_be_init(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) {
|
||||
goto err;
|
||||
}
|
||||
qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL);
|
||||
|
||||
if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
|
||||
/* Check if xen_init() have been called */
|
||||
|
||||
+1
-1
@@ -241,7 +241,7 @@ bool aio_dispatch(AioContext *ctx);
|
||||
bool aio_poll(AioContext *ctx, bool blocking);
|
||||
|
||||
/* Register a file descriptor and associated callbacks. Behaves very similarly
|
||||
* to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will
|
||||
* to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will
|
||||
* be invoked when using aio_poll().
|
||||
*
|
||||
* Code that invokes AIO completion functions should rely on this function
|
||||
|
||||
@@ -126,10 +126,10 @@ void cpu_flush_icache_range(hwaddr start, int len);
|
||||
extern struct MemoryRegion io_mem_rom;
|
||||
extern struct MemoryRegion io_mem_notdirty;
|
||||
|
||||
typedef void (RAMBlockIterFunc)(void *host_addr,
|
||||
typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr,
|
||||
ram_addr_t offset, ram_addr_t length, void *opaque);
|
||||
|
||||
void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque);
|
||||
int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user