dde_bsd: use generic platform API

Ref genodelabs/genode#4578
This commit is contained in:
Stefan Kalkowski 2022-09-09 11:49:07 +02:00 committed by Christian Helmuth
parent 8d746a701e
commit 03cec5cdd7
16 changed files with 317 additions and 631 deletions

View File

@ -4,10 +4,8 @@ Audio
#####
The audio driver is ported from OpenBSD 6.6 and includes support for
Intel HD Audio, ICH as well as for Ensoniq AudioPCI (ES1370) compatible
soundcards. The HDA driver works on real hardware and Virtualbox
whereas the ES1370 driver is only used in Qemu. The ICH driver is only
tested in Virtualbox where it produces audible artifacts.
Intel HD Audio devices. The HDA driver works on real hardware and
supposedly in VirtualBox.
Usage and configuration

View File

@ -1,18 +1,13 @@
sys/lib/libkern/strlcpy.c
sys/dev/audio.c
sys/dev/audio_if.h
sys/dev/pci/auich.c
sys/dev/pci/azalia.c
sys/dev/pci/azalia.h
sys/dev/pci/azalia_codec.c
sys/dev/pci/eap.c
sys/dev/pci/eapreg.h
sys/dev/pci/pcidevs.h
sys/dev/pci/pcidevs_data.h
sys/dev/mulaw.c
sys/dev/mulaw.h
sys/dev/ic/ac97.c
sys/dev/ic/ac97.h
sys/sys/audioio.h
sys/sys/device.h
sys/sys/queue.h

View File

@ -13,7 +13,7 @@ INC_DIR += $(AUDIO_CONTRIB_DIR)
LIBS += dde_bsd_audio_include
SRC_CC += dummies.cc driver.cc irq.cc mem.cc misc.cc scheduler.cc timer.cc
SRC_CC += dummies.cc driver.cc mem.cc misc.cc scheduler.cc timer.cc
SRC_C += bsd_emul.c
SRC_S += setjmp.S

View File

@ -0,0 +1,13 @@
include $(REP_DIR)/lib/mk/dde_bsd_audio.inc
SRC_C += bsd_emul_pci.c
SRC_CC += pci.cc
# enable when debugging
#CC_OPT += -DAZALIA_DEBUG
#CC_OPT += -DDIAGNOSTIC
# HDA driver
SRC_C += dev/pci/azalia.c dev/pci/azalia_codec.c
# vi: set ft=make :

View File

@ -1,49 +0,0 @@
LIB_DIR = $(REP_DIR)/src/lib/audio
LIB_INC_DIR = $(LIB_DIR)/include
AUDIO_CONTRIB_DIR := $(call select_from_ports,dde_bsd)/src/lib/audio
#
# Set include paths up before adding the dde_bsd_audio_include library
# because it will use INC_DIR += and must be at the end
#
INC_DIR += $(LIB_DIR)
INC_DIR += $(LIB_INC_DIR)
INC_DIR += $(AUDIO_CONTRIB_DIR)
LIBS += dde_bsd_audio_include
SRC_C := bsd_emul_pci.c
SRC_CC += pci.cc
CC_OPT += -Wno-unused-but-set-variable
# disable builtins
CC_OPT += -fno-builtin-printf -fno-builtin-snprintf -fno-builtin-vsnprintf \
-fno-builtin-malloc -fno-builtin-free -fno-builtin-log -fno-builtin-log2
CC_OPT += -D_KERNEL
# enable when debugging
#CC_OPT += -DAC97_DEBUG
#CC_OPT += -DAUICH_DEBUG
#CC_OPT += -DAZALIA_DEBUG
#CC_OPT += -DDIAGNOSTIC
# AC97 codec
SRC_C += dev/ic/ac97.c
# HDA driver
SRC_C += dev/pci/azalia.c dev/pci/azalia_codec.c
# ICH driver
SRC_C += dev/pci/auich.c
# ES1370
SRC_C += dev/pci/eap.c
vpath %.c $(AUDIO_CONTRIB_DIR)
vpath %.c $(LIB_DIR)
vpath %.cc $(LIB_DIR)
# vi: set ft=make :

View File

@ -1,6 +1,6 @@
INC_DIR += $(LIB_INC_DIR)/spec/x86_32 $(LIB_INC_DIR)/spec/x86
include $(REP_DIR)/lib/mk/dde_bsd_audio.inc
include $(REP_DIR)/lib/mk/dde_bsd_audio_pci.inc
vpath %.S $(LIB_DIR)/spec/x86_32

View File

@ -1,6 +1,6 @@
INC_DIR += $(LIB_INC_DIR)/spec/x86_64 $(LIB_INC_DIR)/spec/x86
include $(REP_DIR)/lib/mk/dde_bsd_audio.inc
include $(REP_DIR)/lib/mk/dde_bsd_audio_pci.inc
vpath %.S $(LIB_DIR)/spec/x86_64

