diff --git a/README b/README index 05f02c8e2..e67211649 100644 --- a/README +++ b/README @@ -147,9 +147,14 @@ The Genode source tree is composed of the following subdirectories: :'dde_ipxe': - This source-code repository contains the device driver environment for + This source-code repository contains the device-driver environment for executing drivers of the iPXE project. +:'dde_oss': + + This source-code repository contains the device-driver environment for the + audio drivers of the Open Sound System (OSS). + :'qt4': This source-code repository contains the Genode version of Qt4 framework. diff --git a/dde_oss/Makefile b/dde_oss/Makefile new file mode 100644 index 000000000..24e91482d --- /dev/null +++ b/dde_oss/Makefile @@ -0,0 +1,124 @@ +# +# \brief Download and setup OSS sources +# \author Sebastian Sumpf +# \date 2012-08-30 + +CONTRIB_DIR = contrib +DOWNLOAD_DIR = download +VERBOSE ?= @ +ECHO = @echo +CC ?= gcc +PATCHES := $(shell find patches -name \*.patch) + +OSS = oss-v4.2-build2006-src-bsd +OSS_TBZ2 = $(OSS).tar.bz2 +OSS_URL = http://www.4front-tech.com/developer/sources/stable/bsd/$(OSS_TBZ2) + + +# needed for preparation +CONTENT_SETUP = $(addprefix setup/,srcconf.c srcconf_freebsd.inc srcconf_vxworks.inc gen_driver_freebsd.inc) +CONTENT += $(CONTENT_SETUP) + +# oss framework +CONTENT_FRAMEWORK = $(addprefix kernel/framework/include/,oss_config.h oss_memblk.h \ + oss_version.h audio_core.h mixer_core.h oss_calls.h \ + internal.h oss_pci.h spdif.h midi_core.h grc3.h ac97.h \ + ossddk/oss_exports.h ossddk/oss_limits.PHh ossddk/ossddk.h) +CONTENT += $(CONTENT_FRAMEWORK) + +# oss core +CONTENT_CORE = $(addprefix kernel/framework/osscore/,oss_memblk.c oss_core_options.c \ + oss_core_services.c) +CONTENT += $(CONTENT_CORE) +CONTENT += include/soundcard.h kernel/drv/.config + +# audio core +CONTENT_AUDIO = $(addprefix kernel/framework/audio/,oss_audio_core.c oss_spdif.c oss_audiofmt.c \ + ulaw.h audiocnv.inc oss_grc3.c fltdata2_h.inc \ + grc3code.inc grc3inc.inc) +CONTENT += $(CONTENT_AUDIO) + +# mixer core +CONTENT_MIXER = $(addprefix kernel/framework/mixer/,oss_mixer_core.c mixerdefs.h) +CONTENT += $(CONTENT_MIXER) + +# vmixer core +CONTENT_VMIX = $(addprefix kernel/framework/vmix_core/,vmix_core.c vmix_input.c vmix.h db_scale.h \ + vmix_import.inc vmix_import_int.inc \ + rec_export.inc rec_export_int.inc \ + vmix_output.c outexport.inc outexport_int.inc \ + playmix.inc playmix_int.inc playmix_src.inc) +CONTENT += $(CONTENT_VMIX) + +# midi core +CONTENT_MIDI = $(addprefix kernel/framework/midi/,oss_midi_core.c oss_midi_timers.c oss_midi_mapper.c \ + oss_midi_queue.c) +CONTENT += $(CONTENT_MIDI) + +# AC97 core +CONTENT += kernel/framework/ac97 + +# drivers +CONTENT_DRV += oss_ich oss_hdaudio oss_audiopci +CONTENT += $(addprefix kernel/drv/,$(CONTENT_DRV)) + + +# +# Utility to check if a tool is installed +# +check_tool = $(if $(shell which $(1)),,$(error Need to have '$(1)' installed.)) + +$(call check_tool,wget) +$(call check_tool,patch) + +# +# Print help information by default +# +help: + $(ECHO) + $(ECHO) "Download integrate OSS sources with Genode" + $(ECHO) + $(ECHO) "--- available commands ---" + $(ECHO) "prepare - download and integrate OSS source code" + $(ECHO) "clean - remove contib sources except downloaded archives" + $(ECHO) "cleanall - remove contib sources and downloaded archives" + $(ECHO) + +# +# Build and execute 'srcconf' utility, build 'devices.list' +# +setup: + $(VERBOSE)ln -sf srcconf_freebsd.inc $(CONTRIB_DIR)/setup/srcconf_linux.inc + $(VERBOSE)ln -sf gen_driver_freebsd.inc $(CONTRIB_DIR)/setup/gen_driver_linux.inc + $(VERBOSE)mkdir -p $(CONTRIB_DIR)/kernel/framework/include + $(VERBOSE)mkdir -p $(CONTRIB_DIR)/kernel/OS/Linux + $(VERBOSE)$(CC) -g -I$(CONTRIB_DIR)/setup -o srcconf $(CONTRIB_DIR)/setup/srcconf.c + $(VERBOSE)cat `find $(CONTRIB_DIR)/kernel/drv -name .devices`| grep -v '^#' > $(CONTRIB_DIR)/devices.list + $(VERBOSE)cd $(CONTRIB_DIR) && ../srcconf + $(VERBOSE)cd $(CONTRIB_DIR)/target/build ; for f in *.c; do mv $$f pci_$$f; done + $(VERBOSE)rm srcconf + +prepare: $(CONTRIB_DIR)/.prepared setup + +$(CONTRIB_DIR)/.prepared: Makefile +$(CONTRIB_DIR)/.prepared: $(DOWNLOAD_DIR)/$(OSS_TBZ2) + $(ECHO) "extracting source code to '$(CONTRIB_DIR)'" + $(VERBOSE)tar xfj $< --transform "s/$(OSS)/$(CONTRIB_DIR)/" $(addprefix $(OSS)/,$(CONTENT)) + $(VERBOSE)touch $@ + $(ECHO) "applying patches to '$(CONTRIB_DIR)/'" + $(VERBOSE)for i in $(PATCHES); do patch -d $(CONTRIB_DIR) -p1 < $$i; done + + +$(DOWNLOAD_DIR): + $(VERBOSE)mkdir -p $@ + +$(DOWNLOAD_DIR)/$(OSS_TBZ2): $(DOWNLOAD_DIR) + $(ECHO) "downloading source code to '$@'" + $(VERBOSE)cd $(DOWNLOAD_DIR); wget -c $(OSS_URL) + $(VERBOSE)touch $@ + +clean: + $(VERBOSE)rm -rf $(CONTRIB_DIR) + +cleanall: clean + $(VERBOSE)rm -rf $(DOWNLOAD_DIR) diff --git a/dde_oss/README b/dde_oss/README new file mode 100644 index 000000000..a4c43ae3a --- /dev/null +++ b/dde_oss/README @@ -0,0 +1,37 @@ +This repository contains the Genode port of the +[http://http://www.4front-tech.com - Open Sound System] (OSS). + +OSS +### + +Usage +===== + +On first use, please call + +! make prepare + +on the top-level makefile of this repository. Also you need to make sure to +add the 'dde_oss' repository to your REPOSITORIES variable in 'etc/build.conf'. + +Example +======= + +An OSS demo configuration can be found under 'run/oss.run' and can be started +via + +! make run/oss + +from the Genode build directory. Be sure to adjust the 'filename' tag of the +'audio0' program. The file has to reside under: '/bin'. +The file format is header less two channel float 32 at 44100 Hz. You may use +the 'sox' utility to create these audio files: + +! sox -c 2 -r 44100 foo.mp3 foo.f32 + +Supported devices +================= + +Currently supported devices can be found in 'contrib/devices.list' in this +repository after preparation. + diff --git a/dde_oss/patches/hda_irq.patch b/dde_oss/patches/hda_irq.patch new file mode 100644 index 000000000..414baa246 --- /dev/null +++ b/dde_oss/patches/hda_irq.patch @@ -0,0 +1,13 @@ +diff -r aa33e3290f44 kernel/drv/oss_hdaudio/oss_hdaudio.c +--- a/kernel/drv/oss_hdaudio/oss_hdaudio.c Tue Nov 20 11:10:35 2012 +0100 ++++ b/kernel/drv/oss_hdaudio/oss_hdaudio.c Tue Nov 20 11:13:14 2012 +0100 +@@ -255,8 +255,7 @@ + + if (status & (1 << 30)) /* Controller interrupt (RIRB) */ + { +- if (rirb_intr (devc)) +- serviced = 1; ++ serviced = rirb_intr (devc); + } + } + return serviced; diff --git a/dde_oss/run/oss.run b/dde_oss/run/oss.run new file mode 100644 index 000000000..38cff0a70 --- /dev/null +++ b/dde_oss/run/oss.run @@ -0,0 +1,99 @@ + +if {![have_spec x86_32]} { + puts "\nOSS currently only supported on x86_32\n" + exit 0 +} + +# +# Build +# + +set build_components { + core init + drivers/timer + drivers/oss + drivers/pci + drivers/acpi + test/audio_out +} + +build $build_components + +create_boot_directory + +# +# Config +# + +append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sample.raw + + + + + + +} + +install_config $config + +# +# Boot modules +# + +set boot_modules { + core init timer pci_drv oss_drv acpi_drv sample.raw test-audio_out } + +build_boot_image $boot_modules + +append qemu_args " -m 256 -soundhw ac97 -nographic" + +run_genode_until forever + diff --git a/dde_oss/src/drivers/oss/driver.cc b/dde_oss/src/drivers/oss/driver.cc new file mode 100644 index 000000000..557650d02 --- /dev/null +++ b/dde_oss/src/drivers/oss/driver.cc @@ -0,0 +1,176 @@ +/* + * \brief Driver probing and registration + * \author Sebasitian Sumpf + * \date 2012-11-20 + */ + +/* + * Copyright (C) 2012 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. + */ + +#include +#include +#include + +extern "C" { +#include +#include +} + +#include + + +/** + * Driver management class + */ +class Driver +{ + private: + + enum { MAX_DRIVER = 10 }; + + oss_driver *_drivers[MAX_DRIVER + 1]; /* reigsted drivers */ + + Driver() + { + Genode::memset(_drivers, 0, sizeof(oss_driver *) * (MAX_DRIVER + 1)); + dde_kit_pci_init(); + } + + /** + * Match vendor product IDs + */ + bool _match(oss_driver *driver, Pci::Device_capability &cap) + { + Pci::Device_client client(cap); + + unsigned short vendor_id = client.vendor_id(); + unsigned short device_id = client.device_id(); + device_id_t *id_table = driver->id_table; + + for (int i = 0; id_table[i].vendor; i++) { + if (id_table[i].vendor == vendor_id && + id_table[i].product == device_id) { + PINF("Found card: vendor 0x%x: product: 0x%x driver: %s", + vendor_id, device_id, driver->name); + return true; + } + } + + return false; + } + + /** + * Probe device with registered drivers + */ + oss_driver *_probe_driver(Pci::Device_capability &cap) + { + for (int i = 0; _drivers[i]; i++) + if (_match(_drivers[i], cap)) + return _drivers[i]; + + return 0; + } + + public: + + static Driver *d() + { + static Driver _d; + return &_d; + } + + /** + * Register drivers + */ + void add(oss_driver *driver) + { + for (int i = 0; i < MAX_DRIVER; i++) + if (!_drivers[i]) { + _drivers[i] = driver; + return; + } + + PWRN("Driver limit of %d reached", MAX_DRIVER); + } + + /** + * Probe PCI devices with registered drivers + */ + void probe() + { + using namespace Genode; + Pci::Connection pci; + Pci::Device_capability cap = pci.first_device(); + Pci::Device_capability old; + oss_driver *driver = 0; + + while (cap.valid()) { + + Pci::Device_client client(cap); + + /* check for audio device class */ + enum { + CLASS_MASK = 0xff0000, + CLASS_MULTIMEDIA = 0x40000, /* class multimedia */ + }; + + /* + * Just test for multimedia class, since some devices (e.g., intel + * hda) do set the subclass to something different then audio (0x1). + */ + bool audio = (client.class_code() & CLASS_MASK) == CLASS_MULTIMEDIA; + if (audio && (driver = _probe_driver(cap))) { + + uint8_t bus, dev, func; + client.bus_address(&bus, &dev, &func); + + /* setup oss device */ + oss_device_t *ossdev = new (env()->heap()) oss_device_t; + Genode::memset(ossdev, 0, sizeof(oss_device_t)); + + ossdev->bus = bus; + ossdev->dev = dev; + ossdev->fun = func; + + /* set I/O resources */ + for (int i = 0; i < Pci::Device::NUM_RESOURCES; i++) { + Pci::Device::Resource res = client.resource(i); + ossdev->res[i].base = res.base(); + ossdev->res[i].size = res.size(); + ossdev->res[i].io = res.type() == Pci::Device::Resource::IO ? 1 : 0; + } + + ossdev->drv = driver; + + /* set quirks */ + setup_quirks(driver); + driver->attach(ossdev); + } + old = cap; + cap = pci.next_device(cap); + pci.release_device(old); + } + } +}; + + +/***************** + ** C interface ** + *****************/ + +extern "C" void register_driver(struct oss_driver *driver) +{ + dde_kit_log(VERBOSE_OSS, "Register driver: %s", driver->name); + Driver::d()->add(driver); +} + + +extern "C" void probe_drivers(void) +{ + Driver::d()->probe(); +} + diff --git a/dde_oss/src/drivers/oss/dummies.c b/dde_oss/src/drivers/oss/dummies.c new file mode 100644 index 000000000..9a7cb2bde --- /dev/null +++ b/dde_oss/src/drivers/oss/dummies.c @@ -0,0 +1,67 @@ +/* + * \brief OSS-dummy functions + * \author Sebastian Sumpf + * \date 20120-11-20 + */ + +/* + * Copyright (C) 20120-2012 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. + */ + +#include +#include + + +/** + * PCI + */ + +int pci_write_config_dword (oss_device_t * osdev, offset_t where, unsigned int val) +{ TRACE; return 0; } + + +/** + * OSS + */ + +void oss_pci_byteswap (oss_device_t * osdev, int mode) { } + + +int oss_register_device (oss_device_t * osdev, const char *name) +{ TRACE; return 0; } + + +void oss_unregister_device (oss_device_t * osdev) +{ TRACE; } + + +int oss_disable_device (oss_device_t * osdev) +{ TRACE; return 0; } + + +void oss_unregister_interrupts (oss_device_t * osdev) +{ TRACE; } + + +void * oss_get_osid (oss_device_t * osdev) { TRACE; return 0; } + + +int oss_get_cardinfo (int cardnum, oss_card_info * ci) { TRACE; return 0; } + + +oss_device_t * osdev_clone (oss_device_t * orig_osdev, int new_instance) { TRACE; return 0; } + + +timeout_id_t timeout (void (*func) (void *), void *arg, unsigned long long ticks) { TRACE; return 0; } + + +void untimeout(timeout_id_t id) { TRACE; } + + +/* wait queues */ + +void oss_reset_wait_queue (struct oss_wait_queue *wq) { } + diff --git a/dde_oss/src/drivers/oss/include/audio.h b/dde_oss/src/drivers/oss/include/audio.h new file mode 100644 index 000000000..d2225a075 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/audio.h @@ -0,0 +1,27 @@ +/* + * \brief Audio handling + * \author Sebastian Sumpf + * \date 2012-11-20 + */ + +/* + * Copyright (C) 2012 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__AUDIO_H_ +#define _INCLUDE__AUDIO_H_ + +/** + * Initialize audio if device is present + */ +int audio_init(); + +/** + * Play data of size + */ +int audio_play(short *data, int size); + +#endif /* _INCLUDE__AUDIO_H_ */ diff --git a/dde_oss/src/drivers/oss/include/buildid.h b/dde_oss/src/drivers/oss/include/buildid.h new file mode 100644 index 000000000..2997587d8 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/buildid.h @@ -0,0 +1 @@ +/* dummy */ diff --git a/dde_oss/src/drivers/oss/include/devid.h b/dde_oss/src/drivers/oss/include/devid.h new file mode 100644 index 000000000..6b54e1ab6 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/devid.h @@ -0,0 +1,23 @@ +/* + * \brief Operating system specific device id structure + * \author Sebastian Sumpf + * \date 2012-11-20 + */ + +/* + * Copyright (C) 2012 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__DEVID_H_ +#define _INCLUDE__DEVID_H_ + +typedef struct +{ + unsigned short vendor; + unsigned short product; +} device_id_t; + +#endif /* _INCLUDE__DEVID_H_ */ diff --git a/dde_oss/src/drivers/oss/include/inttypes.h b/dde_oss/src/drivers/oss/include/inttypes.h new file mode 100644 index 000000000..a90db857d --- /dev/null +++ b/dde_oss/src/drivers/oss/include/inttypes.h @@ -0,0 +1,19 @@ +/* + * \brief Included by OSS code + * \author Sebastian Sumpf + * \date 2012-11-20 + */ + +/* + * Copyright (C) 2012 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__INTTYPES_H_ +#define _INCLUDE__INTTYPES_H_ + +#include + +#endif /* _INCLUDE__INTTYPES_H_ */ diff --git a/dde_oss/src/drivers/oss/include/os.h b/dde_oss/src/drivers/oss/include/os.h new file mode 100644 index 000000000..d814203d5 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/os.h @@ -0,0 +1,324 @@ +/* + * \brief OS specific definitions + * \author Sebastian Sumpf + * \date 2012-10-19 + */ + +/* + * Copyright (C) 2012 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__OS_H_ +#define _INCLUDE__OS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* DDE kit includes */ +#include +#include +#include +#include +#include + +/* OSS includes */ +#include + +#include + +#define VERBOSE_OSS 0 + + +/******************* + ** Configuration ** + *******************/ + +/* not really used */ +#define OS_VERSION +#define OSS_LICENSE "BSD" +#define OSS_BUILD_ID "" +#define OSS_COMPILE_DATE __DATE__ +#define NULL 0 + + +/*********** + ** Types ** + ***********/ + +typedef dde_kit_uint64_t oss_uint64_t; +typedef dde_kit_int64_t oss_int64_t; +typedef dde_kit_addr_t oss_native_word; +typedef void * oss_dma_handle_t; +typedef int oss_mutex_t; +typedef int oss_poll_event_t; + +typedef dde_kit_size_t size_t; +typedef void dev_info_t; +typedef int pid_t; +typedef dde_kit_addr_t offset_t; +typedef dde_kit_addr_t addr_t; +typedef unsigned timeout_id_t; + +typedef dde_kit_uint8_t uint8_t; +typedef dde_kit_uint16_t uint16_t; +typedef dde_kit_int16_t int16_t; +typedef dde_kit_uint32_t uint32_t; +typedef dde_kit_int32_t int32_t; +typedef dde_kit_uint64_t uint64_t; +typedef dde_kit_int64_t int64_t; + + +struct fileinfo +{ + int mode; + int acc_flags; +}; + +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1 : 0) + +enum uio_rw { UIO_READ, UIO_WRITE }; + +/* + * IO vector + */ +typedef struct uio +{ + char *data; + size_t size; + enum uio_rw rw; +} uio_t; + +/* + * Copy uio vector data to address + */ +int uiomove (void *address, size_t nbytes, enum uio_rw rwflag, uio_t * uio_p); + +/* + * Debugging + */ +enum { + CE_PANIC = 1, + CE_WARN = 2, + CE_NOTE = 3, + CE_CONT = 4, +}; + +#define cmn_err(level, format, arg...) \ +{ \ + if (level < VERBOSE_OSS) \ + dde_kit_printf(format, ##arg); \ +} + +#define DDB(x) x + + +/*********** + ** Posix ** + ***********/ + +/************** + ** string.h ** + **************/ + +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); + + +int strcmp(const char *s1, const char *s2); +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, size_t n); +size_t strlen(const char *); + + +/************* + ** stdio.h ** + *************/ + +int sprintf(char *str, const char *format, ...); + + +/************* + ** fcntl.h ** + *************/ + +enum { + O_ACCMODE = 0x3, + O_NONBLOCK = 0x40000, +}; + + +/************ + ** poll.h ** + ************/ + +enum { + POLLIN, + POLLRDNORM, + POLLWRNORM, + POLLOUT, +}; + + +/********* + ** OSS ** + *********/ + +#define TRACE \ +{ \ + if (VERBOSE_OSS) \ + dde_kit_printf("\033[32m%s\033[0m called, not implemented\n", __PRETTY_FUNCTION__); \ +} + +#define TRACEN(name) \ +{ \ + if (VERBOSE_OSS) \ + dde_kit_printf("\033[32m%s\033[0m called, not implemented\n", name); \ +} + +#define HZ DDE_KIT_HZ +#define GET_JIFFIES() jiffies + + +#define MUTEX_INIT(osdev, mutex, hier) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) { flags = 1; } +#define MUTEX_ENTER(mutex, flags) { flags = 1; } +#define MUTEX_EXIT(mutex, flags) { flags--; } +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) { flags--; } +#define MUTEX_CLEANUP(mutex) + + +#define GET_PROCESS_NAME(f) NULL +#define GET_PROCESS_PID(p) -1 + +#define KERNEL_MALLOC(size) (dde_kit_large_malloc(size)) +#define KERNEL_FREE(ptr) (dde_kit_large_free(ptr)) + +void * dma_alloc(oss_native_word *phys, size_t size); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) dma_alloc(phaddr, sz) +#define CONTIG_FREE(osdev, p, sz, handle) KERNEL_FREE(p) + + +/* + * OSS device */ +struct resource +{ + unsigned base; + unsigned size; + unsigned io; +}; + +struct _oss_device_t +{ + void *devc; + int cardnum; + int available; + char *hw_info; + char nick[32]; + char handle[32]; + /* audio */ + int num_audio_engines; + int num_audiorec; + int num_audioduplex; + + /* mixer */ + int num_mixerdevs; + + /* midi */ + int num_mididevs; + + /* PCI/IRQ */ + int bus, dev, fun; + oss_tophalf_handler_t irq_top; + oss_bottomhalf_handler_t irq_bottom; + + struct resource res[5]; + + int first_mixer; /* This must be set to -1 by osdev_create() */ + + struct oss_driver *drv; /* driver for this device */ +}; + +/* + * I/O mem/ports + */ +void * pci_map(struct _oss_device_t *osdev, int resource, addr_t phys, size_t size); + +#define MAP_PCI_MEM(osdev, ix, phaddr, size) pci_map(osdev, ix, phaddr, size) + +oss_native_word pci_map_io(struct _oss_device_t *osdev, int resource, unsigned base); + +#define MAP_PCI_IOADDR(osdev, nr, io) pci_map_io(osdev, nr, io) + +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) TRACEN("UNMAP_PCI_MEM") +#define UNMAP_PCI_IOADDR(osdev, ix) {} + + +struct oss_driver +{ + char *name; + device_id_t *id_table; + int (*attach)(struct _oss_device_t *osdev); + int (*detach)(struct _oss_device_t *osdev); + unsigned char (*inb_quirk)(struct _oss_device_t *osdev, addr_t port); +}; + +/* + * Used for blocking, is unblocked during IRQs + */ +struct oss_wait_queue +{ + int blocked; +}; + +void oss_udelay(unsigned long ticks); + +timeout_id_t timeout (void (*func) (void *), void *arg, unsigned long long ticks); +void untimeout(timeout_id_t id); + + +/********* + ** PCI ** + *********/ + +#define PCI_READB(odev, addr) (*(volatile unsigned char *)(addr)) +#define PCI_WRITEB(osdev, addr, value) (*(volatile unsigned char *)(addr) = (value)) +#define PCI_READW(odev, addr) (*(volatile unsigned short *)(addr)) +#define PCI_WRITEW(osdev, addr, value) (*(volatile unsigned short *)(addr) = (value)) +#define PCI_READL(odev, addr) (*(volatile unsigned int *)(addr)) +#define PCI_WRITEL(osdev, addr, value) (*(volatile unsigned int *)(addr) = (value)) + + +/************* + ** Port IO ** + *************/ + +unsigned char io_inb(struct _oss_device_t *osdev, addr_t port); + +#define INB(osdev, port) io_inb(osdev, port) +#define INW(osdev, port) dde_kit_inw(port) +#define INL(osdev, port) dde_kit_inl(port) + +#define OUTB(osdev, val, port) dde_kit_outb(port, val) +#define OUTW(osdev, val, port) dde_kit_outw(port, val) +#define OUTL(osdev, val, port) dde_kit_outl(port, val) + + +/********************** + ** Genode interface ** + **********************/ + +void register_driver(struct oss_driver *driver); +void probe_drivers(void); +void request_irq(unsigned irq, struct _oss_device_t *osdev); + + +#ifdef __cplusplus +} +#endif + +#endif /* _INCLUDE__OS_H_ */ diff --git a/dde_oss/src/drivers/oss/include/quirks.h b/dde_oss/src/drivers/oss/include/quirks.h new file mode 100644 index 000000000..ba2e4c32d --- /dev/null +++ b/dde_oss/src/drivers/oss/include/quirks.h @@ -0,0 +1,24 @@ +/* + * \brief Driver quirks interface + * \author Sebastian Sumpf + * \date 2012-11-27 + */ + +/* + * Copyright (C) 2012 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__QUIRKS_H_ +#define _INCLUDE__QUIRKS_H_ + +struct oss_driver; + +/** + * Check and possibly set quirks for given driver + */ +void setup_quirks(struct oss_driver *drv); + +#endif /* _INCLUDE__QUIRKS_H_ */ diff --git a/dde_oss/src/drivers/oss/include/signal.h b/dde_oss/src/drivers/oss/include/signal.h new file mode 100644 index 000000000..dc72dd303 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/signal.h @@ -0,0 +1,102 @@ +/* + * \brief Main-signal receiver and signal-helper functions + * \author Sebastian Sumpf + * \date 2012-05-23 + */ + +/* + * Copyright (C) 2012 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 _SIGNAL_H_ +#define _SIGNAL_H_ + +#include +#include +#include + + +/** + * Context base for IRQ, Timer, etc. + */ +class Driver_context : public Genode::Signal_context +{ + public: + + /** + * Perform context operation + */ + virtual void handle() = 0; + + virtual char const *debug() = 0; +}; + + +/** + * This singelton currently received all signals + */ +class Service_handler +{ + + private: + + Genode::Signal_receiver *_receiver; + + Service_handler() { } + + public: + + static Service_handler * s() + { + static Service_handler _s; + return &_s; + } + + void receiver(Genode::Signal_receiver *recv) { _receiver = recv; } + + void check_signal(bool block = true) + { + while (_receiver->pending() || block) { + + Genode::Signal s = _receiver->wait_for_signal(); + + /* handle signal IRQ, timer, or event signals */ + Driver_context *ctx = static_cast(s.context()); + ctx->handle(); + block = false; + } + } +}; + + +/** + * Helper that holds sender and receiver + */ +class Signal_helper +{ + private: + + Genode::Signal_receiver *_receiver; + Genode::Signal_transmitter *_sender; + + public: + + Signal_helper(Genode::Signal_receiver *recv) + : _receiver(recv), + _sender(new (Genode::env()->heap()) Genode::Signal_transmitter()) { } + + Genode::Signal_receiver *receiver() const { return _receiver; } + Genode::Signal_transmitter *sender() const { return _sender; } +}; + + +namespace Irq +{ + void init(Genode::Signal_receiver *recv); + void check_irq(bool block = false); +} + +#endif /* _SIGNAL_H_ */ diff --git a/dde_oss/src/drivers/oss/include/sys/ioctl.h b/dde_oss/src/drivers/oss/include/sys/ioctl.h new file mode 100644 index 000000000..2997587d8 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/sys/ioctl.h @@ -0,0 +1 @@ +/* dummy */ diff --git a/dde_oss/src/drivers/oss/include/timestamp.h b/dde_oss/src/drivers/oss/include/timestamp.h new file mode 100644 index 000000000..2997587d8 --- /dev/null +++ b/dde_oss/src/drivers/oss/include/timestamp.h @@ -0,0 +1 @@ +/* dummy */ diff --git a/dde_oss/src/drivers/oss/main.cc b/dde_oss/src/drivers/oss/main.cc new file mode 100644 index 000000000..dd1deb550 --- /dev/null +++ b/dde_oss/src/drivers/oss/main.cc @@ -0,0 +1,294 @@ +/* + * \brief Audio-session-entry point + * \author Sebastian Sumpf + * \date 2012-11-20 + */ + +/* + * Copyright (C) 2012 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. + */ + +extern "C" { +#include +} + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Genode; + +static const bool verbose = false; +static bool audio_out_active = false; + + +/** + * Packet-stream signal dispatcher + * + * TODO: Move to generic code + */ +template +class Signal_dispatcher : public Driver_context, + public Genode::Signal_context_capability +{ + private: + + T &obj; + void (T::*member) (); + Genode::Signal_receiver *sig_rec; + + public: + + /** + * Constructor + * + * \param sig_rec signal receiver to associate the signal + * handler with + * \param obj,member object and member function to call when + * the signal occurs + */ + Signal_dispatcher(Genode::Signal_receiver *sig_rec, + T &obj, void (T::*member)()) + : + Genode::Signal_context_capability(sig_rec->manage(this)), + obj(obj), member(member), + sig_rec(sig_rec) + { } + + ~Signal_dispatcher() { sig_rec->dissolve(this); } + + void handle() { (obj.*member)(); } + char const *debug() { return "Signal_dispatcher"; } +}; + + +namespace Audio_out { + + class Session_component; + + enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS }; + + static Session_component *channel_acquired[MAX_CHANNELS]; + + class Session_component : public Session_rpc_object + { + private: + + Ram_dataspace_capability _ds; + Channel_number _channel; + + Signal_dispatcher _process_packet_dispatcher; + + Ram_dataspace_capability _alloc_dataspace(size_t size) + { + _ds = env()->ram_session()->alloc(size); + return _ds; + } + + void _process_packets() + { + /* handle audio-out packets */ + Session_component *left = channel_acquired[LEFT], + *right = channel_acquired[RIGHT]; + while (left->channel()->packet_avail() && + right->channel()->packet_avail() && + left->channel()->ready_to_ack() && + right->channel()->ready_to_ack()) { + + /* get packets for channels */ + Packet_descriptor p[MAX_CHANNELS]; + static short data[2 * PERIOD]; + p[LEFT] = left->channel()->get_packet(); + p[RIGHT] = right->channel()->get_packet(); + + /* convert float to S16LE */ + for (int i = 0; i < 2 * PERIOD; i += 2) { + data[i] = left->channel()->packet_content(p[LEFT])[i / 2] * 32767; + data[i + 1] = right->channel()->packet_content(p[RIGHT])[i / 2] * 32767; + } + + if (verbose) + PDBG("play packet"); + + /* send to driver */ + int err; + if (audio_out_active) + if ((err = audio_play(data, 4 * PERIOD))) + PWRN("Error %d during playback", err); + + /* acknowledge packet to the client */ + if (p[LEFT].valid()) + left->channel()->acknowledge_packet(p[LEFT]); + + if (p[RIGHT].valid()) + right->channel()->acknowledge_packet(p[RIGHT]); + } + } + + public: + + Session_component(Channel_number channel, size_t buffer_size, + Rpc_entrypoint &ep, Signal_receiver *sig_rec) + : Session_rpc_object(_alloc_dataspace(buffer_size), ep), _channel(channel), + _process_packet_dispatcher(sig_rec, *this, + &Session_component::_process_packets) + { + Audio_out::channel_acquired[_channel] = this; + Session_rpc_object::_channel.sigh_packet_avail(_process_packet_dispatcher); + Session_rpc_object::_channel.sigh_ready_to_ack(_process_packet_dispatcher); + } + + ~Session_component() + { + Audio_out::channel_acquired[_channel] = 0; + + env()->ram_session()->free(_ds); + } + + /********************************* + ** Audio-out-session interface ** + *********************************/ + + void flush() + { + while (channel()->packet_avail()) + channel()->acknowledge_packet(channel()->get_packet()); + } + + void sync_session(Session_capability audio_out_session) { } + }; + + static bool channel_number_from_string(const char *name, + Channel_number *out_number) + { + static struct Names { + const char *name; + Channel_number number; + } names[] = { + { "left", LEFT }, { "front left", LEFT }, + { "right", RIGHT }, { "front right", RIGHT }, + { 0, INVALID } + }; + + for (Names *n = names; n->name; ++n) + if (!Genode::strcmp(name, n->name)) { + *out_number = n->number; + return true; + } + + return false; + } + + /** + * Session creation policy for our service + */ + struct Root_policy + { + void aquire(const char *args) + { + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); + size_t buffer_size = + Arg_string::find_arg(args, "buffer_size").ulong_value(0); + size_t session_size = + align_addr(sizeof(Session_component), 12); + + if ((ram_quota < session_size) || + (buffer_size > ram_quota - session_size)) { + PERR("insufficient 'ram_quota', got %zd, need %zd", + ram_quota, buffer_size + session_size); + throw Root::Quota_exceeded(); + } + + char channel_name[16]; + Channel_number channel_number; + Arg_string::find_arg(args, "channel").string(channel_name, + sizeof(channel_name), + "left"); + if (!channel_number_from_string(channel_name, &channel_number)) + throw Root::Invalid_args(); + if (channel_acquired[channel_number]) + throw Root::Unavailable(); + } + + void release() { } + }; + + typedef Root_component Root_component; + + /* + * Root component, handling new session requests. + */ + class Root : public Root_component + { + private: + + Rpc_entrypoint &_channel_ep; + Signal_receiver *_sig_rec; + + protected: + + Session_component *_create_session(const char *args) + { + size_t buffer_size = + Arg_string::find_arg(args, "buffer_size").ulong_value(0); + + if (!audio_out_active) + throw Root::Unavailable(); + + char channel_name[16]; + Channel_number channel_number = INVALID; + Arg_string::find_arg(args, "channel").string(channel_name, + sizeof(channel_name), + "left"); + channel_number_from_string(channel_name, &channel_number); + + return new (md_alloc()) + Session_component(channel_number, buffer_size, _channel_ep, _sig_rec); + } + + public: + + Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, Signal_receiver *sig_rec) + : Root_component(session_ep, md_alloc), _channel_ep(*session_ep), _sig_rec(sig_rec) + { } + }; +} + + +int main() +{ + static Signal_receiver recv; + enum { STACK_SIZE = 1024 * sizeof(addr_t) }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep"); + + dde_kit_timer_init(0, 0); + + Irq::init(&recv); + Service_handler::s()->receiver(&recv); + + /* probe drivers */ + probe_drivers(); + audio_out_active = audio_init() ? false : true; + + if (audio_out_active) { + static Audio_out::Root audio_root(&ep, env()->heap(), &recv); + env()->parent()->announce(ep.manage(&audio_root)); + } + + while (true) + Service_handler::s()->check_signal(true); + + return 1; +} + diff --git a/dde_oss/src/drivers/oss/module.inc b/dde_oss/src/drivers/oss/module.inc new file mode 100644 index 000000000..4c98f63a6 --- /dev/null +++ b/dde_oss/src/drivers/oss/module.inc @@ -0,0 +1,44 @@ +/* + * \brief This file is included for every driver + * \author Sebastian Sumpf + * \date 2012-11-20 + * + * See: Files in 'target/build/', which are generated by 'srcconf' + */ + +/* + * Copyright (C) 2012 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. + */ + +#include + +/* Dummies */ +#define DEFINE_CLASS_0(a,b,c,d) +#define TUNABLE_INT(a,b) +#define DRIVER_MODULE(name, b, c, d, e, f) + +/* defined in specific driver file */ +extern int DRIVER_ATTACH(struct _oss_device_t *); +extern int DRIVER_DETACH(struct _oss_device_t *); + +/* setup driver structure */ +static struct oss_driver driver = +{ + .name = DRIVER_NICK, + .id_table = id_table, + .attach = DRIVER_ATTACH, + .detach = DRIVER_DETACH +}; + +/** + * Register driver upon startup + */ +static void __attribute__((constructor)) init(void) +{ + register_driver(&driver); +} + + diff --git a/dde_oss/src/drivers/oss/os.cc b/dde_oss/src/drivers/oss/os.cc new file mode 100644 index 000000000..9e089fec6 --- /dev/null +++ b/dde_oss/src/drivers/oss/os.cc @@ -0,0 +1,413 @@ +/* + * \brief OSS environment + * \author Sebastian Sumpf + * \date 2012-11-20 + */ + +/* + * Copyright (C) 2012 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. + */ +#include + +extern "C" +{ +#include +#include +#include +#include +#include +} + +#include +#include + +/****************** + ** oss_config.h ** + ******************/ + +int oss_num_cards = 0; + + +/********** + ** os.h ** + **********/ + +using namespace Genode; + +static Timer::Connection _timer; + +void oss_udelay(unsigned long usecs) +{ + unsigned long start = jiffies; + /* check for IRQs etc */ + Service_handler::s()->check_signal(false); + + unsigned delta = (jiffies - start) * 10000; + + /* return if already expired */ + if (delta > usecs) + return; + + usecs -= delta; + /* delay */ + _timer.msleep(usecs < 1000 ? 1 : usecs / 1000); +} + + +/********* + ** PCI ** + *********/ + +extern "C" int pci_read_config_byte(oss_device_t *osdev, offset_t where, unsigned char *val) +{ + dde_kit_pci_readb(osdev->bus, osdev->dev, osdev->fun, where, val); + return *val == ~0 ? -1 : 0; +} + + +extern "C" int pci_write_config_byte(oss_device_t *osdev, offset_t where, unsigned char val) +{ + dde_kit_pci_writeb(osdev->bus, osdev->dev, osdev->fun, where, val); + return 0; +} + + +extern "C" int pci_read_config_word(oss_device_t *osdev, offset_t where, unsigned short *val) +{ + dde_kit_pci_readw(osdev->bus, osdev->dev, osdev->fun, where, val); + return *val == ~0 ? -1 : 0; +} + + +extern "C" int pci_write_config_word(oss_device_t *osdev, offset_t where, unsigned short val) +{ + dde_kit_pci_writew(osdev->bus, osdev->dev, osdev->fun, where, val); + return 0; +} + + +extern "C" int pci_read_config_dword(oss_device_t *osdev, offset_t where, unsigned int *val) +{ + dde_kit_pci_readl(osdev->bus, osdev->dev, osdev->fun, where, val); + return *val == ~0U ? -1 : 0; +} + + +extern "C" int pci_read_config_irq(oss_device_t *osdev, offset_t where, unsigned char *val) +{ + int ret = pci_read_config_byte(osdev, where, val); + return ret; +} + + +extern "C" void *pci_map(oss_device_t *osdev, int resource, addr_t phys, size_t size) +{ + addr_t addr; + if (dde_kit_request_mem(phys, size, 0, &addr)) + return 0; + + return (void *)addr; +} + + +extern "C" oss_native_word pci_map_io(struct _oss_device_t *osdev, int resource, unsigned base) +{ + if (osdev->res[resource].io) + dde_kit_request_io(osdev->res[resource].base, osdev->res[resource].size); + + return base; +} + + +/************* + ** PORT/IO ** + *************/ + +unsigned char io_inb(struct _oss_device_t *osdev, addr_t port) +{ + return osdev->drv->inb_quirk ? osdev->drv->inb_quirk(osdev, port) + : dde_kit_inb(port); +} + +/********* + ** OSS ** + *********/ + +extern "C" int oss_register_interrupts(oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom) +{ + if (!osdev || intrnum != 0 || !top) { + PWRN("Bad interrupt index %d, bad device (%p), or bad handler %p", + intrnum, osdev, top); + return OSS_EINVAL; + } + + unsigned char irq; + pci_read_config_irq(osdev, PCI_INTERRUPT_LINE, &irq); + + /* setup bottom and top half handlers */ + osdev->irq_top = top; + osdev->irq_bottom = bottom; + + request_irq(irq, osdev); + + return 0; +} + + +extern "C" int uiomove (void *address, size_t nbytes, enum uio_rw rwflag, uio_t * uio) +{ + if (rwflag != uio->rw || nbytes > uio->size) + return -1; + + void *target = rwflag == UIO_READ ? (void *)uio->data : address; + void *source = rwflag == UIO_READ ? address : (void *)uio->data; + nbytes = nbytes > uio->size ? uio->size : nbytes; + Genode::memcpy(target, source, nbytes); + uio->size -= nbytes; + uio->data += nbytes; + + return 0; +} + + +/** + * Wait queues + */ +extern "C" int oss_sleep(struct oss_wait_queue *wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ + *status = 0; + + if (!wq) + return 0; + + wq->blocked = 1; + unsigned long start = jiffies; + + while (wq->blocked) { + Irq::check_irq(true); + + if (jiffies - start > (unsigned long)ticks) { + return 0; + } + } + + return 1; +} + + +extern "C" void oss_wakeup(struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + if (wq) + wq->blocked = 0; +} + + +extern "C" oss_wait_queue_t *oss_create_wait_queue(oss_device_t * osdev, const char *name) +{ + return new (env()->heap()) oss_wait_queue; +} + + +/********* + ** DMA ** + *********/ + +extern "C" void * dma_alloc(oss_native_word *phys, size_t size) +{ + void *virt = KERNEL_MALLOC(size); + *phys = (oss_native_word)dde_kit_pgtab_get_physaddr(virt); + + return virt; +} + + +extern "C" int __oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + /* allocate DMA buffer depending on flags */ + oss_native_word phys; + int size = 64 * 1024; + + if (dmap->dmabuf) + return 0; + + if (dmap->flags & DMAP_SMALLBUF) + size = SMALL_DMABUF_SIZE; + if (dmap->flags & DMAP_MEDIUMBUF) + size = MEDIUM_DMABUF_SIZE; + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + if (alloc_flags & DMABUF_LARGE) + size = 356 * 1024; + + if (!(dmap->dmabuf = (unsigned char*)dma_alloc(&phys, size))) + return OSS_ENOMEM; + + dmap->buffsize = size; + dmap->dmabuf_phys = phys; + + return 0; +} + + +void oss_free_dmabuf (int dev, dmap_p dmap) +{ + if (!dmap->dmabuf) + return; + + KERNEL_FREE(dmap->dmabuf); + dmap->dmabuf = 0; +} + + +/************** + ** string.h ** + **************/ + +extern "C" { + +size_t strlen(const char *s) +{ + return Genode::strlen(s); +} + + +char *strcpy(char *to, const char *from) +{ + char *save = to; + + for (; (*to = *from); ++from, ++to); + return(save); +} + + +char *strncpy(char *to, const char *from, size_t n) +{ + return Genode::strncpy(to, from, n); +} + + +int strcmp(const char *s1, const char *s2) +{ + return Genode::strcmp(s1, s2); +} + + +int sprintf(char *str, const char *format, ...) +{ + + va_list list; + va_start(list, format); + + String_console sc(str, 1024); + + sc.vprintf(format, list); + + va_end(list); + + return sc.len(); +} +} /* extern "C" */ + + +static oss_cdev_drv_t *dsp_drv; /* DSP device */ +static oss_cdev_drv_t *mix_drv; /* mixer */ + + +/** + * Internal ioctl + */ +int ioctl_dsp(int cmd, ioctl_arg arg) +{ + return dsp_drv->ioctl(0, 0, cmd, arg); +} + + +/** + * Setup DSP + */ +int audio_init() +{ + + if (!dsp_drv) { + PERR("No output devices"); + return -1; + } + + /* open device */ + int new_dev = 0; + if (dsp_drv->open(0, 0, 0, 0, 0, &new_dev)) { + PERR("Error opening sound card"); + return -2; + } + + /* set fragment policy (TODO: make configurable) */ + int policy = 8; + if (ioctl_dsp(SNDCTL_DSP_POLICY, &policy) == -1) + PERR("Error setting policy"); + + /* set format */ + int val = AFMT_S16_LE; + if (ioctl_dsp(SNDCTL_DSP_SETFMT, &val) == -1) { + PERR("Error setting audio format to S16_LE"); + return -3; + } + + /* set two channels */ + val = 2; + if (ioctl_dsp(SNDCTL_DSP_CHANNELS, &val) == -1) { + PERR("Error enabling two channels"); + return -4; + } + + /* set sample rate */ + val = 44100; + if (ioctl_dsp(SNDCTL_DSP_SPEED, &val) == -1) { + PERR("Error setting sample rate to %d HZ", val); + return -5; + } + + return 0; +} + + +int audio_play(short *data, int size) +{ + uio_t io = { (char *)data, size, UIO_WRITE }; + int ret; + + if ((ret = dsp_drv->write(0, 0, &io, size)) != size) + PERR("Error writing data s: %d r: %d func %p", size, ret, dsp_drv->write); + + Irq::check_irq(); + + return 0; +} + + +extern "C" void oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ + /* only look for first mixer and first dsp */ + if (instance != 0) + return; + + if (dev_class == OSS_DEV_DSP) { + PINF("Found dsp: '%s'", name ? name : ""); + dsp_drv = drv; + } + + if (dev_class == OSS_DEV_MIXER) { + PINF("Found mixer: '%s'", name ? name : ""); + mix_drv = drv; + } +} + diff --git a/dde_oss/src/drivers/oss/quirks.cc b/dde_oss/src/drivers/oss/quirks.cc new file mode 100644 index 000000000..0560ce922 --- /dev/null +++ b/dde_oss/src/drivers/oss/quirks.cc @@ -0,0 +1,47 @@ +/* + * \brief Drivers quirks + * \author Sebastian Sumpf + * \date 2012-11-27 + */ + +/* + * Copyright (C) 2012 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. + */ + +/* OSS includes */ +extern "C" { +#include +} + +#include + +/* Genode includes */ +#include + + +/** + * The Qemu es1370 emulation does not support 'inb' for the serial interface + * control register (ports 0x20 - 0x23) + */ +static unsigned char qemu_es1370_inb_quirk(_oss_device_t *osdev, addr_t port) +{ + unsigned base = osdev->res[0].base; + unsigned addr = port - base; + + if (addr < 0x20 && addr > 0x23) + return dde_kit_inb(port); + + unsigned val = dde_kit_inl(base + 0x20) >> (8 * (addr - 0x20)); + return val & 0xff; +} + + + +void setup_quirks(struct oss_driver *drv) +{ + if (!strcmp(drv->name, "oss_audiopci")) + drv->inb_quirk = qemu_es1370_inb_quirk; +} diff --git a/dde_oss/src/drivers/oss/signal/irq.cc b/dde_oss/src/drivers/oss/signal/irq.cc new file mode 100644 index 000000000..894168606 --- /dev/null +++ b/dde_oss/src/drivers/oss/signal/irq.cc @@ -0,0 +1,205 @@ +/* + * \brief Signal context for IRQ's + * \author Sebastian Sumpf + * \date 2012-05-23 + */ + +/* + * Copyright (C) 2012 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. + */ + +#include + +extern "C" { +#include +#include +} + +/* our local incarnation of sender and receiver */ +static Signal_helper *_signal = 0; +static Genode::Lock _irq_sync(Genode::Lock::LOCKED); +static Genode::Lock _irq_wait(Genode::Lock::LOCKED); + + +/** + * This contains the Linux-driver handlers + */ +struct Irq_handler : Genode::List::Element +{ + oss_device_t *osdev; /* OSS device */ + + Irq_handler(oss_device_t *osdev) + : osdev(osdev) {} +}; + + +/** + * Signal context for IRQs + */ +class Irq_context : public Driver_context, + public Genode::List::Element +{ + private: + + unsigned int _irq; /* IRQ number */ + Genode::List _handler_list; /* List of registered handlers */ + Genode::Signal_context_capability _ctx_cap; /* capability for this context */ + + static Genode::List *_list() + { + static Genode::List _l; + return &_l; + } + + /** + * Find context for given IRQ number + */ + static Irq_context *_find_ctx(unsigned int irq) + { + for (Irq_context *i = _list()->first(); i; i = i->next()) + if (i->_irq == irq) + return i; + + return 0; + } + + /* called by the DDE kit upon IRQ */ + static void _dde_handler(void *irq) + { + /* + * Make sure there is only one interrupt handled at a time, since dde_kit + * will use one thread per IRQ + */ + static Genode::Lock handler_lock; + Genode::Lock::Guard guard(handler_lock); + + /* unlock if main thread is waiting */ + _irq_wait.unlock(); + + Irq_context *ctx = static_cast(irq); + + /* set context & submit signal */ + _signal->sender()->context(ctx->_ctx_cap); + _signal->sender()->submit(); + + /* wait for interrupt to get acked at device side */ + _irq_sync.lock(); + } + + /** + * Call one IRQ handler + */ + inline bool _handle_one(Irq_handler *h) + { + bool handled = false; + + /* + * It might be that the next interrupt triggers right after the device has + * acknowledged the IRQ + */ + oss_device_t *osdev = h->osdev; + do { + if (!osdev->irq_top(osdev)) + return handled; + + if (osdev->irq_bottom) + osdev->irq_bottom(osdev); + + handled = true; + + } while (true); + } + + /** + * Call all handlers registered for this context + */ + bool _handle() + { + bool handled = false; + /* report IRQ to all clients */ + for (Irq_handler *h = _handler_list.first(); h; h = h->next()) { + + handled |= _handle_one(h); + dde_kit_log(0, "IRQ: %u ret: %u h: %p dev: %p", _irq, handled, h->osdev->irq_top, h->osdev); + } + /* interrupt should be acked at device now */ + _irq_sync.unlock(); + + return handled; + } + + public: + + Irq_context(unsigned int irq) + : _irq(irq), + _ctx_cap(_signal->receiver()->manage(this)) + { + /* register at DDE (shared) */ + int ret = dde_kit_interrupt_attach(_irq, 0, 0, _dde_handler, this); + if (ret) + PERR("Interrupt attach return %d for IRQ %u", ret, irq); + + _list()->insert(this); + } + + + inline void handle() { _handle(); } + const char *debug() { return "Irq_context"; } + + /** + * Request an IRQ + */ + static void request_irq(unsigned int irq, oss_device_t *osdev) + { + Irq_handler *h = new(Genode::env()->heap()) Irq_handler(osdev); + Irq_context *ctx = _find_ctx(irq); + + /* if this IRQ is not registered */ + if (!ctx) + ctx = new (Genode::env()->heap()) Irq_context(irq); + + /* register Linux handler */ + ctx->_handler_list.insert(h); + } + + static bool check_irq() + { + bool handled = false; + for (Irq_context *i = _list()->first(); i; i = i->next()) + handled |= i->_handle(); + + return handled; + } + + static void wait() + { + _irq_wait.lock(); + check_irq(); + } +}; + + +void Irq::init(Genode::Signal_receiver *recv) { + _signal = new (Genode::env()->heap()) Signal_helper(recv); } + + + +/*********************** + ** linux/interrupt.h ** + ***********************/ + +extern "C" void request_irq(unsigned int irq, oss_device_t *osdev) +{ + dde_kit_log(VERBOSE_OSS, "Request irq %u handler %p", irq, osdev->irq_top); + Irq_context::request_irq(irq, osdev); +} + + +void Irq::check_irq(bool block) +{ + if (!Irq_context::check_irq() && block) + Irq_context::wait(); +} diff --git a/dde_oss/src/drivers/oss/target.mk b/dde_oss/src/drivers/oss/target.mk new file mode 100644 index 000000000..df650d128 --- /dev/null +++ b/dde_oss/src/drivers/oss/target.mk @@ -0,0 +1,46 @@ +TARGET = oss_drv +REQUIRES = x86_32 +LIBS = cxx env dde_kit signal server +CONTRIB_DIR = $(REP_DIR)/contrib + + + +CC_OPT += -Ulinux -D_KERNEL -fno-omit-frame-pointer + +# +# Silence C code +# +CC_C_OPT = -Wno-unused-variable -Wno-unused-but-set-variable \ + -Wno-implicit-function-declaration \ + -Wno-sign-compare + +# +# Native Genode sources +# +SRC_CC = os.cc main.cc driver.cc irq.cc quirks.cc +SRC_C = dummies.c + +# +# Driver sources +# +DRV = $(addprefix $(CONTRIB_DIR)/,kernel/drv target) + +# +# Framwork sources +# +FRAMEWORK = $(addprefix $(CONTRIB_DIR)/kernel/framework/,\ + osscore audio mixer vmix_core midi ac97) + +# find C files +SRC_C += $(shell find $(DRV) $(FRAMEWORK) -name *.c -exec basename {} ";") + +# find directories for VPATH +PATHS = $(shell find $(DRV) $(FRAMEWORK) -type d) + +# add include directories +INC_DIR += $(CONTRIB_DIR)/kernel/framework/include $(CONTRIB_DIR)/include \ + $(PRG_DIR)/include $(PRG_DIR) + + +vpath %.cc $(PRG_DIR)/signal +vpath %.c $(PATHS) diff --git a/os/include/audio_out_session/rpc_object.h b/os/include/audio_out_session/rpc_object.h index 9274c29ab..161137a36 100644 --- a/os/include/audio_out_session/rpc_object.h +++ b/os/include/audio_out_session/rpc_object.h @@ -25,7 +25,7 @@ namespace Audio_out { class Session_rpc_object : public Genode::Rpc_object { - private: + protected: Packet_stream_tx::Rpc_object _channel; diff --git a/tool/builddir/etc/build.conf.drivers_x86 b/tool/builddir/etc/build.conf.drivers_x86 index 98bcceda0..e0db3ef83 100644 --- a/tool/builddir/etc/build.conf.drivers_x86 +++ b/tool/builddir/etc/build.conf.drivers_x86 @@ -15,6 +15,11 @@ # #REPOSITORIES += $(GENODE_DIR)/linux_drivers +# +# Drivers ported from the Open Sound System +# +#REPOSITORIES += $(GENODE_DIR)/dde_oss + # # Drivers ported from iPXE #