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 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 Intel HD Audio devices. The HDA driver works on real hardware and
soundcards. The HDA driver works on real hardware and Virtualbox supposedly in VirtualBox.
whereas the ES1370 driver is only used in Qemu. The ICH driver is only
tested in Virtualbox where it produces audible artifacts.
Usage and configuration Usage and configuration

View File

@ -1,18 +1,13 @@
sys/lib/libkern/strlcpy.c sys/lib/libkern/strlcpy.c
sys/dev/audio.c sys/dev/audio.c
sys/dev/audio_if.h sys/dev/audio_if.h
sys/dev/pci/auich.c
sys/dev/pci/azalia.c sys/dev/pci/azalia.c
sys/dev/pci/azalia.h sys/dev/pci/azalia.h
sys/dev/pci/azalia_codec.c 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.h
sys/dev/pci/pcidevs_data.h sys/dev/pci/pcidevs_data.h
sys/dev/mulaw.c sys/dev/mulaw.c
sys/dev/mulaw.h sys/dev/mulaw.h
sys/dev/ic/ac97.c
sys/dev/ic/ac97.h
sys/sys/audioio.h sys/sys/audioio.h
sys/sys/device.h sys/sys/device.h
sys/sys/queue.h sys/sys/queue.h

View File

@ -13,7 +13,7 @@ INC_DIR += $(AUDIO_CONTRIB_DIR)
LIBS += dde_bsd_audio_include 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_C += bsd_emul.c
SRC_S += setjmp.S 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 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 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 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 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) 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)) \ LIB_MK := $(addprefix lib/mk/, $(MK_FILES)) \
$(foreach SPEC,x86_32 x86_64,lib/mk/spec/$(SPEC)/dde_bsd_audio.mk) \ $(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/, \ MIRROR_FROM_PORT_DIR := $(addprefix src/lib/audio/, \
dev/pci/azalia_codec.c \ dev/pci/azalia_codec.c \
dev/pci/pcidevs.h \ dev/pci/pcidevs.h \
dev/pci/eap.c \
dev/pci/pcidevs_data.h \ dev/pci/pcidevs_data.h \
dev/pci/azalia.h \ dev/pci/azalia.h \
dev/pci/eapreg.h \
dev/pci/azalia.c \ dev/pci/azalia.c \
dev/pci/auich.c \
dev/mulaw.h \ dev/mulaw.h \
dev/audio_if.h \ dev/audio_if.h \
dev/mulaw.c \ dev/mulaw.c \
dev/audio.c \ dev/audio.c \
dev/ic/ac97.h \
dev/ic/ac97.c \
lib/libkern \ lib/libkern \
sys/device.h \ sys/device.h \
sys/audioio.h \ sys/audioio.h \

View File

@ -1,47 +1,33 @@
assert_spec x86 assert_spec x86
# if {[have_include "power_on/qemu"]} {
# Check used commands 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 create_boot_directory
build {
#
# Build
#
set build_components {
core init timer core init timer
drivers/acpi
drivers/platform
app/pci_decode
server/report_rom
drivers/audio drivers/audio
test/audio_out test/audio_out
} }
lappend_if $use_mixer build_components server/mixer install_config {
<config verbose="yes">
source ${genode_dir}/repos/base/run/platform_drv.inc
append_platform_drv_build_components
build $build_components
create_boot_directory
#
# Config
#
append config {
<config>
<parent-provides> <parent-provides>
<service name="ROM"/> <service name="ROM"/>
<service name="IRQ"/> <service name="IRQ"/>
<service name="IO_MEM"/> <service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/> <service name="PD"/>
<service name="RM"/> <service name="RM"/>
<service name="CPU"/> <service name="CPU"/>
@ -54,60 +40,86 @@ append config {
<start name="timer"> <start name="timer">
<resource name="RAM" quantum="1M"/> <resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides> <provides><service name="Timer"/></provides>
</start>} </start>
append_platform_drv_config <start name="report_rom">
append_if $use_mixer config {
<start name="mixer">
<resource name="RAM" quantum="2M"/> <resource name="RAM" quantum="2M"/>
<provides><service name="Audio_out"/></provides> <provides> <service name="Report"/> <service name="ROM"/> </provides>
<route> <config>
<service name="Audio_out"> <child name="audio_drv"/> </service> <policy label="pci_decode -> system" report="acpi_drv -> acpi"/>
<any-service> <parent/> <any-child/> </any-service> <policy label="platform_drv -> devices" report="pci_decode -> devices"/>
</route> </config>
</start>} </start>
append_if [have_spec linux] config { <start name="acpi_drv" caps="350">
<start name="audio_drv" ld="no">} <resource name="RAM" quantum="4M"/>
append_if [expr ![have_spec linux]] config { <route>
<start name="audio_drv">} <service name="Report"> <child name="report_rom"/> </service>
append config { <service name="IO_MEM"> <parent/> </service>
<binary name="} [audio_drv_binary] {"/> <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"/> <resource name="RAM" quantum="8M"/>
<provides> <service name="Audio_out"/> </provides> <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/> <config/>
</start> </start>
<start name="test-audio_out"> <start name="test-audio_out">
<resource name="RAM" quantum="4M"/> <resource name="RAM" quantum="4M"/>
<config> <config>
<filename>sample.raw</filename> <filename>sample.raw</filename>
</config> </config>
<route>} <route>
append_if $use_mixer config {
<service name="Audio_out"><child name="mixer"/></service>}
append config {
<any-service><parent/><any-child/></any-service> <any-service><parent/><any-child/></any-service>
</route> </route>
</start> </start>
</config>} </config>}
install_config $config
# #
# Get sample file # 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]} { if {![file exists bin/sample.raw]} {
puts "" puts ""
puts "The sample file is missing. Please take a look at" puts "The sample file is missing. Please take a look at"
@ -117,22 +129,12 @@ if {![file exists bin/sample.raw]} {
exit 1 exit 1
} }
# build_boot_image {
# Boot modules core ld.lib.so init timer
# platform_drv acpi_drv pci_decode report_rom
pci_audio_drv test-audio_out sample.raw
append boot_modules {
core ld.lib.so init timer } [audio_drv_binary] {
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 # For obvious reasons the timeout depends on the total

View File

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

View File

@ -24,25 +24,8 @@ namespace Bsd {
int probe_drivers(Genode::Env&, Genode::Allocator&); int probe_drivers(Genode::Env&, Genode::Allocator&);
void mem_init(Genode::Env&, Genode::Allocator &); void mem_init(Genode::Env&, Genode::Allocator &);
void irq_init(Genode::Entrypoint&, Genode::Allocator&);
void timer_init(Genode::Env&); void timer_init(Genode::Env&);
void update_time(); 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_ */ #endif /* _BSD_H_ */

View File

@ -40,8 +40,6 @@ short pv[] = { -1, PCI_BUS_PARENT };
struct cfdata cfdata[] = { struct cfdata cfdata[] = {
{&audio_ca, &audio_cd, 0, 0, 0, 0, pv+0, 0, 0}, {&audio_ca, &audio_cd, 0, 0, 0, 0, pv+0, 0, 0},
{&azalia_ca, &azalia_cd, 0, 0, 0, 0, pv+1, 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) Genode::Signal_context_capability announce_sigh)
{ {
Bsd::mem_init(env, alloc); Bsd::mem_init(env, alloc);
Bsd::irq_init(env.ep(), alloc);
Bsd::timer_init(env); Bsd::timer_init(env);
static Task bsd_task(env, alloc, config, announce_sigh); 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/log.h>
#include <base/object_pool.h> #include <base/object_pool.h>
#include <dataspace/client.h> #include <dataspace/client.h>
#include <io_port_session/connection.h> #include <platform_session/device.h>
#include <io_mem_session/connection.h> #include <platform_session/dma_buffer.h>
#include <legacy/x86/platform_session/connection.h>
#include <legacy/x86/platform_device/client.h>
#include <util/retry.h> #include <util/retry.h>
/* local includes */ /* local includes */
#include <audio/audio.h>
#include <bsd.h> #include <bsd.h>
#include <bsd_emul.h> #include <bsd_emul.h>
#include <scheduler.h>
#include <extern_c_begin.h> #include <extern_c_begin.h>
# include <dev/pci/pcidevs.h> # include <dev/pci/pcidevs.h>
#include <extern_c_end.h> #include <extern_c_end.h>
static constexpr bool debug = false;
extern "C" int probe_cfdata(struct pci_attach_args *); extern "C" int probe_cfdata(struct pci_attach_args *);
static void run_irq(void *args);
namespace { 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: private:
Genode::Env &_env; enum { DMA_SIZE = 1024 * 1024 };
Genode::Allocator &_alloc;
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 }; struct pci_attach_args _pa { 0, 0, 0, 0, 0 };
Platform::Connection _pci { _env }; void _irq_handle()
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
{ {
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; void _handle_device_list() { /* intentionally left empty */ }
Genode::addr_t mapped_base;
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; bool device_list = false;
while (!device_list) {
Dma_region_manager(Genode::Env &env, pci.update();
Genode::Allocator &alloc, pci.with_xml([&] (Xml_node & xml) {
Pci_driver &drv) if (xml.num_sub_nodes()) {
: Genode::Allocator_avl(&alloc), env(env), _drv(drv) { } pci.sigh(Signal_context_capability());
if (handler.constructed())
Genode::addr_t alloc(Genode::size_t size, unsigned align) handler.destruct();
{ device_list = true;
using namespace Genode; return;
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;
} }
_dma_initialized = true;
}
return Allocator_avl::alloc_aligned(size, align).convert<Genode::addr_t>( if (!handler.constructed()) {
[&] (void *ptr) { return (addr_t)ptr; }, handler.construct(ep, *this,
[&] (Alloc_error) { return 0UL; }); &Pci_driver::_handle_device_list);
} pci.sigh(*handler);
}
void free(Genode::addr_t virt, Genode::size_t size) { ep.wait_and_dispatch_one_io_signal();
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;
}); });
} }
/**
* Get physical address for DMA dataspace
*/
Genode::addr_t _dma_addr(Genode::Ram_dataspace_capability ds_cap)
{
return _pci.dma_addr(ds_cap);
} }
public: public:
Pci_driver(Genode::Env &env, Genode::Allocator &alloc) Pci_driver(Genode::Env &env, Genode::Allocator & alloc)
: :
_env(env), _alloc(alloc), _env(env), _alloc(&alloc)
_dma_region_manager(_env, _alloc, *this) {
{ } _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; } uint16_t sub_device_id() { return _sub_device_id; }
uint16_t sub_vendor_id() { return _sub_vendor_id; }
Platform::Device_capability cap() { return _cap; }
Platform::Connection &pci() { return _pci; }
int probe() int probe()
{ {
using namespace Genode;
_pci.upgrade_ram(8*1024); _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_dmat = (bus_dma_tag_t)this;
_pa.pa_pc = (pci_chipset_tag_t)this; _pa.pa_pc = (pci_chipset_tag_t)this;
int found = 0; bool found = false;
while ((_cap = _scan_pci(_cap)).valid()) { _pci.update();
Platform::Device_client device(_cap); _pci.with_xml([&] (Xml_node node) {
node.for_each_sub_node("device", [&] (Xml_node node)
{
if (found) return;
uint8_t bus, dev, func; String<16> name = node.attribute_value("name", String<16>());
device.bus_address(&bus, &dev, &func);
if ((device.device_id() == PCI_PRODUCT_INTEL_CORE4G_HDA_2) || node.with_optional_sub_node("pci-config", [&] (Xml_node node)
(device.vendor_id() == PCI_VENDOR_INTEL && {
bus == 0 && dev == 3 && func == 0)) { _vendor_id = node.attribute_value("vendor_id", 0U);
Genode::warning("ignore ", (unsigned)bus, ":", (unsigned)dev, ":", _device_id = node.attribute_value("device_id", 0U);
(unsigned)func, ", not supported HDMI/DP HDA device"); _class_code = node.attribute_value("class", 0U);
continue; _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 */ if ((_device_id == PCI_PRODUCT_INTEL_CORE4G_HDA_2) ||
_pa.pa_tag = 0x80000000UL | (bus << 16) | (dev << 11) | (func << 8); (_vendor_id == PCI_VENDOR_INTEL && name == "00:03.0")) {
_pa.pa_class = device.class_code() << 8; warning("ignore ", name,
_pa.pa_id = device.vendor_id() | device.device_id() << 16; ", not supported HDMI/DP HDA device");
return;
}
if (probe_cfdata(&_pa)) { /* we only construct the first useable device we find */
found++; _device.construct(_pci, name);
break; _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;
} }
/************************** void irq_handler(intrh_t handler, void * arg)
** Bus_driver interface ** {
**************************/ _irq_func = handler;
_irq_arg = arg;
}
Genode::Irq_session_capability irq_session() override { void handle_irq()
return Platform::Device_client(_cap).irq(0); } {
_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 { Genode::addr_t mmio_base() { return _device->mmio.base(); }
return _dma_region_manager.virt_to_phys(virt); } Genode::size_t mmio_size() { return _device->mmio.size(); }
Genode::addr_t phys_to_virt(Genode::addr_t phys) override { template <typename T>
return _dma_region_manager.phys_to_virt(phys); } 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; Pci_driver * drv = (Pci_driver*) pc;
virtual unsigned read_2(unsigned long address) = 0; drv->irq_handler(intrh, intarg);
virtual unsigned read_4(unsigned long address) = 0; return drv;
}
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; }
};
} /* anonymous namespace */ } /* 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) 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 */ /* calculate BAR from given register */
int r = (reg - 0x10) / 4; int r = (reg - 0x10) / 4;
Pci_driver *drv = (Pci_driver*)pa->pa_pc; if (r) {
Genode::error("MAP BAR ", r, " not implemented yet");
Platform::Device_capability cap = drv->cap(); return -1;
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;
}
} }
*handlep = res.base(); Pci_driver *drv = (Pci_driver*)pa->pa_pc;
*tagp = (Genode::addr_t)drv;
*handlep = drv->mmio_base();
if (basep != 0) if (basep != 0)
*basep = res.base(); *basep = drv->mmio_base();
if (sizep != 0) if (sizep != 0)
*sizep = maxsize > 0 && res.size() > maxsize ? maxsize : res.size(); *sizep = maxsize > 0 && drv->mmio_size() > maxsize ? maxsize : drv->mmio_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);
});
return 0; 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) extern "C" pcireg_t pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{ {
Pci_driver *drv = (Pci_driver *)pc; 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, extern "C" void pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg,
pcireg_t val) pcireg_t val)
{ {
Pci_driver *drv = (Pci_driver *)pc; if (debug)
Platform::Device_client device(drv->cap()); Genode::warning("Ignore writing of PCI config space @ ",
return device.config_write(reg, val, Platform::Device::ACCESS_32BIT); 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_space_handle_t handle,
bus_size_t offset) bus_size_t offset)
{ {
Bus_space *bus = (Bus_space*)space; return ((Pci_driver*)space)->read<Genode::uint8_t>(offset);
return bus->read_1(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_space_handle_t handle,
bus_size_t offset) bus_size_t offset)
{ {
Bus_space *bus = (Bus_space*)space; return ((Pci_driver*)space)->read<Genode::uint16_t>(offset);
return bus->read_2(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_space_handle_t handle,
bus_size_t offset) bus_size_t offset)
{ {
Bus_space *bus = (Bus_space*)space; return ((Pci_driver*)space)->read<Genode::uint32_t>(offset);
return bus->read_4(offset);
} }
@ -473,8 +389,7 @@ extern "C" void bus_space_write_1(bus_space_tag_t space,
bus_space_handle_t handle, bus_space_handle_t handle,
bus_size_t offset, u_int8_t value) bus_size_t offset, u_int8_t value)
{ {
Bus_space *bus = (Bus_space*)space; ((Pci_driver*)space)->write(offset, value);
bus->write_1(offset, value);
} }
@ -482,8 +397,7 @@ extern "C" void bus_space_write_2(bus_space_tag_t space,
bus_space_handle_t handle, bus_space_handle_t handle,
bus_size_t offset, u_int16_t value) bus_size_t offset, u_int16_t value)
{ {
Bus_space *bus = (Bus_space*)space; ((Pci_driver*)space)->write(offset, value);
bus->write_2(offset, value);
} }
@ -491,8 +405,7 @@ extern "C" void bus_space_write_4(bus_space_tag_t space,
bus_space_handle_t handle, bus_space_handle_t handle,
bus_size_t offset, u_int32_t value) bus_size_t offset, u_int32_t value)
{ {
Bus_space *bus = (Bus_space*)space; ((Pci_driver*)space)->write(offset, value);
bus->write_4(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, 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) 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; Genode::addr_t virt = (Genode::addr_t)buf;
dmam->dm_segs[0].ds_addr = drv->virt_to_phys(virt); 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, bus_size_t boundary, bus_dma_segment_t *segs, int nsegs,
int *rsegs, int flags) 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)); Genode::addr_t virt = drv->alloc(size, Genode::log2(alignment));
if (virt == 0) 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) 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++) { for (int i = 0; i < nsegs; i++) {
Genode::addr_t phys = (Genode::addr_t)segs[i].ds_addr; 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; 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 phys = (Genode::addr_t)segs[0].ds_addr;
Genode::addr_t virt = drv->phys_to_virt(phys); Genode::addr_t virt = drv->phys_to_virt(phys);