2012-10-23 15:21:43 +02:00
|
|
|
/*
|
|
|
|
* \brief OSS environment
|
|
|
|
* \author Sebastian Sumpf
|
|
|
|
* \date 2012-11-20
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2012-2013 Genode Labs GmbH
|
2012-10-23 15:21:43 +02:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
#include <timer_session/connection.h>
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
#include <oss_config.h>
|
|
|
|
#include <oss_pci.h>
|
|
|
|
#include <dde_kit/pci.h>
|
|
|
|
#include <dde_kit/pgtab.h>
|
|
|
|
#include <dde_kit/resources.h>
|
|
|
|
}
|
|
|
|
|
|
|
|
#include <audio.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
/******************
|
|
|
|
** 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) */
|
2012-12-20 17:34:33 +01:00
|
|
|
int policy = 1;
|
2012-10-23 15:21:43 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-20 17:34:33 +01:00
|
|
|
int audio_play(short *data, unsigned size)
|
2012-10-23 15:21:43 +02:00
|
|
|
{
|
|
|
|
uio_t io = { (char *)data, size, UIO_WRITE };
|
|
|
|
int ret;
|
|
|
|
|
2012-12-20 17:34:33 +01:00
|
|
|
if ((ret = dsp_drv->write(0, 0, &io, size)) != (int)size)
|
2012-10-23 15:21:43 +02:00
|
|
|
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 : "<noname>");
|
|
|
|
dsp_drv = drv;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev_class == OSS_DEV_MIXER) {
|
|
|
|
PINF("Found mixer: '%s'", name ? name : "<noname>");
|
|
|
|
mix_drv = drv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|