/* * \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; } }