mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 20:05:54 +00:00
qemu-usb: xHCI model as library
NEC USB3 controller ported from Qemu 2.4.1. See README for more details. Issue #1863.
This commit is contained in:
parent
e233fe0b71
commit
a640be9a24
129
repos/libports/include/qemu/usb.h
Normal file
129
repos/libports/include/qemu/usb.h
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* \brief Qemu USB controller interface
|
||||
* \author Josef Soentgen
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-12-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__QEMU__USB_H_
|
||||
#define _INCLUDE__QEMU__USB_H_
|
||||
|
||||
#include <base/stdint.h>
|
||||
#include <base/signal.h>
|
||||
|
||||
namespace Qemu {
|
||||
|
||||
typedef Genode::size_t size_t;
|
||||
typedef Genode::off_t off_t;
|
||||
typedef Genode::int64_t int64_t;
|
||||
typedef Genode::addr_t addr_t;
|
||||
|
||||
|
||||
/************************************
|
||||
** Qemu library backend interface **
|
||||
************************************/
|
||||
|
||||
/*
|
||||
* The backend interface needs to be provided the user of the library.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Timer_queue class
|
||||
*
|
||||
* The library uses the Timer_queue internally to schedule timeouts.
|
||||
*/
|
||||
struct Timer_queue
|
||||
{
|
||||
virtual int64_t get_ns() = 0;
|
||||
virtual void register_timer(void *qtimer, void (*cb)(void *data), void *data) = 0;
|
||||
virtual void delete_timer(void *qtimer) = 0;
|
||||
virtual void activate_timer(void *qtimer, long long int expires_abs) = 0;
|
||||
virtual void deactivate_timer(void *qtimer) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pci_device class
|
||||
*
|
||||
* The library uses the Pci_device to access physical DMA memory and to
|
||||
* raise interrupts.
|
||||
*/
|
||||
struct Pci_device
|
||||
{
|
||||
/**
|
||||
* Raise interrupt
|
||||
*
|
||||
* \param assert 1 == raise, 0 == deassert interrupt
|
||||
*/
|
||||
virtual void raise_interrupt(int assert) = 0;
|
||||
virtual int read_dma(addr_t addr, void *buf, size_t size) = 0;
|
||||
virtual int write_dma(addr_t addr, void const *buf, size_t size) = 0;
|
||||
virtual void *map_dma(addr_t base, size_t size) = 0;
|
||||
virtual void unmap_dma(void *addr, size_t size) = 0;
|
||||
};
|
||||
|
||||
|
||||
/*************************************
|
||||
** Qemu library frontend functions **
|
||||
*************************************/
|
||||
|
||||
/**
|
||||
* Controller class to access MMIO register of the xHCI device model
|
||||
*
|
||||
* This class is implemented by the library.
|
||||
*/
|
||||
struct Controller
|
||||
{
|
||||
/**
|
||||
* Size of the MMIO region of the controller
|
||||
*/
|
||||
virtual size_t mmio_size() const = 0;
|
||||
|
||||
/**
|
||||
* Read/write MMIO offset in the MMIO region of the controller
|
||||
*/
|
||||
virtual int mmio_read(off_t offset, void *buf, size_t size) = 0;
|
||||
virtual int mmio_write(off_t offset, void const *buf, size_t size) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize USB library
|
||||
*
|
||||
* \param tq Timer_queue instance provided by the user of the library
|
||||
* \param pd Pci_device instance provided by the user of the library
|
||||
* \param sr Signal_receiver used by the library to install signals
|
||||
*
|
||||
* \return Pointer to Controller object that is used to access the xHCI device state
|
||||
*/
|
||||
Controller *usb_init(Timer_queue &tq, Pci_device &pd, Genode::Signal_receiver &sr);
|
||||
|
||||
/**
|
||||
* Reset USB libray
|
||||
*/
|
||||
void usb_reset();
|
||||
|
||||
/**
|
||||
* Update USB devices list
|
||||
*
|
||||
* Needs to be called after a library reset.
|
||||
*/
|
||||
void usb_update_devices();
|
||||
|
||||
/**
|
||||
* Library timer callback
|
||||
*
|
||||
* Needs to be called when a timer triggers (see Timer_queue interface).
|
||||
*
|
||||
* \param cb Callback installed by Timer_queue::register_timer()
|
||||
* \param data Data pointer given in Timer_queue::register_timer()
|
||||
*/
|
||||
void usb_timer_callback(void (*cb)(void *data), void *data);
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__QEMU__USB_H_ */
|
11
repos/libports/lib/import/import-qemu-usb_include.mk
Normal file
11
repos/libports/lib/import/import-qemu-usb_include.mk
Normal file
@ -0,0 +1,11 @@
|
||||
QEMU_CONTRIB_DIR := $(call select_from_ports,qemu-usb)/src/lib/qemu
|
||||
|
||||
LIB_DIR := $(REP_DIR)/src/lib/qemu-usb
|
||||
LIB_INC_DIR := $(LIB_DIR)/include
|
||||
|
||||
#
|
||||
# The order of include-search directories is important, we need to look into
|
||||
# 'contrib' before falling back to our custom 'qemu_emul.h' header.
|
||||
INC_DIR += $(LIB_INC_DIR)
|
||||
INC_DIR += $(QEMU_CONTRIB_DIR)/include
|
||||
INC_DIR += $(LIB_CACHE_DIR)/qemu-usb_include/include
|
19
repos/libports/lib/mk/qemu-usb.mk
Normal file
19
repos/libports/lib/mk/qemu-usb.mk
Normal file
@ -0,0 +1,19 @@
|
||||
LIB_DIR = $(REP_DIR)/src/lib/qemu-usb
|
||||
QEMU_USB_DIR = $(call select_from_ports,qemu-usb)/src/lib/qemu/hw/usb
|
||||
|
||||
CC_WARN=
|
||||
|
||||
INC_DIR += $(LIB_DIR) $(QEMU_USB_DIR)
|
||||
|
||||
LIBS = qemu-usb_include
|
||||
|
||||
SRC_CC = dummies.cc qemu_emul.cc host.cc
|
||||
|
||||
SRC_C = hcd-xhci.c core.c bus.c
|
||||
|
||||
SHARED_LIB = yes
|
||||
|
||||
LD_OPT += --version-script=$(LIB_DIR)/symbol.map
|
||||
|
||||
vpath %.c $(QEMU_USB_DIR)
|
||||
vpath %.cc $(LIB_DIR)
|
24
repos/libports/lib/mk/qemu-usb_include.mk
Normal file
24
repos/libports/lib/mk/qemu-usb_include.mk
Normal file
@ -0,0 +1,24 @@
|
||||
ifeq ($(called_from_lib_mk),yes)
|
||||
|
||||
QEMU_CONTRIB_DIR := $(call select_from_ports,qemu-usb)/src/lib/qemu
|
||||
QEMU_EMUL_H := $(REP_DIR)/src/lib/qemu-usb/include/qemu_emul.h
|
||||
|
||||
#
|
||||
# Determine the header files included by the contrib code. For each
|
||||
# of these header files we create a symlink to 'qemu_emul.h'.
|
||||
#
|
||||
GEN_INCLUDES := $(shell grep -rh "^\#include .*" $(QEMU_CONTRIB_DIR) |\
|
||||
sed "s/^\#include [^<\"]*[<\"]\([^>\"]*\)[>\"].*/\1/" | sort | uniq)
|
||||
#
|
||||
# Put Qemu headers in 'GEN_INC' dir
|
||||
#
|
||||
GEN_INC := $(shell pwd)/include
|
||||
GEN_INCLUDES := $(addprefix $(GEN_INC)/,$(GEN_INCLUDES))
|
||||
|
||||
all: $(GEN_INCLUDES)
|
||||
|
||||
$(GEN_INCLUDES):
|
||||
$(VERBOSE)mkdir -p $(dir $@)
|
||||
$(VERBOSE)ln -s $(QEMU_EMUL_H) $@
|
||||
|
||||
endif
|
1
repos/libports/ports/qemu-usb.hash
Normal file
1
repos/libports/ports/qemu-usb.hash
Normal file
@ -0,0 +1 @@
|
||||
cf9157628466843e495e600f15a40e64993dd87f
|
13
repos/libports/ports/qemu-usb.port
Normal file
13
repos/libports/ports/qemu-usb.port
Normal file
@ -0,0 +1,13 @@
|
||||
LICENSE := GPLv2
|
||||
VERSION := 2.4.1
|
||||
DOWNLOADS := qemu.archive
|
||||
|
||||
URL(qemu) := http://wiki.qemu-project.org/download/qemu-$(VERSION).tar.bz2
|
||||
SHA(qemu) := 629fb77fc03713b1267c1d51a8df6c0d9c7fd39b
|
||||
DIR(qemu) := src/lib/qemu
|
||||
TAR_OPT(qemu) := --strip-components=1 --files-from $(REP_DIR)/src/lib/qemu-usb/files.list
|
||||
HASH_INPUT += $(REP_DIR)/src/lib/qemu-usb/files.list
|
||||
|
||||
PATCHES := src/lib/qemu-usb/patches/xhci_state.patch \
|
||||
src/lib/qemu-usb/patches/usb_bus_nfree.patch
|
||||
PATCH_OPT:= -p1
|
30
repos/libports/src/lib/qemu-usb/README
Normal file
30
repos/libports/src/lib/qemu-usb/README
Normal file
@ -0,0 +1,30 @@
|
||||
This library makes the xHCI device model of Qemu available on Genode
|
||||
and is used as a back end for such for device models in existing VMMs.
|
||||
|
||||
Usage
|
||||
~~~~~
|
||||
|
||||
The user of this library is required to provide certain back end
|
||||
functionality, namely a Timer_queue to handle timer events and a Pci_device
|
||||
that handles access to the PCI bus (raise interrupts, (un)map DMA memory)
|
||||
within the VMM device model.
|
||||
|
||||
To use this library the user calls 'Qemu::usb_init' and passes
|
||||
pointers to the back end objects. In addition, a Signal_receiver
|
||||
reference has also to be handed over. It will receive all signals
|
||||
required by this library.
|
||||
|
||||
'Qemu::usb_init' returns a pointer to a Controller object. MMIO
|
||||
access must be forwarded to this object when the device model in the VMM
|
||||
wants to access the MMIO regions of the xHCI device.
|
||||
|
||||
Whenever the VMM requests a device reset the 'Qemu::usb_reset'
|
||||
function has to be called. It will remove and free all attached
|
||||
USB devices and will reset the state of the xHCI device model.
|
||||
|
||||
After the xHCI device model has been reset 'Qemu::usb_update_devices'
|
||||
needs to be called to reattach USB devices.
|
||||
|
||||
Timer callbacks that have been registered using the Timer_queue
|
||||
interface have to be executed by calling 'Qemu::usb_timer_callback'
|
||||
when the timer triggers.
|
202
repos/libports/src/lib/qemu-usb/dummies.cc
Normal file
202
repos/libports/src/lib/qemu-usb/dummies.cc
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* \brief Qemu USB controller interface
|
||||
* \author Josef Soentgen
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-12-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/sleep.h>
|
||||
|
||||
/* local includes */
|
||||
#include <extern_c_begin.h>
|
||||
#include <qemu_emul.h>
|
||||
#include <extern_c_end.h>
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
enum {
|
||||
SHOW_TRACE = 0,
|
||||
};
|
||||
|
||||
#define TRACE_AND_STOP \
|
||||
do { \
|
||||
PWRN("%s not implemented called from: %p", __func__, __builtin_return_address(0)); \
|
||||
Genode::sleep_forever(); \
|
||||
} while (0)
|
||||
|
||||
#define TRACE \
|
||||
do { \
|
||||
if (SHOW_TRACE) \
|
||||
PWRN("%s not implemented", __func__); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/****************
|
||||
** hcd-xhci.c **
|
||||
****************/
|
||||
|
||||
void memory_region_del_subregion(MemoryRegion*, MemoryRegion*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
}
|
||||
|
||||
|
||||
void msix_vector_unuse(PCIDevice*, unsigned int)
|
||||
{
|
||||
TRACE;
|
||||
}
|
||||
|
||||
|
||||
int msix_vector_use(PCIDevice*, unsigned int)
|
||||
{
|
||||
TRACE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ObjectClass* object_class_dynamic_cast_assert(ObjectClass*, const char*, const char*, int,
|
||||
const char*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
Object* object_dynamic_cast_assert(Object*, const char*, const char*, int, const char*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool pci_bus_is_express(PCIBus*)
|
||||
{
|
||||
TRACE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void pci_register_bar(PCIDevice*, int, uint8_t, MemoryRegion*)
|
||||
{
|
||||
TRACE;
|
||||
}
|
||||
|
||||
|
||||
int pcie_endpoint_cap_init(PCIDevice*, uint8_t)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/***********
|
||||
** bus.c **
|
||||
***********/
|
||||
|
||||
static Error _error;
|
||||
Error *error_abort = &_error;
|
||||
|
||||
|
||||
ObjectClass* object_get_class(Object*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* object_get_typename(Object*)
|
||||
{
|
||||
TRACE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void qbus_set_bus_hotplug_handler(BusState *state, Error **error)
|
||||
{
|
||||
TRACE;
|
||||
}
|
||||
|
||||
|
||||
gchar* g_strdup_printf(const gchar*, ...)
|
||||
{
|
||||
TRACE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void pstrcpy(char*, int, const char*)
|
||||
{
|
||||
TRACE;
|
||||
}
|
||||
|
||||
|
||||
long int strtol(const char*, char**, int)
|
||||
{
|
||||
TRACE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
DeviceState* qdev_try_create(BusState*, const char*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void monitor_printf(Monitor*, const char*, ...)
|
||||
{
|
||||
TRACE;
|
||||
}
|
||||
|
||||
|
||||
void qdev_simple_device_unplug_cb(HotplugHandler*, DeviceState*, Error**)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
}
|
||||
|
||||
|
||||
char* qdev_get_dev_path(DeviceState*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const char* qdev_fw_name(DeviceState*)
|
||||
{
|
||||
TRACE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
gchar* g_strdup(const gchar*)
|
||||
{
|
||||
TRACE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t strlen(const char*)
|
||||
{
|
||||
TRACE_AND_STOP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
** libc **
|
||||
**********/
|
||||
|
||||
void abort() { TRACE_AND_STOP; }
|
||||
|
||||
} /* extern "C" */
|
6
repos/libports/src/lib/qemu-usb/files.list
Normal file
6
repos/libports/src/lib/qemu-usb/files.list
Normal file
@ -0,0 +1,6 @@
|
||||
qemu-2.4.1/include/hw/usb.h
|
||||
qemu-2.4.1/include/qemu/queue.h
|
||||
qemu-2.4.1/include/qom/object.h
|
||||
qemu-2.4.1/hw/usb/bus.c
|
||||
qemu-2.4.1/hw/usb/core.c
|
||||
qemu-2.4.1/hw/usb/hcd-xhci.c
|
658
repos/libports/src/lib/qemu-usb/host.cc
Normal file
658
repos/libports/src/lib/qemu-usb/host.cc
Normal file
@ -0,0 +1,658 @@
|
||||
/**
|
||||
* \brief USB session back end
|
||||
* \author Josef Soentgen
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-12-18
|
||||
*/
|
||||
|
||||
#include <base/allocator_avl.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/printf.h>
|
||||
#include <os/attached_rom_dataspace.h>
|
||||
#include <os/server.h>
|
||||
#include <os/signal_rpc_dispatcher.h>
|
||||
#include <usb_session/connection.h>
|
||||
#include <usb/usb.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
#include <extern_c_begin.h>
|
||||
#include <qemu_emul.h>
|
||||
#include <hw/usb.h>
|
||||
#include <extern_c_end.h>
|
||||
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
static bool const verbose_devices = false;
|
||||
static bool const verbose_host = false;
|
||||
Lock _lock;
|
||||
|
||||
|
||||
static void update_ep(USBDevice *);
|
||||
static bool claim_interfaces(USBDevice *dev);
|
||||
|
||||
|
||||
struct Completion : Usb::Completion
|
||||
{
|
||||
USBPacket *p = nullptr;
|
||||
USBDevice *dev = nullptr;
|
||||
uint8_t *data = nullptr;
|
||||
enum State { VALID, FREE, CANCELED };
|
||||
State state = FREE;
|
||||
|
||||
void complete(Usb::Packet_descriptor &p) override { }
|
||||
|
||||
void complete(Usb::Packet_descriptor &packet, char *content)
|
||||
{
|
||||
if (state != VALID)
|
||||
return;
|
||||
|
||||
int actual_size = 0;
|
||||
|
||||
switch (packet.type) {
|
||||
case Usb::Packet_descriptor::CTRL:
|
||||
actual_size = packet.control.actual_size;
|
||||
break;
|
||||
case Usb::Packet_descriptor::BULK:
|
||||
case Usb::Packet_descriptor::IRQ:
|
||||
actual_size = packet.transfer.actual_size;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (actual_size < 0) actual_size = 0;
|
||||
|
||||
if (verbose_host)
|
||||
PDBG("packet.type: %u actual_size: 0x%x", packet.type, actual_size);
|
||||
|
||||
p->actual_length = 0;
|
||||
|
||||
if (p->pid == USB_TOKEN_IN && actual_size > 0) {
|
||||
if (data) Genode::memcpy(data, content, actual_size);
|
||||
else usb_packet_copy(p, content, actual_size);
|
||||
}
|
||||
|
||||
p->actual_length = actual_size;
|
||||
|
||||
if (packet.succeded)
|
||||
p->status = USB_RET_SUCCESS;
|
||||
else
|
||||
p->status = USB_RET_IOERROR;
|
||||
|
||||
switch (packet.type) {
|
||||
case Usb::Packet_descriptor::CONFIG:
|
||||
if (!claim_interfaces(dev))
|
||||
p->status = USB_RET_IOERROR;
|
||||
case Usb::Packet_descriptor::ALT_SETTING:
|
||||
update_ep(dev);
|
||||
case Usb::Packet_descriptor::CTRL:
|
||||
usb_generic_async_ctrl_complete(dev, p);
|
||||
break;
|
||||
case Usb::Packet_descriptor::BULK:
|
||||
case Usb::Packet_descriptor::IRQ:
|
||||
usb_packet_complete(dev, p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Usb_host_device : List<Usb_host_device>::Element
|
||||
{
|
||||
struct Could_not_create_device : Genode::Exception { };
|
||||
|
||||
bool deleted = false;
|
||||
char const *label = nullptr;
|
||||
unsigned bus = 0;
|
||||
unsigned dev = 0;
|
||||
|
||||
USBHostDevice *qemu_dev;
|
||||
Completion completion[Usb::Session::TX_QUEUE_SIZE];
|
||||
|
||||
Signal_receiver &sig_rec;
|
||||
Signal_dispatcher<Usb_host_device> state_dispatcher { sig_rec, *this, &Usb_host_device::state_change };
|
||||
|
||||
Allocator_avl alloc { env()->heap() };
|
||||
Usb::Connection usb_raw { &alloc, label, 1024*1024, state_dispatcher };
|
||||
|
||||
Signal_dispatcher<Usb_host_device> ack_avail_dispatcher { sig_rec, *this, &Usb_host_device::ack_avail };
|
||||
|
||||
void _release_interfaces()
|
||||
{
|
||||
Usb::Config_descriptor cdescr;
|
||||
Usb::Device_descriptor ddescr;
|
||||
|
||||
usb_raw.config_descriptor(&ddescr, &cdescr);
|
||||
|
||||
for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
|
||||
usb_raw.release_interface(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool _claim_interfaces()
|
||||
{
|
||||
Usb::Config_descriptor cdescr;
|
||||
Usb::Device_descriptor ddescr;
|
||||
|
||||
usb_raw.config_descriptor(&ddescr, &cdescr);
|
||||
|
||||
bool result = true;
|
||||
for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
|
||||
try {
|
||||
usb_raw.claim_interface(i);
|
||||
} catch (Usb::Session::Interface_already_claimed) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) PERR("Device already claimed");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Usb_host_device(Signal_receiver &sig_rec, char const *label,
|
||||
unsigned bus, unsigned dev)
|
||||
: label(label), bus(bus), dev(dev), sig_rec(sig_rec)
|
||||
{
|
||||
usb_raw.tx_channel()->sigh_ack_avail(ack_avail_dispatcher);
|
||||
|
||||
if (!_claim_interfaces())
|
||||
throw Could_not_create_device();
|
||||
|
||||
qemu_dev = create_usbdevice(this);
|
||||
|
||||
if (!qemu_dev)
|
||||
throw Could_not_create_device();
|
||||
}
|
||||
|
||||
static int to_qemu_speed(unsigned speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case Usb::Device::SPEED_LOW: return USB_SPEED_LOW;
|
||||
case Usb::Device::SPEED_FULL: return USB_SPEED_FULL;
|
||||
case Usb::Device::SPEED_HIGH: return USB_SPEED_HIGH;
|
||||
case Usb::Device::SPEED_SUPER: return USB_SPEED_SUPER;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ack_avail(unsigned)
|
||||
{
|
||||
Lock::Guard g(_lock);
|
||||
|
||||
/* we are already dead, do nothing */
|
||||
if (deleted == true) return;
|
||||
|
||||
while (usb_raw.source()->ack_avail()) {
|
||||
Usb::Packet_descriptor packet = usb_raw.source()->get_acked_packet();
|
||||
|
||||
char *packet_content = usb_raw.source()->packet_content(packet);
|
||||
dynamic_cast<Completion *>(packet.completion)->complete(packet, packet_content);
|
||||
free_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void _destroy()
|
||||
{
|
||||
/* mark delete before removing */
|
||||
deleted = true;
|
||||
|
||||
/* remove from USB bus */
|
||||
remove_usbdevice(qemu_dev);
|
||||
qemu_dev = nullptr;
|
||||
}
|
||||
|
||||
void state_change(unsigned)
|
||||
{
|
||||
Lock::Guard g(_lock);
|
||||
if (usb_raw.plugged())
|
||||
return;
|
||||
|
||||
_destroy();
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
Lock::Guard g(_lock);
|
||||
|
||||
_release_interfaces();
|
||||
|
||||
_destroy();
|
||||
}
|
||||
|
||||
Usb::Packet_descriptor alloc_packet(int length)
|
||||
{
|
||||
|
||||
if (!usb_raw.source()->ready_to_submit())
|
||||
throw -1;
|
||||
|
||||
Usb::Packet_descriptor packet = usb_raw.source()->alloc_packet(length);
|
||||
packet.completion = alloc_completion();
|
||||
return packet;
|
||||
}
|
||||
|
||||
void free_packet(Usb::Packet_descriptor &packet)
|
||||
{
|
||||
dynamic_cast<Completion *>(packet.completion)->state = Completion::FREE;
|
||||
usb_raw.source()->release_packet(packet);
|
||||
}
|
||||
|
||||
Completion *alloc_completion()
|
||||
{
|
||||
for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++)
|
||||
if (completion[i].state == Completion::FREE) {
|
||||
completion[i]. state = Completion::VALID;
|
||||
return &completion[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Completion *find_completion(USBPacket *p)
|
||||
{
|
||||
for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++)
|
||||
if (completion[i].p == p)
|
||||
return &completion[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void submit(Usb::Packet_descriptor p) {
|
||||
usb_raw.source()->submit_packet(p); }
|
||||
|
||||
bool claim_interfaces() { return _claim_interfaces(); }
|
||||
|
||||
void set_configuration(uint8_t value, USBPacket *p)
|
||||
{
|
||||
_release_interfaces();
|
||||
|
||||
Usb::Packet_descriptor packet = alloc_packet(0);
|
||||
packet.type = Usb::Packet_descriptor::CONFIG;
|
||||
packet.number = value;
|
||||
|
||||
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
||||
c->p = p;
|
||||
c->dev = cast_USBDevice(qemu_dev);
|
||||
submit(packet);
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
void set_interface(int index, uint8_t value, USBPacket *p)
|
||||
{
|
||||
Usb::Packet_descriptor packet = alloc_packet(0);
|
||||
packet.type = Usb::Packet_descriptor::ALT_SETTING;
|
||||
packet.interface.number = index;
|
||||
packet.interface.alt_setting = value;
|
||||
|
||||
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
||||
c->p = p;
|
||||
c->dev = cast_USBDevice(qemu_dev);
|
||||
submit(packet);
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
void update_ep(USBDevice *udev)
|
||||
{
|
||||
usb_ep_reset(udev);
|
||||
|
||||
/* retrieve device speed */
|
||||
Usb::Config_descriptor cdescr;
|
||||
Usb::Device_descriptor ddescr;
|
||||
|
||||
usb_raw.config_descriptor(&ddescr, &cdescr);
|
||||
|
||||
for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
|
||||
udev->altsetting[i] = usb_raw.alt_settings(i);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
|
||||
for (int j = 0; j < udev->altsetting[i]; j++) {
|
||||
Usb::Interface_descriptor iface;
|
||||
usb_raw.interface_descriptor(i, j, &iface);
|
||||
for (unsigned k = 0; k < iface.num_endpoints; k++) {
|
||||
Usb::Endpoint_descriptor endp;
|
||||
usb_raw.endpoint_descriptor(i, j, k, &endp);
|
||||
|
||||
int const pid = (endp.address & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
||||
int const ep = (endp.address & 0xf);
|
||||
uint8_t const type = (endp.attributes & 0x3);
|
||||
|
||||
usb_ep_set_max_packet_size(udev, pid, ep, endp.max_packet_size);
|
||||
usb_ep_set_type(udev, pid, ep, type);
|
||||
usb_ep_set_ifnum(udev, pid, ep, i);
|
||||
usb_ep_set_halted(udev, pid, ep, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/********************
|
||||
** Qemu interface **
|
||||
********************/
|
||||
|
||||
#define TRACE_AND_STOP do { PDBG("not implemented"); } while(false)
|
||||
|
||||
#define USB_HOST_DEVICE(obj) \
|
||||
OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE)
|
||||
|
||||
|
||||
static void update_ep(USBDevice *udev)
|
||||
{
|
||||
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
||||
Usb_host_device *dev = (Usb_host_device *)d->data;
|
||||
|
||||
dev->update_ep(udev);
|
||||
}
|
||||
|
||||
|
||||
static bool claim_interfaces(USBDevice *udev)
|
||||
{
|
||||
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
||||
Usb_host_device *dev = (Usb_host_device *)d->data;
|
||||
|
||||
return dev->claim_interfaces();
|
||||
}
|
||||
|
||||
|
||||
static void usb_host_realize(USBDevice *udev, Error **errp)
|
||||
{
|
||||
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
||||
Usb_host_device *dev = (Usb_host_device *)d->data;
|
||||
|
||||
/* retrieve device speed */
|
||||
Usb::Config_descriptor cdescr;
|
||||
Usb::Device_descriptor ddescr;
|
||||
|
||||
dev->usb_raw.config_descriptor(&ddescr, &cdescr);
|
||||
|
||||
if (verbose_host)
|
||||
PDBG("set udev->speed to %d", Usb_host_device::to_qemu_speed(ddescr.speed));
|
||||
|
||||
udev->speed = Usb_host_device::to_qemu_speed(ddescr.speed);
|
||||
udev->speedmask = (1 << udev->speed);
|
||||
|
||||
udev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
|
||||
|
||||
dev->update_ep(udev);
|
||||
}
|
||||
|
||||
|
||||
static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
||||
Usb_host_device *dev = (Usb_host_device *)d->data;
|
||||
Completion *c = dev->find_completion(p);
|
||||
c->state = Completion::CANCELED;
|
||||
}
|
||||
|
||||
|
||||
static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
||||
Usb_host_device *dev = (Usb_host_device *)d->data;
|
||||
|
||||
size_t size = 0;
|
||||
unsigned timeout = 0;
|
||||
Usb::Packet_descriptor::Type type = Usb::Packet_descriptor::BULK;
|
||||
|
||||
switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
type = Usb::Packet_descriptor::BULK;
|
||||
size = usb_packet_size(p);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
type = Usb::Packet_descriptor::IRQ;
|
||||
size = p->iov.size;
|
||||
|
||||
/* values match usb_drv */
|
||||
switch (udev->speed) {
|
||||
case USB_SPEED_SUPER:
|
||||
timeout = 1<<15;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
timeout = 1<<13;
|
||||
break;
|
||||
case USB_SPEED_FULL:
|
||||
case USB_SPEED_LOW:
|
||||
timeout = 1<<7;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PERR("not supported data request");
|
||||
break;
|
||||
}
|
||||
|
||||
bool const in = p->pid == USB_TOKEN_IN;
|
||||
|
||||
Usb::Packet_descriptor packet = dev->alloc_packet(size);
|
||||
packet.type = type;
|
||||
packet.transfer.ep = p->ep->nr | (in ? USB_DIR_IN : 0);
|
||||
packet.transfer.timeout = timeout;
|
||||
|
||||
if (!in) {
|
||||
char * const content = dev->usb_raw.source()->packet_content(packet);
|
||||
usb_packet_copy(p, content, size);
|
||||
}
|
||||
|
||||
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
||||
c->p = p;
|
||||
c->dev = udev;
|
||||
c->data = nullptr;
|
||||
|
||||
dev->submit(packet);
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
|
||||
static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
|
||||
int request, int value, int index,
|
||||
int length, uint8_t *data)
|
||||
{
|
||||
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
||||
Usb_host_device *dev = (Usb_host_device *)d->data;
|
||||
|
||||
if (verbose_host)
|
||||
PDBG("r: %x v: %x i: %x length: %d", request, value, index, length);
|
||||
|
||||
switch (request) {
|
||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||
udev->addr = value;
|
||||
return;
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||
dev->set_configuration(value & 0xff, p);
|
||||
return;
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
dev->set_interface(index, value, p);
|
||||
return;
|
||||
}
|
||||
|
||||
if (udev->speed == USB_SPEED_SUPER &&
|
||||
!(udev->port->speedmask & USB_SPEED_MASK_SUPER) &&
|
||||
request == 0x8006 && value == 0x100 && index == 0) {
|
||||
PERR("r->usb3ep0quirk = true");
|
||||
}
|
||||
|
||||
Usb::Packet_descriptor packet;
|
||||
try {
|
||||
packet = dev->alloc_packet(length);
|
||||
} catch (...) { PERR("Packet allocation failed"); return; }
|
||||
|
||||
packet.type = Usb::Packet_descriptor::CTRL;
|
||||
packet.control.request_type = request >> 8;
|
||||
packet.control.request = request & 0xff;
|
||||
packet.control.index = index;
|
||||
packet.control.value = value;
|
||||
|
||||
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
||||
c->p = p;
|
||||
c->dev = udev;
|
||||
c->data = data;
|
||||
|
||||
if (!(packet.control.request_type & USB_DIR_IN) && length) {
|
||||
char *packet_content = dev->usb_raw.source()->packet_content(packet);
|
||||
Genode::memcpy(packet_content, data, length);
|
||||
}
|
||||
|
||||
dev->submit(packet);
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
|
||||
static Property usb_host_dev_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
static void usb_host_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||
|
||||
uc->realize = usb_host_realize;
|
||||
uc->product_desc = "USB Host Device";
|
||||
uc->cancel_packet = usb_host_cancel_packet;
|
||||
uc->handle_data = usb_host_handle_data;
|
||||
uc->handle_control = usb_host_handle_control;
|
||||
dc->props = usb_host_dev_properties;
|
||||
}
|
||||
|
||||
|
||||
static TypeInfo usb_host_dev_info;
|
||||
|
||||
|
||||
static void usb_host_register_types(void)
|
||||
{
|
||||
usb_host_dev_info.name = TYPE_USB_HOST_DEVICE;
|
||||
usb_host_dev_info.parent = TYPE_USB_DEVICE;
|
||||
usb_host_dev_info.instance_size = sizeof(USBHostDevice);
|
||||
usb_host_dev_info.class_init = usb_host_class_initfn;
|
||||
|
||||
type_register_static(&usb_host_dev_info);
|
||||
}
|
||||
|
||||
|
||||
struct Usb_devices : List<Usb_host_device>
|
||||
{
|
||||
Signal_receiver &_sig_rec;
|
||||
Signal_dispatcher<Usb_devices> _device_dispatcher { _sig_rec, *this, &Usb_devices::_devices_update };
|
||||
Attached_rom_dataspace _devices_rom { "usb_devices" };
|
||||
|
||||
void _garbage_collect()
|
||||
{
|
||||
for (Usb_host_device *d = first(); d; d = d->next()) {
|
||||
if (d->deleted == false)
|
||||
continue;
|
||||
|
||||
remove(d);
|
||||
Genode::destroy(env()->heap(), d);
|
||||
}
|
||||
}
|
||||
|
||||
Usb_host_device *_find_usb_device(unsigned bus, unsigned dev)
|
||||
{
|
||||
for (Usb_host_device *d = first(); d; d = d->next())
|
||||
if (d->bus == bus && d->dev == dev)
|
||||
return d;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void _devices_update(unsigned)
|
||||
{
|
||||
Lock::Guard g(_lock);
|
||||
|
||||
_garbage_collect();
|
||||
|
||||
_devices_rom.update();
|
||||
if (!_devices_rom.is_valid())
|
||||
return;
|
||||
|
||||
if (verbose_devices)
|
||||
PINF("%s", _devices_rom.local_addr<char>());
|
||||
|
||||
Xml_node devices_node(_devices_rom.local_addr<char>(), _devices_rom.size());
|
||||
devices_node.for_each_sub_node("device", [&] (Xml_node const &node) {
|
||||
|
||||
unsigned product = node.attribute_value<unsigned>("product_id", 0);
|
||||
unsigned vendor = node.attribute_value<unsigned>("vendor_id", 0);
|
||||
unsigned bus = node.attribute_value<unsigned>("bus", 0);
|
||||
unsigned dev = node.attribute_value<unsigned>("dev", 0);
|
||||
|
||||
Genode::String<128> label;
|
||||
try {
|
||||
node.attribute("label").value(&label);
|
||||
} catch (Genode::Xml_attribute::Nonexistent_attribute) {
|
||||
PERR("No label found for device %03x:%03x", bus, dev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ignore if already created */
|
||||
if (_find_usb_device(bus, dev)) return;
|
||||
|
||||
try {
|
||||
Usb_host_device *new_device = new (env()->heap())
|
||||
Usb_host_device(_sig_rec, label.string(), bus, dev);
|
||||
|
||||
insert(new_device);
|
||||
|
||||
PINF("Attach USB device %03x:%03x (%x:%x)",
|
||||
bus, dev, vendor, product);
|
||||
|
||||
} catch (...) {
|
||||
PERR("Could not attach USB device %03x:%03x (%x:%x)",
|
||||
bus, dev, vendor, product);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Usb_devices(Signal_receiver *sig_rec)
|
||||
: _sig_rec(*sig_rec)
|
||||
{
|
||||
_devices_rom.sigh(_device_dispatcher);
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
for (Usb_host_device *d = first(); d; d = d->next())
|
||||
d->destroy();
|
||||
|
||||
_garbage_collect();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Usb_devices *_devices;
|
||||
|
||||
|
||||
extern "C" void usb_host_destroy()
|
||||
{
|
||||
if (_devices == nullptr) return;
|
||||
|
||||
_devices->destroy();
|
||||
}
|
||||
|
||||
|
||||
extern "C" void usb_host_update_devices()
|
||||
{
|
||||
if (_devices == nullptr) return;
|
||||
|
||||
_devices->_devices_update(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Do not use type_init macro because of name mangling
|
||||
*/
|
||||
extern "C" void _type_init_usb_host_register_types(Genode::Signal_receiver *sig_rec)
|
||||
{
|
||||
usb_host_register_types();
|
||||
|
||||
static Usb_devices devices(sig_rec);
|
||||
_devices = &devices;
|
||||
}
|
27
repos/libports/src/lib/qemu-usb/include/extern_c_begin.h
Normal file
27
repos/libports/src/lib/qemu-usb/include/extern_c_begin.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* \brief Include before including Linux headers in C++
|
||||
* \author Christian Helmuth
|
||||
* \date 2014-08-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#define extern_c_begin
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* some warnings should only be switched of for Linux headers */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-arith"
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
|
||||
/* deal with C++ keywords used for identifiers etc. */
|
||||
#define private private_
|
||||
#define class class_
|
||||
#define new new_
|
||||
#define typename typename_
|
21
repos/libports/src/lib/qemu-usb/include/extern_c_end.h
Normal file
21
repos/libports/src/lib/qemu-usb/include/extern_c_end.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* \brief Include after including Linux headers in C++
|
||||
* \author Christian Helmuth
|
||||
* \date 2014-08-21
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#undef typename
|
||||
#undef new
|
||||
#undef class
|
||||
#undef private
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
} /* extern "C" */
|
728
repos/libports/src/lib/qemu-usb/include/qemu_emul.h
Normal file
728
repos/libports/src/lib/qemu-usb/include/qemu_emul.h
Normal file
@ -0,0 +1,728 @@
|
||||
/*
|
||||
* \brief Qemu emulation environment
|
||||
* \author Josef Soentgen
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-12-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__QEMU_EMUL_H_
|
||||
#define _INCLUDE__QEMU_EMUL_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <base/fixed_stdint.h>
|
||||
|
||||
void q_printf(char const *, ...) __attribute__((format(printf, 1, 2)));
|
||||
void q_vprintf(char const *, va_list);
|
||||
|
||||
typedef genode_uint8_t uint8_t;
|
||||
typedef genode_uint16_t uint16_t;
|
||||
typedef genode_uint32_t uint32_t;
|
||||
typedef genode_uint64_t uint64_t;
|
||||
|
||||
typedef genode_int32_t int32_t;
|
||||
typedef genode_int64_t int64_t;
|
||||
typedef signed long ssize_t;
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef _Bool bool;
|
||||
enum { false = 0, true = 1 };
|
||||
#endif
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef unsigned long dma_addr_t;
|
||||
typedef uint64_t hwaddr;
|
||||
|
||||
typedef struct uint16List
|
||||
{
|
||||
union {
|
||||
uint16_t value;
|
||||
uint64_t padding;
|
||||
};
|
||||
struct uint16List *next;
|
||||
} uint16List;
|
||||
|
||||
|
||||
/**********
|
||||
** libc **
|
||||
**********/
|
||||
|
||||
enum {
|
||||
EINVAL = 22,
|
||||
};
|
||||
|
||||
void *malloc(size_t size);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
void free(void *p);
|
||||
|
||||
void abort();
|
||||
|
||||
#define fprintf(fd, fmt, ...) q_printf(fmt, ##__VA_ARGS__)
|
||||
int snprintf(char *buf, size_t size, const char *fmt, ...);
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
size_t strlen(char const *s);
|
||||
long int strtol(const char *nptr, char **endptr, int base);
|
||||
char *strchr(const char *s, int c);
|
||||
|
||||
|
||||
/*************
|
||||
** defines **
|
||||
*************/
|
||||
|
||||
#define NULL (void *)0
|
||||
#define QEMU_SENTINEL
|
||||
|
||||
#define le32_to_cpu(x) (x)
|
||||
#define cpu_to_le32(x) (x)
|
||||
#define cpu_to_le64(x) (x)
|
||||
|
||||
#define le32_to_cpus(x)
|
||||
#define le64_to_cpus(x)
|
||||
|
||||
/**
|
||||
* Forward declarations
|
||||
*/
|
||||
|
||||
typedef struct Monitor Monitor;
|
||||
typedef struct QDict QDict;
|
||||
|
||||
typedef struct CPUReadMemoryFunc CPUReadMemoryFunc;
|
||||
typedef struct CPUWriteMemoryFunc CPUWriteMemoryFunc;
|
||||
|
||||
typedef struct MemoryRegion { unsigned dummy; } MemoryRegion;
|
||||
|
||||
struct tm;
|
||||
|
||||
|
||||
/******************
|
||||
** qapi/error.h **
|
||||
******************/
|
||||
|
||||
typedef struct Error { char string[256]; } Error;
|
||||
|
||||
void error_setg(Error **errp, const char *fmt, ...);
|
||||
|
||||
const char *error_get_pretty(Error *err);
|
||||
void error_report(const char *fmt, ...);
|
||||
void error_free(Error *err);
|
||||
void error_propagate(Error **dst_errp, Error *local_err);
|
||||
|
||||
extern Error *error_abort;
|
||||
|
||||
|
||||
/*******************
|
||||
** qemu/bitops.h **
|
||||
*******************/
|
||||
|
||||
#define BITS_PER_BYTE 8
|
||||
#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE)
|
||||
|
||||
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
|
||||
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
|
||||
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
|
||||
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
|
||||
|
||||
#define DECLARE_BITMAP(name,bits) \
|
||||
unsigned long name[BITS_TO_LONGS(bits)]
|
||||
|
||||
static inline void set_bit(long nr, unsigned long *addr)
|
||||
{
|
||||
unsigned long mask = BIT_MASK(nr);
|
||||
unsigned long *p = addr + BIT_WORD(nr);
|
||||
|
||||
*p |= mask;
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
** qemu-common.h **
|
||||
*******************/
|
||||
|
||||
struct iovec
|
||||
{
|
||||
void *iov_base;
|
||||
size_t iov_len;
|
||||
};
|
||||
|
||||
typedef struct QEMUIOVector
|
||||
{
|
||||
struct iovec *iov;
|
||||
int niov;
|
||||
size_t size;
|
||||
int alloc_hint;
|
||||
} QEMUIOVector;
|
||||
|
||||
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
|
||||
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
|
||||
void qemu_iovec_reset(QEMUIOVector *qiov);
|
||||
void qemu_iovec_destroy(QEMUIOVector *qiov);
|
||||
|
||||
void pstrcpy(char *buf, int buf_size, const char *str);
|
||||
|
||||
|
||||
/****************
|
||||
** qemu/iov.h **
|
||||
****************/
|
||||
|
||||
size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt,
|
||||
size_t offset, const void *buf, size_t bytes);
|
||||
size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
size_t offset, void *buf, size_t bytes);
|
||||
size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
size_t offset, int fillc, size_t bytes);
|
||||
|
||||
/******************
|
||||
** qom/object.h **
|
||||
******************/
|
||||
|
||||
typedef struct Object { unsigned dummy; } Object;
|
||||
typedef struct ObjectClass { unsigned dummy; } ObjectClass;
|
||||
|
||||
struct DeviceState;
|
||||
struct PCIDevice;
|
||||
struct XHCIState;
|
||||
|
||||
struct PCIDevice *cast_PCIDevice(void *);
|
||||
struct XHCIState *cast_XHCIState(void *);
|
||||
struct DeviceState *cast_DeviceState(void *);
|
||||
struct BusState *cast_BusState(void *);
|
||||
struct Object *cast_object(void *);
|
||||
struct USBDevice *cast_USBDevice(void *);
|
||||
struct USBHostDevice *cast_USBHostDevice(void *);
|
||||
|
||||
struct PCIDeviceClass *cast_PCIDeviceClass(void *);
|
||||
struct DeviceClass *cast_DeviceClass(void *);
|
||||
struct BusClass *cast_BusClass(void *);
|
||||
struct HotplugHandlerClass *cast_HotplugHandlerClass(void *);
|
||||
|
||||
struct USBDeviceClass *cast_USBDeviceClass(void *);
|
||||
|
||||
#define OBJECT_CHECK(type, obj, str) \
|
||||
cast_##type((void *)obj)
|
||||
|
||||
#define OBJECT_CLASS_CHECK(type, klass, str) \
|
||||
OBJECT_CHECK(type, (klass), str)
|
||||
|
||||
#define OBJECT(obj) \
|
||||
cast_object(obj)
|
||||
|
||||
#define OBJECT_GET_CLASS(klass, obj, str) \
|
||||
OBJECT_CHECK(klass, obj, str)
|
||||
|
||||
|
||||
typedef struct InterfaceInfo {
|
||||
const char *type;
|
||||
} InterfaceInfo;
|
||||
|
||||
|
||||
typedef struct TypeInfo
|
||||
{
|
||||
char const *name;
|
||||
char const *parent;
|
||||
size_t instance_size;
|
||||
|
||||
bool abstract;
|
||||
size_t class_size;
|
||||
void (*class_init)(ObjectClass *klass, void *data);
|
||||
InterfaceInfo *interfaces;
|
||||
} TypeInfo;
|
||||
|
||||
struct Type_impl { unsigned dummy; };
|
||||
typedef struct Type_impl *Type;
|
||||
|
||||
Type type_register_static(const TypeInfo *info);
|
||||
|
||||
#define object_property_set_bool(...)
|
||||
#define object_unparent(...)
|
||||
|
||||
const char *object_get_typename(Object *obj);
|
||||
|
||||
/********************
|
||||
** glib emulation **
|
||||
********************/
|
||||
|
||||
#define g_new0(type, count)({ \
|
||||
typeof(type) *t = (typeof(type)*)malloc(sizeof(type) * count); \
|
||||
memset(t, 0, sizeof(type) * count); \
|
||||
t; \
|
||||
})
|
||||
|
||||
#define g_free(p) free(p)
|
||||
#define g_malloc malloc
|
||||
|
||||
#define g_malloc0(size) ({ \
|
||||
void *t = malloc((size)); \
|
||||
memset(t, 0, (size)); \
|
||||
t; \
|
||||
})
|
||||
|
||||
typedef void* gpointer;
|
||||
typedef struct GSList GSList;
|
||||
|
||||
struct GSList
|
||||
{
|
||||
gpointer data;
|
||||
GSList *next;
|
||||
};
|
||||
|
||||
GSList *g_slist_append(GSList *list, gpointer data);
|
||||
|
||||
typedef char gchar;
|
||||
gchar *g_strdup(gchar const *str);
|
||||
gchar *g_strdup_printf(gchar const *fmt, ...);
|
||||
|
||||
|
||||
/********************
|
||||
** hw/qdev-core.h **
|
||||
********************/
|
||||
|
||||
typedef enum DeviceCategory {
|
||||
DEVICE_CATEGORY_USB = 1,
|
||||
DEVICE_CATEGORY_MAX
|
||||
} DeviceCategory;
|
||||
|
||||
typedef struct BusState BusState;
|
||||
|
||||
typedef struct DeviceState
|
||||
{
|
||||
BusState *parent_bus;
|
||||
} DeviceState;
|
||||
|
||||
struct VMStateDescription;
|
||||
struct Property;
|
||||
|
||||
typedef void (*DeviceRealize)(DeviceState *dev, Error **errp);
|
||||
typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp);
|
||||
|
||||
typedef struct DeviceClass
|
||||
{
|
||||
DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX);
|
||||
struct Property *props;
|
||||
void (*reset)(DeviceState *dev);
|
||||
DeviceRealize realize;
|
||||
DeviceUnrealize unrealize;
|
||||
|
||||
const struct VMStateDescription *vmsd;
|
||||
const char *bus_type;
|
||||
} DeviceClass;
|
||||
|
||||
#define DEVICE_CLASS(klass) OBJECT_CHECK(DeviceClass, (klass), "device")
|
||||
|
||||
|
||||
typedef struct BusState
|
||||
{
|
||||
DeviceState *parent;
|
||||
char *name;
|
||||
} BusState;
|
||||
|
||||
typedef struct BusClass
|
||||
{
|
||||
void (*print_dev)(Monitor *mon, DeviceState *dev, int indent);
|
||||
char *(*get_dev_path)(DeviceState *dev);
|
||||
char *(*get_fw_dev_path)(DeviceState *dev);
|
||||
|
||||
} BusClass;
|
||||
|
||||
#define TYPE_BUS "bus"
|
||||
#define BUS(obj) OBJECT_CHECK(BusState, (obj), TYPE_BUS)
|
||||
#define BUS_CLASS(klass) OBJECT_CLASS_CHECK(BusClass, (klass), TYPE_BUS)
|
||||
|
||||
#define TYPE_DEVICE "device"
|
||||
#define DEVICE(obj) OBJECT_CHECK(DeviceState, (obj), TYPE_DEVICE)
|
||||
|
||||
enum Prop_type
|
||||
{
|
||||
BIT, UINT32, END
|
||||
};
|
||||
|
||||
typedef struct Property
|
||||
{
|
||||
enum Prop_type type;
|
||||
unsigned offset;
|
||||
unsigned long value;
|
||||
} Property;
|
||||
|
||||
|
||||
#define DEFINE_PROP_BIT(_name, _state, _field, _bit, _bool) \
|
||||
{ .type = BIT, .offset = offsetof(_state, _field), .value = (_bool << _bit) }
|
||||
#define DEFINE_PROP_UINT32(_name, _state, _field, _value) \
|
||||
{ .type = UINT32, .offset = offsetof(_state, _field), .value = _value }
|
||||
#define DEFINE_PROP_END_OF_LIST() { .type = END }
|
||||
#define DEFINE_PROP_STRING(...) {}
|
||||
|
||||
|
||||
/* forward */
|
||||
typedef struct DeviceState DeviceState;
|
||||
typedef struct HotplugHandler HotplugHandler;
|
||||
|
||||
void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp);
|
||||
void qbus_create_inplace(void *bus, size_t size, const char *typename,
|
||||
DeviceState *parent, const char *name);
|
||||
void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp);
|
||||
DeviceState *qdev_create(BusState *bus, const char *name);
|
||||
DeviceState *qdev_try_create(BusState *bus, const char *name);
|
||||
char *qdev_get_dev_path(DeviceState *dev);
|
||||
const char *qdev_fw_name(DeviceState *dev);
|
||||
|
||||
/******************
|
||||
** hw/hotplug.h **
|
||||
******************/
|
||||
|
||||
typedef struct HotplugHandler { unsigned dummy; } HotplugHandler;
|
||||
|
||||
typedef void (*hotplug_fn)(HotplugHandler *plug_handler,
|
||||
DeviceState *plugged_dev, Error **errp);
|
||||
|
||||
typedef struct HotplugHandlerClass
|
||||
{
|
||||
hotplug_fn unplug;
|
||||
} HotplugHandlerClass;
|
||||
|
||||
#define TYPE_HOTPLUG_HANDLER "hotplug-handler"
|
||||
#define HOTPLUG_HANDLER_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(HotplugHandlerClass, (klass), TYPE_HOTPLUG_HANDLER)
|
||||
|
||||
|
||||
/****************
|
||||
** hw/osdep.h **
|
||||
****************/
|
||||
|
||||
#define offsetof(type, member) ((size_t) &((type *)0)->member)
|
||||
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof(((type *) 0)->member) *__mptr = (ptr); \
|
||||
(type *) ((char *) __mptr - offsetof(type, member));})
|
||||
|
||||
#define DO_UPCAST(type, field, dev) cast_DeviceStateToUSBBus()
|
||||
|
||||
struct USBBus *cast_DeviceStateToUSBBus(void);
|
||||
|
||||
#define MAX(a, b) ((a) < (b) ? (b) : (a))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
|
||||
/******************
|
||||
** qemu/timer.h **
|
||||
******************/
|
||||
|
||||
enum QEMUClockType
|
||||
{
|
||||
QEMU_CLOCK_VIRTUAL = 1,
|
||||
};
|
||||
|
||||
typedef enum QEMUClockType QEMUClockType;
|
||||
typedef struct QEMUTimer { unsigned dummy; } QEMUTimer;
|
||||
typedef void QEMUTimerCB(void *opaque);
|
||||
|
||||
QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, void *opaque);
|
||||
void timer_mod(QEMUTimer *ts, int64_t expire_timer);
|
||||
void timer_del(QEMUTimer *ts);
|
||||
void timer_free(QEMUTimer *ts);
|
||||
|
||||
int64_t qemu_clock_get_ns(QEMUClockType type);
|
||||
|
||||
|
||||
/***********************
|
||||
** exec/cpu-common.h **
|
||||
***********************/
|
||||
|
||||
enum device_endian { DEVICE_LITTLE_ENDIAN = 2 };
|
||||
|
||||
|
||||
/*******************
|
||||
** exec/memory.h **
|
||||
*******************/
|
||||
|
||||
typedef struct AddressSpace { unsigned dummy; } AddressSpace;
|
||||
|
||||
typedef struct MemoryRegionOps {
|
||||
uint64_t (*read)(void *opaque, hwaddr addr, unsigned size);
|
||||
void (*write)(void *opaque, hwaddr addr, uint64_t data, unsigned size);
|
||||
|
||||
enum device_endian endianness;
|
||||
|
||||
struct {
|
||||
unsigned min_access_size;
|
||||
unsigned max_access_size;
|
||||
} valid;
|
||||
|
||||
struct {
|
||||
unsigned min_access_size;
|
||||
unsigned max_access_size;
|
||||
} impl;
|
||||
} MemoryRegionOps;
|
||||
|
||||
void memory_region_init(MemoryRegion *mr,
|
||||
struct Object *owner,
|
||||
const char *name,
|
||||
uint64_t size);
|
||||
|
||||
void memory_region_init_io(MemoryRegion *mr,
|
||||
struct Object *owner,
|
||||
const MemoryRegionOps *ops,
|
||||
void *opaque,
|
||||
const char *name,
|
||||
uint64_t size);
|
||||
|
||||
void memory_region_add_subregion(MemoryRegion *mr,
|
||||
hwaddr offset,
|
||||
MemoryRegion *subregion);
|
||||
|
||||
void memory_region_del_subregion(MemoryRegion *mr,
|
||||
MemoryRegion *subregion);
|
||||
|
||||
/******************
|
||||
** sysemu/dma.h **
|
||||
******************/
|
||||
|
||||
typedef struct QEMUIOVector QEMUSGList;
|
||||
|
||||
void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len);
|
||||
void qemu_sglist_destroy(QEMUSGList *qsg);
|
||||
|
||||
int dma_memory_read(AddressSpace *as, dma_addr_t addr, void *buf, dma_addr_t len);
|
||||
|
||||
static inline uint64_t ldq_le_dma(AddressSpace *as, dma_addr_t addr)
|
||||
{
|
||||
uint64_t val;
|
||||
dma_memory_read(as, addr, &val, 8);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline uint64_t ldq_le_pci_dma(void *dev, dma_addr_t addr)
|
||||
{
|
||||
return ldq_le_dma(0, addr);
|
||||
}
|
||||
|
||||
|
||||
/**************
|
||||
** hw/pci.h **
|
||||
**************/
|
||||
|
||||
enum Pci_regs {
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY = 0,
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64 = 0x04,
|
||||
PCI_CLASS_PROG = 0x09,
|
||||
PCI_CACHE_LINE_SIZE = 0x0c,
|
||||
PCI_INTERRUPT_PIN = 0x3d,
|
||||
};
|
||||
|
||||
enum Pci_ids {
|
||||
PCI_CLASS_SERIAL_USB = 0x0c03,
|
||||
PCI_VENDOR_ID_NEC = 0x1033,
|
||||
PCI_DEVICE_ID_NEC_UPD720200 = 0x0194,
|
||||
};
|
||||
|
||||
typedef struct PCIBus { unsigned dummy; } PCIBus;
|
||||
|
||||
typedef struct PCIDevice
|
||||
{
|
||||
uint8_t config[0x1000]; /* PCIe config space */
|
||||
PCIBus *bus;
|
||||
|
||||
uint8_t *msix_table;
|
||||
uint8_t *msix_pba;
|
||||
|
||||
MemoryRegion msix_table_mmio;
|
||||
MemoryRegion msix_pba_mmio;
|
||||
|
||||
unsigned *msix_entry_used;
|
||||
} PCIDevice;
|
||||
|
||||
typedef void PCIUnregisterFunc(PCIDevice *pci_dev);
|
||||
|
||||
typedef struct PCIDeviceClass
|
||||
{
|
||||
void (*realize)(PCIDevice *dev, Error **errp);
|
||||
PCIUnregisterFunc *exit;
|
||||
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint8_t revision;
|
||||
uint16_t class_id;
|
||||
|
||||
int is_express;
|
||||
|
||||
} PCIDeviceClass;
|
||||
|
||||
#define TYPE_PCI_DEVICE "pci-device"
|
||||
#define PCI_DEVICE(obj) \
|
||||
OBJECT_CHECK(PCIDevice, (obj), TYPE_PCI_DEVICE)
|
||||
#define PCI_DEVICE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PCIDeviceClass, (klass), TYPE_PCI_DEVICE)
|
||||
|
||||
int pci_dma_read(PCIDevice *dev, dma_addr_t addr,
|
||||
void *buf, dma_addr_t len);
|
||||
|
||||
int pci_dma_write(PCIDevice *dev, dma_addr_t addr,
|
||||
const void *buf, dma_addr_t len);
|
||||
|
||||
void pci_set_irq(PCIDevice *pci_dev, int level);
|
||||
void pci_irq_assert(PCIDevice *pci_dev);
|
||||
void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev,
|
||||
int alloc_hint);
|
||||
AddressSpace *pci_get_address_space(PCIDevice *dev);
|
||||
|
||||
void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
uint8_t attr, MemoryRegion *memory);
|
||||
bool pci_bus_is_express(PCIBus *bus);
|
||||
|
||||
int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset);
|
||||
|
||||
|
||||
/*********************
|
||||
** hw/pci/msi(x).h **
|
||||
*********************/
|
||||
|
||||
int msi_init(struct PCIDevice *dev, uint8_t offset,
|
||||
unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask);
|
||||
|
||||
int msix_init(PCIDevice *dev, unsigned short nentries,
|
||||
MemoryRegion *table_bar, uint8_t table_bar_nr,
|
||||
unsigned table_offset, MemoryRegion *pba_bar,
|
||||
uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos);
|
||||
|
||||
bool msi_enabled(const PCIDevice *dev);
|
||||
int msix_enabled(PCIDevice *dev);
|
||||
|
||||
int msix_vector_use(PCIDevice *dev, unsigned vector);
|
||||
void msix_vector_unuse(PCIDevice *dev, unsigned vector);
|
||||
|
||||
void msi_notify(PCIDevice *dev, unsigned int vector);
|
||||
void msix_notify(PCIDevice *dev, unsigned vector);
|
||||
|
||||
|
||||
/*************************
|
||||
** migration/vmstate.h **
|
||||
*************************/
|
||||
|
||||
#define VMSTATE_BOOL(...) {}
|
||||
#define VMSTATE_UINT8(...) {}
|
||||
#define VMSTATE_UINT32(...) {}
|
||||
#define VMSTATE_UINT32_TEST(...) {}
|
||||
#define VMSTATE_UINT64(...) {}
|
||||
#define VMSTATE_INT32(...) {}
|
||||
#define VMSTATE_INT64(...) {}
|
||||
#define VMSTATE_STRUCT_ARRAY_TEST(...) {}
|
||||
#define VMSTATE_UINT8_ARRAY(...) {}
|
||||
#define VMSTATE_STRUCT_VARRAY_UINT32(...) {}
|
||||
#define VMSTATE_PCIE_DEVICE(...) {}
|
||||
#define VMSTATE_MSIX(...) {}
|
||||
#define VMSTATE_TIMER_PTR(...) {}
|
||||
#define VMSTATE_STRUCT(...) {}
|
||||
#define VMSTATE_END_OF_LIST() {}
|
||||
|
||||
typedef struct VMStateField { unsigned dummy; } VMStateField;
|
||||
typedef struct VMStateDescription
|
||||
{
|
||||
char const *name;
|
||||
int version_id;
|
||||
int minimum_version_id;
|
||||
int (*post_load)(void *opaque, int version_id);
|
||||
VMStateField *fields;
|
||||
} VMStateDescription;
|
||||
|
||||
|
||||
/**************
|
||||
** assert.h **
|
||||
**************/
|
||||
|
||||
#define assert(cond) do { \
|
||||
if (!(cond)) { \
|
||||
q_printf("assertion faied: %s:%d\n", __FILE__, __LINE__); \
|
||||
int *d = (int *)0x0; \
|
||||
*d = 1; \
|
||||
}} while (0)
|
||||
|
||||
|
||||
/************
|
||||
** traces **
|
||||
************/
|
||||
|
||||
#define trace_usb_xhci_irq_msix_use(v)
|
||||
#define trace_usb_xhci_irq_msix_unuse(v)
|
||||
#define trace_usb_xhci_irq_msix(v)
|
||||
#define trace_usb_xhci_irq_msi(v)
|
||||
#define trace_usb_xhci_queue_event(...)
|
||||
#define trace_usb_xhci_fetch_trb(...)
|
||||
#define trace_usb_xhci_run(...)
|
||||
#define trace_usb_xhci_stop(...)
|
||||
#define trace_usb_xhci_ep_state(...)
|
||||
#define trace_usb_xhci_ep_enable(...)
|
||||
#define trace_usb_xhci_ep_disable(...)
|
||||
#define trace_usb_xhci_ep_stop(...)
|
||||
#define trace_usb_xhci_ep_reset(...)
|
||||
#define trace_usb_xhci_ep_set_dequeue(...)
|
||||
#define trace_usb_xhci_xfer_async(...)
|
||||
#define trace_usb_xhci_xfer_nak(...)
|
||||
#define trace_usb_xhci_xfer_success(...)
|
||||
#define trace_usb_xhci_xfer_error(...)
|
||||
#define trace_usb_xhci_xfer_start(...)
|
||||
#define trace_usb_xhci_unimplemented(...)
|
||||
#define trace_usb_xhci_ep_kick(...)
|
||||
#define trace_usb_xhci_xfer_retry(...)
|
||||
#define trace_usb_xhci_slot_enable(...)
|
||||
#define trace_usb_xhci_slot_disable(...)
|
||||
#define trace_usb_xhci_slot_address(...)
|
||||
#define trace_usb_xhci_slot_configure(...)
|
||||
#define trace_usb_xhci_irq_intx(...)
|
||||
#define trace_usb_xhci_slot_reset(...)
|
||||
#define trace_usb_xhci_slot_evaluate(...)
|
||||
#define trace_usb_xhci_port_notify(...)
|
||||
#define trace_usb_xhci_port_link(...)
|
||||
#define trace_usb_xhci_port_reset(...)
|
||||
#define trace_usb_xhci_reset(...)
|
||||
#define trace_usb_xhci_cap_read(...)
|
||||
#define trace_usb_xhci_port_read(...)
|
||||
#define trace_usb_xhci_port_write(...)
|
||||
#define trace_usb_xhci_oper_read(...)
|
||||
#define trace_usb_xhci_oper_write(...)
|
||||
#define trace_usb_xhci_runtime_read(...)
|
||||
#define trace_usb_xhci_runtime_write(...)
|
||||
#define trace_usb_xhci_doorbell_read(...)
|
||||
#define trace_usb_xhci_doorbell_write(...)
|
||||
#define trace_usb_xhci_exit(...)
|
||||
#define trace_usb_port_claim(...)
|
||||
#define trace_usb_port_release(...)
|
||||
#define trace_usb_port_attach(...)
|
||||
#define trace_usb_port_detach(...)
|
||||
#define trace_usb_packet_state_fault(...)
|
||||
#define trace_usb_packet_state_change(...)
|
||||
|
||||
/***********************
|
||||
** library interface **
|
||||
***********************/
|
||||
|
||||
#define type_init(func) void _type_init_##func(void) { func(); }
|
||||
|
||||
#define TYPE_USB_HOST_DEVICE "usb-host"
|
||||
|
||||
typedef struct USBHostDevice
|
||||
{
|
||||
void *data;
|
||||
} USBHostDevice;
|
||||
|
||||
|
||||
USBHostDevice *create_usbdevice(void *data);
|
||||
void remove_usbdevice(USBHostDevice *device);
|
||||
|
||||
void usb_host_destroy();
|
||||
void usb_host_update_devices();
|
||||
|
||||
|
||||
/***********************
|
||||
** monitor/monitor.h **
|
||||
***********************/
|
||||
|
||||
void monitor_printf(Monitor *mon, const char *fmt, ...);
|
||||
|
||||
|
||||
#endif /* _INCLUDE__QEMU_EMUL_H_ */
|
19
repos/libports/src/lib/qemu-usb/patches/usb_bus_nfree.patch
Normal file
19
repos/libports/src/lib/qemu-usb/patches/usb_bus_nfree.patch
Normal file
@ -0,0 +1,19 @@
|
||||
diff --git a/src/lib/qemu/hw/usb/bus.c b/src/lib/qemu/hw/usb/bus.c
|
||||
index 5f39e1e..b94f3af 100644
|
||||
--- a/src/lib/qemu/hw/usb/bus.c
|
||||
+++ b/src/lib/qemu/hw/usb/bus.c
|
||||
@@ -433,10 +433,10 @@ void usb_claim_port(USBDevice *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
- if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) {
|
||||
- /* Create a new hub and chain it on */
|
||||
- usb_try_create_simple(bus, "usb-hub", NULL);
|
||||
- }
|
||||
+ // if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) {
|
||||
+ // /* Create a new hub and chain it on */
|
||||
+ // usb_try_create_simple(bus, "usb-hub", NULL);
|
||||
+ // }
|
||||
if (bus->nfree == 0) {
|
||||
error_setg(errp, "tried to attach usb device %s to a bus "
|
||||
"with no free ports", dev->product_desc);
|
27
repos/libports/src/lib/qemu-usb/patches/xhci_state.patch
Normal file
27
repos/libports/src/lib/qemu-usb/patches/xhci_state.patch
Normal file
@ -0,0 +1,27 @@
|
||||
diff --git a/src/lib/qemu/hw/usb/hcd-xhci.c b/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
index c673bed..b2a8939 100644
|
||||
--- a/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
+++ b/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
@@ -486,6 +486,8 @@ struct XHCIState {
|
||||
|
||||
#define TYPE_XHCI "nec-usb-xhci"
|
||||
|
||||
+#ifndef __cplusplus
|
||||
+
|
||||
#define XHCI(obj) \
|
||||
OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI)
|
||||
|
||||
@@ -2361,6 +2363,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
||||
|
||||
if (bsr) {
|
||||
slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
|
||||
+ usb_device_reset(dev);
|
||||
} else {
|
||||
USBPacket p;
|
||||
uint8_t buf[1];
|
||||
@@ -3914,3 +3917,5 @@ static void xhci_register_types(void)
|
||||
}
|
||||
|
||||
type_init(xhci_register_types)
|
||||
+
|
||||
+#endif /* __cplusplus */
|
896
repos/libports/src/lib/qemu-usb/qemu_emul.cc
Normal file
896
repos/libports/src/lib/qemu-usb/qemu_emul.cc
Normal file
@ -0,0 +1,896 @@
|
||||
/*
|
||||
* \brief Qemu USB controller interface
|
||||
* \author Josef Soentgen
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-12-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU General Public License version 2.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/env.h>
|
||||
#include <base/printf.h>
|
||||
#include <util/misc_math.h>
|
||||
|
||||
/* local includes */
|
||||
#include <extern_c_begin.h>
|
||||
#include <qemu_emul.h>
|
||||
/* XHCIState is defined in this file */
|
||||
#include <hcd-xhci.c>
|
||||
#include <extern_c_end.h>
|
||||
|
||||
#include <qemu/usb.h>
|
||||
|
||||
/*******************
|
||||
** USB interface **
|
||||
*******************/
|
||||
|
||||
static bool const verbose_irq = false;
|
||||
static bool const verbose_iov = false;
|
||||
static bool const verbose_mmio = false;
|
||||
|
||||
extern "C" void _type_init_usb_register_types();
|
||||
extern "C" void _type_init_usb_host_register_types(Genode::Signal_receiver*);
|
||||
extern "C" void _type_init_xhci_register_types();
|
||||
|
||||
extern Genode::Lock _lock;
|
||||
|
||||
Qemu::Controller *qemu_controller();
|
||||
|
||||
|
||||
static Qemu::Timer_queue* _timer_queue;
|
||||
static Qemu::Pci_device* _pci_device;
|
||||
|
||||
|
||||
Qemu::Controller *Qemu::usb_init(Timer_queue &tq, Pci_device &pci, Genode::Signal_receiver &sig_rec)
|
||||
{
|
||||
_timer_queue = &tq;
|
||||
_pci_device = &pci;
|
||||
|
||||
_type_init_usb_register_types();
|
||||
_type_init_xhci_register_types();
|
||||
_type_init_usb_host_register_types(&sig_rec);
|
||||
|
||||
return qemu_controller();
|
||||
}
|
||||
|
||||
void reset_controller();
|
||||
|
||||
void Qemu::usb_reset()
|
||||
{
|
||||
usb_host_destroy();
|
||||
|
||||
reset_controller();
|
||||
}
|
||||
|
||||
|
||||
void Qemu::usb_update_devices() {
|
||||
usb_host_update_devices(); }
|
||||
|
||||
|
||||
void Qemu::usb_timer_callback(void (*cb)(void*), void *data)
|
||||
{
|
||||
Genode::Lock::Guard g(_lock);
|
||||
|
||||
cb(data);
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
** libc **
|
||||
**********/
|
||||
|
||||
void *malloc(size_t size) {
|
||||
return Genode::env()->heap()->alloc(size); }
|
||||
|
||||
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
return Genode::memset(s, c, n); }
|
||||
|
||||
|
||||
void free(void *p) {
|
||||
Genode::env()->heap()->free(p, 0); }
|
||||
|
||||
|
||||
void q_printf(char const *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
Genode::vprintf(fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
int snprintf(char *buf, size_t size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
Genode::String_console sc(buf, size);
|
||||
sc.vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return sc.len();
|
||||
}
|
||||
|
||||
|
||||
int strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
return Genode::strcmp(s1, s2);
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Qemu emulation **
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Set properties of objects
|
||||
*/
|
||||
template <typename T>
|
||||
void properties_apply(T *state, DeviceClass *klass)
|
||||
{
|
||||
Property *p = klass->props;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
while (p->type != END) {
|
||||
char *s = (char *)state;
|
||||
s += p->offset;
|
||||
uint32_t *member = (uint32_t *)s;
|
||||
if (p->type == BIT)
|
||||
*member |= p->value;
|
||||
if (p->type == UINT32)
|
||||
*member = p->value;
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class and objects wrapper
|
||||
*/
|
||||
struct Wrapper
|
||||
{
|
||||
unsigned long _start;
|
||||
Object _object;
|
||||
DeviceState _device_state;
|
||||
PCIDevice _pci_device;
|
||||
USBDevice _usb_device;
|
||||
BusState _bus_state;
|
||||
XHCIState *_xhci_state = nullptr;
|
||||
USBHostDevice _usb_host_device;
|
||||
|
||||
ObjectClass _object_class;
|
||||
DeviceClass _device_class;
|
||||
PCIDeviceClass _pci_device_class;
|
||||
BusClass _bus_class;
|
||||
USBDeviceClass _usb_device_class;
|
||||
HotplugHandlerClass _hotplug_handler_class;
|
||||
unsigned long _end;
|
||||
|
||||
Wrapper() { }
|
||||
|
||||
bool in_object(void * ptr)
|
||||
{
|
||||
/*
|
||||
* XHCIState is roughly 3 MiB large so we only create on instance
|
||||
* (see below) and have to do this pointer check shenanigans.
|
||||
*/
|
||||
if (_xhci_state != nullptr
|
||||
&& ptr >= _xhci_state
|
||||
&& ptr < ((char*)_xhci_state + sizeof(XHCIState)))
|
||||
return true;
|
||||
|
||||
using addr_t = Genode::addr_t;
|
||||
|
||||
addr_t p = (addr_t)ptr;
|
||||
|
||||
if (p > (addr_t)&_start && p < (addr_t)&_end)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Object_pool
|
||||
{
|
||||
enum {
|
||||
XHCI, /* XHCI controller */
|
||||
USB_BUS, /* bus driver */
|
||||
USB_DEVICE, /* USB device driver */
|
||||
USB_HOST_DEVICE, /* USB host device driver */
|
||||
MAX = 8 /* host devices (USB_HOST_DEVICE - MAX) */
|
||||
};
|
||||
|
||||
bool used[MAX];
|
||||
Wrapper obj[MAX];
|
||||
|
||||
Wrapper *create_object()
|
||||
{
|
||||
for (unsigned i = USB_HOST_DEVICE + 1; i < MAX; i++) {
|
||||
if (used[i] == false) {
|
||||
used[i] = true;
|
||||
return &obj[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void free_object(Wrapper *w)
|
||||
{
|
||||
for (unsigned i = USB_HOST_DEVICE + 1; i < MAX; i++)
|
||||
if (w == &obj[i]) {
|
||||
used[i] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Wrapper * find_object(void *ptr)
|
||||
{
|
||||
for (Wrapper &w : obj) {
|
||||
if(w.in_object(ptr))
|
||||
return &w;
|
||||
}
|
||||
|
||||
PERR("object for pointer not found called from: %p", __builtin_return_address(0));
|
||||
throw -1;
|
||||
}
|
||||
|
||||
XHCIState *xhci_state() {
|
||||
return obj[XHCI]._xhci_state; }
|
||||
|
||||
BusState *bus() {
|
||||
return &obj[USB_BUS]._bus_state; }
|
||||
|
||||
static Object_pool *p()
|
||||
{
|
||||
static Object_pool _p;
|
||||
return &_p;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***********
|
||||
** casts **
|
||||
***********/
|
||||
|
||||
struct PCIDevice *cast_PCIDevice(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_pci_device; }
|
||||
|
||||
|
||||
struct XHCIState *cast_XHCIState(void *ptr) {
|
||||
return Object_pool::p()->find_object(ptr)->_xhci_state; }
|
||||
|
||||
|
||||
struct DeviceState *cast_DeviceState(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_device_state; }
|
||||
|
||||
|
||||
struct BusState *cast_BusState(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_bus_state; }
|
||||
|
||||
|
||||
struct USBDevice *cast_USBDevice(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_usb_device; }
|
||||
|
||||
|
||||
struct Object *cast_object(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_object; }
|
||||
|
||||
|
||||
struct PCIDeviceClass *cast_PCIDeviceClass(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_pci_device_class; }
|
||||
|
||||
|
||||
struct DeviceClass *cast_DeviceClass(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_device_class; }
|
||||
|
||||
|
||||
struct USBDeviceClass *cast_USBDeviceClass(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_usb_device_class; }
|
||||
|
||||
|
||||
struct BusClass *cast_BusClass(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_bus_class; }
|
||||
|
||||
|
||||
struct HotplugHandlerClass *cast_HotplugHandlerClass(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_hotplug_handler_class; }
|
||||
|
||||
|
||||
struct USBHostDevice *cast_USBHostDevice(void *ptr) {
|
||||
return &Object_pool::p()->find_object(ptr)->_usb_host_device; }
|
||||
|
||||
|
||||
struct USBBus *cast_DeviceStateToUSBBus(void) {
|
||||
return &Object_pool::p()->xhci_state()->bus; }
|
||||
|
||||
|
||||
USBHostDevice *create_usbdevice(void *data)
|
||||
{
|
||||
Wrapper *obj = Object_pool::p()->create_object();
|
||||
if (!obj) {
|
||||
PERR("could not create new object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
obj->_usb_device_class = Object_pool::p()->obj[Object_pool::USB_HOST_DEVICE]._usb_device_class;
|
||||
|
||||
/*
|
||||
* Set parent bus object
|
||||
*/
|
||||
DeviceState *dev_state = &obj->_device_state;
|
||||
dev_state->parent_bus = Object_pool::p()->bus();
|
||||
|
||||
/* set data pointer */
|
||||
obj->_usb_host_device.data = data;
|
||||
|
||||
/*
|
||||
* Attach new USB host device to USB device driver
|
||||
*/
|
||||
Error *e = nullptr;
|
||||
DeviceClass *usb_device_class = &Object_pool::p()->obj[Object_pool::USB_DEVICE]._device_class;
|
||||
usb_device_class->realize(dev_state, &e);
|
||||
if (e) {
|
||||
error_free(e);
|
||||
e = nullptr;
|
||||
|
||||
usb_device_class->unrealize(dev_state, &e);
|
||||
Object_pool::p()->free_object(obj);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &obj->_usb_host_device;
|
||||
}
|
||||
|
||||
|
||||
void remove_usbdevice(USBHostDevice *device)
|
||||
{
|
||||
DeviceClass *usb_device_class = &Object_pool::p()->obj[Object_pool::USB_DEVICE]._device_class;
|
||||
DeviceState *usb_device_state = cast_DeviceState(device);
|
||||
|
||||
if (usb_device_class == nullptr)
|
||||
PERR("usb_device_class null");
|
||||
|
||||
if (usb_device_state == nullptr)
|
||||
PERR("usb_device_class null");
|
||||
|
||||
Error *e = nullptr;
|
||||
usb_device_class->unrealize(usb_device_state, &e);
|
||||
|
||||
Wrapper *obj = Object_pool::p()->find_object(device);
|
||||
Object_pool::p()->free_object(obj);
|
||||
}
|
||||
|
||||
|
||||
void reset_controller()
|
||||
{
|
||||
Wrapper *w = &Object_pool::p()->obj[Object_pool::XHCI];
|
||||
DeviceClass *dc = &w->_device_class;
|
||||
|
||||
dc->reset(&w->_device_state);
|
||||
}
|
||||
|
||||
|
||||
/***********************************
|
||||
** Qemu interface implementation **
|
||||
***********************************/
|
||||
|
||||
Type type_register_static(TypeInfo const *t)
|
||||
{
|
||||
if (!Genode::strcmp(t->name, TYPE_XHCI)) {
|
||||
Wrapper *w = &Object_pool::p()->obj[Object_pool::XHCI];
|
||||
w->_xhci_state = (XHCIState*) malloc(sizeof(XHCIState));
|
||||
Genode::memset(w->_xhci_state, 0, sizeof(XHCIState));
|
||||
|
||||
t->class_init(&w->_object_class, 0);
|
||||
|
||||
properties_apply(w->_xhci_state, &w->_device_class);
|
||||
|
||||
PCIDevice *pci = &w->_pci_device;
|
||||
PCIDeviceClass *pci_class = &w->_pci_device_class;
|
||||
Error *e;
|
||||
pci_class->realize(pci, &e);
|
||||
}
|
||||
|
||||
if (!Genode::strcmp(t->name, TYPE_USB_DEVICE)) {
|
||||
Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_DEVICE];
|
||||
t->class_init(&w->_object_class, 0);
|
||||
}
|
||||
|
||||
if (!Genode::strcmp(t->name, TYPE_USB_HOST_DEVICE)) {
|
||||
Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_HOST_DEVICE];
|
||||
t->class_init(&w->_object_class, 0);
|
||||
}
|
||||
|
||||
if (!Genode::strcmp(t->name, TYPE_USB_BUS)) {
|
||||
Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_BUS];
|
||||
ObjectClass *c = &w->_object_class;
|
||||
t->class_init(c, 0);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void qbus_create_inplace(void* bus, size_t size , const char* type,
|
||||
DeviceState *parent, const char *name)
|
||||
{
|
||||
Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_BUS];
|
||||
BusState *b = &w->_bus_state;
|
||||
char const *n = "xhci.0";
|
||||
b->name = (char *)malloc(Genode::strlen(n) + 1);
|
||||
Genode::strncpy(b->name, n, Genode::strlen(n) + 1);
|
||||
}
|
||||
|
||||
|
||||
void timer_del(QEMUTimer *t)
|
||||
{
|
||||
_timer_queue->deactivate_timer(t);
|
||||
}
|
||||
|
||||
|
||||
void timer_free(QEMUTimer *t)
|
||||
{
|
||||
_timer_queue->delete_timer(t);
|
||||
free(t);
|
||||
}
|
||||
|
||||
|
||||
void timer_mod(QEMUTimer *t, int64_t expire)
|
||||
{
|
||||
_timer_queue->activate_timer(t, expire);
|
||||
}
|
||||
|
||||
|
||||
QEMUTimer* timer_new_ns(QEMUClockType, void (*cb)(void*), void *opaque)
|
||||
{
|
||||
QEMUTimer *t = (QEMUTimer*)malloc(sizeof(QEMUTimer));
|
||||
if (t == nullptr) {
|
||||
PERR("could not create QEMUTimer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_timer_queue->register_timer(t, cb, opaque);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
int64_t qemu_clock_get_ns(QEMUClockType) {
|
||||
return _timer_queue->get_ns(); }
|
||||
|
||||
|
||||
struct Controller : public Qemu::Controller
|
||||
{
|
||||
struct Mmio
|
||||
{
|
||||
Genode::addr_t id;
|
||||
Genode::size_t size;
|
||||
Genode::off_t offset;
|
||||
|
||||
MemoryRegionOps const *ops;
|
||||
} mmio_regions [16];
|
||||
|
||||
uint64_t _mmio_size;
|
||||
|
||||
Controller()
|
||||
{
|
||||
Genode::memset(mmio_regions, 0, sizeof(mmio_regions));
|
||||
}
|
||||
|
||||
void mmio_add_region(uint64_t size) { _mmio_size = size; }
|
||||
|
||||
void mmio_add_region_io(Genode::addr_t id, uint64_t size, MemoryRegionOps const *ops)
|
||||
{
|
||||
for (Mmio &mmio : mmio_regions) {
|
||||
if (mmio.id != 0) continue;
|
||||
|
||||
mmio.id = id;
|
||||
mmio.size = size;
|
||||
mmio.ops = ops;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Mmio &find_region(Genode::off_t offset)
|
||||
{
|
||||
for (Mmio &mmio : mmio_regions) {
|
||||
if (offset >= mmio.offset
|
||||
&& (offset < mmio.offset + (Genode::off_t)mmio.size))
|
||||
return mmio;
|
||||
}
|
||||
|
||||
PERR("could not find MMIO region for offset: %lx", offset);
|
||||
throw -1;
|
||||
}
|
||||
|
||||
void mmio_add_sub_region(Genode::addr_t id, Genode::off_t offset)
|
||||
{
|
||||
for (Mmio &mmio : mmio_regions) {
|
||||
if (mmio.id != id) continue;
|
||||
|
||||
mmio.offset = offset;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t mmio_size() const { return _mmio_size; }
|
||||
|
||||
int mmio_read(Genode::off_t offset, void *buf, size_t size)
|
||||
{
|
||||
Genode::Lock::Guard g(_lock);
|
||||
Mmio &mmio = find_region(offset);
|
||||
Genode::off_t reg = offset - mmio.offset;
|
||||
|
||||
void *ptr = Object_pool::p()->xhci_state();
|
||||
|
||||
/*
|
||||
* Handle port access
|
||||
*/
|
||||
if (offset >= (OFF_OPER + 0x400) && offset < OFF_RUNTIME) {
|
||||
uint32_t port = (offset - 0x440) / 0x10;
|
||||
ptr = (void*)&Object_pool::p()->xhci_state()->ports[port];
|
||||
}
|
||||
|
||||
uint64_t v = mmio.ops->read(ptr, reg, size);
|
||||
*((uint32_t*)buf) = v;
|
||||
|
||||
if (verbose_mmio)
|
||||
PDBG("mmio: %lx offset: %lx reg: %lx v: %llx", mmio.id, offset, reg, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmio_write(Genode::off_t offset, void const *buf, size_t size)
|
||||
{
|
||||
Genode::Lock::Guard g(_lock);
|
||||
Mmio &mmio = find_region(offset);
|
||||
Genode::off_t reg = offset - mmio.offset;
|
||||
uint64_t v = *((uint64_t*)buf);
|
||||
|
||||
void *ptr = Object_pool::p()->xhci_state();
|
||||
|
||||
/*
|
||||
* Handle port access
|
||||
*/
|
||||
if (offset >= (OFF_OPER + 0x400) && offset < OFF_RUNTIME) {
|
||||
uint32_t port = (offset - 0x440) / 0x10;
|
||||
ptr = (void*)&Object_pool::p()->xhci_state()->ports[port];
|
||||
}
|
||||
|
||||
mmio.ops->write(ptr, reg, v, size);
|
||||
|
||||
if (verbose_mmio)
|
||||
PDBG("mmio: %lx offset: %lx reg: %lx v: %llx", mmio.id, offset, reg, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Controller *controller()
|
||||
{
|
||||
static Controller _inst;
|
||||
return &_inst;
|
||||
}
|
||||
|
||||
Qemu::Controller *qemu_controller()
|
||||
{
|
||||
return controller();
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
** MMIO **
|
||||
**********/
|
||||
|
||||
void memory_region_init(MemoryRegion *mr, Object *obj, const char *name, uint64_t size) {
|
||||
controller()->mmio_add_region(size); }
|
||||
|
||||
|
||||
void memory_region_init_io(MemoryRegion* mr, Object* obj, const MemoryRegionOps* ops,
|
||||
void *, const char * name, uint64_t size) {
|
||||
controller()->mmio_add_region_io((Genode::addr_t)mr, size, ops); }
|
||||
|
||||
|
||||
void memory_region_add_subregion(MemoryRegion *mr, hwaddr offset, MemoryRegion *sr) {
|
||||
controller()->mmio_add_sub_region((Genode::addr_t)sr, offset); }
|
||||
|
||||
|
||||
/*********
|
||||
** DMA **
|
||||
*********/
|
||||
|
||||
int pci_dma_read(PCIDevice*, dma_addr_t addr, void *buf, dma_addr_t size)
|
||||
{
|
||||
return _pci_device->read_dma(addr, buf, size);
|
||||
}
|
||||
|
||||
|
||||
int pci_dma_write(PCIDevice*, dma_addr_t addr, const void *buf, dma_addr_t size)
|
||||
{
|
||||
return _pci_device->write_dma(addr, buf, size);
|
||||
}
|
||||
|
||||
|
||||
int dma_memory_read(AddressSpace*, dma_addr_t addr, void *buf, dma_addr_t size)
|
||||
{
|
||||
return _pci_device->read_dma(addr, buf, size);
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
** Interrupts **
|
||||
****************/
|
||||
|
||||
void pci_set_irq(PCIDevice*, int level)
|
||||
{
|
||||
if (verbose_irq)
|
||||
PDBG("IRQ level: %d", level);
|
||||
_pci_device->raise_interrupt(level);
|
||||
}
|
||||
|
||||
|
||||
void pci_irq_assert(PCIDevice*)
|
||||
{
|
||||
pci_set_irq(nullptr, 1);
|
||||
}
|
||||
|
||||
|
||||
int msi_init(PCIDevice *pdev, uint8_t offset, unsigned int nr_vectors, bool msi64bit,
|
||||
bool msi_per_vector_mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int msix_init(PCIDevice*, short unsigned int, MemoryRegion*, uint8_t, unsigned int, MemoryRegion*,
|
||||
uint8_t, unsigned int, uint8_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool msi_enabled(const PCIDevice *pdev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int msix_enabled(PCIDevice *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void msi_notify(PCIDevice *pdev, unsigned int level) { }
|
||||
|
||||
|
||||
void msix_notify(PCIDevice *pdev , unsigned int level) { }
|
||||
|
||||
|
||||
/*************************************
|
||||
** IO vector / scatter-gatter list **
|
||||
*************************************/
|
||||
|
||||
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
|
||||
{
|
||||
int niov = qiov->niov;
|
||||
|
||||
if (qiov->alloc_hint <= niov) {
|
||||
if (verbose_iov)
|
||||
PDBG("alloc_hint %d <= niov: %d", qiov->alloc_hint, niov);
|
||||
|
||||
qiov->alloc_hint += 64;
|
||||
iovec *new_iov = (iovec*) malloc(sizeof(iovec) * qiov->alloc_hint);
|
||||
if (new_iov == nullptr) {
|
||||
PERR("Could not reallocate iov");
|
||||
throw -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < niov; i++) {
|
||||
new_iov[i].iov_base = qiov->iov[i].iov_base;
|
||||
new_iov[i].iov_len = qiov->iov[i].iov_len;
|
||||
}
|
||||
|
||||
free(qiov->iov);
|
||||
qiov->iov = new_iov;
|
||||
}
|
||||
|
||||
if (verbose_iov)
|
||||
PDBG("niov: %u iov_base: %p base: %p len: %zu",
|
||||
niov, &qiov->iov[niov].iov_base, base, len);
|
||||
|
||||
qiov->iov[niov].iov_base = base;
|
||||
qiov->iov[niov].iov_len = len;
|
||||
qiov->size += len;
|
||||
qiov->niov++;
|
||||
}
|
||||
|
||||
|
||||
void qemu_iovec_destroy(QEMUIOVector *qiov)
|
||||
{
|
||||
qemu_iovec_reset(qiov);
|
||||
|
||||
free(qiov->iov);
|
||||
qiov->iov = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void qemu_iovec_reset(QEMUIOVector *qiov)
|
||||
{
|
||||
qiov->size = 0;
|
||||
qiov->niov = 0;
|
||||
}
|
||||
|
||||
|
||||
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
|
||||
{
|
||||
if (verbose_iov)
|
||||
PDBG("iov: %p alloc_hint: %d", qiov->iov, alloc_hint);
|
||||
|
||||
iovec *iov = qiov->iov;
|
||||
if (iov != nullptr) {
|
||||
if (alloc_hint > qiov->alloc_hint)
|
||||
PERR("iov already initialized: %p and alloc_hint smaller", iov);
|
||||
|
||||
qemu_iovec_reset(qiov);
|
||||
return;
|
||||
}
|
||||
|
||||
if (alloc_hint <= 0) alloc_hint = 1;
|
||||
|
||||
qiov->alloc_hint = alloc_hint;
|
||||
|
||||
qiov->iov = (iovec*) malloc(sizeof(iovec) * alloc_hint);
|
||||
if (qiov->iov == nullptr) {
|
||||
PERR("Could not allocate iov");
|
||||
throw -1;
|
||||
}
|
||||
|
||||
Genode::memset(qiov->iov, 0, sizeof(iovec) * alloc_hint);
|
||||
|
||||
qemu_iovec_reset(qiov);
|
||||
}
|
||||
|
||||
|
||||
/* taken from util/iov.c */
|
||||
size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt,
|
||||
size_t offset, const void *buf, size_t bytes)
|
||||
{
|
||||
size_t done;
|
||||
unsigned int i;
|
||||
for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
|
||||
if (offset < iov[i].iov_len) {
|
||||
size_t len = Genode::min(iov[i].iov_len - offset, bytes - done);
|
||||
memcpy((char*)iov[i].iov_base + offset, (char*)buf + done, len);
|
||||
done += len;
|
||||
offset = 0;
|
||||
} else {
|
||||
offset -= iov[i].iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
assert(offset == 0);
|
||||
return done;
|
||||
}
|
||||
|
||||
|
||||
/* taken from util/iov.c */
|
||||
size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt,
|
||||
size_t offset, void *buf, size_t bytes)
|
||||
{
|
||||
size_t done;
|
||||
unsigned int i;
|
||||
for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) {
|
||||
if (offset < iov[i].iov_len) {
|
||||
size_t len = Genode::min(iov[i].iov_len - offset, bytes - done);
|
||||
memcpy((char*)buf + done, (char*)iov[i].iov_base + offset, len);
|
||||
done += len;
|
||||
offset = 0;
|
||||
} else {
|
||||
offset -= iov[i].iov_len;
|
||||
}
|
||||
}
|
||||
|
||||
assert(offset == 0);
|
||||
return done;
|
||||
}
|
||||
|
||||
|
||||
void pci_dma_sglist_init(QEMUSGList *sgl, PCIDevice*, int alloc_hint) {
|
||||
qemu_iovec_init(sgl, alloc_hint); }
|
||||
|
||||
|
||||
void qemu_sglist_add(QEMUSGList *sgl, dma_addr_t base, dma_addr_t len) {
|
||||
qemu_iovec_add(sgl, (void*)base, len); }
|
||||
|
||||
|
||||
void qemu_sglist_destroy(QEMUSGList *sgl) {
|
||||
qemu_iovec_destroy(sgl); }
|
||||
|
||||
|
||||
int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
|
||||
{
|
||||
void *mem;
|
||||
|
||||
for (int i = 0; i < sgl->niov; i++) {
|
||||
dma_addr_t base = (dma_addr_t) sgl->iov[i].iov_base;
|
||||
dma_addr_t len = sgl->iov[i].iov_len;
|
||||
|
||||
while (len) {
|
||||
dma_addr_t xlen = len;
|
||||
mem = _pci_device->map_dma(base, xlen);
|
||||
if (verbose_iov)
|
||||
PDBG("mem: 0x%p base: 0x%p len: 0x%lx", mem, (void*)base, len);
|
||||
|
||||
if (!mem) {
|
||||
goto err;
|
||||
}
|
||||
if (xlen > len) {
|
||||
xlen = len;
|
||||
}
|
||||
qemu_iovec_add(&p->iov, mem, xlen);
|
||||
len -= xlen;
|
||||
base += xlen;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
PERR("could not map dma");
|
||||
usb_packet_unmap(p, sgl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl)
|
||||
{
|
||||
for (int i = 0; i < p->iov.niov; i++) {
|
||||
_pci_device->unmap_dma(p->iov.iov[i].iov_base,
|
||||
p->iov.iov[i].iov_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
** qapi/error.h **
|
||||
******************/
|
||||
|
||||
void error_setg(Error **errp, const char *fmt, ...)
|
||||
{
|
||||
assert(*errp == nullptr);
|
||||
|
||||
*errp = (Error*) malloc(sizeof(Error));
|
||||
if (*errp == nullptr) {
|
||||
PERR("Could not allocate Error");
|
||||
return;
|
||||
}
|
||||
|
||||
Error *e = *errp;
|
||||
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
Genode::String_console sc(e->string, sizeof(e->string));
|
||||
sc.vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void error_propagate(Error **dst_errp, Error *local_err) {
|
||||
*dst_errp = local_err; }
|
||||
|
||||
|
||||
void error_free(Error *err) { free(err); }
|
14
repos/libports/src/lib/qemu-usb/symbol.map
Normal file
14
repos/libports/src/lib/qemu-usb/symbol.map
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
{
|
||||
global:
|
||||
|
||||
extern "C++" {
|
||||
Qemu::*;
|
||||
};
|
||||
|
||||
local:
|
||||
|
||||
*;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user