summaryrefslogtreecommitdiff
path: root/kernel/OS/SunOS/os_solaris.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/OS/SunOS/os_solaris.c')
-rw-r--r--kernel/OS/SunOS/os_solaris.c2401
1 files changed, 2401 insertions, 0 deletions
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, &region_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;
+}