Compare commits
375 Commits
pull-gtk-2
...
pull-input
Author | SHA1 | Date | |
---|---|---|---|
|
a5d4d7b580 | ||
|
006a5edebe | ||
|
6f2b9a5b24 | ||
|
0c8ff723bd | ||
|
2a8327e8a8 | ||
|
8c52f0cbba | ||
|
0a3346f5de | ||
|
cb4e0f9ddf | ||
|
ad7020a7e7 | ||
|
033af8e9aa | ||
|
a87310a62d | ||
|
6fad9e986b | ||
|
72e3875485 | ||
|
a88bbb006a | ||
|
a6c3ed2474 | ||
|
7144612370 | ||
|
be67e9ab97 | ||
|
f44c475cb6 | ||
|
4e5d45ae57 | ||
|
f27183abaa | ||
|
9aaaa18194 | ||
|
8bac22423e | ||
|
53432dc9ea | ||
|
d87636b18f | ||
|
daeba9699d | ||
|
0210afe669 | ||
|
a3590dacce | ||
|
a8e3fbedc8 | ||
|
2e4450ff43 | ||
|
a31bdae5a7 | ||
|
bc2256c4ae | ||
|
f08f9271bf | ||
|
b9174d4f25 | ||
|
b1028b4e86 | ||
|
799810fb28 | ||
|
a59d31a1eb | ||
|
cfe67cef48 | ||
|
b58850e79d | ||
|
6396a193d3 | ||
|
2e5577bc55 | ||
|
d6a6b13ea1 | ||
|
f6bda88ff8 | ||
|
6cb0b013a1 | ||
|
3281af8114 | ||
|
b061a82b8a | ||
|
decf4f807b | ||
|
ba890a9b25 | ||
|
ffdb1409a7 | ||
|
89e9429c3c | ||
|
e4a511f8cc | ||
|
965eb2fcdf | ||
|
ae46e23964 | ||
|
fb1a3a051d | ||
|
397c767b2d | ||
|
693a3e01af | ||
|
270746142c | ||
|
1e7398a140 | ||
|
74de5504fd | ||
|
5ba03e2dd7 | ||
|
1717388645 | ||
|
5be7d9f1b1 | ||
|
4ee9b43be9 | ||
|
8524f1c79e | ||
|
473a49460d | ||
|
ff5397bc72 | ||
|
53f77e4562 | ||
|
6bc5cf92c0 | ||
|
1590d266d9 | ||
|
8ffe756da0 | ||
|
e1d4210c3a | ||
|
c5ecd7e18f | ||
|
ae0a7a1090 | ||
|
4f35680023 | ||
|
f9a1427361 | ||
|
75276710ae | ||
|
00e4b285a3 | ||
|
4d076d67c2 | ||
|
e565d934d2 | ||
|
a136608727 | ||
|
8608d25251 | ||
|
54414047ec | ||
|
12c7079449 | ||
|
836c3b01d2 | ||
|
0311c5bde3 | ||
|
1b58f5a7f6 | ||
|
e207527751 | ||
|
a3122b681a | ||
|
c80cd6bb9c | ||
|
04b7a1523d | ||
|
41d283bdab | ||
|
332f64073b | ||
|
06b008d941 | ||
|
bea2f0982b | ||
|
28452758c4 | ||
|
ebe7d8b166 | ||
|
d4862a87e3 | ||
|
9dacf32d2c | ||
|
66ae13bb9e | ||
|
c69403fcd4 | ||
|
5e031072e7 | ||
|
67633bb4f7 | ||
|
f754c3c9cc | ||
|
1f68f1d36c | ||
|
3da0ab3529 | ||
|
83bb161299 | ||
|
2f54394997 | ||
|
311918b979 | ||
|
8d302e7675 | ||
|
f0e0d817c2 | ||
|
2c2275eb41 | ||
|
777c98c32c | ||
|
d453d10383 | ||
|
a8f931a931 | ||
|
fb01bf4c6b | ||
|
6da528d14d | ||
|
fc89efe693 | ||
|
d7ce6b7a0b | ||
|
2e83c49626 | ||
|
ad8a4570ad | ||
|
2ecacb0b4b | ||
|
8df7eef305 | ||
|
cbed0ba78f | ||
|
cc0d079d45 | ||
|
7107e5a756 | ||
|
06e3c077da | ||
|
a499973ff3 | ||
|
d49f4ab48e | ||
|
ae52e585bf | ||
|
a09f4a9d19 | ||
|
8c29f8d6b9 | ||
|
f3bcd42683 | ||
|
7edf2f0ec4 | ||
|
10500ce260 | ||
|
f8d30a4f96 | ||
|
bf6667d63e | ||
|
93f6d1c160 | ||
|
4316536bf4 | ||
|
45c874ebba | ||
|
1d085f6cae | ||
|
876e23cb2e | ||
|
c2aeb2586b | ||
|
1dfe73b94d | ||
|
f264d51d8a | ||
|
b8a0d75ef8 | ||
|
3977ee5d7a | ||
|
63a183ed0e | ||
|
13689d4364 | ||
|
8f325f568f | ||
|
a8e81b319d | ||
|
b7cc4e82f0 | ||
|
8e5d75c950 | ||
|
5e5cf9e35f | ||
|
8085ce63c5 | ||
|
62b44f059a | ||
|
13e1e476b4 | ||
|
8e079caf82 | ||
|
ce3203464b | ||
|
1fd9f2df24 | ||
|
14c3032a7e | ||
|
eb5e1d3c85 | ||
|
8f4d260e70 | ||
|
ac00c79ff6 | ||
|
13b72b2b9a | ||
|
8772de2c53 | ||
|
7525465e6d | ||
|
a79e0218e0 | ||
|
644ead5be1 | ||
|
235069a380 | ||
|
4e42a6ca37 | ||
|
fc1891c74a | ||
|
b500e4db8e | ||
|
6a084ea39a | ||
|
46bca5404b | ||
|
8369e339d2 | ||
|
d95d7d802c | ||
|
fbb7ef56d5 | ||
|
6dd35fd81e | ||
|
2762955f72 | ||
|
73ad33ef7b | ||
|
06ac27f683 | ||
|
65eb1e6b4c | ||
|
81ebb07c56 | ||
|
d1f52a1d70 | ||
|
191e1f0acd | ||
|
49dd6d0d33 | ||
|
f2dcc6cec2 | ||
|
4045a85ad1 | ||
|
765b37da3f | ||
|
9a644c4b4d | ||
|
5706db1deb | ||
|
307119e7d9 | ||
|
f3e3b083d4 | ||
|
3cec7cc22f | ||
|
14382605da | ||
|
0bac111167 | ||
|
79cb1f1d69 | ||
|
8be656b87c | ||
|
8aeaa055f5 | ||
|
67251a3113 | ||
|
bddcec3745 | ||
|
6e93e7c41f | ||
|
ae81693004 | ||
|
6ee4ce1ee7 | ||
|
f3930ed0bb | ||
|
18edf289a8 | ||
|
54861b9280 | ||
|
a646836784 | ||
|
ea6828d81b | ||
|
ef1919df26 | ||
|
7990d2c99c | ||
|
bd50530a9f | ||
|
0a2df857a7 | ||
|
a68197ff5b | ||
|
53a2951312 | ||
|
06207b0ff5 | ||
|
5270b6a0d0 | ||
|
b8684454e1 | ||
|
90df601f06 | ||
|
61f0ed1d54 | ||
|
bc85ef265a | ||
|
a4291eafc5 | ||
|
57e2166959 | ||
|
9aa711d750 | ||
|
ff793890fa | ||
|
9faffeb777 | ||
|
2db33f88d2 | ||
|
a291d5d9b9 | ||
|
b8fe1694e5 | ||
|
db6283385c | ||
|
76f4afb40f | ||
|
1fee955f9c | ||
|
2ff1f2e3a3 | ||
|
0e5b0a2d54 | ||
|
f4a769abaa | ||
|
da5e1de95b | ||
|
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 | ||
|
7a4dfd1e4a | ||
|
a94f0c5ca2 | ||
|
c5d4dac86b | ||
|
9eafb62d47 | ||
|
2c84167b4e | ||
|
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 | ||
|
795dc6e46d | ||
|
f9a535e089 | ||
|
d67f5fe63c | ||
|
8fc639af4b | ||
|
188f24c2c1 | ||
|
d7933ef3ac | ||
|
d8e3b729cf | ||
|
afa25c4bb5 | ||
|
0b70743d4f | ||
|
169b71331e | ||
|
4ebc736e99 | ||
|
c96d9286a6 | ||
|
39e16a5b70 | ||
|
bd8f1ebce4 | ||
|
409b52bfe1 | ||
|
1670735dd7 | ||
|
89ae0ff9b7 | ||
|
104655a5c8 | ||
|
3df5de64f0 | ||
|
84689cbb97 | ||
|
f2f62c4db2 | ||
|
d791d60f1c | ||
|
95c357bc46 | ||
|
dc6e3e1e1a | ||
|
b421f3f52a | ||
|
dd8205130b | ||
|
a3d586f704 | ||
|
9b1d21c53b | ||
|
ce9782f40a | ||
|
adc370a48f | ||
|
3b4afc9e75 | ||
|
be3a8c53b4 | ||
|
71c199c81d | ||
|
7c979afd11 | ||
|
eab9944c78 | ||
|
5a9259a0b5 | ||
|
c6e484707f | ||
|
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 | ||
|
24bfa207ef | ||
|
08d49df0db |
21
MAINTAINERS
21
MAINTAINERS
@@ -356,6 +356,13 @@ F: hw/misc/zynq_slcr.c
|
||||
F: hw/*/cadence_*
|
||||
F: hw/ssi/xilinx_spips.c
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <zhaoshenglong@huawei.com>
|
||||
M: Shannon Zhao <shannon.zhao@linaro.org>
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
F: include/hw/arm/virt-acpi-build.h
|
||||
|
||||
CRIS Machines
|
||||
-------------
|
||||
Axis Dev88
|
||||
@@ -695,6 +702,7 @@ virtio
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: hw/*/virtio*
|
||||
F: net/vhost-user.c
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
@@ -720,6 +728,12 @@ S: Supported
|
||||
F: hw/s390x/virtio-ccw.[hc]
|
||||
T: git git://github.com/cohuck/qemu virtio-ccw-upstr
|
||||
|
||||
virtio-input
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/input/virtio-input*.c
|
||||
F: include/hw/virtio/virtio-input.h
|
||||
|
||||
virtio-serial
|
||||
M: Amit Shah <amit.shah@redhat.com>
|
||||
S: Supported
|
||||
@@ -770,7 +784,6 @@ F: hw/net/rocker/
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
M: Vassili Karpov (malc) <av1474@comtv.ru>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: audio/
|
||||
@@ -946,7 +959,10 @@ M: Markus Armbruster <armbru@redhat.com>
|
||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
S: Supported
|
||||
F: qapi/
|
||||
X: qapi/*.json
|
||||
F: tests/qapi-schema/
|
||||
F: scripts/qapi*
|
||||
F: docs/qapi*
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
QAPI Schema
|
||||
@@ -954,6 +970,7 @@ M: Eric Blake <eblake@redhat.com>
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
S: Supported
|
||||
F: qapi-schema.json
|
||||
F: qapi/*.json
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
QObject
|
||||
@@ -1014,8 +1031,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/
|
||||
|
||||
|
26
Makefile
26
Makefile
@@ -74,7 +74,7 @@ Makefile: ;
|
||||
configure: ;
|
||||
|
||||
.PHONY: all clean cscope distclean dvi html info install install-doc \
|
||||
pdf recurse-all speed test dist
|
||||
pdf recurse-all speed test dist msi
|
||||
|
||||
$(call set-vpath, $(SRC_PATH))
|
||||
|
||||
@@ -287,10 +287,32 @@ $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
|
||||
qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a
|
||||
$(call LINK, $^)
|
||||
|
||||
ifdef QEMU_GA_MSI_ENABLED
|
||||
QEMU_GA_MSI=qemu-ga-$(ARCH).msi
|
||||
|
||||
msi: ${QEMU_GA_MSI}
|
||||
|
||||
$(QEMU_GA_MSI): qemu-ga.exe
|
||||
|
||||
ifdef QEMU_GA_MSI_WITH_VSS
|
||||
$(QEMU_GA_MSI): qga/vss-win32/qga-vss.dll
|
||||
endif
|
||||
|
||||
$(QEMU_GA_MSI): config-host.mak
|
||||
|
||||
$(QEMU_GA_MSI): qga/installer/qemu-ga.wxs
|
||||
$(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" \
|
||||
wixl -o $@ $(QEMU_GA_MSI_ARCH) $(QEMU_GA_MSI_WITH_VSS) $(QEMU_GA_MSI_MINGW_DLL_PATH) $<, " WIXL $@")
|
||||
else
|
||||
msi:
|
||||
@echo MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)
|
||||
endif
|
||||
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
rm -f *.msi
|
||||
find . \( -name '*.l[oa]' -o -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
|
||||
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
|
||||
rm -f fsdev/*.pod
|
||||
@@ -342,7 +364,7 @@ bepo cz
|
||||
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
|
@@ -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
arch_init.c
1611
arch_init.c
File diff suppressed because it is too large
Load Diff
8
async.c
8
async.c
@@ -280,6 +280,12 @@ static void aio_timerlist_notify(void *opaque)
|
||||
aio_notify(opaque);
|
||||
}
|
||||
|
||||
static void aio_rfifolock_cb(void *opaque)
|
||||
{
|
||||
/* Kick owner thread in case they are blocked in aio_poll() */
|
||||
aio_notify(opaque);
|
||||
}
|
||||
|
||||
AioContext *aio_context_new(Error **errp)
|
||||
{
|
||||
int ret;
|
||||
@@ -297,7 +303,7 @@ AioContext *aio_context_new(Error **errp)
|
||||
event_notifier_test_and_clear);
|
||||
ctx->thread_pool = NULL;
|
||||
qemu_mutex_init(&ctx->bh_lock);
|
||||
rfifolock_init(&ctx->lock, NULL, NULL);
|
||||
rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
|
||||
timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
|
||||
|
||||
return ctx;
|
||||
|
@@ -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)
|
||||
|
@@ -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 */ }
|
||||
};
|
||||
|
||||
|
@@ -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 */ }
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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 */ }
|
||||
|
@@ -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",
|
||||
|
@@ -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 */ }
|
||||
|
557
audio/esdaudio.c
557
audio/esdaudio.c
@@ -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)
|
||||
};
|
@@ -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;
|
||||
|
140
audio/ossaudio.c
140
audio/ossaudio.c
@@ -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 */ }
|
||||
};
|
||||
|
||||
|
105
audio/paaudio.c
105
audio/paaudio.c
@@ -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 */ }
|
||||
|
@@ -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[] = {
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
};
|
@@ -113,24 +113,17 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, void *opaque,
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_get_policy(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
static int
|
||||
host_memory_backend_get_policy(Object *obj, Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
int policy = backend->policy;
|
||||
|
||||
visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp);
|
||||
return backend->policy;
|
||||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_set_policy(Object *obj, Visitor *v, void *opaque,
|
||||
const char *name, Error **errp)
|
||||
host_memory_backend_set_policy(Object *obj, int policy, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
int policy;
|
||||
|
||||
visit_type_enum(v, &policy, HostMemPolicy_lookup, NULL, name, errp);
|
||||
backend->policy = policy;
|
||||
|
||||
#ifndef CONFIG_NUMA
|
||||
@@ -252,9 +245,10 @@ static void host_memory_backend_init(Object *obj)
|
||||
object_property_add(obj, "host-nodes", "int",
|
||||
host_memory_backend_get_host_nodes,
|
||||
host_memory_backend_set_host_nodes, NULL, NULL, NULL);
|
||||
object_property_add(obj, "policy", "str",
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, NULL, NULL, NULL);
|
||||
object_property_add_enum(obj, "policy", "HostMemPolicy",
|
||||
HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, NULL);
|
||||
}
|
||||
|
||||
MemoryRegion *
|
||||
|
277
block.c
277
block.c
@@ -36,6 +36,7 @@
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi-event.h"
|
||||
#include "block/throttle-groups.h"
|
||||
|
||||
#ifdef CONFIG_BSD
|
||||
#include <sys/types.h>
|
||||
@@ -79,6 +80,12 @@ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
|
||||
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
||||
QLIST_HEAD_INITIALIZER(bdrv_drivers);
|
||||
|
||||
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role,
|
||||
BlockDriver *drv, Error **errp);
|
||||
|
||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
|
||||
/* If non-zero, use only whitelisted block drivers */
|
||||
static int use_bdrv_whitelist;
|
||||
@@ -682,8 +689,8 @@ static int bdrv_temp_snapshot_flags(int flags)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get, based on the given flags for
|
||||
* the parent BDS
|
||||
* Returns the flags that bs->file should get if a protocol driver is expected,
|
||||
* based on the given flags for the parent BDS
|
||||
*/
|
||||
static int bdrv_inherited_flags(int flags)
|
||||
{
|
||||
@@ -700,6 +707,25 @@ static int bdrv_inherited_flags(int flags)
|
||||
return flags;
|
||||
}
|
||||
|
||||
const BdrvChildRole child_file = {
|
||||
.inherit_flags = bdrv_inherited_flags,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get if the use of formats (and not
|
||||
* only protocols) is permitted for it, based on the given flags for the parent
|
||||
* BDS
|
||||
*/
|
||||
static int bdrv_inherited_fmt_flags(int parent_flags)
|
||||
{
|
||||
int flags = child_file.inherit_flags(parent_flags);
|
||||
return flags & ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
const BdrvChildRole child_format = {
|
||||
.inherit_flags = bdrv_inherited_fmt_flags,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->backing_hd should get, based on the given flags
|
||||
* for the parent BDS
|
||||
@@ -715,6 +741,10 @@ static int bdrv_backing_flags(int flags)
|
||||
return flags;
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_backing = {
|
||||
.inherit_flags = bdrv_backing_flags,
|
||||
};
|
||||
|
||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
{
|
||||
int open_flags = flags | BDRV_O_CACHE_WB;
|
||||
@@ -767,6 +797,19 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
|
||||
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
|
||||
}
|
||||
|
||||
static QemuOptsList bdrv_runtime_opts = {
|
||||
.name = "bdrv_common",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
|
||||
.desc = {
|
||||
{
|
||||
.name = "node-name",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Node name of the block device node",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Common part for opening disk images and files
|
||||
*
|
||||
@@ -778,6 +821,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
int ret, open_flags;
|
||||
const char *filename;
|
||||
const char *node_name = NULL;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(drv != NULL);
|
||||
@@ -798,23 +842,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
|
||||
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
||||
|
||||
node_name = qdict_get_try_str(options, "node-name");
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
node_name = qemu_opt_get(opts, "node-name");
|
||||
bdrv_assign_node_name(bs, node_name, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
qdict_del(options, "node-name");
|
||||
|
||||
/* bdrv_open() with directly using a protocol as drv. This layer is already
|
||||
* opened, so assign it to bs (while file becomes a closed BlockDriverState)
|
||||
* and return immediately. */
|
||||
if (file != NULL && drv->bdrv_file_open) {
|
||||
bdrv_swap(file, bs);
|
||||
return 0;
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
bs->guest_block_size = 512;
|
||||
bs->request_alignment = 512;
|
||||
bs->zero_beyond_eof = true;
|
||||
@@ -827,7 +870,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
? "Driver '%s' can only be used for read-only devices"
|
||||
: "Driver '%s' is not whitelisted",
|
||||
drv->format_name);
|
||||
return -ENOTSUP;
|
||||
ret = -ENOTSUP;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
|
||||
@@ -836,7 +880,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
bdrv_enable_copy_on_read(bs);
|
||||
} else {
|
||||
error_setg(errp, "Can't use copy-on-read on read-only device");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -902,6 +947,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
assert(bdrv_opt_mem_align(bs) != 0);
|
||||
assert(bdrv_min_mem_align(bs) != 0);
|
||||
assert((bs->request_alignment != 0) || bs->sg);
|
||||
|
||||
qemu_opts_del(opts);
|
||||
return 0;
|
||||
|
||||
free_and_fail:
|
||||
@@ -909,6 +956,8 @@ free_and_fail:
|
||||
g_free(bs->opaque);
|
||||
bs->opaque = NULL;
|
||||
bs->drv = NULL;
|
||||
fail_opts:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -942,14 +991,17 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
/*
|
||||
* Fills in default options for opening images and converts the legacy
|
||||
* filename/flags pair to option QDict entries.
|
||||
* The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a
|
||||
* block driver has been specified explicitly.
|
||||
*/
|
||||
static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
||||
BlockDriver *drv, Error **errp)
|
||||
static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||
int *flags, BlockDriver *drv, Error **errp)
|
||||
{
|
||||
const char *filename = *pfilename;
|
||||
const char *drvname;
|
||||
bool protocol = flags & BDRV_O_PROTOCOL;
|
||||
bool protocol = *flags & BDRV_O_PROTOCOL;
|
||||
bool parse_filename = false;
|
||||
BlockDriver *tmp_drv;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Parse json: pseudo-protocol */
|
||||
@@ -967,6 +1019,24 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
||||
*pfilename = filename = NULL;
|
||||
}
|
||||
|
||||
drvname = qdict_get_try_str(*options, "driver");
|
||||
|
||||
/* If the user has explicitly specified the driver, this choice should
|
||||
* override the BDRV_O_PROTOCOL flag */
|
||||
tmp_drv = drv;
|
||||
if (!tmp_drv && drvname) {
|
||||
tmp_drv = bdrv_find_format(drvname);
|
||||
}
|
||||
if (tmp_drv) {
|
||||
protocol = tmp_drv->bdrv_file_open;
|
||||
}
|
||||
|
||||
if (protocol) {
|
||||
*flags |= BDRV_O_PROTOCOL;
|
||||
} else {
|
||||
*flags &= ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
/* Fetch the file name from the options QDict if necessary */
|
||||
if (protocol && filename) {
|
||||
if (!qdict_haskey(*options, "filename")) {
|
||||
@@ -981,7 +1051,6 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
|
||||
|
||||
/* Find the right block driver */
|
||||
filename = qdict_get_try_str(*options, "filename");
|
||||
drvname = qdict_get_try_str(*options, "driver");
|
||||
|
||||
if (drv) {
|
||||
if (drvname) {
|
||||
@@ -1118,9 +1187,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||
}
|
||||
|
||||
assert(bs->backing_hd == NULL);
|
||||
ret = bdrv_open(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL, NULL, options,
|
||||
bdrv_backing_flags(bs->open_flags), NULL, &local_err);
|
||||
ret = bdrv_open_inherit(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL,
|
||||
NULL, options, 0, bs, &child_backing,
|
||||
NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(backing_hd);
|
||||
backing_hd = NULL;
|
||||
@@ -1154,7 +1224,8 @@ free_exit:
|
||||
* To conform with the behavior of bdrv_open(), *pbs has to be NULL.
|
||||
*/
|
||||
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
QDict *options, const char *bdref_key, int flags,
|
||||
QDict *options, const char *bdref_key,
|
||||
BlockDriverState* parent, const BdrvChildRole *child_role,
|
||||
bool allow_none, Error **errp)
|
||||
{
|
||||
QDict *image_options;
|
||||
@@ -1182,7 +1253,8 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp);
|
||||
ret = bdrv_open_inherit(pbs, filename, reference, image_options, 0,
|
||||
parent, child_role, NULL, errp);
|
||||
|
||||
done:
|
||||
qdict_del(options, bdref_key);
|
||||
@@ -1254,6 +1326,19 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
const BdrvChildRole *child_role)
|
||||
{
|
||||
BdrvChild *child = g_new(BdrvChild, 1);
|
||||
*child = (BdrvChild) {
|
||||
.bs = child_bs,
|
||||
.role = child_role,
|
||||
};
|
||||
|
||||
QLIST_INSERT_HEAD(&parent_bs->children, child, next);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens a disk image (raw, qcow2, vmdk, ...)
|
||||
*
|
||||
@@ -1269,9 +1354,11 @@ out:
|
||||
* should be opened. If specified, neither options nor a filename may be given,
|
||||
* nor can an existing BDS be reused (that is, *pbs has to be NULL).
|
||||
*/
|
||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriver *drv, Error **errp)
|
||||
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildRole *child_role,
|
||||
BlockDriver *drv, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverState *file = NULL, *bs;
|
||||
@@ -1280,6 +1367,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
int snapshot_flags = 0;
|
||||
|
||||
assert(pbs);
|
||||
assert(!child_role || !flags);
|
||||
assert(!child_role == !parent);
|
||||
|
||||
if (reference) {
|
||||
bool options_non_empty = options ? qdict_size(options) : false;
|
||||
@@ -1302,6 +1391,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
return -ENODEV;
|
||||
}
|
||||
bdrv_ref(bs);
|
||||
if (child_role) {
|
||||
bdrv_attach_child(parent, bs, child_role);
|
||||
}
|
||||
*pbs = bs;
|
||||
return 0;
|
||||
}
|
||||
@@ -1317,7 +1409,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
options = qdict_new();
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err);
|
||||
if (child_role) {
|
||||
bs->inherits_from = parent;
|
||||
flags = child_role->inherit_flags(parent->open_flags);
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1336,12 +1433,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
}
|
||||
|
||||
assert(drvname || !(flags & BDRV_O_PROTOCOL));
|
||||
if (drv && !drv->bdrv_file_open) {
|
||||
/* If the user explicitly wants a format driver here, we'll need to add
|
||||
* another layer for the protocol in bs->file */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
bs->options = options;
|
||||
options = qdict_clone_shallow(options);
|
||||
|
||||
@@ -1356,9 +1449,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
}
|
||||
|
||||
assert(file == NULL);
|
||||
bs->open_flags = flags;
|
||||
ret = bdrv_open_image(&file, filename, options, "file",
|
||||
bdrv_inherited_flags(flags),
|
||||
true, &local_err);
|
||||
bs, &child_file, true, &local_err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1377,6 +1470,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */
|
||||
assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open);
|
||||
/* file must be NULL if a protocol BDS is about to be created
|
||||
* (the inverse results in an error message from bdrv_open_common()) */
|
||||
assert(!(flags & BDRV_O_PROTOCOL) || !file);
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
@@ -1439,6 +1538,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
goto close_and_fail;
|
||||
}
|
||||
|
||||
if (child_role) {
|
||||
bdrv_attach_child(parent, bs, child_role);
|
||||
}
|
||||
|
||||
QDECREF(options);
|
||||
*pbs = bs;
|
||||
return 0;
|
||||
@@ -1475,6 +1578,14 @@ close_and_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||
const char *reference, QDict *options, int flags,
|
||||
BlockDriver *drv, Error **errp)
|
||||
{
|
||||
return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
|
||||
NULL, drv, errp);
|
||||
}
|
||||
|
||||
typedef struct BlockReopenQueueEntry {
|
||||
bool prepared;
|
||||
BDRVReopenState state;
|
||||
@@ -1505,6 +1616,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
assert(bs != NULL);
|
||||
|
||||
BlockReopenQueueEntry *bs_entry;
|
||||
BdrvChild *child;
|
||||
|
||||
if (bs_queue == NULL) {
|
||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||
QSIMPLEQ_INIT(bs_queue);
|
||||
@@ -1513,8 +1626,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
/* bdrv_open() masks this flag out */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
|
||||
if (bs->file) {
|
||||
bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags));
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
int child_flags;
|
||||
|
||||
if (child->bs->inherits_from != bs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child_flags = child->role->inherit_flags(flags);
|
||||
bdrv_reopen_queue(bs_queue, child->bs, child_flags);
|
||||
}
|
||||
|
||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||
@@ -1725,6 +1845,16 @@ void bdrv_close(BlockDriverState *bs)
|
||||
notifier_list_notify(&bs->close_notifiers, bs);
|
||||
|
||||
if (bs->drv) {
|
||||
BdrvChild *child, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||
if (child->bs->inherits_from == bs) {
|
||||
child->bs->inherits_from = NULL;
|
||||
}
|
||||
QLIST_REMOVE(child, next);
|
||||
g_free(child);
|
||||
}
|
||||
|
||||
if (bs->backing_hd) {
|
||||
BlockDriverState *backing_hd = bs->backing_hd;
|
||||
bdrv_set_backing_hd(bs, NULL);
|
||||
@@ -1822,12 +1952,18 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
||||
bs_dest->enable_write_cache = bs_src->enable_write_cache;
|
||||
|
||||
/* i/o throttled req */
|
||||
memcpy(&bs_dest->throttle_state,
|
||||
&bs_src->throttle_state,
|
||||
sizeof(ThrottleState));
|
||||
bs_dest->throttle_state = bs_src->throttle_state,
|
||||
bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
|
||||
bs_dest->pending_reqs[0] = bs_src->pending_reqs[0];
|
||||
bs_dest->pending_reqs[1] = bs_src->pending_reqs[1];
|
||||
bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0];
|
||||
bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1];
|
||||
bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
|
||||
memcpy(&bs_dest->round_robin,
|
||||
&bs_src->round_robin,
|
||||
sizeof(bs_dest->round_robin));
|
||||
memcpy(&bs_dest->throttle_timers,
|
||||
&bs_src->throttle_timers,
|
||||
sizeof(ThrottleTimers));
|
||||
|
||||
/* r/w error */
|
||||
bs_dest->on_read_error = bs_src->on_read_error;
|
||||
@@ -1869,6 +2005,10 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
||||
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
||||
{
|
||||
BlockDriverState tmp;
|
||||
BdrvChild *child;
|
||||
|
||||
bdrv_drain(bs_new);
|
||||
bdrv_drain(bs_old);
|
||||
|
||||
/* The code needs to swap the node_name but simply swapping node_list won't
|
||||
* work so first remove the nodes from the graph list, do the swap then
|
||||
@@ -1881,12 +2021,21 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
||||
QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list);
|
||||
}
|
||||
|
||||
/* If the BlockDriverState is part of a throttling group acquire
|
||||
* its lock since we're going to mess with the protected fields.
|
||||
* Otherwise there's no need to worry since no one else can touch
|
||||
* them. */
|
||||
if (bs_old->throttle_state) {
|
||||
throttle_group_lock(bs_old);
|
||||
}
|
||||
|
||||
/* bs_new must be unattached and shouldn't have anything fancy enabled */
|
||||
assert(!bs_new->blk);
|
||||
assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
|
||||
assert(bs_new->job == NULL);
|
||||
assert(bs_new->io_limits_enabled == false);
|
||||
assert(!throttle_have_timer(&bs_new->throttle_state));
|
||||
assert(bs_new->throttle_state == NULL);
|
||||
assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
|
||||
|
||||
tmp = *bs_new;
|
||||
*bs_new = *bs_old;
|
||||
@@ -1903,7 +2052,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
||||
/* Check a few fields that should remain attached to the device */
|
||||
assert(bs_new->job == NULL);
|
||||
assert(bs_new->io_limits_enabled == false);
|
||||
assert(!throttle_have_timer(&bs_new->throttle_state));
|
||||
assert(bs_new->throttle_state == NULL);
|
||||
assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
|
||||
|
||||
/* Release the ThrottleGroup lock */
|
||||
if (bs_old->throttle_state) {
|
||||
throttle_group_unlock(bs_old);
|
||||
}
|
||||
|
||||
/* insert the nodes back into the graph node list if needed */
|
||||
if (bs_new->node_name[0] != '\0') {
|
||||
@@ -1913,6 +2068,30 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
||||
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update lh_first.le_prev for non-empty lists.
|
||||
*
|
||||
* The head of the op blocker list doesn't change because it is moved back
|
||||
* in bdrv_move_feature_fields().
|
||||
*/
|
||||
assert(QLIST_EMPTY(&bs_old->tracked_requests));
|
||||
assert(QLIST_EMPTY(&bs_new->tracked_requests));
|
||||
|
||||
QLIST_FIX_HEAD_PTR(&bs_new->children, next);
|
||||
QLIST_FIX_HEAD_PTR(&bs_old->children, next);
|
||||
|
||||
/* Update references in bs->opaque and children */
|
||||
QLIST_FOREACH(child, &bs_old->children, next) {
|
||||
if (child->bs->inherits_from == bs_new) {
|
||||
child->bs->inherits_from = bs_old;
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH(child, &bs_new->children, next) {
|
||||
if (child->bs->inherits_from == bs_old) {
|
||||
child->bs->inherits_from = bs_new;
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_rebind(bs_new);
|
||||
bdrv_rebind(bs_old);
|
||||
}
|
||||
@@ -1935,6 +2114,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
|
||||
/* The contents of 'tmp' will become bs_top, as we are
|
||||
* swapping bs_new and bs_top contents. */
|
||||
bdrv_set_backing_hd(bs_top, bs_new);
|
||||
bdrv_attach_child(bs_top, bs_new, &child_backing);
|
||||
}
|
||||
|
||||
static void bdrv_delete(BlockDriverState *bs)
|
||||
@@ -3220,10 +3400,9 @@ static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
|
||||
uint64_t size = bdrv_nb_sectors(bs);
|
||||
|
||||
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
continue;
|
||||
}
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
hbitmap_truncate(bitmap->bitmap, size);
|
||||
bitmap->size = size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3691,7 +3870,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
if (bs->io_limits_enabled) {
|
||||
throttle_detach_aio_context(&bs->throttle_state);
|
||||
throttle_timers_detach_aio_context(&bs->throttle_timers);
|
||||
}
|
||||
if (bs->drv->bdrv_detach_aio_context) {
|
||||
bs->drv->bdrv_detach_aio_context(bs);
|
||||
@@ -3727,7 +3906,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
bs->drv->bdrv_attach_aio_context(bs, new_context);
|
||||
}
|
||||
if (bs->io_limits_enabled) {
|
||||
throttle_attach_aio_context(&bs->throttle_state, new_context);
|
||||
throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
|
||||
|
@@ -10,6 +10,7 @@ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += raw-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-y += null.o mirror.o io.o
|
||||
block-obj-y += throttle-groups.o
|
||||
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
|
@@ -429,7 +429,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the backing file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
|
||||
flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||
bs, &child_file, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
|
@@ -125,7 +125,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the raw file */
|
||||
assert(bs->file == NULL);
|
||||
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
|
||||
"raw", flags | BDRV_O_PROTOCOL, false, &local_err);
|
||||
"raw", bs, &child_file, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
@@ -134,7 +134,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the test file */
|
||||
assert(s->test_file == NULL);
|
||||
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
|
||||
"test", flags, false, &local_err);
|
||||
"test", bs, &child_format, false, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
s->test_file = NULL;
|
||||
|
75
block/io.c
75
block/io.c
@@ -23,9 +23,9 @@
|
||||
*/
|
||||
|
||||
#include "trace.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/throttle-groups.h"
|
||||
|
||||
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
|
||||
|
||||
@@ -65,7 +65,7 @@ void bdrv_set_io_limits(BlockDriverState *bs,
|
||||
{
|
||||
int i;
|
||||
|
||||
throttle_config(&bs->throttle_state, cfg);
|
||||
throttle_group_config(bs, cfg);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
qemu_co_enter_next(&bs->throttled_reqs[i]);
|
||||
@@ -95,72 +95,33 @@ static bool bdrv_start_throttled_reqs(BlockDriverState *bs)
|
||||
void bdrv_io_limits_disable(BlockDriverState *bs)
|
||||
{
|
||||
bs->io_limits_enabled = false;
|
||||
|
||||
bdrv_start_throttled_reqs(bs);
|
||||
|
||||
throttle_destroy(&bs->throttle_state);
|
||||
}
|
||||
|
||||
static void bdrv_throttle_read_timer_cb(void *opaque)
|
||||
{
|
||||
BlockDriverState *bs = opaque;
|
||||
qemu_co_enter_next(&bs->throttled_reqs[0]);
|
||||
}
|
||||
|
||||
static void bdrv_throttle_write_timer_cb(void *opaque)
|
||||
{
|
||||
BlockDriverState *bs = opaque;
|
||||
qemu_co_enter_next(&bs->throttled_reqs[1]);
|
||||
throttle_group_unregister_bs(bs);
|
||||
}
|
||||
|
||||
/* should be called before bdrv_set_io_limits if a limit is set */
|
||||
void bdrv_io_limits_enable(BlockDriverState *bs)
|
||||
void bdrv_io_limits_enable(BlockDriverState *bs, const char *group)
|
||||
{
|
||||
int clock_type = QEMU_CLOCK_REALTIME;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* For testing block IO throttling only */
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
assert(!bs->io_limits_enabled);
|
||||
throttle_init(&bs->throttle_state,
|
||||
bdrv_get_aio_context(bs),
|
||||
clock_type,
|
||||
bdrv_throttle_read_timer_cb,
|
||||
bdrv_throttle_write_timer_cb,
|
||||
bs);
|
||||
throttle_group_register_bs(bs, group);
|
||||
bs->io_limits_enabled = true;
|
||||
}
|
||||
|
||||
/* This function makes an IO wait if needed
|
||||
*
|
||||
* @nb_sectors: the number of sectors of the IO
|
||||
* @is_write: is the IO a write
|
||||
*/
|
||||
static void bdrv_io_limits_intercept(BlockDriverState *bs,
|
||||
unsigned int bytes,
|
||||
bool is_write)
|
||||
void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group)
|
||||
{
|
||||
/* does this io must wait */
|
||||
bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write);
|
||||
|
||||
/* if must wait or any request of this type throttled queue the IO */
|
||||
if (must_wait ||
|
||||
!qemu_co_queue_empty(&bs->throttled_reqs[is_write])) {
|
||||
qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
|
||||
}
|
||||
|
||||
/* the IO will be executed, do the accounting */
|
||||
throttle_account(&bs->throttle_state, is_write, bytes);
|
||||
|
||||
|
||||
/* if the next request must wait -> do nothing */
|
||||
if (throttle_schedule_timer(&bs->throttle_state, is_write)) {
|
||||
/* this bs is not part of any group */
|
||||
if (!bs->throttle_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* else queue next request for execution */
|
||||
qemu_co_queue_next(&bs->throttled_reqs[is_write]);
|
||||
/* this bs is a part of the same group than the one we want */
|
||||
if (!g_strcmp0(throttle_group_get_name(bs), group)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* need to change the group this bs belong to */
|
||||
bdrv_io_limits_disable(bs);
|
||||
bdrv_io_limits_enable(bs, group);
|
||||
}
|
||||
|
||||
void bdrv_setup_io_funcs(BlockDriver *bdrv)
|
||||
@@ -967,7 +928,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (bs->io_limits_enabled) {
|
||||
bdrv_io_limits_intercept(bs, bytes, false);
|
||||
throttle_group_co_io_limits_intercept(bs, bytes, false);
|
||||
}
|
||||
|
||||
/* Align read if necessary by padding qiov */
|
||||
@@ -1297,7 +1258,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
|
||||
|
||||
/* throttling disk I/O */
|
||||
if (bs->io_limits_enabled) {
|
||||
bdrv_io_limits_intercept(bs, bytes, true);
|
||||
throttle_group_co_io_limits_intercept(bs, bytes, true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "block/qapi.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "block/write-threshold.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qapi-visit.h"
|
||||
@@ -65,7 +66,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
|
||||
|
||||
if (bs->io_limits_enabled) {
|
||||
ThrottleConfig cfg;
|
||||
throttle_get_config(&bs->throttle_state, &cfg);
|
||||
|
||||
throttle_group_get_config(bs, &cfg);
|
||||
|
||||
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||
info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
|
||||
@@ -90,6 +93,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
|
||||
|
||||
info->has_iops_size = cfg.op_size;
|
||||
info->iops_size = cfg.op_size;
|
||||
|
||||
info->has_group = true;
|
||||
info->group = g_strdup(throttle_group_get_name(bs));
|
||||
}
|
||||
|
||||
info->write_threshold = bdrv_write_threshold_get(bs);
|
||||
|
@@ -483,9 +483,11 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
|
||||
[QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
|
||||
};
|
||||
|
||||
static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
|
||||
static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
uint64_t *l2_cache_size,
|
||||
uint64_t *refcount_cache_size, Error **errp)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t combined_cache_size;
|
||||
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
|
||||
|
||||
@@ -525,7 +527,9 @@ static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
|
||||
}
|
||||
} else {
|
||||
if (!l2_cache_size_set && !refcount_cache_size_set) {
|
||||
*l2_cache_size = DEFAULT_L2_CACHE_BYTE_SIZE;
|
||||
*l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
|
||||
(uint64_t)DEFAULT_L2_CACHE_CLUSTERS
|
||||
* s->cluster_size);
|
||||
*refcount_cache_size = *l2_cache_size
|
||||
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO;
|
||||
} else if (!l2_cache_size_set) {
|
||||
@@ -803,7 +807,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
read_cache_sizes(opts, &l2_cache_size, &refcount_cache_size, &local_err);
|
||||
read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
|
@@ -62,11 +62,14 @@
|
||||
#define MIN_CLUSTER_BITS 9
|
||||
#define MAX_CLUSTER_BITS 21
|
||||
|
||||
#define MIN_L2_CACHE_SIZE 1 /* cluster */
|
||||
/* Must be at least 2 to cover COW */
|
||||
#define MIN_L2_CACHE_SIZE 2 /* clusters */
|
||||
|
||||
/* Must be at least 4 to cover all cases of refcount table growth */
|
||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
||||
|
||||
/* Whichever is more */
|
||||
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
|
||||
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
|
||||
|
||||
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
|
||||
|
@@ -866,25 +866,18 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
bool *opened;
|
||||
QDict *sub = NULL;
|
||||
QList *list = NULL;
|
||||
const QListEntry *lentry;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
qdict_flatten(options);
|
||||
qdict_extract_subqdict(options, &sub, "children.");
|
||||
qdict_array_split(sub, &list);
|
||||
|
||||
if (qdict_size(sub)) {
|
||||
error_setg(&local_err, "Invalid option children.%s",
|
||||
qdict_first(sub)->key);
|
||||
/* count how many different children are present */
|
||||
s->num_children = qdict_array_entries(options, "children.");
|
||||
if (s->num_children < 0) {
|
||||
error_setg(&local_err, "Option children is not a valid array");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* count how many different children are present */
|
||||
s->num_children = qlist_size(list);
|
||||
if (s->num_children < 2) {
|
||||
error_setg(&local_err,
|
||||
"Number of provided children must be greater than 1");
|
||||
@@ -937,37 +930,17 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->bs = g_new0(BlockDriverState *, s->num_children);
|
||||
opened = g_new0(bool, s->num_children);
|
||||
|
||||
for (i = 0, lentry = qlist_first(list); lentry;
|
||||
lentry = qlist_next(lentry), i++) {
|
||||
QDict *d;
|
||||
QString *string;
|
||||
|
||||
switch (qobject_type(lentry->value))
|
||||
{
|
||||
/* List of options */
|
||||
case QTYPE_QDICT:
|
||||
d = qobject_to_qdict(lentry->value);
|
||||
QINCREF(d);
|
||||
ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
|
||||
&local_err);
|
||||
break;
|
||||
|
||||
/* QMP reference */
|
||||
case QTYPE_QSTRING:
|
||||
string = qobject_to_qstring(lentry->value);
|
||||
ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
|
||||
flags, NULL, &local_err);
|
||||
break;
|
||||
|
||||
default:
|
||||
error_setg(&local_err, "Specification of child block device %i "
|
||||
"is invalid", i);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
char indexstr[32];
|
||||
ret = snprintf(indexstr, 32, "children.%d", i);
|
||||
assert(ret < 32);
|
||||
|
||||
ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs,
|
||||
&child_format, false, &local_err);
|
||||
if (ret < 0) {
|
||||
goto close_exit;
|
||||
}
|
||||
|
||||
opened[i] = true;
|
||||
}
|
||||
|
||||
@@ -990,8 +963,6 @@ exit:
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
QDECREF(list);
|
||||
QDECREF(sub);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -1848,8 +1848,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||
*pnum = nb_sectors;
|
||||
ret = BDRV_BLOCK_DATA;
|
||||
} else if (data == start) {
|
||||
/* On a data extent, compute sectors to the end of the extent. */
|
||||
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
|
||||
/* On a data extent, compute sectors to the end of the extent,
|
||||
* possibly including a partial sector at EOF. */
|
||||
*pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE));
|
||||
ret = BDRV_BLOCK_DATA;
|
||||
} else {
|
||||
/* On a hole, compute sectors to the beginning of the next extent. */
|
||||
|
496
block/throttle-groups.c
Normal file
496
block/throttle-groups.c
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* QEMU block throttling group infrastructure
|
||||
*
|
||||
* Copyright (C) Nodalink, EURL. 2014
|
||||
* Copyright (C) Igalia, S.L. 2015
|
||||
*
|
||||
* Authors:
|
||||
* Benoît Canet <benoit.canet@nodalink.com>
|
||||
* Alberto Garcia <berto@igalia.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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "block/throttle-groups.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
/* The ThrottleGroup structure (with its ThrottleState) is shared
|
||||
* among different BlockDriverState and it's independent from
|
||||
* AioContext, so in order to use it from different threads it needs
|
||||
* its own locking.
|
||||
*
|
||||
* This locking is however handled internally in this file, so it's
|
||||
* mostly transparent to outside users (but see the documentation in
|
||||
* throttle_groups_lock()).
|
||||
*
|
||||
* The whole ThrottleGroup structure is private and invisible to
|
||||
* outside users, that only use it through its ThrottleState.
|
||||
*
|
||||
* In addition to the ThrottleGroup structure, BlockDriverState has
|
||||
* fields that need to be accessed by other members of the group and
|
||||
* therefore also need to be protected by this lock. Once a BDS is
|
||||
* registered in a group those fields can be accessed by other threads
|
||||
* any time.
|
||||
*
|
||||
* Again, all this is handled internally and is mostly transparent to
|
||||
* the outside. The 'throttle_timers' field however has an additional
|
||||
* constraint because it may be temporarily invalid (see for example
|
||||
* bdrv_set_aio_context()). Therefore in this file a thread will
|
||||
* access some other BDS's timers only after verifying that that BDS
|
||||
* has throttled requests in the queue.
|
||||
*/
|
||||
typedef struct ThrottleGroup {
|
||||
char *name; /* This is constant during the lifetime of the group */
|
||||
|
||||
QemuMutex lock; /* This lock protects the following four fields */
|
||||
ThrottleState ts;
|
||||
QLIST_HEAD(, BlockDriverState) head;
|
||||
BlockDriverState *tokens[2];
|
||||
bool any_timer_armed[2];
|
||||
|
||||
/* These two are protected by the global throttle_groups_lock */
|
||||
unsigned refcount;
|
||||
QTAILQ_ENTRY(ThrottleGroup) list;
|
||||
} ThrottleGroup;
|
||||
|
||||
static QemuMutex throttle_groups_lock;
|
||||
static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
|
||||
QTAILQ_HEAD_INITIALIZER(throttle_groups);
|
||||
|
||||
/* Increments the reference count of a ThrottleGroup given its name.
|
||||
*
|
||||
* If no ThrottleGroup is found with the given name a new one is
|
||||
* created.
|
||||
*
|
||||
* @name: the name of the ThrottleGroup
|
||||
* @ret: the ThrottleGroup
|
||||
*/
|
||||
static ThrottleGroup *throttle_group_incref(const char *name)
|
||||
{
|
||||
ThrottleGroup *tg = NULL;
|
||||
ThrottleGroup *iter;
|
||||
|
||||
qemu_mutex_lock(&throttle_groups_lock);
|
||||
|
||||
/* Look for an existing group with that name */
|
||||
QTAILQ_FOREACH(iter, &throttle_groups, list) {
|
||||
if (!strcmp(name, iter->name)) {
|
||||
tg = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new one if not found */
|
||||
if (!tg) {
|
||||
tg = g_new0(ThrottleGroup, 1);
|
||||
tg->name = g_strdup(name);
|
||||
qemu_mutex_init(&tg->lock);
|
||||
throttle_init(&tg->ts);
|
||||
QLIST_INIT(&tg->head);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
|
||||
}
|
||||
|
||||
tg->refcount++;
|
||||
|
||||
qemu_mutex_unlock(&throttle_groups_lock);
|
||||
|
||||
return tg;
|
||||
}
|
||||
|
||||
/* Decrease the reference count of a ThrottleGroup.
|
||||
*
|
||||
* When the reference count reaches zero the ThrottleGroup is
|
||||
* destroyed.
|
||||
*
|
||||
* @tg: The ThrottleGroup to unref
|
||||
*/
|
||||
static void throttle_group_unref(ThrottleGroup *tg)
|
||||
{
|
||||
qemu_mutex_lock(&throttle_groups_lock);
|
||||
if (--tg->refcount == 0) {
|
||||
QTAILQ_REMOVE(&throttle_groups, tg, list);
|
||||
qemu_mutex_destroy(&tg->lock);
|
||||
g_free(tg->name);
|
||||
g_free(tg);
|
||||
}
|
||||
qemu_mutex_unlock(&throttle_groups_lock);
|
||||
}
|
||||
|
||||
/* Get the name from a BlockDriverState's ThrottleGroup. The name (and
|
||||
* the pointer) is guaranteed to remain constant during the lifetime
|
||||
* of the group.
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of a throttling group
|
||||
* @ret: the name of the group.
|
||||
*/
|
||||
const char *throttle_group_get_name(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
return tg->name;
|
||||
}
|
||||
|
||||
/* Return the next BlockDriverState in the round-robin sequence,
|
||||
* simulating a circular list.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @ret: the next BlockDriverState in the sequence
|
||||
*/
|
||||
static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
BlockDriverState *next = QLIST_NEXT(bs, round_robin);
|
||||
|
||||
if (!next) {
|
||||
return QLIST_FIRST(&tg->head);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/* Return the next BlockDriverState in the round-robin sequence with
|
||||
* pending I/O requests.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @is_write: the type of operation (read/write)
|
||||
* @ret: the next BlockDriverState with pending requests, or bs
|
||||
* if there is none.
|
||||
*/
|
||||
static BlockDriverState *next_throttle_token(BlockDriverState *bs,
|
||||
bool is_write)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
BlockDriverState *token, *start;
|
||||
|
||||
start = token = tg->tokens[is_write];
|
||||
|
||||
/* get next bs round in round robin style */
|
||||
token = throttle_group_next_bs(token);
|
||||
while (token != start && !token->pending_reqs[is_write]) {
|
||||
token = throttle_group_next_bs(token);
|
||||
}
|
||||
|
||||
/* If no IO are queued for scheduling on the next round robin token
|
||||
* then decide the token is the current bs because chances are
|
||||
* the current bs get the current request queued.
|
||||
*/
|
||||
if (token == start && !token->pending_reqs[is_write]) {
|
||||
token = bs;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/* Check if the next I/O request for a BlockDriverState needs to be
|
||||
* throttled or not. If there's no timer set in this group, set one
|
||||
* and update the token accordingly.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @is_write: the type of operation (read/write)
|
||||
* @ret: whether the I/O request needs to be throttled or not
|
||||
*/
|
||||
static bool throttle_group_schedule_timer(BlockDriverState *bs,
|
||||
bool is_write)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
ThrottleTimers *tt = &bs->throttle_timers;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
bool must_wait;
|
||||
|
||||
/* Check if any of the timers in this group is already armed */
|
||||
if (tg->any_timer_armed[is_write]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
must_wait = throttle_schedule_timer(ts, tt, is_write);
|
||||
|
||||
/* If a timer just got armed, set bs as the current token */
|
||||
if (must_wait) {
|
||||
tg->tokens[is_write] = bs;
|
||||
tg->any_timer_armed[is_write] = true;
|
||||
}
|
||||
|
||||
return must_wait;
|
||||
}
|
||||
|
||||
/* Look for the next pending I/O request and schedule it.
|
||||
*
|
||||
* This assumes that tg->lock is held.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @is_write: the type of operation (read/write)
|
||||
*/
|
||||
static void schedule_next_request(BlockDriverState *bs, bool is_write)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
bool must_wait;
|
||||
BlockDriverState *token;
|
||||
|
||||
/* Check if there's any pending request to schedule next */
|
||||
token = next_throttle_token(bs, is_write);
|
||||
if (!token->pending_reqs[is_write]) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set a timer for the request if it needs to be throttled */
|
||||
must_wait = throttle_group_schedule_timer(token, is_write);
|
||||
|
||||
/* If it doesn't have to wait, queue it for immediate execution */
|
||||
if (!must_wait) {
|
||||
/* Give preference to requests from the current bs */
|
||||
if (qemu_in_coroutine() &&
|
||||
qemu_co_queue_next(&bs->throttled_reqs[is_write])) {
|
||||
token = bs;
|
||||
} else {
|
||||
ThrottleTimers *tt = &token->throttle_timers;
|
||||
int64_t now = qemu_clock_get_ns(tt->clock_type);
|
||||
timer_mod(tt->timers[is_write], now + 1);
|
||||
tg->any_timer_armed[is_write] = true;
|
||||
}
|
||||
tg->tokens[is_write] = token;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if an I/O request needs to be throttled, wait and set a timer
|
||||
* if necessary, and schedule the next request using a round robin
|
||||
* algorithm.
|
||||
*
|
||||
* @bs: the current BlockDriverState
|
||||
* @bytes: the number of bytes for this I/O
|
||||
* @is_write: the type of operation (read/write)
|
||||
*/
|
||||
void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
|
||||
unsigned int bytes,
|
||||
bool is_write)
|
||||
{
|
||||
bool must_wait;
|
||||
BlockDriverState *token;
|
||||
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
|
||||
/* First we check if this I/O has to be throttled. */
|
||||
token = next_throttle_token(bs, is_write);
|
||||
must_wait = throttle_group_schedule_timer(token, is_write);
|
||||
|
||||
/* Wait if there's a timer set or queued requests of this type */
|
||||
if (must_wait || bs->pending_reqs[is_write]) {
|
||||
bs->pending_reqs[is_write]++;
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
bs->pending_reqs[is_write]--;
|
||||
}
|
||||
|
||||
/* The I/O will be executed, so do the accounting */
|
||||
throttle_account(bs->throttle_state, is_write, bytes);
|
||||
|
||||
/* Schedule the next request */
|
||||
schedule_next_request(bs, is_write);
|
||||
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Update the throttle configuration for a particular group. Similar
|
||||
* to throttle_config(), but guarantees atomicity within the
|
||||
* throttling group.
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of the group
|
||||
* @cfg: the configuration to set
|
||||
*/
|
||||
void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg)
|
||||
{
|
||||
ThrottleTimers *tt = &bs->throttle_timers;
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
throttle_config(ts, tt, cfg);
|
||||
/* throttle_config() cancels the timers */
|
||||
tg->any_timer_armed[0] = tg->any_timer_armed[1] = false;
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Get the throttle configuration from a particular group. Similar to
|
||||
* throttle_get_config(), but guarantees atomicity within the
|
||||
* throttling group.
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of the group
|
||||
* @cfg: the configuration will be written here
|
||||
*/
|
||||
void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
throttle_get_config(ts, cfg);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
/* ThrottleTimers callback. This wakes up a request that was waiting
|
||||
* because it had been throttled.
|
||||
*
|
||||
* @bs: the BlockDriverState whose request had been throttled
|
||||
* @is_write: the type of operation (read/write)
|
||||
*/
|
||||
static void timer_cb(BlockDriverState *bs, bool is_write)
|
||||
{
|
||||
ThrottleState *ts = bs->throttle_state;
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
bool empty_queue;
|
||||
|
||||
/* The timer has just been fired, so we can update the flag */
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
tg->any_timer_armed[is_write] = false;
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
/* Run the request that was waiting for this timer */
|
||||
empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]);
|
||||
|
||||
/* If the request queue was empty then we have to take care of
|
||||
* scheduling the next one */
|
||||
if (empty_queue) {
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
schedule_next_request(bs, is_write);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_timer_cb(void *opaque)
|
||||
{
|
||||
timer_cb(opaque, false);
|
||||
}
|
||||
|
||||
static void write_timer_cb(void *opaque)
|
||||
{
|
||||
timer_cb(opaque, true);
|
||||
}
|
||||
|
||||
/* Register a BlockDriverState in the throttling group, also
|
||||
* initializing its timers and updating its throttle_state pointer to
|
||||
* point to it. If a throttling group with that name does not exist
|
||||
* yet, it will be created.
|
||||
*
|
||||
* @bs: the BlockDriverState to insert
|
||||
* @groupname: the name of the group
|
||||
*/
|
||||
void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
|
||||
{
|
||||
int i;
|
||||
ThrottleGroup *tg = throttle_group_incref(groupname);
|
||||
int clock_type = QEMU_CLOCK_REALTIME;
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* For testing block IO throttling only */
|
||||
clock_type = QEMU_CLOCK_VIRTUAL;
|
||||
}
|
||||
|
||||
bs->throttle_state = &tg->ts;
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
/* If the ThrottleGroup is new set this BlockDriverState as the token */
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!tg->tokens[i]) {
|
||||
tg->tokens[i] = bs;
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_INSERT_HEAD(&tg->head, bs, round_robin);
|
||||
|
||||
throttle_timers_init(&bs->throttle_timers,
|
||||
bdrv_get_aio_context(bs),
|
||||
clock_type,
|
||||
read_timer_cb,
|
||||
write_timer_cb,
|
||||
bs);
|
||||
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Unregister a BlockDriverState from its group, removing it from the
|
||||
* list, destroying the timers and setting the throttle_state pointer
|
||||
* to NULL.
|
||||
*
|
||||
* The group will be destroyed if it's empty after this operation.
|
||||
*
|
||||
* @bs: the BlockDriverState to remove
|
||||
*/
|
||||
void throttle_group_unregister_bs(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
int i;
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (tg->tokens[i] == bs) {
|
||||
BlockDriverState *token = throttle_group_next_bs(bs);
|
||||
/* Take care of the case where this is the last bs in the group */
|
||||
if (token == bs) {
|
||||
token = NULL;
|
||||
}
|
||||
tg->tokens[i] = token;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove the current bs from the list */
|
||||
QLIST_REMOVE(bs, round_robin);
|
||||
throttle_timers_destroy(&bs->throttle_timers);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
|
||||
throttle_group_unref(tg);
|
||||
bs->throttle_state = NULL;
|
||||
}
|
||||
|
||||
/* Acquire the lock of this throttling group.
|
||||
*
|
||||
* You won't normally need to use this. None of the functions from the
|
||||
* ThrottleGroup API require you to acquire the lock since all of them
|
||||
* deal with it internally.
|
||||
*
|
||||
* This should only be used in exceptional cases when you want to
|
||||
* access the protected fields of a BlockDriverState directly
|
||||
* (e.g. bdrv_swap()).
|
||||
*
|
||||
* @bs: a BlockDriverState that is member of the group
|
||||
*/
|
||||
void throttle_group_lock(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
}
|
||||
|
||||
/* Release the lock of this throttling group.
|
||||
*
|
||||
* See the comments in throttle_group_lock().
|
||||
*/
|
||||
void throttle_group_unlock(BlockDriverState *bs)
|
||||
{
|
||||
ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
}
|
||||
|
||||
static void throttle_groups_init(void)
|
||||
{
|
||||
qemu_mutex_init(&throttle_groups_lock);
|
||||
}
|
||||
|
||||
block_init(throttle_groups_init);
|
84
block/vmdk.c
84
block/vmdk.c
@@ -321,37 +321,13 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Queue extents, if any, for reopen() */
|
||||
/* We have nothing to do for VMDK reopen, stubs just return success */
|
||||
static int vmdk_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
BDRVVmdkState *s;
|
||||
int ret = -1;
|
||||
int i;
|
||||
VmdkExtent *e;
|
||||
|
||||
assert(state != NULL);
|
||||
assert(state->bs != NULL);
|
||||
|
||||
if (queue == NULL) {
|
||||
error_setg(errp, "No reopen queue for VMDK extents");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s = state->bs->opaque;
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
e = &s->extents[i];
|
||||
if (e->file != state->bs->file) {
|
||||
bdrv_reopen_queue(queue, e->file, state->flags);
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vmdk_parent_open(BlockDriverState *bs)
|
||||
@@ -543,7 +519,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
Error **errp);
|
||||
QDict *options, Error **errp);
|
||||
|
||||
static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||
Error **errp)
|
||||
@@ -582,7 +558,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
|
||||
|
||||
static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
BlockDriverState *file,
|
||||
int flags, Error **errp)
|
||||
int flags, QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
uint32_t magic;
|
||||
@@ -606,7 +582,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
|
||||
if (!buf) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
|
||||
g_free(buf);
|
||||
return ret;
|
||||
}
|
||||
@@ -763,7 +739,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
|
||||
/* Open an extent file and append to bs array */
|
||||
static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
BlockDriverState *file, int flags,
|
||||
char *buf, Error **errp)
|
||||
char *buf, QDict *options, Error **errp)
|
||||
{
|
||||
uint32_t magic;
|
||||
|
||||
@@ -773,7 +749,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
|
||||
break;
|
||||
case VMDK4_MAGIC:
|
||||
return vmdk_open_vmdk4(bs, file, flags, errp);
|
||||
return vmdk_open_vmdk4(bs, file, flags, options, errp);
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Image not in VMDK format");
|
||||
@@ -783,7 +759,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
const char *desc_file_path, Error **errp)
|
||||
const char *desc_file_path, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int matches;
|
||||
@@ -797,6 +774,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
BlockDriverState *extent_file;
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
VmdkExtent *extent;
|
||||
char extent_opt_prefix[32];
|
||||
|
||||
while (*p) {
|
||||
/* parse extent line in one of below formats:
|
||||
@@ -846,8 +824,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
extent_path = g_malloc0(PATH_MAX);
|
||||
path_combine(extent_path, PATH_MAX, desc_file_path, fname);
|
||||
extent_file = NULL;
|
||||
ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
|
||||
bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
|
||||
|
||||
ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
|
||||
assert(ret < 32);
|
||||
|
||||
ret = bdrv_open_image(&extent_file, extent_path, options,
|
||||
extent_opt_prefix, bs, &child_file, false, errp);
|
||||
g_free(extent_path);
|
||||
if (ret) {
|
||||
return ret;
|
||||
@@ -870,7 +852,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
||||
if (!buf) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
|
||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf,
|
||||
options, errp);
|
||||
}
|
||||
g_free(buf);
|
||||
if (ret) {
|
||||
@@ -898,7 +881,7 @@ next_line:
|
||||
}
|
||||
|
||||
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
Error **errp)
|
||||
QDict *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char ct[128];
|
||||
@@ -920,7 +903,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
|
||||
}
|
||||
s->create_type = g_strdup(ct);
|
||||
s->desc_offset = 0;
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp);
|
||||
ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
@@ -942,11 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
switch (magic) {
|
||||
case VMDK3_MAGIC:
|
||||
case VMDK4_MAGIC:
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
|
||||
ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp);
|
||||
s->desc_offset = 0x200;
|
||||
break;
|
||||
default:
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, errp);
|
||||
ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
|
||||
break;
|
||||
}
|
||||
if (ret) {
|
||||
@@ -1248,6 +1231,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
|
||||
int64_t sector_num)
|
||||
{
|
||||
uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num;
|
||||
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
return index_in_cluster;
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
@@ -1285,7 +1279,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||
break;
|
||||
}
|
||||
|
||||
index_in_cluster = sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@@ -1413,7 +1407,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int ret;
|
||||
uint64_t n, index_in_cluster;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
VmdkExtent *extent = NULL;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
@@ -1425,9 +1418,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
|
||||
ret = get_cluster_offset(bs, extent, NULL,
|
||||
sector_num << 9, false, &cluster_offset,
|
||||
0, 0);
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
@@ -1489,7 +1480,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
VmdkExtent *extent = NULL;
|
||||
int ret;
|
||||
int64_t index_in_cluster, n;
|
||||
uint64_t extent_begin_sector, extent_relative_sector_num;
|
||||
uint64_t cluster_offset;
|
||||
VmdkMetaData m_data;
|
||||
|
||||
@@ -1505,9 +1495,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
|
||||
if (!extent) {
|
||||
return -EIO;
|
||||
}
|
||||
extent_begin_sector = extent->end_sector - extent->sectors;
|
||||
extent_relative_sector_num = sector_num - extent_begin_sector;
|
||||
index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
|
||||
index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
|
||||
n = extent->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
64
blockdev.c
64
blockdev.c
@@ -34,6 +34,7 @@
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/block/block.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
@@ -357,6 +358,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
const char *id;
|
||||
bool has_driver_specific_opts;
|
||||
BlockdevDetectZeroesOptions detect_zeroes;
|
||||
const char *throttling_group;
|
||||
|
||||
/* Check common options by copying from bs_opts to opts, all other options
|
||||
* stay in bs_opts for processing by bdrv_open(). */
|
||||
@@ -391,13 +393,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool(opts, "cache.writeback", true)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
|
||||
bdrv_flags |= BDRV_O_CACHE_WB;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, "cache.direct", false)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
bdrv_flags |= BDRV_O_NOCACHE;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, "cache.no-flush", false)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
bdrv_flags |= BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
@@ -459,6 +461,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
|
||||
cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
||||
|
||||
throttling_group = qemu_opt_get(opts, "throttling.group");
|
||||
|
||||
if (!check_throttle_config(&cfg, &error)) {
|
||||
error_propagate(errp, error);
|
||||
goto early_err;
|
||||
@@ -547,7 +551,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
|
||||
/* disk I/O throttling */
|
||||
if (throttle_enabled(&cfg)) {
|
||||
bdrv_io_limits_enable(bs);
|
||||
if (!throttling_group) {
|
||||
throttling_group = blk_name(blk);
|
||||
}
|
||||
bdrv_io_limits_enable(bs, throttling_group);
|
||||
bdrv_set_io_limits(bs, &cfg);
|
||||
}
|
||||
|
||||
@@ -711,6 +718,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
|
||||
{ "iops_size", "throttling.iops-size" },
|
||||
|
||||
{ "group", "throttling.group" },
|
||||
|
||||
{ "readonly", "read-only" },
|
||||
};
|
||||
|
||||
@@ -733,16 +742,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
}
|
||||
|
||||
/* Specific options take precedence */
|
||||
if (!qemu_opt_get(all_opts, "cache.writeback")) {
|
||||
qemu_opt_set_bool(all_opts, "cache.writeback",
|
||||
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
|
||||
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
|
||||
!!(flags & BDRV_O_CACHE_WB), &error_abort);
|
||||
}
|
||||
if (!qemu_opt_get(all_opts, "cache.direct")) {
|
||||
qemu_opt_set_bool(all_opts, "cache.direct",
|
||||
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
|
||||
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
|
||||
!!(flags & BDRV_O_NOCACHE), &error_abort);
|
||||
}
|
||||
if (!qemu_opt_get(all_opts, "cache.no-flush")) {
|
||||
qemu_opt_set_bool(all_opts, "cache.no-flush",
|
||||
if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) {
|
||||
qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH,
|
||||
!!(flags & BDRV_O_NO_FLUSH), &error_abort);
|
||||
}
|
||||
qemu_opt_unset(all_opts, "cache");
|
||||
@@ -933,7 +942,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
&error_abort);
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-s390", &error_abort);
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-ccw", &error_abort);
|
||||
} else {
|
||||
qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort);
|
||||
}
|
||||
@@ -1951,7 +1960,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
bool has_iops_wr_max,
|
||||
int64_t iops_wr_max,
|
||||
bool has_iops_size,
|
||||
int64_t iops_size, Error **errp)
|
||||
int64_t iops_size,
|
||||
bool has_group,
|
||||
const char *group, Error **errp)
|
||||
{
|
||||
ThrottleConfig cfg;
|
||||
BlockDriverState *bs;
|
||||
@@ -2004,14 +2015,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
|
||||
bdrv_io_limits_enable(bs);
|
||||
} else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
|
||||
bdrv_io_limits_disable(bs);
|
||||
}
|
||||
|
||||
if (bs->io_limits_enabled) {
|
||||
if (throttle_enabled(&cfg)) {
|
||||
/* Enable I/O limits if they're not enabled yet, otherwise
|
||||
* just update the throttling group. */
|
||||
if (!bs->io_limits_enabled) {
|
||||
bdrv_io_limits_enable(bs, has_group ? group : device);
|
||||
} else if (has_group) {
|
||||
bdrv_io_limits_update_group(bs, group);
|
||||
}
|
||||
/* Set the new throttling configuration */
|
||||
bdrv_set_io_limits(bs, &cfg);
|
||||
} else if (bs->io_limits_enabled) {
|
||||
/* If all throttling settings are set to 0, disable I/O limits */
|
||||
bdrv_io_limits_disable(bs);
|
||||
}
|
||||
|
||||
aio_context_release(aio_context);
|
||||
@@ -3105,15 +3121,15 @@ QemuOptsList qemu_common_drive_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = "cache.writeback",
|
||||
.name = BDRV_OPT_CACHE_WB,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables writeback mode for any caches",
|
||||
},{
|
||||
.name = "cache.direct",
|
||||
.name = BDRV_OPT_CACHE_DIRECT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
||||
},{
|
||||
.name = "cache.no-flush",
|
||||
.name = BDRV_OPT_CACHE_NO_FLUSH,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "ignore any flush requests for the device",
|
||||
},{
|
||||
@@ -3188,6 +3204,10 @@ QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "throttling.iops-size",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "when limiting by iops max size of an I/O in bytes",
|
||||
},{
|
||||
.name = "throttling.group",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "name of the block throttling group",
|
||||
},{
|
||||
.name = "copy-on-read",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
|
134
configure
vendored
134
configure
vendored
@@ -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"
|
||||
@@ -317,6 +315,7 @@ snappy=""
|
||||
bzip2=""
|
||||
guest_agent=""
|
||||
guest_agent_with_vss="no"
|
||||
guest_agent_msi=""
|
||||
vss_win32_sdk=""
|
||||
win_sdk="no"
|
||||
want_tools="yes"
|
||||
@@ -437,6 +436,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 +571,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 +602,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 +617,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 +630,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 +685,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 +855,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"
|
||||
@@ -1075,6 +1079,10 @@ for opt do
|
||||
;;
|
||||
--disable-guest-agent) guest_agent="no"
|
||||
;;
|
||||
--enable-guest-agent-msi) guest_agent_msi="yes"
|
||||
;;
|
||||
--disable-guest-agent-msi) guest_agent_msi="no"
|
||||
;;
|
||||
--with-vss-sdk) vss_win32_sdk=""
|
||||
;;
|
||||
--with-vss-sdk=*) vss_win32_sdk="$optarg"
|
||||
@@ -1349,8 +1357,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
|
||||
@@ -1393,6 +1399,8 @@ Advanced options (experts only):
|
||||
reading bzip2-compressed dmg images)
|
||||
--disable-guest-agent disable building of the QEMU Guest Agent
|
||||
--enable-guest-agent enable building of the QEMU Guest Agent
|
||||
--enable-guest-agent-msi enable building guest agent Windows MSI installation package
|
||||
--disable-guest-agent-msi disable building guest agent Windows MSI installation
|
||||
--with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent
|
||||
--with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb)
|
||||
--disable-seccomp disable seccomp support
|
||||
@@ -2621,21 +2629,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 +2653,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" \
|
||||
@@ -3881,6 +3869,56 @@ if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss"
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# Guest agent Window MSI package
|
||||
|
||||
if test "$guest_agent" != yes; then
|
||||
if test "$guest_agent_msi" = yes; then
|
||||
error_exit "MSI guest agent package requires guest agent enabled"
|
||||
fi
|
||||
guest_agent_msi=no
|
||||
elif test "$mingw32" != "yes"; then
|
||||
if test "$guest_agent_msi" = "yes"; then
|
||||
error_exit "MSI guest agent package is available only for MinGW Windows cross-compilation"
|
||||
fi
|
||||
guest_agent_msi=no
|
||||
elif ! has wixl; then
|
||||
if test "$guest_agent_msi" = "yes"; then
|
||||
error_exit "MSI guest agent package requires wixl tool installed ( usually from msitools package )"
|
||||
fi
|
||||
guest_agent_msi=no
|
||||
fi
|
||||
|
||||
if test "$guest_agent_msi" != "no"; then
|
||||
if test "$guest_agent_with_vss" = "yes"; then
|
||||
QEMU_GA_MSI_WITH_VSS="-D InstallVss"
|
||||
fi
|
||||
|
||||
if test "$QEMU_GA_MANUFACTURER" = ""; then
|
||||
QEMU_GA_MANUFACTURER=QEMU
|
||||
fi
|
||||
|
||||
if test "$QEMU_GA_DISTRO" = ""; then
|
||||
QEMU_GA_DISTRO=Linux
|
||||
fi
|
||||
|
||||
if test "$QEMU_GA_VERSION" = ""; then
|
||||
QEMU_GA_VERSION=`cat $source_path/VERSION`
|
||||
fi
|
||||
|
||||
QEMU_GA_MSI_MINGW_DLL_PATH="-D Mingw_dlls=`$pkg_config --variable=prefix glib-2.0`/bin"
|
||||
|
||||
case "$cpu" in
|
||||
x86_64)
|
||||
QEMU_GA_MSI_ARCH="-a x64 -D Arch=64"
|
||||
;;
|
||||
i386)
|
||||
QEMU_GA_MSI_ARCH="-D Arch=32"
|
||||
;;
|
||||
*)
|
||||
error_exit "CPU $cpu not supported for building installation package"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check if we have fdatasync
|
||||
@@ -4577,6 +4615,15 @@ if test "$mingw32" = "yes" ; then
|
||||
echo "CONFIG_QGA_VSS=y" >> $config_host_mak
|
||||
echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak
|
||||
fi
|
||||
if test "$guest_agent_msi" != "no"; then
|
||||
echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak
|
||||
echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak
|
||||
echo "QEMU_GA_MSI_WITH_VSS=${QEMU_GA_MSI_WITH_VSS}" >> $config_host_mak
|
||||
echo "QEMU_GA_MSI_ARCH=${QEMU_GA_MSI_ARCH}" >> $config_host_mak
|
||||
echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER}" >> $config_host_mak
|
||||
echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO}" >> $config_host_mak
|
||||
echo "QEMU_GA_VERSION=${QEMU_GA_VERSION}" >> $config_host_mak
|
||||
fi
|
||||
else
|
||||
echo "CONFIG_POSIX=y" >> $config_host_mak
|
||||
fi
|
||||
@@ -4629,9 +4676,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
|
||||
|
11
cpus.c
11
cpus.c
@@ -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
|
||||
|
@@ -4,3 +4,4 @@ CONFIG_VIRTIO=y
|
||||
CONFIG_SCLPCONSOLE=y
|
||||
CONFIG_S390_FLIC=y
|
||||
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
|
||||
CONFIG_WDT_DIAG288=y
|
||||
|
@@ -7,6 +7,7 @@ CONFIG_QXL=$(CONFIG_SPICE)
|
||||
CONFIG_VGA_ISA=y
|
||||
CONFIG_VGA_CIRRUS=y
|
||||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_VMMOUSE=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=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 },
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -680,8 +680,6 @@ Example:
|
||||
out:
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
$ python scripts/qapi-commands.py --output-dir="qapi-generated" \
|
||||
--prefix="example-" example-schema.json
|
||||
$ cat qapi-generated/example-qapi-visit.h
|
||||
[Uninteresting stuff omitted...]
|
||||
|
||||
|
@@ -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:
|
||||
|
||||
|
29
exec.c
29
exec.c
@@ -341,6 +341,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
|
||||
hwaddr *plen, bool resolve_subpage)
|
||||
{
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
Int128 diff;
|
||||
|
||||
section = address_space_lookup_region(d, addr, resolve_subpage);
|
||||
@@ -350,8 +351,11 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
|
||||
/* Compute offset within MemoryRegion */
|
||||
*xlat = addr + section->offset_within_region;
|
||||
|
||||
diff = int128_sub(section->mr->size, int128_make64(addr));
|
||||
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
|
||||
mr = section->mr;
|
||||
if (memory_region_is_ram(mr)) {
|
||||
diff = int128_sub(section->size, int128_make64(addr));
|
||||
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@@ -454,6 +458,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 +476,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 +3349,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
|
||||
|
@@ -49,6 +49,7 @@ static struct option helper_opts[] = {
|
||||
{"socket", required_argument, NULL, 's'},
|
||||
{"uid", required_argument, NULL, 'u'},
|
||||
{"gid", required_argument, NULL, 'g'},
|
||||
{},
|
||||
};
|
||||
|
||||
static bool is_daemon;
|
||||
@@ -738,7 +739,12 @@ static int proxy_socket(const char *path, uid_t uid, gid_t gid)
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_assert(strlen(path) < sizeof(proxy.sun_path));
|
||||
if (strlen(path) >= sizeof(proxy.sun_path)) {
|
||||
do_log(LOG_CRIT, "UNIX domain socket path exceeds %zu characters\n",
|
||||
sizeof(proxy.sun_path));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
do_perror("socket");
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#include "cpu.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "exec/semihost.h"
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define GDB_ATTACHED "0"
|
||||
@@ -323,8 +324,6 @@ static GDBState *gdbserver_state;
|
||||
|
||||
bool gdb_has_xml;
|
||||
|
||||
int semihosting_target = SEMIHOSTING_TARGET_AUTO;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* XXX: This is not thread safe. Do we care? */
|
||||
static int gdbserver_fd = -1;
|
||||
@@ -362,10 +361,11 @@ static enum {
|
||||
/* Decide if either remote gdb syscalls or native file IO should be used. */
|
||||
int use_gdb_syscalls(void)
|
||||
{
|
||||
if (semihosting_target == SEMIHOSTING_TARGET_NATIVE) {
|
||||
SemihostingTarget target = semihosting_get_target();
|
||||
if (target == SEMIHOSTING_TARGET_NATIVE) {
|
||||
/* -semihosting-config target=native */
|
||||
return false;
|
||||
} else if (semihosting_target == SEMIHOSTING_TARGET_GDB) {
|
||||
} else if (target == SEMIHOSTING_TARGET_GDB) {
|
||||
/* -semihosting-config target=gdb */
|
||||
return true;
|
||||
}
|
||||
|
@@ -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
|
||||
|
313
hmp.c
313
hmp.c
@@ -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"
|
||||
@@ -399,7 +400,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
|
||||
" iops_max=%" PRId64
|
||||
" iops_rd_max=%" PRId64
|
||||
" iops_wr_max=%" PRId64
|
||||
" iops_size=%" PRId64 "\n",
|
||||
" iops_size=%" PRId64
|
||||
" group=%s\n",
|
||||
inserted->bps,
|
||||
inserted->bps_rd,
|
||||
inserted->bps_wr,
|
||||
@@ -412,7 +414,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
|
||||
inserted->iops_max,
|
||||
inserted->iops_rd_max,
|
||||
inserted->iops_wr_max,
|
||||
inserted->iops_size);
|
||||
inserted->iops_size,
|
||||
inserted->group);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
@@ -1356,7 +1359,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
|
||||
false,
|
||||
0,
|
||||
false, /* No default I/O size */
|
||||
0, &err);
|
||||
0,
|
||||
false,
|
||||
NULL, &err);
|
||||
hmp_handle_error(mon, &err);
|
||||
}
|
||||
|
||||
@@ -1999,3 +2004,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);
|
||||
}
|
||||
|
4
hmp.h
4
hmp.h
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -574,15 +574,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||
struct arm_boot_info *info =
|
||||
container_of(n, struct arm_boot_info, load_kernel_notifier);
|
||||
|
||||
/* CPU objects (unlike devices) are not automatically reset on system
|
||||
* reset, so we must always register a handler to do so. If we're
|
||||
* actually loading a kernel, the handler is also responsible for
|
||||
* arranging that we start it correctly.
|
||||
*/
|
||||
for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
|
||||
qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
|
||||
}
|
||||
|
||||
/* Load the kernel. */
|
||||
if (!info->kernel_filename || info->firmware_loaded) {
|
||||
|
||||
@@ -783,7 +774,18 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||
|
||||
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
||||
info->load_kernel_notifier.cpu = cpu;
|
||||
info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify;
|
||||
qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier);
|
||||
|
||||
/* CPU objects (unlike devices) are not automatically reset on system
|
||||
* reset, so we must always register a handler to do so. If we're
|
||||
* actually loading a kernel, the handler is also responsible for
|
||||
* arranging that we start it correctly.
|
||||
*/
|
||||
for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
|
||||
qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
|
||||
}
|
||||
}
|
||||
|
260
hw/arm/pxa2xx.c
260
hw/arm/pxa2xx.c
@@ -334,10 +334,10 @@ static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
static const ARMCPRegInfo pxa_cp_reginfo[] = {
|
||||
/* cp14 crm==1: perf registers */
|
||||
{ .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW,
|
||||
.access = PL1_RW, .type = ARM_CP_IO,
|
||||
.readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write },
|
||||
{ .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW,
|
||||
.access = PL1_RW, .type = ARM_CP_IO,
|
||||
.readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore },
|
||||
{ .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
@@ -356,11 +356,11 @@ static const ARMCPRegInfo pxa_cp_reginfo[] = {
|
||||
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
/* cp14 crn==6: CLKCFG */
|
||||
{ .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW,
|
||||
.access = PL1_RW, .type = ARM_CP_IO,
|
||||
.readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write },
|
||||
/* cp14 crn==7: PWRMODE */
|
||||
{ .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0,
|
||||
.access = PL1_RW,
|
||||
.access = PL1_RW, .type = ARM_CP_IO,
|
||||
.readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write },
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
@@ -457,7 +457,7 @@ typedef struct {
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
int enable;
|
||||
uint32_t enable;
|
||||
SSIBus *bus;
|
||||
|
||||
uint32_t sscr[2];
|
||||
@@ -470,10 +470,39 @@ typedef struct {
|
||||
uint8_t ssacd;
|
||||
|
||||
uint32_t rx_fifo[16];
|
||||
int rx_level;
|
||||
int rx_start;
|
||||
uint32_t rx_level;
|
||||
uint32_t rx_start;
|
||||
} PXA2xxSSPState;
|
||||
|
||||
static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id)
|
||||
{
|
||||
PXA2xxSSPState *s = opaque;
|
||||
|
||||
return s->rx_start < sizeof(s->rx_fifo);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pxa2xx_ssp = {
|
||||
.name = "pxa2xx-ssp",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(enable, PXA2xxSSPState),
|
||||
VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2),
|
||||
VMSTATE_UINT32(sspsp, PXA2xxSSPState),
|
||||
VMSTATE_UINT32(ssto, PXA2xxSSPState),
|
||||
VMSTATE_UINT32(ssitr, PXA2xxSSPState),
|
||||
VMSTATE_UINT32(sssr, PXA2xxSSPState),
|
||||
VMSTATE_UINT8(sstsa, PXA2xxSSPState),
|
||||
VMSTATE_UINT8(ssrsa, PXA2xxSSPState),
|
||||
VMSTATE_UINT8(ssacd, PXA2xxSSPState),
|
||||
VMSTATE_UINT32(rx_level, PXA2xxSSPState),
|
||||
VMSTATE_UINT32(rx_start, PXA2xxSSPState),
|
||||
VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate),
|
||||
VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
#define SSCR0 0x00 /* SSP Control register 0 */
|
||||
#define SSCR1 0x04 /* SSP Control register 1 */
|
||||
#define SSSR 0x08 /* SSP Status register */
|
||||
@@ -705,55 +734,20 @@ static const MemoryRegionOps pxa2xx_ssp_ops = {
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void pxa2xx_ssp_save(QEMUFile *f, void *opaque)
|
||||
static void pxa2xx_ssp_reset(DeviceState *d)
|
||||
{
|
||||
PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
|
||||
int i;
|
||||
PXA2xxSSPState *s = PXA2XX_SSP(d);
|
||||
|
||||
qemu_put_be32(f, s->enable);
|
||||
|
||||
qemu_put_be32s(f, &s->sscr[0]);
|
||||
qemu_put_be32s(f, &s->sscr[1]);
|
||||
qemu_put_be32s(f, &s->sspsp);
|
||||
qemu_put_be32s(f, &s->ssto);
|
||||
qemu_put_be32s(f, &s->ssitr);
|
||||
qemu_put_be32s(f, &s->sssr);
|
||||
qemu_put_8s(f, &s->sstsa);
|
||||
qemu_put_8s(f, &s->ssrsa);
|
||||
qemu_put_8s(f, &s->ssacd);
|
||||
|
||||
qemu_put_byte(f, s->rx_level);
|
||||
for (i = 0; i < s->rx_level; i ++)
|
||||
qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]);
|
||||
}
|
||||
|
||||
static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
|
||||
int i, v;
|
||||
|
||||
s->enable = qemu_get_be32(f);
|
||||
|
||||
qemu_get_be32s(f, &s->sscr[0]);
|
||||
qemu_get_be32s(f, &s->sscr[1]);
|
||||
qemu_get_be32s(f, &s->sspsp);
|
||||
qemu_get_be32s(f, &s->ssto);
|
||||
qemu_get_be32s(f, &s->ssitr);
|
||||
qemu_get_be32s(f, &s->sssr);
|
||||
qemu_get_8s(f, &s->sstsa);
|
||||
qemu_get_8s(f, &s->ssrsa);
|
||||
qemu_get_8s(f, &s->ssacd);
|
||||
|
||||
v = qemu_get_byte(f);
|
||||
if (v < 0 || v > ARRAY_SIZE(s->rx_fifo)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
s->rx_level = v;
|
||||
s->rx_start = 0;
|
||||
for (i = 0; i < s->rx_level; i ++)
|
||||
s->rx_fifo[i] = qemu_get_byte(f);
|
||||
|
||||
return 0;
|
||||
s->enable = 0;
|
||||
s->sscr[0] = s->sscr[1] = 0;
|
||||
s->sspsp = 0;
|
||||
s->ssto = 0;
|
||||
s->ssitr = 0;
|
||||
s->sssr = 0;
|
||||
s->sstsa = 0;
|
||||
s->ssrsa = 0;
|
||||
s->ssacd = 0;
|
||||
s->rx_start = s->rx_level = 0;
|
||||
}
|
||||
|
||||
static int pxa2xx_ssp_init(SysBusDevice *sbd)
|
||||
@@ -766,8 +760,6 @@ static int pxa2xx_ssp_init(SysBusDevice *sbd)
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s,
|
||||
"pxa2xx-ssp", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
register_savevm(dev, "pxa2xx_ssp", -1, 0,
|
||||
pxa2xx_ssp_save, pxa2xx_ssp_load, s);
|
||||
|
||||
s->bus = ssi_create_bus(dev, "ssi");
|
||||
return 0;
|
||||
@@ -1759,24 +1751,33 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
|
||||
}
|
||||
|
||||
/* PXA Fast Infra-red Communications Port */
|
||||
#define TYPE_PXA2XX_FIR "pxa2xx-fir"
|
||||
#define PXA2XX_FIR(obj) OBJECT_CHECK(PXA2xxFIrState, (obj), TYPE_PXA2XX_FIR)
|
||||
|
||||
struct PXA2xxFIrState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
qemu_irq rx_dma;
|
||||
qemu_irq tx_dma;
|
||||
int enable;
|
||||
uint32_t enable;
|
||||
CharDriverState *chr;
|
||||
|
||||
uint8_t control[3];
|
||||
uint8_t status[2];
|
||||
|
||||
int rx_len;
|
||||
int rx_start;
|
||||
uint32_t rx_len;
|
||||
uint32_t rx_start;
|
||||
uint8_t rx_fifo[64];
|
||||
};
|
||||
|
||||
static void pxa2xx_fir_reset(PXA2xxFIrState *s)
|
||||
static void pxa2xx_fir_reset(DeviceState *d)
|
||||
{
|
||||
PXA2xxFIrState *s = PXA2XX_FIR(d);
|
||||
|
||||
s->control[0] = 0x00;
|
||||
s->control[1] = 0x00;
|
||||
s->control[2] = 0x00;
|
||||
@@ -1953,73 +1954,94 @@ static void pxa2xx_fir_event(void *opaque, int event)
|
||||
{
|
||||
}
|
||||
|
||||
static void pxa2xx_fir_save(QEMUFile *f, void *opaque)
|
||||
static void pxa2xx_fir_instance_init(Object *obj)
|
||||
{
|
||||
PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
|
||||
int i;
|
||||
PXA2xxFIrState *s = PXA2XX_FIR(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
qemu_put_be32(f, s->enable);
|
||||
|
||||
qemu_put_8s(f, &s->control[0]);
|
||||
qemu_put_8s(f, &s->control[1]);
|
||||
qemu_put_8s(f, &s->control[2]);
|
||||
qemu_put_8s(f, &s->status[0]);
|
||||
qemu_put_8s(f, &s->status[1]);
|
||||
|
||||
qemu_put_byte(f, s->rx_len);
|
||||
for (i = 0; i < s->rx_len; i ++)
|
||||
qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]);
|
||||
memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s,
|
||||
"pxa2xx-fir", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->rx_dma);
|
||||
sysbus_init_irq(sbd, &s->tx_dma);
|
||||
}
|
||||
|
||||
static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id)
|
||||
static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
|
||||
int i;
|
||||
PXA2xxFIrState *s = PXA2XX_FIR(dev);
|
||||
|
||||
s->enable = qemu_get_be32(f);
|
||||
|
||||
qemu_get_8s(f, &s->control[0]);
|
||||
qemu_get_8s(f, &s->control[1]);
|
||||
qemu_get_8s(f, &s->control[2]);
|
||||
qemu_get_8s(f, &s->status[0]);
|
||||
qemu_get_8s(f, &s->status[1]);
|
||||
|
||||
s->rx_len = qemu_get_byte(f);
|
||||
s->rx_start = 0;
|
||||
for (i = 0; i < s->rx_len; i ++)
|
||||
s->rx_fifo[i] = qemu_get_byte(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
|
||||
hwaddr base,
|
||||
qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma,
|
||||
CharDriverState *chr)
|
||||
{
|
||||
PXA2xxFIrState *s = (PXA2xxFIrState *)
|
||||
g_malloc0(sizeof(PXA2xxFIrState));
|
||||
|
||||
s->irq = irq;
|
||||
s->rx_dma = rx_dma;
|
||||
s->tx_dma = tx_dma;
|
||||
s->chr = chr;
|
||||
|
||||
pxa2xx_fir_reset(s);
|
||||
|
||||
memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000);
|
||||
memory_region_add_subregion(sysmem, base, &s->iomem);
|
||||
|
||||
if (chr) {
|
||||
qemu_chr_fe_claim_no_fail(chr);
|
||||
qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
|
||||
if (s->chr) {
|
||||
qemu_chr_fe_claim_no_fail(s->chr);
|
||||
qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty,
|
||||
pxa2xx_fir_rx, pxa2xx_fir_event, s);
|
||||
}
|
||||
}
|
||||
|
||||
register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save,
|
||||
pxa2xx_fir_load, s);
|
||||
static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
|
||||
{
|
||||
PXA2xxFIrState *s = opaque;
|
||||
|
||||
return s;
|
||||
return s->rx_start < ARRAY_SIZE(s->rx_fifo);
|
||||
}
|
||||
|
||||
static const VMStateDescription pxa2xx_fir_vmsd = {
|
||||
.name = "pxa2xx-fir",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(enable, PXA2xxFIrState),
|
||||
VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3),
|
||||
VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2),
|
||||
VMSTATE_UINT32(rx_len, PXA2xxFIrState),
|
||||
VMSTATE_UINT32(rx_start, PXA2xxFIrState),
|
||||
VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate),
|
||||
VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property pxa2xx_fir_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pxa2xx_fir_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pxa2xx_fir_realize;
|
||||
dc->vmsd = &pxa2xx_fir_vmsd;
|
||||
dc->props = pxa2xx_fir_properties;
|
||||
dc->reset = pxa2xx_fir_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo pxa2xx_fir_info = {
|
||||
.name = TYPE_PXA2XX_FIR,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PXA2xxFIrState),
|
||||
.class_init = pxa2xx_fir_class_init,
|
||||
.instance_init = pxa2xx_fir_instance_init,
|
||||
};
|
||||
|
||||
static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
|
||||
hwaddr base,
|
||||
qemu_irq irq, qemu_irq rx_dma,
|
||||
qemu_irq tx_dma,
|
||||
CharDriverState *chr)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *sbd;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_PXA2XX_FIR);
|
||||
qdev_prop_set_chr(dev, "chardev", chr);
|
||||
qdev_init_nofail(dev);
|
||||
sbd = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(sbd, 0, base);
|
||||
sysbus_connect_irq(sbd, 0, irq);
|
||||
sysbus_connect_irq(sbd, 1, rx_dma);
|
||||
sysbus_connect_irq(sbd, 2, tx_dma);
|
||||
return PXA2XX_FIR(dev);
|
||||
}
|
||||
|
||||
static void pxa2xx_reset(void *opaque, int line, int level)
|
||||
@@ -2306,8 +2328,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
|
||||
static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
sdc->init = pxa2xx_ssp_init;
|
||||
dc->reset = pxa2xx_ssp_reset;
|
||||
dc->vmsd = &vmstate_pxa2xx_ssp;
|
||||
}
|
||||
|
||||
static const TypeInfo pxa2xx_ssp_info = {
|
||||
@@ -2323,6 +2348,7 @@ static void pxa2xx_register_types(void)
|
||||
type_register_static(&pxa2xx_ssp_info);
|
||||
type_register_static(&pxa2xx_i2c_info);
|
||||
type_register_static(&pxa2xx_rtc_sysbus_info);
|
||||
type_register_static(&pxa2xx_fir_info);
|
||||
}
|
||||
|
||||
type_init(pxa2xx_register_types)
|
||||
|
@@ -232,7 +232,7 @@ static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
|
||||
#define REGINFO_FOR_PIC_CP(NAME, CRN) \
|
||||
{ .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \
|
||||
.access = PL1_RW, \
|
||||
.access = PL1_RW, .type = ARM_CP_IO, \
|
||||
.readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write }
|
||||
|
||||
static const ARMCPRegInfo pxa_pic_cp_reginfo[] = {
|
||||
|
@@ -26,6 +26,9 @@
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/vfio/vfio-platform.h"
|
||||
#include "hw/vfio/vfio-calxeda-xgmac.h"
|
||||
#include "hw/arm/fdt.h"
|
||||
|
||||
/*
|
||||
* internal struct that contains the information to create dynamic
|
||||
@@ -53,11 +56,81 @@ typedef struct NodeCreationPair {
|
||||
int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
|
||||
} NodeCreationPair;
|
||||
|
||||
/* Device Specific Code */
|
||||
|
||||
/**
|
||||
* add_calxeda_midway_xgmac_fdt_node
|
||||
*
|
||||
* Generates a simple node with following properties:
|
||||
* compatible string, regs, interrupts, dma-coherent
|
||||
*/
|
||||
static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
|
||||
{
|
||||
PlatformBusFDTData *data = opaque;
|
||||
PlatformBusDevice *pbus = data->pbus;
|
||||
void *fdt = data->fdt;
|
||||
const char *parent_node = data->pbus_node_name;
|
||||
int compat_str_len, i, ret = -1;
|
||||
char *nodename;
|
||||
uint32_t *irq_attr, *reg_attr;
|
||||
uint64_t mmio_base, irq_number;
|
||||
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
|
||||
VFIODevice *vbasedev = &vdev->vbasedev;
|
||||
|
||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
|
||||
nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
|
||||
vbasedev->name, mmio_base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
|
||||
compat_str_len = strlen(vdev->compat) + 1;
|
||||
qemu_fdt_setprop(fdt, nodename, "compatible",
|
||||
vdev->compat, compat_str_len);
|
||||
|
||||
qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0);
|
||||
|
||||
reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
|
||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||
mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
|
||||
reg_attr[2 * i] = cpu_to_be32(mmio_base);
|
||||
reg_attr[2 * i + 1] = cpu_to_be32(
|
||||
memory_region_size(&vdev->regions[i]->mem));
|
||||
}
|
||||
ret = qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
|
||||
vbasedev->num_regions * 2 * sizeof(uint32_t));
|
||||
if (ret) {
|
||||
error_report("could not set reg property of node %s", nodename);
|
||||
goto fail_reg;
|
||||
}
|
||||
|
||||
irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
|
||||
for (i = 0; i < vbasedev->num_irqs; i++) {
|
||||
irq_number = platform_bus_get_irqn(pbus, sbdev , i)
|
||||
+ data->irq_start;
|
||||
irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
|
||||
irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
|
||||
irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
|
||||
}
|
||||
ret = qemu_fdt_setprop(fdt, nodename, "interrupts",
|
||||
irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
|
||||
if (ret) {
|
||||
error_report("could not set interrupts property of node %s",
|
||||
nodename);
|
||||
}
|
||||
g_free(irq_attr);
|
||||
fail_reg:
|
||||
g_free(reg_attr);
|
||||
g_free(nodename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* list of supported dynamic sysbus devices */
|
||||
static const NodeCreationPair add_fdt_node_functions[] = {
|
||||
{TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node},
|
||||
{"", NULL}, /* last element */
|
||||
};
|
||||
|
||||
/* Generic Code */
|
||||
|
||||
/**
|
||||
* add_fdt_node - add the device tree node of a dynamic sysbus device
|
||||
*
|
||||
|
@@ -84,6 +84,12 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, uart_irq));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
/* The _ADR entry is used to link this device to the UART described
|
||||
* in the SPCR table, i.e. SPCR.base_address.address == _ADR.
|
||||
*/
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base)));
|
||||
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
@@ -333,6 +339,38 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
|
||||
return rsdp_table;
|
||||
}
|
||||
|
||||
static void
|
||||
build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
{
|
||||
AcpiSerialPortConsoleRedirection *spcr;
|
||||
const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART];
|
||||
int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE;
|
||||
|
||||
spcr = acpi_data_push(table_data, sizeof(*spcr));
|
||||
|
||||
spcr->interface_type = 0x3; /* ARM PL011 UART */
|
||||
|
||||
spcr->base_address.space_id = AML_SYSTEM_MEMORY;
|
||||
spcr->base_address.bit_width = 8;
|
||||
spcr->base_address.bit_offset = 0;
|
||||
spcr->base_address.access_width = 1;
|
||||
spcr->base_address.address = cpu_to_le64(uart_memmap->base);
|
||||
|
||||
spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */
|
||||
spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */
|
||||
|
||||
spcr->baud = 3; /* Baud Rate: 3 = 9600 */
|
||||
spcr->parity = 0; /* No Parity */
|
||||
spcr->stopbits = 1; /* 1 Stop bit */
|
||||
spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */
|
||||
spcr->term_type = 0; /* Terminal Type: 0 = VT100 */
|
||||
|
||||
spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
|
||||
spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
|
||||
|
||||
build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
|
||||
}
|
||||
|
||||
static void
|
||||
build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
{
|
||||
@@ -514,7 +552,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
dsdt = tables_blob->len;
|
||||
build_dsdt(tables_blob, tables->linker, guest_info);
|
||||
|
||||
/* FADT MADT GTDT pointed to by RSDT */
|
||||
/* FADT MADT GTDT SPCR pointed to by RSDT */
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_fadt(tables_blob, tables->linker, dsdt);
|
||||
|
||||
@@ -527,6 +565,9 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_mcfg(tables_blob, tables->linker, guest_info);
|
||||
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_spcr(tables_blob, tables->linker, guest_info);
|
||||
|
||||
/* RSDT is pointed to by RSDP */
|
||||
rsdt = tables_blob->len;
|
||||
build_rsdt(tables_blob, tables->linker, table_offsets);
|
||||
|
@@ -47,21 +47,11 @@
|
||||
#include "hw/arm/virt-acpi-build.h"
|
||||
#include "hw/arm/sysbus-fdt.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/arm/fdt.h"
|
||||
|
||||
/* Number of external interrupt lines to configure the GIC with */
|
||||
#define NUM_IRQS 256
|
||||
|
||||
#define GIC_FDT_IRQ_TYPE_SPI 0
|
||||
#define GIC_FDT_IRQ_TYPE_PPI 1
|
||||
|
||||
#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1
|
||||
#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2
|
||||
#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4
|
||||
#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8
|
||||
|
||||
#define GIC_FDT_IRQ_PPI_CPU_START 8
|
||||
#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8
|
||||
|
||||
#define PLATFORM_BUS_NUM_IRQS 64
|
||||
|
||||
static ARMPlatformBusSystemParams platform_bus_params;
|
||||
@@ -144,6 +134,11 @@ static VirtBoardInfo machines[] = {
|
||||
.memmap = a15memmap,
|
||||
.irqmap = a15irqmap,
|
||||
},
|
||||
{
|
||||
.cpu_model = "cortex-a53",
|
||||
.memmap = a15memmap,
|
||||
.irqmap = a15irqmap,
|
||||
},
|
||||
{
|
||||
.cpu_model = "cortex-a57",
|
||||
.memmap = a15memmap,
|
||||
@@ -306,7 +301,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
|
||||
"enable-method", "psci");
|
||||
}
|
||||
|
||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity);
|
||||
g_free(nodename);
|
||||
}
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ static void xlnx_ep108_init(MachineState *machine)
|
||||
xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
|
||||
xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
|
||||
xlnx_ep108_binfo.loader_start = 0;
|
||||
arm_load_kernel(&s->soc.cpu[0], &xlnx_ep108_binfo);
|
||||
arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_ep108_binfo);
|
||||
}
|
||||
|
||||
static QEMUMachine xlnx_ep108_machine = {
|
||||
|
@@ -64,10 +64,17 @@ static void xlnx_zynqmp_init(Object *obj)
|
||||
XlnxZynqMPState *s = XLNX_ZYNQMP(obj);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) {
|
||||
object_initialize(&s->cpu[i], sizeof(s->cpu[i]),
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
|
||||
object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]),
|
||||
"cortex-a53-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
|
||||
object_property_add_child(obj, "apu-cpu[*]", OBJECT(&s->apu_cpu[i]),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
|
||||
object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
|
||||
"cortex-r5-" TYPE_ARM_CPU);
|
||||
object_property_add_child(obj, "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
@@ -90,12 +97,13 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
XlnxZynqMPState *s = XLNX_ZYNQMP(dev);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
uint8_t i;
|
||||
const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]";
|
||||
qemu_irq gic_spi[GIC_NUM_SPI_INTR];
|
||||
Error *err = NULL;
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32);
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_CPUS);
|
||||
qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS);
|
||||
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
@@ -121,38 +129,77 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_CPUS; i++) {
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
|
||||
qemu_irq irq;
|
||||
char *name;
|
||||
|
||||
object_property_set_int(OBJECT(&s->cpu[i]), QEMU_PSCI_CONDUIT_SMC,
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC,
|
||||
"psci-conduit", &error_abort);
|
||||
if (i > 0) {
|
||||
|
||||
name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i]));
|
||||
if (strcmp(name, boot_cpu)) {
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
object_property_set_bool(OBJECT(&s->cpu[i]), true,
|
||||
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true,
|
||||
"start-powered-off", &error_abort);
|
||||
} else {
|
||||
s->boot_cpu_ptr = &s->apu_cpu[i];
|
||||
}
|
||||
|
||||
object_property_set_int(OBJECT(&s->cpu[i]), GIC_BASE_ADDR,
|
||||
object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
|
||||
"reset-cbar", &err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->cpu[i]), true, "realized", &err);
|
||||
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
return;
|
||||
}
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
|
||||
qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
|
||||
qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]),
|
||||
ARM_CPU_IRQ));
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->gic),
|
||||
arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), 0, irq);
|
||||
qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 0, irq);
|
||||
irq = qdev_get_gpio_in(DEVICE(&s->gic),
|
||||
arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI));
|
||||
qdev_connect_gpio_out(DEVICE(&s->cpu[i]), 1, irq);
|
||||
qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq);
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
|
||||
char *name;
|
||||
|
||||
name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i]));
|
||||
if (strcmp(name, boot_cpu)) {
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true,
|
||||
"start-powered-off", &error_abort);
|
||||
} else {
|
||||
s->boot_cpu_ptr = &s->rpu_cpu[i];
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
|
||||
&err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate((errp), (err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!s->boot_cpu_ptr) {
|
||||
error_setg(errp, "ZynqMP Boot cpu %s not found\n", boot_cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < GIC_NUM_SPI_INTR; i++) {
|
||||
@@ -188,10 +235,16 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static Property xlnx_zynqmp_props[] = {
|
||||
DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->props = xlnx_zynqmp_props;
|
||||
dc->realize = xlnx_zynqmp_realize;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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(),
|
||||
};
|
||||
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "hw/nmi.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "monitor/monitor.h"
|
||||
|
||||
struct do_nmi_s {
|
||||
int cpu_index;
|
||||
@@ -70,6 +71,25 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
void inject_nmi(void)
|
||||
{
|
||||
#if defined(TARGET_I386)
|
||||
CPUState *cs;
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
|
||||
if (!cpu->apic_state) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_NMI);
|
||||
} else {
|
||||
apic_deliver_nmi(cpu->apic_state);
|
||||
}
|
||||
}
|
||||
#else
|
||||
nmi_monitor_handle(0, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const TypeInfo nmi_info = {
|
||||
.name = TYPE_NMI,
|
||||
.parent = TYPE_INTERFACE,
|
||||
|
@@ -126,9 +126,9 @@ void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp)
|
||||
qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp);
|
||||
}
|
||||
|
||||
/* Create a new device. This only initializes the device state structure
|
||||
and allows properties to be set. qdev_init should be called to
|
||||
initialize the actual device emulation. */
|
||||
/* Create a new device. This only initializes the device state
|
||||
structure and allows properties to be set. The device still needs
|
||||
to be realized. See qdev-core.h. */
|
||||
DeviceState *qdev_create(BusState *bus, const char *name)
|
||||
{
|
||||
DeviceState *dev;
|
||||
@@ -168,27 +168,6 @@ DeviceState *qdev_try_create(BusState *bus, const char *type)
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* Initialize a device. Device properties should be set before calling
|
||||
this function. IRQs and MMIO regions should be connected/mapped after
|
||||
calling this function.
|
||||
On failure, destroy the device and return negative value.
|
||||
Return 0 on success. */
|
||||
int qdev_init(DeviceState *dev)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(!dev->realized);
|
||||
|
||||
object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
|
||||
if (local_err != NULL) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
object_unparent(OBJECT(dev));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners
|
||||
= QTAILQ_HEAD_INITIALIZER(device_listeners);
|
||||
|
||||
@@ -364,13 +343,19 @@ void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
|
||||
object_unparent(OBJECT(dev));
|
||||
}
|
||||
|
||||
/* Like qdev_init(), but terminate program via error_report() instead of
|
||||
returning an error value. This is okay during machine creation.
|
||||
Don't use for hotplug, because there callers need to recover from
|
||||
failure. Exception: if you know the device's init() callback can't
|
||||
fail, then qdev_init_nofail() can't fail either, and is therefore
|
||||
usable even then. But relying on the device implementation that
|
||||
way is somewhat unclean, and best avoided. */
|
||||
/*
|
||||
* Realize @dev.
|
||||
* Device properties should be set before calling this function. IRQs
|
||||
* and MMIO regions should be connected/mapped after calling this
|
||||
* function.
|
||||
* On failure, report an error with error_report() and terminate the
|
||||
* program. This is okay during machine creation. Don't use for
|
||||
* hotplug, because there callers need to recover from failure.
|
||||
* Exception: if you know the device's init() callback can't fail,
|
||||
* then qdev_init_nofail() can't fail either, and is therefore usable
|
||||
* even then. But relying on the device implementation that way is
|
||||
* somewhat unclean, and best avoided.
|
||||
*/
|
||||
void qdev_init_nofail(DeviceState *dev)
|
||||
{
|
||||
Error *err = NULL;
|
||||
@@ -563,6 +548,7 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
|
||||
object_property_add_alias(OBJECT(container), propname,
|
||||
OBJECT(dev), propname,
|
||||
&error_abort);
|
||||
g_free(propname);
|
||||
}
|
||||
for (i = 0; i < ngl->num_out; i++) {
|
||||
const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
|
||||
@@ -571,6 +557,7 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
|
||||
object_property_add_alias(OBJECT(container), propname,
|
||||
OBJECT(dev), propname,
|
||||
&error_abort);
|
||||
g_free(propname);
|
||||
}
|
||||
QLIST_REMOVE(ngl, node);
|
||||
QLIST_INSERT_HEAD(&container->gpios, ngl, node);
|
||||
|
@@ -281,19 +281,15 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
|
||||
static char *sysbus_get_fw_dev_path(DeviceState *dev)
|
||||
{
|
||||
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||
char path[40];
|
||||
int off;
|
||||
|
||||
off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
|
||||
|
||||
if (s->num_mmio) {
|
||||
snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx,
|
||||
s->mmio[0].addr);
|
||||
} else if (s->num_pio) {
|
||||
snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
|
||||
return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev),
|
||||
s->mmio[0].addr);
|
||||
}
|
||||
|
||||
return g_strdup(path);
|
||||
if (s->num_pio) {
|
||||
return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]);
|
||||
}
|
||||
return g_strdup(qdev_fw_name(dev));
|
||||
}
|
||||
|
||||
void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
|
||||
|
@@ -36,3 +36,5 @@ obj-$(CONFIG_VGA) += vga.o
|
||||
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
|
||||
|
||||
obj-$(CONFIG_VIRTIO) += virtio-gpu.o
|
||||
obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
|
||||
obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
|
||||
|
@@ -337,7 +337,7 @@ static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data)
|
||||
if (swap_ctl & FIMD_WINCON_SWAP_BITS) {
|
||||
res = 0;
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (x & (1ULL << (64 - i))) {
|
||||
if (x & (1ULL << (63 - i))) {
|
||||
res |= (1ULL << i);
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -204,10 +204,10 @@ static const MemoryRegionOps pci_vga_qext_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void pci_std_vga_mmio_region_init(VGACommonState *s,
|
||||
MemoryRegion *parent,
|
||||
MemoryRegion *subs,
|
||||
bool qext)
|
||||
void pci_std_vga_mmio_region_init(VGACommonState *s,
|
||||
MemoryRegion *parent,
|
||||
MemoryRegion *subs,
|
||||
bool qext)
|
||||
{
|
||||
memory_region_init_io(&subs[0], NULL, &pci_vga_ioport_ops, s,
|
||||
"vga ioports remapped", PCI_VGA_IOPORT_SIZE);
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -219,4 +219,10 @@ extern const uint8_t gr_mask[16];
|
||||
|
||||
extern const MemoryRegionOps vga_mem_ops;
|
||||
|
||||
/* vga-pci.c */
|
||||
void pci_std_vga_mmio_region_init(VGACommonState *s,
|
||||
MemoryRegion *parent,
|
||||
MemoryRegion *subs,
|
||||
bool qext);
|
||||
|
||||
#endif
|
||||
|
68
hw/display/virtio-gpu-pci.c
Normal file
68
hw/display/virtio-gpu-pci.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Virtio video device
|
||||
*
|
||||
* Copyright Red Hat
|
||||
*
|
||||
* Authors:
|
||||
* Dave Airlie
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "hw/virtio/virtio-pci.h"
|
||||
#include "hw/virtio/virtio-gpu.h"
|
||||
|
||||
static Property virtio_gpu_pci_properties[] = {
|
||||
DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOGPUPCI, vdev.conf),
|
||||
DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
|
||||
{
|
||||
VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
|
||||
DeviceState *vdev = DEVICE(&vgpu->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_gpu_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);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
||||
dc->props = virtio_gpu_pci_properties;
|
||||
k->realize = virtio_gpu_pci_realize;
|
||||
pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
|
||||
}
|
||||
|
||||
static void virtio_gpu_initfn(Object *obj)
|
||||
{
|
||||
VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_gpu_pci_info = {
|
||||
.name = TYPE_VIRTIO_GPU_PCI,
|
||||
.parent = TYPE_VIRTIO_PCI,
|
||||
.instance_size = sizeof(VirtIOGPUPCI),
|
||||
.instance_init = virtio_gpu_initfn,
|
||||
.class_init = virtio_gpu_pci_class_init,
|
||||
};
|
||||
|
||||
static void virtio_gpu_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&virtio_gpu_pci_info);
|
||||
}
|
||||
type_init(virtio_gpu_pci_register_types)
|
@@ -534,7 +534,7 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
|
||||
|
||||
if (ab->nr_entries > 16384) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: nr_entries is too big (%d > 1024)\n",
|
||||
"%s: nr_entries is too big (%d > 16384)\n",
|
||||
__func__, ab->nr_entries);
|
||||
return -1;
|
||||
}
|
||||
|
175
hw/display/virtio-vga.c
Normal file
175
hw/display/virtio-vga.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "ui/console.h"
|
||||
#include "vga_int.h"
|
||||
#include "hw/virtio/virtio-pci.h"
|
||||
|
||||
/*
|
||||
* virtio-vga: This extends VirtioPCIProxy.
|
||||
*/
|
||||
#define TYPE_VIRTIO_VGA "virtio-vga"
|
||||
#define VIRTIO_VGA(obj) \
|
||||
OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
|
||||
|
||||
typedef struct VirtIOVGA {
|
||||
VirtIOPCIProxy parent_obj;
|
||||
VirtIOGPU vdev;
|
||||
VGACommonState vga;
|
||||
MemoryRegion vga_mrs[3];
|
||||
} VirtIOVGA;
|
||||
|
||||
static void virtio_vga_invalidate_display(void *opaque)
|
||||
{
|
||||
VirtIOVGA *vvga = opaque;
|
||||
|
||||
if (vvga->vdev.enable) {
|
||||
virtio_gpu_ops.invalidate(&vvga->vdev);
|
||||
} else {
|
||||
vvga->vga.hw_ops->invalidate(&vvga->vga);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_vga_update_display(void *opaque)
|
||||
{
|
||||
VirtIOVGA *vvga = opaque;
|
||||
|
||||
if (vvga->vdev.enable) {
|
||||
virtio_gpu_ops.gfx_update(&vvga->vdev);
|
||||
} else {
|
||||
vvga->vga.hw_ops->gfx_update(&vvga->vga);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
|
||||
{
|
||||
VirtIOVGA *vvga = opaque;
|
||||
|
||||
if (vvga->vdev.enable) {
|
||||
if (virtio_gpu_ops.text_update) {
|
||||
virtio_gpu_ops.text_update(&vvga->vdev, chardata);
|
||||
}
|
||||
} else {
|
||||
if (vvga->vga.hw_ops->text_update) {
|
||||
vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
{
|
||||
VirtIOVGA *vvga = opaque;
|
||||
|
||||
if (virtio_gpu_ops.ui_info) {
|
||||
return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const GraphicHwOps virtio_vga_ops = {
|
||||
.invalidate = virtio_vga_invalidate_display,
|
||||
.gfx_update = virtio_vga_update_display,
|
||||
.text_update = virtio_vga_text_update,
|
||||
.ui_info = virtio_vga_ui_info,
|
||||
};
|
||||
|
||||
/* VGA device wrapper around PCI device around virtio GPU */
|
||||
static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
|
||||
{
|
||||
VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
|
||||
VirtIOGPU *g = &vvga->vdev;
|
||||
VGACommonState *vga = &vvga->vga;
|
||||
uint32_t offset;
|
||||
|
||||
/* init vga compat bits */
|
||||
vga->vram_size_mb = 8;
|
||||
vga_common_init(vga, OBJECT(vpci_dev), false);
|
||||
vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev),
|
||||
pci_address_space_io(&vpci_dev->pci_dev), true);
|
||||
pci_register_bar(&vpci_dev->pci_dev, 0,
|
||||
PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
|
||||
|
||||
/*
|
||||
* Configure virtio bar and regions
|
||||
*
|
||||
* We use bar #2 for the mmio regions, to be compatible with stdvga.
|
||||
* virtio regions are moved to the end of bar #2, to make room for
|
||||
* the stdvga mmio registers at the start of bar #2.
|
||||
*/
|
||||
vpci_dev->modern_mem_bar = 2;
|
||||
vpci_dev->msix_bar = 4;
|
||||
offset = memory_region_size(&vpci_dev->modern_bar);
|
||||
offset -= vpci_dev->notify.size;
|
||||
vpci_dev->notify.offset = offset;
|
||||
offset -= vpci_dev->device.size;
|
||||
vpci_dev->device.offset = offset;
|
||||
offset -= vpci_dev->isr.size;
|
||||
vpci_dev->isr.offset = offset;
|
||||
offset -= vpci_dev->common.size;
|
||||
vpci_dev->common.offset = offset;
|
||||
|
||||
/* init virtio bits */
|
||||
qdev_set_parent_bus(DEVICE(g), 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(g), true, "realized", errp);
|
||||
|
||||
/* add stdvga mmio regions */
|
||||
pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar,
|
||||
vvga->vga_mrs, true);
|
||||
|
||||
vga->con = g->scanout[0].con;
|
||||
graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga);
|
||||
}
|
||||
|
||||
static void virtio_vga_reset(DeviceState *dev)
|
||||
{
|
||||
VirtIOVGA *vvga = VIRTIO_VGA(dev);
|
||||
vvga->vdev.enable = 0;
|
||||
|
||||
vga_dirty_log_start(&vvga->vga);
|
||||
}
|
||||
|
||||
static Property virtio_vga_properties[] = {
|
||||
DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOVGA, vdev.conf),
|
||||
DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_vga_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
|
||||
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
||||
dc->props = virtio_vga_properties;
|
||||
dc->reset = virtio_vga_reset;
|
||||
dc->hotpluggable = false;
|
||||
|
||||
k->realize = virtio_vga_realize;
|
||||
pcidev_k->romfile = "vgabios-virtio.bin";
|
||||
pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
|
||||
}
|
||||
|
||||
static void virtio_vga_inst_initfn(Object *obj)
|
||||
{
|
||||
VirtIOVGA *dev = VIRTIO_VGA(obj);
|
||||
object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
|
||||
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
|
||||
}
|
||||
|
||||
static TypeInfo virtio_vga_info = {
|
||||
.name = TYPE_VIRTIO_VGA,
|
||||
.parent = TYPE_VIRTIO_PCI,
|
||||
.instance_size = sizeof(struct VirtIOVGA),
|
||||
.instance_init = virtio_vga_inst_initfn,
|
||||
.class_init = virtio_vga_class_init,
|
||||
};
|
||||
|
||||
static void virtio_vga_register_types(void)
|
||||
{
|
||||
type_register_static(&virtio_vga_info);
|
||||
}
|
||||
|
||||
type_init(virtio_vga_register_types)
|
468
hw/dma/rc4030.c
468
hw/dma/rc4030.c
@@ -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)
|
||||
@@ -401,6 +403,7 @@ DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
|
||||
static void pc_q35_2_3_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_2_4_machine_options(m);
|
||||
m->no_floppy = 0;
|
||||
m->alias = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
16
hw/ide/pci.c
16
hw/ide/pci.c
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -11,6 +11,7 @@ common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
|
||||
ifeq ($(CONFIG_LINUX),y)
|
||||
common-obj-$(CONFIG_VIRTIO) += virtio-input.o
|
||||
common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
|
||||
common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
188
hw/input/virtio-input-host.c
Normal file
188
hw/input/virtio-input-host.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/sockets.h"
|
||||
|
||||
#include "hw/qdev.h"
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-input.h"
|
||||
|
||||
#include "standard-headers/linux/input.h"
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
static struct virtio_input_config virtio_input_host_config[] = {
|
||||
{ /* empty list */ },
|
||||
};
|
||||
|
||||
static void virtio_input_host_event(void *opaque)
|
||||
{
|
||||
VirtIOInputHost *vih = opaque;
|
||||
VirtIOInput *vinput = VIRTIO_INPUT(vih);
|
||||
struct virtio_input_event virtio;
|
||||
struct input_event evdev;
|
||||
int rc;
|
||||
|
||||
for (;;) {
|
||||
rc = read(vih->fd, &evdev, sizeof(evdev));
|
||||
if (rc != sizeof(evdev)) {
|
||||
break;
|
||||
}
|
||||
|
||||
virtio.type = cpu_to_le16(evdev.type);
|
||||
virtio.code = cpu_to_le16(evdev.code);
|
||||
virtio.value = cpu_to_le32(evdev.value);
|
||||
virtio_input_send(vinput, &virtio);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_input_bits_config(VirtIOInputHost *vih,
|
||||
int type, int count)
|
||||
{
|
||||
virtio_input_config bits;
|
||||
int rc, i, size = 0;
|
||||
|
||||
memset(&bits, 0, sizeof(bits));
|
||||
rc = ioctl(vih->fd, EVIOCGBIT(type, count/8), bits.u.bitmap);
|
||||
if (rc < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < count/8; i++) {
|
||||
if (bits.u.bitmap[i]) {
|
||||
size = i+1;
|
||||
}
|
||||
}
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bits.select = VIRTIO_INPUT_CFG_EV_BITS;
|
||||
bits.subsel = type;
|
||||
bits.size = size;
|
||||
virtio_input_add_config(VIRTIO_INPUT(vih), &bits);
|
||||
}
|
||||
|
||||
static void virtio_input_host_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
|
||||
VirtIOInput *vinput = VIRTIO_INPUT(dev);
|
||||
virtio_input_config id;
|
||||
struct input_id ids;
|
||||
int rc, ver;
|
||||
|
||||
if (!vih->evdev) {
|
||||
error_setg(errp, "evdev property is required");
|
||||
return;
|
||||
}
|
||||
|
||||
vih->fd = open(vih->evdev, O_RDWR);
|
||||
if (vih->fd < 0) {
|
||||
error_setg_file_open(errp, errno, vih->evdev);
|
||||
return;
|
||||
}
|
||||
qemu_set_nonblock(vih->fd);
|
||||
|
||||
rc = ioctl(vih->fd, EVIOCGVERSION, &ver);
|
||||
if (rc < 0) {
|
||||
error_setg(errp, "%s: is not an evdev device", vih->evdev);
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
rc = ioctl(vih->fd, EVIOCGRAB, 1);
|
||||
if (rc < 0) {
|
||||
error_setg_errno(errp, errno, "%s: failed to get exclusive access",
|
||||
vih->evdev);
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
memset(&id, 0, sizeof(id));
|
||||
ioctl(vih->fd, EVIOCGNAME(sizeof(id.u.string)-1), id.u.string);
|
||||
id.select = VIRTIO_INPUT_CFG_ID_NAME;
|
||||
id.size = strlen(id.u.string);
|
||||
virtio_input_add_config(vinput, &id);
|
||||
|
||||
if (ioctl(vih->fd, EVIOCGID, &ids) == 0) {
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.select = VIRTIO_INPUT_CFG_ID_DEVIDS;
|
||||
id.size = sizeof(struct virtio_input_devids);
|
||||
id.u.ids.bustype = cpu_to_le16(ids.bustype);
|
||||
id.u.ids.vendor = cpu_to_le16(ids.vendor);
|
||||
id.u.ids.product = cpu_to_le16(ids.product);
|
||||
id.u.ids.version = cpu_to_le16(ids.version);
|
||||
virtio_input_add_config(vinput, &id);
|
||||
}
|
||||
|
||||
virtio_input_bits_config(vih, EV_KEY, KEY_CNT);
|
||||
virtio_input_bits_config(vih, EV_REL, REL_CNT);
|
||||
virtio_input_bits_config(vih, EV_ABS, ABS_CNT);
|
||||
virtio_input_bits_config(vih, EV_MSC, MSC_CNT);
|
||||
virtio_input_bits_config(vih, EV_SW, SW_CNT);
|
||||
|
||||
qemu_set_fd_handler(vih->fd, virtio_input_host_event, NULL, vih);
|
||||
return;
|
||||
|
||||
err_close:
|
||||
close(vih->fd);
|
||||
vih->fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
static void virtio_input_host_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
|
||||
|
||||
if (vih->fd > 0) {
|
||||
qemu_set_fd_handler(vih->fd, NULL, NULL, NULL);
|
||||
close(vih->fd);
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_virtio_input_host = {
|
||||
.name = "virtio-input-host",
|
||||
.unmigratable = 1,
|
||||
};
|
||||
|
||||
static Property virtio_input_host_properties[] = {
|
||||
DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_input_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_virtio_input_host;
|
||||
dc->props = virtio_input_host_properties;
|
||||
vic->realize = virtio_input_host_realize;
|
||||
vic->unrealize = virtio_input_host_unrealize;
|
||||
}
|
||||
|
||||
static void virtio_input_host_init(Object *obj)
|
||||
{
|
||||
VirtIOInput *vinput = VIRTIO_INPUT(obj);
|
||||
|
||||
virtio_input_init_config(vinput, virtio_input_host_config);
|
||||
}
|
||||
|
||||
static const TypeInfo virtio_input_host_info = {
|
||||
.name = TYPE_VIRTIO_INPUT_HOST,
|
||||
.parent = TYPE_VIRTIO_INPUT,
|
||||
.instance_size = sizeof(VirtIOInputHost),
|
||||
.instance_init = virtio_input_host_init,
|
||||
.class_init = virtio_input_host_class_init,
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
static void virtio_register_types(void)
|
||||
{
|
||||
type_register_static(&virtio_input_host_info);
|
||||
}
|
||||
|
||||
type_init(virtio_register_types)
|
@@ -216,7 +216,7 @@ static void virtio_input_device_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
|
||||
vinput->input.serial);
|
||||
vinput->serial);
|
||||
|
||||
QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
|
||||
if (vinput->cfg_size < cfg->config.size) {
|
||||
@@ -248,11 +248,17 @@ static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
|
||||
virtio_cleanup(vdev);
|
||||
}
|
||||
|
||||
static Property virtio_input_properties[] = {
|
||||
DEFINE_PROP_STRING("serial", VirtIOInput, serial),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void virtio_input_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||
|
||||
dc->props = virtio_input_properties;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
vdc->realize = virtio_input_device_realize;
|
||||
vdc->unrealize = virtio_input_device_unrealize;
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -71,7 +71,7 @@ void gic_update(GICState *s)
|
||||
|| !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) {
|
||||
qemu_irq_lower(s->parent_irq[cpu]);
|
||||
qemu_irq_lower(s->parent_fiq[cpu]);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
best_prio = 0x100;
|
||||
best_irq = 1023;
|
||||
|
@@ -179,6 +179,9 @@ ISADevice *isa_vga_init(ISABus *bus)
|
||||
case VGA_VMWARE:
|
||||
fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__);
|
||||
return NULL;
|
||||
case VGA_VIRTIO:
|
||||
fprintf(stderr, "%s: virtio-vga: no PCI bus\n", __func__);
|
||||
return NULL;
|
||||
case VGA_NONE:
|
||||
default:
|
||||
return NULL;
|
||||
|
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -102,6 +102,20 @@ static void mcf_intc_write(void *opaque, hwaddr addr,
|
||||
case 0x0c:
|
||||
s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val;
|
||||
break;
|
||||
case 0x1c:
|
||||
if (val & 0x40) {
|
||||
s->imr = ~0ull;
|
||||
} else {
|
||||
s->imr |= (0x1ull << (val & 0x3f));
|
||||
}
|
||||
break;
|
||||
case 0x1d:
|
||||
if (val & 0x40) {
|
||||
s->imr = 0ull;
|
||||
} else {
|
||||
s->imr &= ~(0x1ull << (val & 0x3f));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
hw_error("mcf_intc_write: Bad write offset %d\n", offset);
|
||||
break;
|
||||
|
@@ -64,20 +64,6 @@
|
||||
#define SPI_IRQ 4
|
||||
#define UART16550_IRQ 5
|
||||
|
||||
static void machine_cpu_reset(MicroBlazeCPU *cpu)
|
||||
{
|
||||
CPUMBState *env = &cpu->env;
|
||||
|
||||
env->pvr.regs[10] = 0x0e000000; /* virtex 6 */
|
||||
/* setup pvr to match kernel setting */
|
||||
env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
|
||||
env->pvr.regs[0] |= PVR0_USE_FPU_MASK | PVR0_ENDI;
|
||||
env->pvr.regs[0] = (env->pvr.regs[0] & ~PVR0_VERSION_MASK) | (0x14 << 8);
|
||||
env->pvr.regs[2] ^= PVR2_USE_FPU2_MASK;
|
||||
env->pvr.regs[4] = 0xc56b8000;
|
||||
env->pvr.regs[5] = 0xc56be000;
|
||||
}
|
||||
|
||||
static void
|
||||
petalogix_ml605_init(MachineState *machine)
|
||||
{
|
||||
@@ -95,6 +81,13 @@ petalogix_ml605_init(MachineState *machine)
|
||||
|
||||
/* init CPUs */
|
||||
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
|
||||
/* Use FPU but don't use floating point conversion and square
|
||||
* root instructions
|
||||
*/
|
||||
object_property_set_int(OBJECT(cpu), 1, "use-fpu", &error_abort);
|
||||
object_property_set_bool(OBJECT(cpu), true, "dcache-writeback",
|
||||
&error_abort);
|
||||
object_property_set_bool(OBJECT(cpu), true, "endianness", &error_abort);
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
|
||||
|
||||
/* Attach emulated BRAM through the LMB. */
|
||||
@@ -201,10 +194,15 @@ petalogix_ml605_init(MachineState *machine)
|
||||
}
|
||||
}
|
||||
|
||||
/* setup PVR to match kernel settings */
|
||||
cpu->env.pvr.regs[4] = 0xc56b8000;
|
||||
cpu->env.pvr.regs[5] = 0xc56be000;
|
||||
cpu->env.pvr.regs[10] = 0x0e000000; /* virtex 6 */
|
||||
|
||||
microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size,
|
||||
machine->initrd_filename,
|
||||
BINARY_DEVICE_TREE_FILE,
|
||||
machine_cpu_reset);
|
||||
NULL);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -51,18 +51,10 @@
|
||||
#define ETHLITE_IRQ 1
|
||||
#define UARTLITE_IRQ 3
|
||||
|
||||
static void machine_cpu_reset(MicroBlazeCPU *cpu)
|
||||
{
|
||||
CPUMBState *env = &cpu->env;
|
||||
|
||||
env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
|
||||
}
|
||||
|
||||
static void
|
||||
petalogix_s3adsp1800_init(MachineState *machine)
|
||||
{
|
||||
ram_addr_t ram_size = machine->ram_size;
|
||||
const char *cpu_model = machine->cpu_model;
|
||||
DeviceState *dev;
|
||||
MicroBlazeCPU *cpu;
|
||||
DriveInfo *dinfo;
|
||||
@@ -73,11 +65,8 @@ petalogix_s3adsp1800_init(MachineState *machine)
|
||||
qemu_irq irq[32];
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
cpu_model = "microblaze";
|
||||
}
|
||||
cpu = cpu_mb_init(cpu_model);
|
||||
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
|
||||
|
||||
/* Attach emulated BRAM through the LMB. */
|
||||
memory_region_init_ram(phys_lmb_bram, NULL,
|
||||
@@ -132,7 +121,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
|
||||
microblaze_load_kernel(cpu, ddr_base, ram_size,
|
||||
machine->initrd_filename,
|
||||
BINARY_DEVICE_TREE_FILE,
|
||||
machine_cpu_reset);
|
||||
NULL);
|
||||
}
|
||||
|
||||
static QEMUMachine petalogix_s3adsp1800_machine = {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user