/* * Purpose: Operating system abstraction functions for SCO OpenServer/UnixWare */ /* * * This file is part of Open Sound System. * * Copyright (C) 4Front Technologies 1996-2008. * * This this source file is released under GPL v2 license (no other versions). * See the COPYING file included in the main directory of this source * distribution for the license terms and conditions. * */ #include "oss_config.h" #include "midi_core.h" #include #include #include #include /* * MAX_CARDS must be larger than life. The system will panic if there are * more sound devices (cards os sound chips) than MAX_CARDS. */ #define MAX_CARDS 32 static oss_device_t *cards[MAX_CARDS]; int oss_num_cards = 0; static int oss_expired = 0; volatile int oss_open_devices = 0; static bcb_t *oss_bcb; /* * Driver information structures, for drv_attach(). */ static int oss_config (cfg_func_t func, void *idata, rm_key_t key); static const drvops_t oss_ops = { oss_config, oss_open, oss_close, oss_devinfo, oss_biostart, oss_ioctl, NULL, /* drvctl */ NULL /* mmap */ }; static const drvinfo_t oss_drvinfo = { &oss_ops, "osscore", D_MP, /* MP-safe */ NULL, /* Not a STREAMS driver */ 10 /* Must match $maxchan in Node file */ }; static physreq_t *physreq_isa = NULL; /* 24 bits */ static physreq_t *physreq_28bit = NULL; static physreq_t *physreq_30bit = NULL; static physreq_t *physreq_31bit = NULL; static physreq_t *physreq_32bit = NULL; #ifdef MEMDEBUG typedef struct { void *addr; int size; char file[40]; int line; } mem_block_t; #define MAX_MEMBLOCKS 1024 static mem_block_t memblocks[MAX_MEMBLOCKS]; static int num_memblocks = 0; #endif #ifdef MEMDEBUG void * oss_kmem_alloc (size_t size, int flags, char *file, int line) #else void * oss_kmem_alloc (size_t size, int flags) #endif { /* * This routine allocates a memory block and stores length of it in * the beginning. This length information can be used when later unallocating * the memory. */ char *ptr; uint64_t *len; ptr = kmem_zalloc (size + sizeof (uint64_t), flags); #ifdef MEMDEBUG cmn_err (CE_CONT, "kmalloc(%d, %s:%d)=%x\n", size, file, line, ptr); #endif if (ptr == NULL) return NULL; len = (uint64_t *) ptr; ptr += sizeof (uint64_t); *len = size + sizeof (uint64_t); #ifdef MEMDEBUG #if 1 { int i; for (i = 0; i < num_memblocks; i++) if (memblocks[i].addr == NULL) { memblocks[i].addr = ptr; memblocks[i].size = size; strncpy (memblocks[i].file, file, 39); memblocks[i].line = line; return ptr; } } #endif if (num_memblocks < MAX_MEMBLOCKS) { memblocks[num_memblocks].addr = ptr; memblocks[num_memblocks].size = size; strncpy (memblocks[num_memblocks].file, file, 39); memblocks[num_memblocks].line = line; num_memblocks++; } #endif return ptr; } void #ifdef MEMDEBUG oss_kmem_free (void *addr, char *file, int line) #else oss_kmem_free (void *addr) #endif { uint64_t *len; int i; char *ptr = addr; if (addr == NULL) return; ptr -= sizeof (uint64_t); len = (uint64_t *) ptr; kmem_free (ptr, *len); #ifdef MEMDEBUG { int i; for (i = 0; i < num_memblocks; i++) if (addr == memblocks[i].addr) { memblocks[i].addr = NULL; return; } } cmn_err (CE_WARN, "Bad kfree(%x, %s:%d)\n", addr, file, line); #endif } /* * Table for permanently allocated memory (to be freed by _fini) */ #define OSS_MAX_MEM 1024 void *oss_mem_blocks[OSS_MAX_MEM]; int oss_nblocks = 0; void * #ifdef MEMDEBUG oss_pmalloc (size_t size, char *file, int line) #else oss_pmalloc (size_t size) #endif { void *mem_ptr; #ifdef MEMDEBUG mem_ptr = (oss_mem_blocks[oss_nblocks] = oss_kmem_alloc (size, KM_SLEEP, file, line)); #else mem_ptr = (oss_mem_blocks[oss_nblocks] = KERNEL_MALLOC (size)); #endif if (oss_nblocks <= OSS_MAX_MEM) oss_nblocks++; else cmn_err (CE_NOTE, "Out of mem blocks\n"); return mem_ptr; } int __oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, oss_uint64_t maxaddr, int direction) { void *p; oss_native_word phaddr; int size = 64 * 1024; extern int dma_buffsize; if (dma_buffsize > 16 && dma_buffsize <= 128) size = dma_buffsize * 1024; if (dmap->dmabuf != NULL) return 0; /* * Some applications and virtual drivers need shorter buffer. */ if (dmap->flags & DMAP_SMALLBUF) { size = SMALL_DMABUF_SIZE; } else if (dmap->flags & DMAP_MEDIUMBUF) { size = MEDIUM_DMABUF_SIZE; } if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) size = 32 * 1024; if ((p = oss_contig_malloc (audio_engines[dev]->osdev, size, maxaddr, &phaddr)) == NULL) return OSS_ENOMEM; dmap->dmabuf = p; dmap->dmabuf_phys = phaddr; dmap->buffsize = size; return 0; } void oss_free_dmabuf (int dev, dmap_p dmap) { if (dmap->dmabuf == NULL) return; oss_contig_free (audio_engines[dev]->osdev, dmap->dmabuf, dmap->buffsize); dmap->dmabuf = NULL; dmap->dmabuf_phys = 0; dmap->buffsize = 0; } void * oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, oss_native_word * phaddr) { void *p = NULL; physreq_t *preqp = physreq_32bit; paddr32_t pa; *phaddr = 0; switch (memlimit) { case MEMLIMIT_ISA: preqp = physreq_isa; break; case MEMLIMIT_28BITS: preqp = physreq_28bit; break; case MEMLIMIT_30BITS: preqp = physreq_30bit; break; case MEMLIMIT_31BITS: preqp = physreq_31bit; break; case MEMLIMIT_32BITS: preqp = physreq_32bit; break; default: cmn_err (CE_WARN, "osscore: Bad DMA memlimit for %s\n", osdev->nick); } if ((p = kmem_alloc_phys (size, preqp, &pa, 0)) == NULL) { cmn_err (CE_WARN, "osscore: kmem_alloc_phys() failed\n"); return NULL; } *phaddr = pa; return p; } void oss_contig_free (oss_device_t * osdev, void *p, int sz) { if (p) kmem_free (p, sz); } /* * Wait queue support */ oss_wait_queue_t * oss_create_wait_queue (oss_device_t * osdev, const char *name) { oss_wait_queue_t *q; if ((q = KERNEL_MALLOC (sizeof (*q))) == NULL) { cmn_err (CE_WARN, "osscore: Cannot allocate memory for a wait queue\n"); return NULL; } memset (q, 0, sizeof (*q)); if ((q->sv = SV_ALLOC (KM_SLEEP)) == NULL) { cmn_err (CE_WARN, "osscore: Cannot allocate synchronization variable\n"); return NULL; } return q; } void oss_reset_wait_queue (oss_wait_queue_t * wq) { // NOP } void oss_remove_wait_queue (oss_wait_queue_t * wq) { SV_DEALLOC (wq->sv); KERNEL_FREE (wq); } static void sleep_timeout (caddr_t arg) { oss_wait_queue_t *wq = (oss_wait_queue_t *) arg; SV_BROADCAST (wq->sv, 0); } int oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks, oss_native_word * flags, unsigned int *status) { timeout_id_t tid; *status = 0; wq->flags = 0; if (wq == NULL) { cmn_err (CE_WARN, "osscore: Unallocated wait queue\n"); *status |= WK_SIGNAL; return 1; } if (ticks > 0) tid = timeout (sleep_timeout, wq, ticks); /* * Note SV_WAIT_SIG() will release the mutex/lock so we must re-acquire * it before returning. */ #ifdef MUTEX_CHECKS // Pop mutex because it will be released by SV_WAIT_SIG() pop_mutex (*mutex, __FILE__, __LINE__); #endif if (!SV_WAIT_SIG (wq->sv, prihi, *mutex)) /* Signal */ { *status |= WK_SIGNAL; wq->flags |= WK_WAKEUP; /* Needed to prevent false timeout messages */ } MUTEX_ENTER_IRQDISABLE (*mutex, *flags); if (ticks > 0 && (wq->flags & WK_WAKEUP)) untimeout (tid); return (wq->flags & WK_WAKEUP); } int oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex, oss_native_word * flags, oss_poll_event_t * ev) { // NOP: DDI8 doesn't support chpoll } void oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex, oss_native_word * flags, short events) { wq->flags |= WK_WAKEUP; SV_BROADCAST (wq->sv, 0); } void * oss_get_osid (oss_device_t * osdev) { return osdev->osid; } static int grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) { oss_cdev_t **old=*arr, **new = *arr; int old_size = *size; int new_size = *size; new_size += increment; if ((new=PMALLOC(osdev, new_size * element_size))==NULL) return 0; memset(new, 0, new_size * element_size); if (old != NULL) memcpy(new, old, old_size * element_size); *size = new_size; *arr = new; if (old != NULL) PMFREE(osdev, old); return 1; } void oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, int instance, oss_cdev_drv_t * drv, int flags) { /* * oss_install_chrdev creates a character device (minor). However if * name==NULL the device will not be exported (made visible to userland * clients). */ int i, num; oss_cdev_t *cdev = NULL; if (dev_class != OSS_DEV_STATUS) if (oss_expired && instance > 0) return; /* * Find if this dev_class&instance already exists (after previous module * detach). */ for (num = 0; num < oss_num_cdevs; num++) if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ if (oss_cdevs[num]->dev_class == dev_class && oss_cdevs[num]->instance == instance) { cdev = oss_cdevs[num]; break; } if (cdev == NULL) { if (oss_num_cdevs >= OSS_MAX_CDEVS) { if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) { cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); return; } } if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) { cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); return; } num = oss_num_cdevs++; } memset (cdev, 0, sizeof (*cdev)); cdev->dev_class = dev_class; cdev->instance = instance; cdev->d = drv; cdev->osdev = osdev; if (name != NULL) strncpy (cdev->name, name, sizeof (cdev->name) - 1); else strcpy (cdev->name, "NONE"); cdev->name[sizeof (cdev->name) - 1] = 0; oss_cdevs[num] = cdev; } int oss_find_minor (int dev_class, int instance) { int i; for (i = 0; i < oss_num_cdevs; i++) if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class && oss_cdevs[i]->instance == instance) return i; return OSS_EIO; } int oss_get_cardinfo (int cardnum, oss_card_info * ci) { /* * Print information about a 'card' in a format suitable for /dev/sndstat */ if (cardnum < 0 || cardnum >= oss_num_cards) return OSS_ENXIO; if (cards[cardnum]->name != NULL) strncpy (ci->longname, cards[cardnum]->name, 128); ci->shortname[127] = 0; if (cards[cardnum]->nick != NULL) strncpy (ci->shortname, cards[cardnum]->nick, 16); ci->shortname[15] = 0; if (cards[cardnum]->hw_info != NULL) strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info) - 1); ci->hw_info[sizeof (ci->hw_info) - 1] = 0; return 0; } void oss_reserve_device (oss_device_t * osdev) { osdev->refcount++; } void oss_unreserve_device (oss_device_t * osdev, int decrement) { osdev->refcount--; if (osdev->refcount < 0) osdev->refcount = 0; } /* * Some standard C library routines */ void * memset (void *t, int c, size_t l) { int i; if (t == NULL) return NULL; for (i = 0; i < l; i++) ((char *) t)[i] = c; return t; } void * memcpy (void *t, const void *s, size_t l) { bcopy (s, t, l); return t; } oss_device_t * osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, const char *handle) { oss_device_t *osdev = NULL; int i, err; caddr_t addr; dev_info_t ldip = 0; if (dip == NULL) dip = &ldip; if (handle == NULL) handle = nick; /* * Don't accept any more drivers if expired */ if (oss_expired && oss_num_cards > 0) return NULL; /* * Check if a driver/device was reinserted */ if (dip != &ldip) /* Not a virtual driver */ for (i = 0; i < oss_num_cards; i++) { if (!cards[i]->available && cards[i]->key == *dip) { osdev = cards[i]; break; } } if (osdev == NULL) { if (oss_num_cards >= MAX_CARDS) { cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", MAX_CARDS); return NULL; } if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) { cmn_err (CE_WARN, "osdev_create: Out of memory\n"); return NULL; } osdev->cardnum = oss_num_cards; cards[oss_num_cards++] = osdev; } osdev->key = *dip; osdev->osid = dip; osdev->available = 1; osdev->first_mixer = -1; osdev->instance = instance; osdev->dev_type = dev_type; osdev->devc = NULL; MUTEX_INIT (osdev, osdev->mutex, MH_GLOBAL); sprintf (osdev->nick, "%s%d", nick, instance); strcpy (osdev->modname, nick); switch (dev_type) { case DRV_PCI: //pci_config_setup (dip, &osdev->pci_config_handle); break; case DRV_VIRTUAL: case DRV_VMIX: case DRV_STREAMS: /* NOP */ break; case DRV_USB: /* NOP */ break; default: cmn_err (CE_WARN, "Bad device type\n"); return NULL; } /* * Create the device handle */ switch (dev_type) { case DRV_PCI: { unsigned int subvendor = 0; pci_read_config_dword (osdev, 0x2c, &subvendor); sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance); } break; #if 0 case DRV_USB: sprintf (osdev->handle, "USB-%s%d", handle, instance); break; #endif default: sprintf (osdev->handle, "%s%d", handle, instance); } return osdev; } oss_device_t * osdev_clone (oss_device_t * orig_osdev, int new_instance) { oss_device_t *osdev; osdev = PMALLOC (NULL, sizeof (*osdev)); if (osdev == NULL) { cmn_err (CE_WARN, "osdev_create: Out of memory\n"); return NULL; } memcpy (osdev, orig_osdev, sizeof (*osdev)); osdev->dev_type = DRV_CLONE; osdev->instance = new_instance; sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); return osdev; } void osdev_delete (oss_device_t * osdev) { int i; if (osdev == NULL) return; if (!osdev->available) { cmn_err (CE_WARN, "device %s, osdev already deleted\n", osdev->nick); return; } osdev->available = 0; switch (osdev->dev_type) { case DRV_PCI: break; } /* * Mark all minor nodes for this module as invalid. */ for (i = 0; i < oss_num_cdevs; i++) if (oss_cdevs[i]->osdev == osdev) { oss_cdevs[i]->d = NULL; oss_cdevs[i]->osdev = NULL; strcpy (oss_cdevs[i]->name, "Removed device"); } MUTEX_CLEANUP (osdev->mutex); } int oss_register_device (oss_device_t * osdev, const char *name) { if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) { cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); osdev->name = "Unknown device"; } strcpy (osdev->name, name); return 0; } int oss_disable_device (oss_device_t * osdev) { int i; /* * This routine should check if the device is ready to be unloaded (no devices are in use). * If the device cannot be unloaded this routine must return OSS_EBUSY. * * If the device can be unloaded then disable any timers or other features that may cause the * device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. * However the interrupt handler should still stay enabled. The low level driver will call * oss_unregister_interrupts() after it has cleared the interrupt enable register. */ if (osdev->refcount > 0) { return OSS_EBUSY; } /* * Now mark all devices unavailable (for the time being) */ for (i = 0; i < num_mixers; i++) if (mixer_devs[i]->osdev == osdev) { mixer_devs[i]->unloaded = 1; } for (i = 0; i < num_mididevs; i++) { if (midi_devs[i]->osdev == osdev) { midi_devs[i]->unloaded = 1; } } for (i = 0; i < num_audio_engines; i++) if (audio_engines[i]->osdev == osdev) { audio_uninit_device (i); } return 0; } void oss_unregister_device (oss_device_t * osdev) { } #ifdef MUTEX_CHECKS static int oss_context = 0; /* 0=user context, 1=interrupt context */ #endif static int ossintr (void *idata) { oss_device_t *osdev = idata; oss_native_word flags; #ifdef MUTEX_CHECKS int saved_context; saved_context = oss_context; if (oss_context == 1) cmn_err (CE_WARN, "Recursive interrupt\n"); oss_context = 1; #endif MUTEX_ENTER_IRQDISABLE (osdev->mutex, flags); if (!osdev->tophalf_handler (osdev)) { MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); #ifdef MUTEX_CHECKS oss_context = saved_context; #endif return ISTAT_NONE; } if (osdev->bottomhalf_handler != NULL) osdev->bottomhalf_handler (osdev); MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); #ifdef MUTEX_CHECKS oss_context = saved_context; #endif return ISTAT_ASSERTED; } int oss_register_interrupts (oss_device_t * osdev, int intrnum, oss_tophalf_handler_t top, oss_bottomhalf_handler_t bottom) { int err; if (intrnum != 0) { cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum, osdev->name); return OSS_EINVAL; } if (osdev == NULL) { cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n"); return OSS_EINVAL; } if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL) { cmn_err (CE_WARN, "Interrupts already registered for %s\n", osdev->name); return OSS_EINVAL; } if (top == NULL) { cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name); return OSS_EINVAL; } osdev->tophalf_handler = top; osdev->bottomhalf_handler = bottom; if (cm_intr_attach (osdev->key, ossintr, osdev, osdev->drvinfo, &osdev->intr_cookie) == 0) { cmn_err (CE_WARN, "cm_intr_attach failed for %s\n", osdev->nick); } return 0; } void oss_unregister_interrupts (oss_device_t * osdev) { if (osdev->intr_cookie != NULL) cm_intr_detach (osdev->intr_cookie); osdev->intr_cookie = NULL; } static char * ksprintn (ul, base, lenp) register u_long ul; register int base, *lenp; { /* A long in base 8, plus NULL. */ static char buf[sizeof (long) * NBBY / 3 + 2]; register char *p; p = buf; do { *++p = "0123456789abcdef"[ul % base]; } while (ul /= base); if (lenp) *lenp = p - buf; return (p); } int oss_sprintf (char *buf, char *cfmt, ...) { const char *fmt = cfmt; register char *p, *bp; register int ch, base; unsigned long ul; int lflag; int count = 10; va_list ap; va_start (ap, fmt); for (bp = buf;;) { while ((ch = *(unsigned char *) fmt++) != '%') if ((*bp++ = ch) == '\0') { va_end (ap); return ((bp - buf) - 1); } lflag = 0; reswitch: switch (ch = *(unsigned char *) fmt++) { case 'l': lflag = 1; goto reswitch; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': goto reswitch; case 'c': *bp++ = va_arg (ap, int); break; case 's': p = va_arg (ap, char *); while (*bp++ = *p++) ; --bp; break; case 'd': ul = lflag ? va_arg (ap, long) : va_arg (ap, int); if ((long) ul < 0) { *bp++ = '-'; ul = -(long) ul; } base = 10; goto number; break; case 'o': ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); base = 8; goto number; break; case 'u': ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); base = 10; goto number; break; case 'x': ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int); base = 16; number: for (p = (char *) ksprintn (ul, base, NULL); ch = *p--;) *bp++ = ch; break; default: *bp++ = '%'; if (lflag) *bp++ = 'l'; /* FALLTHROUGH */ case '%': *bp++ = ch; } } /* va_end(ap); */ } static physreq_t * build_physreq (int bits) { physreq_t *pr; long long tmp; if ((pr = physreq_alloc (KM_SLEEP)) == NULL) return NULL; pr->phys_align = 4096; pr->phys_boundary = 0; pr->phys_dmasize = bits; pr->phys_max_scgth = 0; pr->phys_flags = PREQ_PHYSCONTIG; if (physreq_prep (pr, KM_SLEEP) != B_TRUE) { cmn_err (CE_WARN, "osscore: physreq_prep failed\n"); } return pr; } /* * Driver info device file */ static int drvinfo_busy = 0; static int drvinfo_len = 0, drvinfo_ptr = 0; static char *drvinfo_buf = NULL; #define DRVINFO_SIZE 4096 static int drvinfo_open (int dev, int dev_class, struct fileinfo *file, int recursive, int open_flags, int *redirect) { int i; if (drvinfo_busy) return OSS_EBUSY; drvinfo_busy = 1; if ((drvinfo_buf = KERNEL_MALLOC (DRVINFO_SIZE)) == NULL) { cmn_err (CE_WARN, "Cannot allocate drvinfo buffer\n"); return OSS_ENOMEM; } drvinfo_len = 0; drvinfo_ptr = 0; for (i = 1; i < oss_num_cdevs; i++) if (oss_cdevs[i]->name[0] != 'N' || oss_cdevs[i]->name[1] != 'O' || oss_cdevs[i]->name[2] != 'N' || oss_cdevs[i]->name[3] != 'E') /* Not a dummy placeholder device */ if (DRVINFO_SIZE - drvinfo_len > 32) { char *s = drvinfo_buf + drvinfo_len; drvinfo_len += oss_sprintf (s, "%s %s %d\n", oss_cdevs[i]->name, oss_cdevs[i]->osdev->nick, i); } return 0; } static void drvinfo_close (int dev, struct fileinfo *file) { KERNEL_FREE (drvinfo_buf); drvinfo_buf = NULL; drvinfo_len = 0; drvinfo_ptr = 0; drvinfo_busy = 0; } static int drvinfo_read (int dev, struct fileinfo *file, uio_t * buf, int count) { /* * Return at most 'count' bytes from the drvinfo_buf. */ int l, c; l = count; c = drvinfo_len - drvinfo_ptr; if (l > c) l = c; if (l <= 0) return 0; if (uiomove (&drvinfo_buf[drvinfo_ptr], l, UIO_READ, buf) != 0) return OSS_EFAULT; drvinfo_ptr += l; return l; } static oss_cdev_drv_t drvinfo_cdev_drv = { drvinfo_open, drvinfo_close, drvinfo_read }; static void install_drvinfo (oss_device_t * osdev) { oss_install_chrdev (osdev, "ossinfo", OSS_DEV_MISC, 0, &drvinfo_cdev_drv, 0); } /* * Driver entry point routines */ int _load () { int err = 0; oss_device_t *osdev; time_t t; if ((err = drv_attach (&oss_drvinfo)) != 0) { cmn_err (CE_WARN, "drv_attach failed %d\n", err); return err; } #ifdef LICENSED_VERSION if (drv_getparm (TIME, &t) != 0) { cmn_err (CE_WARN, "drv_getparm(TIME) failed\n"); return EBUSY; } if (!oss_license_handle_time (t)) { cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); cmn_err (CE_CONT, "Please download the latest version from www.opensound.com\n"); oss_expired = 1; } #endif if ((osdev = osdev_create (NULL, DRV_VIRTUAL, 0, "oss", NULL)) == NULL) { cmn_err (CE_WARN, "Creating osdev failed\n"); return ENOMEM; } /* * Allocate physrec structures for various memory ranges. Remember to free them in the _load * entry point. */ physreq_isa = build_physreq (24); physreq_28bit = build_physreq (28); physreq_30bit = build_physreq (30); physreq_31bit = build_physreq (31); physreq_32bit = build_physreq (32); /* * Create the BCB structure */ oss_bcb = bcb_alloc (KM_SLEEP); oss_bcb->bcb_addrtypes = BA_UIO; oss_bcb->bcb_flags = 0; oss_bcb->bcb_max_xfer = 0; oss_bcb->bcb_granularity = 1; oss_bcb->bcb_physreqp = physreq_32bit; bcb_prep (oss_bcb, KM_SLEEP); install_drvinfo (osdev); oss_common_init (osdev); oss_register_device (osdev, "OSS core services"); return 0; } int _unload () { int i; static int already_unloaded = 0; if (oss_open_devices > 0) return EBUSY; drv_detach (&oss_drvinfo); if (already_unloaded) return 0; already_unloaded = 1; oss_unload_drivers (); physreq_free (physreq_isa); physreq_free (physreq_28bit); physreq_free (physreq_30bit); physreq_free (physreq_31bit); physreq_free (physreq_32bit); bcb_free (oss_bcb); for (i = 0; i < oss_nblocks; i++) KERNEL_FREE (oss_mem_blocks[i]); oss_nblocks = 0; return 0; } void oss_pci_byteswap (oss_device_t * osdev, int mode) { // NOP } void oss_pcie_init (oss_device_t * osdev, int flags) { /* TODO: Should we do something? */ } int pci_read_config_byte (oss_device_t * osdev, offset_t where, unsigned char *val) { *val = 0; if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == sizeof (*val)) return PCIBIOS_SUCCESSFUL; return PCIBIOS_FAILED; } int pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) { *val = 0; if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == sizeof (*val)) return PCIBIOS_SUCCESSFUL; return PCIBIOS_FAILED; } int pci_read_config_word (oss_device_t * osdev, offset_t where, unsigned short *val) { *val = 0; #if 1 if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == sizeof (*val)) return PCIBIOS_SUCCESSFUL; #else switch (where) { case PCI_VENDOR_ID: *val = osdev->vendor; return PCIBIOS_SUCCESSFUL; break; case PCI_DEVICE_ID: *val = osdev->product; return PCIBIOS_SUCCESSFUL; break; } #endif return PCIBIOS_FAILED; } int pci_read_config_dword (oss_device_t * osdev, offset_t where, unsigned int *val) { *val = 0; if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) == sizeof (*val)) return PCIBIOS_SUCCESSFUL; return PCIBIOS_FAILED; } int pci_write_config_byte (oss_device_t * osdev, offset_t where, unsigned char val) { if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) == sizeof (val)) return PCIBIOS_SUCCESSFUL; return PCIBIOS_FAILED; } int pci_write_config_word (oss_device_t * osdev, offset_t where, unsigned short val) { if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) == sizeof (val)) return PCIBIOS_SUCCESSFUL; return PCIBIOS_FAILED; } int pci_write_config_dword (oss_device_t * osdev, offset_t where, unsigned int val) { if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) == sizeof (val)) return PCIBIOS_SUCCESSFUL; return PCIBIOS_FAILED; } /* * Entry point routines */ static int oss_config (cfg_func_t func, void *idata, rm_key_t key) { return EOPNOTSUPP; } int oss_devinfo (void *idata, channel_t channel, di_parm_t parm, void *valp) { switch (parm) { case DI_MEDIA: break; case DI_SIZE: break; case DI_RBCBP: *(bcb_t **) valp = oss_bcb; return 0; break; case DI_WBCBP: *(bcb_t **) valp = oss_bcb; return 0; break; case DI_PHYS_HINT: break; } return EOPNOTSUPP; } void oss_biostart (void *idata, channel_t channel, buf_t * bp) { int dev = channel; oss_cdev_t *cdev; int count = bp->b_un.b_uio->uio_resid; int retval; if ((cdev = oss_cdevs[dev]) == NULL) { bioerror (bp, ENXIO); biodone (bp); return; } cdev->file.acc_flags = 0; if (bp->b_flags & B_READ) { /* Read operation */ if (cdev->d->read == NULL) { bioerror (bp, ENXIO); biodone (bp); return; } retval = cdev->d->read (cdev->instance, &cdev->file, bp->b_un.b_uio, count); if (retval < 0) bioerror (bp, -retval); else if (retval < count) bp->b_resid = count - retval; biodone (bp); return; } /* Write operation */ if (cdev->d->write == NULL) { bioerror (bp, ENXIO); biodone (bp); return; } retval = cdev->d->write (cdev->instance, &cdev->file, bp->b_un.b_uio, count); if (retval < 0) bioerror (bp, -retval); else if (retval < count) bp->b_resid = count - retval; biodone (bp); } int oss_open (void *idata, channel_t * channelp, int open_flags, cred_t * crp, queue_t * q) { oss_device_t *osdev; int dev = *channelp, tmpdev; oss_cdev_t *cdev; int retval; osdev = idata; if (dev >= OSS_MAX_CDEVS) return ENXIO; if (dev >= oss_num_cdevs) { return ENODEV; } if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) return ENODEV; if (cdev->d->open == NULL) { return ENODEV; } memset (&cdev->file, 0, sizeof (cdev->file)); cdev->file.mode = 0; cdev->file.acc_flags = open_flags; if (open_flags & FREAD && open_flags & FWRITE) cdev->file.mode = OPEN_READWRITE; else if (open_flags & FREAD) cdev->file.mode = OPEN_READ; else if (open_flags & FWRITE) cdev->file.mode = OPEN_WRITE; tmpdev = dev; retval = cdev->d->open (cdev->instance, cdev->dev_class, &cdev->file, 0, 0, &tmpdev); *channelp = tmpdev; dev = tmpdev; if (retval < 0) { return -retval; } oss_open_devices++; cdev->open_count++; return 0; } int oss_close (void *idata, channel_t channel, int oflags, cred_t * crp, queue_t * q) { oss_cdev_t *cdev; int dev; dev = channel; if (dev >= OSS_MAX_CDEVS) return ENXIO; if ((cdev = oss_cdevs[dev]) == NULL) return ENXIO; if (cdev->open_count == 0) /* Not opened */ return 0; cdev->d->close (cdev->instance, &cdev->file); oss_open_devices--; cdev->open_count--; return 0; } int oss_ioctl (void *idata, channel_t channel, int cmd, void *arg, int oflags, cred_t * crp, int *rvalp) { int dev = channel; int retval; int len = 0; char localbuf[256]; /* All frequently used ioctl calls fit in 256 bytes */ char *buf = localbuf, *auxbuf = NULL; oss_cdev_t *cdev; if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL) return *rvalp = ENXIO; if (oss_expired) return *rvalp = ENODEV; if (cmd & (SIOC_OUT | SIOC_IN)) { len = __SIOC_SIZE (cmd); if (len < 0) len = 0; if (len > sizeof (localbuf)) { /* * For the few largest ioctl calls we need to allocate an off-stack * buffer because otherwise the kernel stack will overflow. * This approach is slower but fortunately "sane" applications don't * use these ioctl calls too frequently. */ if ((auxbuf = KERNEL_MALLOC (len)) == NULL) { cmn_err (CE_WARN, "Failed to allocate an ioctl buffer of %d bytes\n", len); return *rvalp = ENOMEM; } buf = auxbuf; } if ((cmd & SIOC_IN) && len > 0) { if (copyin ((char *) arg, buf, len) == -1) { if (auxbuf != NULL) KERNEL_FREE (auxbuf); return *rvalp = EFAULT; } } } retval = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) buf); if ((cmd & SIOC_OUT) && len > 0) { if (copyout (buf, (char *) arg, len) == -1) { if (auxbuf != NULL) KERNEL_FREE (auxbuf); return *rvalp = EFAULT; } } *rvalp = (((retval) < 0) ? -(retval) : 0); if (auxbuf != NULL) KERNEL_FREE (auxbuf); return *rvalp; } #ifdef MUTEX_CHECKS typedef struct { int active; void *mutex; const char *filename; int line; int context; } mutex_debug_t; static mutex_debug_t mutex_tab[1024]; static int n_mutexes = 0; void push_mutex (void *mutex, const char *file, int line) { int i, n = -1; //cmn_err(CE_CONT, "Push mutex %08x, %s:%d, context=%d\n", mutex, file, line, oss_context); for (i = 0; n == -1 && i < n_mutexes; i++) if (!mutex_tab[i].active) n = i; else { if (mutex_tab[i].mutex == mutex && mutex_tab[i].context == oss_context) { cmn_err (CE_NOTE, "Mutex %08x already held\n", mutex); cmn_err (CE_CONT, " Locked by %s:%d\n", mutex_tab[i].filename, mutex_tab[i].line); cmn_err (CE_CONT, " Acquire by %s:%d\n", file, line); } } if (n == -1) { if (n_mutexes >= 1024) { cmn_err (CE_NOTE, "Mutex debug table full\n"); return; } n = n_mutexes++; } mutex_tab[n].active = 1; mutex_tab[n].mutex = mutex; mutex_tab[n].filename = file; mutex_tab[n].line = line; mutex_tab[n].context = oss_context; } void pop_mutex (void *mutex, const char *file, int line) { int i; //cmn_err(CE_CONT, "Pop mutex %08x, %s:%d, context=%d\n", mutex, file, line, oss_context); for (i = 0; i < n_mutexes; i++) if (mutex_tab[i].active && mutex_tab[i].mutex == mutex && mutex_tab[i].context == oss_context) { mutex_tab[i].active = 0; mutex_tab[i].filename = file; mutex_tab[i].line = line; return; } cmn_err (CE_NOTE, "Mutex %08x not locked (%s:%d), context=%d\n", mutex, file, line, oss_context); for (i = 0; i < n_mutexes; i++) if (mutex_tab[i].active == 0 && mutex_tab[i].mutex == mutex) { cmn_err (CE_CONT, " Previous unlock at %s:%d, context=%d\n", mutex_tab[i].filename, mutex_tab[i].line, mutex_tab[i].context); } } void print_mutexes (void) { int i, n = 0; for (i = 0; i < n_mutexes; i++) if (mutex_tab[i].active) n++; cmn_err (CE_CONT, "%d mutexes held\n", n); for (i = 0; i < n_mutexes; i++) if (mutex_tab[i].active) cmn_err (CE_CONT, " %08x %s:%d\n", mutex_tab[i].mutex, mutex_tab[i].filename, mutex_tab[i].line); } #endif int oss_get_procinfo(int what) { // TODO return OSS_EINVAL; }