diff options
Diffstat (limited to 'setup/FreeBSD/oss/build/osscore.c')
-rw-r--r-- | setup/FreeBSD/oss/build/osscore.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/setup/FreeBSD/oss/build/osscore.c b/setup/FreeBSD/oss/build/osscore.c new file mode 100644 index 0000000..5264118 --- /dev/null +++ b/setup/FreeBSD/oss/build/osscore.c @@ -0,0 +1,559 @@ +/* + * Purpose: OSS core functions that need to be compiled in the target system + * + * Some parts of the FreeBSD operating system interface of OSS are sensitive + * to changes in internal structures of FreeBSD. For this reason these + * files have to be compiled in the target system when OSS is installed. + * In this way the same OSS binary package can be used with several FreeBSD + * versions. + */ +#include <machine/stdarg.h> +#include <sys/param.h> /* defines used in kernel.h */ +#include <sys/module.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> /* types used in module initialization */ +#include <sys/conf.h> /* cdevsw struct */ +#include <sys/uio.h> /* uio struct */ +#include <sys/malloc.h> + +#include <sys/bus.h> /* structs, prototypes for pci bus stuff */ +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/param.h> /* defines used in kernel.h */ +#include <dev/pci/pcivar.h> /* For pci_get macros! */ +#include <dev/pci/pcireg.h> +#include <machine/intr_machdep.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <sys/proc.h> + +typedef struct _oss_device_t oss_device_t; +#include "bsddefs.h" + +/* The PCIBIOS_* defines must match oss_pci.h */ +#define PCIBIOS_SUCCESSFUL 0x00 +#define PCIBIOS_FAILED -1 + +extern int soundcard_attach (void); +extern int soundcard_detach (void); + +void * +memset (void *t, int val, int l) +{ + char *c = t; + while (l-- > 0) + *c++ = val; + + return t; +} + +void +cmn_err (int level, char *s, ...) +{ + char tmp[1024], *a[6]; + va_list ap; + int i, n = 0; + + va_start (ap, s); + + for (i = 0; i < strlen (s); i++) + if (s[i] == '%') + n++; + + for (i = 0; i < n && i < 6; i++) + a[i] = va_arg (ap, char *); + + for (i = n; i < 6; i++) + a[i] = NULL; + + 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); + printf ("%s", tmp); +#if 0 + /* This may cause a crash under SMP */ + if (sound_started) + store_msg (tmp); +#endif + + va_end (ap); +} + +void +oss_udelay (unsigned long t) +{ + DELAY (t); +} + +typedef struct +{ + int irq; + oss_device_t *osdev; + oss_tophalf_handler_t top; + oss_bottomhalf_handler_t bottom; + struct resource *irqres; + int irqid; + void *cookie; +} osscore_intr_t; + +#define MAX_INTRS 32 + +static osscore_intr_t intrs[MAX_INTRS] = { {0} }; +static int nintrs = 0; + +static void +ossintr (void *arg) +{ + osscore_intr_t *intr = arg; + int serviced = 0; + + if (intr->top) + serviced = intr->top (intr->osdev); + if (intr->bottom) + intr->bottom (intr->osdev); + oss_inc_intrcount (intr->osdev, serviced); +} + +int +oss_register_interrupts (oss_device_t * osdev, int intrnum, + oss_tophalf_handler_t top, + oss_bottomhalf_handler_t bottom) +{ + + osscore_intr_t *intr; + char name[32]; + + if (nintrs >= MAX_INTRS) + { + cmn_err (CE_CONT, + "oss_register_interrupts: Too many interrupt handlers\n"); + return -ENOMEM; + } + + intr = &intrs[nintrs]; + + intr->irq = 0; + intr->osdev = osdev; + intr->top = top; + intr->bottom = bottom; + + sprintf (name, "%s%d", osdev->nick, osdev->instance); + + intr->irqid = 0; + intr->irqres = bus_alloc_resource (osdev->dip, SYS_RES_IRQ, &(intr->irqid), + 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); + if (intr->irqres == NULL) + { + cmn_err (CE_CONT, + "oss_register_interrupts: bus_alloc_resource failed.\n"); + return -EIO; + } + + intr->irq = bus_setup_intr (osdev->dip, intr->irqres, + INTR_TYPE_AV | INTR_MPSAFE, +#if __FreeBSD_version >= 700031 + NULL, +#endif + ossintr, intr, &(intr->cookie)); + + nintrs++; + + return 0; +} + +void +oss_unregister_interrupts (oss_device_t * osdev) +{ + int i; + + for (i = 0; i < nintrs; i++) + if (intrs[i].osdev == osdev) + { + osscore_intr_t *intr; + + intr = &intrs[i]; + bus_teardown_intr (osdev->dip, intr->irqres, intr->cookie); + bus_release_resource (osdev->dip, SYS_RES_IRQ, intr->irqid, + intr->irqres); + } +} + +/* + * PCI config space access + */ +char * +oss_pci_read_devpath (dev_info_t * dip) +{ + return "Unknown PCI path"; // TODO +} + +int +pci_read_config_byte (oss_device_t * osdev, offset_t where, + unsigned char *val) +{ + *val = pci_read_config (osdev->dip, where, 1); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) +{ + *val = pci_read_config (osdev->dip, where, 1); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_word (oss_device_t * osdev, offset_t where, + unsigned short *val) +{ + *val = pci_read_config (osdev->dip, where, 2); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_read_config_dword (oss_device_t * osdev, offset_t where, + unsigned int *val) +{ + *val = pci_read_config (osdev->dip, where, 4); + return PCIBIOS_SUCCESSFUL; +} + +int +pci_write_config_byte (oss_device_t * osdev, offset_t where, + unsigned char val) +{ + pci_write_config (osdev->dip, where, val, 1); + return PCIBIOS_FAILED; +} + +int +pci_write_config_word (oss_device_t * osdev, offset_t where, + unsigned short val) +{ + pci_write_config (osdev->dip, where, val, 2); + return PCIBIOS_FAILED; +} + +int +pci_write_config_dword (oss_device_t * osdev, offset_t where, + unsigned int val) +{ + pci_write_config (osdev->dip, where, val, 4); + return PCIBIOS_FAILED; +} + +void * +oss_contig_malloc (unsigned long buffsize, unsigned long memlimit, + oss_native_word * phaddr) +{ + char *tmpbuf; + *phaddr = 0; + + tmpbuf = + (char *) contigmalloc (buffsize, M_DEVBUF, M_WAITOK, 0ul, memlimit, + PAGE_SIZE, 0ul); + if (tmpbuf == NULL) + { + cmn_err (CE_CONT, "OSS: Unable to allocate %lu bytes for a DMA buffer\n", + buffsize); + cmn_err (CE_CONT, "run soundoff and run soundon again.\n"); + return NULL; + } + *phaddr = vtophys (tmpbuf); + return tmpbuf; +} + +void +oss_contig_free (void *p, unsigned long sz) +{ + if (p) + contigfree (p, sz, M_DEVBUF); +} + +/* + * Load handler that deals with the loading and unloading of a KLD. + */ + +static int +osscore_loader (struct module *m, int what, void *arg) +{ + int err = 0; + + switch (what) + { + case MOD_LOAD: /* kldload */ + return soundcard_attach (); + break; + case MOD_UNLOAD: + return soundcard_detach (); + break; + default: + err = EINVAL; + break; + } + return (err); +} + +/* Declare this module to the rest of the kernel */ + +static moduledata_t osscore_mod = { + "osscore", + osscore_loader, + NULL +}; + +#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 */ + +#define asm __asm__ + +#if defined(__amd64__) +#define AMD64 +#endif + +static inline void +cpuid (int op, int *eax, int *ebx, int *ecx, int *edx) +{ +__asm__ ("cpuid": "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx):"0" (op), "c" + (0)); +} + +#ifdef AMD64 +# define local_save_flags(x) asm volatile("pushfq ; popq %0":"=g" (x):) +# define local_restore_flags(x) asm volatile("pushq %0 ; popfq"::"g" (x):"memory", "cc") +#else +# define local_save_flags(x) asm volatile("pushfl ; popl %0":"=g" (x):) +# define local_restore_flags(x) asm volatile("pushl %0 ; popfl"::"g" (x):"memory", "cc") +#endif + +static inline unsigned long +read_cr0 (void) +{ + unsigned long cr0; +#ifdef AMD64 +asm ("movq %%cr0,%0":"=r" (cr0)); +#else +asm ("movl %%cr0,%0":"=r" (cr0)); +#endif + return cr0; +} + +static inline void +write_cr0 (unsigned long val) +{ +#ifdef AMD64 + asm ("movq %0,%%cr0"::"r" (val)); +#else + asm ("movl %0,%%cr0"::"r" (val)); +#endif +} + +static inline unsigned long +read_cr4 (void) +{ + unsigned long cr4; +#ifdef AMD64 +asm ("movq %%cr4,%0":"=r" (cr4)); +#else +asm ("movl %%cr4,%0":"=r" (cr4)); +#endif + return cr4; +} + +static inline void +write_cr4 (unsigned long val) +{ +#ifdef AMD64 + asm ("movq %0,%%cr4"::"r" (val)); +#else + asm ("movl %0,%%cr4"::"r" (val)); +#endif +} + +static inline unsigned long long +read_mxcsr (void) +{ + unsigned long long mxcsr; +asm ("stmxcsr %0":"=m" (mxcsr)); + return mxcsr; +} + +static inline void +write_mxcsr (unsigned long long val) +{ + asm ("ldmxcsr %0"::"m" (val)); +} + +int +oss_fp_check (void) +{ + int eax, ebx, ecx, edx; +#define FLAGS_ID (1<<21) + + oss_native_word flags_reg; + + local_save_flags (flags_reg); + flags_reg &= ~FLAGS_ID; + local_restore_flags (flags_reg); + + local_save_flags (flags_reg); + if (flags_reg & FLAGS_ID) + return 0; + + flags_reg |= FLAGS_ID; + local_restore_flags (flags_reg); + + local_save_flags (flags_reg); + if (!(flags_reg & FLAGS_ID)) + return 0; + +#define OSS_CPUID_FXSR (1<<24) +#define OSS_CPUID_SSE (1<<25) +#define OSS_CPUID_SSE2 (1<<26) + + cpuid (1, &eax, &ebx, &ecx, &edx); + + if (!(edx & OSS_CPUID_FXSR)) + return 0; + + /* + * Older machines require different FP handling than the latest ones. Use the SSE + * instruction set as an indicator. + */ + if (!(edx & OSS_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 */ +} + +#ifdef VDEV_SUPPORT +static void +oss_file_free_private (void *v) +{ + free (v, M_DEVBUF); +} + +int +oss_file_get_private (void **v) +{ + int error; + + error = devfs_get_cdevpriv (v); + if (error) + { + cmn_err (CE_CONT, "Couldn't retrieve private data from file handle!\n"); + return error; + } + return 0; +} + +int +oss_file_set_private (struct thread *t, void *v, size_t l) +{ + int error; + void * p; + + p = malloc (l, M_DEVBUF, M_WAITOK); + if (p == NULL) + { + cmn_err (CE_CONT, "Couldn't allocate memory!\n"); + return -1; + } + memcpy (p, v, l); + error = devfs_set_cdevpriv (p, oss_file_free_private); + if (error) + { + cmn_err (CE_CONT, "Couldn't attach private data to file handle!\n"); + oss_file_free_private (p); + return error; + } + return 0; +} +#endif + +int +oss_get_uid(void) +{ + return curthread->td_ucred->cr_uid; +} + +extern int max_intrate; +extern int detect_trace; +extern int src_quality; +extern int flat_device_model; +extern int vmix_disabled; +extern int vmix_loopdevs; +extern int vmix_no_autoattach; +extern int ac97_amplifier; +extern int ac97_recselect; +extern int cooked_enable; +extern int dma_buffsize; +extern int excl_policy; +extern int mixer_muted; +TUNABLE_INT("osscore.max_intrate", &max_intrate); +TUNABLE_INT("osscore.detect_trace", &detect_trace); +TUNABLE_INT("osscore.src_quality", &src_quality); +TUNABLE_INT("osscore.flat_device_model", &flat_device_model); +TUNABLE_INT("osscore.vmix_disabled", &vmix_disabled); +TUNABLE_INT("osscore.vmix_loopdevs", &vmix_loopdevs); +TUNABLE_INT("osscore.vmix_no_autoattach", &vmix_no_autoattach); +TUNABLE_INT("osscore.ac97_amplifier", &ac97_amplifier); +TUNABLE_INT("osscore.ac97_recselect", &ac97_recselect); +TUNABLE_INT("osscore.cooked_enable", &cooked_enable); +TUNABLE_INT("osscore.dma_buffsize", &dma_buffsize); +TUNABLE_INT("osscore.excl_policy", &excl_policy); +TUNABLE_INT("osscore.mixer_muted", &mixer_muted); + +DECLARE_MODULE (osscore, osscore_mod, SI_SUB_KLD, SI_ORDER_ANY); +MODULE_VERSION (osscore, 4); |