View File

@ -1 +1 @@
4d3a973ccec12ca00589f9213c2ce663d4a4e496
03360eec0f7a11d523e2b0c88568c95cb691d3ac

View File

@ -1,6 +1,6 @@
PORT_DIR := $(call port_dir,$(REP_DIR)/ports/dde_bsd)
MK_FILES := dde_bsd_audio.inc dde_bsd_audio_include.mk dde_bsd_audio_pci.mk
MK_FILES := dde_bsd_audio.inc dde_bsd_audio_include.mk dde_bsd_audio_pci.inc
LIB_MK := $(addprefix lib/mk/, $(MK_FILES)) \
$(foreach SPEC,x86_32 x86_64,lib/mk/spec/$(SPEC)/dde_bsd_audio.mk) \
@ -11,18 +11,13 @@ MIRROR_FROM_REP_DIR := $(LIB_MK) src/lib src/drivers patches include
MIRROR_FROM_PORT_DIR := $(addprefix src/lib/audio/, \
dev/pci/azalia_codec.c \
dev/pci/pcidevs.h \
dev/pci/eap.c \
dev/pci/pcidevs_data.h \
dev/pci/azalia.h \
dev/pci/eapreg.h \
dev/pci/azalia.c \
dev/pci/auich.c \
dev/mulaw.h \
dev/audio_if.h \
dev/mulaw.c \
dev/audio.c \
dev/ic/ac97.h \
dev/ic/ac97.c \
lib/libkern \
sys/device.h \
sys/audioio.h \

View File

@ -1,47 +1,33 @@
assert_spec x86
#
# Check used commands
#
if {[have_include "power_on/qemu"]} {
puts "\nAudio_out test running on Qemu is not supported.\n"
exit 0
}
set wget [installed_command wget]
if {[have_spec linux]} {
puts"\nAudio_out test running on Linux is not supported.\n"
exit 0
}
#
# Configure
#
set use_mixer 0
#
# Build
#
set build_components {
create_boot_directory
build {
core init timer
drivers/acpi
drivers/platform
app/pci_decode
server/report_rom
drivers/audio
test/audio_out
}
lappend_if $use_mixer build_components server/mixer
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
build $build_components
create_boot_directory
#
# Config
#
append config {
<config>
install_config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
@ -54,60 +40,86 @@ append config {
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>}
</start>
append_platform_drv_config
append_if $use_mixer config {
<start name="mixer">
<start name="report_rom">
<resource name="RAM" quantum="2M"/>
<provides><service name="Audio_out"/></provides>
<route>
<service name="Audio_out"> <child name="audio_drv"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>}
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config>
<policy label="pci_decode -> system" report="acpi_drv -> acpi"/>
<policy label="platform_drv -> devices" report="pci_decode -> devices"/>
</config>
</start>
append_if [have_spec linux] config {
<start name="audio_drv" ld="no">}
append_if [expr ![have_spec linux]] config {
<start name="audio_drv">}
append config {
<binary name="} [audio_drv_binary] {"/>
<start name="acpi_drv" caps="350">
<resource name="RAM" quantum="4M"/>
<route>
<service name="Report"> <child name="report_rom"/> </service>
<service name="IO_MEM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="ROM"> <parent/> </service>
</route>
</start>
<start name="pci_decode" caps="350">
<resource name="RAM" quantum="1M"/>
<route>
<service name="Report"> <child name="report_rom"/> </service>
<service name="ROM" label="system"> <child name="report_rom"/> </service>
<service name="IO_MEM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="ROM"> <parent/> </service>
</route>
</start>
<start name="platform_drv" caps="100" managing_system="yes">
<resource name="RAM" quantum="1M"/>
<provides>
<service name="Platform"/>
</provides>
<route>
<service name="ROM" label="devices"> <child name="report_rom"/> </service>
<service name="IRQ"> <parent/> </service>
<service name="IO_MEM"> <parent/> </service>
<service name="ROM"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="Timer"> <parent/> </service>
</route>
<config>
<policy label="audio_drv -> "> <pci class="AUDIO"/> <pci class="HDAUDIO"/> </policy>
</config>
</start>
<start name="audio_drv" caps="150">
<binary name="pci_audio_drv"/>
<resource name="RAM" quantum="8M"/>
<provides> <service name="Audio_out"/> </provides>
<!--
The proper ALSA device might need to be configured, e.g.
<config alsa_device="hw:CARD=Schiit,DEV=0"/>
when using Linux.
-->
<config/>
</start>
<start name="test-audio_out">
<resource name="RAM" quantum="4M"/>
<config>
<filename>sample.raw</filename>
</config>
<route>}
append_if $use_mixer config {
<service name="Audio_out"><child name="mixer"/></service>}
append config {
<route>
<any-service><parent/><any-child/></any-service>
</route>
</start>
</config>}
install_config $config
#
# Get sample file
#
if {[info exists env(GENODE_SAMPLE_RAW)]} {
catch { exec $wget $::env(GENODE_SAMPLE_RAW) -O bin/sample.raw }
}
if {![file exists bin/sample.raw]} {
puts ""
puts "The sample file is missing. Please take a look at"
@ -117,22 +129,12 @@ if {![file exists bin/sample.raw]} {
exit 1
}
#
# Boot modules
#
append boot_modules {
core ld.lib.so init timer } [audio_drv_binary] {
test-audio_out sample.raw
build_boot_image {
core ld.lib.so init timer
platform_drv acpi_drv pci_decode report_rom
pci_audio_drv test-audio_out sample.raw
}
lappend_if $use_mixer boot_modules mixer
append_platform_drv_boot_modules
build_boot_image $boot_modules
append qemu_args " -nographic -soundhw es1370 "
#
# For obvious reasons the timeout depends on the total

View File

@ -1,7 +1,7 @@
REQUIRES = x86
TARGET = pci_audio_drv
SRC_CC = main.cc
LIBS = dde_bsd_audio dde_bsd_audio_pci base
LIBS = dde_bsd_audio base
INC_DIR += $(REP_DIR)/include
vpath %.cc $(REP_DIR)/src/drivers/audio

View File

@ -24,25 +24,8 @@ namespace Bsd {
int probe_drivers(Genode::Env&, Genode::Allocator&);
void mem_init(Genode::Env&, Genode::Allocator &);
void irq_init(Genode::Entrypoint&, Genode::Allocator&);
void timer_init(Genode::Env&);
void update_time();
/**************************
** Bus_driver interface **
**************************/
struct Bus_driver
{
virtual Genode::Irq_session_capability irq_session() = 0;
virtual Genode::addr_t alloc(Genode::size_t size, int align) = 0;
virtual void free(Genode::addr_t virt, Genode::size_t size) = 0;
virtual Genode::addr_t virt_to_phys(Genode::addr_t virt) = 0;
virtual Genode::addr_t phys_to_virt(Genode::addr_t phys) = 0;
};
}
#endif /* _BSD_H_ */

View File

@ -40,8 +40,6 @@ short pv[] = { -1, PCI_BUS_PARENT };
struct cfdata cfdata[] = {
{&audio_ca, &audio_cd, 0, 0, 0, 0, pv+0, 0, 0},
{&azalia_ca, &azalia_cd, 0, 0, 0, 0, pv+1, 0, 0},
{&eap_ca, &eap_cd, 0, 0, 0, 0, pv+1, 0, 0},
{&auich_ca, &auich_cd, 0, 0, 0, 0, pv+1, 0, 0},
};

View File

@ -616,7 +616,6 @@ void Audio::init_driver(Genode::Env &env, Genode::Allocator &alloc,
Genode::Signal_context_capability announce_sigh)
{
Bsd::mem_init(env, alloc);
Bsd::irq_init(env.ep(), alloc);
Bsd::timer_init(env);
static Task bsd_task(env, alloc, config, announce_sigh);

View File

@ -1,161 +0,0 @@
/*
* \brief Signal context for IRQ's
* \author Josef Soentgen
* \date 2014-10-14
*/
/*
* Copyright (C) 2014-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/log.h>
#include <base/thread.h>
#include <base/tslab.h>
#include <timer_session/connection.h>
#include <irq_session/connection.h>
/* local includes */
#include <audio/audio.h>
#include <bsd.h>
#include <bsd_emul.h>
#include <scheduler.h>
namespace Bsd {
class Irq;
}
static void run_irq(void *args);
class Bsd::Irq
{
public:
typedef int (*intrh_t)(void*);
/**
* Context encapsulates the handling of an IRQ
*/
class Context
{
private:
enum { STACK_SIZE = 1024 * sizeof(long) };
Bsd::Task _task;
Genode::Irq_session_client _irq;
Genode::Signal_handler<Context> _dispatcher;
intrh_t _intrh;
void *_intarg;
/**
* Signal handler
*/
void _handle()
{
_task.unblock();
Bsd::scheduler().schedule();
}
public:
/**
* Constructor
*/
Context(Genode::Entrypoint &ep,
Genode::Irq_session_capability cap,
intrh_t intrh, void *intarg)
:
_task(run_irq, this, "irq", Bsd::Task::PRIORITY_3,
Bsd::scheduler(), STACK_SIZE),
_irq(cap),
_dispatcher(ep, *this, &Context::_handle),
_intrh(intrh), _intarg(intarg)
{
_irq.sigh(_dispatcher);
_irq.ack_irq();
}
/**
* Handle IRQ
*/
void handle_irq()
{
_intrh(_intarg);
_irq.ack_irq();
}
};
private:
Genode::Allocator &_alloc;
Genode::Entrypoint &_ep;
Context *_ctx;
public:
/**
* Constructor
*/
Irq(Genode::Allocator &alloc, Genode::Entrypoint &ep)
: _alloc(alloc), _ep(ep), _ctx(nullptr) { }
/**
* Request an IRQ
*/
void establish_intr(Genode::Irq_session_capability cap, intrh_t intrh, void *intarg)
{
if (_ctx) {
Genode::error("interrupt already established");
Genode::sleep_forever();
}
_ctx = new (&_alloc) Context(_ep, cap, intrh, intarg);
}
};
static Bsd::Irq *_bsd_irq;
void Bsd::irq_init(Genode::Entrypoint &ep, Genode::Allocator &alloc)
{
static Bsd::Irq irq_context(alloc, ep);
_bsd_irq = &irq_context;
}
static void run_irq(void *args)
{
Bsd::Irq::Context *ctx = static_cast<Bsd::Irq::Context*>(args);
while (true) {
Bsd::scheduler().current()->block_and_schedule();
ctx->handle_irq();
}
}
/**********************
** dev/pci/pcivar.h **
**********************/
extern "C" int pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ih) {
return 0; }
extern "C" void *pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
int ipl, int (*intrh)(void *), void *intarg,
const char *intrstr)
{
Bsd::Bus_driver *drv = (Bsd::Bus_driver*)pc;
_bsd_irq->establish_intr(drv->irq_session(), intrh, intarg);
return _bsd_irq;
}

View File

@ -16,162 +16,130 @@
#include <base/log.h>
#include <base/object_pool.h>
#include <dataspace/client.h>
#include <io_port_session/connection.h>
#include <io_mem_session/connection.h>
#include <legacy/x86/platform_session/connection.h>
#include <legacy/x86/platform_device/client.h>
#include <platform_session/device.h>
#include <platform_session/dma_buffer.h>
#include <util/retry.h>
/* local includes */
#include <audio/audio.h>
#include <bsd.h>
#include <bsd_emul.h>
#include <scheduler.h>
#include <extern_c_begin.h>
# include <dev/pci/pcidevs.h>
#include <extern_c_end.h>
static constexpr bool debug = false;
extern "C" int probe_cfdata(struct pci_attach_args *);
static void run_irq(void *args);
namespace {
class Pci_driver : public Bsd::Bus_driver
class Pci_driver
{
public:
enum Pci_config { IRQ = 0x3c, CMD = 0x4,
CMD_IO = 0x1, CMD_MEMORY = 0x2, CMD_MASTER = 0x4 };
private:
Genode::Env &_env;
Genode::Allocator &_alloc;
enum { DMA_SIZE = 1024 * 1024 };
Genode::Env & _env;
Platform::Connection _pci { _env };
Platform::Dma_buffer _buffer { _pci, DMA_SIZE, Genode::UNCACHED };
Genode::Allocator_avl _alloc;
struct Device
{
Platform::Device dev;
Platform::Device::Irq irq;
Platform::Device::Mmio mmio;
Device(Platform::Connection &pci,
Platform::Device::Name const &name)
: dev { pci, name }, irq { dev }, mmio { dev }
{ }
};
Genode::Constructible<Device> _device { };
uint16_t _vendor_id { 0U };
uint16_t _device_id { 0U };
uint32_t _class_code { 0U };
uint16_t _sub_vendor_id { 0U };
uint16_t _sub_device_id { 0U };
typedef int (*intrh_t)(void*);
intrh_t _irq_func { nullptr };
void * _irq_arg { nullptr };
Bsd::Task _irq_task { run_irq, this, "irq", Bsd::Task::PRIORITY_3,
Bsd::scheduler(), 1024 * sizeof(long) };
Genode::Io_signal_handler<Pci_driver> _irq_handler { _env.ep(), *this,
&Pci_driver::_irq_handle };
struct pci_attach_args _pa { 0, 0, 0, 0, 0 };
Platform::Connection _pci { _env };
Platform::Device_capability _cap;
Genode::Io_port_connection *_io_port { nullptr };
/**
* The Dma_region_manager provides memory used for DMA
* and manages its mappings.
*/
struct Dma_region_manager : public Genode::Allocator_avl
void _irq_handle()
{
Genode::Env &env;
_irq_task.unblock();
Bsd::scheduler().schedule();
}
enum { BACKING_STORE_SIZE = 1024 * 1024 };
Genode::addr_t _buffer_base() {
return (Genode::addr_t)_buffer.local_addr<char>(); }
Genode::addr_t base;
Genode::addr_t mapped_base;
void _handle_device_list() { /* intentionally left empty */ }
bool _dma_initialized { false };
void _wait_for_device_list(Genode::Entrypoint &ep,
Platform::Connection &pci)
{
using namespace Genode;
Constructible<Io_signal_handler<Pci_driver>> handler { };
Pci_driver &_drv;
Dma_region_manager(Genode::Env &env,
Genode::Allocator &alloc,
Pci_driver &drv)
: Genode::Allocator_avl(&alloc), env(env), _drv(drv) { }
Genode::addr_t alloc(Genode::size_t size, unsigned align)
{
using namespace Genode;
if (!_dma_initialized) {
try {
Ram_dataspace_capability cap = _drv._alloc_dma_memory(BACKING_STORE_SIZE);
mapped_base = (addr_t)env.rm().attach(cap);
base = _drv._dma_addr(cap);
Allocator_avl::add_range(mapped_base, BACKING_STORE_SIZE);
} catch (...) {
Genode::error("alloc DMA memory failed");
return 0;
bool device_list = false;
while (!device_list) {
pci.update();
pci.with_xml([&] (Xml_node & xml) {
if (xml.num_sub_nodes()) {
pci.sigh(Signal_context_capability());
if (handler.constructed())
handler.destruct();
device_list = true;
return;
}
_dma_initialized = true;
}
return Allocator_avl::alloc_aligned(size, align).convert<Genode::addr_t>(
[&] (void *ptr) { return (addr_t)ptr; },
[&] (Alloc_error) { return 0UL; });
}
if (!handler.constructed()) {
handler.construct(ep, *this,
&Pci_driver::_handle_device_list);
pci.sigh(*handler);
}
void free(Genode::addr_t virt, Genode::size_t size) {
Genode::Allocator_avl::free((void*)virt, size); }
Genode::addr_t virt_to_phys(Genode::addr_t virt) {
return virt - mapped_base + base; }
Genode::addr_t phys_to_virt(Genode::addr_t phys) {
return phys - base + mapped_base; }
} _dma_region_manager;
/**
* Scan pci bus for sound devices
*/
Platform::Device_capability _scan_pci(Platform::Device_capability const &prev)
{
Platform::Device_capability cap;
/* shift values for Pci interface used by Genode */
cap = _pci.with_upgrade([&] () {
return _pci.next_device(prev,
PCI_CLASS_MULTIMEDIA << 16,
PCI_CLASS_MASK << 16); });
if (prev.valid())
_pci.release_device(prev);
return cap;
}
/**
* Allocate DMA memory from the PCI driver
*/
Genode::Ram_dataspace_capability _alloc_dma_memory(Genode::size_t size)
{
size_t donate = size;
return Genode::retry<Genode::Out_of_ram>(
[&] () {
return Genode::retry<Genode::Out_of_caps>(
[&] () { return _pci.alloc_dma_buffer(size, Genode::UNCACHED); },
[&] () { _pci.upgrade_caps(2); });
},
[&] () {
_pci.upgrade_ram(donate);
donate = donate * 2 > size ? 4096 : donate * 2;
ep.wait_and_dispatch_one_io_signal();
});
}
/**
* Get physical address for DMA dataspace
*/
Genode::addr_t _dma_addr(Genode::Ram_dataspace_capability ds_cap)
{
return _pci.dma_addr(ds_cap);
}
}
public:
Pci_driver(Genode::Env &env, Genode::Allocator &alloc)
Pci_driver(Genode::Env &env, Genode::Allocator & alloc)
:
_env(env), _alloc(alloc),
_dma_region_manager(_env, _alloc, *this)
{ }
_env(env), _alloc(&alloc)
{
_alloc.add_range(_buffer_base(), DMA_SIZE);
Genode::Env &env() { return _env; }
/* will "block" until device list becomes available */
_wait_for_device_list(_env.ep(), _pci);
}
Genode::Allocator &alloc() { return _alloc; }
Platform::Device_capability cap() { return _cap; }
Platform::Connection &pci() { return _pci; }
uint16_t sub_device_id() { return _sub_device_id; }
uint16_t sub_vendor_id() { return _sub_vendor_id; }
int probe()
{
using namespace Genode;
_pci.upgrade_ram(8*1024);
/*
@ -183,149 +151,130 @@ class Pci_driver : public Bsd::Bus_driver
_pa.pa_dmat = (bus_dma_tag_t)this;
_pa.pa_pc = (pci_chipset_tag_t)this;
int found = 0;
while ((_cap = _scan_pci(_cap)).valid()) {
Platform::Device_client device(_cap);
bool found = false;
_pci.update();
_pci.with_xml([&] (Xml_node node) {
node.for_each_sub_node("device", [&] (Xml_node node)
{
if (found) return;
uint8_t bus, dev, func;
device.bus_address(&bus, &dev, &func);
String<16> name = node.attribute_value("name", String<16>());
if ((device.device_id() == PCI_PRODUCT_INTEL_CORE4G_HDA_2) ||
(device.vendor_id() == PCI_VENDOR_INTEL &&
bus == 0 && dev == 3 && func == 0)) {
Genode::warning("ignore ", (unsigned)bus, ":", (unsigned)dev, ":",
(unsigned)func, ", not supported HDMI/DP HDA device");
continue;
}
node.with_optional_sub_node("pci-config", [&] (Xml_node node)
{
_vendor_id = node.attribute_value("vendor_id", 0U);
_device_id = node.attribute_value("device_id", 0U);
_class_code = node.attribute_value("class", 0U);
_sub_vendor_id = node.attribute_value("sub_vendor_id", 0U);
_sub_device_id = node.attribute_value("sub_device_id", 0U);
/* we do the shifting to match OpenBSD's assumptions */
_pa.pa_tag = 0x80000000UL | (bus << 16) | (dev << 11) | (func << 8);
_pa.pa_class = device.class_code() << 8;
_pa.pa_id = device.vendor_id() | device.device_id() << 16;
if ((_device_id == PCI_PRODUCT_INTEL_CORE4G_HDA_2) ||
(_vendor_id == PCI_VENDOR_INTEL && name == "00:03.0")) {
warning("ignore ", name,
", not supported HDMI/DP HDA device");
return;
}
if (probe_cfdata(&_pa)) {
found++;
break;
}
}
/* we only construct the first useable device we find */
_device.construct(_pci, name);
_device->irq.sigh(_irq_handler);
return found;
/* we do the shifting to match OpenBSD's assumptions */
_pa.pa_tag = 0x80000000UL;
_pa.pa_class = _class_code << 8;
_pa.pa_id = _vendor_id | _device_id << 16;
if (probe_cfdata(&_pa))
found = true;
});
});
});
return found ? 1 : 0;
}
/**************************
** Bus_driver interface **
**************************/
void irq_handler(intrh_t handler, void * arg)
{
_irq_func = handler;
_irq_arg = arg;
}
Genode::Irq_session_capability irq_session() override {
return Platform::Device_client(_cap).irq(0); }
void handle_irq()
{
_irq_func(_irq_arg);
_device->irq.ack();
}
Genode::addr_t alloc(Genode::size_t size, int align) override {
return _dma_region_manager.alloc(size, align); }
void free(Genode::addr_t virt, Genode::size_t size) override {
_dma_region_manager.free(virt, size); }
/*********************
** Mmio management **
*********************/
Genode::addr_t virt_to_phys(Genode::addr_t virt) override {
return _dma_region_manager.virt_to_phys(virt); }
Genode::addr_t mmio_base() { return _device->mmio.base(); }
Genode::size_t mmio_size() { return _device->mmio.size(); }
Genode::addr_t phys_to_virt(Genode::addr_t phys) override {
return _dma_region_manager.phys_to_virt(phys); }
template <typename T>
T read(Genode::size_t offset) {
return *(volatile T*)(_device->mmio.base() + offset); }
template <typename T>
void write(Genode::size_t offset, T value) {
*(volatile T*)(_device->mmio.base() + offset) = value; }
/********************
** DMA management **
********************/
Genode::addr_t alloc(Genode::size_t size, unsigned align)
{
using namespace Genode;
return _alloc.alloc_aligned(size, align).convert<Genode::addr_t>(
[&] (void *ptr) { return (addr_t)ptr; },
[&] (Allocator_avl::Alloc_error) { return 0UL; });
}
void free(Genode::addr_t virt, Genode::size_t size) {
_alloc.free((void*)virt, size); }
Genode::addr_t virt_to_phys(Genode::addr_t virt) {
return virt - _buffer_base() + _buffer.dma_addr(); }
Genode::addr_t phys_to_virt(Genode::addr_t phys) {
return phys - _buffer.dma_addr() + _buffer_base(); }
};
/**********************
** Bus space helper **
** dev/pci/pcivar.h **
**********************/
struct Bus_space
extern "C" int pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ih) {
return 0; }
extern "C" void *pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
int ipl, int (*intrh)(void *), void *intarg,
const char *intrstr)
{
virtual unsigned read_1(unsigned long address) = 0;
virtual unsigned read_2(unsigned long address) = 0;
virtual unsigned read_4(unsigned long address) = 0;
virtual void write_1(unsigned long address, unsigned char value) = 0;
virtual void write_2(unsigned long address, unsigned short value) = 0;
virtual void write_4(unsigned long address, unsigned int value) = 0;
};
/*********************
** I/O port helper **
*********************/
struct Io_port : public Bus_space
{
Genode::Io_port_session_client _io;
Genode::addr_t _base;
Io_port(Genode::addr_t base, Genode::Io_port_session_capability cap)
: _io(cap), _base(base) { }
unsigned read_1(unsigned long address) {
return _io.inb(_base + address); }
unsigned read_2(unsigned long address) {
return _io.inw(_base + address); }
unsigned read_4(unsigned long address) {
return _io.inl(_base + address); }
void write_1(unsigned long address, unsigned char value) {
_io.outb(_base + address, value); }
void write_2(unsigned long address, unsigned short value) {
_io.outw(_base + address, value); }
void write_4(unsigned long address, unsigned int value) {
_io.outl(_base + address, value); }
};
/***********************
** I/O memory helper **
***********************/
struct Io_memory : public Bus_space
{
Genode::Io_mem_session_client _mem;
Genode::Io_mem_dataspace_capability _mem_ds;
Genode::addr_t _vaddr;
Io_memory(Genode::Region_map &rm,
Genode::addr_t base,
Genode::Io_mem_session_capability cap)
:
_mem(cap),
_mem_ds(_mem.dataspace())
{
if (!_mem_ds.valid())
throw Genode::Exception();
_vaddr = rm.attach(_mem_ds);
_vaddr |= base & 0xfff;
}
unsigned read_1(unsigned long address) {
return *(volatile unsigned char*)(_vaddr + address); }
unsigned read_2(unsigned long address) {
return *(volatile unsigned short*)(_vaddr + address); }
unsigned read_4(unsigned long address) {
return *(volatile unsigned int*)(_vaddr + address); }
void write_1(unsigned long address, unsigned char value) {
*(volatile unsigned char*)(_vaddr + address) = value; }
void write_2(unsigned long address, unsigned short value) {
*(volatile unsigned short*)(_vaddr + address) = value; }
void write_4(unsigned long address, unsigned int value) {
*(volatile unsigned int*)(_vaddr + address) = value; }
};
Pci_driver * drv = (Pci_driver*) pc;
drv->irq_handler(intrh, intarg);
return drv;
}
} /* anonymous namespace */
static void run_irq(void *args)
{
Pci_driver & pci_drv = *(Pci_driver*)args;
while (true) {
Bsd::scheduler().current()->block_and_schedule();
pci_drv.handle_irq();
}
}
int Bsd::probe_drivers(Genode::Env &env, Genode::Allocator &alloc)
{
@ -362,57 +311,19 @@ extern "C" int pci_mapreg_map(struct pci_attach_args *pa,
/* calculate BAR from given register */
int r = (reg - 0x10) / 4;
Pci_driver *drv = (Pci_driver*)pa->pa_pc;
Platform::Device_capability cap = drv->cap();
Platform::Device_client device(cap);
Platform::Device::Resource res = device.resource(r);
switch (res.type()) {
case Platform::Device::Resource::IO:
{
Io_port *iop = new (&drv->alloc())
Io_port(res.base(), device.io_port(r));
*tagp = (Genode::addr_t) iop;
break;
}
case Platform::Device::Resource::MEMORY:
{
Io_memory *iom = new (&drv->alloc())
Io_memory(drv->env().rm(), res.base(), device.io_mem(r));
*tagp = (Genode::addr_t) iom;
break;
}
case Platform::Device::Resource::INVALID:
{
Genode::error("PCI resource type invalid");
return -1;
}
if (r) {
Genode::error("MAP BAR ", r, " not implemented yet");
return -1;
}
*handlep = res.base();
Pci_driver *drv = (Pci_driver*)pa->pa_pc;
*tagp = (Genode::addr_t)drv;
*handlep = drv->mmio_base();
if (basep != 0)
*basep = res.base();
*basep = drv->mmio_base();
if (sizep != 0)
*sizep = maxsize > 0 && res.size() > maxsize ? maxsize : res.size();
/* enable bus master and I/O or memory bits */
uint16_t cmd = device.config_read(Pci_driver::CMD, Platform::Device::ACCESS_16BIT);
if (res.type() == Platform::Device::Resource::IO) {
cmd &= ~Pci_driver:: CMD_MEMORY;
cmd |= Pci_driver::CMD_IO;
} else {
cmd &= ~Pci_driver::CMD_IO;
cmd |= Pci_driver::CMD_MEMORY;
}
cmd |= Pci_driver::CMD_MASTER;
drv->pci().with_upgrade([&] () {
device.config_write(Pci_driver::CMD, cmd, Platform::Device::ACCESS_16BIT);
});
*sizep = maxsize > 0 && drv->mmio_size() > maxsize ? maxsize : drv->mmio_size();
return 0;
}
@ -424,17 +335,25 @@ extern "C" int pci_mapreg_map(struct pci_attach_args *pa,
extern "C" pcireg_t pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
Pci_driver *drv = (Pci_driver *)pc;
Platform::Device_client device(drv->cap());
return device.config_read(reg, Platform::Device::ACCESS_32BIT);
switch (reg) {
case 0x4: return 0x207; /* command register */
case 0x10: return drv->mmio_base();
case 0x2c: return drv->sub_device_id() << 16 | drv->sub_vendor_id();
default:
if (debug)
Genode::warning("Ignore reading of PCI config space @ ", reg);
};
return 0;
}
extern "C" void pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg,
pcireg_t val)
{
Pci_driver *drv = (Pci_driver *)pc;
Platform::Device_client device(drv->cap());
return device.config_write(reg, val, Platform::Device::ACCESS_32BIT);
if (debug)
Genode::warning("Ignore writing of PCI config space @ ",
reg, " val=", val);
}
@ -446,8 +365,7 @@ extern "C" u_int8_t bus_space_read_1(bus_space_tag_t space,
bus_space_handle_t handle,
bus_size_t offset)
{
Bus_space *bus = (Bus_space*)space;
return bus->read_1(offset);
return ((Pci_driver*)space)->read<Genode::uint8_t>(offset);
}
@ -455,8 +373,7 @@ extern "C" u_int16_t bus_space_read_2(bus_space_tag_t space,
bus_space_handle_t handle,
bus_size_t offset)
{
Bus_space *bus = (Bus_space*)space;
return bus->read_2(offset);
return ((Pci_driver*)space)->read<Genode::uint16_t>(offset);
}
@ -464,8 +381,7 @@ extern "C" u_int32_t bus_space_read_4(bus_space_tag_t space,
bus_space_handle_t handle,
bus_size_t offset)
{
Bus_space *bus = (Bus_space*)space;
return bus->read_4(offset);
return ((Pci_driver*)space)->read<Genode::uint32_t>(offset);
}
@ -473,8 +389,7 @@ extern "C" void bus_space_write_1(bus_space_tag_t space,
bus_space_handle_t handle,
bus_size_t offset, u_int8_t value)
{
Bus_space *bus = (Bus_space*)space;
bus->write_1(offset, value);
((Pci_driver*)space)->write(offset, value);
}
@ -482,8 +397,7 @@ extern "C" void bus_space_write_2(bus_space_tag_t space,
bus_space_handle_t handle,
bus_size_t offset, u_int16_t value)
{
Bus_space *bus = (Bus_space*)space;
bus->write_2(offset, value);
((Pci_driver*)space)->write(offset, value);
}
@ -491,8 +405,7 @@ extern "C" void bus_space_write_4(bus_space_tag_t space,
bus_space_handle_t handle,
bus_size_t offset, u_int32_t value)
{
Bus_space *bus = (Bus_space*)space;
bus->write_4(offset, value);
((Pci_driver*)space)->write(offset, value);
}
@ -520,7 +433,7 @@ extern "C" void bus_dmamap_destroy(bus_dma_tag_t tag, bus_dmamap_t map) {
extern "C" int bus_dmamap_load(bus_dma_tag_t tag, bus_dmamap_t dmam, void *buf,
bus_size_t buflen, struct proc *p, int flags)
{
Bsd::Bus_driver *drv = (Bsd::Bus_driver *)tag;
Pci_driver * drv = (Pci_driver*) tag;
Genode::addr_t virt = (Genode::addr_t)buf;
dmam->dm_segs[0].ds_addr = drv->virt_to_phys(virt);
@ -540,7 +453,7 @@ extern "C" int bus_dmamem_alloc(bus_dma_tag_t tag, bus_size_t size, bus_size_t a
bus_size_t boundary, bus_dma_segment_t *segs, int nsegs,
int *rsegs, int flags)
{
Bsd::Bus_driver *drv = (Bsd::Bus_driver *)tag;
Pci_driver * drv = (Pci_driver*) tag;
Genode::addr_t virt = drv->alloc(size, Genode::log2(alignment));
if (virt == 0)
@ -556,7 +469,7 @@ extern "C" int bus_dmamem_alloc(bus_dma_tag_t tag, bus_size_t size, bus_size_t a
extern "C" void bus_dmamem_free(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs)
{
Bsd::Bus_driver *drv = (Bsd::Bus_driver *)tag;
Pci_driver * drv = (Pci_driver*) tag;
for (int i = 0; i < nsegs; i++) {
Genode::addr_t phys = (Genode::addr_t)segs[i].ds_addr;
@ -575,7 +488,7 @@ extern "C" int bus_dmamem_map(bus_dma_tag_t tag, bus_dma_segment_t *segs, int ns
return -1;
}
Bsd::Bus_driver *drv = (Bsd::Bus_driver *)tag;
Pci_driver * drv = (Pci_driver*) tag;
Genode::addr_t phys = (Genode::addr_t)segs[0].ds_addr;
Genode::addr_t virt = drv->phys_to_virt(phys);