diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/OS | |
download | oss4-upstream/4.2-build2006.tar.gz |
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/OS')
30 files changed, 14763 insertions, 0 deletions
diff --git a/kernel/OS/.nomake b/kernel/OS/.nomake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/kernel/OS/.nomake diff --git a/kernel/OS/BeOS/.config b/kernel/OS/BeOS/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/BeOS/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/BeOS/driver_beos.c b/kernel/OS/BeOS/driver_beos.c new file mode 100644 index 0000000..61f4bf9 --- /dev/null +++ b/kernel/OS/BeOS/driver_beos.c @@ -0,0 +1,104 @@ +/* + * Purpose: devfs interface for BeOS/Haiku + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2007. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution. Please contact sales@opensound.com for further info. + * + */ + +#include "oss_config.h" +#include "midi_core.h" +#include <oss_pci.h> +#include <KernelExport.h> +#include <Drivers.h> + +#define DRIVER_NAME "ossdrv" + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +oss_core_module_info *gOSSCore = NULL; + +// #pragma mark - + +// XXX: malloc it + update it from device list + +const char ** +publish_devices(void) +{ + return gOSSCore->oss_publish_devices(); +} + + +device_hooks * +find_device(const char *name) +{ + FENTRYA("%s", name); + + FEXIT(); + return gOSSCore->oss_get_driver_hooks(); +} + +// #pragma mark - + +status_t +init_hardware(void) +{ + status_t err; + FENTRY(); + + err = get_module(OSS_CORE_MODULE_NAME, (module_info **)&gOSSCore); + if (err < B_OK) { + FEXIT(); + return err; + } + + put_module(OSS_CORE_MODULE_NAME); + + FEXIT(); + return B_OK; +} + +status_t +init_driver(void) +{ + status_t err = ENOMEM; + FENTRY(); + + err = get_module(OSS_CORE_MODULE_NAME, (module_info **)&gOSSCore); + if (err < B_OK) + goto err1; + err = gOSSCore->init_osscore(); + dprintf("oss:init_osscore: 0x%08lx\n", err); + if (err < B_OK) + goto err2; + err = gOSSCore->oss_load_drivers(); + err = B_OK; + FEXITR(err); + return err; + +err2: + put_module(OSS_CORE_MODULE_NAME); +err1: + FEXITR(err); + return err; +} + +void +uninit_driver(void) +{ + status_t err; + FENTRY(); + + err = gOSSCore->oss_unload_all_drivers(); + err = gOSSCore->uninit_osscore(); + dprintf("oss:uninit_osscore: 0x%08lx\n", err); + put_module(OSS_CORE_MODULE_NAME); + FEXIT(); +} diff --git a/kernel/OS/BeOS/module.inc b/kernel/OS/BeOS/module.inc new file mode 100644 index 0000000..be7ab1b --- /dev/null +++ b/kernel/OS/BeOS/module.inc @@ -0,0 +1,186 @@ +/* -*- c -*- + * Purpose: OSS module wrapper for BeOS/Haiku + * + * This file will be included from the auto-generated drv_cfg.c files. Under + * UnixWare and OpenServer this will will be compiled during the initial build + * of OSS (in the development system). + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2007. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution. Please contact sales@opensound.com for further info. + * + */ + +#include <PCI.h> + +//pci_module_info *gPCI = NULL; +extern pci_module_info *gPCI; + +#if DRIVER_TYPE==DRV_VIRTUAL +static oss_device_t *osdev = NULL; +#endif + +#if DRIVER_TYPE==DRV_PCI +int DRIVER_PROBE() +{ + oss_device_t *osdev; + pci_info pcii; + uint32 pci_index = 0; + uint32 count = 0; + int err = ENOENT; + static int instance = 0; + FENTRY(); + + /* while there are more pci devices */ + while ((*gPCI->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) + { + int vendor; + bool match = false; + bool sub = true; + + /* we first loop through all subsystem entries, then loop again */ +rescan: + vendor = 0; + /* if we match a supported vendor */ + while (id_table[vendor].vendor) + { + + /* check for function vendor & device id */ + if (!sub && + id_table[vendor].subsystem == false && + id_table[vendor].vendor == pcii.vendor_id && + id_table[vendor].product == pcii.device_id) + { + dprintf("oss: matching pci %04x,%04x\n", id_table[vendor].vendor, id_table[vendor].product); + match = true; + } + + /* check for subsystem vendor & device id */ + if (sub && + id_table[vendor].subsystem == true && + pcii.header_type == 0 && + id_table[vendor].vendor == pcii.u.h0.subsystem_vendor_id && + id_table[vendor].product == pcii.u.h0.subsystem_id) + { + dprintf("oss: matching pci subsystem %04x,%04x\n", id_table[vendor].vendor, id_table[vendor].product); + match = true; + } + + if (match) + { + dev_info_t *di; + di = PMALLOC(NULL, sizeof(dev_info_t)); + if (!di) + break; + memcpy(di, &pcii, sizeof(pcii)); + if ((osdev = + osdev_create (di, DRIVER_TYPE, instance, DRIVER_NICK, NULL)) == NULL) + { + break; + } + + /* should be called once, but we need an osdev */ + oss_load_options (osdev, local_driver_options); + + if (!DRIVER_ATTACH (osdev)) + { + cmn_err (CE_WARN, "Attach failed\n"); + osdev_delete (osdev); + break; + } + + instance++; + count++; + break; + } + vendor++; + } + /* we've checked for subsystem IDs, now rescan for IDs */ + if (!match && sub) { + sub = false; + goto rescan; + } + /* next pci_info struct, please */ + pci_index++; + } + + oss_audio_delayed_attach (); + + if (count) + err = 0; +err: + FEXITR(err); + return err; +} +#endif + +#if DRIVER_TYPE==DRV_VIRTUAL +int DRIVER_PROBE() +{ + int err = EIO; + FENTRY(); + if ((osdev = + osdev_create (NULL, DRIVER_TYPE, 0, DRIVER_NICK, NULL)) == NULL) + { + goto err; + } + + oss_load_options (osdev, local_driver_options); + + if (!DRIVER_ATTACH (osdev)) + { + cmn_err (CE_WARN, "Attach failed\n"); + osdev_delete (osdev); + goto err; + } + + err = 0; +err: + FEXITR(err); + return err; +} +#endif + + + + + + +static status_t +stdops(int32 op, ...) +{ + status_t err; + switch (op) + { + case B_MODULE_INIT: + err = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI); + return err; + + case B_MODULE_UNINIT: + //err = unload_driver(DRIVER_NICK); + //if (err < B_OK) + // return err; + put_module(B_PCI_MODULE_NAME); + return B_OK; + + } + return B_ERROR; +} + +oss_drv_module_info DRIVER_MODULE_OBJECT = { + { + OSS_MAKE_DRV_MOD_NAME(DRIVER_NICK), + 0, + stdops, + }, + DRIVER_PROBE, + DRIVER_ATTACH, + DRIVER_DETACH +}; + diff --git a/kernel/OS/BeOS/os_beos.c b/kernel/OS/BeOS/os_beos.c new file mode 100644 index 0000000..08587c5 --- /dev/null +++ b/kernel/OS/BeOS/os_beos.c @@ -0,0 +1,2222 @@ +/* + * Purpose: Operating system abstraction functions for BeOS/Haiku + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2007. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution. Please contact sales@opensound.com for further info. + * + */ + +#include "oss_config.h" +#include "midi_core.h" +#include <oss_pci.h> +#include <Drivers.h> +#include <KernelExport.h> +#include <driver_settings.h> + +/* + * The BeOS and Haiku kernels are preemptible, + * therefore we must ensure safe access to global variables. + * Such variables are marked by GM. + * Other protected variables: + * - oss_cdevs + * - mixer_devs + * XXX: use a benaphore ?? + */ +#if 0 +// spinlock +static oss_mutex_t osscore_mutex; +#define CORE_LOCK_VAR \ + oss_native_word osscore_mutex_flags +#define CORE_LOCK_INIT() \ + MUTEX_INIT (osdev, osscore_mutex, MH_TOP) +#define CORE_LOCK_CLEANUP() \ + MUTEX_CLEANUP (osscore_mutex) +#define LOCK_CORE() \ + MUTEX_ENTER_IRQDISABLE (osscore_mutex, osscore_mutex_flags) +#define UNLOCK_CORE() \ + MUTEX_EXIT_IRQRESTORE (osscore_mutex, osscore_mutex_flags) +#else +// benaphore +typedef struct benaphore { + sem_id sem; + int32 count; +} benaphore; +benaphore osscore_benaphore; +#define CORE_LOCK_VAR \ + int dummy_ocl +#define CORE_LOCK_INIT() \ + osscore_benaphore.count = 1; \ + osscore_benaphore.sem = create_sem(0, "OSS CORE LOCK") +#define CORE_LOCK_CLEANUP() \ + delete_sem(osscore_benaphore.sem) +#define LOCK_CORE() \ + { \ + if (atomic_add(&osscore_benaphore.count, -1) <= 0) \ + acquire_sem(osscore_benaphore.sem); \ + } +#define UNLOCK_CORE() \ + { \ + if (atomic_add(&osscore_benaphore.count, 1) < 0) \ + release_sem(osscore_benaphore.sem); \ + } +#endif + +#define DEBUG_IRQ 1 +#if DEBUG_IRQ +vint32 irq_count = 0; +#endif + +volatile int oss_open_devices = 0; +#define MAX_CARDS 16 +int oss_num_cards = 0; /* GM */ +static oss_device_t *cards[MAX_CARDS]; /* GM */ +static int oss_expired = 0; +extern int vmix_disabled; + +//static struct fileinfo files[OSS_MAX_CDEVS]; +//static volatile int open_count[OSS_MAX_CDEVS] = { 0 }; +//static volatile int open_devices = 0; /* GM */ + +pci_module_info *gPCI = NULL; + +static char **gDeviceNames = NULL; // buffer for all names + +device_hooks oss_driver_hooks; + +/* + * Table for permanently allocated memory (to be freed by std_op(UNLOAD)) + */ +#define MAX_MEMBLOCKS 4096 +static void *memblocks[MAX_MEMBLOCKS]; /* GM */ +static int nmemblocks = 0; /* GM */ + +void * +oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, + oss_native_word * phaddr) +{ + status_t err; + area_id id; + void *p = NULL; + uint32 lock = B_CONTIGUOUS; + physical_entry pent[1]; + + *phaddr = 0; + + switch (memlimit) + { + case MEMLIMIT_ISA: + case MEMLIMIT_28BITS: + case MEMLIMIT_30BITS: + case MEMLIMIT_31BITS: + /* no known way to force a physical address limit other than <16M */ + lock = B_LOMEM; + break; + case MEMLIMIT_32BITS: + lock = B_CONTIGUOUS; + break; + + default: + cmn_err (CE_WARN, "Bad DMA memlimit for %s\n", osdev->nick); + } + + /* round up to page size */ + size += B_PAGE_SIZE - 1; + size &= ~(B_PAGE_SIZE - 1); + + if ((err = id = create_area(OSS_CONTIG_AREA_NAME, &p, B_ANY_KERNEL_ADDRESS, + size, lock, 0)) < B_OK) + { + cmn_err (CE_WARN, "create_area() failed\n"); + return NULL; + } + + if ((err = get_memory_map(p, size, pent, 1)) < B_OK) + { + cmn_err (CE_WARN, "get_memory_map() failed\n"); + delete_area(id); + return NULL; + } + //XXX:DEBUG + *phaddr = (oss_native_word)pent[0].address; + dprintf("oss_contig_malloc: area %d @ va %p, pa %p, sz %d\n", id, p, (void *)(*phaddr), size); + return p; +} + +void +oss_contig_free (oss_device_t * osdev, void *p, int sz) +{ + area_id id; + if (p == NULL) + return; + id = area_for(p); + if (id < B_OK) + return; +#ifdef MEMDEBUG + { + area_info ai; + if ((get_area_info(id, &ai) < B_OK) || strncmp(ai.name, OSS_CONTIG_AREA_NAME)) + { + cmn_err (CE_NOTE, "oss_contig_free: bad area (%ld)!\n", id); + return; + } + } +#endif + delete_area(id); +} + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + void *tmpbuf; + oss_native_word phaddr; + int size = 64 * 1024; + extern int dma_buffsize; + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + + if (dmap->dmabuf != NULL) + return 0; + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + tmpbuf = CONTIG_MALLOC (dmap->osdev, size, maxaddr, &phaddr, NULL); + if (tmpbuf == NULL) + return OSS_ENOMEM; + dmap->dmabuf = tmpbuf; + dmap->buffsize = size; + dmap->dmabuf_phys = phaddr; + + return 0; +} + +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + if (dmap->dmabuf == NULL) + return; + + CONTIG_FREE (dmap->osdev, dmap->dmabuf, dmap->buffsize, NULL); + dmap->dmabuf = NULL; + dmap->buffsize = 0; + dmap->dmabuf_phys = 0; +} + + +oss_native_word +oss_virt_to_bus (void *addr) +{ + physical_entry pent[2]; + status_t err; + + if (addr == NULL) + return 0; + + /* XXX: ROUNDUP(B_PAGE_SIZE) ? */ + if ((err = get_memory_map(addr, 1, pent, 2)) < 1) + { + cmn_err (CE_WARN, "Virtual address %x not mapped\n", (int) addr); + return 0; + } + //XXX:which??? + //return (oss_native_word)pent[0].address; + return (oss_native_word)(gPCI->ram_address(pent[0].address)); +} + + +void * +oss_pmalloc (size_t sz) +{ + void *tmp; + + tmp = KERNEL_MALLOC (sz); + + if (nmemblocks < MAX_MEMBLOCKS) + memblocks[nmemblocks++] = tmp; + + return tmp; +} + +int +oss_create_uio (uio_t * uio, char *buf, size_t count, uio_rw_t rw, + int is_kernel) +{ + memset (uio, 0, sizeof (*uio)); + + if (is_kernel) + { + oss_cmn_err (CE_CONT, + "oss_create_uio: Kernel space buffers not supported\n"); + return OSS_EIO; + } + + uio->ptr = buf; + uio->resid = count; + uio->kernel_space = is_kernel; + uio->rw = rw; + + return 0; +} + +int +oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, uio_t * uio) +{ + int err = EFAULT; + FENTRY(); + + if (rwflag != uio->rw) + { + oss_cmn_err (CE_WARN, "uiomove: Bad direction\n"); + goto err; + } + + if (uio->resid < nbytes) + { + oss_cmn_err (CE_WARN, "uiomove: Bad count %d (%d)\n", nbytes, + uio->resid); + goto err; + } + + if (uio->kernel_space) + goto err; + + switch (rwflag) + { + case UIO_READ: + //XXX:user_memcpy... + memcpy (uio->ptr, address, nbytes); + break; + + case UIO_WRITE: + //XXX:user_memcpy... + memcpy (address, uio->ptr, nbytes); + break; + } + + uio->resid -= nbytes; + uio->ptr += nbytes; + + err = B_OK; +err: + FEXITR(err); + return err; +} + + +void +oss_cmn_err (int level, char *s, ...) +{ + char tmp[1024], *a[6]; + va_list ap; + int i, n = 0; + + va_start (ap, s); + + for (i = 0; i < strlen (s); i++) + if (s[i] == '%') + n++; + + for (i = 0; i < n && i < 6; i++) + a[i] = va_arg (ap, char *); + + for (i = n; i < 6; i++) + a[i] = NULL; + + if (level == CE_CONT) + { + sprintf (tmp, s, a[0], a[1], a[2], a[3], a[4], a[5], NULL, + NULL, NULL, NULL); + dprintf ("%s", tmp); + } + else + { + strcpy (tmp, "osscore: "); + sprintf (tmp + strlen (tmp), s, a[0], a[1], a[2], a[3], a[4], a[5], + NULL, NULL, NULL, NULL); + if (level == CE_PANIC) + panic (tmp); + + dprintf ("%s", tmp); + } + va_end (ap); +} + + +/* + * Sleep/wakeup + */ + +struct oss_wait_queue * +oss_create_wait_queue (oss_device_t * osdev, const char *name) +{ + struct oss_wait_queue *wq; + status_t err; + FENTRYA(", %s", name); + + if ((wq = malloc (sizeof (*wq))) == NULL) + { + oss_cmn_err (CE_WARN, "malloc(%d) failed (wq)\n", sizeof (*wq)); + return NULL; + } + sprintf(wq->name, OSS_WQ_SEM_NAME "%-20s", name); + err = wq->sem = create_sem(0, wq->name); + if (err < B_OK) + { + free(wq); + oss_cmn_err (CE_WARN, "create_sem() failed (wq)\n"); + return NULL; + } + + return wq; +} + +void +oss_reset_wait_queue (struct oss_wait_queue *wq) +{ + sem_info si; + status_t err; + FENTRY(); + + wq->flags = 0; + err = create_sem(0, wq->name); + if (err >= 0) { + /* replace with the new one */ + delete_sem(wq->sem); + wq->sem = err; + } + + FEXIT(); +} + +void +oss_remove_wait_queue (struct oss_wait_queue *wq) +{ + FENTRY(); + delete_sem(wq->sem); + free (wq); +} + +int +oss_sleep (struct oss_wait_queue *wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ + bigtime_t timeout = B_INFINITE_TIMEOUT; + uint32 semflags = B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT; + int result = 0; + FENTRYA("(%s), , %d, , ", wq->name, ticks); + *status = 0; + + if (wq == NULL) + return 0; + +#ifdef B_WAKE_ON_TIMEOUT + // Dano only; sure it's what we want ? + if (wq->flags & WK_WAKEUP) + semflags |= B_WAKE_ON_TIMEOUT; +#endif + + wq->flags = 0; + MUTEX_EXIT_IRQRESTORE(*mutex, *flags); + + if (ticks > 0) + timeout = ticks * 1000000LL / OSS_HZ; + result = acquire_sem_etc (wq->sem, 1, semflags, timeout); + //dprintf("oss_sleep:acquire_sem(s:%ld, 1, %x, %Ld): 0x%08lx\n", wq->sem, semflags, timeout, result); + + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); + + if (result == EINTR) /* Signal received */ + { + *status |= WK_SIGNAL; + return 1; + } + + if (result == B_TIMED_OUT) + //if (!(wq->flags & WK_WAKEUP)) /* Timeout */ + { + return 0; + } + + return 1; +} + +int +oss_register_poll (struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev) +{ + FENTRYA("(%s), , , , ", wq->name); + dprintf("oss:UNIMPLEMENTED:%s\n", __FUNCTION__); + MUTEX_EXIT_IRQRESTORE(*mutex, *flags); + //poll_wait ((struct file *) ev->file, &wq->wq, (struct wait *) ev->wait); + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); + return 0; +} + +void +oss_wakeup (struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + FENTRYA("(%s), , %x, , ", wq->name, events); + if (wq == NULL) + return; + + wq->flags |= WK_WAKEUP; + MUTEX_EXIT_IRQRESTORE(*mutex, *flags); + + //dprintf("oss_wakeup:release_sem(s:%ld)\n", wq->sem); + release_sem_etc (wq->sem, 1, B_DO_NOT_RESCHEDULE); + //XXX:handle select here + + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); +} + +unsigned long +oss_get_time (void) +{ + return (unsigned long) (system_time() / (1000000 / OSS_HZ)); +} + +typedef struct tmout_desc +{ + struct timer timer; /* MUST be first */ + + volatile int active; + int timestamp; + void (*func) (void *); + void *arg; +} tmout_desc_t; + +static volatile int next_id = 0; +#define MAX_TMOUTS 128 + +tmout_desc_t tmouts[MAX_TMOUTS] = { {0} }; + +int timeout_random = 0x12123400; + +int32 +oss_timer_callback (struct timer *timer) +{ + tmout_desc_t *tmout = (tmout_desc_t *)timer; + int ix; + void *arg; + + timeout_random++; + + if (!tmout->active) + return; + + arg = tmout->arg; + tmout->active = 0; + tmout->timestamp = 0; + + tmout->func (arg); + return B_HANDLED_INTERRUPT;//B_INVOKE_SCHEDULER ?; +} + +timeout_id_t +oss_timeout (void (*func) (void *), void *arg, unsigned long long ticks) +{ + tmout_desc_t *tmout = NULL; + bigtime_t period; + int id, n; + + timeout_random++; + + n = 0; + id = -1; + + while (id == -1 && n < MAX_TMOUTS) + { + if (!tmouts[next_id].active) + { + tmouts[next_id].active = 1; + id = next_id++; + tmout = &tmouts[id]; + break; + } + + next_id = (next_id + 1) % MAX_TMOUTS; + } + + if (id == -1) /* No timer slots available */ + { + oss_cmn_err (CE_WARN, "Timeout table full\n"); + return 0; + } + + tmout->func = func; + tmout->arg = arg; + tmout->timestamp = id | (timeout_random & ~0xff); + + period = ticks * 1000000LL / OSS_HZ; + add_timer (&tmout->timer, oss_timer_callback, period, B_ONE_SHOT_RELATIVE_TIMER); + + return id | (timeout_random & ~0xff); +} + +void +oss_untimeout (timeout_id_t id) +{ + tmout_desc_t *tmout; + int ix; + + ix = id & 0xff; + if (ix < 0 || ix >= MAX_TMOUTS) + return; + + timeout_random++; + tmout = &tmouts[ix]; + + if (tmout->timestamp != id) /* Expired timer */ + return; + if (tmout->active) + cancel_timer (&tmout->timer); + tmout->active = 0; + tmout->timestamp = 0; +} + + +caddr_t +oss_map_pci_mem (oss_device_t * osdev, int nr, int phaddr, int size) +{ + status_t err; + void *va = NULL; + FENTRYA("%p,%d,%u,%d", osdev, nr, phaddr, size); + //XXX:align phaddr ? + /* round up to page size */ + size += B_PAGE_SIZE - 1; + size &= ~(B_PAGE_SIZE - 1); + + err = map_physical_memory(OSS_PCI_AREA_NAME, (void *)phaddr, size, + B_ANY_KERNEL_BLOCK_ADDRESS, 0, &va); + if (err < B_OK) + va = NULL; + FEXITR((uint32)va); + return (caddr_t)va; +} + +void +oss_unmap_pci_mem (void *addr) +{ + area_id id; + if (addr == NULL) + return; + id = area_for(addr); + if (id < B_OK) + return; +#ifdef MEMDEBUG + { + area_info ai; + if ((get_area_info(id, &ai) < B_OK) || strncmp(ai.name, OSS_PCI_AREA_NAME)) + { + cmn_err (CE_NOTE, "oss_unmap_pci_mem: bad area (%ld)!\n", id); + return; + } + } +#endif + delete_area(id); +} + +void +oss_pci_byteswap (oss_device_t * osdev, int mode) +{ + // NOP +} + +void +oss_pcie_init (oss_device_t * osdev, int flags) +{ + /* TODO: Should we do something? */ +} + +int +pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) + return PCIBIOS_FAILED; + *val = (unsigned char)gPCI->read_pci_config (osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + (uchar)where, 1); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) +{ + int ret; + + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + ret = pci_read_config_byte (osdev, where, val); + return ret; +} + + +int +pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) + return PCIBIOS_FAILED; + *val = (unsigned short)gPCI->read_pci_config (osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + (uchar)where, 2); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) + return PCIBIOS_FAILED; + *val = (unsigned int)gPCI->read_pci_config (osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + (uchar)where, 4); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) + return PCIBIOS_FAILED; + gPCI->write_pci_config (osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + (uchar)where, 1, val); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) + return PCIBIOS_FAILED; + gPCI->write_pci_config (osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + (uchar)where, 2, val); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) + return PCIBIOS_FAILED; + gPCI->write_pci_config (osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + (uchar)where, 4, val); + return PCIBIOS_SUCCESSFUL; +} + + + +#ifdef MUTEX_CHECKS +static int oss_context = 0; /* 0=user context, 1=interrupt context */ +#endif + +static int32 +ossintr (void *idata) +{ + oss_device_t *osdev = idata; + oss_native_word flags; + //dprintf("oss:intr(%ld)!\n", osdev->irq); +#ifdef MUTEX_CHECKS + int saved_context; + saved_context = oss_context; + if (oss_context == 1) + cmn_err (CE_WARN, "Recursive interrupt\n"); + oss_context = 1; +#endif + + MUTEX_ENTER_IRQDISABLE (osdev->mutex, flags); + + if (!osdev->tophalf_handler (osdev)) + { + MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); +#ifdef MUTEX_CHECKS + oss_context = saved_context; +#endif + return B_UNHANDLED_INTERRUPT; + } + + if (osdev->bottomhalf_handler != NULL) + osdev->bottomhalf_handler (osdev); + + MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); +#ifdef MUTEX_CHECKS + oss_context = saved_context; +#endif + + return B_HANDLED_INTERRUPT; +} + +int +oss_register_interrupts (oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom) +{ + unsigned char pci_irq_line; + int err; + FENTRYA(", %d, , ", intrnum); + + if (intrnum != 0) + { + cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum, + osdev->name); + return OSS_EINVAL; + } + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n"); + return OSS_EINVAL; + } + + if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL) + { + cmn_err (CE_WARN, "Interrupts already registered for %s\n", + osdev->name); + return OSS_EINVAL; + } + + if (top == NULL) + { + cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name); + return OSS_EINVAL; + } + + // could probably use osdev->dip->pciinfo... + if (pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line) > 0) + return OSS_EIO; + + osdev->irq = pci_irq_line; + osdev->tophalf_handler = top; + osdev->bottomhalf_handler = bottom; + err = install_io_interrupt_handler (pci_irq_line, ossintr, osdev, 0); + dprintf("install_io_interrupt_handler (%d, %p, %p, 0) = 0x%08lx\n", pci_irq_line, ossintr, osdev, err); + if (err < B_OK) + { + cmn_err (CE_WARN, "install_io_interrupt_handler failed for %s\n", osdev->nick); + osdev->irq = -1; + osdev->tophalf_handler = NULL; + osdev->bottomhalf_handler = NULL; + return err; + } + +#if DEBUG_IRQ + atomic_add(&irq_count, 1); +#endif + + return 0; +} + +void +oss_unregister_interrupts (oss_device_t * osdev) +{ + status_t err = B_OK; + FENTRY(); + dprintf("remove_io_interrupt_handler (%d, %p, %p)\n", osdev->irq, ossintr, osdev); + if (osdev->irq >= 0) + err = remove_io_interrupt_handler (osdev->irq, ossintr, osdev); + if (err < B_OK) + cmn_err (CE_WARN, "Error removing interrupt index (%d) for %s: %s\n", + osdev->irq, osdev->name, strerror(err)); +#if DEBUG_IRQ + atomic_add(&irq_count, -1); +#endif + osdev->irq = -1; +} + +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + static int dev_instance = 0; + FENTRYA(", %s", name); + + DDB (cmn_err (CE_CONT, "OSS device %d is %s\n", dev_instance++, name)); + + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + FEXITR(0); + return 0; +} + +int +oss_disable_device (oss_device_t * osdev) +{ + int i; + CORE_LOCK_VAR; + FENTRY(); +/* + * This routine should check if the device is ready to be unloaded (no devices are in use). + * If the device cannot be unloaded this routine must return OSS_EBUSY. + * + * If the device can be unloaded then disable any timers or other features that may cause the + * device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. + * However the interrupt handler should still stay enabled. The low level driver will call + * oss_unregister_interrupts() after it has cleared the interrupt enable register. + */ + LOCK_CORE(); + if (osdev->refcount > 0 || oss_open_devices > 0) + { + UNLOCK_CORE(); + cmn_err (CE_CONT, "Refcount %d, open_devices %d\n", osdev->refcount, + oss_open_devices); + return OSS_EBUSY; + } + +/* + * Now mark all devices unavailable (for the time being) + */ + + for (i = 0; i < num_mixers; i++) + if (mixer_devs[i]->osdev == osdev) + { + mixer_devs[i]->unloaded = 1; + } + + for (i = 0; i < num_mididevs; i++) + { + if (midi_devs[i]->osdev == osdev) + { + midi_devs[i]->unloaded = 1; + } + } + + for (i = 0; i < num_audio_engines; i++) + if (audio_engines[i]->osdev == osdev) + { + UNLOCK_CORE(); //needed until a benaphore is used. + audio_uninit_device (i); + LOCK_CORE(); + } + + UNLOCK_CORE(); + + FEXIT(); + return 0; +} + +void +oss_unregister_device (oss_device_t * osdev) +{ + FENTRY(); +/* + * Notice! The driver calling this routine (the owner of the osdev parameter) + * has already uninitialized itself. Do not do any actions that may call this + * driver directly or indirectly. + */ + +/* + * Force reload of all drivers if any application tries to open any + * of the devices. + */ + //do_forceload = 1; + FEXIT(); +} + +void +oss_reserve_device (oss_device_t * osdev) +{ + CORE_LOCK_VAR; + + //XXX:use atomic_add() ? + LOCK_CORE(); + osdev->refcount++; + UNLOCK_CORE(); +} + +void +oss_unreserve_device (oss_device_t * osdev, int decrement) +{ + CORE_LOCK_VAR; + + //XXX:use atomic_add() ? + LOCK_CORE(); + osdev->refcount--; + if (osdev->refcount < 0) + osdev->refcount = 0; + UNLOCK_CORE(); +} + +void * +oss_get_osid (oss_device_t * osdev) +{ +// return osdev->osid; + return osdev->dip; + return NULL; // XXX:TODO +} + +int +oss_get_procinfo(int what) +{ + switch (what) + { + case OSS_GET_PROCINFO_UID: + return getuid(); + break; + } + + return OSS_EINVAL; +} + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, + const char *handle) +{ + oss_device_t *osdev = NULL; + int i, err; + caddr_t addr; + off_t region_size; + CORE_LOCK_VAR; + FENTRYA(", %d, %d, %s, %s", dev_type, instance, nick, handle); + + if (handle == NULL) + handle = nick; + + /* + * Don't accept any more drivers if expired + */ + if (oss_expired && oss_num_cards > 0) + return NULL; + + LOCK_CORE(); + for (i = 0; dip && (i < oss_num_cards); i++) + { + if (cards[i] == NULL) + continue; + if (cards[i]->available) + continue; + if (cards[i]->dip == dip) + { + osdev = cards[i]; + break; + } + } + UNLOCK_CORE(); + + if (osdev == NULL) + { + if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + + LOCK_CORE(); + if (oss_num_cards >= MAX_CARDS) + { + UNLOCK_CORE(); + cmn_err (CE_PANIC, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + return NULL; + } + memset (osdev, 0, sizeof (*osdev)); + + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + UNLOCK_CORE(); + } + + osdev->dip = dip; + //osdev->osid = dip; + osdev->unloaded = 0; + osdev->available = 1; + osdev->first_mixer = -1; + osdev->instance = instance; + osdev->dev_type = dev_type; + osdev->devc = NULL; + MUTEX_INIT (osdev, osdev->mutex, MH_GLOBAL); + sprintf (osdev->nick, "%s%d", nick, instance); + strcpy (osdev->modname, nick); + + switch (dev_type) + { + case DRV_PCI: + /* NOP */ +#ifdef __HAIKU__ + if (gPCI->reserve_device(osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + "oss", osdev) != B_OK) { + cmn_err (CE_WARN, "Could not reserve PCI device\n"); + /* XXX: CLEANUP! */ + return NULL; + } +#endif + break; + + case DRV_VIRTUAL: + case DRV_STREAMS: + /* NOP */ + break; + + case DRV_USB: + /* NOP */ + break; + + default: + cmn_err (CE_WARN, "Bad device type\n"); + return NULL; + } + +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { + unsigned int subvendor; + pci_read_config_dword (osdev, 0x2c, &subvendor); + + sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance); + } + break; + + case DRV_USB: + /* TODO: Get the vendor information */ + sprintf (osdev->handle, "USB-%s%d", handle, instance); + break; + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + FEXIT(); + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + FENTRYA(", %d", new_instance); + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + FEXIT(); + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + CORE_LOCK_VAR; + + FENTRY(); + if (osdev == NULL) + return; + osdev->available=0; + + switch (osdev->dev_type) + { + case DRV_PCI: + /* NOP */ + //pci_config_teardown (&osdev->pci_config_handle); + //osdev->pci_config_handle = NULL; +#ifdef __HAIKU__ + gPCI->unreserve_device(osdev->dip->pciinfo.bus, + osdev->dip->pciinfo.device, + osdev->dip->pciinfo.function, + "oss", osdev); +#endif + break; + } + +/* + * Mark all minor nodes for this module as invalid. + */ + LOCK_CORE(); + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->osdev == osdev) + { + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->osdev = NULL; + strcpy (oss_cdevs[i]->name, "Removed device"); + } + UNLOCK_CORE(); + + MUTEX_CLEANUP (osdev->mutex); + osdev->unloaded = 1; + FEXIT(); +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * element_size))==NULL) + return 0; + + memset(new, 0, new_size * element_size); + if (old != NULL) + memcpy(new, old, old_size * element_size); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ +/* + * oss_install_chrdev creates a character device (minor). However if + * name==NULL the device will not be exported (made visible to userland + * clients). + */ + + int i, num; + oss_cdev_t *cdev = NULL; + CORE_LOCK_VAR; + FENTRYA(", %s, %d, %d, , %d", name, dev_class, instance, flags); + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; +/* + * Find if this dev_class&instance already exists (after previous module + * detach). + */ + + LOCK_CORE(); + for (num = 0; num < oss_num_cdevs; num++) + if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ + if (oss_cdevs[num]->dev_class == dev_class + && oss_cdevs[num]->instance == instance) + { + cdev = oss_cdevs[num]; + //dprintf("oss:reusing cdev[%d]\n", num); + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name) - 1); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + break; + } + UNLOCK_CORE(); + + if (cdev == NULL) + { + /* must alloc before locking, the rest must be atomic */ + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + + LOCK_CORE(); + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) + { + cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); + UNLOCK_CORE(); + return; + } + } + + num = oss_num_cdevs++; + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name) - 1); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + //dprintf("oss:reusing cdev[%d]: @%p\n", num, cdev); + UNLOCK_CORE(); + } + +/* + * Export the device only if name != NULL + */ +#if 0 + if (name != NULL) + { + char tmp[64], *s; + char *dev_type = "oss_sysdev"; + +//XXX: maybe do something ?? + } +#endif + FEXIT(); +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ + CORE_LOCK_VAR; +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + LOCK_CORE(); + if (cardnum < 0 || cardnum >= oss_num_cards) + { + UNLOCK_CORE(); + return OSS_ENXIO; + } + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->shortname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info)-1); + ci->hw_info[sizeof(ci->hw_info)-1]=0; + + UNLOCK_CORE(); + + return 0; +} + +/* XXX: major/minors don't exist in BeOS, WTF */ +int +oss_find_minor (int dev_class, int instance) +{ + int i, minor = -1; + CORE_LOCK_VAR; + + LOCK_CORE(); + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class + && oss_cdevs[i]->instance == instance) + { + minor = i; + break; + } + UNLOCK_CORE(); + + return minor; +} + + +#ifdef MUTEX_CHECKS +void +debug_mutex_init (oss_mutex_t * mutex, char *file, int line) +{ + memset (mutex, 0, sizeof (mutex)); + mutex->lock = 0; + mutex->owner = -1; +} + +void +debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line) +{ + if (find_thread(NULL) == mutex->owner) + { + cmn_err (CE_NOTE, "%s:%d: mutex still owned (%d)\n", file, line, mutex->owner); + } + if (mutex->lock) + { + cmn_err (CE_NOTE, "%s:%d: mutex still locked (%d)\n", file, line, mutex->lock); + } +} + +void +debug_mutex_enter (oss_mutex_t * mutex, char *file, int line, oss_native_word *flags) +{ + if (find_thread(NULL) == mutex->owner) + { + cmn_err (CE_NOTE, "%s:%d: Re-entrant mutex (%s:%d %d)\n", file, line, + mutex->file, mutex->line, mutex->busy_flags); + return; + } + + mutex->file = file; + mutex->line = line; + mutex->busy_flags = flags ? CNTX_INTR : CNTX_USER; + if (flags) + *flags = (oss_native_word) disable_interrupts(); + acquire_spinlock (&mutex->lock); + +} + +void +debug_mutex_exit (oss_mutex_t * mutex, char *file, int line, oss_native_word *flags) +{ + if (find_thread(NULL) != mutex->owner) + { + cmn_err (CE_NOTE, "Mutex not owned %s:%d\n", file, line); + } + else + { + release_spinlock(&mutex->lock); + if (flags) + restore_interrupts((cpu_status)*flags); + } + + mutex->owner = -1; + mutex->file = NULL; + mutex->line = 0; + mutex->busy_flags = 0; +} +#endif + + +void +oss_load_options (oss_device_t * osdev, oss_option_map_t map[]) +{ + char name[32] = OSS_CONFIG_FILE_PREFIX "core"; + void *handle; + const char *valstr; + int i; + + if (osdev == NULL) + return; + + /* if not core module, take the module name */ + if (strcmp(osdev->modname, "oss")) + strncpy (name, osdev->modname, 16); + + dprintf("oss_load_options(): %s\n", name); + handle = load_driver_settings (name); + /* not there */ + if (handle == NULL) + return; + + for (i = 0; map[i].name != NULL; i++) + { + /* discard given without value */ + if ((valstr = + get_driver_parameter (handle, + map[i].name, NULL, NULL)) != NULL) + { + int base = 10; + if (!strncmp (valstr, "0x", 2)) + { + base = 16; + valstr += 2; + } + *map[i].ptr = (int)strtol (valstr, NULL, base); + } + } + unload_driver_settings (handle); +} + +#ifdef DEBUG_KDLCMD +static int +kdl_oss_dump_cards(int argc, char **argv) +{ + int i; + kprintf("oss_num_cards = %d\n", oss_num_cards); + for (i = 0; i < oss_num_cards; i++) + { + if (cards[i] == NULL) + continue; + kprintf("oss_cards[%d] = {\n", i); + kprintf(" cardnum= %d\n", cards[i]->cardnum); + kprintf(" dev_type= %d\n", cards[i]->dev_type); + kprintf(" instance= %d\n", cards[i]->instance); + + kprintf(" unloaded= %d\n", cards[i]->unloaded); + + kprintf(" name= %s\n", cards[i]->name); + kprintf(" nick= %-16s\n", cards[i]->nick); + kprintf(" modname= %-32s\n", cards[i]->modname); + kprintf(" handle= %-32s\n", cards[i]->handle); + kprintf(" num_audio_engines= %d\n", cards[i]->num_audio_engines); + kprintf(" num_audioplay= %d\n", cards[i]->num_audioplay); + kprintf(" num_audiorec= %d\n", cards[i]->num_audiorec); + kprintf(" num_audioduplex= %d\n", cards[i]->num_audioduplex); + kprintf(" num_mididevs= %d\n", cards[i]->num_mididevs); + kprintf(" num_mixerdevs= %d\n", cards[i]->num_mixerdevs); + + kprintf(" refcount= %d\n", cards[i]->refcount); + + kprintf(" irq= %d\n", cards[i]->irq); + kprintf("}\n"); + } + return 0; +} + +static int +kdl_oss_dump_cdevs(int argc, char **argv) +{ + int i; + kprintf("oss_num_cdevs = %d\n", oss_num_cdevs); + for (i = 0; i < oss_num_cdevs; i++) + { + if (oss_cdevs[i] == NULL) + continue; + kprintf("oss_cdevs[%d] = {\n", i); + kprintf(" dev_class= %d\n", oss_cdevs[i]->dev_class); + kprintf(" instance= %d\n", oss_cdevs[i]->instance); + kprintf(" name= %-32s\n", oss_cdevs[i]->name); + kprintf(" osdev= %p\n", oss_cdevs[i]->osdev); + //kprintf(" opencount= %d\n", open_count[i]); + kprintf("}\n"); + } + return 0; +} +#endif + +/* + * Driver entry point routines + */ + +status_t +init_osscore (void) +{ + int err = 0; + oss_device_t *osdev; + FENTRY(); + +#ifdef LICENSED_VERSION +/*WRITEME*/ +#endif + + add_debugger_command("oss_dump_cards", &kdl_oss_dump_cards, "Dump the OSS cards[] array."); + add_debugger_command("oss_dump_cdevs", &kdl_oss_dump_cdevs, "Dump the OSS cdevs[] array."); + + //MUTEX_INIT (osdev, osscore_mutex, MH_TOP); + CORE_LOCK_INIT(); + + if ((osdev = osdev_create (NULL, DRV_VIRTUAL, 0, "oss", NULL)) == NULL) + { + cmn_err (CE_WARN, "Creating osdev failed\n"); + FEXITR(ENOMEM); + return OSS_ENOMEM; + } + + + oss_load_options (osdev, oss_global_options); + + oss_common_init (osdev); + + oss_register_device (osdev, "OSS core services"); + + FEXIT(); + return 0; +} + +status_t +uninit_osscore (void) +{ + int i; + static int already_unloaded = 0; + FENTRY(); + + if (oss_open_devices > 0) + return EBUSY; + + if (already_unloaded) + return 0; + already_unloaded = 1; + + oss_unload_drivers (); + + //LOCK_CORE(); + for (i = 0; i < nmemblocks; i++) + KERNEL_FREE (memblocks[i]); + nmemblocks = 0; + + CORE_LOCK_CLEANUP(); + //MUTEX_CLEANUP (osscore_mutex); + + remove_debugger_command("oss_dump_cdevs", &kdl_oss_dump_cdevs); + remove_debugger_command("oss_dump_cards", &kdl_oss_dump_cards); + +#if DEBUG_IRQ + dprintf("oss: %ld irq handlers left\n", irq_count); +#endif + + + return 0; +} + +const char ** +oss_publish_devices(void) +{ + int i, j; + char *name; + CORE_LOCK_VAR; + + FENTRY(); + + LOCK_CORE(); + gDeviceNames = realloc(gDeviceNames, (oss_num_cdevs + 1) * sizeof(char *) + + (oss_num_cdevs * DEVICE_NAME_LEN)); + if (gDeviceNames == NULL) { + UNLOCK_CORE(); + FEXIT(); + return NULL; + } + name = (char *)(&gDeviceNames[oss_num_cdevs + 1]); + for (i = 0; i < oss_num_cdevs+1; i++) + gDeviceNames[i] = NULL; + //dprintf("oss_num_cdevs = %d\n", oss_num_cdevs); + for (i = 0, j = 0; i < oss_num_cdevs; i++) + { + oss_cdev_t *cdev = oss_cdevs[i]; + if (cdev && cdev->d) + { + strcpy(name, DEVICE_PREFIX); + strncat(name, cdev->name, 32-1); + //dprintf("oss: publishing %s\n", name); + gDeviceNames[j++] = name; + name += DEVICE_NAME_LEN; + } + } + UNLOCK_CORE(); + + FEXIT(); + return (const char**)gDeviceNames; +} + +device_hooks *oss_get_driver_hooks (void) +{ + return &oss_driver_hooks; +} + +status_t +oss_load_drivers (void) +{ + oss_drv_module_info *drv; + char module[256]; + size_t modulesz = sizeof(module); + void *cookie; + status_t err = ENOENT; + FENTRY(); + cookie = open_module_list(OSS_MODULES_PREFIX); + if (cookie == NULL) + goto err1; + while (read_next_module_name(cookie, module, &modulesz) >= B_OK) + { + err = get_module(module, (module_info **)&drv); + if (err >= B_OK) + { + if (drv->driver_probe() < B_OK) + put_module(module); + } + modulesz = sizeof(module); + } + err = B_OK; +err2: + close_module_list(cookie); +err1: + FEXITR(err); + return B_OK; +} + +status_t +oss_probe_pci (void) +{ + FENTRY(); + //XXX:TODO:remove me + //ali5455_probe(); + //atiaudio_probe(); + FEXIT(); + return B_OK; +} + + +static status_t +unload_driver (const char *nick) +{ + oss_drv_module_info *drv; + char module[256]; + status_t err = B_OK; + int i; + CORE_LOCK_VAR; + FENTRYA("%s", nick); + + /* skip ourselves */ + if (!strcmp(nick, "oss")) + goto err1; + + sprintf(module, "%s%s%s", OSS_MODULES_PREFIX, nick, OSS_MODULES_SUFFIX); + err = get_module(module, (module_info **)&drv); + if (err < B_OK) + goto err1; + + err = EBUSY; + + LOCK_CORE(); + /* detach all osdevs for this driver */ + for (i = 0; i < oss_num_cards; i++) + { + oss_device_t *osdev = cards[i]; + if (!osdev) + continue; + if (strncmp(osdev->modname, nick, 32)) + continue; + UNLOCK_CORE(); + err = drv->driver_detach(osdev); + if (err < B_OK) + goto err2; + LOCK_CORE(); + //cards[i] = NULL; // XXX: not sure... + } + UNLOCK_CORE(); + err = B_OK; + put_module(module); + /* twice */ +err2: + put_module(module); +err1: + FEXITR(err); + return err; +} + +status_t +oss_unload_all_drivers(void) +{ + char module[256]; + status_t err = B_OK; + int i; + CORE_LOCK_VAR; + FENTRY(); + + /* for each osdev still around, unload the module it came from, + * which should delete them. + */ + LOCK_CORE(); + for (i = 0; i < oss_num_cards; i++) + { + if (!cards[i]) + continue; + if (cards[i]->unloaded) + continue; + UNLOCK_CORE(); + err = unload_driver(cards[i]->modname); + if (err < B_OK) + break; + LOCK_CORE(); + } + UNLOCK_CORE(); + + FEXITR(err); + return err; +} + + +static status_t +oss_stdops(int32 op, ...) +{ + status_t err; + switch (op) + { + case B_MODULE_INIT: + //vmix_disabled = 1; /* disable vmix */ + err = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI); + if (err >= B_OK) + { + //XXX:call init_osscore() here + return err; + put_module(B_PCI_MODULE_NAME); + } + return err; + case B_MODULE_UNINIT: + free(gDeviceNames); + put_module(B_PCI_MODULE_NAME); + return B_OK; + + } + return B_ERROR; +} + +oss_core_module_info gOSSCoreModule = { + { + OSS_CORE_MODULE_NAME, + /*B_KEEP_LOADED*/0, + oss_stdops, + }, + init_osscore, + uninit_osscore, + oss_publish_devices, + oss_get_driver_hooks, + oss_probe_pci, + oss_load_drivers, + oss_unload_all_drivers, +}; + +// for f in kernel/drv/*; do echo "extern oss_drv_module_info gModule_$(basename $f);"; done +extern oss_drv_module_info gModule_oss_ali5455; +extern oss_drv_module_info gModule_oss_atiaudio; +extern oss_drv_module_info gModule_oss_audigyls; +//extern oss_drv_module_info gModule_oss_audiocs; +extern oss_drv_module_info gModule_oss_audioloop; +extern oss_drv_module_info gModule_oss_audiopci; +extern oss_drv_module_info gModule_oss_cmi878x; +extern oss_drv_module_info gModule_oss_cmpci; +extern oss_drv_module_info gModule_oss_cs4281; +extern oss_drv_module_info gModule_oss_cs461x; +extern oss_drv_module_info gModule_oss_digi96; +extern oss_drv_module_info gModule_oss_emu10k1x; +extern oss_drv_module_info gModule_oss_envy24; +extern oss_drv_module_info gModule_oss_envy24ht; +extern oss_drv_module_info gModule_oss_fmedia; +extern oss_drv_module_info gModule_oss_geode; +extern oss_drv_module_info gModule_oss_hdaudio; +extern oss_drv_module_info gModule_oss_ich; +//extern oss_drv_module_info gModule_oss_imux; +//extern oss_drv_module_info gModule_oss_midiloop; +//extern oss_drv_module_info gModule_oss_midimix; +//extern oss_drv_module_info gModule_oss_sadasupport; +extern oss_drv_module_info gModule_oss_sblive; +extern oss_drv_module_info gModule_oss_sbpci; +extern oss_drv_module_info gModule_oss_sbxfi; +extern oss_drv_module_info gModule_oss_solo; +extern oss_drv_module_info gModule_oss_trident; +//extern oss_drv_module_info gModule_oss_usb; +//extern oss_drv_module_info gModule_oss_userdev; +extern oss_drv_module_info gModule_oss_via823x; +extern oss_drv_module_info gModule_oss_via97; +extern oss_drv_module_info gModule_oss_ymf7xx; +//extern oss_drv_module_info gModule_osscore; + +module_info *modules[] = { + (module_info *)&gOSSCoreModule, +//for f in kernel/drv/*; do echo " (module_info *)&gModule_$(basename $f),"; done >> kernel/OS/BeOS/os_beos.c (module_info *)&gModule_oss_ali5455, + (module_info *)&gModule_oss_atiaudio, + (module_info *)&gModule_oss_audigyls, + //(module_info *)&gModule_oss_audiocs, + //(module_info *)&gModule_oss_audioloop, + (module_info *)&gModule_oss_audiopci, + (module_info *)&gModule_oss_cmi878x, + (module_info *)&gModule_oss_cmpci, + (module_info *)&gModule_oss_cs4281, + (module_info *)&gModule_oss_cs461x, + (module_info *)&gModule_oss_digi96, + (module_info *)&gModule_oss_emu10k1x, + (module_info *)&gModule_oss_envy24, + (module_info *)&gModule_oss_envy24ht, + (module_info *)&gModule_oss_fmedia, + (module_info *)&gModule_oss_geode, + (module_info *)&gModule_oss_hdaudio, + (module_info *)&gModule_oss_ich, + //(module_info *)&gModule_oss_imux, + //(module_info *)&gModule_oss_midiloop, + //(module_info *)&gModule_oss_midimix, + //(module_info *)&gModule_oss_sadasupport, + (module_info *)&gModule_oss_sblive, + (module_info *)&gModule_oss_sbpci, + (module_info *)&gModule_oss_sbxfi, + (module_info *)&gModule_oss_solo, + (module_info *)&gModule_oss_trident, + //(module_info *)&gModule_oss_usb, + //(module_info *)&gModule_oss_userdev, + (module_info *)&gModule_oss_via823x, + (module_info *)&gModule_oss_via97, + (module_info *)&gModule_oss_ymf7xx, +// (module_info *)&gModule_osscore, + +#if 0 /* OLD */ + + + (module_info *)&gModule_oss_ali5455, + + (module_info *)&gModule_oss_allegro, + (module_info *)&gModule_oss_als300, + (module_info *)&gModule_oss_als4000, + (module_info *)&gModule_oss_apci97, + + (module_info *)&gModule_oss_atiaudio, + + (module_info *)&gModule_oss_audigyls, + //(module_info *)&gModule_oss_audioloop, + (module_info *)&gModule_oss_audiopci, + (module_info *)&gModule_oss_cmi8788, + (module_info *)&gModule_oss_cmpci, + (module_info *)&gModule_oss_cs4280, + (module_info *)&gModule_oss_cs4281, + (module_info *)&gModule_oss_digi32, + (module_info *)&gModule_oss_digi96, + (module_info *)&gModule_oss_emu10k1x, + (module_info *)&gModule_oss_envy24, + (module_info *)&gModule_oss_envy24ht, + (module_info *)&gModule_oss_fm801, + (module_info *)&gModule_oss_geode, + (module_info *)&gModule_oss_hdaudio, + + (module_info *)&gModule_oss_ich, + +#ifdef ENABLE_IMUX + (module_info *)&gModule_oss_imux, +#endif + (module_info *)&gModule_oss_maestro, + (module_info *)&gModule_oss_neomagic, + (module_info *)&gModule_oss_s3vibes, + (module_info *)&gModule_oss_sblive, +#ifdef ENABLE_SOFTOSS + //(module_info *)&gModule_oss_softoss, +#endif + (module_info *)&gModule_oss_solo, + (module_info *)&gModule_oss_trident, + (module_info *)&gModule_oss_via8233, + (module_info *)&gModule_oss_via97, + (module_info *)&gModule_oss_vortex, + (module_info *)&gModule_oss_ymf7xx, +#endif + + NULL +}; + +/* + * Driver hooks + */ + + +typedef struct ossdev_cookie { + int minor; /* index into cdevs[] */ + oss_cdev_t *cdev; + struct fileinfo file; +} ossdev_cookie_t; + +static int find_cdev(const char *name) +{ + int i, which = -1; + CORE_LOCK_VAR; +// if (strlen(name) < strlen(DEVICE_PREFIX)) +// return -1; + name += strlen(DEVICE_PREFIX); + + LOCK_CORE(); + for (i = 0; i < oss_num_cdevs; i++) + { + oss_cdev_t *cdev = oss_cdevs[i]; + //dprintf("oss:find_cdev: cdev %p, ->d %p, %s <> %s\n", cdev, cdev?cdev->d:NULL, cdev?cdev->name:"-", name); + if (cdev && cdev->d && !strncmp(cdev->name, name, 32)) + { + which = i; + break; + } + } + UNLOCK_CORE(); + + return which; +} + +static status_t +ossdrv_open(const char *name, uint32 oflags, void **cookie) +{ + ossdev_cookie_t *c; + status_t err; + int dev, tmpdev; + oss_cdev_t *cdev; + CORE_LOCK_VAR; + FENTRYA("%s, %ld, ", name, oflags); + + dev = find_cdev(name); + err = ENOENT; + if (dev < 0) + goto err1; + err = ENXIO; + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + goto err1; + + DDB (cmn_err + (CE_CONT, "oss_cdev_open(%d): %s, class=%d, instance=%d\n", dev, + cdev->name, cdev->dev_class, cdev->instance)); + + err = ENODEV; + if (cdev->d->open == NULL) + goto err1; + + err = ENOMEM; + c = malloc(sizeof(ossdev_cookie_t)); + if (!c) + goto err1; + + switch (oflags & O_RWMASK) + { + case O_RDONLY: + c->file.mode = OPEN_READ; + break; + case O_WRONLY: + c->file.mode = OPEN_WRITE; + break; + case O_RDWR: + c->file.mode = OPEN_READWRITE; + break; + default: + err = EINVAL; + goto err2; + } + //c->file.flags = oflags; + c->file.acc_flags = oflags; + + tmpdev = -1; + err = + cdev->d->open (cdev->instance, cdev->dev_class, &c->file, 0, 0, &tmpdev); + if (err < 0) + goto err3; + if (tmpdev > -1) + dev = tmpdev; + + c->minor = dev; + c->cdev = cdev = oss_cdevs[dev]; + + //XXX:locking + LOCK_CORE(); + oss_open_devices++; + //open_count[dev]++; + UNLOCK_CORE(); + + *cookie = c; + FEXITR(B_OK); + return B_OK; + +err3: +err2: + free(c); +err1: + FEXITR(err); + return err; +} + + +static status_t +ossdrv_close(void *cookie) +{ + FENTRY(); + FEXIT(); + return B_OK; +} + + +static status_t +ossdrv_freecookie(ossdev_cookie_t *cookie) +{ + oss_cdev_t *cdev = cookie->cdev; + int dev = cookie->minor; + CORE_LOCK_VAR; + status_t err; + FENTRY(); + + err = ENXIO; + if (dev >= OSS_MAX_CDEVS) + goto err; + +#if 0 // not safe (not locked) + err = B_OK; + if (open_count[dev] == 0) /* Not opened */ + goto err; +#endif + + err = ENXIO; + if (cdev != oss_cdevs[dev]) + goto err; + + //in close or free ?? + cdev->d->close (cdev->instance, &cookie->file); + + LOCK_CORE(); + oss_open_devices--; + //open_count[dev]--; + UNLOCK_CORE(); + + free(cookie); + err = B_OK; +err: + FEXITR(err); + return B_OK; +} + +/* note: IOC stuff seems to not collide with the base ioctls from Drivers.h + * as long as the type field is printable ascii (>32). Lucky are we. + */ +static status_t +ossdrv_ioctl(ossdev_cookie_t *cookie, uint32 op, void *buffer, size_t length) +{ + oss_cdev_t *cdev = cookie->cdev; + status_t err = ENXIO; + uint32 cmd = op; + int len = 0; + char buf[4096]; + FENTRYA(", %lx, , %ld", op, length); + + if ((cdev != oss_cdevs[cookie->minor]) || cdev->d->ioctl == NULL) + goto err1; + + if (cmd & (SIOC_OUT | SIOC_IN)) + { + + len = (cmd >> 16) & SIOCPARM_MASK; + if (len < 0) + len = 0; + if (len > sizeof (buf)) + { + cmn_err (CE_WARN, "Bad ioctl buffer size %d\n", len); + err = EFAULT; + goto err1; + } + + if ((cmd & SIOC_IN) && len > 0) + { + memcpy (buf, buffer, len); + //return EFAULT; + } + + } + + err = cdev->d->ioctl (cdev->instance, &cookie->file, cmd, (ioctl_arg) buf); + + if ((cmd & SIOC_OUT) && len > 0) + { + memcpy (buffer, buf, len); + //return EFAULT; + } + + +err1: + FEXITR(err); + return err; +} + + +static status_t +ossdrv_read(ossdev_cookie_t *cookie, off_t pos, void *buffer, size_t *_length) +{ + oss_cdev_t *cdev = cookie->cdev; + uio_t uio; + int count = *_length; + int err; + FENTRYA(", %Ld, , %ld", pos, *_length); + + err = ENXIO; + if ((cdev != oss_cdevs[cookie->minor]) || cdev->d->read == NULL) + goto err; + //files[dev].acc_flags = uiop->uio_fmode; + if ((err = oss_create_uio (&uio, buffer, count, UIO_READ, 0)) < 0) + goto err; + + err = cdev->d->read (cdev->instance, &cookie->file, &uio, count); + //*_length = (err > 0) ? err : 0; + if (err < 0) + goto err; + + *_length = err; + err = B_OK; + FEXITR(err); + return err; + +err: + *_length = 0; + FEXITR(err); + return err; +} + + +static status_t +ossdrv_write(ossdev_cookie_t *cookie, off_t pos, const void *buffer, size_t *_length) +{ + oss_cdev_t *cdev = cookie->cdev; + uio_t uio; + int count = *_length; + int err; + FENTRYA(", %Ld, , %ld", pos, *_length); + + err = ENXIO; + if ((cdev != oss_cdevs[cookie->minor]) || cdev->d->write == NULL) + goto err; + //files[dev].acc_flags = uiop->uio_fmode; + if ((err = oss_create_uio (&uio, (void *)buffer, count, UIO_WRITE, 0)) < 0) + goto err; + + err = cdev->d->write (cdev->instance, &cookie->file, &uio, count); + //*_length = (err > 0) ? err : 0; + if (err < 0) + goto err; + + *_length = err; + err = B_OK; + FEXITR(err); + return err; + +err: + *_length = 0; + FEXITR(err); + return err; +} + + +device_hooks oss_driver_hooks = { + &ossdrv_open, + &ossdrv_close, + (device_free_hook)&ossdrv_freecookie, + (device_control_hook)&ossdrv_ioctl, + (device_read_hook)&ossdrv_read, + (device_write_hook)&ossdrv_write, + /* Leave select/deselect/readv/writev undefined. The kernel will + * use its own default implementation. The basic hooks above this + * line MUST be defined, however. */ + NULL, + NULL, + NULL, + NULL +}; diff --git a/kernel/OS/BeOS/os_beos.h b/kernel/OS/BeOS/os_beos.h new file mode 100644 index 0000000..1e8f0b5 --- /dev/null +++ b/kernel/OS/BeOS/os_beos.h @@ -0,0 +1,471 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for BeOS/Haiku + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2007. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution. Please contact sales@opensound.com for further info. + * + */ +#define OS_VERSION "5" +#define __inline__ inline +#define __inline inline +#define EXTERN_C extern "C" + +/* + * Debugging and misc settings + */ +#undef DO_TIMINGS +#undef MUTEX_CHECKS +#undef MEMDEBUG +/* very verbose */ +//#define DO_DEBUG_FUNC_CALLS +/* XXX */ +#define DEBUG_KDLCMD 1 +//#define ENABLE_IMUX 1 +//#define ENABLE_SOFTOSS 1 + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ +//Disable?? +//#define APPLIST_SUPPORT +#define USE_DEVICE_SUBDIRS +/* no VDEV */ +#undef VDEV_SUPPORT + +//XXX: try to avoid crashing +#define MIDI_DISABLED 1 + +#include <OS.h> +#include <stdarg.h> +//#include <stdint.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/param.h> +#include <oss_errno.h> +#include <sys/uio.h> +#include <fcntl.h> +#ifdef __HAIKU__ +#include <poll.h> +#endif +//#include <sys/malloc.h> +#include <malloc.h> + + +#include <KernelExport.h> +#include <Drivers.h> +#include <PCI.h> + +#ifndef ENOTSUP +#define ENOTSUP ENOSYS +#endif + +#define OSS_CONTIG_AREA_NAME "OSS CONTIG MEM" +#define OSS_PCI_AREA_NAME "OSS PCI MEM" +#define OSS_WQ_SEM_NAME "OSS WAITQ: " + +//#define DEVICE_PREFIX "audio/oss/" +#define DEVICE_PREFIX "" +#define DEVICE_NAME_LEN (32+sizeof(DEVICE_PREFIX)) + +#define OSS_CONFIG_FILE_PREFIX "oss_" + +#ifdef _KERNEL_MODE + +/* references to modules */ +extern pci_module_info *gPCI; + +#endif /* _KERNEL_MODE */ + +/* + * Some integer types + */ +#if defined(amd64) || defined(sparc) // 64bit ? not yet +typedef uint64 oss_native_word; /* Same as the address and status register size */ +#else +typedef uint32 oss_native_word; /* Same as the address and status register size */ +#endif + +typedef int64 oss_int64_t; /* Signed 64 bit integer */ +typedef uint64 oss_uint64_t; /* Unsigned 64 bit integer */ +typedef unsigned long offset_t; + + +extern void oss_cmn_err (int level, char *format, ...); +#define CE_CONT 0 +#define CE_NOTE 1 +#define CE_WARN 2 +#define CE_PANIC 3 +#define cmn_err oss_cmn_err + +//snooze(1000000); \ + +#ifdef DO_DEBUG_FUNC_CALLS +/* verbose debugging */ +#define FENTRY() \ +dprintf("oss>%s()\n", __FUNCTION__) +#define FENTRYA(f,a...) \ +dprintf("oss>%s(" f ")\n", __FUNCTION__, a) +#define FEXIT() \ +dprintf("oss<%s\n", __FUNCTION__) +#define FEXITR(e) \ +dprintf("oss<%s returned 0x%08lx\n", __FUNCTION__, (uint32)e) +#else +#define FENTRY() {} +#define FENTRYA(f,a...) {} +#define FEXIT() {} +#define FEXITR(e) {} +#endif + +struct _dev_info_t +{ + //struct pci_dev *pcidev; + struct pci_info pciinfo; //XXX: ptr or not ??? +}; +typedef struct _dev_info_t dev_info_t; + + +/* + * timeout wrappers + */ +#undef timeout +#define timeout oss_timeout +#undef untimeout +#define untimeout oss_untimeout +typedef int timeout_id_t; +extern timeout_id_t oss_timeout (void (*func) (void *), void *arg, + unsigned long long ticks); +extern void oss_untimeout (timeout_id_t id); + +#define uiomove oss_uiomove +typedef enum uio_rw +{ UIO_READ, UIO_WRITE } uio_rw_t; +struct uio +{ + char *ptr; + int resid; + int kernel_space; /* Set if this uio points to a kernel space buffer */ + uio_rw_t rw; +}; +typedef struct uio uio_t; +extern int oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, + uio_t * uio); +extern int oss_create_uio (uio_t * uiop, char *buf, size_t count, uio_rw_t rw, + int is_kernel); + + + +/* + * Mutexes + */ + +#ifdef MUTEX_CHECKS +/* Debugging version */ +struct _oss_mutex_t +{ + spinlock lock; + thread_id owner; + char *file; + int line; + int busy_flags; +#define CNTX_INTR 1 +#define CNTX_USER 2 +}; + +typedef struct _oss_mutex_t oss_mutex_t; +extern void debug_mutex_init (oss_mutex_t * mutex, char *file, int line); +extern void debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line); +extern void debug_mutex_enter (oss_mutex_t * mutex, char *file, int line, oss_native_word *flags); +extern void debug_mutex_exit (oss_mutex_t * mutex, char *file, int line, oss_native_word *flags); + +#define MUTEX_INIT(osdev, mutex, hier) debug_mutex_init(&mutex, NULL, __FILE__, __LINE__) +#define MUTEX_CLEANUP(mutex) debug_mutex_destroy(&mutex, __FILE__, __LINE__) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) debug_mutex_enter(&mutex, __FILE__, __LINE__, &flags) +#define MUTEX_ENTER(mutex, flags) debug_mutex_enter(&mutex, __FILE__, __LINE__, NULL) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) debug_mutex_exit(&mutex, __FILE__, __LINE__, &flags) +#define MUTEX_EXIT(mutex, flags) debug_mutex_exit(&mutex, __FILE__, __LINE__, NULL) +#else +typedef spinlock oss_mutex_t; +#define MUTEX_INIT(osdev, mutex, hier) { mutex = 0; } +#define MUTEX_CLEANUP(mutex) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) \ +{ \ + flags = (oss_native_word) disable_interrupts(); \ + acquire_spinlock(&(mutex)); \ +} +#define MUTEX_ENTER(mutex, flags) acquire_spinlock(&(mutex)) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) \ +{ \ + release_spinlock(&(mutex)); \ + restore_interrupts((cpu_status)(flags)); \ +} +#define MUTEX_EXIT(mutex, flags) release_spinlock(&(mutex)) +#endif + +/* The soundcard.h could be in a nonstandard place so include it here. */ +#include "soundcard.h" + +typedef struct udi_usb_devc udi_usb_devc; +struct _oss_device_t +{ +#ifdef NEEDED_FOR_DRIVERS + int instance; + void *devc; + char *name; +#endif + + int cardnum; + int dev_type; + int instance; + int available; + dev_info_t *dip; + int unloaded; + void *osid; + void *devc; + char *name; + char *hw_info; + char nick[16]; + char modname[32]; + char handle[32]; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +/* Interrupts */ + + long irq; //XXX:init =-1 + oss_tophalf_handler_t tophalf_handler; + oss_bottomhalf_handler_t bottomhalf_handler; + oss_mutex_t mutex; + +}; + +/* XXX we'll deal with select() later */ +#undef ALLOW_SELECT +/* BeOS doesn't support mmap. Haiku does, so FIXME someday */ +#undef ALLOW_BUFFER_MAPPING + +/* + * Sleep/wakeup + */ + +#ifdef _KERNEL +struct oss_wait_queue +{ +//XXX: +// oss_mutex_t mutex; + char name[32]; + sem_id sem; + unsigned long flags; +// struct selinfo poll_info; +}; +#endif + +/* Busy wait routine */ +#define oss_udelay(d) spin((bigtime_t)d) + + +/* usec time base */ +#undef HZ +/* XXX: might need to be lower to handle casts to long */ +#define OSS_HZ 1000000 + +/* System wall timer access */ +extern unsigned long oss_get_time (void); +#define GET_JIFFIES() oss_get_time() + + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +/* I/O Mapped devices */ +#define INB(o, p) gPCI->read_io_8(p) +#define INW(o, p) gPCI->read_io_16(p) +#define INL(o, p) gPCI->read_io_32(p) + +#define OUTB(o, v, p) gPCI->write_io_8(p,v) +#define OUTW(o, v, p) gPCI->write_io_16(p,v) +#define OUTL(o, v, p) gPCI->write_io_32(p,v) + +/* Memory Mapped devices */ +#define PCI_READB(osdev, addr) *(volatile unsigned char *)(addr) +#define PCI_READW(osdev, addr) *(volatile unsigned short *)(addr) +#define PCI_READL(osdev, addr) *(volatile unsigned int *)(addr) + +#define PCI_WRITEB(osdev, addr, data) *(volatile unsigned char *)(addr)=data +#define PCI_WRITEW(osdev, addr, data) *(volatile unsigned short *)(addr)=data +#define PCI_WRITEL(osdev, addr, data) *(volatile unsigned int *)(addr)=data + +typedef void *oss_dma_handle_t; + +/* + * When a error (such as EINVAL) is returned by a function, + * the following macro is used. The driver assumes that a + * error is signalled by returning a negative value. + */ + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#define KERNEL_MALLOC(nbytes) calloc(1,nbytes) +#define KERNEL_FREE(addr) {if (addr)free(addr);addr=NULL;} + +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +extern oss_native_word oss_virt_to_bus (void *addr); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) + +/* + * PMALLOC is used to allocate memory that will get automatically freed when + * OSS unloads. Usable for per-instance structures allocated when OSS modules + * are being loaded. + */ +extern void *oss_pmalloc (size_t sz); + +//#define PMALLOC(osdev, sz) oss_pmalloc(sz) + +/* + * Timer macros + * + * These macros are obsolete and should not be used in any new code. + * Use the timeout mechanism (see the timeout(9F) Solaris man page). + */ +#define DEFINE_TIMER(name, proc) static timeout_id_t name = 0 +#define REMOVE_TIMER(name, proc) {if (name != 0) untimeout(name);} +#define INIT_TIMER(name,proc) +typedef void (*timeout_func_t) (void *); +#define ACTIVATE_TIMER(name, proc, time) \ + name=timeout((timeout_func_t)proc, (void*)&name, time) +#endif + +struct fileinfo +{ + int mode; /* Open mode */ + //int flags; + int acc_flags; +}; +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +#define OSS_OS "BeOS" +#define OSS_OS_LONGNAME "BeOS R" OS_VERSION + +typedef void (*softintr_func_t) (int); + +struct oss_softintr +{ + int id; + softintr_func_t func; + volatile int armed, running; +}; + +struct _oss_poll_event_t +{ + short events, revents; + struct thread *p; + struct cdev *bsd_dev; +}; +typedef struct _oss_poll_event_t oss_poll_event_t; +#ifndef POLLIN +/* events & revents - compatible with the B_SELECT_xxx definitions in Drivers.h */ +#define POLLIN 0x0001 /* any readable data available */ +#define POLLOUT 0x0002 /* file descriptor is writeable */ +#define POLLRDNORM POLLIN +#define POLLWRNORM POLLOUT +#define POLLRDBAND 0x0008 /* priority readable data */ +#define POLLWRBAND 0x0010 /* priority data can be written */ +#define POLLPRI 0x0020 /* high priority readable data */ +#endif + +extern int detect_trace; +#define DDB(x) if (detect_trace) x + +extern caddr_t oss_map_pci_mem (oss_device_t * osdev, int nr, int phaddr, + int size); +extern void oss_unmap_pci_mem (void *addr); +#define MAP_PCI_IOADDR(osdev, nr, io) (oss_native_word)io +#define MAP_PCI_MEM(osdev, ix, phaddr, size) oss_map_pci_mem(osdev, ix, phaddr, size) +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) oss_unmap_pci_mem(virt) +#define UNMAP_PCI_IOADDR(osdev, ix) {} + +#define GET_PROCESS_PID(f) find_thread(NULL) +#define GET_PROCESS_UID() getuid() +#define GET_PROCESS_NAME(f) NULL + +#define abs(x) ((x) >= 0 ? (x) : -(x)) + +/* + * Interface with the front-end driver + */ +extern status_t init_osscore (void); +extern status_t uninit_osscore (void); +extern status_t oss_probe_pci (void); +extern status_t oss_load_drivers (void); +extern status_t oss_unload_all_drivers (void); + +/* + * osscore module for use by low-level driver + */ +#define OSS_CORE_MODULE_NAME "media/oss/oss_core/v1" +#define OSS_MODULES_PREFIX "media/oss/drivers/" +#define OSS_MODULES_SUFFIX "/v1" +#define OSS_MAKE_DRV_MOD_NAME(nick) OSS_MODULES_PREFIX nick OSS_MODULES_SUFFIX +typedef struct oss_drv_module_info { + module_info minfo; + int (*driver_probe)(void); + int (*driver_attach)(oss_device_t *osdev); + int (*driver_detach)(oss_device_t *osdev); +} oss_drv_module_info; + +typedef struct oss_core_module_info { + module_info minfo; + status_t (*init_osscore) (void); + status_t (*uninit_osscore) (void); + const char **(*oss_publish_devices) (void); + device_hooks *(*oss_get_driver_hooks) (void); + status_t (*oss_probe_pci) (void); + status_t (*oss_load_drivers) (void); + status_t (*oss_unload_all_drivers) (void); +} oss_core_module_info; +extern oss_core_module_info *gOSSCore; + +//XXX: not yet +#ifdef ASMODULES + +struct oss_core_module { + module_info minfo; + void (*oss_foo)(void); +}; +extern struct oss_core_info *gOSSCore; +#ifdef BUILDING_DRIVER + #define oss_foo gOSSCore->oss_foo +#endif +#endif /* ASMODULES */ diff --git a/kernel/OS/FreeBSD/.config b/kernel/OS/FreeBSD/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/FreeBSD/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/FreeBSD/os_freebsd.c b/kernel/OS/FreeBSD/os_freebsd.c new file mode 100644 index 0000000..58b11cc --- /dev/null +++ b/kernel/OS/FreeBSD/os_freebsd.c @@ -0,0 +1,1107 @@ +/* + * Purpose: Operating system abstraction functions for FreeBSD + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_config.h" +#include "midi_core.h" +#include <oss_pci.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/sx.h> +#include <sys/mman.h> +#include <sys/lockmgr.h> +#include <fs/devfs/devfs.h> +#include <sys/poll.h> +#include <sys/param.h> +#include <sys/filio.h> + +/* Function prototypes */ +static d_open_t oss_open; +static d_close_t oss_close; +static d_read_t oss_read; +static d_write_t oss_write; +static d_ioctl_t oss_ioctl; +static d_poll_t oss_poll; +static d_mmap_t oss_mmap; + +/* Character device entry points */ + +static struct cdevsw oss_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_TRACKCLOSE, + .d_open = oss_open, + .d_close = oss_close, + .d_read = oss_read, + .d_write = oss_write, + .d_ioctl = oss_ioctl, + .d_poll = oss_poll, + .d_mmap = oss_mmap, + .d_name = "oss" +}; + +static int oss_major = 30; +oss_device_t *core_osdev = NULL; +static int open_devices = 0; +static int refcount = 0; + +#define MAX_CARDS 32 +int oss_num_cards = 0; +static oss_device_t *cards[MAX_CARDS]; +static int oss_expired = 0; + + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + void *tmpbuf; + oss_native_word phaddr; + int size = 64 * 1024; + extern int dma_buffsize; + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + + if (dmap->dmabuf != NULL) + return 0; + + if (dmap == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: dmap==NULL\n"); + return OSS_EIO; + } + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + tmpbuf = CONTIG_MALLOC (dmap->osdev, size, maxaddr, &phaddr, NULL); + if (tmpbuf == NULL) + return OSS_ENOMEM; + dmap->dmabuf = tmpbuf; + dmap->buffsize = size; + dmap->dmabuf_phys = phaddr; + + return 0; +} + +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + if (dmap->dmabuf == NULL) + return; + + CONTIG_FREE (dmap->osdev, dmap->dmabuf, dmap->buffsize, NULL); + dmap->dmabuf = NULL; + dmap->buffsize = 0; + dmap->dmabuf_phys = 0; +} + +oss_wait_queue_t * +oss_create_wait_queue (oss_device_t * osdev, const char *name) +{ + oss_wait_queue_t *wq; + + if ((wq = PMALLOC (NULL, sizeof (*wq))) == NULL) + return NULL; + + MUTEX_INIT (osdev, wq->mutex, MH_TOP); + + return wq; +} + +void +oss_reset_wait_queue (oss_wait_queue_t * wq) +{ + wq->flags = 0; +} + +void +oss_remove_wait_queue (oss_wait_queue_t * wq) +{ + MUTEX_CLEANUP (wq->mutex); +} + +int +oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ + int flag; + *status = 0; + + if (wq == NULL) + return 0; + + wq->flags = 0; +#ifdef USE_SX_LOCK + flag = sx_sleep (wq, *mutex, PRIBIO | PCATCH, "oss", ticks); +#else +#if __FreeBSD_version >= 602000 + flag = msleep_spin (wq, *mutex, "oss", ticks); +#else + flag = msleep (wq, *mutex, PRIBIO | PCATCH, "oss", ticks); +#endif +#endif + if (flag == EWOULDBLOCK) /* Timeout */ + { + return 0; + } + + if (flag != 0) + { + *status |= WK_SIGNAL; + } + + return 1; +} + +int +oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev) +{ + MUTEX_EXIT_IRQRESTORE (*mutex, *flags); + selrecord (ev->p, &wq->poll_info); + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); + return 0; +} + +void +oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + MUTEX_EXIT_IRQRESTORE (*mutex, *flags); + wakeup (wq); + selwakeup (&wq->poll_info); + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); +} + +void +oss_register_module (char *name) +{ + refcount++; +} + +void +oss_unregister_module (char *name) +{ + refcount--; +} + +void +oss_reserve_device (oss_device_t * osdev) +{ + osdev->refcount++; +} + +void +oss_unreserve_device (oss_device_t * osdev, int decrement) +{ + osdev->refcount--; + if (osdev->refcount < 0) + osdev->refcount = 0; +} + +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + if (osdev->dip != NULL) + device_set_desc (osdev->dip, name); + return OSS_ENXIO; +} + +void +oss_unregister_device (oss_device_t * osdev) +{ + // NOP +} + +int +oss_find_minor (int dev_class, int instance) +{ + int i; + + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class + && oss_cdevs[i]->instance == instance) + return i; + + return OSS_EIO; +} + +void * +oss_find_minor_info (int dev_class, int instance) +{ + int i; + + for (i = 0; i < oss_num_cdevs; i++) + { + if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class + && oss_cdevs[i]->instance == instance) + return oss_cdevs[i]->info; + } + + return NULL; +} + +int +oss_disable_device (oss_device_t * osdev) +{ + int i; + + if (osdev->refcount > 0) + return OSS_EBUSY; + + if (open_devices > 0) + return OSS_EBUSY; + + for (i = 0; i < num_mixers; i++) + if (mixer_devs[i]->osdev == osdev) + { + mixer_devs[i]->unloaded = 1; + } + + for (i = 0; i < num_mididevs; i++) + { + if (midi_devs[i]->osdev == osdev) + { + midi_devs[i]->unloaded = 1; + } + } + + for (i = 0; i < num_audio_engines; i++) + if (audio_engines[i]->osdev == osdev) + { + audio_uninit_device (i); + } + return 0; +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + if (cardnum < 0 || cardnum >= oss_num_cards) + return OSS_ENXIO; + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->shortname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info)); + ci->hw_info[sizeof (ci->hw_info) - 1] = 0; + ci->intr_count = cards[cardnum]->intrcount; + ci->ack_count = cards[cardnum]->ackcount; + + return 0; +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * element_size))==NULL) + return 0; + + memset(new, 0, new_size * element_size); + if (old != NULL) + memcpy(new, old, old_size * element_size); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ + struct cdev *bsd_cdev; + oss_cdev_t *cdev = NULL; + int i, num; + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; +/* + * Find if this dev_class&instance already exists (after previous module + * detach). + */ + + for (num = 0; num < oss_num_cdevs; num++) + if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ + if (oss_cdevs[num]->dev_class == dev_class + && oss_cdevs[num]->instance == instance) + { + cdev = oss_cdevs[num]; + break; + } + + if (cdev == NULL) + { + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) + { + cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); + return; + } + } + + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + num = oss_num_cdevs++; + } + + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name) - 1); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + + if (!(flags & CHDEV_VIRTUAL) && (name != NULL)) + { + bsd_cdev = + make_dev (&oss_cdevsw, num, UID_ROOT, GID_WHEEL, 0666, name, 0); + cdev->info = bsd_cdev; + } +} + +#define MAX_MEMBLOCKS 4096 +static void *memblocks[MAX_MEMBLOCKS]; +static int nmemblocks = 0; + +void * +oss_pmalloc (size_t sz) +{ + void *tmp; + + tmp = KERNEL_MALLOC (sz); + + if (nmemblocks < MAX_MEMBLOCKS) + memblocks[nmemblocks++] = tmp; + + return tmp; +} + +int +oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, uio_t * uio_p) +{ + int err; + +#undef uiomove + err = uiomove (address, nbytes, uio_p); + + return err; +} + +#undef timeout +#undef untimeout + +typedef struct tmout_desc +{ + volatile int active; + int timestamp; + void (*func) (void *); + void *arg; + + struct callout_handle timer; +} tmout_desc_t; + +static volatile int next_id = 0; +#define MAX_TMOUTS 128 + +tmout_desc_t tmouts[MAX_TMOUTS] = { {0} }; + +int timeout_random = 0x12123400; + +void +oss_timer_callback (void *arg) +{ + int ix; + tmout_desc_t *tmout = arg; + + timeout_random++; + + if (!tmout->active) + return; + + arg = tmout->arg; + tmout->active = 0; + tmout->timestamp = 0; + + tmout->func (arg); +} + +timeout_id_t +oss_timeout (void (*func) (void *), void *arg, unsigned long long ticks) +{ + tmout_desc_t *tmout = NULL; + int id, n; + + timeout_random++; + + n = 0; + id = -1; + + while (id == -1 && n < MAX_TMOUTS) + { + if (!tmouts[next_id].active) + { + tmouts[next_id].active = 1; + id = next_id++; + tmout = &tmouts[id]; + break; + } + + next_id = (next_id + 1) % MAX_TMOUTS; + } + + if (id == -1) /* No timer slots available */ + { + cmn_err (CE_CONT, "Timeout table full\n"); + return 0; + } + + tmout->func = func; + tmout->arg = arg; + tmout->timestamp = id | (timeout_random & ~0xff); + + tmout->timer = timeout (oss_timer_callback, tmout, ticks); + + return id | (timeout_random & ~0xff); +} + +void +oss_untimeout (timeout_id_t id) +{ + tmout_desc_t *tmout; + int ix; + + ix = id & 0xff; + if (ix < 0 || ix >= MAX_TMOUTS) + return; + + timeout_random++; + tmout = &tmouts[ix]; + + if (tmout->timestamp != id) /* Expired timer */ + return; + if (tmout->active) + untimeout (oss_timer_callback, tmout, tmout->timer); + tmout->active = 0; + tmout->timestamp = 0; +} + +int +oss_get_procinfo (int what) +{ + switch (what) + { + case OSS_GET_PROCINFO_UID: + return oss_get_uid(); + } + + return EINVAL; +} + +unsigned long +oss_get_time (void) +{ + struct timeval timecopy; + + getmicrotime (&timecopy); + return timecopy.tv_usec / (1000000 / hz) + (u_long) timecopy.tv_sec * hz; +} + +caddr_t +oss_map_pci_mem (oss_device_t * osdev, int nr, int paddr, int psize) +{ + void *vaddr = 0; + u_int32_t poffs; + + poffs = paddr - trunc_page (paddr); + vaddr = (caddr_t) pmap_mapdev (paddr - poffs, psize + poffs) + poffs; + + return vaddr; +} + +void +oss_pci_byteswap (oss_device_t * osdev, int mode) +{ + // NOP +} + +void +oss_pcie_init (oss_device_t * osdev, int flags) +{ + /* TODO: Should we do something? */ +} + +static time_t +oss_get_walltime (void) +{ + struct timeval timecopy; + + getmicrotime (&timecopy); + return timecopy.tv_sec; +} + +int +soundcard_attach (void) +{ + oss_device_t *osdev; + + if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) + { + return ENOSPC; + } + + memset (osdev, 0, sizeof (*osdev)); + core_osdev = osdev; + +#ifdef LICENSED_VERSION + if (!oss_license_handle_time (oss_get_walltime ())) + { + cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); + cmn_err (CE_CONT, + "Please download the latest version from www.opensound.com\n"); + oss_expired = 1; + } +#endif + + osdev->major = oss_major; + oss_register_device (osdev, "OSS core services"); + + oss_common_init (osdev); + + return 0; +} + +int +soundcard_detach (void) +{ + int i; + + if (refcount > 0 || open_devices > 0) + return EBUSY; + + oss_unload_drivers (); + + osdev_delete (core_osdev); + + for (i = 0; i < nmemblocks; i++) + KERNEL_FREE (memblocks[i]); + nmemblocks = 0; + + return 0; +} + +static void +init_fileinfo (struct fileinfo *fi, int flags) +{ + memset (fi, 0, sizeof (*fi)); + if ((flags & FREAD) && (flags & FWRITE)) + fi->mode = OPEN_READWRITE; + else if (flags & FREAD) + fi->mode = OPEN_READ; + else if (flags & FWRITE) + fi->mode = OPEN_WRITE; + fi->acc_flags = flags; + fi->pid = -1; + fi->dev = -1; + fi->cmd = NULL; +} + +static int +oss_read (struct cdev *bsd_dev, struct uio *buf, int flags) +{ + int retval; + int dev; + oss_cdev_t *cdev; +#ifndef VDEV_SUPPORT + struct fileinfo _fi, * fi = &_fi; + dev = MINOR (bsd_dev); + init_fileinfo (fi, flags); +#else + struct fileinfo * fi; + if (oss_file_get_private ((void **)&fi)) return ENXIO; + dev = fi->dev; +#endif + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (cdev->d->read == NULL) + { + return ENODEV; + } + + retval = cdev->d->read (cdev->instance, fi, buf, buf->uio_resid); + if (retval < 0) + return -retval; + return 0; + +} + +static int +oss_write (struct cdev *bsd_dev, struct uio *buf, int flags) +{ + int retval; + int dev; + oss_cdev_t *cdev; +#ifndef VDEV_SUPPORT + struct fileinfo _fi, * fi = &_fi; + dev = MINOR (bsd_dev); + init_fileinfo (fi, flags); +#else + struct fileinfo * fi; + if (oss_file_get_private ((void **)&fi)) return ENXIO; + dev = fi->dev; +#endif + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (cdev->d->write == NULL) + { + return ENODEV; + } + + retval = cdev->d->write (cdev->instance, fi, buf, buf->uio_resid); + if (retval < 0) + return -retval; + return 0; +} + +static int +oss_open (struct cdev *bsd_dev, int flags, int mode, struct thread *p) +{ + int dev = MINOR (bsd_dev); + oss_cdev_t *cdev; + struct fileinfo fi; + int tmpdev, retval; + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (cdev->d->open == NULL) + { + return ENODEV; + } + + init_fileinfo (&fi, flags); + fi.pid = p->td_proc->p_pid; + fi.cmd = p->td_proc->p_comm; + tmpdev = dev; + + retval = + cdev->d->open (cdev->instance, cdev->dev_class, &fi, 0, 0, &tmpdev); + + if (tmpdev != -1) fi.dev = tmpdev; + else fi.dev = dev; + if (retval < 0) + return -retval; + +#ifdef VDEV_SUPPORT + if (oss_file_set_private (p, (void *)&fi, sizeof (struct fileinfo))) + return ENXIO; +#endif + + open_devices++; + return 0; +} + +static int +oss_close (struct cdev *bsd_dev, int flags, int mode, struct thread *p) +{ + int dev; + oss_cdev_t *cdev; +#ifndef VDEV_SUPPORT + struct fileinfo _fi, * fi = &_fi; + dev = MINOR (bsd_dev); + init_fileinfo (fi, flags); +#else + struct fileinfo * fi; + if (oss_file_get_private ((void **)&fi)) return ENXIO; + dev = fi->dev; +#endif + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (cdev->d->close == NULL) + { + return ENODEV; + } + + cdev->d->close (cdev->instance, fi); + open_devices--; + return 0; +} + +static int +oss_ioctl (struct cdev *bsd_dev, u_long cmd, caddr_t arg, int mode, + struct thread *p) +{ + int retval; + int dev; + oss_cdev_t *cdev; +#ifndef VDEV_SUPPORT + struct fileinfo _fi, * fi = &_fi; + dev = MINOR (bsd_dev); + init_fileinfo (fi, mode); +#else + struct fileinfo * fi; + if (oss_file_get_private ((void **)&fi)) return ENXIO; + dev = fi->dev; +#endif + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (cdev->d->ioctl == NULL) + { + return ENODEV; + } + + switch (cmd) + { + /* + * FreeBSD uses these ioctls to (un)set nonblocking I/O for devices. e.g. + * in case of fcntl (fd, F_SETFL, O_RDONLY|O_NONBLOCK) it will fire + * both ioctls. + * We deal with them here, and not in oss_audio_core because struct file + * isn't available in oss_audio_ioctl and we want the flags to remain + * accurate. oss_audio_core checks for O_NONBLOCK, and will pick + * up the change next read/write. + */ + case FIONBIO: + if (arg != NULL) + { + if (*arg) fi->acc_flags |= O_NONBLOCK; + else fi->acc_flags &= ~O_NONBLOCK; + } + case FIOASYNC: + return 0; + default: break; + } + + retval = cdev->d->ioctl (cdev->instance, fi, cmd, (ioctl_arg) arg); + if (retval < 0) + return -retval; + return 0; +} + +static int +oss_poll (struct cdev *bsd_dev, int events, struct thread *p) +{ + int retval; + int dev; + oss_cdev_t *cdev; + oss_poll_event_t ev; + int err; +#ifndef VDEV_SUPPORT + struct fileinfo _fi, * fi = &_fi; + dev = MINOR (bsd_dev); + init_fileinfo (fi, 0); +#else + struct fileinfo * fi; + if (oss_file_get_private ((void **)&fi)) return ENXIO; + dev = fi->dev; +#endif + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (cdev->d->chpoll == NULL) + { + return ENODEV; + } + + ev.events = events; + ev.revents = 0; + ev.p = p; + ev.bsd_dev = bsd_dev; + + err = cdev->d->chpoll (cdev->instance, fi, &ev); + if (err < 0) + { + return -err; + } + return ev.revents; +} + +#if defined(D_VERSION_03) && (D_VERSION == D_VERSION_03) +static int +oss_mmap (struct cdev *bsd_dev, vm_ooffset_t offset, vm_paddr_t * paddr, + int nprot, vm_memattr_t *memattr) +#else +static int +oss_mmap (struct cdev *bsd_dev, vm_offset_t offset, vm_paddr_t * paddr, + int nprot) +#endif +{ + int retval; + int dev; + oss_cdev_t *cdev; + oss_poll_event_t ev; + dmap_p dmap = NULL; + int err; +#ifndef VDEV_SUPPORT + dev = MINOR (bsd_dev); +#else + struct fileinfo * fi; + if (oss_file_get_private ((void **)&fi)) return ENXIO; + dev = fi->dev; +#endif + + if (dev >= oss_num_cdevs) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENXIO; + + if (nprot & PROT_EXEC) + return EACCES; + + if ((cdev->dev_class != OSS_DEV_DSP) && + (cdev->dev_class != OSS_DEV_DSP_ENGINE)) /* Only mmapable devices */ + { + cmn_err (CE_NOTE, "mmap() is only possible with DSP devices (%d)\n", + cdev->dev_class); + return EINVAL; + } + + dev = cdev->instance; + + if (dev < 0 || dev >= num_audio_engines) + return ENODEV; + + if (nprot & PROT_WRITE) + dmap = audio_engines[dev]->dmap_out; + else + dmap = audio_engines[dev]->dmap_in; + + if (dmap == NULL) + return EIO; + + if (dmap->dmabuf_phys == 0) + return EIO; + + if (dmap->flags & DMAP_COOKED) + { + cmn_err (CE_WARN, + "mmap() not possible with currently selected sample format.\n"); + return EIO; + } + + dmap->mapping_flags |= DMA_MAP_MAPPED; + *paddr = dmap->dmabuf_phys + offset; + + return 0; +} + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, + const char *handle) +{ + oss_device_t *osdev = NULL; + int i, err; + caddr_t addr; + off_t region_size; + + if (handle == NULL) + handle = nick; + + /* + * Don't accept any more drivers if expired + */ + if (oss_expired && oss_num_cards > 0) + return NULL; + + for (i = 0; i < oss_num_cards; i++) + { + if (cards[i]->dip == dip) + { + osdev = cards[i]; + break; + } + } + + if (osdev == NULL) + { + if (oss_num_cards >= MAX_CARDS) + { + cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + return NULL; + } + + if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + } + + osdev->dip = dip; + osdev->osid = dip; + osdev->available = 1; + osdev->instance = instance; + osdev->dev_type = dev_type; + osdev->devc = NULL; + osdev->first_mixer = -1; + sprintf (osdev->nick, "%s%d", nick, instance); + strcpy (osdev->modname, nick); + +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { + sprintf (osdev->handle, "OSS-PCI"); + } + break; + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + + if (osdev == NULL) + return; + + osdev->available = 0; +/* + * Mark all minor nodes for this module as invalid. + */ + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->osdev == osdev) + { + if (oss_cdevs[i]->info != NULL) + destroy_dev (oss_cdevs[i]->info); + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->info = NULL; + oss_cdevs[i]->osdev = NULL; + strcpy (oss_cdevs[i]->name, "Removed device"); + } +} + +void * +oss_get_osid (oss_device_t * osdev) +{ + return osdev->osid; +} + +void +oss_inc_intrcount (oss_device_t * osdev, int claimed) +{ + osdev->intrcount++; + + if (claimed) + osdev->ackcount++; +} diff --git a/kernel/OS/FreeBSD/os_freebsd.h b/kernel/OS/FreeBSD/os_freebsd.h new file mode 100644 index 0000000..161a2e3 --- /dev/null +++ b/kernel/OS/FreeBSD/os_freebsd.h @@ -0,0 +1,263 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for FreeBSD + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#define OS_VERSION "6" +#define __inline__ inline +#define __inline inline +#define EXTERN_C extern "C" + +/* + * Debugging and misc settings + */ +#undef MUTEX_CHECKS +#undef MEMDEBUG + +#if (!defined(__i386__) && !defined(__x86_64__)) || defined(CONFIG_OSS_FIXDEPOINT) +// Floating point is not supported or it's disabled +#undef CONFIG_OSS_VMIX_FLOAT +#endif + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ +#undef APPLIST_SUPPORT +#define USE_DEVICE_SUBDIRS + +#include <stdarg.h> +#include <sys/types.h> +#ifdef _KERNEL +#include <sys/systm.h> +#endif +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/malloc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/kernel.h> +#include <machine/cpufunc.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <sys/selinfo.h> +#include <oss_errno.h> + +#if __FreeBSD_version >= 800062 +#define MINOR(x) dev2unit(x) +#else +#define MINOR(x) minor(x) +#endif + +#undef timeout +#define timeout oss_timeout +#undef untimeout +#define untimeout oss_untimeout +typedef int timeout_id_t; +extern timeout_id_t oss_timeout (void (*func) (void *), void *arg, + unsigned long long ticks); +extern void oss_untimeout (timeout_id_t id); + +#include "kernel/OS/FreeBSD/wrapper/bsddefs.h" + +#ifdef USE_SX_LOCK +#include <sys/proc.h> /* XXX for curthread */ +#include <sys/sx.h> +#else +#include <sys/mutex.h> +#endif + + +#define uiomove oss_uiomove +typedef struct uio uio_t; +extern int oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, + uio_t * uio_p); + +#undef HZ +#define OSS_HZ hz + +/* The soundcard.h could be in a nonstandard place so include it here. */ +#include "soundcard.h" + +typedef struct udi_usb_devc udi_usb_devc; + +#define ALLOW_SELECT +#define ALLOW_BUFFER_MAPPING + +/* + * Sleep/wakeup + */ + +#ifdef _KERNEL +struct oss_wait_queue +{ + oss_mutex_t mutex; + unsigned long flags; + struct selinfo poll_info; +}; +#endif + +/* Busy wait routine */ + +/* System wall timer access */ +extern unsigned long oss_get_time (void); +#define GET_JIFFIES() oss_get_time() + +/* + * Mutexes + */ + +#ifdef USE_SX_LOCK +struct sx; +#define MUTEX_INIT(osdev, mutex, hier) \ +do { \ + mutex = malloc(sizeof(*mutex), M_DEVBUF, M_WAITOK | M_ZERO); \ + sx_init(mutex, "oss"); \ +} while (0) +#define MUTEX_CLEANUP(mutex) \ +do { \ + sx_destroy(mutex); \ + free(mutex, M_DEVBUF); \ +} while (0) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) sx_xlock(mutex) +#define MUTEX_ENTER(mutex, flags) sx_slock(mutex) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) sx_xunlock(mutex) +#define MUTEX_EXIT(mutex, flags) sx_sunlock(mutex) +#else /* !USE_SX_LOCK */ +struct mtx; +#define MUTEX_INIT(osdev, mutex, hier) \ +do { \ + mutex = malloc(sizeof(*mutex), M_DEVBUF, M_WAITOK | M_ZERO); \ + mtx_init(mutex, "oss", NULL, MTX_RECURSE | MTX_SPIN); \ +} while (0) +#define MUTEX_CLEANUP(mutex) \ +do { \ + mtx_destroy(mutex); \ + free(mutex, M_DEVBUF); \ +} while (0) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) mtx_lock_spin_flags(mutex, flags) +#define MUTEX_ENTER(mutex, flags) mtx_lock_spin(mutex) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) mtx_unlock_spin_flags(mutex, flags) +#define MUTEX_EXIT(mutex, flags) mtx_unlock_spin(mutex) +#endif /* USE_SX_LOCK */ + + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +/* I/O Mapped devices */ +#define INB(o, p) inb(p) +#define INW(o, p) inw(p) +#define INL(o, p) inl(p) + +#define OUTB(o, v, p) outb(p,v) +#define OUTW(o, v, p) outw(p,v) +#define OUTL(o, v, p) outl(p,v) + +/* Memory Mapped devices */ +#define PCI_READL(osp, p) readl(p) +#define PCI_READW(osp, p) readw(p) +#define PCI_READB(osp, p) readb(p) +#define PCI_WRITEL(osp, addr, data) writel(addr, data) +#define PCI_WRITEW(osp, addr, data) writew(addr, data) +#define PCI_WRITEB(osp, addr, data) writeb(addr, data) + +typedef void *oss_dma_handle_t; + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_DEVBUF, M_NOWAIT|M_ZERO) +#define KERNEL_FREE(addr) {if (addr)free(addr, M_DEVBUF);addr=NULL;} + +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(sz, memlimit, phaddr) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(p, sz) + +/* + * Timer macros + * + * These macros are obsolete and should not be used in any new code. + * Use the timeout mechanism (see the timeout(9F) Solaris man page). + */ +#define DEFINE_TIMER(name, proc) static timeout_id_t name = 0 +#define REMOVE_TIMER(name, proc) {if (name != 0) untimeout(name);} +#define INIT_TIMER(name,proc) +typedef void (*timeout_func_t) (void *); +#define ACTIVATE_TIMER(name, proc, time) \ + name=timeout((timeout_func_t)proc, (void*)&name, time) +#endif + +struct fileinfo +{ + int mode; /* Open mode */ + int acc_flags; + int pid; + int dev; + char *cmd; +}; +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +#define OSS_OS "FreeBSD" +#define OSS_OS_LONGNAME "FreeBSD " OS_VERSION + +typedef void (*softintr_func_t) (int); + +struct oss_softintr +{ + int id; + softintr_func_t func; + volatile int armed, running; +}; + +struct _oss_poll_event_t +{ + short events, revents; + struct thread *p; + struct cdev *bsd_dev; +}; +typedef struct _oss_poll_event_t oss_poll_event_t; + +extern int detect_trace; +#define DDB(x) if (detect_trace) x + +extern caddr_t oss_map_pci_mem (oss_device_t * osdev, int nr, int phaddr, + int size); +#define MAP_PCI_IOADDR(osdev, nr, io) (oss_native_word)(io) +#define MAP_PCI_MEM(osdev, ix, phaddr, size) oss_map_pci_mem(osdev, ix, phaddr, size) +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) {} +#define UNMAP_PCI_IOADDR(osdev, ix) {} + +#define GET_PROCESS_PID(f) f->pid +#define GET_PROCESS_NAME(f) f->cmd + +#define abs(x) ((x) >= 0 ? (x) : -(x)) + +/* + * PCI config space access (in os.c) + */ +extern char *oss_pci_read_devpath (dev_info_t * dip); diff --git a/kernel/OS/FreeBSD/wrapper/bsddefs.h b/kernel/OS/FreeBSD/wrapper/bsddefs.h new file mode 100644 index 0000000..c22965c --- /dev/null +++ b/kernel/OS/FreeBSD/wrapper/bsddefs.h @@ -0,0 +1,150 @@ +/* + * Purpose: Definitions for routines and variables exported by osscore.c + * + * Do not make any modifications to these settings because OSS core modules + * have been compiled against them. Full rebuild of OSS will be required if + * this file is changed. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include <sys/types.h> +#if 0 /* __FreeBSD_version >= 700031 */ +/* Some crashes have been reported with SX on 7-STABLE/8-CURRENT: + * http://4front-tech.com/forum/viewtopic.php?t=2718 + * http://4front-tech.com/forum/viewtopic.php?t=2563 + */ +#define USE_SX_LOCK 1 +#endif +#undef VDEV_SUPPORT +#if __FreeBSD_version >= 700111 +#define VDEV_SUPPORT +extern int oss_file_set_private (struct thread *p, void *v, size_t l); +extern int oss_file_get_private (void **v); +#endif +extern int oss_get_uid (void); + +typedef struct device dev_info_t; +typedef long long oss_int64_t; /* Signed 64 bit integer */ +typedef unsigned long long oss_uint64_t; /* Unsigned 64 bit integer */ +typedef unsigned long offset_t; + +/* + * Some integer types + */ +#if defined(__amd64__) +typedef unsigned long long oss_native_word; /* Same as the address and status register size */ +#else +typedef unsigned long oss_native_word; /* Same as the address and status register size */ +#endif + +struct _oss_device_t +{ + int cardnum; + int dev_type; + int instance; + int available; + dev_info_t *dip; + void *osid; + void *devc; + char *name; + char *hw_info; + int major; + char nick[16]; + char modname[16]; + char handle[32]; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + + int intrcount; + int ackcount; + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +}; + +extern void cmn_err (int level, char *format, ...); +#define CE_CONT 0 +#define CE_NOTE 1 +#define CE_WARN 2 +#define CE_PANIC 3 + +#ifdef USE_SX_LOCK +typedef struct sx *oss_mutex_t; +#else +typedef struct mtx *oss_mutex_t; +#endif + +typedef int ddi_iblock_cookie_t; + +extern void oss_udelay (unsigned long t); + +#ifdef _KERNEL +#define memset oss_memset +extern void *oss_memset (void *t, int val, int l); +#endif + +extern oss_device_t *osdev_create (dev_info_t * dip, int dev_type, + int instance, const char *nick, + const char *handle); +extern void osdev_delete (oss_device_t * osdev); + +extern char *oss_pci_read_devpath (dev_info_t * dip); +extern int pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val); +extern int pci_read_config_irq (oss_device_t * osdev, offset_t where, + unsigned char *val); +extern int pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val); +extern int pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val); +extern int pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val); +extern int pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val); +extern int pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val); +#ifndef OSS_CONFIG_H +/* These definitions must match with oss_config.h */ +typedef int (*oss_tophalf_handler_t) (struct _oss_device_t * osdev); +typedef void (*oss_bottomhalf_handler_t) (struct _oss_device_t * osdev); +#endif + +extern int oss_register_interrupts (oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom); +extern void oss_unregister_interrupts (oss_device_t * osdev); + +extern void *oss_contig_malloc (unsigned long sz, unsigned long memlimit, + oss_native_word * phaddr); +extern void oss_contig_free (void *p, unsigned long sz); + +extern void oss_register_module (char *name); +extern void oss_unregister_module (char *name); +extern void *oss_find_minor_info (int dev_class, int instance); +extern int oss_find_minor (int dev_class, int instance); +extern void oss_inc_intrcount (oss_device_t * osdev, int claimed); + +#define FP_SUPPORT + +#ifdef FP_SUPPORT +typedef short fp_env_t[512]; +typedef unsigned int fp_flags_t[4]; +extern int oss_fp_check (void); +extern void oss_fp_save (short *envbuf, fp_flags_t flags); +extern void oss_fp_restore (short *envbuf, fp_flags_t flags); +# define FP_SAVE(envbuf, flags) oss_fp_save(envbuf, flags) +# define FP_RESTORE(envbuf, flags) oss_fp_restore(envbuf, flags) +#endif diff --git a/kernel/OS/Linux/.config b/kernel/OS/Linux/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/Linux/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/Linux/os_linux.c b/kernel/OS/Linux/os_linux.c new file mode 100644 index 0000000..c47f3da --- /dev/null +++ b/kernel/OS/Linux/os_linux.c @@ -0,0 +1,1034 @@ +/* + * Purpose: Operating system abstraction functions for Linux + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include <oss_config.h> +#include <midi_core.h> + +/* + * OSS has traditionally used fixed character device number (14). However + * current OSS uses fully dynamic major number allocation. The legacy + * character device 14 is left for ALSA. + */ +static int osscore_major = 0; +static int oss_expired = 0; + +/* + * Number of cards supported in the same system. This should be largish + * because unplugging/replugging USB cards in wrong way may create + * large number of card instances. + */ +#define MAX_CARDS 32 + +static oss_device_t *cards[MAX_CARDS]; +int oss_num_cards = 0; + +void +oss_pci_byteswap (oss_device_t * osdev, int mode) +{ + // NOP +} + +int +oss_pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + return osscore_pci_read_config_byte (osdev->dip, where, val); +} + +int +oss_pci_read_config_irq (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + return osscore_pci_read_config_irq (osdev->dip, where, val); +} + +int +oss_pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + if (osdev == NULL) + { + cmn_err (CE_CONT, "oss_pci_read_config_word: osdev==NULL\n"); + return PCIBIOS_FAILED; + } + + return osscore_pci_read_config_word (osdev->dip, where, val); +} + +int +oss_pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + return osscore_pci_read_config_dword (osdev->dip, where, val); +} + +int +oss_pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + return osscore_pci_write_config_byte (osdev->dip, where, val); +} + +int +oss_pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + return osscore_pci_write_config_word (osdev->dip, where, val); +} + +int +oss_pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + return osscore_pci_write_config_dword (osdev->dip, where, val); +} + +int +oss_pci_enable_msi (oss_device_t * osdev) +{ + return osscore_pci_enable_msi (osdev->dip); +} + +int +oss_find_minor (int dev_class, int instance) +{ + int i; + + for (i = 0; i < oss_num_cdevs; i++) + { + if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class + && oss_cdevs[i]->instance == instance) + return i; + } + + return OSS_ENXIO; +} + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, + int instance, const char *nick, const char *handle) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + + memset (osdev, 0, sizeof (*osdev)); + + sprintf (osdev->nick, "%s%d", nick, instance); + osdev->instance = instance; + osdev->dip = dip; + osdev->available = 1; + osdev->first_mixer = -1; + + strcpy (osdev->modname, nick); + + if (handle == NULL) + handle = nick; + + if (oss_num_cards >= MAX_CARDS) + cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + else + { + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + } +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { + unsigned int subvendor; + char *devpath; + devpath = oss_pci_read_devpath (osdev->dip); + oss_pci_read_config_dword (osdev, 0x2c, &subvendor); + + sprintf (osdev->handle, "PCI%08x-%s", subvendor, devpath); + } + break; + + case DRV_USB: + // TODO: Get the vendor information + sprintf (osdev->handle, "USB-%s%d", handle, instance); + break; + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + + if (osdev == NULL) + return; + + osdev->available = 0; +/* + * Mark all minor nodes for this module as invalid. + */ + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->osdev == osdev) + { + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->osdev = NULL; + strcpy (oss_cdevs[i]->name, "Removed device"); + } +} + +void +osdev_set_owner (oss_device_t * osdev, struct module *owner) +{ + osdev->owner = owner; +} + +void +osdev_set_major (oss_device_t * osdev, int major) +{ + osdev->major = major; +} + +void +osdev_set_irqparms (oss_device_t * osdev, void *irqparms) +{ + osdev->irqparms = irqparms; +} + +void * +osdev_get_irqparms (oss_device_t * osdev) +{ + return osdev->irqparms; +} + +char * +osdev_get_nick (oss_device_t * osdev) +{ + return osdev->nick; +} + +int +osdev_get_instance (oss_device_t * osdev) +{ + return osdev->instance; +} + +void +oss_inc_intrcount (oss_device_t * osdev, int claimed) +{ + osdev->intrcount++; + + if (claimed) + osdev->ackcount++; +} + +struct module * +osdev_get_owner (oss_device_t * osdev) +{ + return osdev->owner; +} + +void * +oss_get_osid (oss_device_t * osdev) +{ + return NULL; // TODO +} + +int +oss_get_procinfo(int what) +{ + switch (what) + { + case OSS_GET_PROCINFO_UID: + return oss_get_uid(); + break; + } + + return OSS_EINVAL; +} + +int +oss_disable_device (oss_device_t * osdev) +{ + int i; + + if (osdev->major > 0) + oss_unregister_chrdev (osdev->major, osdev->nick); + osdev->major = 0; + +/* + * Now mark all devices unavailable (for the time being) + * TODO: Mobe this stuff to some common OSS module (also in Solaris) + */ + if (osdev->refcount > 0) + { + return OSS_EBUSY; + } + + for (i = 0; i < num_mixers; i++) + if (mixer_devs[i]->osdev == osdev) + { + mixer_devs[i]->unloaded = 1; + } + + for (i = 0; i < num_mididevs; i++) + { + if (midi_devs[i]->osdev == osdev) + { + midi_devs[i]->unloaded = 1; + } + } + + for (i = 0; i < num_audio_engines; i++) + if (audio_engines[i]->osdev == osdev) + { + audio_uninit_device (i); + } + + return 0; +} + +void +oss_reserve_device (oss_device_t * osdev) +{ + osdev->refcount++; +} + +void +oss_unreserve_device (oss_device_t * osdev, int decrement) +{ + osdev->refcount--; + if (osdev->refcount < 0) + osdev->refcount = 0; +} + +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + if (name == NULL) + { + cmn_err (CE_WARN, "oss_register_device: name==NULL\n"); + osdev->name = "Undefined name"; + return 0; + } + + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + return 0; +} + +void +oss_unregister_device (oss_device_t * osdev) +{ +/* + * Notice! The driver calling this routine (the owner of the osdev parameter) + * has already uninitialized itself. Do not do any actions that may call this + * driver directly or indirectly. + */ + +// TODO: Move this to some common OSS module (also under Solaris) +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + if (cardnum < 0 || cardnum >= oss_num_cards) + return OSS_ENXIO; + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->longname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info)); + ci->hw_info[sizeof (ci->hw_info) - 1] = 0; + ci->intr_count = cards[cardnum]->intrcount; + ci->ack_count = cards[cardnum]->ackcount; + + return 0; +} + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + void *buf; + int err; + oss_native_word phaddr; + int size = 64 * 1024; + extern int dma_buffsize; + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + + if (dmap->dmabuf != NULL) + return 0; /* Already done */ + + if (dmap == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: dmap==NULL\n"); + return OSS_EIO; + } + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + dmap->dmabuf = NULL; + dmap->buffsize = size; + + err = -1; + + while (err < 0 && dmap->dmabuf == NULL && dmap->buffsize >= 4 * 1024) + { + if ((buf = + oss_contig_malloc (dmap->osdev, dmap->buffsize, maxaddr, + &phaddr)) == NULL) + { + if ((dmap->buffsize = (dmap->buffsize / 2)) < 8 * 1024) + return OSS_ENOMEM; + cmn_err (CE_CONT, "Dropping DMA buffer size to %d bytes.\n", + dmap->buffsize); + continue; + } + + dmap->dmabuf = buf; + dmap->dmabuf_phys = phaddr; + + return 0; + } + + return OSS_ENOMEM; +} + +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + void *buf = dmap->dmabuf; + + if (dmap->dmabuf == NULL) + return; + + dmap->dmabuf = NULL; + oss_contig_free (NULL, buf, dmap->buffsize); + dmap->dmabuf_phys = 0; +} + +static inline int +cpy_file (oss_file_handle_t * f, struct fileinfo *fi) +{ + // TODO: Handle acc_flags properly + fi->acc_flags = 0; + fi->mode = 0; + fi->acc_flags = oss_file_get_flags (f); + + if ((fi->acc_flags & O_ACCMODE) == O_RDWR) + fi->mode = OPEN_READWRITE; + if ((fi->acc_flags & O_ACCMODE) == O_RDONLY) + fi->mode = OPEN_READ; + if ((fi->acc_flags & O_ACCMODE) == O_WRONLY) + fi->mode = OPEN_WRITE; + + return fi->mode; +} + +static int +oss_cdev_open (oss_inode_handle_t * inode, oss_file_handle_t * file) +{ + int dev = oss_inode_get_minor (inode); + oss_native_word d; + int tmpdev, dev_class, retval; + struct fileinfo fi; + oss_cdev_t *cdev; + + cpy_file (file, &fi); + + if (dev > oss_num_cdevs) + return OSS_ENXIO; + if (oss_cdevs == NULL) + return OSS_ENXIO; + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return OSS_ENXIO; + + DDB (cmn_err + (CE_CONT, "oss_cdev_open(%d): %s, class=%d, instance=%d\n", dev, + cdev->name, cdev->dev_class, cdev->instance)); + + if (cdev->d->open == NULL) + { + return OSS_ENODEV; + } + + dev_class = cdev->dev_class; + + tmpdev = -1; + oss_inc_refcounts (); + retval = + cdev->d->open (cdev->instance, cdev->dev_class, &fi, 0, 0, &tmpdev); + + if (retval < 0) + { + oss_dec_refcounts (); + return retval; + } + + if (tmpdev != -1) + dev = tmpdev; + + //open_devices++; + //open_count[dev]++; + + d = dev; + oss_file_set_private (file, (void *) d); + + return 0; +} + +static int +oss_cdev_release (oss_inode_handle_t * inode, oss_file_handle_t * file) +{ + oss_native_word d = (oss_native_word) oss_file_get_private (file); + int dev = d; + struct fileinfo fi; + oss_cdev_t *cdev; + + cpy_file (file, &fi); + + if (dev > oss_num_cdevs) + return 0; + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->close == NULL) + { + return 0; + } + + cdev->d->close (cdev->instance, &fi); + oss_dec_refcounts (); + + return 0; +} + +static ssize_t +oss_cdev_read (oss_file_handle_t * file, char *buf, size_t count, + loff_t * offs) +{ + oss_native_word d = (oss_native_word) oss_file_get_private (file); + int dev = d; + oss_cdev_t *cdev; + int err; + uio_t uio; + + struct fileinfo fi; + cpy_file (file, &fi); + + if (dev > oss_num_cdevs) + return OSS_ENXIO; + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->read == NULL) + { + return OSS_ENXIO; + } + + if ((err = oss_create_uio (&uio, buf, count, UIO_READ, 0)) < 0) + { + return err; + } + + err = cdev->d->read (cdev->instance, &fi, &uio, count); + + return err; +} + +static ssize_t +oss_cdev_write (oss_file_handle_t * file, char *buf, size_t count, + loff_t * offs) +{ + oss_native_word d = (oss_native_word) oss_file_get_private (file); + int dev = d; + oss_cdev_t *cdev; + int err; + uio_t uio; + + struct fileinfo fi; + cpy_file (file, &fi); + + if (dev > oss_num_cdevs) + return OSS_ENXIO; + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->write == NULL) + { + return OSS_ENXIO; + } + + if ((err = oss_create_uio (&uio, buf, count, UIO_WRITE, 0)) < 0) + { + return err; + } + + err = cdev->d->write (cdev->instance, &fi, &uio, count); + + return err; +} + +static int +oss_cdev_ioctl (oss_inode_handle_t * inode, oss_file_handle_t * file, + unsigned int cmd, unsigned long arg) +{ + oss_native_word d = (oss_native_word) oss_file_get_private (file); + int dev = d; + oss_cdev_t *cdev; + int err; + int localbuf[64]; /* 256 bytes is the largest frequently used ioctl size */ + + int len = 0; + int alloced = 0; + int *ptr = (int *) arg; + + struct fileinfo fi; + cpy_file (file, &fi); + + if (dev > oss_num_cdevs) + return OSS_ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL) + { + return OSS_ENXIO; + } + + if (__SIOC_DIR (cmd) != __SIOC_NONE && __SIOC_DIR (cmd) != 0) + { + len = __SIOC_SIZE (cmd); + if (len < 1 || len > 65536 || arg == 0) + { + cmn_err (CE_WARN, "Bad ioctl command %x, %d, %x\n", cmd, len, arg); + return OSS_EFAULT; + } + + /* Use statically allocated buffer for short arguments */ + if (len > sizeof (localbuf)) + { + ptr = KERNEL_MALLOC (len); + alloced = 1; + } + else + ptr = localbuf; + + if (ptr == NULL || arg == 0) + { + return OSS_EFAULT; + } + + if (__SIOC_DIR (cmd) & __SIOC_WRITE) + { + if (oss_copy_from_user (ptr, (char *) arg, len)) + { + if (alloced) + KERNEL_FREE (ptr); + return OSS_EFAULT; + } + } + } + + if ((err = cdev->d->ioctl (cdev->instance, &fi, cmd, (ioctl_arg) ptr)) < 0) + { + if (alloced) + KERNEL_FREE (ptr); + return err; + } + + if (__SIOC_DIR (cmd) & __SIOC_READ) + { + if (oss_copy_to_user ((char *) arg, ptr, len)) + { + if (alloced) + KERNEL_FREE (ptr); + return OSS_EFAULT; + } + } + + /* Free the local buffer unless it was statically allocated */ + if (ptr != NULL && alloced) + if (len > sizeof (localbuf)) + KERNEL_FREE (ptr); + + return ((err < 0) ? err : 0); + +} + +/* No BKL if this is used */ +static long +oss_cdev_unlocked_ioctl (oss_file_handle_t * file, unsigned int cmd, + unsigned long arg) +{ + return oss_cdev_ioctl (NULL, file, cmd, arg); +} + +/* Used for 32 bit clients on a 64 bit kernel. No BKL here either */ +static long +oss_cdev_compat_ioctl (oss_file_handle_t * file, unsigned int cmd, + unsigned long arg) +{ + return oss_cdev_ioctl (NULL, file, cmd, arg); +} + +static unsigned int +oss_cdev_poll (oss_file_handle_t * file, oss_poll_table_handle_t * wait) +{ + oss_poll_event_t ev; + oss_native_word d = (oss_native_word) oss_file_get_private (file); + int dev = d; + oss_cdev_t *cdev; + int err; + + struct fileinfo fi; + cpy_file (file, &fi); + + if (dev > oss_num_cdevs) + return OSS_ENXIO; + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->chpoll == NULL) + { + return OSS_ENXIO; + } + + ev.wait = wait; + ev.file = file; + ev.events = POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM; + ev.revents = 0; + err = cdev->d->chpoll (cdev->instance, &fi, &ev); + if (err < 0) + { + return err; + } + + return ev.revents; +} + +static int +oss_cdev_mmap (oss_file_handle_t * file, oss_vm_area_handle_t * vma) +{ + oss_native_word d = (oss_native_word) oss_file_get_private (file); + int dev = d; + oss_cdev_t *cdev; + dmap_p dmap = NULL; + int err; + + if (dev > oss_num_cdevs) + return OSS_ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL) + { + return OSS_ENXIO; + } + + if (cdev->dev_class != OSS_DEV_DSP && cdev->dev_class != OSS_DEV_DSP_ENGINE) /* Only mmap audio devices */ + { + return OSS_ENXIO; + } + + dev = cdev->instance; + if (dev < 0 || dev >= num_audio_engines) + return OSS_ENXIO; + + if (audio_engines[dev]->flags & ADEV_NOMMAP) + return OSS_EIO; + + if (oss_vma_get_flags (vma) & VM_WRITE) /* Map write and read/write to the output buf */ + { + dmap = audio_engines[dev]->dmap_out; + } + else if (oss_vma_get_flags (vma) & VM_READ) + { + dmap = audio_engines[dev]->dmap_in; + } + else + { + cmn_err (CE_WARN, "Undefined mmap() access\n"); + return OSS_EINVAL; + } + + if (dmap == NULL) + { + cmn_err (CE_WARN, "mmap() error. dmap == NULL\n"); + return OSS_EIO; + } + + if (dmap->dmabuf == NULL) + { + cmn_err (CE_WARN, "mmap() called when raw_buf == NULL\n"); + return OSS_EIO; + } + + if (dmap->dmabuf_phys == 0) + { + cmn_err (CE_WARN, "mmap() not supported by device /dev/dsp%d.\n", dev); + return OSS_EIO; + } + + if (dmap->mapping_flags) + { + cmn_err (CE_WARN, "mmap() called twice for the same DMA buffer\n"); + return OSS_EIO; + } + + if (dmap->flags & DMAP_COOKED) + { + cmn_err (CE_WARN, + "mmap() not possible with currently selected sample format.\n"); + return OSS_EIO; + } + + if ((err = oss_do_mmap (vma, dmap->dmabuf_phys, dmap->bytes_in_use)) < 0) + return err; + + dmap->mapping_flags |= DMA_MAP_MAPPED; + + memset (dmap->dmabuf, dmap->neutral_byte, dmap->bytes_in_use); + return 0; +} + +oss_file_operation_handle_t oss_fops = { + oss_cdev_read, + oss_cdev_write, + NULL, /* oss_readdir */ + oss_cdev_poll, + oss_cdev_ioctl, + oss_cdev_mmap, + oss_cdev_open, + oss_cdev_release, + oss_cdev_compat_ioctl, + oss_cdev_unlocked_ioctl +}; + +static int +hookup_cdevsw (oss_device_t * osdev) +{ + return oss_register_chrdev (osdev, osscore_major, "osscore", &oss_fops); +} + +int +oss_request_major (oss_device_t * osdev, int major, char *module) +{ + int err; + + err = oss_register_chrdev (osdev, major, module, &oss_fops); + + return err; +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * sizeof (oss_cdev_t *)))==NULL) + return 0; + + memset(new, 0, new_size * sizeof(oss_cdev_t *)); + if (old != NULL) + memcpy(new, old, old_size * sizeof(oss_cdev_t *)); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ +/* + * oss_install_chrdev creates a character device (minor). However if + * name==NULL the device will not be exported (made visible to userland + * clients). + */ + + int num; + oss_cdev_t *cdev = NULL; + + if (osdev->major == 0) + { + cmn_err (CE_WARN, "Module %s major=0\n", osdev->nick); + return; + } + + if (osdev->major < 0) + { + cmn_err (CE_WARN, "Failed to allocate major device for %s\n", + osdev->nick); + } + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; +/* + * Find if this dev_class&instance already exists (after previous module + * detach). + */ + + for (num = 0; num < oss_num_cdevs; num++) + if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ + if (oss_cdevs[num]->dev_class == dev_class + && oss_cdevs[num]->instance == instance) + { + cdev = oss_cdevs[num]; + break; + } + + if (cdev == NULL) + { + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, 100)) + { + cmn_err (CE_WARN, "Out of minor numbers.\n"); + return; + } + } + + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + + num = oss_num_cdevs++; + } + + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name)); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + +/* + * Export the device only if name != NULL + */ + if (name != NULL) + { + strcpy (cdev->name, name); + oss_register_minor (osdev->major, num, name); + } +} + +int +oss_init_osscore (oss_device_t * osdev) +{ + +#ifdef LICENSED_VERSION + if (!oss_license_handle_time (oss_get_time ())) + { + cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); + cmn_err (CE_CONT, + "Please download the latest version from www.opensound.com\n"); + oss_expired = 1; + } +#endif + + if ((osscore_major = hookup_cdevsw (osdev)) < 0) + { + cmn_err (CE_WARN, "Failed to allocate character major number %d\n", + osscore_major); + return OSS_EBUSY; + } + + osdev->major = osscore_major; + oss_register_device (osdev, "OSS core services"); + + oss_common_init (osdev); + + return 0; +} + +void +oss_uninit_osscore (oss_device_t * osdev) +{ + oss_unload_drivers (); + + // free_all_irqs (); /* If something was left allocated by accident */ + oss_unregister_chrdev (osscore_major, "osscore"); +} + +/* + * oss_pmalloc() is only used by usb_wraper.inc. + */ +void * +oss_pmalloc (size_t sz) +{ + return oss_memblk_malloc(&oss_global_memblk, sz); +} diff --git a/kernel/OS/Linux/os_linux.h b/kernel/OS/Linux/os_linux.h new file mode 100644 index 0000000..d881859 --- /dev/null +++ b/kernel/OS/Linux/os_linux.h @@ -0,0 +1,287 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for Linux + * + * Under Linux os.h (this file) defines just the macros, functions and + * structures used by the code compiled in the development system. However + * there are other Linux specific definitions contained in {!nlink Linux/wrap.h} + * that are used both by the code compiled in the devlopment and target systems. + * This means that some definitions found in os.h under some other operating + * systems may be in wrap.h under Linux. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#define OS_VERSION "2.6.x" +#define __EXTENDED__ +#define OSS_MAINLINE_BUILD + +/* + * Debugging and misc settings + */ +#undef MUTEX_CHECKS +#undef MEMDEBUG +#define VDEV_SUPPORT + +#if (!defined(__i386__) && !defined(__x86_64__)) || defined(CONFIG_OSS_FIXDEPOINT) +// Floating point is not supported or it's disabled +#undef CONFIG_OSS_VMIX_FLOAT +#endif + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ + +#undef APPLIST_SUPPORT +#define USE_DEVICE_SUBDIRS +#define EXTERN_C + +#define __invalid_size_argument_for_IOC 0 /* Dummy define to cure some broken ioctl.h versions */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/signal.h> +#include <oss_errno.h> +#include <sys/file.h> +#include "oss_ddi.h" +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <asm/poll.h> +#include "kernel/OS/Linux/wrapper/wrap.h" + +#undef HZ +extern int oss_hz; +#define OSS_HZ oss_hz + +/* The soundcard.h could be in a nonstandard place so include it here. */ +#include "soundcard.h" + +struct _oss_device_t +{ + int cardnum; + int dev_type; + int available; + int instance; + dev_info_t *dip; + void *devc; + char *name; + char nick[16]; + char handle[32]; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + int major; + struct module *owner; /* Pointer to THISMODULE (needed by osscore.c) */ + char modname[32]; + char *hw_info; + + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +/* Interrupts */ + + ddi_iblock_cookie_t iblock_cookie; /* Dummy field under Linux */ + void *irqparms; + int intrcount, ackcount; + +/* PCI related fields */ + +#ifdef _KERNEL + ddi_acc_handle_t pci_config_handle; + ddi_acc_handle_t acc_handle; + int swap_mode; /* 0=DDI_STRUCTURE_NEVERSWAP_ACC, 1=DDI_STRUCTURE_LE_ACC */ +#endif +}; + +#define ALLOW_BUFFER_MAPPING +#define ALLOW_SELECT +#define SEL_IN 0 +#define SEL_OUT 1 +#define SEL_EX 0xffffffff + +/* Busy wait routine */ +#define oss_udelay drv_usecwait +/* System wall timer access */ +#define GET_JIFFIES() oss_get_jiffies() + +extern inline unsigned int +__inb (unsigned short port) +{ + unsigned int _v; + __asm__ __volatile__ ("in" "b" " %" "w" "1,%" "b" "0":"=a" (_v):"d" (port), + "0" (0)); + return _v; +} +extern inline unsigned int +__inw (unsigned short port) +{ + unsigned int _v; + __asm__ __volatile__ ("in" "w" " %" "w" "1,%" "w" "0":"=a" (_v):"d" (port), + "0" (0)); + return _v; +} +extern inline unsigned int +__inl (unsigned short port) +{ + unsigned int _v; + __asm__ __volatile__ ("in" "l" " %" "w" "1,%" "" "0":"=a" (_v):"d" (port)); + return _v; +} + +extern inline void +__outb (unsigned char value, unsigned short port) +{ + __asm__ __volatile__ ("out" "b" " %" "b" "0,%" "w" "1"::"a" (value), + "d" (port)); +} +extern inline void +__outw (unsigned short value, unsigned short port) +{ + __asm__ __volatile__ ("out" "w" " %" "w" "0,%" "w" "1"::"a" (value), + "d" (port)); +} +extern inline void +__outl (unsigned int value, unsigned short port) +{ + __asm__ __volatile__ ("out" "l" " %" "0,%" "w" "1"::"a" (value), + "d" (port)); +} + +#define INB(osdev,a) __inb(a) +#define INW(osdev,a) __inw(a) +#define INL(osdev,a) __inl(a) + +#define OUTB(osdev, d, a) __outb(d, a) + +#define OUTW(osdev, d, a) __outw(d, a) +#define OUTL(osdev, d, a) __outl(d, a) + +#define PCI_READL(osdev, p) (*(volatile unsigned int *) (p)) +#define PCI_WRITEL(osdev, addr, data) (*(volatile unsigned int *) (addr) = (data)) +#define PCI_READW(osdev, p) (*(volatile unsigned short *) (p)) +#define PCI_WRITEW(osdev, addr, data) (*(volatile unsigned short *) (addr) = (data)) +#define PCI_READB(osdev, p) (*(volatile unsigned char *) (p)) +#define PCI_WRITEB(osdev, addr, data) (*(volatile unsigned char *) (addr) = (data)) + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#ifdef MEMDEBUG +extern void *oss_kmem_alloc (size_t size, char *file, int line); +extern void oss_kmem_free (void *addr); +#define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, __FILE__, __LINE__) +#define KERNEL_FREE(addr) oss_kmem_free(addr) +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr, char *file, + int line); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +extern oss_native_word oss_virt_to_bus (void *addr); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr, __FILE__, __LINE__) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) +#else +#define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes) +#define KERNEL_FREE(addr) oss_kmem_free(addr) +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) +#endif + +/* + * Timer macros + * + * These macros are obsolete and should not be used in any new code. + * Use the timeout mechanism (see the timeout(9F) Solaris man page). + */ +#define DEFINE_TIMER(name, proc) static timeout_id_t name = 0 +#define REMOVE_TIMER(name, proc) {if (name != 0) oss_untimeout(name);} +#define INIT_TIMER(name,proc) +typedef void (*timeout_func_t) (void *); +#define ACTIVATE_TIMER(name, proc, time) \ + name=oss_timeout((timeout_func_t)proc, (void*)&name, time) + +#endif + +#define OSS_OS "Linux" +#define OSS_OS_LONGNAME "Linux" + +#undef DMA_TRY_PSEUDOINIT + +int get_dma_residue (int chn); +void disable_dma (int chn); +void enable_dma (int chn); + +typedef void (*softintr_func_t) (int); + +struct oss_softintr +{ + int id; + softintr_func_t func; + volatile int armed, running; +}; + +#define MUTEX_INIT(osdev, mutex, hier) mutex=oss_mutex_init() +#define MUTEX_CLEANUP(mutex) {oss_mutex_cleanup(mutex);mutex=NULL;} +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) flags=0;oss_spin_lock_irqsave(mutex, &flags) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) oss_spin_unlock_irqrestore(mutex, flags);(flags)++ +#define MUTEX_ENTER(mutex, flags) flags=0;oss_spin_lock(mutex) +#define MUTEX_EXIT(mutex, flags) oss_spin_unlock(mutex);(flags)++ + +extern int detect_trace; +#define DDB(x) if (detect_trace) x + +#define MAP_PCI_IOADDR(osdev, nr, io) (oss_native_word)io +#define MAP_PCI_MEM(osdev, ix, phaddr, size) oss_map_pci_mem(osdev, size, phaddr) +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) oss_unmap_pci_mem(virt) +#define UNMAP_PCI_IOADDR(osdev, ix) {} + +#define GET_PROCESS_PID(x) oss_get_pid() +#define GET_PROCESS_UID(x) oss_get_uid() + +#define GET_PROCESS_NAME(x) oss_get_procname() + +#define pci_read_config_irq oss_pci_read_config_irq +#define pci_read_config_byte oss_pci_read_config_byte +#define pci_read_config_word oss_pci_read_config_word +#define pci_read_config_dword oss_pci_read_config_dword +#define pci_write_config_byte oss_pci_write_config_byte +#define pci_write_config_word oss_pci_write_config_word +#define pci_write_config_dword oss_pci_write_config_dword +#define pci_enable_msi oss_pci_enable_msi + +#define VM_READ 0x1 +#define VM_WRITE 0x2 + +#define FP_SUPPORT + +#ifdef FP_SUPPORT +typedef short fp_env_t[512]; +typedef unsigned int fp_flags_t[4]; +extern int oss_fp_check (void); +extern void oss_fp_save (short *envbuf, fp_flags_t flags); +extern void oss_fp_restore (short *envbuf, fp_flags_t flags); +# define FP_SAVE(envbuf, flags) oss_fp_save(envbuf, flags) +# define FP_RESTORE(envbuf, flags) oss_fp_restore(envbuf, flags) +#endif + +#include "oss_pci.h" diff --git a/kernel/OS/Linux/oss_ddi.h b/kernel/OS/Linux/oss_ddi.h new file mode 100644 index 0000000..36111fe --- /dev/null +++ b/kernel/OS/Linux/oss_ddi.h @@ -0,0 +1,46 @@ +/* + * Purpose: Solaris compatible partial DDI interface for OSS/Linux + */ + +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#ifndef NULL +#define NULL 0 +#endif + +typedef int ddi_iblock_cookie_t; +typedef int kmutex_t; +typedef int cred_t; + +typedef int ddi_acc_handle_t; +typedef int kcondvar_t; +typedef int ddi_dma_handle_t; +typedef int ddi_dma_cookie_t; +typedef int ddi_dma_win_t; +typedef int ddi_dma_seg_t; +typedef int offset_t; +typedef int ddi_info_cmd_t; +typedef int ddi_attach_cmd_t; +typedef int ddi_detach_cmd_t; + +#include <stdint.h> + +typedef struct _ddi_dma_attr_t +{ +#define DMA_ATTR_V0 0 + int a, b, c, d, e, f, g, h, i, j, k, l, m, n; +} ddi_dma_attr_t; + +struct pollhead +{ + int dummy; +}; diff --git a/kernel/OS/Linux/wrapper/.nomake b/kernel/OS/Linux/wrapper/.nomake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/kernel/OS/Linux/wrapper/.nomake diff --git a/kernel/OS/Linux/wrapper/wrap.h b/kernel/OS/Linux/wrapper/wrap.h new file mode 100644 index 0000000..0a3d3b9 --- /dev/null +++ b/kernel/OS/Linux/wrapper/wrap.h @@ -0,0 +1,288 @@ +/* + * Purpose: Wrapper routines for Linux kernel services + * + * The functions and structures declared here are part of the osscore.c + * file that is compiled in the target system. This file must not be + * modified in the target system because the precompiled binaries included + * in the OSS installation package depend on it too. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +/* + * Some integer types + */ +#if defined(__x86_64__) +typedef unsigned long long oss_native_word; /* Same as the address and status register size */ +#else +typedef unsigned long oss_native_word; /* Same as the address and status register size */ +#endif +typedef long long oss_int64_t; /* Signed 64 bit integer */ +typedef unsigned long long oss_uint64_t; /* Unsigned 64 bit integer */ + +extern char *oss_strcpy (char *s1, const char *s2); +extern void *oss_memcpy (void *t_, const void *f_, size_t l); +extern void *oss_memset (void *t, int val, size_t l); +extern int oss_strcmp (const char *s1, const char *s2); +extern size_t oss_strlen (const char *s); +extern char *oss_strncpy (char *s1, const char *s2, size_t l); +extern void oss_udelay (unsigned long d); + +typedef struct _oss_mutex_t *oss_mutex_t; +typedef struct _poll_table_handle oss_poll_table_handle_t; +typedef struct _file_handle_t oss_file_handle_t; + +struct _oss_poll_event_t +{ + short events, revents; + oss_poll_table_handle_t *wait; + oss_file_handle_t *file; +}; +typedef struct _oss_poll_event_t oss_poll_event_t; + +extern oss_mutex_t oss_mutex_init (void); +extern void oss_mutex_cleanup (oss_mutex_t mutex); +extern void oss_spin_lock_irqsave (oss_mutex_t mutex, + oss_native_word * flags); +extern void oss_spin_unlock_irqrestore (oss_mutex_t mutex, + oss_native_word flags); +extern void oss_spin_lock (oss_mutex_t mutex); +extern void oss_spin_unlock (oss_mutex_t mutex); +extern unsigned long long oss_get_jiffies (void); +extern char *oss_get_procname (void); +extern int oss_get_pid (void); +extern int oss_get_uid (void); + +struct oss_wait_queue; +struct module; +struct _oss_device_t; +struct pci_dev; + +typedef void *oss_dma_handle_t; /* Unused type */ + +/* + * Sleep/wakeup/poll support. These definitions are duplicates from + * oss_config.h which is the official place. Both definitions must match. + */ + +extern struct oss_wait_queue *oss_create_wait_queue (oss_device_t * osdev, + const char *name); +extern void oss_reset_wait_queue (struct oss_wait_queue *wq); +extern void oss_remove_wait_queue (struct oss_wait_queue *wq); +extern int oss_sleep (struct oss_wait_queue *wq, oss_mutex_t * mutex, + int ticks, oss_native_word * flags, + unsigned int *status); +extern int oss_register_poll (struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev); +extern void oss_wakeup (struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, short events); + +extern void oss_cmn_err (int level, const char *format, ...); +#define CE_CONT 0 +#define CE_NOTE 1 +#define CE_WARN 2 +#define CE_PANIC 3 + +typedef int timeout_id_t; +extern timeout_id_t oss_timeout (void (*func) (void *), void *arg, + unsigned long long ticks); +extern void oss_untimeout (timeout_id_t id); + +extern int sprintf (char *buf, const char *s, ...); + +typedef enum uio_rw +{ UIO_READ, UIO_WRITE } uio_rw_t; +struct uio +{ + char *ptr; + int resid; + int kernel_space; /* Set if this uio points to a kernel space buffer */ + uio_rw_t rw; +}; +typedef struct uio uio_t; + +extern int oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, + uio_t * uio_p); +extern int oss_create_uio (uio_t * uiop, char *buf, size_t count, uio_rw_t rw, + int is_kernel); +extern void *oss_kmem_alloc (size_t size); +extern void oss_kmem_free (void *addr); +extern void *oss_pmalloc (size_t sz); +extern oss_native_word oss_virt_to_bus (void *addr); +extern void oss_reserve_pages (oss_native_word start_addr, + oss_native_word end_addr); +extern void oss_unreserve_pages (oss_native_word start_addr, + oss_native_word end_addr); +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); + +extern time_t oss_get_time (void); + +typedef struct _inode_handle_t oss_inode_handle_t; +typedef struct _vm_aread_handle oss_vm_area_handle_t; + +extern int oss_vma_get_flags (oss_vm_area_handle_t *); + +typedef struct oss_file_operation_handle +{ + ssize_t (*read) (oss_file_handle_t *, char *, size_t, loff_t *); + ssize_t (*write) (oss_file_handle_t *, char *, size_t, loff_t *); + int (*readdir) (oss_inode_handle_t *, oss_file_handle_t *, void *, int); + unsigned int (*poll) (oss_file_handle_t *, oss_poll_table_handle_t *); + int (*ioctl) (oss_inode_handle_t *, oss_file_handle_t *, unsigned int, + unsigned long); + int (*mmap) (oss_file_handle_t *, oss_vm_area_handle_t *); + int (*open) (oss_inode_handle_t *, oss_file_handle_t *); + int (*release) (oss_inode_handle_t *, oss_file_handle_t *); + long (*compat_ioctl) (oss_file_handle_t *, unsigned int, unsigned long); + long (*unlocked_ioctl) (oss_file_handle_t *, unsigned int, unsigned long); + int (*fsync) (oss_inode_handle_t *, oss_file_handle_t *); + int (*fasync) (oss_inode_handle_t *, oss_file_handle_t *, int); +} +oss_file_operation_handle_t; + +extern int oss_do_mmap (oss_vm_area_handle_t * vma, + oss_native_word dmabuf_phys, int bytes_in_use); +extern int oss_register_chrdev (oss_device_t * osdev, unsigned int major, + const char *name, + oss_file_operation_handle_t * op); +extern void oss_register_minor (int major, int minor, char *name); +extern int oss_unregister_chrdev (unsigned int major, const char *name); + +extern int oss_inode_get_minor (oss_inode_handle_t * inode); +extern int oss_file_get_flags (oss_file_handle_t * file); +extern void *oss_file_get_private (oss_file_handle_t * file); +extern void oss_file_set_private (oss_file_handle_t * file, void *v); + +extern void oss_inc_refcounts (void); +extern void oss_dec_refcounts (void); + +/* + * Redefinitions of some routines defined in oss_config.h + * just to ensure they are defined in the same way in both places. The + * osscore/wrapper modules only include wrap.h so they can't see the "official" + * declarations in oss_config.h. + */ + +typedef struct _dev_info_t dev_info_t; +extern dev_info_t *oss_create_pcidip (struct pci_dev *pcidev); +extern oss_device_t *osdev_create (dev_info_t * dip, int dev_type, + int instance, const char *nick, + const char *handle); + +extern void osdev_delete (oss_device_t * osdev); + +/* + * PCI config space access (in osscore.c) + */ +extern char *oss_pci_read_devpath (dev_info_t * dip); +extern int osscore_pci_read_config_byte (dev_info_t * dip, unsigned int where, + unsigned char *val); +extern int osscore_pci_read_config_irq (dev_info_t * dip, unsigned int where, + unsigned char *val); +extern int osscore_pci_read_config_word (dev_info_t * dip, unsigned int where, + unsigned short *val); +extern int osscore_pci_read_config_dword (dev_info_t * dip, + unsigned int where, + unsigned int *val); +extern int osscore_pci_write_config_byte (dev_info_t * dip, + unsigned int where, + unsigned char val); +extern int osscore_pci_write_config_word (dev_info_t * dip, + unsigned int where, + unsigned short val); +extern int osscore_pci_write_config_dword (dev_info_t * dip, + unsigned int where, + unsigned int val); + +extern int osscore_pci_enable_msi (dev_info_t * dip); + +extern void *oss_map_pci_mem (oss_device_t * osdev, int size, + unsigned long offset); + +extern void oss_unmap_pci_mem (void *addr); + +extern int oss_copy_to_user (void *to, const void *from, unsigned long n); +extern int oss_copy_from_user (void *to, const void *from, unsigned long n); + +extern void oss_register_module (struct module *mod); +extern void oss_unregister_module (struct module *mod); + +#ifdef _KERNEL +extern char *oss_strcpy (char *s1, const char *s2); +#undef strcpy +#define strcpy oss_strcpy + +extern void *oss_memcpy (void *t_, const void *f_, size_t l); +#undef memcpy +#define memcpy oss_memcpy + +extern void *oss_memset (void *t, int val, size_t l); +#undef memset +#define memset oss_memset + +extern int oss_strcmp (const char *s1, const char *s2); +#undef strcmp +#define strcmp oss_strcmp +extern int oss_strncmp (const char *s1, const char *s2, size_t len); +#undef strncmp +#define strncmp oss_strncmp + +#undef strlen +#define strlen oss_strlen + +#undef strncpy +#define strncpy oss_strncpy +#endif // KERNEL + +#undef timeout +#define timeout oss_timeout + +#undef untimeout +#define untimeout oss_untimeout + +#define drv_usecwait oss_udelay + +#define uiomove oss_uiomove + +#define cmn_err oss_cmn_err + +struct fileinfo +{ + int mode; /* Open mode */ + int acc_flags; +}; +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +/* + * USB related definitions + */ +typedef struct udi_usb_devc udi_usb_devc; + +/* + * Functions exported by os.c + */ +extern int oss_init_osscore (oss_device_t * osdev); +extern void oss_uninit_osscore (oss_device_t * osdev); +extern void osdev_set_owner (oss_device_t * osdev, struct module *owner); +extern void osdev_set_major (oss_device_t * osdev, int major); +extern void osdev_set_irqparms (oss_device_t * osdev, void *irqparms); +extern void *osdev_get_irqparms (oss_device_t * osdev); +extern void oss_inc_intrcount(oss_device_t *osdev, int claimed); +extern struct module *osdev_get_owner (oss_device_t * osdev); +extern char *osdev_get_nick (oss_device_t * osdev); +extern int osdev_get_instance (oss_device_t * osdev); +extern int oss_request_major (oss_device_t * osdev, int major, char *module); +extern int oss_register_device (oss_device_t * osdev, const char *name); /* from oss_config.h */ + diff --git a/kernel/OS/SCO_SV/.config b/kernel/OS/SCO_SV/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/SCO_SV/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/SCO_SV/module.inc b/kernel/OS/SCO_SV/module.inc new file mode 100644 index 0000000..8891691 --- /dev/null +++ b/kernel/OS/SCO_SV/module.inc @@ -0,0 +1,277 @@ +/* + * Purpose: OSS module wrapper for SCO OpenServer/UnixWare + * + * This file will be included from the auto-generated drv_cfg.c files. Under + * UnixWare and OpenServer this will will be compiled during the initial build + * of OSS (in the development system). + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include <errno.h> + +static int ossdrv_config (cfg_func_t func, void *idata, rm_key_t key); +#if DRIVER_TYPE==DRV_VIRTUAL || DRIVER_TYPE==DRV_VMIX +oss_device_t *osdev = NULL; +#endif +/* + * Driver information structures, for drv_attach(). + */ +static const drvops_t oss_ops = { + ossdrv_config, + oss_open, + oss_close, + oss_devinfo, + oss_biostart, + oss_ioctl, + NULL, /* drvctl */ + NULL /* mmap */ +}; + +static const drvinfo_t oss_drvinfo = { + &oss_ops, + DRIVER_NICK, + D_MP, /* MP-safe */ + NULL, /* Not a STREAMS driver */ + 256 /* Must match $maxchan in Node file */ +}; + +static int +rd_hex (char *s) +{ +/* + * Convert a 4 digit hexadecimal string to integer value + */ + int v = 0; + int i; + + for (i = 0; i < 4; i++) + { + char c = *s++; + + if (c >= '0' && c <= '9') + { + v = (v << 4) | (c - '0'); + continue; + } + + if (c >= 'A' && c <= 'F') + { + v = (v << 4) | (c - 'A' + 10); + continue; + } + + if (c >= 'a' && c <= 'f') + { + v = (v << 4) | (c - 'a' + 10); + continue; + } + } + + return v; +} + +static int +cfg_add (void *idata, rm_key_t key) +{ + int err; + cm_args_t cma; + cm_num_t btype; + char id[32]; + int vendor, product; + static int instance = 0; + oss_device_t *osdev; + + cm_begin_trans (key, RM_READ); + cma.cm_key = key; + cma.cm_param = CM_BRDBUSTYPE; + cma.cm_val = &btype; + cma.cm_vallen = sizeof (btype); + cma.cm_n = 0; + err = cm_getval (&cma); + cm_end_trans (key); + + if (err != 0 || btype != CM_BUS_PCI) + { + cmn_err (CE_WARN, "Bad BUS type %d\n", btype); + return ENODEV; + } + + if ((osdev = + osdev_create (&key, DRIVER_TYPE, instance, DRIVER_NICK, NULL)) == NULL) + { + return EIO; + } + + cm_begin_trans (key, RM_READ); + cma.cm_key = key; + cma.cm_param = CM_BRDID; + cma.cm_val = id; + cma.cm_vallen = sizeof (id); + cma.cm_n = 0; + err = cm_getval (&cma); + cm_end_trans (key); + + vendor = rd_hex (id + 2); + product = rd_hex (id + 6); + + osdev->vendor = vendor; + osdev->product = product; + osdev->drvinfo = (drvinfo_t *) & oss_drvinfo; + osdev->key = key; + + if (!DRIVER_ATTACH (osdev)) + { + cmn_err (CE_WARN, "Attach failed\n"); + osdev_delete (osdev); + return EIO; + } + + *(void **) idata = osdev; + oss_audio_delayed_attach (); + + instance++; + + return 0; +} + +static int +cfg_remove (void *idata) +{ + oss_device_t *osdev = idata; + + if (idata == NULL) + { + cmn_err (CE_WARN, DRIVER_NICK ": ossdrv_detach: dip==NULL\n"); + return EIO; + } + + if (osdev == NULL) + { + cmn_err (CE_WARN, DRIVER_NICK ": Bad idatata\n"); + return 0; + } + + if (DRIVER_DETACH (osdev) <= 0) + { + DDB (cmn_err (CE_WARN, "Driver busy - cannot detach\n")); + return EBUSY; + } + + osdev_delete (osdev); + + DDB (cmn_err (CE_CONT, "Detach done " DRIVER_NICK, "\n")); + return 0; +} + +#if DRIVER_TYPE==DRV_VIRTUAL || DRIVER_TYPE==DRV_VMIX +static int +attach_virtual (void) +{ + DDB (cmn_err (CE_CONT, "Attach started " DRIVER_NICK "\n")); + if ((osdev = + osdev_create (NULL, DRIVER_TYPE, 0, DRIVER_NICK, NULL)) == NULL) + { + return EIO; + } + + osdev->drvinfo = &oss_drvinfo; + + if (!DRIVER_ATTACH (osdev)) + { + cmn_err (CE_WARN, "Attach failed\n"); + osdev_delete (osdev); + return EIO; + } + + return 0; +} + +static int +detach_virtual (void) +{ + if (osdev == NULL) + { + return 0; + } + + if (DRIVER_DETACH (osdev) <= 0) + { + DDB (cmn_err (CE_WARN, "Driver busy - cannot detach\n")); + return EBUSY; + } + + osdev_delete (osdev); + + return 0; +} +#endif + +static int +ossdrv_config (cfg_func_t func, void *idata, rm_key_t key) +{ + switch (func) + { + case CFG_ADD: + return cfg_add (idata, key); + break; + + case CFG_REMOVE: + return cfg_remove (idata); + break; + + case CFG_VERIFY: + return 0; + break; + + } + + return EOPNOTSUPP; +} + +/* + * Driver entry point routines + */ + +int +_load () +{ + int err; + + if ((err = drv_attach (&oss_drvinfo)) != 0) + { + cmn_err (CE_WARN, "drv_attach failed %d\n", err); + return err; + } + +#if DRIVER_TYPE==DRV_VIRTUAL || DRIVER_TYPE==DRV_VMIX + attach_virtual (); +#endif + + return 0; +} + +int +_unload () +{ + extern volatile int oss_open_devices; + + if (oss_open_devices > 0) + return EBUSY; + +#if DRIVER_TYPE==DRV_VIRTUAL || DRIVER_TYPE==DRV_VMIX + detach_virtual (); +#endif + + drv_detach (&oss_drvinfo); + return 0; +} diff --git a/kernel/OS/SCO_SV/os_sco.c b/kernel/OS/SCO_SV/os_sco.c new file mode 100644 index 0000000..b5380ef --- /dev/null +++ b/kernel/OS/SCO_SV/os_sco.c @@ -0,0 +1,1679 @@ +/* + * Purpose: Operating system abstraction functions for SCO OpenServer/UnixWare + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_config.h" +#include "midi_core.h" +#include <oss_pci.h> +#include <sys/exec.h> +#include <sys/user.h> +#include <errno.h> + +/* + * MAX_CARDS must be larger than life. The system will panic if there are + * more sound devices (cards os sound chips) than MAX_CARDS. + */ +#define MAX_CARDS 32 + +static oss_device_t *cards[MAX_CARDS]; +int oss_num_cards = 0; +static int oss_expired = 0; + +volatile int oss_open_devices = 0; + +static bcb_t *oss_bcb; + +/* + * Driver information structures, for drv_attach(). + */ +static int oss_config (cfg_func_t func, void *idata, rm_key_t key); + +static const drvops_t oss_ops = { + oss_config, + oss_open, + oss_close, + oss_devinfo, + oss_biostart, + oss_ioctl, + NULL, /* drvctl */ + NULL /* mmap */ +}; + +static const drvinfo_t oss_drvinfo = { + &oss_ops, + "osscore", + D_MP, /* MP-safe */ + NULL, /* Not a STREAMS driver */ + 10 /* Must match $maxchan in Node file */ +}; + +static physreq_t *physreq_isa = NULL; /* 24 bits */ +static physreq_t *physreq_28bit = NULL; +static physreq_t *physreq_30bit = NULL; +static physreq_t *physreq_31bit = NULL; +static physreq_t *physreq_32bit = NULL; + +#ifdef MEMDEBUG +typedef struct +{ + void *addr; + int size; + char file[40]; + int line; +} mem_block_t; + +#define MAX_MEMBLOCKS 1024 + +static mem_block_t memblocks[MAX_MEMBLOCKS]; +static int num_memblocks = 0; +#endif + +#ifdef MEMDEBUG +void * +oss_kmem_alloc (size_t size, int flags, char *file, int line) +#else +void * +oss_kmem_alloc (size_t size, int flags) +#endif +{ +/* + * This routine allocates a memory block and stores length of it in + * the beginning. This length information can be used when later unallocating + * the memory. + */ + + char *ptr; + uint64_t *len; + + ptr = kmem_zalloc (size + sizeof (uint64_t), flags); +#ifdef MEMDEBUG + cmn_err (CE_CONT, "kmalloc(%d, %s:%d)=%x\n", size, file, line, ptr); +#endif + + if (ptr == NULL) + return NULL; + + len = (uint64_t *) ptr; + + ptr += sizeof (uint64_t); + *len = size + sizeof (uint64_t); + +#ifdef MEMDEBUG +#if 1 + { + int i; + for (i = 0; i < num_memblocks; i++) + if (memblocks[i].addr == NULL) + { + memblocks[i].addr = ptr; + memblocks[i].size = size; + strncpy (memblocks[i].file, file, 39); + memblocks[i].line = line; + return ptr; + } + } +#endif + + if (num_memblocks < MAX_MEMBLOCKS) + { + memblocks[num_memblocks].addr = ptr; + memblocks[num_memblocks].size = size; + strncpy (memblocks[num_memblocks].file, file, 39); + memblocks[num_memblocks].line = line; + num_memblocks++; + } +#endif + + return ptr; +} + +void +#ifdef MEMDEBUG +oss_kmem_free (void *addr, char *file, int line) +#else +oss_kmem_free (void *addr) +#endif +{ + uint64_t *len; + int i; + + char *ptr = addr; + + if (addr == NULL) + return; + + ptr -= sizeof (uint64_t); + + len = (uint64_t *) ptr; + + kmem_free (ptr, *len); +#ifdef MEMDEBUG + { + int i; + + for (i = 0; i < num_memblocks; i++) + if (addr == memblocks[i].addr) + { + memblocks[i].addr = NULL; + return; + } + } + cmn_err (CE_WARN, "Bad kfree(%x, %s:%d)\n", addr, file, line); +#endif +} + +/* + * Table for permanently allocated memory (to be freed by _fini) + */ +#define OSS_MAX_MEM 1024 +void *oss_mem_blocks[OSS_MAX_MEM]; +int oss_nblocks = 0; + +void * +#ifdef MEMDEBUG +oss_pmalloc (size_t size, char *file, int line) +#else +oss_pmalloc (size_t size) +#endif +{ + void *mem_ptr; +#ifdef MEMDEBUG + mem_ptr = (oss_mem_blocks[oss_nblocks] = + oss_kmem_alloc (size, KM_SLEEP, file, line)); +#else + mem_ptr = (oss_mem_blocks[oss_nblocks] = KERNEL_MALLOC (size)); +#endif + + if (oss_nblocks <= OSS_MAX_MEM) + oss_nblocks++; + else + cmn_err (CE_NOTE, "Out of mem blocks\n"); + return mem_ptr; +} + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + void *p; + oss_native_word phaddr; + int size = 64 * 1024; + extern int dma_buffsize; + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + + if (dmap->dmabuf != NULL) + return 0; + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + if ((p = + oss_contig_malloc (audio_engines[dev]->osdev, size, maxaddr, + &phaddr)) == NULL) + return OSS_ENOMEM; + + dmap->dmabuf = p; + dmap->dmabuf_phys = phaddr; + dmap->buffsize = size; + + return 0; +} + +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + if (dmap->dmabuf == NULL) + return; + + oss_contig_free (audio_engines[dev]->osdev, dmap->dmabuf, dmap->buffsize); + dmap->dmabuf = NULL; + dmap->dmabuf_phys = 0; + dmap->buffsize = 0; +} + +void * +oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, + oss_native_word * phaddr) +{ + void *p = NULL; + physreq_t *preqp = physreq_32bit; + paddr32_t pa; + + *phaddr = 0; + + switch (memlimit) + { + case MEMLIMIT_ISA: + preqp = physreq_isa; + break; + case MEMLIMIT_28BITS: + preqp = physreq_28bit; + break; + case MEMLIMIT_30BITS: + preqp = physreq_30bit; + break; + case MEMLIMIT_31BITS: + preqp = physreq_31bit; + break; + case MEMLIMIT_32BITS: + preqp = physreq_32bit; + break; + + default: + cmn_err (CE_WARN, "osscore: Bad DMA memlimit for %s\n", osdev->nick); + } + + if ((p = kmem_alloc_phys (size, preqp, &pa, 0)) == NULL) + { + cmn_err (CE_WARN, "osscore: kmem_alloc_phys() failed\n"); + return NULL; + } + + *phaddr = pa; + return p; +} + +void +oss_contig_free (oss_device_t * osdev, void *p, int sz) +{ + if (p) + kmem_free (p, sz); +} + +/* + * Wait queue support + */ +oss_wait_queue_t * +oss_create_wait_queue (oss_device_t * osdev, const char *name) +{ + oss_wait_queue_t *q; + + if ((q = KERNEL_MALLOC (sizeof (*q))) == NULL) + { + cmn_err (CE_WARN, "osscore: Cannot allocate memory for a wait queue\n"); + return NULL; + } + + memset (q, 0, sizeof (*q)); + + if ((q->sv = SV_ALLOC (KM_SLEEP)) == NULL) + { + cmn_err (CE_WARN, + "osscore: Cannot allocate synchronization variable\n"); + return NULL; + } + + return q; +} + +void +oss_reset_wait_queue (oss_wait_queue_t * wq) +{ + // NOP +} + +void +oss_remove_wait_queue (oss_wait_queue_t * wq) +{ + SV_DEALLOC (wq->sv); + KERNEL_FREE (wq); +} + +static void +sleep_timeout (caddr_t arg) +{ + oss_wait_queue_t *wq = (oss_wait_queue_t *) arg; + + SV_BROADCAST (wq->sv, 0); +} + +int +oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ + timeout_id_t tid; + + *status = 0; + wq->flags = 0; + + if (wq == NULL) + { + cmn_err (CE_WARN, "osscore: Unallocated wait queue\n"); + *status |= WK_SIGNAL; + return 1; + } + if (ticks > 0) + tid = timeout (sleep_timeout, wq, ticks); + + /* + * Note SV_WAIT_SIG() will release the mutex/lock so we must re-acquire + * it before returning. + */ +#ifdef MUTEX_CHECKS + // Pop mutex because it will be released by SV_WAIT_SIG() + pop_mutex (*mutex, __FILE__, __LINE__); +#endif + if (!SV_WAIT_SIG (wq->sv, prihi, *mutex)) /* Signal */ + { + *status |= WK_SIGNAL; + wq->flags |= WK_WAKEUP; /* Needed to prevent false timeout messages */ + } + + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); + if (ticks > 0 && (wq->flags & WK_WAKEUP)) + untimeout (tid); + + return (wq->flags & WK_WAKEUP); +} + +int +oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev) +{ + // NOP: DDI8 doesn't support chpoll +} + +void +oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + wq->flags |= WK_WAKEUP; + SV_BROADCAST (wq->sv, 0); +} + +void * +oss_get_osid (oss_device_t * osdev) +{ + return osdev->osid; +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * element_size))==NULL) + return 0; + + memset(new, 0, new_size * element_size); + if (old != NULL) + memcpy(new, old, old_size * element_size); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ +/* + * oss_install_chrdev creates a character device (minor). However if + * name==NULL the device will not be exported (made visible to userland + * clients). + */ + + int i, num; + oss_cdev_t *cdev = NULL; + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; +/* + * Find if this dev_class&instance already exists (after previous module + * detach). + */ + + for (num = 0; num < oss_num_cdevs; num++) + if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ + if (oss_cdevs[num]->dev_class == dev_class + && oss_cdevs[num]->instance == instance) + { + cdev = oss_cdevs[num]; + break; + } + + if (cdev == NULL) + { + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) + { + cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); + return; + } + } + + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + num = oss_num_cdevs++; + } + + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name) - 1); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; +} + +int +oss_find_minor (int dev_class, int instance) +{ + int i; + + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class + && oss_cdevs[i]->instance == instance) + return i; + + return OSS_EIO; +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + if (cardnum < 0 || cardnum >= oss_num_cards) + return OSS_ENXIO; + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->shortname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info) - 1); + ci->hw_info[sizeof (ci->hw_info) - 1] = 0; + + return 0; +} + +void +oss_reserve_device (oss_device_t * osdev) +{ + osdev->refcount++; +} + +void +oss_unreserve_device (oss_device_t * osdev, int decrement) +{ + osdev->refcount--; + if (osdev->refcount < 0) + osdev->refcount = 0; +} + +/* + * Some standard C library routines + */ +void * +memset (void *t, int c, size_t l) +{ + int i; + + if (t == NULL) + return NULL; + + for (i = 0; i < l; i++) + ((char *) t)[i] = c; + + return t; +} + +void * +memcpy (void *t, const void *s, size_t l) +{ + bcopy (s, t, l); + return t; +} + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, + const char *handle) +{ + oss_device_t *osdev = NULL; + int i, err; + caddr_t addr; + dev_info_t ldip = 0; + + if (dip == NULL) + dip = &ldip; + + if (handle == NULL) + handle = nick; + + /* + * Don't accept any more drivers if expired + */ + if (oss_expired && oss_num_cards > 0) + return NULL; + +/* + * Check if a driver/device was reinserted + */ + if (dip != &ldip) /* Not a virtual driver */ + for (i = 0; i < oss_num_cards; i++) + { + if (!cards[i]->available && cards[i]->key == *dip) + { + osdev = cards[i]; + break; + } + } + + if (osdev == NULL) + { + if (oss_num_cards >= MAX_CARDS) + { + cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + return NULL; + } + + if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + } + osdev->key = *dip; + osdev->osid = dip; + osdev->available = 1; + osdev->first_mixer = -1; + osdev->instance = instance; + osdev->dev_type = dev_type; + osdev->devc = NULL; + MUTEX_INIT (osdev, osdev->mutex, MH_GLOBAL); + sprintf (osdev->nick, "%s%d", nick, instance); + strcpy (osdev->modname, nick); + + switch (dev_type) + { + case DRV_PCI: + //pci_config_setup (dip, &osdev->pci_config_handle); + break; + + case DRV_VIRTUAL: + case DRV_VMIX: + case DRV_STREAMS: + /* NOP */ + break; + + case DRV_USB: + /* NOP */ + break; + + default: + cmn_err (CE_WARN, "Bad device type\n"); + return NULL; + } + +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { + unsigned int subvendor = 0; + pci_read_config_dword (osdev, 0x2c, &subvendor); + + sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance); + } + break; + +#if 0 + case DRV_USB: + sprintf (osdev->handle, "USB-%s%d", handle, instance); + break; +#endif + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + + if (osdev == NULL) + return; + + if (!osdev->available) + { + cmn_err (CE_WARN, "device %s, osdev already deleted\n", osdev->nick); + return; + } + + osdev->available = 0; + + switch (osdev->dev_type) + { + case DRV_PCI: + break; + } + +/* + * Mark all minor nodes for this module as invalid. + */ + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->osdev == osdev) + { + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->osdev = NULL; + strcpy (oss_cdevs[i]->name, "Removed device"); + } + MUTEX_CLEANUP (osdev->mutex); +} + +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + return 0; +} + +int +oss_disable_device (oss_device_t * osdev) +{ + int i; +/* + * This routine should check if the device is ready to be unloaded (no devices are in use). + * If the device cannot be unloaded this routine must return OSS_EBUSY. + * + * If the device can be unloaded then disable any timers or other features that may cause the + * device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. + * However the interrupt handler should still stay enabled. The low level driver will call + * oss_unregister_interrupts() after it has cleared the interrupt enable register. + */ + if (osdev->refcount > 0) + { + return OSS_EBUSY; + } + +/* + * Now mark all devices unavailable (for the time being) + */ + + for (i = 0; i < num_mixers; i++) + if (mixer_devs[i]->osdev == osdev) + { + mixer_devs[i]->unloaded = 1; + } + + for (i = 0; i < num_mididevs; i++) + { + if (midi_devs[i]->osdev == osdev) + { + midi_devs[i]->unloaded = 1; + } + } + + for (i = 0; i < num_audio_engines; i++) + if (audio_engines[i]->osdev == osdev) + { + audio_uninit_device (i); + } + + return 0; +} + +void +oss_unregister_device (oss_device_t * osdev) +{ +} + +#ifdef MUTEX_CHECKS +static int oss_context = 0; /* 0=user context, 1=interrupt context */ +#endif + +static int +ossintr (void *idata) +{ + oss_device_t *osdev = idata; + oss_native_word flags; +#ifdef MUTEX_CHECKS + int saved_context; + saved_context = oss_context; + if (oss_context == 1) + cmn_err (CE_WARN, "Recursive interrupt\n"); + oss_context = 1; +#endif + + MUTEX_ENTER_IRQDISABLE (osdev->mutex, flags); + + if (!osdev->tophalf_handler (osdev)) + { + MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); +#ifdef MUTEX_CHECKS + oss_context = saved_context; +#endif + return ISTAT_NONE; + } + + if (osdev->bottomhalf_handler != NULL) + osdev->bottomhalf_handler (osdev); + + MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); +#ifdef MUTEX_CHECKS + oss_context = saved_context; +#endif + + return ISTAT_ASSERTED; +} + +int +oss_register_interrupts (oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom) +{ + int err; + + if (intrnum != 0) + { + cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum, + osdev->name); + return OSS_EINVAL; + } + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n"); + return OSS_EINVAL; + } + + if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL) + { + cmn_err (CE_WARN, "Interrupts already registered for %s\n", + osdev->name); + return OSS_EINVAL; + } + + if (top == NULL) + { + cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name); + return OSS_EINVAL; + } + + osdev->tophalf_handler = top; + osdev->bottomhalf_handler = bottom; + if (cm_intr_attach + (osdev->key, ossintr, osdev, osdev->drvinfo, &osdev->intr_cookie) == 0) + { + cmn_err (CE_WARN, "cm_intr_attach failed for %s\n", osdev->nick); + } + + return 0; +} + +void +oss_unregister_interrupts (oss_device_t * osdev) +{ + if (osdev->intr_cookie != NULL) + cm_intr_detach (osdev->intr_cookie); + osdev->intr_cookie = NULL; +} + +static char * +ksprintn (ul, base, lenp) + register u_long ul; + register int base, *lenp; +{ /* A long in base 8, plus NULL. */ + static char buf[sizeof (long) * NBBY / 3 + 2]; + register char *p; + + p = buf; + do + { + *++p = "0123456789abcdef"[ul % base]; + } + while (ul /= base); + if (lenp) + *lenp = p - buf; + return (p); +} + +int +oss_sprintf (char *buf, char *cfmt, ...) +{ + const char *fmt = cfmt; + register char *p, *bp; + register int ch, base; + unsigned long ul; + int lflag; + int count = 10; + va_list ap; + + va_start (ap, fmt); + for (bp = buf;;) + { + while ((ch = *(unsigned char *) fmt++) != '%') + if ((*bp++ = ch) == '\0') + { + va_end (ap); + return ((bp - buf) - 1); + } + + lflag = 0; + reswitch: + switch (ch = *(unsigned char *) fmt++) + { + case 'l': + lflag = 1; + goto reswitch; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + goto reswitch; + + case 'c': + *bp++ = va_arg (ap, int); + break; + + case 's': + p = va_arg (ap, char *); + while (*bp++ = *p++) + ; + --bp; + break; + + case 'd': + ul = lflag ? va_arg (ap, long) : va_arg (ap, int); + if ((long) ul < 0) + { + *bp++ = '-'; + ul = -(long) ul; + } + base = 10; + goto number; + break; + + case 'o': + ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); + base = 8; + goto number; + break; + + case 'u': + ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); + base = 10; + goto number; + break; + + case 'x': + ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); + base = 16; + number: + for (p = (char *) ksprintn (ul, base, NULL); ch = *p--;) + *bp++ = ch; + break; + + default: + *bp++ = '%'; + if (lflag) + *bp++ = 'l'; + /* FALLTHROUGH */ + + case '%': + *bp++ = ch; + } + } + + /* va_end(ap); */ +} + +static physreq_t * +build_physreq (int bits) +{ + physreq_t *pr; + long long tmp; + + if ((pr = physreq_alloc (KM_SLEEP)) == NULL) + return NULL; + + pr->phys_align = 4096; + pr->phys_boundary = 0; + pr->phys_dmasize = bits; + pr->phys_max_scgth = 0; + pr->phys_flags = PREQ_PHYSCONTIG; + + if (physreq_prep (pr, KM_SLEEP) != B_TRUE) + { + cmn_err (CE_WARN, "osscore: physreq_prep failed\n"); + } + + return pr; +} + +/* + * Driver info device file + */ +static int drvinfo_busy = 0; +static int drvinfo_len = 0, drvinfo_ptr = 0; +static char *drvinfo_buf = NULL; +#define DRVINFO_SIZE 4096 + +static int +drvinfo_open (int dev, int dev_class, struct fileinfo *file, + int recursive, int open_flags, int *redirect) +{ + int i; + + if (drvinfo_busy) + return OSS_EBUSY; + drvinfo_busy = 1; + + if ((drvinfo_buf = KERNEL_MALLOC (DRVINFO_SIZE)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate drvinfo buffer\n"); + return OSS_ENOMEM; + } + + drvinfo_len = 0; + drvinfo_ptr = 0; + + for (i = 1; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->name[0] != 'N' || oss_cdevs[i]->name[1] != 'O' || oss_cdevs[i]->name[2] != 'N' || oss_cdevs[i]->name[3] != 'E') /* Not a dummy placeholder device */ + if (DRVINFO_SIZE - drvinfo_len > 32) + { + char *s = drvinfo_buf + drvinfo_len; + + drvinfo_len += + oss_sprintf (s, "%s %s %d\n", oss_cdevs[i]->name, + oss_cdevs[i]->osdev->nick, i); + + } + + return 0; +} + +static void +drvinfo_close (int dev, struct fileinfo *file) +{ + KERNEL_FREE (drvinfo_buf); + drvinfo_buf = NULL; + drvinfo_len = 0; + drvinfo_ptr = 0; + drvinfo_busy = 0; +} + +static int +drvinfo_read (int dev, struct fileinfo *file, uio_t * buf, int count) +{ + /* + * Return at most 'count' bytes from the drvinfo_buf. + */ + int l, c; + + l = count; + c = drvinfo_len - drvinfo_ptr; + + if (l > c) + l = c; + if (l <= 0) + return 0; + + if (uiomove (&drvinfo_buf[drvinfo_ptr], l, UIO_READ, buf) != 0) + return OSS_EFAULT; + drvinfo_ptr += l; + + return l; +} + +static oss_cdev_drv_t drvinfo_cdev_drv = { + drvinfo_open, + drvinfo_close, + drvinfo_read +}; + +static void +install_drvinfo (oss_device_t * osdev) +{ + oss_install_chrdev (osdev, "ossinfo", OSS_DEV_MISC, 0, &drvinfo_cdev_drv, + 0); +} + +/* + * Driver entry point routines + */ + +int +_load () +{ + int err = 0; + oss_device_t *osdev; + time_t t; + + if ((err = drv_attach (&oss_drvinfo)) != 0) + { + cmn_err (CE_WARN, "drv_attach failed %d\n", err); + return err; + } + +#ifdef LICENSED_VERSION + if (drv_getparm (TIME, &t) != 0) + { + cmn_err (CE_WARN, "drv_getparm(TIME) failed\n"); + return EBUSY; + } + + if (!oss_license_handle_time (t)) + { + cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); + cmn_err (CE_CONT, + "Please download the latest version from www.opensound.com\n"); + oss_expired = 1; + } +#endif + + if ((osdev = osdev_create (NULL, DRV_VIRTUAL, 0, "oss", NULL)) == NULL) + { + cmn_err (CE_WARN, "Creating osdev failed\n"); + return ENOMEM; + } + +/* + * Allocate physrec structures for various memory ranges. Remember to free them in the _load + * entry point. + */ + physreq_isa = build_physreq (24); + physreq_28bit = build_physreq (28); + physreq_30bit = build_physreq (30); + physreq_31bit = build_physreq (31); + physreq_32bit = build_physreq (32); + +/* + * Create the BCB structure + */ + oss_bcb = bcb_alloc (KM_SLEEP); + oss_bcb->bcb_addrtypes = BA_UIO; + oss_bcb->bcb_flags = 0; + oss_bcb->bcb_max_xfer = 0; + oss_bcb->bcb_granularity = 1; + oss_bcb->bcb_physreqp = physreq_32bit; + bcb_prep (oss_bcb, KM_SLEEP); + + install_drvinfo (osdev); + oss_common_init (osdev); + + oss_register_device (osdev, "OSS core services"); + + return 0; +} + +int +_unload () +{ + int i; + static int already_unloaded = 0; + + if (oss_open_devices > 0) + return EBUSY; + drv_detach (&oss_drvinfo); + + if (already_unloaded) + return 0; + already_unloaded = 1; + + oss_unload_drivers (); + + physreq_free (physreq_isa); + physreq_free (physreq_28bit); + physreq_free (physreq_30bit); + physreq_free (physreq_31bit); + physreq_free (physreq_32bit); + + bcb_free (oss_bcb); + + for (i = 0; i < oss_nblocks; i++) + KERNEL_FREE (oss_mem_blocks[i]); + oss_nblocks = 0; + + return 0; +} + +void +oss_pci_byteswap (oss_device_t * osdev, int mode) +{ + // NOP +} + +void +oss_pcie_init (oss_device_t * osdev, int flags) +{ + /* TODO: Should we do something? */ +} + +int +pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + *val = 0; + if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == + sizeof (*val)) + return PCIBIOS_SUCCESSFUL; + + return PCIBIOS_FAILED; +} + +int +pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) +{ + *val = 0; + if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == + sizeof (*val)) + return PCIBIOS_SUCCESSFUL; + + return PCIBIOS_FAILED; +} + + +int +pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + *val = 0; +#if 1 + if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == + sizeof (*val)) + return PCIBIOS_SUCCESSFUL; +#else + switch (where) + { + case PCI_VENDOR_ID: + *val = osdev->vendor; + return PCIBIOS_SUCCESSFUL; + break; + + case PCI_DEVICE_ID: + *val = osdev->product; + return PCIBIOS_SUCCESSFUL; + break; + + } + +#endif + return PCIBIOS_FAILED; +} + +int +pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + *val = 0; + if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == + sizeof (*val)) + return PCIBIOS_SUCCESSFUL; + + return PCIBIOS_FAILED; +} + +int +pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) == + sizeof (val)) + return PCIBIOS_SUCCESSFUL; + + return PCIBIOS_FAILED; +} + +int +pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) == + sizeof (val)) + return PCIBIOS_SUCCESSFUL; + + return PCIBIOS_FAILED; +} + +int +pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) == + sizeof (val)) + return PCIBIOS_SUCCESSFUL; + + return PCIBIOS_FAILED; +} + +/* + * Entry point routines + */ +static int +oss_config (cfg_func_t func, void *idata, rm_key_t key) +{ + return EOPNOTSUPP; +} + +int +oss_devinfo (void *idata, channel_t channel, di_parm_t parm, void *valp) +{ + switch (parm) + { + case DI_MEDIA: + break; + + case DI_SIZE: + break; + + case DI_RBCBP: + *(bcb_t **) valp = oss_bcb; + return 0; + break; + + case DI_WBCBP: + *(bcb_t **) valp = oss_bcb; + return 0; + break; + + case DI_PHYS_HINT: + break; + + } + return EOPNOTSUPP; +} + +void +oss_biostart (void *idata, channel_t channel, buf_t * bp) +{ + int dev = channel; + oss_cdev_t *cdev; + int count = bp->b_un.b_uio->uio_resid; + int retval; + + if ((cdev = oss_cdevs[dev]) == NULL) + { + bioerror (bp, ENXIO); + biodone (bp); + return; + } + + cdev->file.acc_flags = 0; + + if (bp->b_flags & B_READ) + { + /* Read operation */ + if (cdev->d->read == NULL) + { + bioerror (bp, ENXIO); + biodone (bp); + return; + } + retval = + cdev->d->read (cdev->instance, &cdev->file, bp->b_un.b_uio, count); + if (retval < 0) + bioerror (bp, -retval); + else if (retval < count) + bp->b_resid = count - retval; + + biodone (bp); + return; + } + + /* Write operation */ + if (cdev->d->write == NULL) + { + bioerror (bp, ENXIO); + biodone (bp); + return; + } + retval = + cdev->d->write (cdev->instance, &cdev->file, bp->b_un.b_uio, count); + if (retval < 0) + bioerror (bp, -retval); + else if (retval < count) + bp->b_resid = count - retval; + + biodone (bp); +} + +int +oss_open (void *idata, channel_t * channelp, + int open_flags, cred_t * crp, queue_t * q) +{ + oss_device_t *osdev; + int dev = *channelp, tmpdev; + oss_cdev_t *cdev; + int retval; + + osdev = idata; + + if (dev >= OSS_MAX_CDEVS) + return ENXIO; + + if (dev >= oss_num_cdevs) + { + return ENODEV; + } + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENODEV; + + if (cdev->d->open == NULL) + { + return ENODEV; + } + + memset (&cdev->file, 0, sizeof (cdev->file)); + cdev->file.mode = 0; + cdev->file.acc_flags = open_flags; + + if (open_flags & FREAD && open_flags & FWRITE) + cdev->file.mode = OPEN_READWRITE; + else if (open_flags & FREAD) + cdev->file.mode = OPEN_READ; + else if (open_flags & FWRITE) + cdev->file.mode = OPEN_WRITE; + + tmpdev = dev; + retval = + cdev->d->open (cdev->instance, cdev->dev_class, &cdev->file, 0, 0, &tmpdev); + *channelp = tmpdev; + dev = tmpdev; + + if (retval < 0) + { + return -retval; + } + + oss_open_devices++; + cdev->open_count++; + + return 0; +} + +int +oss_close (void *idata, channel_t channel, + int oflags, cred_t * crp, queue_t * q) +{ + oss_cdev_t *cdev; + int dev; + + dev = channel; + + if (dev >= OSS_MAX_CDEVS) + return ENXIO; + + if ((cdev = oss_cdevs[dev]) == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + return 0; + + cdev->d->close (cdev->instance, &cdev->file); + + oss_open_devices--; + cdev->open_count--; + return 0; +} + +int +oss_ioctl (void *idata, channel_t channel, int cmd, void *arg, + int oflags, cred_t * crp, int *rvalp) +{ + int dev = channel; + int retval; + int len = 0; + char localbuf[256]; /* All frequently used ioctl calls fit in 256 bytes */ + char *buf = localbuf, *auxbuf = NULL; + oss_cdev_t *cdev; + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL) + return *rvalp = ENXIO; + + if (oss_expired) + return *rvalp = ENODEV; + + if (cmd & (SIOC_OUT | SIOC_IN)) + { + + len = __SIOC_SIZE (cmd); + if (len < 0) + len = 0; + if (len > sizeof (localbuf)) + { + /* + * For the few largest ioctl calls we need to allocate an off-stack + * buffer because otherwise the kernel stack will overflow. + * This approach is slower but fortunately "sane" applications don't + * use these ioctl calls too frequently. + */ + if ((auxbuf = KERNEL_MALLOC (len)) == NULL) + { + cmn_err (CE_WARN, + "Failed to allocate an ioctl buffer of %d bytes\n", + len); + return *rvalp = ENOMEM; + } + buf = auxbuf; + } + + if ((cmd & SIOC_IN) && len > 0) + { + if (copyin ((char *) arg, buf, len) == -1) + { + if (auxbuf != NULL) + KERNEL_FREE (auxbuf); + return *rvalp = EFAULT; + } + } + + } + + retval = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) buf); + + if ((cmd & SIOC_OUT) && len > 0) + { + if (copyout (buf, (char *) arg, len) == -1) + { + if (auxbuf != NULL) + KERNEL_FREE (auxbuf); + return *rvalp = EFAULT; + } + } + + *rvalp = (((retval) < 0) ? -(retval) : 0); + + if (auxbuf != NULL) + KERNEL_FREE (auxbuf); + + return *rvalp; +} + +#ifdef MUTEX_CHECKS + +typedef struct +{ + int active; + void *mutex; + const char *filename; + int line; + int context; +} mutex_debug_t; + +static mutex_debug_t mutex_tab[1024]; +static int n_mutexes = 0; + +void +push_mutex (void *mutex, const char *file, int line) +{ + int i, n = -1; +//cmn_err(CE_CONT, "Push mutex %08x, %s:%d, context=%d\n", mutex, file, line, oss_context); + + for (i = 0; n == -1 && i < n_mutexes; i++) + if (!mutex_tab[i].active) + n = i; + else + { + if (mutex_tab[i].mutex == mutex + && mutex_tab[i].context == oss_context) + { + cmn_err (CE_NOTE, "Mutex %08x already held\n", mutex); + cmn_err (CE_CONT, " Locked by %s:%d\n", mutex_tab[i].filename, + mutex_tab[i].line); + cmn_err (CE_CONT, " Acquire by %s:%d\n", file, line); + } + } + + if (n == -1) + { + if (n_mutexes >= 1024) + { + cmn_err (CE_NOTE, "Mutex debug table full\n"); + return; + } + n = n_mutexes++; + } + + mutex_tab[n].active = 1; + mutex_tab[n].mutex = mutex; + mutex_tab[n].filename = file; + mutex_tab[n].line = line; + mutex_tab[n].context = oss_context; +} + +void +pop_mutex (void *mutex, const char *file, int line) +{ + int i; + +//cmn_err(CE_CONT, "Pop mutex %08x, %s:%d, context=%d\n", mutex, file, line, oss_context); + for (i = 0; i < n_mutexes; i++) + if (mutex_tab[i].active && mutex_tab[i].mutex == mutex + && mutex_tab[i].context == oss_context) + { + mutex_tab[i].active = 0; + mutex_tab[i].filename = file; + mutex_tab[i].line = line; + return; + } + + cmn_err (CE_NOTE, "Mutex %08x not locked (%s:%d), context=%d\n", + mutex, file, line, oss_context); + for (i = 0; i < n_mutexes; i++) + if (mutex_tab[i].active == 0 && mutex_tab[i].mutex == mutex) + { + cmn_err (CE_CONT, " Previous unlock at %s:%d, context=%d\n", + mutex_tab[i].filename, mutex_tab[i].line, + mutex_tab[i].context); + } +} + +void +print_mutexes (void) +{ + int i, n = 0; + + for (i = 0; i < n_mutexes; i++) + if (mutex_tab[i].active) + n++; + + cmn_err (CE_CONT, "%d mutexes held\n", n); + + for (i = 0; i < n_mutexes; i++) + if (mutex_tab[i].active) + cmn_err (CE_CONT, " %08x %s:%d\n", mutex_tab[i].mutex, + mutex_tab[i].filename, mutex_tab[i].line); +} +#endif + +int +oss_get_procinfo(int what) +{ + // TODO + + return OSS_EINVAL; +} diff --git a/kernel/OS/SCO_SV/os_sco.h b/kernel/OS/SCO_SV/os_sco.h new file mode 100644 index 0000000..0d5d345 --- /dev/null +++ b/kernel/OS/SCO_SV/os_sco.h @@ -0,0 +1,408 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for SCO OpenServer and SCO UnixWare (DDI8) + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#define OS_VERSION "6" +#define OpenServer + +#if (!defined(i386) && !defined(x86_64)) || defined(CONFIG_OSS_FIXDEPOINT) +// Floating point is not supported or it's disabled +#undef CONFIG_OSS_VMIX_FLOAT +#endif + +#if 0 +// Enable kernel level debugging +#define DEBUG +/* + * NOTE! Virtual devices (such as vmix, imux and midiloop) cannot be used + * when OSS is compiled with _LOCKTEST. Such drivers will not pass + * deadlock detection because virtual drivers make calls to the audio + * core and other drivers that use lower hierarchy levels. For the + * reason the system will crash as soon as the virtual devices get used. + */ +#define _LOCKTEST +#define MUTEX_CHECKS +#undef MEMDEBUG +#endif + +// Timing/tracing analysis stuff. This info can be read with the readtimings utility. + +#ifdef DEBUG +#include <sys/debug.h> +#endif + +#define VDEV_SUPPORT +#define USE_DEVICE_SUBDIRS + +#define __inline__ inline +#define __inline inline +#define EXTERN_C extern "C" + +/* + * Do not export global parameters in oss_core_options.c since they will be + * handled in Space.c for osscore + */ +#define NO_GLOBAL_OPTIONS + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ +#undef APPLIST_SUPPORT + +/* + * Some integer types + */ +#if defined(amd64) || defined(sparc) +typedef unsigned long long oss_native_word; /* Same as the address and status register size */ +#else +typedef unsigned long oss_native_word; /* Same as the address and status register size */ +#endif + +typedef long long oss_int64_t; /* Signed 64 bit integer */ +typedef unsigned long long oss_uint64_t; /* Unsigned 64 bit integer */ +typedef unsigned long offset_t; + +#include <stdarg.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/signal.h> +#include <oss_errno.h> +#include <sys/file.h> +#include <sys/conf.h> +#include <sys/uio.h> +#include <sys/map.h> +#include <sys/debug.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/open.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <sys/ksynch.h> +#include <sys/confmgr.h> +#include <sys/cm_i386at.h> +#include <sys/poll.h> + +/* + * ddi.h must be the last include + */ +#include <sys/ddi.h> + +#ifndef HZ +extern int hz; +#define HZ hz +#endif +#define OSS_HZ HZ + +/* + * Mutexes + */ + +#ifdef _KERNEL +typedef lock_t *oss_mutex_t; +#else +typedef int oss_mutex_t; +#endif + +/* The soundcard.h could be in a nonstandard place so include it here. */ +#include "soundcard.h" + +typedef struct udi_usb_devc udi_usb_devc; +typedef rm_key_t dev_info_t; + +struct _oss_device_t +{ +#ifdef _KERNEL + int cardnum; + int dev_type; + int available; + int instance; + rm_key_t key; + drvinfo_t *drvinfo; + void *osid; + void *devc; + char *name; + char nick[16]; + char modname[16]; + char handle[32]; + char *hw_info; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +/* Interrupts */ + + void *intr_cookie; /* Returned by cm_intr_attach */ + oss_tophalf_handler_t tophalf_handler; + oss_bottomhalf_handler_t bottomhalf_handler; + oss_mutex_t mutex; + +/* PCI configuration */ + int vendor, product; +#else + int dummy; +#endif +}; + +/* + * Support for select() + */ + +struct _oss_poll_event_t +{ + short events, revents; + struct pollhead *php; + int anyyet; +}; +typedef struct _oss_poll_event_t oss_poll_event_t; + +#define ALLOW_SELECT +#define SEL_IN 0 +#define SEL_OUT 1 +#define SEL_EX 0xffffffff + +/* + * Sleep/wakeup + */ + +#ifdef _KERNEL +struct oss_wait_queue +{ + sv_t *sv; + short pollevents; + volatile unsigned int flags; +}; +#endif + +#define FP_SUPPORT + +#ifdef FP_SUPPORT +typedef short fp_env_t[512]; +typedef unsigned int fp_flags_t[4]; +extern int oss_fp_check (void); +extern void oss_fp_save (short *envbuf, fp_flags_t flags); +extern void oss_fp_restore (short *envbuf, fp_flags_t flags); +# define FP_SAVE(envbuf, flags) oss_fp_save(envbuf, flags) +# define FP_RESTORE(envbuf, flags) oss_fp_restore(envbuf, flags) +#endif + +/* Busy wait routine */ +#define oss_udelay drv_usecwait +/* System wall timer access */ +#define GET_JIFFIES() TICKS() + +#ifdef MUTEX_CHECKS +extern void push_mutex (void *mutex, const char *file, int line); +extern void pop_mutex (void *mutex, const char *file, int line); +extern void print_mutexes (void); +#else +#define push_mutex(a, b, c) +#define pop_mutex(a, b, c) +#endif + +#ifdef _LOCKTEST +# define MUTEX_INIT(osdev, mutex, hier) \ + {\ + static LKINFO_DECL(mtx_name, __FILE__ ":" #mutex, 0);\ + mutex=LOCK_ALLOC((uchar_t)(hier), pldisk, &mtx_name, KM_SLEEP);\ + } +#else +# define MUTEX_INIT(osdev, mutex, hier) \ + {\ + mutex=LOCK_ALLOC((uchar_t)(hier), pldisk, NULL, KM_SLEEP);\ + } +#endif + +#define MUTEX_CLEANUP(mutex) LOCK_DEALLOC(mutex) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) {push_mutex(mutex, __FILE__, __LINE__);flags=LOCK(mutex, pldisk);} +#define MUTEX_ENTER(mutex, flags) {push_mutex(mutex, __FILE__, __LINE__);flags=LOCK(mutex, pldisk);} +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) {pop_mutex(mutex, __FILE__, __LINE__);UNLOCK(mutex, flags);} +#define MUTEX_EXIT(mutex, flags) {pop_mutex(mutex, __FILE__, __LINE__);UNLOCK(mutex, flags);} + +/* + * Move bytes from the buffer which the application given in a + * write() call. + * offs is position relative to the beginning of the buffer in + * user space. The count is number of bytes to be moved. + */ +#define COPY_FROM_USER(target, source, offs, count) \ + if (uiomove((target), count, UIO_WRITE, source)) { \ + cmn_err(CE_WARN, "Bad copyin()!\n"); \ + } +/* Like COPY_FOM_USER but for writes. */ +#define COPY_TO_USER(target, offs, source, count) \ + if (uiomove((source), count, UIO_READ, target)) { \ + cmn_err(CE_WARN, "Bad copyout()!\n"); \ + } + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +/* I/O Mapped devices */ +#define INB(o, p) inb(p) +#define INW(o, p) inw(p) +#define INL(o, p) inl(p) + +#define OUTB(o, v, p) outb(p,v) +#define OUTW(o, v, p) outw(p,v) +#define OUTL(o, v, p) outl(p,v) + +/* Memory Mapped devices */ +#define PCI_READB(osdev, addr) *(volatile unsigned char *)(addr) +#define PCI_READW(osdev, addr) *(volatile unsigned short *)(addr) +#define PCI_READL(osdev, addr) *(volatile unsigned int *)(addr) + +#define PCI_WRITEB(osdev, addr, data) *(volatile unsigned char *)(addr)=data +#define PCI_WRITEW(osdev, addr, data) *(volatile unsigned short *)(addr)=data +#define PCI_WRITEL(osdev, addr, data) *(volatile unsigned int *)(addr)=data + +#ifndef TRUE +#define TRUE (1) +#define FALSE (0) +#endif + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#ifdef MEMDEBUG +extern void *oss_kmem_alloc (size_t size, int flags, char *file, int line); +extern void oss_kmem_free (void *addr, char *file, int line); +# define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, KM_SLEEP, __FILE__, __LINE__) +# define KERNEL_FREE(addr) oss_kmem_free(addr, __FILE__, __LINE__) +#else +extern void *oss_kmem_alloc (size_t size, int flags); +extern void oss_kmem_free (void *addr); +# define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, KM_SLEEP) +# define KERNEL_FREE(addr) oss_kmem_free(addr) +#endif + +typedef void *oss_dma_handle_t; + +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +extern oss_native_word oss_virt_to_bus (void *addr); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) + +/* + * Timer macros + * + * These macros are obsolete and should not be used in any new code. + * Use the timeout mechanism (see the timeout(9F) Solaris man page). + */ +#define timeout(fn, arg, ticks) itimeout(fn, arg, ticks, pltimeout) +typedef int timeout_id_t; +#define DEFINE_TIMER(name, proc) static timeout_id_t name = 0 +#define REMOVE_TIMER(name, proc) {if (name != 0) untimeout(name);} +#define INIT_TIMER(name,proc) +typedef void (*timeout_func_t) (void *); +#define ACTIVATE_TIMER(name, proc, time) \ + name=timeout((timeout_func_t)proc, (void*)&name, time) + +#ifdef _KERNEL +struct os_dma_params +{ + int state; /* 0=unavail, 1=avail, 2=busy */ + oss_device_t *osdev; + void *orig_buf; + + volatile int enabled, ignore; +}; +#define OS_DMA_PARMS \ + struct os_dma_params dma_parms; +#endif +#endif +struct fileinfo +{ + int mode; /* Open mode */ + int acc_flags; +}; +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +#define OSS_OS "SCO" +#define OSS_OS_LONGNAME "SCO OpenServer/UnixWare" + +#undef DMA_TRY_PSEUDOINIT + +int get_dma_residue (int chn); +void disable_dma (int chn); +void enable_dma (int chn); + +typedef void (*softintr_func_t) (int); + +struct oss_softintr +{ + int id; + softintr_func_t func; + volatile int armed, running; +}; + +#undef ALLOW_BUFFER_MAPPING + +#undef SMALL_DMABUF_SIZE +#define SMALL_DMABUF_SIZE (16*1024) + +extern int detect_trace; +#define DDB(x) if (detect_trace) x + +#define MAP_PCI_IOADDR(osdev, nr, io) io +#define MAP_PCI_MEM(osdev, ix, phaddr, size) devmem_mapin(osdev->key, ix, 0, size) +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) devmem_mapout(virt, size) +#define UNMAP_PCI_IOADDR(osdev, ix) {} +#define GET_PROCESS_PID(x) -1 +#define GET_PROCESS_NAME(x) NULL + +#define abs(x) ((x) >= 0 ? (x) : -(x)) + +#ifdef _KERNEL +extern void *oss_memset (void *s, int c, size_t n); +#define memset oss_memset + +extern void *oss_memcpy (void *s1, const void *s2, size_t n); +#define memcpy oss_memcpy +int oss_sprintf (char *buf, char *cfmt, ...); +#define sprintf oss_sprintf + +extern int oss_open (void *idata, channel_t * channelp, + int oflags, cred_t * crp, queue_t * q); +extern int oss_close (void *idata, channel_t channel, + int oflags, cred_t * crp, queue_t * q); +extern int oss_ioctl (void *idata, channel_t channel, int cmd, void *arg, + int oflags, cred_t * crp, int *rvalp); +extern int oss_devinfo (void *idata, channel_t channel, di_parm_t parm, + void *valp); +extern void oss_biostart (void *idata, channel_t channel, buf_t * bp); + +#endif diff --git a/kernel/OS/SunOS/.config b/kernel/OS/SunOS/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/SunOS/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/SunOS/module.inc b/kernel/OS/SunOS/module.inc new file mode 100644 index 0000000..a522492 --- /dev/null +++ b/kernel/OS/SunOS/module.inc @@ -0,0 +1,389 @@ +/* + * Purpose: OSS module wrapper for Solaris + * + * This file will be included from the auto-generated drv_cfg.c files. Under + * Solaris this file will be compiled in the development system. + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#include <sys/types.h> +#include <sys/modctl.h> +#include <sys/kmem.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> + +static int ossdrv_getinfo (dev_info_t *, ddi_info_cmd_t, void *, void **); +static int ossdrv_attach (dev_info_t *, ddi_attach_cmd_t); +static int ossdrv_detach (dev_info_t *, ddi_detach_cmd_t); + +#ifdef OSS_POWER_MANAGE +static int ossdrv_power (dev_info_t *, int component, int level); +#endif + +/* Entry points structure */ +#if DRIVER_TYPE!=DRV_STREAMS + +static struct cb_ops ossdrv_cb_ops = { + oss_open, + oss_close, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + oss_read, + oss_write, + oss_ioctl, +#ifdef ALLOW_BUFFER_MAPPING + oss_devmap, +#else + nodev, /* no devmap routine */ +#endif + nodev, + nodev, /* no segmap routine */ + oss_chpoll, /* no chpoll routine */ + ddi_prop_op, + 0, /* not a STREAMS driver */ + D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ + CB_REV +}; +#else +extern struct streamtab DRIVER_STR_INFO; + +struct cb_ops ossdrv_streams_cb_ops = { + nulldev, + nulldev, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + nodev, + nodev, + nodev, + nodev, /* no devmap routine */ + nodev, + nodev, /* no segmap routine */ + nochpoll, /* no chpoll routine */ + ddi_prop_op, + &DRIVER_STR_INFO, /* cb_str */ + D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ + CB_REV, + nodev, /* cb_aread */ + nodev, /* cb_awrite */ +}; +#endif + +static struct dev_ops ossdrv_dev_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + ossdrv_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify - obsolete */ +#if DRIVER_TYPE==DRV_ISA + ossdrv_probe, +#else + nulldev, /* devo_probe */ +#endif + ossdrv_attach, /* devo_attach */ + ossdrv_detach, /* devo_detach */ + nodev, /* devo_reset */ +#if DRIVER_TYPE==DRV_STREAMS + &ossdrv_streams_cb_ops, /* devi_cb_ops */ +#else + &ossdrv_cb_ops, /* devi_cb_ops */ +#endif + NULL, /* devo_bus_ops */ +#ifdef OSS_POWER_MANAGE + ossdrv_power +#else + NULL /* devo_power */ +#endif +}; + +static struct modldrv ossdrv_modldrv = { + &mod_driverops, /* drv_modops */ + "OSS " OSS_VERSION_STRING, + &ossdrv_dev_ops, /* drv_dev_ops */ +}; + +static struct modlinkage ossdrv_modlinkage = { + MODREV_1, /* ml_rev */ + (void *) &ossdrv_modldrv, /* ml_linkage */ + NULL /* NULL terminates the list */ +}; + +/* + * _init, _info, and _fini support loading and unloading the driver. + */ +int +_init (void) +{ + int error; + + error = mod_install (&ossdrv_modlinkage); + + return error; +} + +int +_fini (void) +{ + int error; + + error = mod_remove (&ossdrv_modlinkage); + return error; +} + +int +_info (struct modinfo *modinfop) +{ + int error; + + error = mod_info (&ossdrv_modlinkage, modinfop); + + return error; +} + +/*ARGSUSED*/ +static int +ossdrv_getinfo (dev_info_t * dontcare_dip, ddi_info_cmd_t cmd, void *arg, + void **result) +{ + dev_t dev; + register int error; + int instance; + dev_info_t *dip; +#ifndef SOL9 + oss_cdev_t *cdev; + int minor; + + dev = (dev_t) arg; + + minor = getminor (dev); + + if (minor >= oss_num_cdevs) + { + *result = NULL; + return DDI_FAILURE; + } + + if ((cdev = oss_cdevs[minor]) == NULL || cdev->osdev == NULL) + { + *result = NULL; + return DDI_FAILURE; + } + + dip = cdev->osdev->dip; +#else + dip = dontcare_dip; +#endif + + if (dip == NULL) + { + /* cmn_err (CE_WARN, "ossdrv_getinfo: dip==NULL\n"); */ + return DDI_FAILURE; + } + + instance = ddi_get_instance (dip); + + switch (cmd) + { + case DDI_INFO_DEVT2DEVINFO: + *result = dip; + error = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *) (long)instance; + error = DDI_SUCCESS; + break; + default: + *result = NULL; + error = DDI_FAILURE; + } + + DDB (cmn_err (CE_CONT, + "oss_getinfo: returns %d, result=%lx instance=%d dev=%x\n", + error, (unsigned long)*result, instance, (unsigned int)dev)); + return error; +} + +static int +ossdrv_attach (dev_info_t * dip, ddi_attach_cmd_t cmd) +{ + int instance; + oss_device_t *osdev; + +#ifdef OSS_SUSPEND_RESUME + if (cmd != DDI_RESUME) /* Allow resume */ +#endif + if (cmd != DDI_ATTACH) + { + cmn_err (CE_WARN, "bad attach cmd %d\n", cmd); + return 0; + } + + if (dip == NULL) + { + cmn_err (CE_WARN, "ossdrv_attach: dip==NULL\n"); + return DDI_FAILURE; + } + +#ifdef OSS_SUSPEND_RESUME + if (cmd == DDI_RESUME) + { + if ((osdev = (oss_device_t *) ddi_get_driver_private (dip)) == NULL) + { + cmn_err (CE_WARN, "ddi_get_driver_private() failed\n"); + return DDI_SUCCESS; + } + if (!DRIVER_RESUME(osdev)) + return DDI_FAILURE; + + return DDI_SUCCESS; + } +#endif + + instance = ddi_get_instance (dip); + DDB (cmn_err + (CE_CONT, "Attach started " DRIVER_NICK "%d (%s)\n", instance, + ddi_get_name (dip))); + + if ((osdev = + osdev_create (dip, DRIVER_TYPE, instance, DRIVER_NICK, NULL)) == NULL) + { + return DDI_FAILURE; + } + + oss_load_options (osdev, oss_global_options); + oss_load_options (osdev, local_driver_options); + +#if DRIVER_TYPE==DRV_PCI || DRIVER_TYPE == DRV_ISA + { + int err; + if ((err = + ddi_get_iblock_cookie (dip, 0, &osdev->iblock_cookie)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Cannot get iblock cookie (%d)\n", err); + return DDI_FAILURE; + } + } +#else +/* NOTE! The USB driver (actually udi.c) will call usb_get_data() to obtain */ +/* the iblock_cookie. There is no need to do that here. */ + osdev->iblock_cookie = 0; +#endif + + if (!DRIVER_ATTACH (osdev)) + { + cmn_err (CE_WARN, "Attach failed\n"); + osdev_delete (osdev); + return DDI_FAILURE; + } + + ddi_set_driver_private (dip, (caddr_t) osdev); + ddi_report_dev (dip); +#if DRIVER_TYPE != DRV_USB + oss_audio_delayed_attach (); +#endif + + DDB (cmn_err (CE_CONT, "Attach done " DRIVER_NICK "%d\n", instance)); + + return DDI_SUCCESS; +} + +static int +ossdrv_detach (dev_info_t * dip, ddi_detach_cmd_t cmd) +{ + oss_device_t *osdev; + +#ifdef OSS_SUSPEND_RESUME + if (cmd != DDI_SUSPEND) /* Allow suspend */ +#endif + if (cmd != DDI_DETACH) + { + cmn_err (CE_WARN, "bad detach cmd %d\n", cmd); + return 0; + } + + if (dip == NULL) + { + cmn_err (CE_WARN, "ossdrv_detach: dip==NULL\n"); + return DDI_FAILURE; + } +//cmn_err(CE_CONT, "Detach started " DRIVER_NICK "\n"); + + DDB (cmn_err + (CE_CONT, "Detach started " DRIVER_NICK "(%s)\n", ddi_get_name (dip))); + if ((osdev = (oss_device_t *) ddi_get_driver_private (dip)) == NULL) + { + cmn_err (CE_WARN, "ddi_get_driver_private() failed\n"); + return DDI_SUCCESS; + } + + if (osdev==NULL) + { + cmn_err (CE_WARN, "Driver osdev==NULL - cannot detach\n"); + return DDI_FAILURE; + } + +#ifdef OSS_SUSPEND_RESUME + if (cmd == DDI_SUSPEND) + { + if (!DRIVER_SUSPEND(osdev)) + return DDI_FAILURE; + return DDI_SUCCESS; + } +#endif + + if (DRIVER_DETACH (osdev) <= 0) + { + cmn_err (CE_WARN, "Driver busy - cannot detach\n"); + return DDI_FAILURE; + } + +#if DRIVER_TYPE!=DRV_VMIX + osdev_delete (osdev); +#endif + + DDB (cmn_err (CE_CONT, "Detach done " DRIVER_NICK "\n")); + return DDI_SUCCESS; +} + +#ifdef OSS_POWER_MANAGE +static int +ossdrv_power (dev_info_t *dip, int component, int level) +{ + oss_device_t *osdev; + + if (dip == NULL) + { + cmn_err (CE_WARN, "ossdrv_detach: dip==NULL\n"); + return DDI_FAILURE; + } + + DDB (cmn_err + (CE_CONT, "ossdrv_power " DRIVER_NICK "(%s, %d, %d)\n", ddi_get_name (dip), component, level)); + if ((osdev = (oss_device_t *) ddi_get_driver_private (dip)) == NULL) + { + cmn_err (CE_WARN, "ddi_get_driver_private() failed\n"); + return DDI_SUCCESS; + } + + if (osdev==NULL) + { + cmn_err (CE_WARN, "Driver osdev==NULL - cannot detach\n"); + return DDI_FAILURE; + } + + if (!DRIVER_POWER(osdev, component, level)) + return DDI_FAILURE; + + return DDI_SUCCESS; +} +#endif diff --git a/kernel/OS/SunOS/os_solaris.c b/kernel/OS/SunOS/os_solaris.c new file mode 100644 index 0000000..b4ceac3 --- /dev/null +++ b/kernel/OS/SunOS/os_solaris.c @@ -0,0 +1,2401 @@ +/* + * Purpose: Operating system abstraction functions for Solaris + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#include "oss_config.h" +#include "midi_core.h" +#include <oss_pci.h> +#if !defined(SOL9) && !defined(SOL8) +#include <sys/sunldi.h> +#endif +#include <sys/mman.h> + +#if 1 +/* + * Some older DDI routines are used by OSS instead of the latest ones. + * In this way the same OSS binary can be run both under Sol10 and Sol11. + */ +uint16_t ddi_mem_get16 (ddi_acc_handle_t handle, uint16_t * host_addr); +uint32_t ddi_mem_get32 (ddi_acc_handle_t handle, uint32_t * host_addr); +void ddi_mem_put16 (ddi_acc_handle_t handle, uint16_t * dev_addr, + uint16_t value); +void ddi_mem_put32 (ddi_acc_handle_t handle, uint32_t * dev_addr, + uint32_t value); +int ddi_mem_alloc (dev_info_t * dip, ddi_dma_lim_t * limits, uint_t length, + uint_t flags, caddr_t * kaddrp, uint_t * real_length); +void ddi_mem_free (caddr_t kaddr); +#endif + +#if 0 +/* TODO: Disable this debugging stuff */ +unsigned char tmp_status = 0; +# define UP_STATUS(v) OUTB(NULL, (tmp_status=tmp_status|(v)), 0x378) +# define DOWN_STATUS(v) OUTB(NULL, (tmp_status=tmp_status&~(v)), 0x378) +#else +# define UP_STATUS(v) +# define DOWN_STATUS(v) +#endif + +static int oss_expired = 0; + +static oss_mutex_t osscore_mutex; + +#define TRC(x) +#define TRC2(x) + +#define FIX_RET_VALUE(ret) (((ret)<0) ? -(ret) : 0) + +static volatile int open_devices = 0; +static volatile int do_forceload = 0; +static volatile int forceload_in_progress = 0; + +/* + * MAX_CARDS must be larger than life. The system will panic if there are + * more sound devices (cards os sound chips) than MAX_CARDS. + */ +#define MAX_CARDS 32 + +static oss_device_t *cards[MAX_CARDS]; +int oss_num_cards = 0; + +int oss_detach_enabled = 0; + +#ifdef MUTEX_CHECKS +static volatile int inside_intr = 0; /* For mutex debugging only */ +#endif + +/* + * These are the entry points into our driver that are called when the + * driver is loaded, during a system call, or in response to an interrupt. + */ +static int oss_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void *arg, + void **result); +static int oss_attach (dev_info_t * dip, ddi_attach_cmd_t cmd); +static int oss_detach (dev_info_t * dip, ddi_detach_cmd_t cmd); +static void free_all_contig_memory (void); + +/* + * DMA memory management + */ + +typedef struct contig_desc +{ + int is_special; + struct contig_desc *next; + unsigned char *first_addr, *last_addr; + unsigned long physaddr; + void *orig_buf; + + oss_device_t *osdev; + ddi_dma_handle_t handle; + ddi_dma_handle_t dhandle; + ddi_acc_handle_t dma_acc_handle; + ddi_dma_cookie_t cookie; + ddi_dma_win_t win; + ddi_dma_seg_t seg; + size_t size; +} +contig_desc; + +static contig_desc *contig_list = NULL; + +#ifdef MEMDEBUG +typedef struct +{ + void *addr; + int size; + char file[40]; + int line; +} mem_block_t; + +#define MAX_MEMBLOCKS 1024 + +static mem_block_t memblocks[MAX_MEMBLOCKS]; +static int num_memblocks = 0; +#endif + +#define CDEV_NUMHASH 79 // Prime number +static oss_cdev_t *cdev_hash[CDEV_NUMHASH] = {NULL}; +#define compute_cdev_hash(dev_class, instance) (dev_class*13+instance) % CDEV_NUMHASH + +static void +unlink_cdev_hash(oss_cdev_t *this_cdev) +{ + oss_cdev_t *cdev = cdev_hash[compute_cdev_hash(this_cdev->dev_class, this_cdev->instance)]; + + if (cdev == this_cdev) // First in the hash chain + { + cdev_hash[compute_cdev_hash(this_cdev->dev_class, this_cdev->instance)] = cdev->hl; + return; + } + + while (cdev != NULL) + { + if (cdev->hl == this_cdev) + { + cdev->hl = this_cdev->hl; + return; + } + + cdev = cdev->hl; + } + + cmn_err(CE_WARN, "unlink_cdev_hash: Cannot find cdev %p\n", this_cdev); +} + +#if 1 +/* + * Unfortunately this handy function is not safe (incompatibilities + * between kenel versions/builds). So let's hope it gets moved to + * the kernel. + */ +char * +oss_get_procname (void) /* Return the command name of the current thread */ +{ + return ttoproc (curthread)->p_user.u_comm; +} +#endif + +#ifdef MEMDEBUG +void * +oss_kmem_alloc (size_t size, int flags, char *file, int line) +#else +void * +oss_kmem_alloc (size_t size, int flags) +#endif +{ +/* + * This routine allocates a memory block and stores length of it in + * the beginning. This length information can be used when later unallocating + * the memory. + */ + + char *ptr; + uint64_t *len; + + ptr = kmem_zalloc (size + sizeof (uint64_t), flags); + + if (ptr == NULL) + return NULL; + + len = (uint64_t *) ptr; + + ptr += sizeof (uint64_t); + *len = size + sizeof (uint64_t); + +#ifdef MEMDEBUG +#if 1 + { + int i; + for (i = 0; i < num_memblocks; i++) + if (memblocks[i].addr == NULL) + { + memblocks[i].addr = ptr; + memblocks[i].size = size; + strncpy (memblocks[i].file, file, 39); + memblocks[i].line = line; + return ptr; + } + } +#endif + + if (num_memblocks < MAX_MEMBLOCKS) + { + memblocks[num_memblocks].addr = ptr; + memblocks[num_memblocks].size = size; + strncpy (memblocks[num_memblocks].file, file, 39); + memblocks[num_memblocks].line = line; + num_memblocks++; + } +#endif + + return ptr; +} + +void +oss_kmem_free (void *addr) +{ + uint64_t *len; + + char *ptr = addr; + + if (addr == NULL) + return; + + ptr -= sizeof (uint64_t); + + len = (uint64_t *) ptr; + + kmem_free (ptr, *len); +#ifdef MEMDEBUG + { + int i; + + for (i = 0; i < num_memblocks; i++) + if (addr == memblocks[i].addr) + { + memblocks[i].addr = NULL; + break; + } + } +#endif +} + +static const ddi_dma_attr_t dma_attr_pci = { + DMA_ATTR_V0, // Version + 0x00000000ULL, // Address low + 0xfffffff0ULL, // Address high + 0xffffffffULL, // Counter max + 1ULL, // Default byte align + 0x7f, // Burst size + 0x1, // Minimum xfer size + 0xffffffffULL, // Maximum xfer size + 0xffffffffULL, // Max segment size + 1, // S/G list length + 1, // Granularity + 0 // Flag +}; + +#if !defined(SOL9) && !defined(SOL8) +static void +forceload_drivers (dev_t dev, cred_t * credp) +{ +/* + * This routine will be called whenever the first application tries + * to access OSS devices. It's purpose is to load all the drivers + * just in case they have not been loaded yet. In this way it's possible to + * guarantee that the audio device numbering doesn't change between + * reboots. + */ + char path[20]; + + int i, err; + ldi_handle_t lh; + ldi_ident_t li; + DDB (cmn_err (CE_NOTE, "Forceloading OSS devices\n")); + + if (ldi_ident_from_dev (dev, &li) != 0) + { + cmn_err (CE_NOTE, "ldi_ident_from_dev failed\n"); + return; + } + + if ((err = ldi_open_by_name ("/dev/sndstat", FWRITE, credp, &lh, li)) != 0) + { + if (err != ENODEV) + cmn_err (CE_NOTE, "Forceload error %d (/dev/sndstat)\n", err); + } + else + err = ldi_close (lh, FWRITE, credp); + + for (i = 0; i < MAX_MIXER_DEV; i++) + { + sprintf (path, "/dev/mixer%d", i); + + if ((err = ldi_open_by_name (path, FWRITE, credp, &lh, li)) != 0) + { + if (err != ENODEV) + cmn_err (CE_NOTE, "Forceload error %d\n", err); + } + else + err = ldi_close (lh, FWRITE, credp); + } + + for (i = 0; i < MAX_AUDIO_DEVFILES; i++) + { + sprintf (path, "/dev/dsp%d", i); + + if ((err = ldi_open_by_name (path, FWRITE, credp, &lh, li)) != 0) + { + if (err != ENODEV) + cmn_err (CE_NOTE, "Forceload error %d\n", err); + } + else + err = ldi_close (lh, FWRITE, credp); + } + + ldi_ident_release (li); + DDB (cmn_err (CE_NOTE, "Forceloading OSS devices done\n")); +} + +void +oss_forceload_drivers (int dev, cred_t * cred_p) +{ + + if (do_forceload) + { + do_forceload = 0; + forceload_in_progress = 1; + forceload_drivers (dev, cred_p); + forceload_in_progress = 0; + } +} +#endif + +/*ARGSUSED*/ +int +oss_open (dev_t * dev_p, int open_flags, int otyp, cred_t * cred_p) +{ + int retval; + dev_t dev = *dev_p; + oss_cdev_t *cdev; + int tmpdev, maj; + oss_native_word flags; + +#ifdef DO_TIMINGS + oss_timing_printf ("**** oss_open(%x) ****", getminor (dev)); +#endif +//cmn_err(CE_CONT, "**** oss_open(%x) ****\n", getminor (dev)); +//cmn_err(CE_CONT, " PID %d, cmd %s\n", GET_PROCESS_PID(x), GET_PROCESS_NAME(x)); + maj = getmajor (dev); + dev = getminor (dev); + +#if !defined(SOL9) && !defined(SOL8) +/* + * Handle driver forceload + */ + + if (forceload_in_progress) + return ENODEV; + + oss_forceload_drivers (dev, cred_p); +#endif + + if (dev >= OSS_MAX_CDEVS) + return ENXIO; + + if (dev >= oss_num_cdevs) + { + return ENODEV; + } + + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + return ENODEV; + + if (cdev->d->open == NULL) + { + return ENODEV; + } + + memset (&cdev->file, 0, sizeof (cdev->file)); + cdev->file.mode = 0; + cdev->file.acc_flags = open_flags; + + if (open_flags & FREAD && open_flags & FWRITE) + cdev->file.mode = OPEN_READWRITE; + else if (open_flags & FREAD) + cdev->file.mode = OPEN_READ; + else if (open_flags & FWRITE) + cdev->file.mode = OPEN_WRITE; + + tmpdev = dev; + retval = + cdev->d->open (cdev->instance, cdev->dev_class, &cdev->file, 0, 0, &tmpdev); + *dev_p = makedevice (maj, tmpdev); + dev = tmpdev; + + if (retval < 0) + { + return -retval; + } + + MUTEX_ENTER_IRQDISABLE (osscore_mutex, flags); + open_devices++; + cdev = oss_cdevs[dev]; /* Switch to the cdev that was actually opened */ + + cdev->open_count++; + if (open_flags & FREAD && open_flags & FWRITE) + cdev->file.mode = OPEN_READWRITE; + else if (open_flags & FREAD) + cdev->file.mode = OPEN_READ; + else if (open_flags & FWRITE) + cdev->file.mode = OPEN_WRITE; + + oss_reserve_device (cdev->osdev); +//cmn_err(CE_CONT, "Increment open_devices=%d, refcount=%d\n", open_devices, cdev->osdev->refcount); + MUTEX_EXIT_IRQRESTORE (osscore_mutex, flags); + + return 0; +} + +/*ARGSUSED*/ +int +oss_close (dev_t dev, int flag, int otyp, cred_t * cred_p) +{ + oss_cdev_t *cdev; + oss_native_word flags; +#ifdef DO_TIMINGS + oss_timing_printf ("***** oss_close(%x) ****", getminor (dev)); +#endif +//cmn_err(CE_CONT, "***** oss_close(%x) ****\n", getminor (dev)); +//cmn_err(CE_CONT, " PID %d, cmd %s\n", GET_PROCESS_PID(x), GET_PROCESS_NAME(x)); + + if (getminor (dev) >= OSS_MAX_CDEVS) + { + cmn_err (CE_NOTE, "Closing bad minor %d\n", getminor (dev)); + return ENXIO; + } + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + { + cmn_err (CE_NOTE, "Closing undefined minor %d\n", getminor (dev)); + return ENXIO; + } + + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Closing minor %d that is not open\n", dev); + return 0; + } + + cdev->d->close (cdev->instance, &cdev->file); + + MUTEX_ENTER_IRQDISABLE (osscore_mutex, flags); + open_devices -= cdev->open_count; +//cmn_err(CE_CONT, "Decrement open_devices=%d\n", open_devices); + oss_unreserve_device (cdev->osdev, cdev->open_count); + cdev->open_count = 0; + MUTEX_EXIT_IRQRESTORE (osscore_mutex, flags); + return 0; +} + +/*ARGSUSED*/ +int +oss_ioctl (dev_t dev, int cmd, intptr_t arg, int mode, cred_t * cred_p, + int *rval_p) +{ + int retval; + int len = 0; + char b[4096], *buf = b; + oss_cdev_t *cdev; +#ifdef DO_TIMINGS + oss_timing_printf ("OSS ioctl(%x, %x, %x)", getminor (dev), cmd, arg); +#endif + + *rval_p = 0; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to ioctl on minor %d that is not open\n", dev); + return ENXIO; + } + + if (oss_expired) + return ENODEV; + + if (mode & FKIOCTL) /* Called from kernel space */ + buf = (char *) arg; + else if (cmd & (SIOC_OUT | SIOC_IN)) + { + + len = (cmd >> 16) & SIOCPARM_MASK; + if (len < 0) + len = 0; + if (len > sizeof (b)) + { + cmn_err (CE_WARN, "Bad ioctl buffer size %d\n", len); + return EFAULT; + } + + if ((cmd & SIOC_IN) && len > 0) + { + if (copyin ((char *) arg, buf, len) == -1) + return EFAULT; + } + + } + + retval = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) buf); + + if (!(mode & FKIOCTL)) /* Not called from kernel space */ + if ((cmd & SIOC_OUT) && len > 0) + { + if (copyout (buf, (char *) arg, len) == -1) + return EFAULT; + } + + return FIX_RET_VALUE (retval); +} + +/*ARGSUSED*/ +int +oss_read (dev_t dev, struct uio *uiop, cred_t * credp) +{ + int count = uiop->uio_resid; + int retval; + oss_cdev_t *cdev; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->read == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to read on minor %d that is not open\n", dev); + return ENXIO; + } + + cdev->file.acc_flags = uiop->uio_fmode; + + retval = cdev->d->read (cdev->instance, &cdev->file, uiop, count); + + return FIX_RET_VALUE (retval); +} + +/*ARGSUSED*/ +int +oss_write (dev_t dev, struct uio *uiop, cred_t * cred_p) +{ + int count = uiop->uio_resid; + int retval; + oss_cdev_t *cdev; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->write == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to write on minor %d that is not open\n", dev); + return ENXIO; + } + cdev->file.acc_flags = uiop->uio_fmode; + + retval = cdev->d->write (cdev->instance, &cdev->file, uiop, count); + return FIX_RET_VALUE (retval); +} + +int +oss_chpoll (dev_t dev, short events, int anyyet, short *reventsp, + struct pollhead **phpp) +{ + oss_cdev_t *cdev; + oss_poll_event_t ev; + int ret; + +#ifdef DO_TIMINGS + oss_timing_printf ("***** oss_chpoll(%x) ****", getminor (dev)); +#endif + + if (getminor (dev) >= OSS_MAX_CDEVS) + return ENXIO; + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->chpoll == NULL) + return ENXIO; + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to chpoll on minor %d that is not open\n", dev); + return ENXIO; + } + + *reventsp = 0; + ev.events = events; + ev.anyyet = anyyet; + ev.revents = 0; + ev.php = NULL; + + ret = cdev->d->chpoll (cdev->instance, &cdev->file, &ev); + if (ret < 0) + { + return -ret; + } + + *reventsp |= ev.revents; + if (ev.php != NULL) + *phpp = ev.php; + + return 0; +} + +#ifdef ALLOW_BUFFER_MAPPING +int +oss_devmap (dev_t dev, devmap_cookie_t handle, offset_t off, size_t len, + size_t * maplen, uint_t model) +{ + oss_cdev_t *cdev; + oss_poll_event_t ev; + int ret; + +#ifdef DO_TIMINGS + oss_timing_printf ("***** oss_devmap(%x) ****", getminor (dev)); +#endif + + if (getminor (dev) >= OSS_MAX_CDEVS) + { + return ENXIO; + } + + dev = getminor (dev); + if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) + { + return ENXIO; + } + + if (cdev->open_count == 0) /* Not opened */ + { + cmn_err (CE_NOTE, "Call to devmap on minor %d that is not open\n", dev); + return EPERM; + } + + if (cdev->dev_class != OSS_DEV_DSP && cdev->dev_class != OSS_DEV_DSP_ENGINE) /* Only audio devices can be mapped */ + { + return ENXIO; + } + + dev = cdev->instance; + if (dev < 0 || dev >= num_audio_engines) + return ENXIO; + + return ENOTSUP; +} +#endif + +#if 0 +// Not used in misc modules. +static struct cb_ops oss_cb_ops = { + oss_open, + oss_close, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + oss_read, + oss_write, + oss_ioctl, +#ifdef ALLOW_BUFFER_MAPPING + oss_devmap, +#else + nodev, /* no devmap routine */ +#endif + nodev, + nodev, /* no segmap routine */ + oss_chpoll, /* no chpoll routine */ + ddi_prop_op, + 0, /* not a STREAMS driver */ + D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ + CB_REV +}; + +static struct dev_ops oss_ops = { + DEVO_REV, /* DEVO_REV indicated by manual */ + 0, /* device reference count */ + oss_getinfo, + nulldev, + nulldev, + oss_attach, + oss_detach, + nodev, /* device reset routine */ + &oss_cb_ops, + NULL, /* bus operations */ + NULL /* TODO: Power management */ +}; +#endif + +extern struct mod_ops mod_miscops; +static struct modldrv modldrv = { + &mod_miscops, + "Open Sound System " OSS_VERSION_STRING " framework" + // &oss_ops, +}; + +static struct modlinkage modlinkage = { + MODREV_1, /* MODREV_1 indicated by manual */ + {(void *) &modldrv, + NULL} /* termination of list of linkage structures */ +}; + +static ddi_device_acc_attr_t acc_attr_neverswap = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC +}; + +#ifdef OSS_BIG_ENDIAN +static ddi_device_acc_attr_t acc_attr_le_swap = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; +#endif + + +/*ARGSUSED*/ +static ddi_device_acc_attr_t * +get_acc_attr (oss_device_t * osdev) +{ +#ifdef OSS_BIG_ENDIAN + if (osdev->swap_mode == 1) + { + return &acc_attr_le_swap; + } + else +#endif + { + return &acc_attr_neverswap; + } +} + +void +oss_load_options (oss_device_t * osdev, oss_option_map_t map[]) +{ + int i, val; + + for (i = 0; map[i].name != NULL; i++) + { + if ((val = + ddi_prop_get_int (DDI_DEV_T_ANY, osdev->dip, + DDI_PROP_NOTPROM, + map[i].name, -12345)) != -12345) + { + *map[i].ptr = val; + } + } +} + +static int +oss_attach (dev_info_t * dip, ddi_attach_cmd_t cmd) +{ + oss_device_t *osdev; + + if (cmd != DDI_ATTACH) + { + cmn_err (CE_WARN, "oss_attach: Command %x\n", cmd); + return (DDI_FAILURE); + } + + if ((osdev = osdev_create (dip, DRV_VIRTUAL, 0, "osscore", NULL)) == NULL) + { + cmn_err (CE_WARN, "Creating osdev failed\n"); + return DDI_FAILURE; + } + + MUTEX_INIT (osdev, osscore_mutex, 0); + + oss_load_options (osdev, oss_global_options); + + oss_common_init (osdev); + + ddi_report_dev (dip); + oss_register_device (osdev, "OSS core services"); + + return (DDI_SUCCESS); +} + +/* + * This is a pretty generic getinfo routine as describe in the manual. + */ + +/*ARGSUSED*/ +static int +oss_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void *arg, + void **result) +{ + dev_t dev; + register int error; + int instance; + + dev = (dev_t) arg; + instance = getminor (dev) >> 4; + + switch (infocmd) + { + case DDI_INFO_DEVT2DEVINFO: + *result = dip; + error = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *) (uintptr_t) instance; + error = DDI_SUCCESS; + break; + default: + *result = NULL; + error = DDI_FAILURE; + } + +#if 0 + DDB (cmn_err (CE_CONT, + "oss_getinfo: returns %d, result=%x minor=%d instance=%d dev=%x\n", + error, *result, minor_num, instance, dev)); +#endif + return (error); +} + +/* + * _init, _info, and _fini support loading and unloading the driver. + */ +int +_init (void) +{ + register int error = 0; + error = mod_install (&modlinkage); + return error; +} + +int +_info (struct modinfo *modinfop) +{ + return (mod_info (&modlinkage, modinfop)); +} + +int +_fini (void) +{ + int status; + +#ifdef MEMDEBUG + int i; +#endif + + if ((status = mod_remove (&modlinkage)) != 0) + return (status); + + free_all_contig_memory (); + +#ifdef MEMDEBUG + if (num_memblocks >= MAX_MEMBLOCKS) + cmn_err (CE_NOTE, "All memory allocations were not checked\n"); + + for (i = 0; i < num_memblocks; i++) + { + if (memblocks[i].addr != NULL) + { + cmn_err (CE_NOTE, "Memory leak in %s:%d\n", + memblocks[i].file, memblocks[i].line); + } + } +#endif + + return status; +} + +/* + * When the osscore module is unloaded, oss_detach cleans up and frees the + * resources we allocated in oss_attach. + */ +static int +oss_detach (dev_info_t * dip, ddi_detach_cmd_t cmd) +{ + static int already_unloaded = 0; + + if (cmd != DDI_DETACH) + return DDI_FAILURE; + + if (already_unloaded) + return DDI_SUCCESS; + already_unloaded = 1; +//cmn_err(CE_CONT, "Oss detach\n"); + + /* instance = ddi_get_instance (dip); */ + + /* + * Remove the minor nodes created in attach + */ + ddi_remove_minor_node (dip, NULL); + + oss_unload_drivers (); + + MUTEX_CLEANUP (osscore_mutex); + + return (DDI_SUCCESS); +} + +/* + * Some support routines + */ +void * +memset (void *t, int c, size_t l) +{ + int i; + + for (i = 0; i < l; i++) + ((char *) t)[i] = c; + + return t; +} + +#ifdef sparc +/* + * I/O functions that do byte swapping (for Sparc) + */ +void +oss_put16 (ddi_acc_handle_t handle, unsigned short *addr, unsigned short val) +{ + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); + ddi_put16 (handle, addr, val); +} + +void +oss_put32 (ddi_acc_handle_t handle, unsigned int *addr, unsigned int val) +{ +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + ddi_put32 (handle, addr, val); +} + +unsigned short +oss_get16 (ddi_acc_handle_t handle, unsigned short *addr) +{ + unsigned short val; + + val = ddi_get16 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); +#endif + return val; +} + +unsigned int +oss_get32 (ddi_acc_handle_t handle, unsigned int *addr) +{ + unsigned int val; + + val = ddi_get32 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + return val; +} + +uint16_t +oss_mem_get16 (ddi_acc_handle_t handle, uint16_t * addr) +{ + uint16_t val; + + val = ddi_mem_get16 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); +#endif + return val; +} + +uint32_t +oss_mem_get32 (ddi_acc_handle_t handle, uint32_t * addr) +{ + + uint32_t val; + + val = ddi_mem_get32 (handle, addr); +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + return val; +} + +void +oss_mem_put16 (ddi_acc_handle_t handle, uint16_t * addr, uint16_t val) +{ +#ifdef OSS_BIG_ENDIAN + val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); +#endif + + ddi_mem_put16 (handle, addr, val); +} + +void +oss_mem_put32 (ddi_acc_handle_t handle, uint32_t * addr, uint32_t val) +{ +#ifdef OSS_BIG_ENDIAN + val = ((val & 0x000000ff) << 24) | + ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); +#endif + ddi_mem_put32 (handle, addr, val); +} +#endif + +void +oss_pci_byteswap (oss_device_t * osdev, int mode) +{ + osdev->swap_mode = mode; +} + +void +oss_pcie_init (oss_device_t * osdev, int flags) +{ + /* TODO: Should we do something? */ +} + +/*ARGSUSED*/ +caddr_t +oss_map_pci_ioaddr (oss_device_t * osdev, int nr, int io) +{ + caddr_t addr; + off_t region_size; + int err; + ddi_device_acc_attr_t *acc_attr; + + if (nr >= OSS_MAX_ACC_HANDLE) + { + cmn_err(CE_WARN, "Too large I/O region number %d\n", nr); + + return 0; + } + + acc_attr = get_acc_attr (osdev); + + if ((err = + ddi_dev_regsize (osdev->dip, nr + 1, ®ion_size)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Getting device regsize failed (%d)\n", err); + return 0; + } + + if ((err = ddi_regs_map_setup + (osdev->dip, nr + 1, &addr, 0, region_size, acc_attr, + &osdev->acc_handle[nr])) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Register setup failed (%d)\n", err); + return 0; + } + + return addr; +} + +void +oss_unmap_pci_ioaddr(oss_device_t * osdev, int nr) +{ + if (nr >= OSS_MAX_ACC_HANDLE) + { + cmn_err(CE_WARN, "Too large I/O region number %d\n", nr); + return; + } + + ddi_regs_map_free(&osdev->acc_handle[nr]); +} + +int +pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; +#if defined (sparc) + if (where == PCI_INTERRUPT_LINE) + *val = 7; /* PC emulation hack */ + else +#endif + *val = pci_config_get8 (osdev->pci_config_handle, where); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) +{ + int ret; + + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + ret = pci_read_config_byte (osdev, where, val); + return ret; +} + + +int +pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + *val = pci_config_get16 (osdev->pci_config_handle, where); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + *val = pci_config_get32 (osdev->pci_config_handle, where); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + pci_config_put8 (osdev->pci_config_handle, where, val); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + pci_config_put16 (osdev->pci_config_handle, where, val); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + if (osdev->dev_type != DRV_PCI) + return PCIBIOS_FAILED; + pci_config_put32 (osdev->pci_config_handle, where, val); + return PCIBIOS_SUCCESSFUL; +} + +void * +memcpy (void *t, const void *s, size_t l) +{ + bcopy (s, t, l); + return t; +} + +#ifdef MEMDEBUG +void * +oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, + oss_native_word * phaddr, oss_dma_handle_t *dma_handle, char *file, int line) +#else +void * +oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, + oss_native_word * phaddr, oss_dma_handle_t *dma_handle) +#endif +{ +/* + * Allocate physically contiguous memory (suitable for DMA). + * + * The memlimit parameter is equal to oss_alloc_dmabuf(). + */ + int err; +#if defined(sparc) + uint_t len; +#else + size_t len; +#endif + uint_t count; + contig_desc *desc; + ddi_dma_attr_t dma_attr; + ddi_device_acc_attr_t *acc_attr; + + int flags = + DDI_DMA_REDZONE | DDI_DMA_CONSISTENT | DDI_DMA_READ | DDI_DMA_WRITE; + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_contig_malloc: osdev==NULL\n"); + return NULL; + } + + acc_attr = get_acc_attr (osdev); + + memcpy (&dma_attr, &dma_attr_pci, sizeof (ddi_dma_attr_t)); + dma_attr.dma_attr_addr_hi = memlimit; + + desc = KERNEL_MALLOC (sizeof (contig_desc)); + if (desc == NULL) + { + cmn_err (CE_WARN, "Failed to allocate contig buffer descriptor\n"); + return NULL; + } + + desc->osdev = osdev; + desc->next = NULL; + desc->is_special = 0; + + if ((err = ddi_dma_alloc_handle (osdev->dip, + &dma_attr, + DDI_DMA_SLEEP, + NULL, &desc->dhandle)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Failed to allocate pci DMA handle (%d)\n", err); + return NULL; + } + + if (dma_handle != NULL) + *dma_handle = desc->dhandle; + +#ifndef IOMEM_DATA_UNCACHED +#define IOMEM_DATA_UNCACHED 0 // Fix for Solaris 10 +#endif + +#if defined(sparc) + if ((err = ddi_mem_alloc (osdev->dip, + NULL, + size + 4096, + 0, + (caddr_t *) & desc->first_addr, + (uint_t *) & len)) != DDI_SUCCESS) + + +#else + if ((err = ddi_dma_mem_alloc (desc->dhandle, + size + 4096, + acc_attr, + flags, + DDI_DMA_SLEEP, + NULL, + (caddr_t *) & desc->first_addr, + (size_t *) & len, + &desc->dma_acc_handle)) != DDI_SUCCESS) +#endif + { + cmn_err (CE_WARN, "Failed to allocate %d bytes of contig memory (%d)\n", + size, err); + return NULL; + + } + + desc->size = len; + desc->orig_buf = desc->first_addr; + + if (desc->first_addr == NULL) + { + cmn_err (CE_WARN, "Can't allocate a contig buffer\n"); + return NULL; + } + + DDB (cmn_err + (CE_CONT, "Allocated contig memory, addr=%x, len=%d\n", + (unsigned int) desc->first_addr, desc->size)); + + if ((err = ddi_dma_addr_bind_handle (desc->dhandle, + NULL, + (char *) desc->first_addr, + desc->size, + flags | DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, + NULL, + &desc->cookie, &count)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Contig address setup failed (%d)\n", err); + return NULL; + } + + desc->physaddr = desc->cookie.dmac_address; + + desc->last_addr = desc->first_addr + desc->size - 1; + desc->first_addr = + (void *) (((unsigned long) desc->first_addr + 4095) & ~4095); + desc->physaddr = (desc->physaddr + 4095) & ~4095; + *phaddr = desc->physaddr; + + desc->next = contig_list; + contig_list = desc; +/* HW_PRINTF(("Alloc contig: %x-%x, ph=%x\n", desc->first_addr, desc->last_addr, desc->physaddr)); */ + + return desc->first_addr; +} + +/*ARGSUSED*/ +void +oss_contig_free (oss_device_t * osdev, void *p, int sz) +{ + int err; + contig_desc *d, *desc = NULL, *prev = NULL; + + if (p == NULL) + return; + + d = contig_list; + + while (d && desc == NULL) + { + if (d->is_special) + { + prev = d; + d = d->next; + continue; + } + + if (d->first_addr == p) + { + desc = d; + break; + } + + prev = d; + d = d->next; + } + + if (desc == NULL) + { + cmn_err (CE_WARN, "OSS: Can't free memory\n"); + return; + } + + if ((err = ddi_dma_unbind_handle (desc->dhandle)) != DDI_SUCCESS) + cmn_err (CE_WARN, "Failed to free DMA handle (%d)\n", err); + +#if defined(sparc) + ddi_mem_free (desc->orig_buf); +#else + if (desc->dma_acc_handle == NULL) + cmn_err (CE_WARN, "desc->dma_acc_handle==NULL\n"); + else + ddi_dma_mem_free (&desc->dma_acc_handle); +#endif + ddi_dma_free_handle (&desc->dhandle); + + + if (desc == contig_list) + contig_list = desc->next; + else + { + prev->next = desc->next; + } + KERNEL_FREE (desc); + +} + +static void +free_all_contig_memory (void) +{ + contig_desc *p, *desc = contig_list; + + while (desc != NULL) + { + p = desc; + desc = desc->next; + + if (p->is_special) + continue; + + oss_contig_free (p->osdev, p->orig_buf, 0); + } + + contig_list = NULL; +} + +#if !defined(SOL9) && !defined(DISABLE_FMA) +/*ARGSUSED*/ +static int +oss_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *osdev_) +{ + pci_ereport_post(dip, err, NULL); + return err->fme_status; +} +#endif + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, + const char *handle) +{ + oss_device_t *osdev = NULL; + int i; + ddi_iblock_cookie_t iblk; + static int license_checked=0; + +#ifdef LICENSED_VERSION + if (!license_checked) + { + timestruc_t ts; + + license_checked = 1; + gethrestime (&ts); + if (!oss_license_handle_time (ts.tv_sec)) + { + cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); + cmn_err (CE_CONT, + "Please download the latest version from www.opensound.com\n"); + oss_expired = 1; + } + } +#endif + + if (handle == NULL) + handle = nick; + + /* + * Don't accept any more drivers if expired + */ + if (oss_expired && oss_num_cards > 0) + return NULL; + + /* + * Check if the same device is being reinserted. + */ + for (i = 0; i < oss_num_cards; i++) + { + if (cards[i]->available) /* Not deleted */ + continue; + + if (cards[i]->dip == dip) + { + osdev = cards[i]; + break; + } + } + +#if 1 + if (osdev == NULL) + { + /* + * Check if there are some deleted devices. + */ + for (i = 0; i < oss_num_cards; i++) + { + if (cards[i]->available) /* Not deleted */ + continue; + + osdev = cards[i]; + break; + } + } +#endif + + if (osdev == NULL) + { + for (i=0;i<oss_num_cards;i++) + if (!cards[i]->available) + { + osdev = cards[i]; + break; + } + } + + if (osdev == NULL) + { + if (oss_num_cards >= MAX_CARDS) + { + cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + return NULL; + } + + if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memset (osdev, 0, sizeof (*osdev)); + + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + } + + osdev->dip = dip; + osdev->osid = dip; + osdev->available = 1; + osdev->instance = instance; + osdev->dev_type = dev_type; + osdev->devc = NULL; + sprintf (osdev->nick, "%s%d", nick, instance); + strcpy (osdev->modname, nick); + osdev->num_audio_engines = 0; + osdev->num_audioplay = 0; + osdev->num_audiorec = 0; + osdev->num_audioduplex = 0; + osdev->num_mididevs = 0; + osdev->num_mixerdevs = 0; + osdev->first_mixer = -1; + + switch (dev_type) + { + case DRV_PCI: + if (pci_config_setup (dip, &osdev->pci_config_handle) != DDI_SUCCESS) + cmn_err (CE_NOTE, "pci_config_setup() failed\n"); +#if !defined(SOL9) && !defined(DISABLE_FMA) + osdev->fm_capabilities = DDI_FM_EREPORT_CAPABLE | DDI_FM_DMACHK_CAPABLE | + DDI_FM_ERRCB_CAPABLE; + ddi_fm_init(dip, &osdev->fm_capabilities, &iblk); + ddi_fm_handler_register(dip, oss_fm_error_cb, (void*)osdev); + pci_ereport_setup(dip); +#endif + break; + + case DRV_VIRTUAL: + case DRV_VMIX: + case DRV_STREAMS: +#if !defined(SOL9) && !defined(DISABLE_FMA) + osdev->fm_capabilities=DDI_FM_EREPORT_CAPABLE; + ddi_fm_init(dip, &osdev->fm_capabilities, &iblk); +#endif + break; + + case DRV_USB: + /* NOP */ + break; + + default: + cmn_err (CE_WARN, "Bad device type\n"); + return NULL; + } + +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { + unsigned int subvendor; + pci_read_config_dword (osdev, 0x2c, &subvendor); + + sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance); + } + break; + + case DRV_USB: + /* TODO: Get the vendor information */ + sprintf (osdev->handle, "USB-%s%d", handle, instance); + break; + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + + if (osdev == NULL) + return; + + if (!osdev->available) /* Already deleted */ + return; + + osdev->available = 0; + + switch (osdev->dev_type) + { + case DRV_PCI: +#if !defined(SOL9) && !defined(DISABLE_FMA) + ddi_fm_handler_unregister(osdev->dip); + pci_ereport_teardown(osdev->dip); +#endif + pci_config_teardown (&osdev->pci_config_handle); +#if !defined(SOL9) && !defined(DISABLE_FMA) + ddi_fm_fini(osdev->dip); +#endif + osdev->pci_config_handle = NULL; + break; + + case DRV_VIRTUAL: + case DRV_VMIX: + case DRV_STREAMS: +#if !defined(SOL9) && !defined(DISABLE_FMA) + ddi_fm_fini(osdev->dip); +#endif + break; + } + + ddi_remove_minor_node (osdev->dip, NULL); + +/* + * Mark all minor nodes for this module as invalid. + */ + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i] != NULL) + if (oss_cdevs[i]->osdev == osdev) + { + unlink_cdev_hash(oss_cdevs[i]); + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->osdev = NULL; + oss_cdevs[i]->active = 0; /* Device removed */ + } +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + if (cardnum < 0 || cardnum >= oss_num_cards) + return OSS_ENXIO; + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->shortname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info) - 1); + ci->hw_info[sizeof (ci->hw_info) - 1] = 0; + ci->intr_count = cards[cardnum]->intrcount; + ci->ack_count = cards[cardnum]->ackcount; + + return 0; +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * element_size))==NULL) + return 0; + + memset(new, 0, new_size * element_size); + if (old != NULL) + memcpy(new, old, old_size * element_size); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +/*ARGSUSED*/ +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ +/* + * oss_install_chrdev creates a character device (minor). However if + * name==NULL the device will not be exported (made visible to userland + * clients). + */ + + int num; + int hash_link; + oss_cdev_t *cdev = NULL; + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; +/* + * Find if this dev_class&instance already exists (after previous module + * detach). + */ + + if (flags & CHDEV_REPLUG) + for (num = 0; num < oss_num_cdevs; num++) + if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ + if (oss_cdevs[num]->dev_class == dev_class + && oss_cdevs[num]->instance == instance) + { + cdev = oss_cdevs[num]; + break; + } + + if (cdev == NULL) + { + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) + { + cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); + return; + } + } + + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + num = oss_num_cdevs++; + } + + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->active = 1; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name) - 1); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + + cdev->minor = num; + + /* + * Add to the cdev_hash list. + */ + hash_link = compute_cdev_hash (dev_class, instance); + cdev->hl = cdev_hash[hash_link]; + cdev_hash[hash_link] = cdev; + +/* + * Export the device only if name != NULL + */ + if (name != NULL) + { + char tmp[64], *s; + char *dev_type = "oss_sysdev"; + +/* + * Convert "oss/device/node" style names to the + * "device,node" style naming required by Solaris. + */ + if (name[0] == 'o' && name[3] == '/') /* oss/ prefix */ + { + strcpy (tmp, name + 4); + name = tmp; + + s = tmp; + while (*s) + { + if (*s == '/') + *s = ','; + s++; + } + dev_type = "oss_audio"; + } + + if (ddi_create_minor_node (osdev->dip, name, S_IFCHR, num, + dev_type, 0) == DDI_FAILURE) + { + cmn_err (CE_WARN, "ddi_create_minor_node failed\n"); + } + } +} + +int +oss_find_minor (int dev_class, int instance) +{ + oss_cdev_t *cdev; + + cdev = cdev_hash[compute_cdev_hash(dev_class, instance)]; + + while (cdev != NULL) + { + if (cdev->d != NULL && cdev->dev_class == dev_class + && cdev->instance == instance) + return cdev->minor; + cdev = cdev->hl; /* Next in the hash chain */ + } + + return OSS_EIO; +} + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + int err; +#if defined(sparc) + uint_t len; +#else + size_t len; +#endif + uint_t ncookies; + contig_desc *desc; + oss_device_t *osdev = dmap->osdev; + ddi_dma_attr_t dma_attr; + int size = 32 * 1024; + extern int dma_buffsize; + ddi_device_acc_attr_t *acc_attr; + + int flags = DDI_DMA_REDZONE | DDI_DMA_CONSISTENT | + (direction == OPEN_READ) ? DDI_DMA_READ : DDI_DMA_WRITE; + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: osdev==NULL\n"); + return OSS_EIO; + } + + acc_attr = get_acc_attr (osdev); + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if (alloc_flags & DMABUF_LARGE) + size = 256 * 1024; + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + memcpy (&dma_attr, &dma_attr_pci, sizeof (ddi_dma_attr_t)); + dma_attr.dma_attr_addr_hi = maxaddr; + +#ifndef SOL9 + if (osdev->dev_type == DRV_PCI) + dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; +#endif + + if (dmap->dmabuf != NULL) + return 0; /* Already done */ + + dmap->dma_parms.state = 0; + dmap->dma_parms.enabled = 1; + dmap->dma_parms.ignore = 0; + + if (osdev->dip == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: osdev->dip==NULL\n"); + return OSS_EIO; + } + + if (dmap == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: dmap==NULL\n"); + return OSS_EIO; + } + + if ((err = ddi_dma_alloc_handle (osdev->dip, + &dma_attr, + DDI_DMA_SLEEP, + NULL, + &dmap->dmabuf_dma_handle)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "Failed to allocate DMA handle (error %d)\n", err); + return OSS_ENOMEM; + } + + + dmap->dmabuf = NULL; + dmap->buffsize = size; + + err = -1; + + while (err != DDI_SUCCESS && dmap->dmabuf == NULL && dmap->buffsize >= 4096) + { +#if defined(sparc) + if ((err = ddi_mem_alloc (osdev->dip, + NULL, + dmap->buffsize, + 0, + (caddr_t *) & dmap->dmabuf, + (uint_t *) & len)) != DDI_SUCCESS) +#else + if ((err = ddi_dma_mem_alloc (dmap->dmabuf_dma_handle, + dmap->buffsize, + acc_attr, + flags, + DDI_DMA_SLEEP, + NULL, + (caddr_t *) & dmap->dmabuf, + (size_t *) & len, + &dmap->dma_parms.dma_acc_handle)) != + DDI_SUCCESS) +#endif + { + if (!(alloc_flags & DMABUF_QUIET)) + DDB (cmn_err (CE_WARN, + "failed to allocate %d bytes of DMA memory (%d)\n", + dmap->buffsize, err)); + dmap->dmabuf = NULL; + dmap->buffsize /= 2; + } + } + + if (dmap->dmabuf == NULL) + { + cmn_err (CE_WARN, "Can't allocate a DMA buffer for device %d\n", dev); + ddi_dma_free_handle (&dmap->dmabuf_dma_handle); + return OSS_ENOMEM; + } + + DDB (cmn_err (CE_CONT, "Allocated DMA memory, addr=%x, len=%d\n", + (int) dmap->dmabuf, (int) dmap->buffsize)); + + dmap->dma_parms.orig_buf = (caddr_t) dmap->dmabuf; + + if ((err = ddi_dma_addr_bind_handle (dmap->dmabuf_dma_handle, + NULL, + (char *) dmap->dmabuf, + dmap->buffsize, + flags | DDI_DMA_STREAMING, + DDI_DMA_DONTWAIT, + NULL, + &dmap->dma_parms.cookie, + &ncookies)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "DMA address setup failed (%d)\n", err); + + return OSS_EIO; + } + + dmap->dmabuf = + (unsigned char *) ((((unsigned long) dmap->dmabuf) + 4095) & ~4095); + dmap->dmabuf_phys = (dmap->dma_parms.cookie.dmac_address + 4095) & ~4095; + + desc = PMALLOC (osdev, sizeof (contig_desc)); + if (desc == NULL) + return OSS_ENOMEM; + + desc->osdev = osdev; + desc->next = NULL; + desc->is_special = 1; + desc->first_addr = dmap->dmabuf; + desc->last_addr = dmap->dmabuf + dmap->buffsize - 1; + desc->physaddr = dmap->dmabuf_phys; + desc->orig_buf = dmap->dma_parms.orig_buf; + + if (contig_list != NULL) + desc->next = contig_list; +/* HW_PRINTF(("Alloc DMA: %x-%x, ph=%x\n", desc->first_addr, desc->last_addr, desc->physaddr)); */ + contig_list = desc; + + return 0; +} + +/*ARGSUSED*/ +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + int err; + + if (dmap->dmabuf == NULL) + return; + + if ((err = ddi_dma_unbind_handle (dmap->dmabuf_dma_handle)) != DDI_SUCCESS) + cmn_err (CE_WARN, "Failed to free DMA handle (%d)\n", err); +#if defined(sparc) + ddi_mem_free (dmap->dma_parms.orig_buf); +#else + ddi_dma_mem_free (&dmap->dma_parms.dma_acc_handle); +#endif + ddi_dma_free_handle (&dmap->dmabuf_dma_handle); + + dmap->dmabuf = NULL; + dmap->dmabuf_phys = 0; +} + +/* + * Interrupt management + */ +static u_int +oss_intr (caddr_t arg) /* Global interrupt handler */ +{ + oss_device_t *osdev = (oss_device_t *) arg; + int serviced; +#ifdef MUTEX_CHECKS + int x = inside_intr; + inside_intr = 1; /* For mutex debugging only */ +#endif + + if (osdev == NULL) + { +#ifdef MUTEX_CHECKS + inside_intr = x; +#endif + return DDI_INTR_UNCLAIMED; + } + osdev->intrcount++; + + UP_STATUS (0x01); + serviced = osdev->tophalf_handler (osdev); + DOWN_STATUS (0x01); + + if (serviced == 0) + return DDI_INTR_UNCLAIMED; + osdev->ackcount++; + + if (osdev->bottomhalf_handler == NULL) + return DDI_INTR_CLAIMED; + +#if 0 + if (osdev->intr_is_hilevel) + { +/* TODO: Schedule the bottom half handler */ + } + else +#endif + osdev->bottomhalf_handler (osdev); + +#ifdef MUTEX_CHECKS + inside_intr = x; +#endif + return DDI_INTR_CLAIMED; +} + +int +oss_register_interrupts (oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom) +{ + int err; + ddi_idevice_cookie_t ic; + + + if (intrnum != 0) + { + cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum, + osdev->name); + return OSS_EINVAL; + } + + if (osdev == NULL) + { + cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n"); + return OSS_EINVAL; + } + + if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL) + { + cmn_err (CE_WARN, "Interrupts already registered for %s\n", + osdev->name); + return OSS_EINVAL; + } + + if (top == NULL) + { + cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name); + return OSS_EINVAL; + } + + osdev->tophalf_handler = top; + osdev->bottomhalf_handler = bottom; + osdev->intr_is_hilevel = ddi_intr_hilevel (osdev->dip, intrnum); + if (osdev->intr_is_hilevel) + { + if (bottom == NULL) + { + cmn_err (CE_WARN, + "The driver for %s doesn't support hilevel interrupts\n", + osdev->name); + return OSS_EINVAL; + } + + DDB (cmn_err (CE_NOTE, "Using hilevel intr for %s\n", osdev->name)); + + /* TODO: Fix hilevel intr handling */ + cmn_err (CE_WARN, "Hilevel interrupts are not supported yet.\n"); + return OSS_EINVAL; + } + + ddi_get_iblock_cookie (osdev->dip, intrnum, &osdev->iblock_cookie); + + if ((err = ddi_add_intr (osdev->dip, intrnum, NULL, &ic, + oss_intr, (caddr_t) osdev)) != DDI_SUCCESS) + { + cmn_err (CE_WARN, "ddi_add_intr() failed, error=%d\n", err); + return OSS_EIO; + } + + return 0; +} + +void +oss_unregister_interrupts (oss_device_t * osdev) +{ + ddi_remove_intr (osdev->dip, 0, osdev->iblock_cookie); + osdev->tophalf_handler = NULL; + osdev->bottomhalf_handler = NULL; +} + +/*ARGSUSED*/ +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + return 0; +} + +int +oss_disable_device (oss_device_t * osdev) +{ + int i; +/* + * This routine should check if the device is ready to be unloaded (no devices are in use). + * If the device cannot be unloaded this routine must return OSS_EBUSY. + * + * If the device can be unloaded then disable any timers or other features that may cause the + * device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. + * However the interrupt handler should still stay enabled. The low level driver will call + * oss_unregister_interrupts() after it has cleared the interrupt enable register. + */ + if (osdev->refcount > 0 || open_devices > 0) + { + cmn_err (CE_CONT, "Refcount %d (%s) , open_devices %d\n", + osdev->refcount, osdev->nick, open_devices); + if (open_devices > 0) + { + int i; + + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->open_count) + { + cmn_err (CE_CONT, "%s is opened\n", oss_cdevs[i]->name); + } + } + return OSS_EBUSY; + } +//cmn_err(CE_CONT, "oss_disable_device %s\n", osdev->nick); + +/* + * Now mark all devices unavailable (for the time being) + */ + + for (i = 0; i < num_mixers; i++) + if (mixer_devs[i]->osdev == osdev) + { + mixer_devs[i]->unloaded = 1; + } + + for (i = 0; i < num_mididevs; i++) + { + if (midi_devs[i]->osdev == osdev) + { + midi_devs[i]->unloaded = 1; + } + } + + for (i = 0; i < num_audio_engines; i++) + if (audio_engines[i]->osdev == osdev) + { + audio_uninit_device (i); + } + + return 0; +} + +/*ARGSUSED*/ +void +oss_unregister_device (oss_device_t * osdev) +{ +/* + * Notice! The driver calling this routine (the owner of the osdev parameter) + * has already uninitialized itself. Do not do any actions that may call this + * driver directly or indirectly. + */ + +/* + * Force reload of all drivers if any application tries to open any + * of the devices. + */ + do_forceload = 1; +} + +void +oss_reserve_device (oss_device_t * osdev) +{ + osdev->refcount++; +} + +void +oss_unreserve_device (oss_device_t * osdev, int decrement) +{ + osdev->refcount -= decrement; +} + +/* + * Wait queue support + */ + /*ARGSUSED*/ + oss_wait_queue_t * +oss_create_wait_queue (oss_device_t * osdev, const char *name) +{ + oss_wait_queue_t *wq; + + if ((wq = PMALLOC (NULL, sizeof (*wq))) == NULL) + return NULL; + + cv_init (&wq->cv, NULL, CV_DRIVER, NULL); + + return wq; +} + +void * +oss_get_osid (oss_device_t * osdev) +{ + return osdev->osid; +} + +/*ARGSUSED*/ +void +oss_reset_wait_queue (oss_wait_queue_t * wq) +{ + /* NOP */ +} + +void +oss_remove_wait_queue (oss_wait_queue_t * wq) +{ + cv_destroy (&wq->cv); +} + +/*ARGSUSED*/ +int +oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ +/* + * oss_sleep will return 0 if timeout occurred and 1 otherwise. The WK_SIGNAL bit will be reported on + * status if a signal was caught. + */ + *status = 0; + + if (ticks > 0) + { + int res; + + if ((res = + cv_timedwait_sig (&wq->cv, mutex, ddi_get_lbolt () + ticks)) == 0) + *status |= WK_SIGNAL; /* Got signal */ + if (res < 0) /* Timeout */ + return 0; + } + else + { + if (cv_wait_sig (&wq->cv, mutex) == 0) + *status |= WK_SIGNAL; /* Got signal */ + } + return 1; +} + +/*ARGSUSED*/ +int +oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev) +{ + ev->php = &wq->ph; + wq->pollevents |= ev->events; + return 0; +} + +/*ARGSUSED*/ +void +oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + cv_broadcast (&wq->cv); + + if (wq->pollevents & events) + { + wq->pollevents &= ~events; + MUTEX_EXIT_IRQRESTORE (*mutex, *flags); + pollwakeup (&wq->ph, events); + MUTEX_ENTER_IRQDISABLE (*mutex, *flags); + } +} + +#ifdef MUTEX_CHECKS +void +debug_mutex_init (oss_mutex_t * mutex, void *dummy, int typ, void *arg, + char *file, int line) +{ + memset (mutex, 0, sizeof (mutex)); + mutex_init (&mutex->mu, dummy, typ, arg); +} + +void +debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line) +{ + mutex_destroy (&mutex->mu); +} + +void +debug_mutex_enter (oss_mutex_t * mutex, char *file, int line) +{ + if (mutex_owned (&mutex->mu)) + { + cmn_err (CE_NOTE, "%s:%d: Re-entrant mutex (%s:%d %d)\n", file, line, + mutex->file, mutex->line, mutex->busy_flags); + return; + } + + mutex->file = file; + mutex->line = line; + mutex_enter (&mutex->mu); +} + +void +debug_mutex_exit (oss_mutex_t * mutex, char *file, int line) +{ + if (!mutex_owned (&mutex->mu)) + { + cmn_err (CE_NOTE, "Mutex not owned %s:%d\n", file, line); + } + else + mutex_exit (&mutex->mu); + + mutex->file = NULL; + mutex->line = 0; +} +#endif + +int +oss_get_procinfo(int what) +{ + + switch (what) + { + case OSS_GET_PROCINFO_UID: + return ddi_get_cred()->cr_uid; + break; + + case OSS_GET_PROCINFO_GID: + return ddi_get_cred()->cr_gid; + break; + +#if 0 + case OSS_GET_PROCINFO_PGID: + return ddi_get_cred()->cr_pgid; + break; +#endif + + } + return OSS_EINVAL; +} diff --git a/kernel/OS/SunOS/os_solaris.h b/kernel/OS/SunOS/os_solaris.h new file mode 100644 index 0000000..2b36b86 --- /dev/null +++ b/kernel/OS/SunOS/os_solaris.h @@ -0,0 +1,518 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for Solaris + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#define OS_VERSION "10" +#define SUNDDI +#define __EXTENDED__ +#define Solaris + +#if (!defined(i386) && !defined(x86_64)) || defined(CONFIG_OSS_FIXDEPOINT) +// Floating point is not supported or it's disabled +#undef CONFIG_OSS_VMIX_FLOAT +#endif + +#define VDEV_SUPPORT +#define USE_DEVICE_SUBDIRS + +#define __inline__ inline +#define __inline inline +#define EXTERN_C extern "C" + +/* + * Debugging and misc settings + */ +#undef MUTEX_CHECKS +#undef MEMDEBUG + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ +#undef APPLIST_SUPPORT + +/* + * Some integer types + */ +#if defined(amd64) || defined(sparc) +typedef unsigned long long oss_native_word; /* Same as the address and status register size */ +#else +typedef unsigned long oss_native_word; /* Same as the address and status register size */ +#endif + +typedef long long oss_int64_t; /* Signed 64 bit integer */ +typedef unsigned long long oss_uint64_t; /* Unsigned 64 bit integer */ + +#include <stdarg.h> +#include <sys/types.h> +#include <sys/types32.h> +#include <sys/param.h> +#include <sys/signal.h> +#include <oss_errno.h> +#include <sys/file.h> +#include <sys/conf.h> +#include <sys/uio.h> +#include <sys/map.h> +#include <sys/debug.h> +#include <sys/modctl.h> +#include <sys/kmem.h> +#include <sys/cmn_err.h> +#include <sys/open.h> +#include <sys/stat.h> +#ifndef sparc +#include <sys/dditypes.h> +#include <sys/ddidmareq.h> +#include <sys/dma_engine.h> +#endif +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/fcntl.h> +#include <sys/ddidmareq.h> +#include <sys/ksynch.h> +#include <sys/poll.h> +#ifdef SOL9 +#include <sys/cred.h> +#else +#include <sys/cred_impl.h> +#endif + +#ifndef SOL9 +#include <sys/ddifm.h> +#include <sys/fm/protocol.h> +#include <sys/fm/util.h> +#include <sys/fm/io/ddi.h> +#endif + +#undef HZ +#define OSS_HZ hz + +#ifdef sparc +/* + * It's not possible to use SB Live! under Sparc architecture. + */ +#define AUDIGY_ONLY +#endif + +/* + * Need to use slightly larger buffer because otherwise the interrupt + * rate will become too high for Solaris. + */ +#undef SMALL_DMABUF_SIZE +#define SMALL_DMABUF_SIZE (16*1024) + +/* The soundcard.h could be in a nonstandard place so include it here. */ +#include "soundcard.h" + +typedef struct udi_usb_devc udi_usb_devc; + +struct _oss_device_t +{ + int cardnum; + int dev_type; + int instance; + int available; + dev_info_t *dip; + void *osid; + void *devc; + char *name; + char nick[16]; + char modname[32]; + char handle[32]; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + char *hw_info; + + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +/* Interrupts */ + + ddi_iblock_cookie_t iblock_cookie; + int intr_is_hilevel; + oss_tophalf_handler_t tophalf_handler; + oss_bottomhalf_handler_t bottomhalf_handler; + int intrcount, ackcount; + +#ifdef _KERNEL +/* PCI related fields */ + ddi_acc_handle_t pci_config_handle; +#define OSS_MAX_ACC_HANDLE 10 + ddi_acc_handle_t acc_handle[OSS_MAX_ACC_HANDLE]; + int swap_mode; /* 0=DDI_STRUCTURE_NEVERSWAP_ACC, 1=DDI_STRUCTURE_LE_ACC */ + +/* USB fields */ + udi_usb_devc *usbdev; + +#ifndef SOL9 +/* Fault management (FMA) */ + int fm_capabilities; +#endif +#endif +}; + +/* + * Support for select() + */ + +struct _oss_poll_event_t +{ + short events, revents; + struct pollhead *php; + int anyyet; +}; +typedef struct _oss_poll_event_t oss_poll_event_t; + +#define ALLOW_SELECT +#define SEL_IN 0 +#define SEL_OUT 1 +#define SEL_EX 0xffffffff + +/* + * Sleep/wakeup + */ + +#ifdef _KERNEL +struct oss_wait_queue +{ + kcondvar_t cv; + short pollevents; + struct pollhead ph; +}; +#endif + +/* Busy wait routine */ +#define oss_udelay drv_usecwait +/* System wall timer access */ +#define GET_JIFFIES() ddi_get_lbolt() + +/* + * Mutexes + */ + +#ifdef MUTEX_CHECKS +/* Debugging version */ +struct _oss_mutex_t +{ + kmutex_t mu; + char *file; + int line; + int busy_flags; +#define CNTX_INTR 1 +#define CNTX_USER 2 +}; + +typedef struct _oss_mutex_t oss_mutex_t; +extern void debug_mutex_init (oss_mutex_t * mutex, void *dummy, int typ, + void *arg, char *file, int line); +extern void debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line); +extern void debug_mutex_enter (oss_mutex_t * mutex, char *file, int line); +extern void debug_mutex_exit (oss_mutex_t * mutex, char *file, int line); + +#define MUTEX_INIT(osdev, mutex, hier) debug_mutex_init(&mutex, NULL, MUTEX_DRIVER, osdev->iblock_cookie, __FILE__, __LINE__) +#define MUTEX_CLEANUP(mutex) debug_mutex_destroy(&mutex, __FILE__, __LINE__) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) flags=0;debug_mutex_enter(&mutex, __FILE__, __LINE__) +#define MUTEX_ENTER(mutex, flags) flags=0;debug_mutex_enter(&mutex, __FILE__, __LINE__) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) debug_mutex_exit(&mutex, __FILE__, __LINE__);(flags)++ +#define MUTEX_EXIT(mutex, flags) debug_mutex_exit(&mutex, __FILE__, __LINE__);(flags)++ +#else +typedef kmutex_t oss_mutex_t; +#define MUTEX_INIT(osdev, mutex, hier) mutex_init(&mutex, NULL, MUTEX_DRIVER, osdev->iblock_cookie) +#define MUTEX_CLEANUP(mutex) mutex_destroy(&mutex) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) flags=0;mutex_enter(&mutex) +#define MUTEX_ENTER(mutex, flags) flags=0;mutex_enter(&mutex) +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) mutex_exit(&mutex);(flags)++ +#define MUTEX_EXIT(mutex, flags) mutex_exit(&mutex);(flags)++ +#endif + +/* + * Move bytes from the buffer which the application given in a + * write() call. + * offs is position relative to the beginning of the buffer in + * user space. The count is number of bytes to be moved. + */ +#define COPY_FROM_USER(target, source, offs, count) \ + if (uiomove((target), count, UIO_WRITE, source)) { \ + cmn_err(CE_WARN, "Bad copyin()!\n"); \ + } +/* Like COPY_FOM_USER but for writes. */ +#define COPY_TO_USER(target, offs, source, count) \ + if (uiomove((source), count, UIO_READ, target)) { \ + cmn_err(CE_WARN, "Bad copyout()!\n"); \ + } + +/* + * INB() and OUTB() should be obvious. NOTE! The order of + * paratemeters of OUTB() is different than on some other + * operating systems. + */ + +#ifdef sparc +#ifdef _KERNEL +extern void oss_put16 (ddi_acc_handle_t handle, unsigned short *addr, + unsigned short value); +extern void oss_put32 (ddi_acc_handle_t handle, unsigned int *addr, + unsigned int value); +extern unsigned short oss_get16 (ddi_acc_handle_t handle, + unsigned short *addr); +extern unsigned int oss_get32 (ddi_acc_handle_t handle, unsigned int *addr); + +#define INB(osdev,port) ddi_get8((osdev)->acc_handle[0], (uint8_t*)(port)) +#define OUTB(osdev, d, port) ddi_put8((osdev)->acc_handle[0], (uint8_t*)(port), (d)) +#define INW(osdev,port) oss_get16((osdev)->acc_handle[0], (uint16_t*)(port)) +#define OUTW(osdev, d, port) oss_put16((osdev)->acc_handle[0], (uint16_t*)(port), (d)) +#define INL(osdev,port) oss_get32((osdev)->acc_handle[0], (uint32_t*)(port)) +#define OUTL(osdev,d, port) oss_put32((osdev)->acc_handle[0], (uint32_t*)(port), (d)) + +/* Memory Mapped devices */ +extern uint16_t oss_mem_get16 (ddi_acc_handle_t handle, uint16_t * addr); +extern uint32_t oss_mem_get32 (ddi_acc_handle_t handle, uint32_t * addr); +extern void oss_mem_put16 (ddi_acc_handle_t handle, uint16_t * add, + uint16_t v); +extern void oss_mem_put32 (ddi_acc_handle_t handle, uint32_t * add, + uint32_t v); +#define PCI_READW(osdev, addr) oss_mem_get16((osdev)->acc_handle[0], (uint16_t*)(addr)) +#define PCI_READL(osdev, addr) oss_mem_get32((osdev)->acc_handle[0], (uint32_t*)(addr)) +#define PCI_WRITEW(osdev, addr, data) oss_mem_put16((osdev)->acc_handle[0], (uint16_t*)(addr), data) +#define PCI_WRITEL(osdev, addr, data) oss_mem_put32((osdev)->acc_handle[0], (uint32_t*)(addr), data) +#endif +#else /* x86/AMD64 */ +/* I/O Mapped devices */ +#define INB(o, p) inb(p) +#define INW(o, p) inw(p) +#define INL(o, p) inl(p) + +#define OUTB(o, v, p) outb(p,v) +#define OUTW(o, v, p) outw(p,v) +#define OUTL(o, v, p) outl(p,v) + +/* Memory Mapped devices */ +#define PCI_READW(osdev, addr) ddi_mem_get16((osdev)->acc_handle[0], (uint16_t*)(addr)) +#define PCI_READL(osdev, addr) ddi_mem_get32((osdev)->acc_handle[0], (uint32_t*)(addr)) +#define PCI_WRITEW(osdev, addr, data) ddi_mem_put16((osdev)->acc_handle[0], (uint16_t*)(addr), data) +#define PCI_WRITEL(osdev, addr, data) ddi_mem_put32((osdev)->acc_handle[0], (uint32_t*)(addr), data) +#endif + +#define PCI_READB(osdev, addr) ddi_mem_get8((osdev)->acc_handle[0], addr) +#define PCI_WRITEB(osdev, addr, data) ddi_mem_put8((osdev)->acc_handle[0], addr, data) + +typedef ddi_dma_handle_t oss_dma_handle_t; + +/* + KERNEL_MALLOC() allocates requested number of memory and + KERNEL_FREE is used to free it. + These macros are never called from interrupt, in addition the + nbytes will never be more than 4096 bytes. Generally the driver + will allocate memory in blocks of 4k. If the kernel has just a + page level memory allocation, 4K can be safely used as the size + (the nbytes parameter can be ignored). +*/ +#ifdef MEMDEBUG +extern void *oss_kmem_alloc (size_t size, int flags, char *file, int line); +extern void oss_kmem_free (void *addr); +#define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, KM_SLEEP, __FILE__, __LINE__) +#define KERNEL_FREE(addr) oss_kmem_free(addr) +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word *phaddr, + oss_dma_handle_t *dma_handle, + char * file, int line); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr, &handle, __FILE__, __LINE__) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz, handle) +#else +extern void *oss_kmem_alloc (size_t size, int flags); +extern void oss_kmem_free (void *addr); +#define KERNEL_MALLOC(nbytes) oss_kmem_alloc(nbytes, KM_SLEEP) +#define KERNEL_FREE(addr) oss_kmem_free(addr) +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr, + oss_dma_handle_t *dma_handle); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr, &handle) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) +#endif + +/* + * Timer macros + * + * These macros are obsolete and should not be used in any new code. + * Use the timeout mechanism (see the timeout(9F) Solaris man page). + */ +#define DEFINE_TIMER(name, proc) static timeout_id_t name = 0 +#define REMOVE_TIMER(name, proc) {if (name != 0) untimeout(name);} +#define INIT_TIMER(name,proc) +typedef void (*timeout_func_t) (void *); +#define ACTIVATE_TIMER(name, proc, time) \ + name=timeout((timeout_func_t)proc, (void*)&name, time) + +#define OSS_DMA_SYNC_INBOUND DDI_DMA_SYNC_FORCPU +#define OSS_DMA_SYNC_OUTBOUND DDI_DMA_SYNC_FORDEV +#define OSS_DMA_SYNC(handle, offs, len, direc) (ddi_dma_sync(handle, offs, len, direc)==DDI_SUCCESS) + +#ifdef _KERNEL +struct os_dma_params +{ + int state; /* 0=unavail, 1=avail, 2=busy */ + oss_device_t *osdev; + ddi_dma_handle_t handle; + ddi_acc_handle_t dma_acc_handle; + ddi_dma_cookie_t cookie; + ddi_dma_win_t win; + ddi_dma_seg_t seg; + void *orig_buf; + + volatile int enabled, ignore; +}; +#define OS_DMA_PARMS \ + struct os_dma_params dma_parms; +#endif +#endif + +struct fileinfo +{ + int mode; /* Open mode */ + int acc_flags; +}; + +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +#ifdef sparc +#define OSS_OS "Sparc" +#define OSS_OS_LONGNAME "Solaris (Sparc)" +#else +#define OSS_OS "Solaris" +#define OSS_OS_LONGNAME "Solaris (x86)" +#endif + +#undef DMA_TRY_PSEUDOINIT + +int get_dma_residue (int chn); +void disable_dma (int chn); +void enable_dma (int chn); + +typedef void (*softintr_func_t) (int); + +struct oss_softintr +{ + int id; + softintr_func_t func; + volatile int armed, running; +}; + +/* + * There is apparently no documented way to map DMA buffers allocated + * using ddi_dma_mem_alloc() to applications. For this reason we cannot + * support mmap() under Solaris. + */ +#undef ALLOW_BUFFER_MAPPING + +#if 0 +extern int detect_trace; +#define DDB(x) if (detect_trace) x +#else +#define DDB(x) {} +#endif + +/* + * PCI I/O and memory address mapping. + * + * Note that for compatibility with the other operating systems supported by + * OSS the region numbering is different. So 0 means BAR0 while under + * Solaris (ddi_regs_map_setup) BAR0 is 1. oss_map_pci_ioaddr() will + * add 1 to the region number. + */ +extern caddr_t oss_map_pci_ioaddr (oss_device_t * osdev, int nr, int io); +extern void oss_unmap_pci_ioaddr(oss_device_t * osdev, int nr); +#define MAP_PCI_IOADDR(osdev, nr, io) (oss_native_word)oss_map_pci_ioaddr(osdev, nr, io) +#define MAP_PCI_MEM(osdev, ix, phaddr, size) oss_map_pci_ioaddr(osdev, ix, phaddr) +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) oss_unmap_pci_ioaddr(osdev, ix) +#define UNMAP_PCI_IOADDR(osdev, ix) oss_unmap_pci_ioaddr(osdev, ix) + +#define GET_PROCESS_PID(x) ddi_get_pid() + +/* + * Instead of returning UID check if the process has PRIV_SYS_DEVICES privilege. + * Report such users as UID=0 (root) and others as UID=1. + */ +#define GET_PROCESS_UID() ((drv_priv(ddi_get_cred())==0) ? 0 : 1) + +#if 1 +/* TODO: This works OK but may cause crashes with different kernel versions/builds */ +extern char *oss_get_procname (void); +#define GET_PROCESS_NAME(x) oss_get_procname() +#endif + +#define FP_SUPPORT + +#ifdef FP_SUPPORT +typedef short fp_env_t[512]; +typedef unsigned int fp_flags_t[5]; +extern int oss_fp_check (void); +extern void oss_fp_save (short *envbuf, fp_flags_t flags); +extern void oss_fp_restore (short *envbuf, fp_flags_t flags); +# define FP_SAVE(envbuf, flags) oss_fp_save(envbuf, flags) +# define FP_RESTORE(envbuf, flags) oss_fp_restore(envbuf, flags) +#endif + +#define abs(x) ((x) >= 0 ? (x) : -(x)) + +extern int oss_open (dev_t * dev_p, int open_flags, int otyp, + cred_t * cred_p); +extern int oss_close (dev_t dev, int flag, int otyp, cred_t * cred_p); +extern int oss_ioctl (dev_t dev, int cmd, intptr_t arg, int mode, + cred_t * cred_p, int *rval_p); +extern int oss_read (dev_t dev, struct uio *uiop, cred_t * credp); +extern int oss_write (dev_t dev, struct uio *uiop, cred_t * cred_p); +extern int oss_chpoll (dev_t dev, short events, int anyyet, short *reventsp, + struct pollhead **phpp); +#ifdef _KERNEL +extern int oss_devmap (dev_t dev, devmap_cookie_t handle, offset_t off, + size_t len, size_t * maplen, uint_t model); + +extern void *oss_memset (void *s, int c, size_t n); +#define memset oss_memset + +extern void *oss_memcpy (void *s1, const void *s2, size_t n); +#define memcpy oss_memcpy +#endif + +#define DISABLE_FMA +#if !defined(SOL9) && defined(DDI_FM_DEVICE) && !defined(DISABLE_FMA) +/* + * Fault management (FMA) support. + */ + +#define FMA_EREPORT(osdev, detail, name, type, value) \ +{ \ + uint64_t ena; \ + char buf[FM_MAX_CLASS]; \ + (void) snprintf (buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); \ + ena = fm_ena_generate(0, FM_ENA_FMT1); \ + if (osdev->dip != NULL && osdev->fm_capabilities != 0) \ + ddi_fm_ereport_post(osdev->dip, buf, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, name, type, value, NULL); \ +} + +#define FMA_IMPACT(osdev, impact) \ + if (osdev->fm_capabilities != 0) \ + ddi_fm_service_impact(osdev->dip, impact) +#endif diff --git a/kernel/OS/SunOS/udi.c b/kernel/OS/SunOS/udi.c new file mode 100644 index 0000000..ad4d4fa --- /dev/null +++ b/kernel/OS/SunOS/udi.c @@ -0,0 +1,1365 @@ +/* + * Purpose: USB device interface (udi.h) routines for Solaris + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +#if !defined(SOL9) +#include "oss_config.h" +#include <udi.h> + +#define USBDRV_MAJOR_VER 2 +#define USBDRV_MINOR_VER 0 +#include <sys/usb/usba.h> +#include <sys/strsubr.h> + +#undef IO_DEBUG +int udi_usb_trace = 0; + +#define MAX_DEVICE_SLOTS 20 + +struct udi_usb_devc +{ + oss_device_t *osdev; + usb_client_dev_data_t *dev_data; + char *name; + + const struct usb_device_id *id; + const udi_usb_devinfo *udi_usb_dev; + char devpath[32]; + + int enabled; + + struct usb_interface *iface; + udi_usb_driver *drv; + void *client_devc; + void (*client_disconnect) (void *devc); +}; + +#define MAX_DEVC 32 + +int +udi_attach_usbdriver (oss_device_t * osdev, const udi_usb_devinfo * devlist, + udi_usb_driver * driver) +{ + int err, i, nalt, vendor, product, busaddr; + udi_usb_devc *usbdev; + dev_info_t *dip = osdev->dip; + char *name; + unsigned long osid; + //usb_alt_if_data_t *altif_data; + usb_client_dev_data_t *dev_data; + + if (dip==NULL) + { + cmn_err(CE_WARN, "dip==NULL\n"); + return 0; + } + + name = ddi_get_name (osdev->dip); + + if (strcmp (name, "oss_usb") == 0) + { + oss_register_device (osdev, "USB audio/MIDI device"); + return 1; + } + + /* + * Select the default configuration + */ + if ((err = usb_set_cfg (osdev->dip, USB_DEV_DEFAULT_CONFIG_INDEX, + USB_FLAGS_SLEEP, NULL, NULL)) == USB_SUCCESS) + { + cmn_err (CE_CONT, "usb_set_cfg returned %d\n", err); + } + + busaddr = ddi_prop_get_int (DDI_DEV_T_ANY, osdev->dip, + DDI_PROP_NOTPROM, "assigned-address", 1234); + if ((usbdev = KERNEL_MALLOC (sizeof (*usbdev))) == NULL) + { + cmn_err (CE_WARN, "udi_attach_usbdriver: Out of memory\n"); + return 0; + } + + memset (usbdev, 0, sizeof (*usbdev)); + osdev->usbdev = usbdev; + usbdev->osdev = osdev; + + if ((err = usb_client_attach (dip, USBDRV_VERSION, 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_client_attach failed, err=%d\n", err); + KERNEL_FREE (usbdev); + return 0; + } + + if ((err = + usb_get_dev_data (osdev->dip, &usbdev->dev_data, USB_PARSE_LVL_CFG, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_get_dev_data failed, err=%d\n", err); + KERNEL_FREE (usbdev); + return 0; + } + dev_data = usbdev->dev_data; + + osdev->iblock_cookie = dev_data->dev_iblock_cookie; + + sprintf (usbdev->devpath, "usb%x,%x@port%d", udi_usbdev_get_vendor (usbdev), + udi_usbdev_get_product (usbdev), busaddr); + oss_register_device (osdev, "USB audio/MIDI device"); + sprintf (osdev->nick, "usb%x_%x_%d", udi_usbdev_get_vendor (usbdev), + udi_usbdev_get_product (usbdev), busaddr); + +#if 0 + if (udi_usb_trace > 5) + usb_print_descr_tree (osdev->dip, dev_data); +#endif + + nalt = 1; + if (nalt >= dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt) + nalt = 0; + + //altif_data = &dev_data->dev_curr_cfg-> + // cfg_if[dev_data->dev_curr_if].if_alt[nalt]; + + usbdev->name = usbdev->dev_data->dev_product; + vendor = udi_usbdev_get_vendor (usbdev); + product = udi_usbdev_get_product (usbdev); + + /* inum = usbdev->dev_data->dev_curr_if; */ + osid = (vendor << 16) | product; + osdev->osid = (void *) osid; + + sprintf (osdev->handle, "usb%x,%x.%d:%d", vendor, product, + dev_data->dev_curr_if, osdev->instance); + + for (i = 0; devlist[i].vendor != -1; i++) + if (devlist[i].vendor == vendor && devlist[i].product == product) + { + usbdev->name = devlist[i].name; + usbdev->udi_usb_dev = &devlist[i]; + oss_register_device (osdev, usbdev->name); + break; + } + + if ((usbdev->client_devc = driver->attach (usbdev, osdev)) == NULL) + { + cmn_err (CE_WARN, "Client attach failed, err=%d\n", err); + udi_unload_usbdriver (osdev); + return 0; + } + + usbdev->client_disconnect = driver->disconnect; + + return 1; +} + +static char * +usb_errstr (int err) +{ + + static struct errmsg_ + { + int err; + char *str; + } errs[] = + { + { + USB_FAILURE, "USB_FAILURE"}, + { + USB_NO_RESOURCES, "USB_NO_RESOURCES"}, + { + USB_NO_BANDWIDTH, "USB_NO_BANDWIDTH"}, + { + USB_NOT_SUPPORTED, "USB_NOT_SUPPORTED"}, + { + USB_PIPE_ERROR, "USB_PIPE_ERROR"}, + { + USB_INVALID_PIPE, "USB_INVALID_PIPE"}, + { + USB_NO_FRAME_NUMBER, "USB_NO_FRAME_NUMBER"}, + { + USB_INVALID_START_FRAME, "USB_INVALID_START_FRAME"}, + { + USB_HC_HARDWARE_ERROR, "USB_HC_HARDWARE_ERROR"}, + { + USB_INVALID_REQUEST, "USB_INVALID_REQUEST"}, + { + USB_INVALID_CONTEXT, "USB_INVALID_CONTEXT"}, + { + USB_INVALID_VERSION, "USB_INVALID_VERSION"}, + { + USB_INVALID_ARGS, "USB_INVALID_ARGS"}, + { + USB_INVALID_PERM, "USB_INVALID_PERM"}, + { + USB_BUSY, "USB_BUSY"}, + { + 0, NULL} + }; + + int i; + static char msg[20]; + + for (i = 0; errs[i].err != 0; i++) + if (errs[i].err == err) + { + return errs[i].str; + } + + sprintf (msg, "Err %d", err); + return msg; +} + +static char * +usb_cb_err (int err) +{ + + static struct errmsg_ + { + int err; + char *str; + } errs[] = + { + { + USB_CB_STALL_CLEARED, "USB_CB_STALL_CLEARED"}, + { + USB_CB_FUNCTIONAL_STALL, "USB_CB_FUNCTIONAL_STALL"}, + { + USB_CB_PROTOCOL_STALL, "USB_CB_PROTOCOL_STALL"}, + { + USB_CB_RESET_PIPE, "USB_CB_RESET_PIPE"}, + { + USB_CB_ASYNC_REQ_FAILED, "USB_CB_ASYNC_REQ_FAILED"}, + { + USB_CB_NO_RESOURCES, "USB_CB_NO_RESOURCES"}, + { + USB_CB_SUBMIT_FAILED, "USB_CB_SUBMIT_FAILED"}, + { + USB_CB_INTR_CONTEXT, "USB_CB_INTR_CONTEXT"}, + { + USB_FAILURE, "USB_FAILURE"}, + { + USB_NO_RESOURCES, "USB_NO_RESOURCES"}, + { + USB_NO_BANDWIDTH, "USB_NO_BANDWIDTH"}, + { + USB_NOT_SUPPORTED, "USB_NOT_SUPPORTED"}, + { + USB_PIPE_ERROR, "USB_PIPE_ERROR"}, + { + USB_INVALID_PIPE, "USB_INVALID_PIPE"}, + { + USB_NO_FRAME_NUMBER, "USB_NO_FRAME_NUMBER"}, + { + USB_INVALID_START_FRAME, "USB_INVALID_START_FRAME"}, + { + USB_HC_HARDWARE_ERROR, "USB_HC_HARDWARE_ERROR"}, + { + USB_INVALID_REQUEST, "USB_INVALID_REQUEST"}, + { + USB_INVALID_CONTEXT, "USB_INVALID_CONTEXT"}, + { + USB_INVALID_VERSION, "USB_INVALID_VERSION"}, + { + USB_INVALID_ARGS, "USB_INVALID_ARGS"}, + { + USB_INVALID_PERM, "USB_INVALID_PERM"}, + { + USB_BUSY, "USB_BUSY"}, + { + 0, NULL} + }; + + int i; + static char msg[20]; + + if (err == 0) + return "USB_CB_NO_INFO"; + + for (i = 0; errs[i].err != 0; i++) + { + if (errs[i].err == err) + { + return errs[i].str; + } + } + + sprintf (msg, "CB Err %d", err); + return msg; +} + +static char * +usb_cr_err (int err) +{ + + static struct errmsg_ + { + int err; + char *str; + } errs[] = + { + { + USB_CR_CRC, "USB_CR_CRC"}, + { + USB_CR_BITSTUFFING, "USB_CR_BITSTUFFING"}, + { + USB_CR_DATA_TOGGLE_MM, "USB_CR_DATA_TOGGLE_MM"}, + { + USB_CR_STALL, "USB_CR_STALL"}, + { + USB_CR_DEV_NOT_RESP, "USB_CR_DEV_NOT_RESP"}, + { + USB_CR_PID_CHECKFAILURE, "USB_CR_PID_CHECKFAILURE"}, + { + USB_CR_UNEXP_PID, "USB_CR_UNEXP_PID"}, + { + USB_CR_DATA_OVERRUN, "USB_CR_DATA_OVERRUN"}, + { + USB_CR_DATA_UNDERRUN, "USB_CR_DATA_UNDERRUN"}, + { + USB_CR_BUFFER_OVERRUN, "USB_CR_BUFFER_OVERRUN"}, + { + USB_CR_BUFFER_UNDERRUN, "USB_CR_BUFFER_UNDERRUN"}, + { + USB_CR_TIMEOUT, "USB_CR_TIMEOUT"}, + { + USB_CR_NOT_ACCESSED, "USB_CR_NOT_ACCESSED"}, + { + USB_CR_NO_RESOURCES, "USB_CR_NO_RESOURCES"}, + { + USB_CR_UNSPECIFIED_ERR, "USB_CR_UNSPECIFIED_ERR"}, + { + USB_CR_STOPPED_POLLING, "USB_CR_STOPPED_POLLING"}, + { + USB_CR_PIPE_CLOSING, "USB_CR_PIPE_CLOSING"}, + { + USB_CR_PIPE_RESET, "USB_CR_PIPE_RESET"}, + { + USB_CR_NOT_SUPPORTED, "USB_CR_NOT_SUPPORTED"}, + { + USB_CR_FLUSHED, "USB_CR_FLUSHED"}, + { + USB_CR_HC_HARDWARE_ERR, "USB_CR_HC_HARDWARE_ERR"}, + { + 0, NULL} + }; + + int i; + static char msg[20]; + + if (err == 0) + return "USB_CR_OK"; + + for (i = 0; errs[i].err != 0; i++) + { + if (errs[i].err == err) + { + return errs[i].str; + } + } + + sprintf (msg, "CR Err %d", err); + return msg; +} + +void +udi_unload_usbdriver (oss_device_t * osdev) +{ + udi_usb_devc *usbdev = osdev->usbdev; + + if (usbdev == NULL) + return; + + if (usbdev->client_disconnect != NULL) + usbdev->client_disconnect (usbdev->client_devc); + usb_client_detach (osdev->dip, usbdev->dev_data); + + KERNEL_FREE (usbdev); + osdev->usbdev = NULL; +} + +/* + * Device access routines + */ + +int +udi_usbdev_get_class (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_curr_cfg->cfg_if[devc->dev_data->dev_curr_if]. + if_alt[0].altif_descr.bInterfaceClass; +} + +int +udi_usbdev_get_subclass (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_curr_cfg->cfg_if[devc->dev_data->dev_curr_if]. + if_alt[0].altif_descr.bInterfaceSubClass; +} + +int +udi_usbdev_get_vendor (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_descr->idVendor; +} + +int +udi_usbdev_get_product (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_descr->idProduct; +} + +int +udi_usbdev_get_inum (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->dev_data->dev_curr_if; +} + +int +udi_usbdev_set_interface (udi_usb_devc * usbdev, int inum, int altset) +{ + //udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + if (usb_set_alt_if (usbdev->osdev->dip, inum, altset, USB_FLAGS_SLEEP, + NULL, 0) != USB_SUCCESS) + { + return OSS_EIO; + } + + return 0; +} + +unsigned char * +udi_usbdev_get_endpoint (udi_usb_devc * usbdev, int altsetting, int n, + int *len) +{ + usb_alt_if_data_t *altif_data; + usb_client_dev_data_t *dev_data; + + *len = 0; + + if (usbdev->dev_data == NULL) + { + cmn_err (CE_WARN, "Missing USB devdata\n"); + return NULL; + } + dev_data = usbdev->dev_data; + + if (altsetting < 0 + || altsetting >= + dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt) + { + return NULL; + } + + altif_data = &dev_data->dev_curr_cfg-> + cfg_if[dev_data->dev_curr_if].if_alt[altsetting]; + + if (altif_data == NULL) + return NULL; + + if (n < 0 || n >= altif_data->altif_n_ep) + return NULL; + + *len = altif_data->altif_ep[n].ep_descr.bLength; + return (unsigned char *) &altif_data->altif_ep[n].ep_descr; +} + +int +udi_usbdev_get_num_altsettings (udi_usb_devc * usbdev) +{ + //udi_usb_devc *devc = (udi_usb_devc *) usbdev; + usb_client_dev_data_t *dev_data; + + dev_data = usbdev->dev_data; + return dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt; +} + +unsigned char * +udi_usbdev_get_altsetting (udi_usb_devc * usbdev, int nalt, int *size) +{ + int i, n; + usb_cvs_data_t *cvs; + usb_alt_if_data_t *altif_data; + usb_client_dev_data_t *dev_data; + static unsigned char buf[1024]; + + dev_data = usbdev->dev_data; + if ((unsigned long) dev_data <= 4096) + { + cmn_err (CE_WARN, "Internal error: dev_data==NULL)\n"); + return NULL; + } + + if (nalt < 0 + || nalt >= + dev_data->dev_curr_cfg->cfg_if[dev_data->dev_curr_if].if_n_alt) + { + return NULL; + } + + altif_data = &dev_data->dev_curr_cfg-> + cfg_if[dev_data->dev_curr_if].if_alt[nalt]; + + if (altif_data == NULL) + return NULL; + + n = 0; + + for (i = 0; i < altif_data->altif_n_cvs; i++) + { + cvs = &altif_data->altif_cvs[i]; + memcpy (buf + n, cvs->cvs_buf, cvs->cvs_buf_len); + n += cvs->cvs_buf_len; + } + + cvs = &altif_data->altif_cvs[0]; + if (cvs == NULL || cvs->cvs_buf == NULL) + return NULL; + + *size = n; + return buf; +} + +char * +udi_usbdev_get_name (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->name; +} + +char * +udi_usbdev_get_altsetting_labels (udi_usb_devc * usbdev, int if_num, + int *default_alt, unsigned int *mask) +{ + int i; + + *default_alt = 1; + *mask = 0xffffffff; + + if (usbdev->udi_usb_dev == NULL) /* No device definitions available */ + { + return NULL; + } + + for (i = 0; usbdev->udi_usb_dev->altsettings[i].altsetting_labels != NULL; + i++) + if (i == if_num) + { + *default_alt = usbdev->udi_usb_dev->altsettings[i].default_altsetting; + *mask = usbdev->udi_usb_dev->altsettings[i].altsetting_mask; + if (*mask == 0) + *mask = 0xffffffff; + return usbdev->udi_usb_dev->altsettings[i].altsetting_labels; + } + + return NULL; /* Not found */ +} + +char * +udi_usbdev_get_string (udi_usb_devc * usbdev, int ix) +{ + static char str[256]; + + if (usb_get_string_descr + (usbdev->osdev->dip, USB_LANG_ID, ix, str, + sizeof (str) - 1) != USB_SUCCESS) + return NULL; + return str; +} + +char * +udi_usbdev_get_devpath (udi_usb_devc * usbdev) +{ + udi_usb_devc *devc = (udi_usb_devc *) usbdev; + + return devc->devpath; +} + +/*ARGSUSED*/ +int +udi_usb_snd_control_msg (udi_usb_devc * usbdev, unsigned int endpoint, + unsigned char rq, + unsigned char rqtype, + unsigned short value, + unsigned short index, + void *buf, int len, int timeout) +{ + + int err; + + usb_ctrl_setup_t setup; + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + mblk_t *data = NULL; + + if (usbdev == NULL) + { + cmn_err (CE_CONT, "udi_usb_snd_control_msg: usbdev==NULL\n"); + return OSS_EFAULT; + } + + data = allocb_wait (len + 1, BPRI_HI, STR_NOSIG, NULL); + + memcpy (data->b_wptr, buf, len); + data->b_wptr = data->b_rptr + len; + + setup.bmRequestType = rqtype | USB_DEV_REQ_HOST_TO_DEV; + setup.bRequest = rq; + setup.wValue = value; + setup.wIndex = index; + setup.wLength = len; + setup.attrs = 0; + + if ((err = usb_pipe_ctrl_xfer_wait (usbdev->dev_data->dev_default_ph, + &setup, + &data, + &completion_reason, + &cb_flags, 0)) != USB_SUCCESS) + { + char tmp[128], *s = tmp; + unsigned char *p = buf; + int i; + + sprintf (s, "Msg (rq=0x%x, val=0x%04x, ix=0x%04x, len=%d): ", rq, value, + index, len); + for (i = 0; i < len; i++) + { + s += strlen (s); + sprintf (s, "%02x ", p[i]); + } + cmn_err (CE_CONT, "%s\n", tmp); + + cmn_err (CE_NOTE, "usb_pipe_ctrl_xfer_wait write failed: %s (%s)\n", + usb_errstr (err), usb_cb_err (completion_reason)); + cmn_err (CE_CONT, "bRq %x, wIx %x, wVal %x, wLen %d\n", rq, index, + value, len); + freemsg (data); + return OSS_EIO; + } + + freemsg (data); + return len; +} + +/*ARGSUSED*/ +int +udi_usb_rcv_control_msg (udi_usb_devc * usbdev, unsigned int endpoint, + unsigned char rq, + unsigned char rqtype, + unsigned short value, + unsigned short index, + void *buf, int len, int timeout) +{ + int err, l; + + usb_ctrl_setup_t setup; + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + mblk_t *data = NULL; + + if (usbdev == NULL) + { + cmn_err (CE_CONT, "udi_usb_rcv_control_msg: usbdev==NULL\n"); + return OSS_EFAULT; + } + + setup.bmRequestType = rqtype | USB_DEV_REQ_DEV_TO_HOST; + setup.bRequest = rq; + setup.wValue = value; + setup.wIndex = index; + setup.wLength = len; + setup.attrs = 0; + + if ((err = usb_pipe_ctrl_xfer_wait (usbdev->dev_data->dev_default_ph, + &setup, + &data, + &completion_reason, + &cb_flags, 0)) != USB_SUCCESS) + { + char tmp[128], *s = tmp; + + sprintf (s, "Msg (rq=0x%02x, val=0x%04x, ix=0x%04x, len=%d): ", rq, + value, index, len); + cmn_err (CE_CONT, "%s\n", tmp); + cmn_err (CE_NOTE, "usb_pipe_ctrl_xfer_wait read failed: %s (%s)\n", + usb_errstr (err), usb_cb_err (completion_reason)); + freemsg (data); + return OSS_EIO; + } + + /*LINTED*/ l = data->b_wptr - data->b_rptr; + + memcpy (buf, data->b_rptr, l); + freemsg (data); + + return l; +} + +/* Endpoint/pipe access */ + +struct _udi_endpoint_handle_t +{ + usb_pipe_handle_t pipe_handle; + unsigned char *ep_desc; + udi_usb_devc *usbdev; +}; + +udi_endpoint_handle_t * +udi_open_endpoint (udi_usb_devc * usbdev, void *ep_desc) +{ + udi_endpoint_handle_t *h; + int err; + usb_pipe_policy_t policy; + + if ((h = KERNEL_MALLOC (sizeof (*h))) == NULL) + return NULL; + + policy.pp_max_async_reqs = 2; + + if ((err = usb_pipe_open (usbdev->osdev->dip, ep_desc, &policy, + USB_FLAGS_SLEEP, &h->pipe_handle)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_open() failed %d (%s)\n", err, + usb_cb_err (err)); + KERNEL_FREE (h); + return NULL; + } + + h->ep_desc = ep_desc; + h->usbdev = usbdev; + + return h; +} + +void +udi_close_endpoint (udi_endpoint_handle_t * h) +{ + usb_pipe_close (h->usbdev->osdev->dip, h->pipe_handle, USB_FLAGS_SLEEP, + NULL, 0); + KERNEL_FREE (h); +} + +int +udi_endpoint_get_num (udi_endpoint_handle_t * eph) +{ + return eph->ep_desc[2] & 0xff; +} + +/* Request stuff */ + +struct udi_usb_request +{ + dev_info_t *dip; + usb_pipe_handle_t pipe_handle; + udi_usb_complete_func_t callback; + void *callback_arg; + int active; + int xfer_type; + mblk_t *data; + + /* + * Recording + */ + int actlen; + + usb_isoc_req_t *isoc_req; + usb_bulk_req_t *bulk_req; + usb_intr_req_t *intr_req; +}; + + /*ARGSUSED*/ + udi_usb_request_t + * udi_usb_alloc_request (udi_usb_devc * usbdev, udi_endpoint_handle_t * eph, + int nframes, int xfer_type) +{ + udi_usb_request_t *req; + + if ((req = KERNEL_MALLOC (sizeof (*req))) == NULL) + { + cmn_err (CE_WARN, "udi_usb_alloc_request: Out of memory\n"); + return NULL; + } + + req->xfer_type = xfer_type; + req->dip = usbdev->osdev->dip; + req->pipe_handle = eph->pipe_handle; + + switch (xfer_type) + { + case UDI_USBXFER_ISO_READ: + return req; + break; + + case UDI_USBXFER_ISO_WRITE: + return req; + break; + + case UDI_USBXFER_BULK_READ: + return req; + break; + + case UDI_USBXFER_INTR_READ: + return req; + break; + + case UDI_USBXFER_BULK_WRITE: + return req; + break; + + default: + cmn_err (CE_WARN, "Internal error - bad transfer type %d\n", xfer_type); + KERNEL_FREE (req); + return NULL; + } +} + +void +udi_usb_free_request (udi_usb_request_t * req) +{ + if (req == NULL) + return; + + udi_usb_cancel_request (req); + + if (req->active) + { + cmn_err (CE_WARN, "Warning: Freeing active request\n"); + } + + switch (req->xfer_type) + { + case UDI_USBXFER_ISO_WRITE: + req->isoc_req = NULL; + break; + + case UDI_USBXFER_ISO_READ: + req->isoc_req = NULL; + break; + + case UDI_USBXFER_BULK_WRITE: + req->bulk_req = NULL; + break; + + case UDI_USBXFER_BULK_READ: + req->bulk_req = NULL; + break; + + case UDI_USBXFER_INTR_READ: + req->intr_req = NULL; + break; + + default: + cmn_err (CE_WARN, "Internal error - bad transfer type %d\n", + req->xfer_type); + } + + KERNEL_FREE (req); +} + +/*ARGSUSED*/ +static void +isoc_play_callback (usb_pipe_handle_t ph, usb_isoc_req_t * isoc_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) isoc_req->isoc_client_private; + + usb_free_isoc_req (isoc_req); + + req->isoc_req = NULL; + req->active = 0; + + req->callback (req, req->callback_arg); +} + +/*ARGSUSED*/ +static void +isoc_rec_callback (usb_pipe_handle_t ph, usb_isoc_req_t * isoc_req) +{ + int i; + udi_usb_request_t *req = + (udi_usb_request_t *) isoc_req->isoc_client_private; + + req->data = isoc_req->isoc_data; + + for (i = 0; i < isoc_req->isoc_pkts_count; i++) + { + int len; + + len = isoc_req->isoc_pkt_descr[i].isoc_pkt_actual_length; + + req->actlen = len; + + req->callback (req, req->callback_arg); + + req->data->b_rptr += len; + } + + req->active = 0; + usb_free_isoc_req (isoc_req); +} + +/*ARGSUSED*/ +static void +isoc_exc_callback (usb_pipe_handle_t ph, usb_isoc_req_t * isoc_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) isoc_req->isoc_client_private; + usb_cr_t reason; + + reason = isoc_req->isoc_completion_reason; + + usb_free_isoc_req (isoc_req); + req->isoc_req = NULL; + req->active = 0; + + if (reason && reason != USB_CR_FLUSHED && reason != USB_CR_PIPE_CLOSING) + cmn_err (CE_CONT, "USB isoc transfer completion status %s\n", + usb_cr_err (reason)); +} + +/*ARGSUSED*/ +void +bulk_play_callback (usb_pipe_handle_t ph, usb_bulk_req_t * bulk_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) bulk_req->bulk_client_private; + + usb_free_bulk_req (bulk_req); + + req->bulk_req = NULL; + req->active = 0; + + req->callback (req, req->callback_arg); +} + +/*ARGSUSED*/ +void +bulk_rec_callback (usb_pipe_handle_t ph, usb_bulk_req_t * bulk_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) bulk_req->bulk_client_private; + + req->data = bulk_req->bulk_data; + req->actlen = bulk_req->bulk_len; + req->active = 0; + + req->callback (req, req->callback_arg); + //usb_free_bulk_req (bulk_req); +} + +/*ARGSUSED*/ +static void +bulk_exc_callback (usb_pipe_handle_t ph, usb_bulk_req_t * bulk_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) bulk_req->bulk_client_private; + usb_cr_t reason; + + reason = bulk_req->bulk_completion_reason; + + usb_free_bulk_req (bulk_req); + req->bulk_req = NULL; + req->active = 0; + + if (reason && reason != USB_CR_FLUSHED && reason != USB_CR_PIPE_CLOSING) + cmn_err (CE_CONT, "USB bulk transfer completion status %s\n", + usb_cr_err (reason)); +} + +/*ARGSUSED*/ +void +intr_rec_callback (usb_pipe_handle_t ph, usb_intr_req_t * intr_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) intr_req->intr_client_private; + + req->data = intr_req->intr_data; + req->actlen = intr_req->intr_len; + req->active = 0; + + req->callback (req, req->callback_arg); + //usb_free_intr_req (intr_req); +} + +/*ARGSUSED*/ +static void +intr_exc_callback (usb_pipe_handle_t ph, usb_intr_req_t * intr_req) +{ + udi_usb_request_t *req = + (udi_usb_request_t *) intr_req->intr_client_private; + usb_cr_t reason; + + reason = intr_req->intr_completion_reason; + + usb_free_intr_req (intr_req); + req->intr_req = NULL; + req->active = 0; + + if (reason && reason != USB_CR_FLUSHED && reason != USB_CR_PIPE_CLOSING) + cmn_err (CE_CONT, "USB intr transfer completion status %s\n", + usb_cr_err (reason)); +} + +/*ARGSUSED*/ +int +udi_usb_submit_request (udi_usb_request_t * req, + udi_usb_complete_func_t callback, void *callback_arg, + udi_endpoint_handle_t * eph, int xfer_type, + void *data, int len) +{ + int err; + + req->callback = callback; + req->callback_arg = callback_arg; + + if (req->active) + return 0; + + switch (xfer_type) + { + case UDI_USBXFER_ISO_WRITE: + { + usb_isoc_req_t *isoc_req; + + if (req->isoc_req != NULL) + isoc_req = req->isoc_req; + else + { + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (isoc) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + + if ((isoc_req = usb_alloc_isoc_req (req->dip, 1, 0, 0)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_isoc_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->isoc_req = isoc_req; + } + + if (isoc_req == NULL) + { + cmn_err (CE_WARN, "req->isoc==NULL\n"); + return OSS_EIO; + } + + memcpy (req->data->b_wptr, data, len); + req->data->b_wptr += len; + + isoc_req->isoc_data = req->data; + isoc_req->isoc_pkt_descr[0].isoc_pkt_length = len; + isoc_req->isoc_pkts_count = 1; + isoc_req->isoc_pkts_length = len; + isoc_req->isoc_attributes = + USB_ATTRS_ISOC_XFER_ASAP | USB_ATTRS_AUTOCLEARING; + isoc_req->isoc_cb = isoc_play_callback; + isoc_req->isoc_exc_cb = isoc_exc_callback; + isoc_req->isoc_client_private = (usb_opaque_t) req; + + if ((err = + usb_pipe_isoc_xfer (req->pipe_handle, isoc_req, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_isoc_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + req->active = 1; + } + break; + + case UDI_USBXFER_ISO_READ: + { + usb_isoc_req_t *isoc_req; + + if (req->isoc_req != NULL) + isoc_req = req->isoc_req; + else + { + if ((isoc_req = + usb_alloc_isoc_req (req->dip, 1, len, + USB_FLAGS_SLEEP)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_isoc_req failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->isoc_req = isoc_req; + } + + if (isoc_req == NULL) + { + cmn_err (CE_WARN, "req->isoc==NULL\n"); + return OSS_EIO; + } + + isoc_req->isoc_attributes = USB_ATTRS_ISOC_XFER_ASAP; + isoc_req->isoc_cb = isoc_rec_callback; + isoc_req->isoc_exc_cb = isoc_exc_callback; + isoc_req->isoc_client_private = (usb_opaque_t) req; + isoc_req->isoc_pkt_descr[0].isoc_pkt_length = len; + isoc_req->isoc_pkts_count = 1; + isoc_req->isoc_pkts_length = len; + req->active = 1; + + if ((err = + usb_pipe_isoc_xfer (req->pipe_handle, isoc_req, + USB_FLAGS_NOSLEEP)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_isoc_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + } + break; + + case UDI_USBXFER_BULK_WRITE: + { + usb_bulk_req_t *bulk_req; + + if (req->bulk_req != NULL) + bulk_req = req->bulk_req; + else + { + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (bulk) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + + if ((bulk_req = usb_alloc_bulk_req (req->dip, len, 0)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_bulk_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->bulk_req = bulk_req; + } + + if (bulk_req == NULL) + { + cmn_err (CE_WARN, "req->bulk==NULL\n"); + return OSS_EIO; + } + + memcpy (req->data->b_wptr, data, len); + req->data->b_wptr += len; + + bulk_req->bulk_data = req->data; + bulk_req->bulk_len = len; + bulk_req->bulk_timeout = 5; /* 5 seconds */ + bulk_req->bulk_attributes = USB_ATTRS_AUTOCLEARING; + bulk_req->bulk_cb = bulk_play_callback; + bulk_req->bulk_exc_cb = bulk_exc_callback; + bulk_req->bulk_client_private = (usb_opaque_t) req; + + if ((err = + usb_pipe_bulk_xfer (req->pipe_handle, bulk_req, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_bulk_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + req->active = 1; + } + break; + + case UDI_USBXFER_BULK_READ: + { + usb_bulk_req_t *bulk_req; + + if (req->bulk_req != NULL) + bulk_req = req->bulk_req; + else + { +#if 0 + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (bulk) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } +#endif + + if ((bulk_req = + usb_alloc_bulk_req (req->dip, len, USB_FLAGS_SLEEP)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_bulk_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->bulk_req = bulk_req; + } + + if (bulk_req == NULL) + { + cmn_err (CE_WARN, "req->bulk==NULL\n"); + return OSS_EIO; + } + + bulk_req->bulk_attributes = USB_ATTRS_SHORT_XFER_OK; + bulk_req->bulk_cb = bulk_rec_callback; + bulk_req->bulk_exc_cb = bulk_exc_callback; + bulk_req->bulk_client_private = (usb_opaque_t) req; + // bulk_req->bulk_data = data; + bulk_req->bulk_len = len; + bulk_req->bulk_timeout = 0x7fffffff; /* As long as possible */ + req->active = 1; + + if ((err = + usb_pipe_bulk_xfer (req->pipe_handle, bulk_req, + USB_FLAGS_NOSLEEP)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_bulk_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + } + break; + + case UDI_USBXFER_INTR_READ: + { + usb_intr_req_t *intr_req; + + if (req->intr_req != NULL) + intr_req = req->intr_req; + else + { +#if 0 + req->data = allocb (len, BPRI_HI); + if (req->data == NULL) + { + cmn_err (CE_WARN, "allocb_wait (intr) failed\n"); + KERNEL_FREE (req); + return OSS_ENOMEM; + } +#endif + + if ((intr_req = + usb_alloc_intr_req (req->dip, len, USB_FLAGS_SLEEP)) == NULL) + { + cmn_err (CE_WARN, "usb_alloc_intr_req failed\n"); + freemsg (req->data); + KERNEL_FREE (req); + return OSS_ENOMEM; + } + req->intr_req = intr_req; + } + + if (intr_req == NULL) + { + cmn_err (CE_WARN, "req->intr==NULL\n"); + return OSS_EIO; + } + + intr_req->intr_attributes = + USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_ONE_XFER; + intr_req->intr_cb = intr_rec_callback; + intr_req->intr_exc_cb = intr_exc_callback; + intr_req->intr_client_private = (usb_opaque_t) req; + intr_req->intr_data = NULL; + intr_req->intr_len = len; + intr_req->intr_timeout = 0x7fffffff; /* As long as possible */ + req->active = 1; + + if ((err = + usb_pipe_intr_xfer (req->pipe_handle, intr_req, + 0)) != USB_SUCCESS) + { + cmn_err (CE_WARN, "usb_pipe_intr_xfer failed (%s)\n", + usb_errstr (err)); + return OSS_EIO; + } + } + break; + + default: + cmn_err (CE_WARN, "Unimplemented transfer type %d\n", xfer_type); + return OSS_EIO; + } + + return 0; +} + +int +udi_usb_request_actlen (udi_usb_request_t * request) +{ + return request->actlen; +} + +unsigned char * +udi_usb_request_actdata (udi_usb_request_t * request) +{ + return request->data->b_rptr; +} + +void +udi_usb_cancel_request (udi_usb_request_t * req) +{ + if (req == NULL || !req->active) + return; + + req->active = 0; + usb_pipe_reset (req->dip, req->pipe_handle, USB_FLAGS_SLEEP, NULL, 0); + + switch (req->xfer_type) + { + case UDI_USBXFER_ISO_WRITE: + break; + + case UDI_USBXFER_ISO_READ: + usb_pipe_stop_isoc_polling (req->pipe_handle, USB_FLAGS_SLEEP); + //if (req->isoc_req!=NULL) + //usb_free_isoc_req(req->isoc_req); + req->isoc_req = NULL; + break; + + case UDI_USBXFER_BULK_WRITE: + break; + + case UDI_USBXFER_BULK_READ: + req->bulk_req = NULL; + break; + + case UDI_USBXFER_INTR_READ: + req->intr_req = NULL; + break; + + default: + cmn_err (CE_WARN, "Internal error - bad transfer type %d\n", + req->xfer_type); + } +} +#endif diff --git a/kernel/OS/VxWorks/.config b/kernel/OS/VxWorks/.config new file mode 100644 index 0000000..816ac62 --- /dev/null +++ b/kernel/OS/VxWorks/.config @@ -0,0 +1 @@ +mode=kernel diff --git a/kernel/OS/VxWorks/linux/ioctl.h b/kernel/OS/VxWorks/linux/ioctl.h new file mode 100644 index 0000000..937d84c --- /dev/null +++ b/kernel/OS/VxWorks/linux/ioctl.h @@ -0,0 +1,5 @@ +/* + * This file is currently required when cross compiling + * for VxWorks under Linux. + */ +#include <sys/ioctl.h> diff --git a/kernel/OS/VxWorks/module.inc b/kernel/OS/VxWorks/module.inc new file mode 100644 index 0000000..31f17f0 --- /dev/null +++ b/kernel/OS/VxWorks/module.inc @@ -0,0 +1,127 @@ +#if DRIVER_TYPE == DRV_PCI +#include <drv/pci/pciConfigLib.h> +#undef PCI_LATENCY_TIMER +#include <oss_pci.h> + +int +oss_pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + oss_pci_device_t *pd = osdev->dip; + + return pciConfigInByte (pd->bus, pd->dev, pd->func, where, val); +} + +int +oss_pci_read_config_irq (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + oss_pci_device_t *pd = osdev->dip; + + return pciConfigInByte (pd->bus, pd->dev, pd->func, where, val); +} + +int +oss_pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + oss_pci_device_t *pd = osdev->dip; + + if (osdev == NULL) + { + cmn_err (CE_CONT, "oss_pci_read_config_word: osdev==NULL\n"); + return PCIBIOS_FAILED; + } + + return pciConfigInWord (pd->bus, pd->dev, pd->func, where, val); +} + +int +oss_pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + oss_pci_device_t *pd = osdev->dip; + + return pciConfigInLong (pd->bus, pd->dev, pd->func, where, val); +} + +int +oss_pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + oss_pci_device_t *pd = osdev->dip; + + return pciConfigOutByte (pd->bus, pd->dev, pd->func, where, val); +} + +int +oss_pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + oss_pci_device_t *pd = osdev->dip; + + return pciConfigOutWord (pd->bus, pd->dev, pd->func, where, val); +} + +int +oss_pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + oss_pci_device_t *pd = osdev->dip; + + return pciConfigOutLong (pd->bus, pd->dev, pd->func, where, val); +} +#endif + +int +DRIVER_NAME(void) +{ +#if DRIVER_TYPE == DRV_PCI + int i; + int bus, dev, func; + unsigned int d, vendor_id, dev_id; + static int instance = 0; + + if (id_table[0] == 0) + { + cmn_err (CE_WARN, DRIVER_NICK ": ID table is empty\n"); + return OSS_EIO; + } + + i=0; + + while ((d=id_table[i]) != 0) + { + int index=0; + vendor_id = (d >> 16) & 0xffff; + dev_id = d & 0xffff; + + while (pciFindDevice(vendor_id, dev_id, instance,&bus, &dev, &func) == OK) + { + oss_pci_device_t *pcidev = malloc(sizeof(*pcidev)); + oss_device_t *osdev; + + + cmn_err(CE_CONT, "Found pci device %08x / %d : b=%d, d=%d, f=%d\n", d, index, bus, dev, func); + + pcidev->bus = bus; + pcidev->dev = dev; + pcidev->func = func; + + if ((osdev = + osdev_create ((dev_info_t*)pcidev, DRIVER_TYPE, instance++, DRIVER_NICK, + NULL)) == NULL) + { + return OSS_ENOMEM; + } + + index++; + } + + i++; + } + +#endif + + return 0; +} diff --git a/kernel/OS/VxWorks/os_vxworks.c b/kernel/OS/VxWorks/os_vxworks.c new file mode 100644 index 0000000..a679e6c --- /dev/null +++ b/kernel/OS/VxWorks/os_vxworks.c @@ -0,0 +1,1133 @@ +/* + * oss_vxworks.c: Common entry points for OSS under VxWorks. + */ + +#include <oss_config.h> +#include <oss_pci.h> +#include <drv/pci/pciConfigLib.h> +#include <memLib.h> + +#if 0 +// TODO: Obsolete +typedef struct oss_device_handle +{ + DEV_HDR devHdr; + int minor; + int valid; /* 1=valid, 0=undefined */ + struct fileinfo finfo; +} +oss_device_handle; +#endif +/* + * Number of cards supported in the same system. + */ +#define MAX_CARDS 8 + +static oss_device_t *cards[MAX_CARDS]; +int oss_num_cards = 0; + +static int oss_driver_num = ERROR; +static int oss_expired = 0; +static oss_device_t *core_osdev = NULL; + +int oss_hz = 100; + +void +oss_cmn_err (int level, const char *s, ...) +{ + char tmp[1024], *a[6]; + va_list ap; + int i, n = 0; + + va_start (ap, s); + + for (i = 0; i < strlen (s); i++) + if (s[i] == '%') + n++; + + for (i = 0; i < n && i < 6; i++) + a[i] = ( (sizeof(char *) == 32) ? ( *((char * **)(ap += ((sizeof(char * *)+sizeof(int)-1) & ~(sizeof(int)-1))))[-1] ) : ( ((char * *)(ap += ((sizeof(char *)+sizeof(int)-1) & ~(sizeof(int)-1))))[-1] )); + //a[i] = va_arg (ap, char *); // This was supposed to be used instead of above. Unfortunately va_arg() seems to be buggy + + for (i = n; i < 6; i++) + a[i] = NULL; + + if (level == CE_CONT) + { + sprintf (tmp, s, a[0], a[1], a[2], a[3], a[4], a[5], NULL, + NULL, NULL, NULL); + printf ("%s", tmp); + } + else + { + strcpy (tmp, "osscore: "); + sprintf (tmp + strlen (tmp), s, a[0], a[1], a[2], a[3], a[4], a[5], + NULL, NULL, NULL, NULL); + + printf ("%s", tmp); + } + + va_end (ap); +} + +int +oss_uiomove (void *addr, size_t nbytes, enum uio_rw rwflag, uio_t * uio) +{ +/* + * NOTE! Returns 0 upon success and EFAULT on failure (instead of -EFAULT + * (for Solaris/BSD compatibility)). + */ + + if (rwflag != uio->rw) + { + oss_cmn_err (CE_WARN, "uiomove: Bad direction\n"); + return EFAULT; + } + + if (uio->resid < nbytes) + { + oss_cmn_err (CE_WARN, "uiomove: Bad count %d (%d)\n", nbytes, + uio->resid); + return EFAULT; + } + + if (uio->kernel_space) + return EFAULT; + + switch (rwflag) + { + case UIO_READ: + memcpy(uio->ptr, addr, nbytes); + break; + + case UIO_WRITE: + memcpy(addr, uio->ptr, nbytes); + break; + } + + uio->resid -= nbytes; + uio->ptr += nbytes; + + return 0; +} + +int +oss_create_uio (uio_t * uio, char *buf, size_t count, uio_rw_t rw, + int is_kernel) +{ + memset (uio, 0, sizeof (*uio)); + + if (is_kernel) + { + oss_cmn_err (CE_CONT, + "oss_create_uio: Kernel space buffers not supported\n"); + return -EIO; + } + + uio->ptr = buf; + uio->resid = count; + uio->kernel_space = is_kernel; + uio->rw = rw; + + return 0; +} + +static int +grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int increment) +{ + oss_cdev_t **old=*arr, **new = *arr; + int old_size = *size; + int new_size = *size; + + new_size += increment; + + if ((new=PMALLOC(osdev, new_size * sizeof (oss_cdev_t *)))==NULL) + return 0; + + memset(new, 0, new_size * sizeof(oss_cdev_t *)); + if (old != NULL) + memcpy(new, old, old_size * sizeof(oss_cdev_t *)); + + *size = new_size; + *arr = new; + + if (old != NULL) + PMFREE(osdev, old); + + return 1; +} + +static void +register_chrdev(oss_cdev_t *cdev, char *name) +{ + if (iosDevAdd ((void *)cdev, name, oss_driver_num) == ERROR) + { + cmn_err (CE_WARN, "Failed to add device %s\n", name); + } +} + +void +oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, + int instance, oss_cdev_drv_t * drv, int flags) +{ +/* + * oss_install_chrdev creates a character device (minor). However if + * name==NULL the device will not be exported (made visible to userland + * clients). + */ + + int num; + oss_cdev_t *cdev = NULL; + + if (dev_class != OSS_DEV_STATUS) + if (oss_expired && instance > 0) + return; + + if (oss_num_cdevs >= OSS_MAX_CDEVS) + { + if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, 100)) + { + cmn_err (CE_WARN, "Out of minor numbers.\n"); + return; + } + } + + if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); + return; + } + + num = oss_num_cdevs++; + + memset (cdev, 0, sizeof (*cdev)); + cdev->dev_class = dev_class; + cdev->instance = instance; + cdev->d = drv; + cdev->osdev = osdev; + if (name != NULL) + strncpy (cdev->name, name, sizeof (cdev->name)); + else + strcpy (cdev->name, "NONE"); + cdev->name[sizeof (cdev->name) - 1] = 0; + oss_cdevs[num] = cdev; + +/* + * Export the device only if name != NULL + */ + if (name != NULL) + { + strcpy (cdev->name, name); + register_chrdev (cdev, name); + } +} + +int +oss_find_minor (int dev_class, int instance) +{ + int i; + + for (i = 0; i < oss_num_cdevs; i++) + { + if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class + && oss_cdevs[i]->instance == instance) + return i; + } + + return OSS_ENXIO; +} + +static inline int +cpy_file (int mode, struct fileinfo *fi) +{ + fi->mode = 0; + fi->acc_flags = mode; + + if ((fi->acc_flags & O_ACCMODE) == O_RDWR) + fi->mode = OPEN_READWRITE; + if ((fi->acc_flags & O_ACCMODE) == O_RDONLY) + fi->mode = OPEN_READ; + if ((fi->acc_flags & O_ACCMODE) == O_WRONLY) + fi->mode = OPEN_WRITE; + + return fi->mode; +} + +static void * +ossOpen (oss_cdev_t *cdev, char *reminder, int mode) +{ + int tmpdev, retval; + struct fileinfo fi; + + cpy_file (mode, &fi); + + DDB (cmn_err + (CE_CONT, "ossOpen(%p): %s, class=%d, instance=%d\n", cdev, + cdev->name, cdev->dev_class, cdev->instance)); + + if (cdev->d->open == NULL) + { + errnoSet(ENODEV); + return (void*)ERROR; + } + + tmpdev = -1; + retval = + cdev->d->open (cdev->instance, cdev->dev_class, &fi, 0, 0, &tmpdev); + + if (retval < 0) + { + errnoSet(-retval); + return (void*)ERROR; + } + + if (tmpdev != -1) + { + if (tmpdev >= 0 && tmpdev < oss_num_cdevs) + { + cdev = oss_cdevs[tmpdev]; + } + else + { + errnoSet(ENODEV); + return (void*)ERROR; + } + } + + errnoSet (0); + memcpy(&cdev->file, &fi, sizeof(struct fileinfo)); + + return cdev; +} + +static int +ossClose (oss_cdev_t *cdev) +{ + if (cdev->d->close == NULL) + { + return OK; + } + + cdev->d->close (cdev->instance, &cdev->file); + + return OK; +} + +static int +ossRead (oss_cdev_t *cdev, char *buf, int count) +{ + int err, len; + uio_t uio; + + if (cdev->d->read == NULL) + { + errnoSet (ENXIO); + return ERROR; + } + + if ((err = oss_create_uio (&uio, buf, count, UIO_READ, 0)) < 0) + { + errnoSet (-err); + return ERROR; + } + + len = cdev->d->read (cdev->instance, &cdev->file, &uio, count); + + if (len >= 0) + return len; + + errnoSet (-len); + return ERROR; +} + +static int +ossWrite (oss_cdev_t *cdev, char *buf, int count) +{ + int err, len; + uio_t uio; + + if (cdev->d->write == NULL) + { + errnoSet (ENXIO); + return ERROR; + } + + if ((err = oss_create_uio (&uio, buf, count, UIO_WRITE, 0)) < 0) + { + errnoSet (-err); + return ERROR; + } + + len = cdev->d->write (cdev->instance, &cdev->file, &uio, count); + + if (len >= 0) + return len; + + errnoSet (-len); + return ERROR; +} + +static int +ossIoctl (oss_cdev_t *cdev, int cmd, int *arg) +{ + int err; + + if (cdev->d->ioctl == NULL) + { + errnoSet (ENXIO); + return ERROR; + } + + if ((err = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) arg)) < 0) + { + errnoSet (-err); + return ERROR; + } + return OK; +} + +oss_device_t * +osdev_create (dev_info_t * dip, int dev_type, + int instance, const char *nick, const char *handle) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + + memset (osdev, 0, sizeof (*osdev)); + + sprintf (osdev->nick, "%s%d", nick, instance); + osdev->instance = instance; + osdev->dip = dip; + osdev->available = 1; + osdev->first_mixer = -1; + + strcpy (osdev->modname, nick); + + if (handle == NULL) + handle = nick; + + if (oss_num_cards >= MAX_CARDS) + cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", + MAX_CARDS); + else + { + osdev->cardnum = oss_num_cards; + cards[oss_num_cards++] = osdev; + } +/* + * Create the device handle + */ + switch (dev_type) + { + case DRV_PCI: + { +#if 0 + // TODO + unsigned int subvendor; + char *devpath; + devpath = oss_pci_read_devpath (osdev->dip); + oss_pci_read_config_dword (osdev, 0x2c, &subvendor); + + sprintf (osdev->handle, "PCI%08x-%s", subvendor, devpath); +#else + strcpy(osdev->handle, "PCICARD"); +#endif + } + break; + + case DRV_USB: + // TODO: Get the vendor information + sprintf (osdev->handle, "USB-%s%d", handle, instance); + break; + + default: + sprintf (osdev->handle, "%s%d", handle, instance); + } + + return osdev; +} + +oss_device_t * +osdev_clone (oss_device_t * orig_osdev, int new_instance) +{ + oss_device_t *osdev; + + osdev = PMALLOC (NULL, sizeof (*osdev)); + if (osdev == NULL) + { + cmn_err (CE_WARN, "osdev_create: Out of memory\n"); + return NULL; + } + memcpy (osdev, orig_osdev, sizeof (*osdev)); + osdev->dev_type = DRV_CLONE; + osdev->instance = new_instance; + sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); + sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); + + return osdev; +} + +void +osdev_delete (oss_device_t * osdev) +{ + int i; + + if (osdev == NULL) + return; + + osdev->available = 0; +/* + * Mark all minor nodes for this module as invalid. + */ + for (i = 0; i < oss_num_cdevs; i++) + if (oss_cdevs[i]->osdev == osdev) + { + oss_cdevs[i]->d = NULL; + oss_cdevs[i]->osdev = NULL; + strcpy (oss_cdevs[i]->name, "Removed device"); + } +} + +void * +oss_get_osid (oss_device_t * osdev) +{ + return NULL; // TODO +} + +int +oss_register_device (oss_device_t * osdev, const char *name) +{ + if (name == NULL) + { + cmn_err (CE_WARN, "oss_register_device: name==NULL\n"); + osdev->name = "Undefined name"; + return 0; + } + + if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) + { + cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); + osdev->name = "Unknown device"; + } + strcpy (osdev->name, name); + return 0; +} + +void +oss_unregister_device (oss_device_t * osdev) +{ +/* + * Notice! The driver calling this routine (the owner of the osdev parameter) + * has already uninitialized itself. Do not do any actions that may call this + * driver directly or indirectly. + */ + +// TODO: Move this to some common OSS module (also under Solaris) +} + +int +oss_get_cardinfo (int cardnum, oss_card_info * ci) +{ +/* + * Print information about a 'card' in a format suitable for /dev/sndstat + */ + + if (cardnum < 0 || cardnum >= oss_num_cards) + return OSS_ENXIO; + + if (cards[cardnum]->name != NULL) + strncpy (ci->longname, cards[cardnum]->name, 128); + ci->longname[127] = 0; + + if (cards[cardnum]->nick != NULL) + strncpy (ci->shortname, cards[cardnum]->nick, 16); + ci->shortname[15] = 0; + + if (cards[cardnum]->hw_info != NULL) + strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info)); + ci->hw_info[sizeof (ci->hw_info) - 1] = 0; + ci->intr_count = cards[cardnum]->intrcount; + ci->ack_count = cards[cardnum]->ackcount; + + return 0; +} + +int +ossDrv (void) +{ + oss_hz = sysClkRateGet(); + + if (oss_driver_num != ERROR) + { + cmn_err (CE_WARN, "OSS is already running\n"); + return -1; + } + +#ifdef LICENSED_VERSION + if (!oss_license_handle_time (oss_get_time ())) + { + cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); + cmn_err (CE_CONT, + "Please download the latest version from www.opensound.com\n"); + oss_expired = 1; + return -1; + } +#endif + + oss_driver_num = iosDrvInstall ((FUNCPTR) NULL, /* create */ + (FUNCPTR) NULL, /* delete */ + (FUNCPTR) ossOpen, (FUNCPTR) ossClose, (FUNCPTR) ossRead, (FUNCPTR) ossWrite, (FUNCPTR) ossIoctl /* ioctl */ + ); + if (oss_driver_num == ERROR) + { + cmn_err (CE_WARN, "Module osscore failed to install\n"); + return -1; + } + + if ((core_osdev = + osdev_create (NULL, DRV_UNKNOWN, 0, "osscore", NULL)) == NULL) + { + oss_cmn_err (CE_WARN, "Failed to allocate OSDEV structure\n"); + return -1; + } + oss_register_device (core_osdev, "OSS core services"); + + oss_common_init (core_osdev); + + return oss_driver_num; +} + +int +ossDrvRemove (void) +{ +#if 1 + + return ERROR; +#else + int i; + + if (oss_driver_num == ERROR) + return 0; + + for (i = 0; i < SND_NDEVS; i++) + if (oss_files[i].valid) + { + iosDevDelete (&oss_files[i].devHdr); + oss_files[i].valid = 0; + } + + if (iosDrvRemove (oss_driver_num, FALSE) == ERROR) + { + cmn_err (CE_WARN, "Driver busy - cannot remove.\n"); + return ERROR; + } + + // TODO + oss_unload_drivers (); + + oss_driver_num = ERROR; /* Mark it free */ + return OK; +#endif +} + +#ifdef CONFIG_OSS_VMIX_FLOAT + +#undef FP_SAVE +#undef FP_RESTORE +#define FP_SAVE(envbuf) asm ("fnsave %0":"=m" (*envbuf)); +#define FP_RESTORE(envbuf) asm ("frstor %0":"=m" (*envbuf)); + +/* SSE/SSE2 compatible macros */ +#define FX_SAVE(envbuf) asm ("fxsave %0":"=m" (*envbuf)); +#define FX_RESTORE(envbuf) asm ("fxrstor %0":"=m" (*envbuf)); + +static int old_arch = 0; /* No SSE/SSE2 instructions */ + +#if defined(__amd64__) +#define AMD64 +#endif + +static inline void +cpuid (int op, int *eax, int *ebx, int *ecx, int *edx) +{ +__asm__ ("cpuid": "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx):"0" (op), "c" + (0)); +} + +#ifdef AMD64 +# define local_save_flags(x) asm volatile("pushfq ; popq %0":"=g" (x):) +# define local_restore_flags(x) asm volatile("pushq %0 ; popfq"::"g" (x):"memory", "cc") +#else +# define local_save_flags(x) asm volatile("pushfl ; popl %0":"=g" (x):) +# define local_restore_flags(x) asm volatile("pushl %0 ; popfl"::"g" (x):"memory", "cc") +#endif + +static inline unsigned long +read_cr0 (void) +{ + unsigned long cr0; +#ifdef AMD64 +asm ("movq %%cr0,%0":"=r" (cr0)); +#else +asm ("movl %%cr0,%0":"=r" (cr0)); +#endif + return cr0; +} + +static inline void +write_cr0 (unsigned long val) +{ +#ifdef AMD64 + asm ("movq %0,%%cr0"::"r" (val)); +#else + asm ("movl %0,%%cr0"::"r" (val)); +#endif +} + +static inline unsigned long +read_cr4 (void) +{ + unsigned long cr4; +#ifdef AMD64 +asm ("movq %%cr4,%0":"=r" (cr4)); +#else +asm ("movl %%cr4,%0":"=r" (cr4)); +#endif + return cr4; +} + +static inline void +write_cr4 (unsigned long val) +{ +#ifdef AMD64 + asm ("movq %0,%%cr4"::"r" (val)); +#else + asm ("movl %0,%%cr4"::"r" (val)); +#endif +} +static inline unsigned long long +read_mxcsr (void) +{ + unsigned long long mxcsr; + asm volatile ("stmxcsr %0":"=m" (mxcsr)); + return mxcsr; +} + +static inline void +write_mxcsr (unsigned long long val) +{ + asm volatile ("ldmxcsr %0"::"m" (val)); +} + +int +oss_fp_check (void) +{ + int eax, ebx, ecx, edx; +#define FLAGS_ID (1<<21) + + oss_native_word flags_reg; + + local_save_flags (flags_reg); + flags_reg &= ~FLAGS_ID; + local_restore_flags (flags_reg); + + local_save_flags (flags_reg); + if (flags_reg & FLAGS_ID) + return 0; + + flags_reg |= FLAGS_ID; + local_restore_flags (flags_reg); + + local_save_flags (flags_reg); + if (!(flags_reg & FLAGS_ID)) + return 0; + +//#define CPUID_FXSR (1<<24) +//#define CPUID_SSE (1<<25) +//#define CPUID_SSE2 (1<<26) + + cpuid (1, &eax, &ebx, &ecx, &edx); + + if (!(edx & CPUID_FXSR)) + return -1; + + /* + * Older machines require different FP handling than the latest ones. Use the SSE + * instruction set as an indicator. + */ + if (!(edx & CPUID_SSE)) + old_arch = 1; + + return 1; +} + +void +oss_fp_save (short *envbuf, unsigned int flags[]) +{ + flags[0] = read_cr0 (); + write_cr0 (flags[0] & ~0x0e); /* Clear CR0.TS/MP/EM */ + + if (old_arch) + { + FP_SAVE (envbuf); + } + else + { + flags[1] = read_cr4 (); + write_cr4 (flags[1] | 0x600); /* Set OSFXSR & OSXMMEXCEPT */ + FX_SAVE (envbuf); + asm ("fninit"); + asm ("fwait"); + write_mxcsr (0x1f80); + } + flags[2] = read_cr0 (); +} + +void +oss_fp_restore (short *envbuf, unsigned int flags[]) +{ + asm ("fwait"); + if (old_arch) + { + FP_RESTORE (envbuf); + } + else + { + FX_RESTORE (envbuf); + write_cr4 (flags[1]); /* Restore cr4 */ + } + write_cr0 (flags[0]); /* Restore cr0 */ +} +#endif + +typedef struct tmout_desc +{ + volatile int active; + int timestamp; + void (*func) (void *); + void *arg; + + WDOG_ID id; + +} tmout_desc_t; + +static volatile int next_id = 0; +#define MAX_TMOUTS 128 + +tmout_desc_t tmouts[MAX_TMOUTS] = { {0} }; + +int timeout_random = 0x12123400; + +void +oss_timer_callback (int id) +{ + tmout_desc_t *tmout; + int ix; + void *arg; + + timeout_random++; + + ix = id & 0xff; + if (ix < 0 || ix >= MAX_TMOUTS) + return; + tmout = &tmouts[ix]; + + if (tmout->timestamp != id) /* Expired timer */ + return; + + if (!tmout->active) + return; + + arg = tmout->arg; + tmout->active = 0; + tmout->timestamp = 0; + + tmout->func (arg); + wdDelete(tmout->id); +} + +timeout_id_t +oss_timeout (void (*func) (void *), void *arg, + unsigned long long ticks) +{ + + tmout_desc_t *tmout = NULL; + int id, n; + + timeout_random++; + + n = 0; + id = -1; + + while (id == -1 && n < MAX_TMOUTS) + { + if (!tmouts[next_id].active) + { + tmouts[next_id].active = 1; + id = next_id++; + tmout = &tmouts[id]; + break; + } + + next_id = (next_id + 1) % MAX_TMOUTS; + } + + if (id == -1) /* No timer slots available */ + { + oss_cmn_err (CE_WARN, "Timeout table full\n"); + return 0; + } + + tmout->func = func; + tmout->arg = arg; + tmout->timestamp = id | (timeout_random & ~0xff); + + if ((tmout->id=wdCreate()) == NULL) + return 0; + + wdStart(tmout->id, ticks, (FUNCPTR)oss_timer_callback, (int)tmout->timestamp); + return id | (timeout_random & ~0xff); +} + +void +oss_untimeout (timeout_id_t id) +{ + tmout_desc_t *tmout; + int ix; + + ix = id & 0xff; + if (ix < 0 || ix >= MAX_TMOUTS) + return; + + timeout_random++; + tmout = &tmouts[ix]; + + if (tmout->timestamp != id) /* Expired timer */ + return; + if (tmout->active) + { + wdCancel(tmout->id); + wdDelete(tmout->id); + } + + tmout->active = 0; + tmout->timestamp = 0; +} + +void +oss_udelay(unsigned long ticks) +{ + // TODO +} + +void * +oss_contig_malloc (oss_device_t * osdev, int buffsize, oss_uint64_t memlimit, + oss_native_word * phaddr) +{ + char *start_addr, *end_addr; + + *phaddr = 0; + + start_addr = NULL; + + // TODO: See if there is a previously freed buffer available + + start_addr = (char *) valloc (buffsize); + + if (start_addr == NULL) + { + cmn_err (CE_NOTE, "Failed to allocate memory buffer of %d bytes\n", + buffsize); + return NULL; + } + else + { + /* make some checks */ + end_addr = start_addr + buffsize - 1; + } + + *phaddr = (oss_native_word)start_addr; + return start_addr; +} + +void +oss_contig_free (oss_device_t * osdev, void *p, int buffsize) +{ + if (p == NULL) + return; + + // TODO: Put the freed memory block to available list + cmn_err (CE_WARN, "Cannot free %d bytes of DMA buffer\n", buffsize); +} + +int +__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, + oss_uint64_t maxaddr, int direction) +{ + void *buf; + int err; + oss_native_word phaddr; + int size = 64 * 1024; + extern int dma_buffsize; + + if (dma_buffsize > 16 && dma_buffsize <= 128) + size = dma_buffsize * 1024; + + if (dmap->dmabuf != NULL) + return 0; /* Already done */ + + if (dmap == NULL) + { + cmn_err (CE_WARN, "oss_alloc_dmabuf: dmap==NULL\n"); + return OSS_EIO; + } + +/* + * Some applications and virtual drivers need shorter buffer. + */ + if (dmap->flags & DMAP_SMALLBUF) + { + size = SMALL_DMABUF_SIZE; + } + else if (dmap->flags & DMAP_MEDIUMBUF) + { + size = MEDIUM_DMABUF_SIZE; + } + + if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) + size = 32 * 1024; + + dmap->dmabuf = NULL; + dmap->buffsize = size; + + err = -1; + + while (err < 0 && dmap->dmabuf == NULL && dmap->buffsize >= 4 * 1024) + { + if ((buf = + oss_contig_malloc (dmap->osdev, dmap->buffsize, maxaddr, + &phaddr)) == NULL) + { + if ((dmap->buffsize = (dmap->buffsize / 2)) < 8 * 1024) + return OSS_ENOMEM; + cmn_err (CE_CONT, "Dropping DMA buffer size to %d bytes.\n", + dmap->buffsize); + continue; + } + + dmap->dmabuf = buf; + dmap->dmabuf_phys = phaddr; + + return 0; + } + + return OSS_ENOMEM; +} + +void +oss_free_dmabuf (int dev, dmap_p dmap) +{ + void *buf = dmap->dmabuf; + + if (dmap->dmabuf == NULL) + return; + + dmap->dmabuf = NULL; + oss_contig_free (NULL, buf, dmap->buffsize); + dmap->dmabuf_phys = 0; +} + +/* + * Sleep/wakeup + */ + +struct oss_wait_queue +{ + volatile int flags; + SEM_ID wq; +}; + +struct oss_wait_queue * +oss_create_wait_queue (oss_device_t * osdev, const char *name) +{ + struct oss_wait_queue *wq; + + if ((wq = malloc (sizeof (*wq))) == NULL) + { + oss_cmn_err (CE_WARN, "vmalloc(%d) failed (wq)\n", sizeof (*wq)); + return NULL; + } + wq->wq = semBCreate(SEM_Q_FIFO, SEM_EMPTY); + + return wq; +} + +void +oss_reset_wait_queue (struct oss_wait_queue *wq) +{ + // TODO: ? +} + +void +oss_remove_wait_queue (struct oss_wait_queue *wq) +{ + free (wq); +} + +int +oss_sleep (struct oss_wait_queue *wq, oss_mutex_t * mutex, int ticks, + oss_native_word * flags, unsigned int *status) +{ + int result; + + *status = 0; + + if (wq == NULL) + return 0; + + wq->flags = 0; + + if (ticks <= 0) + ticks = WAIT_FOREVER; + + result = + semTake(wq->wq, ticks); + + if (result == ERROR) /* Signal received */ + { + *status |= WK_SIGNAL; + return 1; + } + + if (!(wq->flags & WK_WAKEUP)) /* Timeout */ + { + return 0; + } + + return 1; +} + +int +oss_register_poll (struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, oss_poll_event_t * ev) +{ + // TODO: ? + return 0; +} + +void +oss_wakeup (struct oss_wait_queue *wq, oss_mutex_t * mutex, + oss_native_word * flags, short events) +{ + if (wq == NULL) + return; + + wq->flags |= WK_WAKEUP; + semFlush(wq->wq); +} diff --git a/kernel/OS/VxWorks/os_vxworks.h b/kernel/OS/VxWorks/os_vxworks.h new file mode 100644 index 0000000..275aa34 --- /dev/null +++ b/kernel/OS/VxWorks/os_vxworks.h @@ -0,0 +1,297 @@ +#ifndef _OS_H_ +#define _OS_H_ + +/* + * Purpose: OS specific definitions for VxWorks + * + */ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +#define OS_VERSION "5.5" + +#if (!defined(i386) && !defined(x86_64)) || defined(CONFIG_OSS_FIXDEPOINT) +// Floating point is not supported or it's disabled +#undef CONFIG_OSS_VMIX_FLOAT +#endif + +#define VDEV_SUPPORT +#undef USE_DEVICE_SUBDIRS + +#define __inline__ inline +#define __inline inline +#define EXTERN_C extern "C" + +/* + * Disable support for per-application features such as /dev/dsp device + * selection based on command name. Requires working GET_PROCESS_NAME + * macro implementation. + */ +#undef APPLIST_SUPPORT + +#undef ALLOW_BUFFER_MAPPING + +/* + * Some integer types + */ +#if defined(amd64) || defined(sparc) +typedef unsigned long long oss_native_word; /* Same as the address and status register size */ +#else +typedef unsigned long oss_native_word; /* Same as the address and status register size */ +#endif + +typedef long long oss_int64_t; /* Signed 64 bit integer */ +typedef unsigned long long oss_uint64_t; /* Unsigned 64 bit integer */ + +#include <stdarg.h> +#include <sys/types.h> +#include "vxWorks.h" +#include "iv.h" +#include "ioLib.h" +#include "fioLib.h" +#include "iosLib.h" +#include "wdLib.h" +#include "intLib.h" +#include "tickLib.h" +#include "errnoLib.h" +#include "sysLib.h" +#include "objLib.h" +#include "vmLib.h" +#include "semLib.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <oss_errno.h> +/* + * Mutexes + */ +typedef int oss_mutex_t; +#define MUTEX_INIT(osdev, mutex, hier) +#define MUTEX_CLEANUP(mutex) +#define MUTEX_ENTER_IRQDISABLE(mutex, flags) {flags=0;mutex=0;} +#define MUTEX_ENTER(mutex, flags) {flags=0;mutex=0;} +#define MUTEX_EXIT_IRQRESTORE(mutex, flags) (flags)++ +#define MUTEX_EXIT(mutex, flags) (flags)++ + +/* + * Fileinfo structure + */ +struct fileinfo +{ + int mode; /* Open mode */ + int acc_flags; +}; +#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->acc_flags & (flag) ? 1:0) + +/* + * Misc types + */ +typedef int oss_dma_handle_t; +typedef int oss_poll_event_t; +typedef int offset_t; + +/* + * uio_/uiomove() + */ +typedef enum uio_rw uio_rw_t; +typedef struct oss_uio +{ + char *ptr; + int resid; + int kernel_space; /* Set if this uio points to a kernel space buffer */ + uio_rw_t rw; +} uio_t; +extern int oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, + uio_t * uio_p); +extern int oss_create_uio (uio_t * uiop, char *buf, size_t count, uio_rw_t rw, + int is_kernel); +#define uiomove oss_uiomove + +/* + * Error handling + */ +#define cmn_err oss_cmn_err +extern int detect_trace; +#define DDB(x) if (detect_trace) x +extern void oss_cmn_err (int level, const char *format, ...); +#define CE_CONT 0 +#define CE_NOTE 1 +#define CE_WARN 2 +#define CE_PANIC 3 + +/* Busy wait routine */ +extern void oss_udelay(unsigned long ticks); +/* System wall timer access */ +#define GET_JIFFIES() tickGet() +extern inline unsigned int +__inb (unsigned short port) +{ + unsigned int _v; + __asm__ __volatile__ ("in" "b" " %" "w" "1,%" "b" "0":"=a" (_v):"d" (port), + "0" (0)); + return _v; +} +extern inline unsigned int +__inw (unsigned short port) +{ + unsigned int _v; + __asm__ __volatile__ ("in" "w" " %" "w" "1,%" "w" "0":"=a" (_v):"d" (port), + "0" (0)); + return _v; +} +extern inline unsigned int +__inl (unsigned short port) +{ + unsigned int _v; + __asm__ __volatile__ ("in" "l" " %" "w" "1,%" "" "0":"=a" (_v):"d" (port)); + return _v; +} + +extern inline void +__outb (unsigned char value, unsigned short port) +{ + __asm__ __volatile__ ("out" "b" " %" "b" "0,%" "w" "1"::"a" (value), + "d" (port)); +} +extern inline void +__outw (unsigned short value, unsigned short port) +{ + __asm__ __volatile__ ("out" "w" " %" "w" "0,%" "w" "1"::"a" (value), + "d" (port)); +} +extern inline void +__outl (unsigned int value, unsigned short port) +{ + __asm__ __volatile__ ("out" "l" " %" "0,%" "w" "1"::"a" (value), + "d" (port)); +} + +#define INB(osdev,a) __inb(a) +#define INW(osdev,a) __inw(a) +#define INL(osdev,a) __inl(a) + +#define OUTB(osdev, d, a) __outb(d, a) + +#define OUTW(osdev, d, a) __outw(d, a) +#define OUTL(osdev, d, a) __outl(d, a) + +#define PCI_READL(osdev, p) (*(volatile unsigned int *) (p)) +#define PCI_WRITEL(osdev, addr, data) (*(volatile unsigned int *) (addr) = (data)) +#define PCI_READW(osdev, p) (*(volatile unsigned short *) (p)) +#define PCI_WRITEW(osdev, addr, data) (*(volatile unsigned short *) (addr) = (data)) +#define PCI_READB(osdev, p) (*(volatile unsigned char *) (p)) +#define PCI_WRITEB(osdev, addr, data) (*(volatile unsigned char *) (addr) = (data)) + +#define MAP_PCI_IOADDR(osdev, nr, io) (oss_native_word)io +#define MAP_PCI_MEM(osdev, ix, phaddr, size) (oss_native_word)phaddr +#define UNMAP_PCI_MEM(osdev, ix, ph, virt, size) {} +#define UNMAP_PCI_IOADDR(osdev, ix) {} +/* + * Memory allocation and mapping + */ +extern void *oss_contig_malloc (oss_device_t * osdev, int sz, + oss_uint64_t memlimit, + oss_native_word * phaddr); +extern void oss_contig_free (oss_device_t * osdev, void *p, int sz); +extern void oss_reserve_pages (oss_native_word start_addr, + oss_native_word end_addr); +extern void oss_unreserve_pages (oss_native_word start_addr, + oss_native_word end_addr); + +#define KERNEL_MALLOC(nbytes) malloc(nbytes) +#define KERNEL_FREE(addr) free(addr) +#define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) oss_contig_malloc(osdev, sz, memlimit, phaddr) +#define CONTIG_FREE(osdev, p, sz, handle) oss_contig_free(osdev, p, sz) + +typedef void dev_info_t; + +struct _oss_device_t +{ + int cardnum; + int dev_type; + int available; + int instance; + dev_info_t *dip; + void *devc; + char *name; + char nick[16]; + char handle[32]; + int num_audio_engines; + int num_audioplay, num_audiorec, num_audioduplex; + int num_mididevs; + int num_mixerdevs; + int num_loopdevs; + int first_mixer; /* This must be set to -1 by osdev_create() */ + int major; + struct module *owner; /* Pointer to THISMODULE (needed by osscore.c) */ + char modname[32]; + char *hw_info; + + volatile int refcount; /* Nonzero means that the device is needed by some other (virtual) driver. */ + +/* Interrupts */ + + int iblock_cookie; /* Dummy field under Linux */ + void *irqparms; + int intrcount, ackcount; +}; + +typedef struct +{ + int bus, dev, func; +} oss_pci_device_t; + +typedef int timeout_id_t; +extern timeout_id_t oss_timeout (void (*func) (void *), void *arg, + unsigned long long ticks); +extern void oss_untimeout (timeout_id_t id); +#define timeout oss_timeout +#define untimeout oss_untimeout + +extern int oss_hz; +#define HZ oss_hz +#define OSS_HZ HZ + +/* + * Dummy defines for poll() + */ +#define POLLIN 0 +#define POLLOUT 0 +#define POLLRDNORM 0 +#define POLLWRNORM 0 + +/* + * Process info macros + */ +#define GET_PROCESS_PID(x) -1 +#define GET_PROCESS_UID(x) 0 +#define GET_PROCESS_NAME(x) NULL + +/* + * Floating point save/restore support for vmix + */ +#define FP_SUPPORT + +#ifdef FP_SUPPORT +typedef short fp_env_t[512]; +typedef unsigned int fp_flags_t[4]; +extern int oss_fp_check (void); +extern void oss_fp_save (short *envbuf, fp_flags_t flags); +extern void oss_fp_restore (short *envbuf, fp_flags_t flags); +#undef FP_SAVE +#undef FP_RESTORE +# define FP_SAVE(envbuf, flags) oss_fp_save(envbuf, flags) +# define FP_RESTORE(envbuf, flags) oss_fp_restore(envbuf, flags) +#endif + +#endif |