summaryrefslogtreecommitdiff
path: root/setup/Linux/oss/build/osscore.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
commit1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch)
tree4495d23e7b54ab5700e3839081e797c1eafe0db9 /setup/Linux/oss/build/osscore.c
downloadoss4-upstream.tar.gz
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'setup/Linux/oss/build/osscore.c')
-rw-r--r--setup/Linux/oss/build/osscore.c2214
1 files changed, 2214 insertions, 0 deletions
diff --git a/setup/Linux/oss/build/osscore.c b/setup/Linux/oss/build/osscore.c
new file mode 100644
index 0000000..1a029b4
--- /dev/null
+++ b/setup/Linux/oss/build/osscore.c
@@ -0,0 +1,2214 @@
+/*
+ * Purpose: Linux kernel version specific wrapper routines.
+ *
+ * This file will be shipped in source format and compiled in the target
+ * (customer) system. In this way minor changes between Linux versions
+ * can be fixed by the customer.
+ */
+
+/*
+ * Copyright (C) 4Front Technologies 2005-2009. Released under GPL2 license.
+ */
+//#include <linux/config.h>
+typedef int *ioctl_arg;
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <stdarg.h>
+#include <linux/vmalloc.h>
+#include "timestamp.h"
+#include "local_config.h"
+#include "oss_exports.h"
+#include "wrap.h"
+#include "ossdip.h"
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#undef strlen
+#undef strcpy
+#define strlen oss_strlen
+#define strcpy oss_strcpy
+
+typedef struct _smap_t dmap_t;
+
+#include "soundcard.h"
+#include "audio_core.h"
+#include "mixer_core.h"
+#include "ubuntu_version_hack.inc"
+
+MODULE_LICENSE ("GPL v2");
+MODULE_DESCRIPTION ("Open Sound System core services");
+MODULE_AUTHOR ("4Front Technologies (support@opensound.com)");
+
+struct _oss_mutex_t
+{
+ /* Caution! This definition must match cuckoo.h */
+ spinlock_t lock;
+ int filler; /* Make sure this structure doesn't become zero length */
+};
+
+static oss_device_t *core_osdev = NULL;
+/*
+ * Minor device list
+ */
+#define MAX_MINOR 256
+typedef struct
+{
+ int major, minor;
+ char name[32];
+} oss_minor_t;
+
+static oss_minor_t minors[MAX_MINOR];
+static int nminors = 0;
+
+/*
+ * Sleep flags. Make sure these definitions match oss_config.h.
+ */
+#define WK_NONE 0x00
+#define WK_WAKEUP 0x01
+#define WK_TIMEOUT 0x02
+#define WK_SIGNAL 0x04
+#define WK_SLEEP 0x08
+#define WK_SELECT 0x10
+
+time_t
+oss_get_time (void)
+{
+#if 1
+ return get_seconds ();
+#else
+ return xtime.tv_sec;
+#endif
+}
+
+void *oss_memset (void *t, int val, size_t l);
+
+void *
+oss_kmem_alloc (size_t size)
+{
+ void *ptr;
+ if ((ptr = vmalloc (size)) == NULL)
+ {
+ oss_cmn_err (CE_WARN, "vmalloc(%d) failed\n", size);
+ return NULL;
+ }
+ memset (ptr, 0, size);
+ return ptr;
+}
+
+void
+oss_kmem_free (void *addr)
+{
+ vfree (addr);
+}
+
+/* oss_pmalloc() moved to os_linux.c */
+
+extern oss_native_word
+oss_virt_to_bus (void *addr)
+{
+ return virt_to_bus (addr);
+}
+
+void *
+oss_memcpy (void *t_, const void *f_, size_t l)
+{
+ unsigned char *t = t_;
+ unsigned const char *f = f_;
+ int i;
+
+ for (i = 0; i < l; i++)
+ *t++ = *f++;
+
+ return t;
+}
+
+void *
+oss_memset (void *t, int val, size_t l)
+{
+ char *c = t;
+ while (l-- > 0)
+ *c++ = val;
+
+ return t;
+}
+
+int
+oss_strcmp (const char *s1, const char *s2)
+{
+ while (*s1 && *s2)
+ {
+ if (*s1 != *s2)
+ return *s1 - *s2;
+ s1++;
+ s2++;
+ }
+
+ return *s1 - *s2;
+}
+
+int
+oss_strncmp (const char *s1, const char *s2, size_t len)
+{
+ while (*s1 && *s2 && len--)
+ {
+ if (*s1 != *s2)
+ return *s1 - *s2;
+ s1++;
+ s2++;
+ }
+
+ return *s1 - *s2;
+}
+
+char *
+oss_strcpy (char *s1, const char *s2)
+{
+ char *s = s1;
+
+ while (*s2)
+ *s1++ = *s2++;
+ *s1 = '\0';
+ return s;
+}
+
+size_t
+oss_strlen (const char *s)
+{
+ int i;
+
+ for (i = 0; s[i]; i++);
+
+ return i;
+}
+
+char *
+oss_strncpy (char *s1, const char *s2, size_t l)
+{
+ char *s = s1;
+ int n = 0;
+
+ while (*s2)
+ {
+ if (n++ >= l)
+ return s;
+
+ *s1++ = *s2++;
+ }
+ *s1 = '\0';
+ return s;
+}
+
+int oss_hz = HZ;
+extern int max_intrate;
+extern int detect_trace;
+extern int src_quality;
+extern int flat_device_model;
+extern int vmix_disabled;
+extern int vmix_no_autoattach;
+extern int vmix_loopdevs;
+extern int ac97_amplifier;
+extern int ac97_recselect;
+extern int cooked_enable;
+extern int dma_buffsize;
+extern int excl_policy;
+extern int mixer_muted;
+
+module_param (oss_hz, int, S_IRUGO);
+module_param (max_intrate, int, S_IRUGO);
+module_param (detect_trace, int, S_IRUGO);
+module_param (src_quality, int, S_IRUGO);
+module_param (flat_device_model, int, S_IRUGO);
+module_param (vmix_disabled, int, S_IRUGO);
+module_param (vmix_no_autoattach, int, S_IRUGO);
+module_param (vmix_loopdevs, int, S_IRUGO);
+module_param (ac97_amplifier, int, S_IRUGO);
+module_param (ac97_recselect, int, S_IRUGO);
+module_param (cooked_enable, int, S_IRUGO);
+module_param (dma_buffsize, int, S_IRUGO);
+module_param (excl_policy, int, S_IRUGO);
+module_param (mixer_muted, int, S_IRUGO);
+
+static struct proc_dir_entry *oss_proc_root = NULL;
+static struct proc_dir_entry *oss_proc_devfiles = NULL;
+
+static ssize_t
+oss_read_devfiles (struct file *file, char __user * buf, size_t count,
+ loff_t * ppos)
+{
+ static char tmp[8192];
+ char *s;
+ static int eof = 0;
+ int i;
+
+ if (eof)
+ {
+ eof = 0;
+ return 0;
+ }
+
+ *tmp = 0;
+ s = tmp;
+
+ for (i = 0; i < nminors; i++)
+ {
+ if (strlen (tmp) > sizeof (tmp) - 20)
+ {
+ printk (KERN_ALERT "osscore: Procfs buffer too small\n");
+ return -ENOMEM;
+ }
+
+ s += sprintf (s, "%s %d %d\n",
+ minors[i].name, minors[i].major, minors[i].minor);
+ }
+
+ if (copy_to_user (buf, (void *) tmp, strlen (tmp)))
+ return -EFAULT;
+
+ eof = 1;
+ return strlen (tmp);
+}
+
+static struct file_operations oss_proc_operations = {
+ .read = oss_read_devfiles,
+};
+
+static void
+init_proc_fs (void)
+{
+ if ((oss_proc_root =
+ create_proc_entry ("opensound", 0700 | S_IFDIR, NULL)) == NULL)
+ {
+ oss_cmn_err (CE_CONT, "Cannot create /proc/opensound\n");
+ return;
+ }
+
+ if ((oss_proc_devfiles =
+ create_proc_entry ("devfiles", 0600, oss_proc_root)) == NULL)
+ {
+ oss_cmn_err (CE_CONT, "Cannot create /proc/opensound/devfiles\n");
+ return;
+ }
+
+ oss_proc_devfiles->proc_fops = &oss_proc_operations;
+}
+
+static void
+uninit_proc_fs (void)
+{
+ if (oss_proc_root)
+ {
+ if (oss_proc_devfiles)
+ remove_proc_entry ("devfiles", oss_proc_root);
+ remove_proc_entry ("opensound", NULL);
+ }
+}
+
+static int
+osscore_init (void)
+{
+ if ((core_osdev =
+ osdev_create (NULL, DRV_UNKNOWN, 0, "osscore", NULL)) == NULL)
+ {
+ oss_cmn_err (CE_WARN, "Failed to allocate OSDEV structure\n");
+ return -ENOMEM;
+ }
+
+ osdev_set_owner (core_osdev, THIS_MODULE);
+
+ init_proc_fs ();
+
+ return oss_init_osscore (core_osdev);
+}
+
+static void
+osscore_exit (void)
+{
+ uninit_proc_fs ();
+ oss_uninit_osscore (core_osdev);
+}
+
+void
+oss_udelay (unsigned long d)
+{
+ udelay (d);
+}
+
+oss_mutex_t
+oss_mutex_init (void)
+{
+ oss_mutex_t mutex;
+
+ if ((mutex = vmalloc (sizeof (*mutex))) == NULL)
+ {
+ oss_cmn_err (CE_WARN, "vmalloc(%d) failed (mutex)\n", sizeof (*mutex));
+ return NULL;
+ }
+
+ spin_lock_init (&(mutex->lock));
+
+ return mutex;
+}
+
+void
+oss_mutex_cleanup (oss_mutex_t mutex)
+{
+ vfree (mutex);
+}
+
+void
+oss_spin_lock_irqsave (oss_mutex_t mutex, oss_native_word * flags)
+{
+ unsigned long flag;
+ if (mutex == NULL)
+ return;
+ spin_lock_irqsave (&mutex->lock, flag);
+ *flags = flag;
+}
+
+void
+oss_spin_unlock_irqrestore (oss_mutex_t mutex, oss_native_word flags)
+{
+ if (mutex == NULL)
+ return;
+ spin_unlock_irqrestore (&mutex->lock, flags);
+}
+
+void
+oss_spin_lock (oss_mutex_t mutex)
+{
+ if (mutex == NULL)
+ return;
+ spin_lock (&mutex->lock);
+}
+
+void
+oss_spin_unlock (oss_mutex_t mutex)
+{
+ if (mutex == NULL)
+ return;
+ spin_unlock (&mutex->lock);
+}
+
+void *
+oss_map_pci_mem (oss_device_t * osdev, int size, unsigned long offset)
+{
+#ifdef __arm__
+ return (void*)offset;
+#else
+ return ioremap (offset, size);
+#endif
+}
+
+void
+oss_unmap_pci_mem (void *addr)
+{
+#ifndef __arm__
+ iounmap (addr);
+#endif
+}
+
+unsigned long long
+oss_get_jiffies (void)
+{
+ return jiffies_64;
+}
+
+char *
+oss_get_procname (void)
+{
+ return current->comm;
+}
+
+int
+oss_get_pid (void)
+{
+ return current->pid;
+}
+
+int
+oss_get_uid (void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+ return current->cred->uid;
+#else
+ return current->uid;
+#endif
+}
+
+typedef struct tmout_desc
+{
+ volatile int active;
+ int timestamp;
+ void (*func) (void *);
+ void *arg;
+
+ struct timer_list 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 (unsigned long 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);
+}
+
+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);
+
+ init_timer (&tmout->timer);
+ tmout->timer.expires = jiffies + ticks;
+ tmout->timer.data = id | (timeout_random & ~0xff);
+ tmout->timer.function = oss_timer_callback;
+ add_timer (&tmout->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)
+ del_timer (&tmout->timer);
+ tmout->active = 0;
+ tmout->timestamp = 0;
+}
+
+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 compatibilityi)).
+ */
+
+ int c;
+ char *address = addr;
+
+ 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:
+ c = nbytes;
+ if (c > 10)
+ c = 0;
+
+ if ((c = copy_to_user (uio->ptr, address, nbytes) != 0))
+ {
+ uio->resid -= nbytes;
+ oss_cmn_err (CE_CONT, "copy_to_user(%d) failed (%d)\n", nbytes, c);
+ return EFAULT;
+ }
+ break;
+
+ case UIO_WRITE:
+ if (copy_from_user (address, uio->ptr, nbytes) != 0)
+ {
+ oss_cmn_err (CE_CONT, "copy_from_user failed\n");
+ uio->resid -= nbytes;
+ return EFAULT;
+ }
+ 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;
+}
+
+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] = 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);
+ printk ("%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);
+
+ printk (KERN_ALERT "%s", tmp);
+ }
+#if 0
+ /* This may cause a crash under SMP */
+ if (sound_started)
+ store_msg (tmp);
+#endif
+
+ va_end (ap);
+}
+
+/*
+ * Sleep/wakeup
+ */
+
+struct oss_wait_queue
+{
+ volatile int flags;
+ wait_queue_head_t wq;
+};
+
+struct oss_wait_queue *
+oss_create_wait_queue (oss_device_t * osdev, const char *name)
+{
+ struct oss_wait_queue *wq;
+
+ if ((wq = vmalloc (sizeof (*wq))) == NULL)
+ {
+ oss_cmn_err (CE_WARN, "vmalloc(%d) failed (wq)\n", sizeof (*wq));
+ return NULL;
+ }
+ init_waitqueue_head (&wq->wq);
+
+ return wq;
+}
+
+void
+oss_reset_wait_queue (struct oss_wait_queue *wq)
+{
+ wq->flags = 0;
+}
+
+void
+oss_remove_wait_queue (struct oss_wait_queue *wq)
+{
+ vfree (wq);
+}
+
+int
+oss_sleep (struct oss_wait_queue *wq, oss_mutex_t * mutex, int ticks,
+ oss_native_word * flags, unsigned int *status)
+{
+ int result = 0;
+ *status = 0;
+
+ if (wq == NULL)
+ return 0;
+
+ wq->flags = 0;
+ oss_spin_unlock_irqrestore (*mutex, *flags);
+
+ if (ticks <= 0)
+ result = wait_event_interruptible (wq->wq, (wq->flags & WK_WAKEUP));
+ else
+ result =
+ wait_event_interruptible_timeout (wq->wq, (wq->flags & WK_WAKEUP),
+ ticks);
+
+ oss_spin_lock_irqsave (*mutex, flags);
+
+ if (result < 0) /* 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)
+{
+ oss_spin_unlock_irqrestore (*mutex, *flags);
+ poll_wait ((struct file *) ev->file, &wq->wq,
+ (struct poll_table_struct *) ev->wait);
+ oss_spin_lock_irqsave (*mutex, flags);
+ 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;
+ oss_spin_unlock_irqrestore (*mutex, *flags);
+ wake_up (&wq->wq);
+ oss_spin_lock_irqsave (*mutex, flags);
+}
+
+void
+oss_reserve_pages (oss_native_word start_addr, oss_native_word end_addr)
+{
+ struct page *page, *lastpage;
+
+ lastpage = virt_to_page (end_addr);
+
+ for (page = virt_to_page (start_addr); page <= lastpage; page++)
+ set_bit (PG_reserved, &page->flags);
+}
+
+void
+oss_unreserve_pages (oss_native_word start_addr, oss_native_word end_addr)
+{
+ struct page *page, *lastpage;
+
+ lastpage = virt_to_page (end_addr);
+
+ for (page = virt_to_page (start_addr); page <= lastpage; page++)
+ clear_bit (PG_reserved, &page->flags);
+}
+
+void *
+oss_contig_malloc (oss_device_t * osdev, int buffsize, oss_uint64_t memlimit,
+ oss_native_word * phaddr)
+{
+ char *start_addr, *end_addr;
+ int sz, size;
+ int flags = 0;
+
+ *phaddr = 0;
+
+#ifdef GFP_DMA32
+ if (memlimit < 0x0000000100000000LL)
+ flags |= GFP_DMA32;
+#endif
+
+ if (memlimit < 0x00000000ffffffffLL)
+ flags |= GFP_DMA;
+
+ start_addr = NULL;
+
+ for (sz = 0, size = PAGE_SIZE; size < buffsize; sz++, size <<= 1);
+
+ if (buffsize != (PAGE_SIZE * (1 << sz)))
+ {
+#if 0
+ printk
+ ("Contig_malloc: Invalid size %d != %ld\n", buffsize,
+ PAGE_SIZE * (1 << sz));
+#endif
+ }
+
+ start_addr = (char *) __get_free_pages (GFP_KERNEL | flags, sz);
+
+ 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;
+
+ oss_reserve_pages ((oss_native_word) start_addr,
+ (oss_native_word) end_addr);
+ }
+
+ *phaddr = virt_to_bus (start_addr);
+ return start_addr;
+}
+
+void
+oss_contig_free (oss_device_t * osdev, void *p, int buffsize)
+{
+ int sz, size;
+ caddr_t start_addr, end_addr;
+
+ if (p == NULL)
+ return;
+
+ for (sz = 0, size = PAGE_SIZE; size < buffsize; sz++, size <<= 1);
+
+ start_addr = p;
+ end_addr = p + buffsize - 1;
+
+ oss_unreserve_pages ((oss_native_word) start_addr,
+ (oss_native_word) end_addr);
+ free_pages ((unsigned long) p, sz);
+}
+
+/*
+ * These typedefs must match definition of struct file_operations.
+ * (See notes in routine oss_register_chrdev).
+ */
+typedef ssize_t (*read_t) (struct file *, char *, size_t, loff_t *);
+typedef ssize_t (*write_t) (struct file *, const char *, size_t, loff_t *);
+typedef unsigned int (*poll_t) (struct file *, poll_table *);
+typedef poll_table select_table;
+
+typedef int (*readdir_t) (struct inode *, struct file *, void *, filldir_t);
+typedef int (*ioctl_t) (struct inode *, struct file *, unsigned int,
+ unsigned long);
+typedef long (*new_ioctl_t) (struct file *, unsigned int, unsigned long);
+typedef int (*mmap_t) (struct file *, struct vm_area_struct *);
+typedef int (*open_t) (struct inode *, struct file *);
+
+typedef int (*release_t) (struct inode *, struct file *);
+typedef int (*fasync_t) (int, struct file *, int);
+typedef int (*fsync_t) (struct inode *, struct file *);
+
+static loff_t
+oss_no_llseek (struct file *file, loff_t offset, int orig)
+{
+ return -EINVAL;
+}
+
+static int
+oss_no_fsync (struct file *f, struct dentry *d, int datasync)
+{
+ return -EINVAL;
+}
+
+static int
+oss_no_fasync (int x, struct file *f, int m)
+{
+ return -EINVAL;
+}
+
+/*
+ * Wrappers for installing and uninstalling character and block devices.
+ *
+ * The oss_file_operation_handle structure differs from kernel's
+ * file_operations structure in parameters of the driver entry points.
+ * Kernel inode, file, wait_struct, vm_are_struct etc. typed arguments
+ * are replaced by wrapper handles.
+ */
+
+static struct file_operations *
+alloc_fop (oss_device_t * osdev, struct oss_file_operation_handle *op)
+{
+/*
+ * This routine performs initialization of kernel file_operations structure
+ * from oss_file_operation_handle structure. Each procedure pointer is copied
+ * to a temporary variable before doing the actual assignment. This
+ * prevents unnecessary warnings while it still enables compatibility
+ * warnings.
+ *
+ * Any warning given by this routine may indicate that kernel fs
+ * call interface has changed significantly (added parameters to the routines).
+ * In this case definition routine in oss_file_operation_handle must be updated
+ * and WRAPPER_VERSION must be incremented.
+ */
+
+ struct file_operations *fop;
+
+ poll_t tmp_poll = (poll_t) op->poll;
+ read_t tmp_read = (read_t) op->read;
+ write_t tmp_write = (write_t) op->write;
+ /* readdir_t tmp_readdir = (readdir_t)op->readdir; */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)
+ ioctl_t tmp_ioctl = (ioctl_t) op->ioctl;
+#endif
+ mmap_t tmp_mmap = (mmap_t) op->mmap;
+ open_t tmp_open = (open_t) op->open;
+ release_t tmp_release = (release_t) op->release;
+ new_ioctl_t tmp_unlocked_ioctl = (new_ioctl_t) op->unlocked_ioctl;
+ new_ioctl_t tmp_compat_ioctl = (new_ioctl_t) op->compat_ioctl;
+
+ fop = (struct file_operations *)
+ oss_kmem_alloc (sizeof (struct file_operations));
+
+ memset ((char *) fop, 0, sizeof (struct file_operations));
+
+/*
+ * Now the assignment
+ */
+ fop->llseek = oss_no_llseek;
+ fop->read = tmp_read;
+ fop->write = tmp_write;
+ fop->readdir = NULL; /* tmp_readdir; */
+ fop->poll = tmp_poll;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)
+ fop->ioctl = tmp_ioctl;
+#endif
+ fop->mmap = tmp_mmap;
+ fop->open = tmp_open;
+ fop->release = tmp_release;
+ fop->fsync = oss_no_fsync;
+ fop->fasync = oss_no_fasync;
+ fop->lock = NULL;
+ fop->flush = NULL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ fop->owner = osdev_get_owner (osdev);
+#endif
+#ifdef HAVE_UNLOCKED_IOCTL
+ fop->unlocked_ioctl = tmp_unlocked_ioctl;
+#endif
+#ifdef HAVE_COMPAT_IOCTL
+ fop->compat_ioctl = tmp_compat_ioctl;
+#endif
+
+ return fop;
+}
+
+int
+oss_register_chrdev (oss_device_t * osdev, unsigned int major,
+ const char *name, struct oss_file_operation_handle *op)
+{
+ int maj;
+ maj = register_chrdev (major, name, alloc_fop (osdev, op));
+
+ return maj;
+}
+
+void
+oss_register_minor (int major, int minor, char *name)
+{
+ if (nminors >= MAX_MINOR)
+ {
+ oss_cmn_err (CE_WARN, "Too many device files\n");
+ return;
+ }
+
+ minors[nminors].major = major;
+ minors[nminors].minor = minor;
+ strcpy (minors[nminors].name, name);
+ nminors++;
+}
+
+int
+oss_unregister_chrdev (unsigned int major, const char *name)
+{
+ unregister_chrdev (major, name);
+ return 0;
+}
+
+int
+oss_inode_get_minor (oss_inode_handle_t * inode)
+{
+ return MINOR (((struct inode *) inode)->i_rdev);
+}
+
+int
+oss_file_get_flags (oss_file_handle_t * file)
+{
+ return ((struct file *) file)->f_flags;
+}
+
+void *
+oss_file_get_private (oss_file_handle_t * file)
+{
+ return ((struct file *) file)->private_data;
+}
+
+void
+oss_file_set_private (oss_file_handle_t * file, void *v)
+{
+ ((struct file *) file)->private_data = v;
+}
+
+int
+oss_vma_get_flags (oss_vm_area_handle_t * vma)
+{
+ return ((struct vm_area_struct *) vma)->vm_flags;
+}
+
+int
+oss_do_mmap (oss_vm_area_handle_t * v, oss_native_word dmabuf_phys,
+ int bytes_in_use)
+{
+ struct vm_area_struct *vma = (struct vm_area_struct *) v;
+ int res, size;
+
+ if (vma->vm_pgoff != 0)
+ {
+ oss_cmn_err (CE_WARN, "mmap() offset must be 0.\n");
+ return -EINVAL;
+ }
+
+ size = vma->vm_end - vma->vm_start;
+
+ if (size != bytes_in_use)
+ {
+ oss_cmn_err (CE_WARN, "mmap() size = %ld. Should be %d\n",
+ size, bytes_in_use);
+ return -EBUSY;
+ }
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
+ res = io_remap_page_range (vma, vma->vm_start,
+ dmabuf_phys, size, vma->vm_page_prot);
+
+#else
+ res = io_remap_pfn_range (vma, vma->vm_start,
+ dmabuf_phys >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+#endif
+
+ if (res)
+ {
+ oss_cmn_err (CE_WARN, "io_remap_pfn_range returned %d\n", res);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/* As a special exception, if you link this library with other files,
+ some of which are compiled with GCC, to produce an executable,
+ this library does not by itself cause the resulting executable
+ to be covered by the GNU General Public License.
+ This exception does not however invalidate any other reasons why
+ the executable file might be covered by the GNU General Public License. */
+
+typedef unsigned int UQItype __attribute__ ((mode (QI)));
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+typedef int DItype __attribute__ ((mode (DI)));
+typedef unsigned int UDItype __attribute__ ((mode (DI)));
+
+typedef int word_type __attribute__ ((mode (__word__)));
+
+/* DIstructs are pairs of SItype values in the order determined by
+ little/big ENDIAN. */
+
+#if defined(__i386__) || defined(__x86_64__)
+struct DIstruct
+{
+ SItype low, high;
+};
+#endif
+
+
+#ifndef __arm__
+/* We need this union to unpack/pack DImode values, since we don't have
+ any arithmetic yet. Incoming DImode parameters are stored into the
+ `ll' field, and the unpacked result is read from the struct `s'. */
+
+typedef union
+{
+ struct DIstruct s;
+ DItype ll;
+} DIunion;
+
+
+/* From gcc/longlong.h */
+
+#ifndef SI_TYPE_SIZE
+#define SI_TYPE_SIZE 32
+#endif
+
+#define __BITS4 (SI_TYPE_SIZE / 4)
+#define __ll_B (1L << (SI_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((USItype) (t) % __ll_B)
+#define __ll_highpart(t) ((USItype) (t) / __ll_B)
+
+#if defined(__i386__) || defined(__x86_64__)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl %5,%1\n" \
+ "sbbl %3,%0" \
+ : "=r" ((USItype) (sh)), \
+ "=&r" ((USItype) (sl)) \
+ : "0" ((USItype) (ah)), \
+ "g" ((USItype) (bh)), \
+ "1" ((USItype) (al)), \
+ "g" ((USItype) (bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mull %3" \
+ : "=a" ((USItype) (w0)), \
+ "=d" ((USItype) (w1)) \
+ : "%0" ((USItype) (u)), \
+ "rm" ((USItype) (v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divl %4" \
+ : "=a" ((USItype) (q)), \
+ "=d" ((USItype) (r)) \
+ : "0" ((USItype) (n0)), \
+ "1" ((USItype) (n1)), \
+ "rm" ((USItype) (d)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("bsrl %1,%0" \
+ : "=r" (__cbtmp) : "rm" ((USItype) (x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#endif /* __i386__ */
+
+/* If this machine has no inline assembler, use C macros. */
+
+/* Define this unconditionally, so it can be used for debugging. */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ USItype __d1, __d0, __q1, __q0; \
+ USItype __r1, __r0, __m; \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __r1 = (n1) % __d1; \
+ __q1 = (n1) / __d1; \
+ __m = (USItype) __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __r0 = __r1 % __d1; \
+ __q0 = __r1 / __d1; \
+ __m = (USItype) __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = (USItype) __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+/* End of gcc/longlong.h */
+
+
+static inline DItype
+__negdi2 (DItype u)
+{
+ DIunion w;
+ DIunion uu;
+
+ uu.ll = u;
+
+ w.s.low = -uu.s.low;
+ w.s.high = -uu.s.high - ((USItype) w.s.low > 0);
+
+ return w.ll;
+}
+
+static inline UDItype
+__udivmoddi4 (UDItype n, UDItype d, UDItype * rp)
+{
+ DIunion ww;
+ DIunion nn, dd;
+ DIunion rr;
+ USItype d0, d1, n0, n1, n2;
+ USItype q0, q1;
+ USItype b, bm;
+
+ nn.ll = n;
+ dd.ll = d;
+
+ d0 = dd.s.low;
+ d1 = dd.s.high;
+ n0 = nn.s.low;
+ n1 = nn.s.high;
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+ if (d1 == 0)
+ {
+ if (d0 > n1)
+ {
+ /* 0q = nn / 0D */
+
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+ q1 = 0;
+
+ /* Remainder in n0. */
+ }
+ else
+ {
+ /* qq = NN / 0d */
+
+ if (d0 == 0)
+ d0 = 1 / d0; /* Divide intentionally by zero. */
+
+ udiv_qrnnd (q1, n1, 0, n1, d0);
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+
+ /* Remainder in n0. */
+ }
+
+ if (rp != 0)
+ {
+ rr.s.low = n0;
+ rr.s.high = 0;
+ *rp = rr.ll;
+ }
+ }
+
+#else /* UDIV_NEEDS_NORMALIZATION */
+
+ if (d1 == 0)
+ {
+ if (d0 > n1)
+ {
+ /* 0q = nn / 0D */
+
+ count_leading_zeros (bm, d0);
+
+ if (bm != 0)
+ {
+ /* Normalize, i.e. make the most significant bit of the
+ denominator set. */
+
+ d0 = d0 << bm;
+ n1 = (n1 << bm) | (n0 >> (SI_TYPE_SIZE - bm));
+ n0 = n0 << bm;
+ }
+
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+ q1 = 0;
+
+ /* Remainder in n0 >> bm. */
+ }
+ else
+ {
+ /* qq = NN / 0d */
+
+ if (d0 == 0)
+ d0 = 1 / d0; /* Divide intentionally by zero. */
+
+ count_leading_zeros (bm, d0);
+
+ if (bm == 0)
+ {
+ /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
+ conclude (the most significant bit of n1 is set) /\ (the
+ leading quotient digit q1 = 1).
+
+ This special case is necessary, not an optimization.
+ (Shifts counts of SI_TYPE_SIZE are undefined.) */
+
+ n1 -= d0;
+ q1 = 1;
+ }
+ else
+ {
+ /* Normalize. */
+
+ b = SI_TYPE_SIZE - bm;
+
+ d0 = d0 << bm;
+ n2 = n1 >> b;
+ n1 = (n1 << bm) | (n0 >> b);
+ n0 = n0 << bm;
+
+ udiv_qrnnd (q1, n1, n2, n1, d0);
+ }
+
+ /* n1 != d0... */
+
+ udiv_qrnnd (q0, n0, n1, n0, d0);
+
+ /* Remainder in n0 >> bm. */
+ }
+
+ if (rp != 0)
+ {
+ rr.s.low = n0 >> bm;
+ rr.s.high = 0;
+ *rp = rr.ll;
+ }
+ }
+#endif /* UDIV_NEEDS_NORMALIZATION */
+
+ else
+ {
+ if (d1 > n1)
+ {
+ /* 00 = nn / DD */
+
+ q0 = 0;
+ q1 = 0;
+
+ /* Remainder in n1n0. */
+ if (rp != 0)
+ {
+ rr.s.low = n0;
+ rr.s.high = n1;
+ *rp = rr.ll;
+ }
+ }
+ else
+ {
+ /* 0q = NN / dd */
+
+ count_leading_zeros (bm, d1);
+ if (bm == 0)
+ {
+ /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
+ conclude (the most significant bit of n1 is set) /\ (the
+ quotient digit q0 = 0 or 1).
+
+ This special case is necessary, not an optimization. */
+
+ /* The condition on the next line takes advantage of that
+ n1 >= d1 (true due to program flow). */
+ if (n1 > d1 || n0 >= d0)
+ {
+ q0 = 1;
+ sub_ddmmss (n1, n0, n1, n0, d1, d0);
+ }
+ else
+ q0 = 0;
+
+ q1 = 0;
+
+ if (rp != 0)
+ {
+ rr.s.low = n0;
+ rr.s.high = n1;
+ *rp = rr.ll;
+ }
+ }
+ else
+ {
+ USItype m1, m0;
+ /* Normalize. */
+
+ b = SI_TYPE_SIZE - bm;
+
+ d1 = (d1 << bm) | (d0 >> b);
+ d0 = d0 << bm;
+ n2 = n1 >> b;
+ n1 = (n1 << bm) | (n0 >> b);
+ n0 = n0 << bm;
+
+ udiv_qrnnd (q0, n1, n2, n1, d1);
+ umul_ppmm (m1, m0, q0, d0);
+
+ if (m1 > n1 || (m1 == n1 && m0 > n0))
+ {
+ q0--;
+ sub_ddmmss (m1, m0, m1, m0, d1, d0);
+ }
+
+ q1 = 0;
+
+ /* Remainder in (n1n0 - m1m0) >> bm. */
+ if (rp != 0)
+ {
+ sub_ddmmss (n1, n0, n1, n0, m1, m0);
+ rr.s.low = (n1 << b) | (n0 >> bm);
+ rr.s.high = n1 >> bm;
+ *rp = rr.ll;
+ }
+ }
+ }
+ }
+
+ ww.s.low = q0;
+ ww.s.high = q1;
+ return ww.ll;
+}
+
+DItype
+__divdi3 (DItype u, DItype v)
+{
+ word_type c = 0;
+ DIunion uu, vv;
+ DItype w;
+
+ uu.ll = u;
+ vv.ll = v;
+
+ if (uu.s.high < 0)
+ c = ~c, uu.ll = __negdi2 (uu.ll);
+ if (vv.s.high < 0)
+ c = ~c, vv.ll = __negdi2 (vv.ll);
+
+ w = __udivmoddi4 (uu.ll, vv.ll, (UDItype *) 0);
+ if (c)
+ w = __negdi2 (w);
+
+ return w;
+}
+
+
+DItype
+__moddi3 (DItype u, DItype v)
+{
+ word_type c = 0;
+ DIunion uu, vv;
+ DItype w;
+
+ uu.ll = u;
+ vv.ll = v;
+
+ if (uu.s.high < 0)
+ c = ~c, uu.ll = __negdi2 (uu.ll);
+ if (vv.s.high < 0)
+ vv.ll = __negdi2 (vv.ll);
+
+ (void) __udivmoddi4 (uu.ll, vv.ll, (UDItype *) & w);
+ if (c)
+ w = __negdi2 (w);
+
+ return w;
+}
+
+
+UDItype
+__umoddi3 (UDItype u, UDItype v)
+{
+ UDItype w;
+
+ (void) __udivmoddi4 (u, v, (UDItype *) & w);
+
+ return w;
+}
+
+
+UDItype
+__udivdi3 (UDItype n, UDItype d)
+{
+ return __udivmoddi4 (n, d, (UDItype *) 0);
+}
+#endif
+
+#ifdef __arm__
+void
+raise(void)
+{
+ oss_cmn_err (CE_PANIC, "raise() got called\n");
+}
+
+#endif
+
+dev_info_t *
+oss_create_pcidip (struct pci_dev * pcidev)
+{
+ dev_info_t *dip;
+
+ if ((dip = oss_pmalloc (sizeof (*dip))) == NULL)
+ return NULL;
+
+ memset (dip, 0, sizeof (*dip));
+ dip->pcidev = pcidev;
+
+ return dip;
+}
+
+int
+osscore_pci_read_config_byte (dev_info_t * dip, unsigned int where,
+ unsigned char *val)
+{
+ return pci_read_config_byte (dip->pcidev, where, val);
+}
+
+int
+osscore_pci_read_config_irq (dev_info_t * dip, unsigned int where,
+ unsigned char *val)
+{
+ *val = dip->pcidev->irq;
+ return 0; // PCIBIOS_SUCCESSFUL
+}
+
+int
+osscore_pci_read_config_word (dev_info_t * dip, unsigned int where,
+ unsigned short *val)
+{
+ if (dip == NULL)
+ {
+ oss_cmn_err (CE_CONT, "osscore_pci_read_config_word: dip==NULL\n");
+ return -1;
+ }
+ return pci_read_config_word (dip->pcidev, where, val);
+}
+
+int
+osscore_pci_read_config_dword (dev_info_t * dip, unsigned int where,
+ unsigned int *val)
+{
+ return pci_read_config_dword (dip->pcidev, where, val);
+}
+
+int
+osscore_pci_write_config_byte (dev_info_t * dip, unsigned int where,
+ unsigned char val)
+{
+ return pci_write_config_byte (dip->pcidev, where, val);
+}
+
+int
+osscore_pci_write_config_word (dev_info_t * dip, unsigned int where,
+ unsigned short val)
+{
+ return pci_write_config_word (dip->pcidev, where, val);
+}
+
+int
+osscore_pci_write_config_dword (dev_info_t * dip, unsigned int where,
+ unsigned int val)
+{
+ return pci_write_config_dword (dip->pcidev, where, val);
+}
+
+int
+osscore_pci_enable_msi (dev_info_t * dip)
+{
+ pci_enable_msi (dip->pcidev);
+ return (dip->pcidev->irq);
+}
+
+/* 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);
+
+typedef struct
+{
+ int irq;
+ oss_device_t *osdev;
+ oss_tophalf_handler_t top;
+ oss_bottomhalf_handler_t bottom;
+} osscore_intr_t;
+
+#define MAX_INTRS 32
+
+static osscore_intr_t intrs[MAX_INTRS] = { {0} };
+static int nintrs = 0;
+
+static irqreturn_t
+osscore_intr (int irq, void *arg)
+{
+ int serviced;
+ osscore_intr_t *intr = arg;
+
+ if (intr->osdev == NULL || intr->top == NULL) /* Removed interrupt */
+ return 0;
+
+ serviced = intr->top (intr->osdev);
+ oss_inc_intrcount (intr->osdev, serviced);
+ if (serviced)
+ {
+ if (intr->bottom != NULL)
+ intr->bottom (intr->osdev);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+extern int oss_pci_read_config_irq (oss_device_t * osdev, unsigned long where,
+ unsigned char *val);
+
+char *
+oss_pci_read_devpath (dev_info_t * dip)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+ return dev_name(&dip->pcidev->dev);
+#else
+ return dip->pcidev->dev.bus_id;
+#endif
+}
+
+int
+oss_register_interrupts (oss_device_t * osdev, int irqnum,
+ oss_tophalf_handler_t top,
+ oss_bottomhalf_handler_t bottom)
+{
+
+ unsigned char pci_irq_line;
+ osscore_intr_t *intr;
+ char *name;
+ int err;
+
+ if (nintrs >= MAX_INTRS)
+ {
+ oss_cmn_err (CE_CONT,
+ "oss_register_interrupts: Too many interrupt handlers\n");
+ return -ENOMEM;
+ }
+
+ intr = &intrs[nintrs];
+
+ if (oss_pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line) > 0)
+ return -EIO;
+
+ intr->irq = pci_irq_line;
+ intr->osdev = osdev;
+ intr->top = top;
+ intr->bottom = bottom;
+
+ name = osdev_get_nick (osdev);
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+ if ((err =
+ request_irq (pci_irq_line, osscore_intr, IRQF_SHARED, name, intr)) < 0)
+ {
+ oss_cmn_err (CE_CONT, "request_irq(%d) failed, err=%d\n", pci_irq_line,
+ err);
+ return err;
+ }
+
+ nintrs++;
+
+ return 0;
+}
+
+void
+oss_unregister_interrupts (oss_device_t * osdev)
+{
+ int i;
+
+ for (i = 0; i < nintrs; i++)
+ if (intrs[i].osdev == osdev)
+ {
+ free_irq (intrs[i].irq, &intrs[i]);
+ intrs[i].osdev = NULL;
+ intrs[i].top = NULL;
+ intrs[i].bottom = NULL;
+ }
+}
+
+int
+oss_copy_to_user (void *to, const void *from, unsigned long n)
+{
+ return copy_to_user (to, from, n);
+}
+
+int
+oss_copy_from_user (void *to, const void *from, unsigned long n)
+{
+ return copy_from_user (to, from, n);
+}
+
+static int refcount = 0;
+
+typedef struct
+{
+ struct module *module;
+ int active;
+} oss_module_t;
+
+#define OSS_MAX_MODULES 50
+
+static oss_module_t oss_modules[OSS_MAX_MODULES];
+static int num_oss_modules = 0;
+
+void
+oss_inc_refcounts (void)
+{
+ int i;
+ refcount++;
+ for (i = 0; i < num_oss_modules; i++)
+ if (oss_modules[i].active)
+ {
+ if (!try_module_get (oss_modules[i].module))
+ oss_cmn_err (CE_WARN, "try_module_get() failed\n");
+ }
+}
+
+void
+oss_dec_refcounts (void)
+{
+ int i;
+ refcount--;
+ for (i = 0; i < num_oss_modules; i++)
+ if (oss_modules[i].active)
+ {
+ module_put (oss_modules[i].module);
+ }
+}
+
+void
+oss_register_module (struct module *mod)
+{
+ int i, n = -1;
+
+ for (i = 0; i < num_oss_modules; i++)
+ if (!oss_modules[i].active)
+ {
+ n = i;
+ break;
+ }
+
+ if (n == -1)
+ {
+ if (num_oss_modules >= OSS_MAX_MODULES)
+ {
+ printk (KERN_ALERT "Too many OSS modules\n");
+ return;
+ }
+
+ n = num_oss_modules++;
+ }
+
+ oss_modules[n].module = mod;
+ oss_modules[n].active = 1;
+}
+
+void
+oss_unregister_module (struct module *mod)
+{
+ int i;
+
+ for (i = 0; i < num_oss_modules; i++)
+ if (oss_modules[i].active && oss_modules[i].module == mod)
+ {
+ oss_modules[i].active = 0;
+ oss_modules[i].module = NULL;
+ return;
+ }
+}
+
+module_init (osscore_init);
+module_exit (osscore_exit);
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+
+#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 0
+static inline unsigned long
+read_cr0 (void)
+{
+ unsigned long cr0;
+ asm volatile ("movq %%cr0,%0":"=r" (cr0));
+ return cr0;
+}
+
+static inline void
+write_cr0 (unsigned long val)
+{
+ asm volatile ("movq %0,%%cr0"::"r" (val));
+}
+
+static inline unsigned long
+read_cr4 (void)
+{
+ unsigned long cr4;
+ asm volatile ("movq %%cr4,%0":"=r" (cr4));
+ return cr4;
+}
+
+static inline void
+write_cr4 (unsigned long val)
+{
+ asm volatile ("movq %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_irq_restore (flags_reg);
+
+ local_save_flags (flags_reg);
+ if (flags_reg & FLAGS_ID)
+ return 0;
+
+ flags_reg |= FLAGS_ID;
+ local_irq_restore (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
+
+#if 0
+void
+__stack_chk_fail (void)
+{
+ panic ("__stack_chk_fail\n");
+}
+#endif
+
+/*
+ * Exported symbols
+ */
+
+#define EXPORT_FUNC(name) extern void name(void);EXPORT_SYMBOL(name);
+#define EXPORT_DATA(name) extern int name;EXPORT_SYMBOL(name);
+
+/* EXPORT_SYMBOL (__stack_chk_fail); */
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+EXPORT_SYMBOL (oss_fp_check);
+EXPORT_SYMBOL (oss_fp_save);
+EXPORT_SYMBOL (oss_fp_restore);
+#endif
+
+EXPORT_SYMBOL (oss_register_chrdev);
+EXPORT_SYMBOL (oss_unregister_chrdev);
+EXPORT_SYMBOL (oss_reserve_pages);
+EXPORT_SYMBOL (oss_unreserve_pages);
+EXPORT_FUNC (ac97_install);
+EXPORT_FUNC (ac97_install_full);
+EXPORT_FUNC (ac97_playrate);
+EXPORT_FUNC (ac97_recrate);
+EXPORT_FUNC (ac97_varrate);
+EXPORT_FUNC (ac97_override_control);
+EXPORT_FUNC (ac97_init_ext);
+EXPORT_FUNC (ac97_mixer_set);
+EXPORT_FUNC (ac97_spdif_setup);
+EXPORT_FUNC (ac97_spdifout_ctl);
+EXPORT_FUNC (ac97_remove_control);
+EXPORT_SYMBOL (ac97_amplifier);
+EXPORT_FUNC (ac97_disable_spdif);
+EXPORT_FUNC (ac97_enable_spdif);
+EXPORT_FUNC (ac97_mixext_ctl);
+EXPORT_FUNC (ac97_spdifin_ctl);
+EXPORT_FUNC (oss_pci_byteswap);
+EXPORT_SYMBOL (mixer_ext_truncate);
+EXPORT_SYMBOL (mixer_ext_rebuild_all);
+EXPORT_FUNC (remux_install);
+EXPORT_SYMBOL (oss_strlen);
+EXPORT_SYMBOL (oss_strcmp);
+EXPORT_FUNC (oss_install_mididev);
+EXPORT_DATA (detect_trace);
+EXPORT_SYMBOL (dmap_get_qhead);
+EXPORT_SYMBOL (dmap_get_qtail);
+EXPORT_FUNC (oss_alloc_dmabuf);
+EXPORT_SYMBOL (oss_contig_free);
+EXPORT_SYMBOL (oss_contig_malloc);
+EXPORT_FUNC (oss_disable_device);
+EXPORT_FUNC (oss_free_dmabuf);
+EXPORT_SYMBOL (oss_map_pci_mem);
+EXPORT_SYMBOL (oss_install_audiodev);
+EXPORT_SYMBOL (oss_install_audiodev_with_devname);
+EXPORT_FUNC (oss_audio_set_error);
+EXPORT_SYMBOL (load_mixer_volumes);
+EXPORT_SYMBOL (oss_unmap_pci_mem);
+EXPORT_SYMBOL (oss_memset);
+EXPORT_SYMBOL (oss_mutex_cleanup);
+EXPORT_SYMBOL (oss_mutex_init);
+EXPORT_SYMBOL (oss_register_device);
+EXPORT_SYMBOL (oss_register_interrupts);
+EXPORT_SYMBOL (oss_inc_intrcount);
+EXPORT_SYMBOL (oss_spin_lock);
+EXPORT_SYMBOL (oss_spin_lock_irqsave);
+EXPORT_SYMBOL (oss_spin_unlock);
+EXPORT_SYMBOL (oss_spin_unlock_irqrestore);
+EXPORT_SYMBOL (oss_udelay);
+EXPORT_FUNC (oss_unregister_device);
+EXPORT_SYMBOL (oss_unregister_interrupts);
+EXPORT_SYMBOL (oss_virt_to_bus);
+EXPORT_FUNC (oss_pci_read_config_byte);
+EXPORT_FUNC (oss_pci_read_config_word);
+EXPORT_FUNC (oss_pci_read_config_dword);
+EXPORT_FUNC (oss_pci_write_config_byte);
+EXPORT_FUNC (oss_pci_write_config_word);
+EXPORT_FUNC (oss_pci_write_config_dword);
+EXPORT_FUNC (oss_pci_enable_msi);
+EXPORT_SYMBOL (oss_pci_read_config_irq);
+EXPORT_SYMBOL (oss_pci_read_devpath);
+EXPORT_SYMBOL (oss_get_jiffies);
+EXPORT_SYMBOL (mixer_find_ext);
+EXPORT_SYMBOL (oss_install_mixer);
+EXPORT_SYMBOL (oss_strcpy);
+EXPORT_SYMBOL (oss_kmem_free);
+#ifndef __arm__
+EXPORT_FUNC (uart401_init);
+EXPORT_FUNC (uart401_disable);
+EXPORT_FUNC (uart401_irq);
+#endif
+EXPORT_SYMBOL (mixer_ext_set_init_fn);
+EXPORT_SYMBOL (mixer_ext_set_vmix_init_fn);
+#ifdef CONFIG_OSS_VMIX
+EXPORT_FUNC (vmix_attach_audiodev);
+EXPORT_FUNC (vmix_detach_audiodev);
+EXPORT_FUNC (vmix_change_devnames);
+#endif
+EXPORT_SYMBOL (mixer_ext_set_strings);
+EXPORT_SYMBOL (mixer_ext_create_group);
+EXPORT_SYMBOL (mixer_ext_create_group_flags);
+EXPORT_SYMBOL (mixer_ext_create_control);
+EXPORT_SYMBOL (oss_strncpy);
+EXPORT_SYMBOL (oss_memcpy);
+EXPORT_SYMBOL (oss_kmem_alloc);
+EXPORT_DATA (oss_hz);
+EXPORT_FUNC (oss_spdif_open);
+EXPORT_FUNC (oss_spdif_ioctl);
+EXPORT_FUNC (oss_spdif_install);
+EXPORT_FUNC (oss_spdif_uninstall);
+EXPORT_FUNC (oss_spdif_close);
+EXPORT_FUNC (oss_spdif_mix_init);
+EXPORT_FUNC (oss_spdif_setrate);
+EXPORT_FUNC (create_new_card);
+EXPORT_FUNC (oss_audio_ioctl);
+EXPORT_FUNC (oss_audio_open_engine);
+EXPORT_FUNC (oss_audio_release);
+EXPORT_FUNC (oss_audio_set_rate);
+EXPORT_SYMBOL (oss_uiomove);
+EXPORT_SYMBOL (oss_get_pid);
+EXPORT_SYMBOL (oss_get_uid);
+EXPORT_SYMBOL (oss_get_procname);
+EXPORT_SYMBOL (mix_cvt);
+EXPORT_FUNC (oss_audio_set_format);
+EXPORT_FUNC (oss_audio_set_channels);
+EXPORT_FUNC (midiparser_create);
+EXPORT_FUNC (midiparser_input);
+EXPORT_FUNC (midiparser_unalloc);
+EXPORT_FUNC (mixer_ext_create_device);
+EXPORT_SYMBOL (mixer_ext_recrw);
+EXPORT_SYMBOL (mixer_ext_rw);
+EXPORT_SYMBOL (mixer_ext_set_enum);
+EXPORT_SYMBOL (mixer_ext_set_description);
+EXPORT_SYMBOL (osdev_create);
+EXPORT_FUNC (osdev_clone);
+EXPORT_SYMBOL (osdev_delete);
+EXPORT_FUNC (oss_audio_chpoll);
+EXPORT_FUNC (oss_audio_delayed_attach);
+EXPORT_FUNC (oss_audio_read);
+EXPORT_FUNC (oss_audio_write);
+EXPORT_SYMBOL (oss_create_wait_queue);
+EXPORT_SYMBOL (oss_remove_wait_queue);
+EXPORT_SYMBOL (oss_reset_wait_queue);
+EXPORT_SYMBOL (oss_sleep);
+EXPORT_SYMBOL (oss_strncmp);
+EXPORT_SYMBOL (oss_timeout);
+EXPORT_SYMBOL (oss_untimeout);
+EXPORT_SYMBOL (oss_wakeup);
+#if 0
+EXPORT_FUNC (ossddk_ac97_install);
+EXPORT_FUNC (ossddk_ac97_is_varrate);
+EXPORT_FUNC (ossddk_ac97_remove);
+EXPORT_FUNC (ossddk_ac97_set_ext_init);
+EXPORT_FUNC (ossddk_ac97_set_playrate);
+EXPORT_FUNC (ossddk_ac97_set_recrate);
+EXPORT_FUNC (ossddk_adev_get_devc);
+EXPORT_FUNC (ossddk_adev_get_dmapin);
+EXPORT_FUNC (ossddk_adev_get_dmapout);
+EXPORT_FUNC (ossddk_adev_get_flags);
+EXPORT_FUNC (ossddk_adev_get_label);
+EXPORT_FUNC (ossddk_adev_get_portc);
+EXPORT_FUNC (ossddk_adev_get_portc_play);
+EXPORT_FUNC (ossddk_adev_get_portc_record);
+EXPORT_FUNC (ossddk_adev_get_songname);
+EXPORT_FUNC (ossddk_adev_set_buflimits);
+EXPORT_FUNC (ossddk_adev_set_caps);
+EXPORT_FUNC (ossddk_adev_set_channels);
+EXPORT_FUNC (ossddk_adev_set_devc);
+EXPORT_FUNC (ossddk_adev_set_enable_flag);
+EXPORT_FUNC (ossddk_adev_set_flags);
+EXPORT_FUNC (ossddk_adev_set_formats);
+EXPORT_FUNC (ossddk_adev_set_label);
+EXPORT_FUNC (ossddk_adev_set_magic);
+EXPORT_FUNC (ossddk_adev_set_mixer);
+EXPORT_FUNC (ossddk_adev_set_portc);
+EXPORT_FUNC (ossddk_adev_set_portc_play);
+EXPORT_FUNC (ossddk_adev_set_portc_record);
+EXPORT_FUNC (ossddk_adev_set_portnum);
+EXPORT_FUNC (ossddk_adev_set_rates);
+EXPORT_FUNC (ossddk_adev_set_ratesource);
+EXPORT_FUNC (ossddk_adev_set_songname);
+EXPORT_FUNC (ossddk_adev_set_unloaded_flag);
+EXPORT_FUNC (ossddk_audio_inputintr);
+EXPORT_FUNC (ossddk_audio_outputintr);
+EXPORT_FUNC (ossddk_disable_device);
+EXPORT_FUNC (ossddk_dmap_get_buffsize);
+EXPORT_FUNC (ossddk_dmap_get_buffused);
+EXPORT_FUNC (ossddk_dmap_get_dmabuf);
+EXPORT_FUNC (ossddk_dmap_get_fragsize);
+EXPORT_FUNC (ossddk_dmap_get_numfrags);
+EXPORT_FUNC (ossddk_dmap_get_phys);
+EXPORT_FUNC (ossddk_dmap_get_private);
+EXPORT_FUNC (ossddk_dmap_get_qhead);
+EXPORT_FUNC (ossddk_dmap_get_qtail);
+EXPORT_FUNC (ossddk_dmap_set_buffsize);
+EXPORT_FUNC (ossddk_dmap_set_callback);
+EXPORT_FUNC (ossddk_dmap_set_dmabuf);
+EXPORT_FUNC (ossddk_dmap_set_fragsize);
+EXPORT_FUNC (ossddk_dmap_set_numfrags);
+EXPORT_FUNC (ossddk_dmap_set_phys);
+EXPORT_FUNC (ossddk_dmap_set_playerror);
+EXPORT_FUNC (ossddk_dmap_set_private);
+EXPORT_FUNC (ossddk_dmap_set_recerror);
+EXPORT_FUNC (ossddk_install_audiodev);
+EXPORT_FUNC (ossddk_install_mixer);
+EXPORT_FUNC (ossddk_mixer_create_control);
+EXPORT_FUNC (ossddk_mixer_create_group);
+EXPORT_FUNC (ossddk_mixer_get_devc);
+EXPORT_FUNC (ossddk_mixer_set_strings);
+EXPORT_FUNC (ossddk_mixer_touch);
+EXPORT_FUNC (ossddk_mixer_truncate);
+EXPORT_FUNC (ossddk_osdev_get_devc);
+EXPORT_FUNC (ossddk_register_device);
+EXPORT_FUNC (ossddk_unregister_device);
+#endif
+EXPORT_SYMBOL (osdev_set_major);
+EXPORT_SYMBOL (osdev_set_owner);
+EXPORT_SYMBOL (osdev_get_owner);
+EXPORT_SYMBOL (oss_create_pcidip);
+EXPORT_SYMBOL (touch_mixer);
+EXPORT_SYMBOL (oss_mixer_ext);
+EXPORT_SYMBOL (oss_request_major);
+EXPORT_SYMBOL (audio_engines);
+EXPORT_DATA (midi_devs);
+EXPORT_SYMBOL (mixer_devs);
+EXPORT_SYMBOL (mixer_devs_p);
+EXPORT_DATA (num_audio_engines);
+EXPORT_DATA (num_mididevs);
+EXPORT_SYMBOL (num_mixers);
+EXPORT_DATA (oss_timing_mutex);
+EXPORT_DATA (oss_num_cards);
+EXPORT_FUNC (oss_do_timing);
+EXPORT_FUNC (oss_do_timing2);
+EXPORT_FUNC (oss_timing_enter);
+EXPORT_FUNC (oss_timing_leave);
+#ifndef __arm__
+EXPORT_SYMBOL (__udivdi3);
+EXPORT_SYMBOL (__umoddi3);
+EXPORT_SYMBOL (__divdi3);
+#else
+EXPORT_SYMBOL (raise);
+#endif
+EXPORT_SYMBOL (oss_copy_from_user);
+EXPORT_SYMBOL (oss_copy_to_user);
+EXPORT_SYMBOL (osdev_set_irqparms);
+EXPORT_SYMBOL (osdev_get_irqparms);
+EXPORT_SYMBOL (osdev_get_nick);
+EXPORT_SYMBOL (osdev_get_instance);
+EXPORT_SYMBOL (oss_inc_refcounts);
+EXPORT_SYMBOL (oss_dec_refcounts);
+EXPORT_SYMBOL (oss_register_module);
+EXPORT_SYMBOL (oss_unregister_module);
+EXPORT_SYMBOL (oss_audio_reset);
+EXPORT_SYMBOL (oss_audio_start_syncgroup);
+EXPORT_SYMBOL (oss_encode_enum);
+EXPORT_SYMBOL (dmap_get_qlen);
+EXPORT_SYMBOL (num_audio_devfiles);
+EXPORT_SYMBOL (oss_audio_inc_byte_counter);
+EXPORT_SYMBOL (oss_audio_register_client);
+EXPORT_SYMBOL (audio_devfiles);
+EXPORT_FUNC (oss_get_cardinfo);
+EXPORT_SYMBOL (oss_pmalloc);
+EXPORT_SYMBOL (oss_add_audio_devlist);
+EXPORT_FUNC (oss_memblk_malloc);
+EXPORT_FUNC (oss_memblk_free);
+EXPORT_FUNC (oss_memblk_unalloc);
+EXPORT_DATA (oss_global_memblk);
+EXPORT_FUNC (oss_get_procinfo);
+EXPORT_DATA (mixer_muted);
+
+#ifdef CONFIG_OSS_MIDI
+EXPORT_FUNC (oss_midi_ioctl);
+EXPORT_FUNC (oss_midi_copy_timer);
+#endif