Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file is part of Open Sound System.
Copyright (C) 4Front Technologies 1996-2008.
This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.
#include "oss_config.h" #include "midi_core.h" #include <oss_pci.h> #if !defined(SOL9) && !defined(SOL8) #include <sys/sunldi.h> #endif #include <sys/mman.h> #if 1
Some older DDI routines are used by OSS instead of the latest ones. In this way the same OSS binary can be run both under Sol10 and Sol11.
uint16_t ddi_mem_get16 (ddi_acc_handle_t handle, uint16_t * host_addr); uint32_t ddi_mem_get32 (ddi_acc_handle_t handle, uint32_t * host_addr); void ddi_mem_put16 (ddi_acc_handle_t handle, uint16_t * dev_addr, uint16_t value); void ddi_mem_put32 (ddi_acc_handle_t handle, uint32_t * dev_addr, uint32_t value); int ddi_mem_alloc (dev_info_t * dip, ddi_dma_lim_t * limits, uint_t length, uint_t flags, caddr_t * kaddrp, uint_t * real_length); void ddi_mem_free (caddr_t kaddr); #endif #if 0 /* TODO: Disable this debugging stuff */ unsigned char tmp_status = 0; # define UP_STATUS(v) OUTB(NULL, (tmp_status=tmp_status|(v)), 0x378) # define DOWN_STATUS(v) OUTB(NULL, (tmp_status=tmp_status&~(v)), 0x378) #else # define UP_STATUS(v) # define DOWN_STATUS(v) #endif static int oss_expired = 0; static oss_mutex_t osscore_mutex; #define TRC(x) #define TRC2(x) #define FIX_RET_VALUE(ret) (((ret)<0) ? -(ret) : 0) static volatile int open_devices = 0; static volatile int do_forceload = 0; static volatile int forceload_in_progress = 0;
MAX_CARDS must be larger than life. The system will panic if there are more sound devices (cards os sound chips) than MAX_CARDS.
#define MAX_CARDS 32 static oss_device_t *cards[MAX_CARDS]; int oss_num_cards = 0; int oss_detach_enabled = 0; #ifdef MUTEX_CHECKS static volatile int inside_intr = 0; /* For mutex debugging only */ #endif
These are the entry points into our driver that are called when the driver is loaded, during a system call, or in response to an interrupt.
static int oss_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int oss_attach (dev_info_t * dip, ddi_attach_cmd_t cmd); static int oss_detach (dev_info_t * dip, ddi_detach_cmd_t cmd); static void free_all_contig_memory (void);
DMA memory management
typedef struct contig_desc { int is_special; struct contig_desc *next; unsigned char *first_addr, *last_addr; unsigned long physaddr; void *orig_buf; oss_device_t *osdev; ddi_dma_handle_t handle; ddi_dma_handle_t dhandle; ddi_acc_handle_t dma_acc_handle; ddi_dma_cookie_t cookie; ddi_dma_win_t win; ddi_dma_seg_t seg; size_t size; } contig_desc; static contig_desc *contig_list = NULL; #ifdef MEMDEBUG typedef struct { void *addr; int size; char file[40]; int line; } mem_block_t; #define MAX_MEMBLOCKS 1024 static mem_block_t memblocks[MAX_MEMBLOCKS]; static int num_memblocks = 0; #endif #define CDEV_NUMHASH 79 // Prime number static oss_cdev_t *cdev_hash[CDEV_NUMHASH] = {NULL}; #define compute_cdev_hash(dev_class, instance) (dev_class*13+instance) % CDEV_NUMHASH static void unlink_cdev_hash(oss_cdev_t *this_cdev) { oss_cdev_t *cdev = cdev_hash[compute_cdev_hash(this_cdev->dev_class, this_cdev->instance)]; if (cdev == this_cdev) // First in the hash chain { cdev_hash[compute_cdev_hash(this_cdev->dev_class, this_cdev->instance)] = cdev->hl; return; } while (cdev != NULL) { if (cdev->hl == this_cdev) { cdev->hl = this_cdev->hl; return; } cdev = cdev->hl; } cmn_err(CE_WARN, "unlink_cdev_hash: Cannot find cdev %p\n", this_cdev); } #if 1
Unfortunately this handy function is not safe (incompatibilities between kenel versions/builds). So let's hope it gets moved to the kernel.
char * oss_get_procname (void) /* Return the command name of the current thread */ { return ttoproc (curthread)->p_user.u_comm; } #endif #ifdef MEMDEBUG void * oss_kmem_alloc (size_t size, int flags, char *file, int line) #else void * oss_kmem_alloc (size_t size, int flags) #endif {
This routine allocates a memory block and stores length of it in the beginning. This length information can be used when later unallocating the memory.
char *ptr; uint64_t *len; ptr = kmem_zalloc (size + sizeof (uint64_t), flags); if (ptr == NULL) return NULL; len = (uint64_t *) ptr; ptr += sizeof (uint64_t); *len = size + sizeof (uint64_t); #ifdef MEMDEBUG #if 1 { int i; for (i = 0; i < num_memblocks; i++) if (memblocks[i].addr == NULL) { memblocks[i].addr = ptr; memblocks[i].size = size; strncpy (memblocks[i].file, file, 39); memblocks[i].line = line; return ptr; } } #endif if (num_memblocks < MAX_MEMBLOCKS) { memblocks[num_memblocks].addr = ptr; memblocks[num_memblocks].size = size; strncpy (memblocks[num_memblocks].file, file, 39); memblocks[num_memblocks].line = line; num_memblocks++; } #endif return ptr; } void oss_kmem_free (void *addr) { uint64_t *len; char *ptr = addr; if (addr == NULL) return; ptr -= sizeof (uint64_t); len = (uint64_t *) ptr; kmem_free (ptr, *len); #ifdef MEMDEBUG { int i; for (i = 0; i < num_memblocks; i++) if (addr == memblocks[i].addr) { memblocks[i].addr = NULL; break; } } #endif } static const ddi_dma_attr_t dma_attr_pci = { DMA_ATTR_V0, // Version 0x00000000ULL, // Address low 0xfffffff0ULL, // Address high 0xffffffffULL, // Counter max 1ULL, // Default byte align 0x7f, // Burst size 0x1, // Minimum xfer size 0xffffffffULL, // Maximum xfer size 0xffffffffULL, // Max segment size 1, // S/G list length 1, // Granularity 0 // Flag }; #if !defined(SOL9) && !defined(SOL8) static void forceload_drivers (dev_t dev, cred_t * credp) {
This routine will be called whenever the first application tries to access OSS devices. It's purpose is to load all the drivers just in case they have not been loaded yet. In this way it's possible to guarantee that the audio device numbering doesn't change between reboots.
char path[20]; int i, err; ldi_handle_t lh; ldi_ident_t li; DDB (cmn_err (CE_NOTE, "Forceloading OSS devices\n")); if (ldi_ident_from_dev (dev, &li) != 0) { cmn_err (CE_NOTE, "ldi_ident_from_dev failed\n"); return; } if ((err = ldi_open_by_name ("/dev/sndstat", FWRITE, credp, &lh, li)) != 0) { if (err != ENODEV) cmn_err (CE_NOTE, "Forceload error %d (/dev/sndstat)\n", err); } else err = ldi_close (lh, FWRITE, credp); for (i = 0; i < MAX_MIXER_DEV; i++) { sprintf (path, "/dev/mixer%d", i); if ((err = ldi_open_by_name (path, FWRITE, credp, &lh, li)) != 0) { if (err != ENODEV) cmn_err (CE_NOTE, "Forceload error %d\n", err); } else err = ldi_close (lh, FWRITE, credp); } for (i = 0; i < MAX_AUDIO_DEVFILES; i++) { sprintf (path, "/dev/dsp%d", i); if ((err = ldi_open_by_name (path, FWRITE, credp, &lh, li)) != 0) { if (err != ENODEV) cmn_err (CE_NOTE, "Forceload error %d\n", err); } else err = ldi_close (lh, FWRITE, credp); } ldi_ident_release (li); DDB (cmn_err (CE_NOTE, "Forceloading OSS devices done\n")); } void oss_forceload_drivers (int dev, cred_t * cred_p) { if (do_forceload) { do_forceload = 0; forceload_in_progress = 1; forceload_drivers (dev, cred_p); forceload_in_progress = 0; } } #endif /*ARGSUSED*/ int oss_open (dev_t * dev_p, int open_flags, int otyp, cred_t * cred_p) { int retval; dev_t dev = *dev_p; oss_cdev_t *cdev; int tmpdev, maj; oss_native_word flags; #ifdef DO_TIMINGS oss_timing_printf ("**** oss_open(%x) ****", getminor (dev)); #endif //cmn_err(CE_CONT, "**** oss_open(%x) ****\n", getminor (dev)); //cmn_err(CE_CONT, " PID %d, cmd %s\n", GET_PROCESS_PID(x), GET_PROCESS_NAME(x)); maj = getmajor (dev); dev = getminor (dev); #if !defined(SOL9) && !defined(SOL8)
Handle driver forceload
if (forceload_in_progress) return ENODEV; oss_forceload_drivers (dev, cred_p); #endif if (dev >= OSS_MAX_CDEVS) return ENXIO; if (dev >= oss_num_cdevs) { return ENODEV; } if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) return ENODEV; if (cdev->d->open == NULL) { return ENODEV; } memset (&cdev->file, 0, sizeof (cdev->file)); cdev->file.mode = 0; cdev->file.acc_flags = open_flags; if (open_flags & FREAD && open_flags & FWRITE) cdev->file.mode = OPEN_READWRITE; else if (open_flags & FREAD) cdev->file.mode = OPEN_READ; else if (open_flags & FWRITE) cdev->file.mode = OPEN_WRITE; tmpdev = dev; retval = cdev->d->open (cdev->instance, cdev->dev_class, &cdev->file, 0, 0, &tmpdev); *dev_p = makedevice (maj, tmpdev); dev = tmpdev; if (retval < 0) { return -retval; } MUTEX_ENTER_IRQDISABLE (osscore_mutex, flags); open_devices++; cdev = oss_cdevs[dev]; /* Switch to the cdev that was actually opened */ cdev->open_count++; if (open_flags & FREAD && open_flags & FWRITE) cdev->file.mode = OPEN_READWRITE; else if (open_flags & FREAD) cdev->file.mode = OPEN_READ; else if (open_flags & FWRITE) cdev->file.mode = OPEN_WRITE; oss_reserve_device (cdev->osdev); //cmn_err(CE_CONT, "Increment open_devices=%d, refcount=%d\n", open_devices, cdev->osdev->refcount); MUTEX_EXIT_IRQRESTORE (osscore_mutex, flags); return 0; } /*ARGSUSED*/ int oss_close (dev_t dev, int flag, int otyp, cred_t * cred_p) { oss_cdev_t *cdev; oss_native_word flags; #ifdef DO_TIMINGS oss_timing_printf ("***** oss_close(%x) ****", getminor (dev)); #endif //cmn_err(CE_CONT, "***** oss_close(%x) ****\n", getminor (dev)); //cmn_err(CE_CONT, " PID %d, cmd %s\n", GET_PROCESS_PID(x), GET_PROCESS_NAME(x)); if (getminor (dev) >= OSS_MAX_CDEVS) { cmn_err (CE_NOTE, "Closing bad minor %d\n", getminor (dev)); return ENXIO; } dev = getminor (dev); if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) { cmn_err (CE_NOTE, "Closing undefined minor %d\n", getminor (dev)); return ENXIO; } if (cdev->open_count == 0) /* Not opened */ { cmn_err (CE_NOTE, "Closing minor %d that is not open\n", dev); return 0; } cdev->d->close (cdev->instance, &cdev->file); MUTEX_ENTER_IRQDISABLE (osscore_mutex, flags); open_devices -= cdev->open_count; //cmn_err(CE_CONT, "Decrement open_devices=%d\n", open_devices); oss_unreserve_device (cdev->osdev, cdev->open_count); cdev->open_count = 0; MUTEX_EXIT_IRQRESTORE (osscore_mutex, flags); return 0; } /*ARGSUSED*/ int oss_ioctl (dev_t dev, int cmd, intptr_t arg, int mode, cred_t * cred_p, int *rval_p) { int retval; int len = 0; char b[4096], *buf = b; oss_cdev_t *cdev; #ifdef DO_TIMINGS oss_timing_printf ("OSS ioctl(%x, %x, %x)", getminor (dev), cmd, arg); #endif *rval_p = 0; dev = getminor (dev); if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL) return ENXIO; if (cdev->open_count == 0) /* Not opened */ { cmn_err (CE_NOTE, "Call to ioctl on minor %d that is not open\n", dev); return ENXIO; } if (oss_expired) return ENODEV; if (mode & FKIOCTL) /* Called from kernel space */ buf = (char *) arg; else if (cmd & (SIOC_OUT | SIOC_IN)) { len = (cmd >> 16) & SIOCPARM_MASK; if (len < 0) len = 0; if (len > sizeof (b)) { cmn_err (CE_WARN, "Bad ioctl buffer size %d\n", len); return EFAULT; } if ((cmd & SIOC_IN) && len > 0) { if (copyin ((char *) arg, buf, len) == -1) return EFAULT; } } retval = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) buf); if (!(mode & FKIOCTL)) /* Not called from kernel space */ if ((cmd & SIOC_OUT) && len > 0) { if (copyout (buf, (char *) arg, len) == -1) return EFAULT; } return FIX_RET_VALUE (retval); } /*ARGSUSED*/ int oss_read (dev_t dev, struct uio *uiop, cred_t * credp) { int count = uiop->uio_resid; int retval; oss_cdev_t *cdev; dev = getminor (dev); if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->read == NULL) return ENXIO; if (cdev->open_count == 0) /* Not opened */ { cmn_err (CE_NOTE, "Call to read on minor %d that is not open\n", dev); return ENXIO; } cdev->file.acc_flags = uiop->uio_fmode; retval = cdev->d->read (cdev->instance, &cdev->file, uiop, count); return FIX_RET_VALUE (retval); } /*ARGSUSED*/ int oss_write (dev_t dev, struct uio *uiop, cred_t * cred_p) { int count = uiop->uio_resid; int retval; oss_cdev_t *cdev; dev = getminor (dev); if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->write == NULL) return ENXIO; if (cdev->open_count == 0) /* Not opened */ { cmn_err (CE_NOTE, "Call to write on minor %d that is not open\n", dev); return ENXIO; } cdev->file.acc_flags = uiop->uio_fmode; retval = cdev->d->write (cdev->instance, &cdev->file, uiop, count); return FIX_RET_VALUE (retval); } int oss_chpoll (dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp) { oss_cdev_t *cdev; oss_poll_event_t ev; int ret; #ifdef DO_TIMINGS oss_timing_printf ("***** oss_chpoll(%x) ****", getminor (dev)); #endif if (getminor (dev) >= OSS_MAX_CDEVS) return ENXIO; dev = getminor (dev); if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->chpoll == NULL) return ENXIO; if (cdev->open_count == 0) /* Not opened */ { cmn_err (CE_NOTE, "Call to chpoll on minor %d that is not open\n", dev); return ENXIO; } *reventsp = 0; ev.events = events; ev.anyyet = anyyet; ev.revents = 0; ev.php = NULL; ret = cdev->d->chpoll (cdev->instance, &cdev->file, &ev); if (ret < 0) { return -ret; } *reventsp |= ev.revents; if (ev.php != NULL) *phpp = ev.php; return 0; } #ifdef ALLOW_BUFFER_MAPPING int oss_devmap (dev_t dev, devmap_cookie_t handle, offset_t off, size_t len, size_t * maplen, uint_t model) { oss_cdev_t *cdev; oss_poll_event_t ev; int ret; #ifdef DO_TIMINGS oss_timing_printf ("***** oss_devmap(%x) ****", getminor (dev)); #endif if (getminor (dev) >= OSS_MAX_CDEVS) { return ENXIO; } dev = getminor (dev); if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) { return ENXIO; } if (cdev->open_count == 0) /* Not opened */ { cmn_err (CE_NOTE, "Call to devmap on minor %d that is not open\n", dev); return EPERM; } if (cdev->dev_class != OSS_DEV_DSP && cdev->dev_class != OSS_DEV_DSP_ENGINE) /* Only audio devices can be mapped */ { return ENXIO; } dev = cdev->instance; if (dev < 0 || dev >= num_audio_engines) return ENXIO; return ENOTSUP; } #endif #if 0 // Not used in misc modules. static struct cb_ops oss_cb_ops = { oss_open, oss_close, nodev, /* not a block driver */ nodev, /* no print routine */ nodev, /* no dump routine */ oss_read, oss_write, oss_ioctl, #ifdef ALLOW_BUFFER_MAPPING oss_devmap, #else nodev, /* no devmap routine */ #endif nodev, nodev, /* no segmap routine */ oss_chpoll, /* no chpoll routine */ ddi_prop_op, 0, /* not a STREAMS driver */ D_NEW | D_MP | D_64BIT, /* safe for multi-thread/multi-processor */ CB_REV }; static struct dev_ops oss_ops = { DEVO_REV, /* DEVO_REV indicated by manual */ 0, /* device reference count */ oss_getinfo, nulldev, nulldev, oss_attach, oss_detach, nodev, /* device reset routine */ &oss_cb_ops, NULL, /* bus operations */ NULL /* TODO: Power management */ }; #endif extern struct mod_ops mod_miscops; static struct modldrv modldrv = { &mod_miscops, "Open Sound System " OSS_VERSION_STRING " framework" // &oss_ops, }; static struct modlinkage modlinkage = { MODREV_1, /* MODREV_1 indicated by manual */ {(void *) &modldrv, NULL} /* termination of list of linkage structures */ }; static ddi_device_acc_attr_t acc_attr_neverswap = { DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC }; #ifdef OSS_BIG_ENDIAN static ddi_device_acc_attr_t acc_attr_le_swap = { DDI_DEVICE_ATTR_V0, DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC }; #endif /*ARGSUSED*/ static ddi_device_acc_attr_t * get_acc_attr (oss_device_t * osdev) { #ifdef OSS_BIG_ENDIAN if (osdev->swap_mode == 1) { return &acc_attr_le_swap; } else #endif { return &acc_attr_neverswap; } } void oss_load_options (oss_device_t * osdev, oss_option_map_t map[]) { int i, val; for (i = 0; map[i].name != NULL; i++) { if ((val = ddi_prop_get_int (DDI_DEV_T_ANY, osdev->dip, DDI_PROP_NOTPROM, map[i].name, -12345)) != -12345) { *map[i].ptr = val; } } } static int oss_attach (dev_info_t * dip, ddi_attach_cmd_t cmd) { oss_device_t *osdev; if (cmd != DDI_ATTACH) { cmn_err (CE_WARN, "oss_attach: Command %x\n", cmd); return (DDI_FAILURE); } if ((osdev = osdev_create (dip, DRV_VIRTUAL, 0, "osscore", NULL)) == NULL) { cmn_err (CE_WARN, "Creating osdev failed\n"); return DDI_FAILURE; } MUTEX_INIT (osdev, osscore_mutex, 0); oss_load_options (osdev, oss_global_options); oss_common_init (osdev); ddi_report_dev (dip); oss_register_device (osdev, "OSS core services"); return (DDI_SUCCESS); }
This is a pretty generic getinfo routine as describe in the manual.
/*ARGSUSED*/ static int oss_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void *arg, void **result) { dev_t dev; register int error; int instance; dev = (dev_t) arg; instance = getminor (dev) >> 4; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: *result = dip; error = DDI_SUCCESS; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *) (uintptr_t) instance; error = DDI_SUCCESS; break; default: *result = NULL; error = DDI_FAILURE; } #if 0 DDB (cmn_err (CE_CONT, "oss_getinfo: returns %d, result=%x minor=%d instance=%d dev=%x\n", error, *result, minor_num, instance, dev)); #endif return (error); }
_init, _info, and _fini support loading and unloading the driver.
int _init (void) { register int error = 0; error = mod_install (&modlinkage); return error; } int _info (struct modinfo *modinfop) { return (mod_info (&modlinkage, modinfop)); } int _fini (void) { int status; #ifdef MEMDEBUG int i; #endif if ((status = mod_remove (&modlinkage)) != 0) return (status); free_all_contig_memory (); #ifdef MEMDEBUG if (num_memblocks >= MAX_MEMBLOCKS) cmn_err (CE_NOTE, "All memory allocations were not checked\n"); for (i = 0; i < num_memblocks; i++) { if (memblocks[i].addr != NULL) { cmn_err (CE_NOTE, "Memory leak in %s:%d\n", memblocks[i].file, memblocks[i].line); } } #endif return status; }
When the osscore module is unloaded, oss_detach cleans up and frees the resources we allocated in oss_attach.
static int oss_detach (dev_info_t * dip, ddi_detach_cmd_t cmd) { static int already_unloaded = 0; if (cmd != DDI_DETACH) return DDI_FAILURE; if (already_unloaded) return DDI_SUCCESS; already_unloaded = 1; //cmn_err(CE_CONT, "Oss detach\n"); /* instance = ddi_get_instance (dip); */
Remove the minor nodes created in attach
ddi_remove_minor_node (dip, NULL); oss_unload_drivers (); MUTEX_CLEANUP (osscore_mutex); return (DDI_SUCCESS); }
Some support routines
void * memset (void *t, int c, size_t l) { int i; for (i = 0; i < l; i++) ((char *) t)[i] = c; return t; } #ifdef sparc
I/O functions that do byte swapping (for Sparc)
void oss_put16 (ddi_acc_handle_t handle, unsigned short *addr, unsigned short val) { val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); ddi_put16 (handle, addr, val); } void oss_put32 (ddi_acc_handle_t handle, unsigned int *addr, unsigned int val) { #ifdef OSS_BIG_ENDIAN val = ((val & 0x000000ff) << 24) | ((val & 0x0000ff00) << 8) | ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); #endif ddi_put32 (handle, addr, val); } unsigned short oss_get16 (ddi_acc_handle_t handle, unsigned short *addr) { unsigned short val; val = ddi_get16 (handle, addr); #ifdef OSS_BIG_ENDIAN val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); #endif return val; } unsigned int oss_get32 (ddi_acc_handle_t handle, unsigned int *addr) { unsigned int val; val = ddi_get32 (handle, addr); #ifdef OSS_BIG_ENDIAN val = ((val & 0x000000ff) << 24) | ((val & 0x0000ff00) << 8) | ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); #endif return val; } uint16_t oss_mem_get16 (ddi_acc_handle_t handle, uint16_t * addr) { uint16_t val; val = ddi_mem_get16 (handle, addr); #ifdef OSS_BIG_ENDIAN val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); #endif return val; } uint32_t oss_mem_get32 (ddi_acc_handle_t handle, uint32_t * addr) { uint32_t val; val = ddi_mem_get32 (handle, addr); #ifdef OSS_BIG_ENDIAN val = ((val & 0x000000ff) << 24) | ((val & 0x0000ff00) << 8) | ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); #endif return val; } void oss_mem_put16 (ddi_acc_handle_t handle, uint16_t * addr, uint16_t val) { #ifdef OSS_BIG_ENDIAN val = ((val >> 8) & 0xff) | ((val & 0xff) << 8); #endif ddi_mem_put16 (handle, addr, val); } void oss_mem_put32 (ddi_acc_handle_t handle, uint32_t * addr, uint32_t val) { #ifdef OSS_BIG_ENDIAN val = ((val & 0x000000ff) << 24) | ((val & 0x0000ff00) << 8) | ((val & 0x00ff0000) >> 8) | ((val & 0xff000000) >> 24); #endif ddi_mem_put32 (handle, addr, val); } #endif void oss_pci_byteswap (oss_device_t * osdev, int mode) { osdev->swap_mode = mode; } void oss_pcie_init (oss_device_t * osdev, int flags) { /* TODO: Should we do something? */ } /*ARGSUSED*/ caddr_t oss_map_pci_ioaddr (oss_device_t * osdev, int nr, int io) { caddr_t addr; off_t region_size; int err; ddi_device_acc_attr_t *acc_attr; if (nr >= OSS_MAX_ACC_HANDLE) { cmn_err(CE_WARN, "Too large I/O region number %d\n", nr); return 0; } acc_attr = get_acc_attr (osdev); if ((err = ddi_dev_regsize (osdev->dip, nr + 1, ®ion_size)) != DDI_SUCCESS) { cmn_err (CE_WARN, "Getting device regsize failed (%d)\n", err); return 0; } if ((err = ddi_regs_map_setup (osdev->dip, nr + 1, &addr, 0, region_size, acc_attr, &osdev->acc_handle[nr])) != DDI_SUCCESS) { cmn_err (CE_WARN, "Register setup failed (%d)\n", err); return 0; } return addr; } void oss_unmap_pci_ioaddr(oss_device_t * osdev, int nr) { if (nr >= OSS_MAX_ACC_HANDLE) { cmn_err(CE_WARN, "Too large I/O region number %d\n", nr); return; } ddi_regs_map_free(&osdev->acc_handle[nr]); } int pci_read_config_byte (oss_device_t * osdev, offset_t where, unsigned char *val) { if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; #if defined (sparc) if (where == PCI_INTERRUPT_LINE) *val = 7; /* PC emulation hack */ else #endif *val = pci_config_get8 (osdev->pci_config_handle, where); return PCIBIOS_SUCCESSFUL; } int pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val) { int ret; if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; ret = pci_read_config_byte (osdev, where, val); return ret; } int pci_read_config_word (oss_device_t * osdev, offset_t where, unsigned short *val) { if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; *val = pci_config_get16 (osdev->pci_config_handle, where); return PCIBIOS_SUCCESSFUL; } int pci_read_config_dword (oss_device_t * osdev, offset_t where, unsigned int *val) { if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; *val = pci_config_get32 (osdev->pci_config_handle, where); return PCIBIOS_SUCCESSFUL; } int pci_write_config_byte (oss_device_t * osdev, offset_t where, unsigned char val) { if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; pci_config_put8 (osdev->pci_config_handle, where, val); return PCIBIOS_SUCCESSFUL; } int pci_write_config_word (oss_device_t * osdev, offset_t where, unsigned short val) { if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; pci_config_put16 (osdev->pci_config_handle, where, val); return PCIBIOS_SUCCESSFUL; } int pci_write_config_dword (oss_device_t * osdev, offset_t where, unsigned int val) { if (osdev->dev_type != DRV_PCI) return PCIBIOS_FAILED; pci_config_put32 (osdev->pci_config_handle, where, val); return PCIBIOS_SUCCESSFUL; } void * memcpy (void *t, const void *s, size_t l) { bcopy (s, t, l); return t; } #ifdef MEMDEBUG void * oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, oss_native_word * phaddr, oss_dma_handle_t *dma_handle, char *file, int line) #else void * oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, oss_native_word * phaddr, oss_dma_handle_t *dma_handle) #endif {
Allocate physically contiguous memory (suitable for DMA).
The memlimit parameter is equal to oss_alloc_dmabuf().
int err; #if defined(sparc) uint_t len; #else size_t len; #endif uint_t count; contig_desc *desc; ddi_dma_attr_t dma_attr; ddi_device_acc_attr_t *acc_attr; int flags = DDI_DMA_REDZONE | DDI_DMA_CONSISTENT | DDI_DMA_READ | DDI_DMA_WRITE; if (osdev == NULL) { cmn_err (CE_WARN, "oss_contig_malloc: osdev==NULL\n"); return NULL; } acc_attr = get_acc_attr (osdev); memcpy (&dma_attr, &dma_attr_pci, sizeof (ddi_dma_attr_t)); dma_attr.dma_attr_addr_hi = memlimit; desc = KERNEL_MALLOC (sizeof (contig_desc)); if (desc == NULL) { cmn_err (CE_WARN, "Failed to allocate contig buffer descriptor\n"); return NULL; } desc->osdev = osdev; desc->next = NULL; desc->is_special = 0; if ((err = ddi_dma_alloc_handle (osdev->dip, &dma_attr, DDI_DMA_SLEEP, NULL, &desc->dhandle)) != DDI_SUCCESS) { cmn_err (CE_WARN, "Failed to allocate pci DMA handle (%d)\n", err); return NULL; } if (dma_handle != NULL) *dma_handle = desc->dhandle; #ifndef IOMEM_DATA_UNCACHED #define IOMEM_DATA_UNCACHED 0 // Fix for Solaris 10 #endif #if defined(sparc) if ((err = ddi_mem_alloc (osdev->dip, NULL, size + 4096, 0, (caddr_t *) & desc->first_addr, (uint_t *) & len)) != DDI_SUCCESS) #else if ((err = ddi_dma_mem_alloc (desc->dhandle, size + 4096, acc_attr, flags, DDI_DMA_SLEEP, NULL, (caddr_t *) & desc->first_addr, (size_t *) & len, &desc->dma_acc_handle)) != DDI_SUCCESS) #endif { cmn_err (CE_WARN, "Failed to allocate %d bytes of contig memory (%d)\n", size, err); return NULL; } desc->size = len; desc->orig_buf = desc->first_addr; if (desc->first_addr == NULL) { cmn_err (CE_WARN, "Can't allocate a contig buffer\n"); return NULL; } DDB (cmn_err (CE_CONT, "Allocated contig memory, addr=%x, len=%d\n", (unsigned int) desc->first_addr, desc->size)); if ((err = ddi_dma_addr_bind_handle (desc->dhandle, NULL, (char *) desc->first_addr, desc->size, flags | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &desc->cookie, &count)) != DDI_SUCCESS) { cmn_err (CE_WARN, "Contig address setup failed (%d)\n", err); return NULL; } desc->physaddr = desc->cookie.dmac_address; desc->last_addr = desc->first_addr + desc->size - 1; desc->first_addr = (void *) (((unsigned long) desc->first_addr + 4095) & ~4095); desc->physaddr = (desc->physaddr + 4095) & ~4095; *phaddr = desc->physaddr; desc->next = contig_list; contig_list = desc; /* HW_PRINTF(("Alloc contig: %x-%x, ph=%x\n", desc->first_addr, desc->last_addr, desc->physaddr)); */ return desc->first_addr; } /*ARGSUSED*/ void oss_contig_free (oss_device_t * osdev, void *p, int sz) { int err; contig_desc *d, *desc = NULL, *prev = NULL; if (p == NULL) return; d = contig_list; while (d && desc == NULL) { if (d->is_special) { prev = d; d = d->next; continue; } if (d->first_addr == p) { desc = d; break; } prev = d; d = d->next; } if (desc == NULL) { cmn_err (CE_WARN, "OSS: Can't free memory\n"); return; } if ((err = ddi_dma_unbind_handle (desc->dhandle)) != DDI_SUCCESS) cmn_err (CE_WARN, "Failed to free DMA handle (%d)\n", err); #if defined(sparc) ddi_mem_free (desc->orig_buf); #else if (desc->dma_acc_handle == NULL) cmn_err (CE_WARN, "desc->dma_acc_handle==NULL\n"); else ddi_dma_mem_free (&desc->dma_acc_handle); #endif ddi_dma_free_handle (&desc->dhandle); if (desc == contig_list) contig_list = desc->next; else { prev->next = desc->next; } KERNEL_FREE (desc); } static void free_all_contig_memory (void) { contig_desc *p, *desc = contig_list; while (desc != NULL) { p = desc; desc = desc->next; if (p->is_special) continue; oss_contig_free (p->osdev, p->orig_buf, 0); } contig_list = NULL; } #if !defined(SOL9) && !defined(DISABLE_FMA) /*ARGSUSED*/ static int oss_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *osdev_) { pci_ereport_post(dip, err, NULL); return err->fme_status; } #endif oss_device_t * osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, const char *handle) { oss_device_t *osdev = NULL; int i; ddi_iblock_cookie_t iblk; static int license_checked=0; #ifdef LICENSED_VERSION if (!license_checked) { timestruc_t ts; license_checked = 1; gethrestime (&ts); if (!oss_license_handle_time (ts.tv_sec)) { cmn_err (CE_WARN, "This version of Open Sound System has expired\n"); cmn_err (CE_CONT, "Please download the latest version from www.opensound.com\n"); oss_expired = 1; } } #endif if (handle == NULL) handle = nick;
Don't accept any more drivers if expired
if (oss_expired && oss_num_cards > 0) return NULL;
Check if the same device is being reinserted.
for (i = 0; i < oss_num_cards; i++) { if (cards[i]->available) /* Not deleted */ continue; if (cards[i]->dip == dip) { osdev = cards[i]; break; } } #if 1 if (osdev == NULL) {
Check if there are some deleted devices.
for (i = 0; i < oss_num_cards; i++) { if (cards[i]->available) /* Not deleted */ continue; osdev = cards[i]; break; } } #endif if (osdev == NULL) { for (i=0;i<oss_num_cards;i++) if (!cards[i]->available) { osdev = cards[i]; break; } } if (osdev == NULL) { if (oss_num_cards >= MAX_CARDS) { cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", MAX_CARDS); return NULL; } if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) { cmn_err (CE_WARN, "osdev_create: Out of memory\n"); return NULL; } memset (osdev, 0, sizeof (*osdev)); osdev->cardnum = oss_num_cards; cards[oss_num_cards++] = osdev; } osdev->dip = dip; osdev->osid = dip; osdev->available = 1; osdev->instance = instance; osdev->dev_type = dev_type; osdev->devc = NULL; sprintf (osdev->nick, "%s%d", nick, instance); strcpy (osdev->modname, nick); osdev->num_audio_engines = 0; osdev->num_audioplay = 0; osdev->num_audiorec = 0; osdev->num_audioduplex = 0; osdev->num_mididevs = 0; osdev->num_mixerdevs = 0; osdev->first_mixer = -1; switch (dev_type) { case DRV_PCI: if (pci_config_setup (dip, &osdev->pci_config_handle) != DDI_SUCCESS) cmn_err (CE_NOTE, "pci_config_setup() failed\n"); #if !defined(SOL9) && !defined(DISABLE_FMA) osdev->fm_capabilities = DDI_FM_EREPORT_CAPABLE | DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE; ddi_fm_init(dip, &osdev->fm_capabilities, &iblk); ddi_fm_handler_register(dip, oss_fm_error_cb, (void*)osdev); pci_ereport_setup(dip); #endif break; case DRV_VIRTUAL: case DRV_VMIX: case DRV_STREAMS: #if !defined(SOL9) && !defined(DISABLE_FMA) osdev->fm_capabilities=DDI_FM_EREPORT_CAPABLE; ddi_fm_init(dip, &osdev->fm_capabilities, &iblk); #endif break; case DRV_USB: /* NOP */ break; default: cmn_err (CE_WARN, "Bad device type\n"); return NULL; }
Create the device handle
switch (dev_type) { case DRV_PCI: { unsigned int subvendor; pci_read_config_dword (osdev, 0x2c, &subvendor); sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance); } break; case DRV_USB: /* TODO: Get the vendor information */ sprintf (osdev->handle, "USB-%s%d", handle, instance); break; default: sprintf (osdev->handle, "%s%d", handle, instance); } return osdev; } oss_device_t * osdev_clone (oss_device_t * orig_osdev, int new_instance) { oss_device_t *osdev; osdev = PMALLOC (NULL, sizeof (*osdev)); if (osdev == NULL) { cmn_err (CE_WARN, "osdev_create: Out of memory\n"); return NULL; } memcpy (osdev, orig_osdev, sizeof (*osdev)); osdev->dev_type = DRV_CLONE; osdev->instance = new_instance; sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance); sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance); return osdev; } void osdev_delete (oss_device_t * osdev) { int i; if (osdev == NULL) return; if (!osdev->available) /* Already deleted */ return; osdev->available = 0; switch (osdev->dev_type) { case DRV_PCI: #if !defined(SOL9) && !defined(DISABLE_FMA) ddi_fm_handler_unregister(osdev->dip); pci_ereport_teardown(osdev->dip); #endif pci_config_teardown (&osdev->pci_config_handle); #if !defined(SOL9) && !defined(DISABLE_FMA) ddi_fm_fini(osdev->dip); #endif osdev->pci_config_handle = NULL; break; case DRV_VIRTUAL: case DRV_VMIX: case DRV_STREAMS: #if !defined(SOL9) && !defined(DISABLE_FMA) ddi_fm_fini(osdev->dip); #endif break; } ddi_remove_minor_node (osdev->dip, NULL);
Mark all minor nodes for this module as invalid.
for (i = 0; i < oss_num_cdevs; i++) if (oss_cdevs[i] != NULL) if (oss_cdevs[i]->osdev == osdev) { unlink_cdev_hash(oss_cdevs[i]); oss_cdevs[i]->d = NULL; oss_cdevs[i]->osdev = NULL; oss_cdevs[i]->active = 0; /* Device removed */ } } int oss_get_cardinfo (int cardnum, oss_card_info * ci) {
Print information about a 'card' in a format suitable for /dev/sndstat
if (cardnum < 0 || cardnum >= oss_num_cards) return OSS_ENXIO; if (cards[cardnum]->name != NULL) strncpy (ci->longname, cards[cardnum]->name, 128); ci->shortname[127] = 0; if (cards[cardnum]->nick != NULL) strncpy (ci->shortname, cards[cardnum]->nick, 16); ci->shortname[15] = 0; if (cards[cardnum]->hw_info != NULL) strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info) - 1); ci->hw_info[sizeof (ci->hw_info) - 1] = 0; ci->intr_count = cards[cardnum]->intrcount; ci->ack_count = cards[cardnum]->ackcount; return 0; } static int grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment) { oss_cdev_t **old=*arr, **new = *arr; int old_size = *size; int new_size = *size; new_size += increment; if ((new=PMALLOC(osdev, new_size * element_size))==NULL) return 0; memset(new, 0, new_size * element_size); if (old != NULL) memcpy(new, old, old_size * element_size); *size = new_size; *arr = new; if (old != NULL) PMFREE(osdev, old); return 1; } /*ARGSUSED*/ void oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, int instance, oss_cdev_drv_t * drv, int flags) {
oss_install_chrdev creates a character device (minor). However if name==NULL the device will not be exported (made visible to userland clients).
int num; int hash_link; oss_cdev_t *cdev = NULL; if (dev_class != OSS_DEV_STATUS) if (oss_expired && instance > 0) return;
Find if this dev_class&instance already exists (after previous module detach).
if (flags & CHDEV_REPLUG) for (num = 0; num < oss_num_cdevs; num++) if (oss_cdevs[num]->d == NULL) /* Unloaded driver */ if (oss_cdevs[num]->dev_class == dev_class && oss_cdevs[num]->instance == instance) { cdev = oss_cdevs[num]; break; } if (cdev == NULL) { if (oss_num_cdevs >= OSS_MAX_CDEVS) { if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100)) { cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n"); return; } } if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) { cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); return; } num = oss_num_cdevs++; } memset (cdev, 0, sizeof (*cdev)); cdev->dev_class = dev_class; cdev->instance = instance; cdev->d = drv; cdev->active = 1; cdev->osdev = osdev; if (name != NULL) strncpy (cdev->name, name, sizeof (cdev->name) - 1); else strcpy (cdev->name, "NONE"); cdev->name[sizeof (cdev->name) - 1] = 0; oss_cdevs[num] = cdev; cdev->minor = num;
Add to the cdev_hash list.
hash_link = compute_cdev_hash (dev_class, instance); cdev->hl = cdev_hash[hash_link]; cdev_hash[hash_link] = cdev;
Export the device only if name != NULL
if (name != NULL) { char tmp[64], *s; char *dev_type = "oss_sysdev";
Convert "oss/device/node" style names to the "device,node" style naming required by Solaris.
if (name[0] == 'o' && name[3] == '/') /* oss/ prefix */ { strcpy (tmp, name + 4); name = tmp; s = tmp; while (*s) { if (*s == '/') *s = ','; s++; } dev_type = "oss_audio"; } if (ddi_create_minor_node (osdev->dip, name, S_IFCHR, num, dev_type, 0) == DDI_FAILURE) { cmn_err (CE_WARN, "ddi_create_minor_node failed\n"); } } } int oss_find_minor (int dev_class, int instance) { oss_cdev_t *cdev; cdev = cdev_hash[compute_cdev_hash(dev_class, instance)]; while (cdev != NULL) { if (cdev->d != NULL && cdev->dev_class == dev_class && cdev->instance == instance) return cdev->minor; cdev = cdev->hl; /* Next in the hash chain */ } return OSS_EIO; } int __oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, oss_uint64_t maxaddr, int direction) { int err; #if defined(sparc) uint_t len; #else size_t len; #endif uint_t ncookies; contig_desc *desc; oss_device_t *osdev = dmap->osdev; ddi_dma_attr_t dma_attr; int size = 32 * 1024; extern int dma_buffsize; ddi_device_acc_attr_t *acc_attr; int flags = DDI_DMA_REDZONE | DDI_DMA_CONSISTENT | (direction == OPEN_READ) ? DDI_DMA_READ : DDI_DMA_WRITE; if (osdev == NULL) { cmn_err (CE_WARN, "oss_alloc_dmabuf: osdev==NULL\n"); return OSS_EIO; } acc_attr = get_acc_attr (osdev); if (dma_buffsize > 16 && dma_buffsize <= 128) size = dma_buffsize * 1024;
Some applications and virtual drivers need shorter buffer.
if (dmap->flags & DMAP_SMALLBUF) { size = SMALL_DMABUF_SIZE; } else if (dmap->flags & DMAP_MEDIUMBUF) { size = MEDIUM_DMABUF_SIZE; } if (alloc_flags & DMABUF_LARGE) size = 256 * 1024; if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024) size = 32 * 1024; memcpy (&dma_attr, &dma_attr_pci, sizeof (ddi_dma_attr_t)); dma_attr.dma_attr_addr_hi = maxaddr; #ifndef SOL9 if (osdev->dev_type == DRV_PCI) dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; #endif if (dmap->dmabuf != NULL) return 0; /* Already done */ dmap->dma_parms.state = 0; dmap->dma_parms.enabled = 1; dmap->dma_parms.ignore = 0; if (osdev->dip == NULL) { cmn_err (CE_WARN, "oss_alloc_dmabuf: osdev->dip==NULL\n"); return OSS_EIO; } if (dmap == NULL) { cmn_err (CE_WARN, "oss_alloc_dmabuf: dmap==NULL\n"); return OSS_EIO; } if ((err = ddi_dma_alloc_handle (osdev->dip, &dma_attr, DDI_DMA_SLEEP, NULL, &dmap->dmabuf_dma_handle)) != DDI_SUCCESS) { cmn_err (CE_WARN, "Failed to allocate DMA handle (error %d)\n", err); return OSS_ENOMEM; } dmap->dmabuf = NULL; dmap->buffsize = size; err = -1; while (err != DDI_SUCCESS && dmap->dmabuf == NULL && dmap->buffsize >= 4096) { #if defined(sparc) if ((err = ddi_mem_alloc (osdev->dip, NULL, dmap->buffsize, 0, (caddr_t *) & dmap->dmabuf, (uint_t *) & len)) != DDI_SUCCESS) #else if ((err = ddi_dma_mem_alloc (dmap->dmabuf_dma_handle, dmap->buffsize, acc_attr, flags, DDI_DMA_SLEEP, NULL, (caddr_t *) & dmap->dmabuf, (size_t *) & len, &dmap->dma_parms.dma_acc_handle)) != DDI_SUCCESS) #endif { if (!(alloc_flags & DMABUF_QUIET)) DDB (cmn_err (CE_WARN, "failed to allocate %d bytes of DMA memory (%d)\n", dmap->buffsize, err)); dmap->dmabuf = NULL; dmap->buffsize /= 2; } } if (dmap->dmabuf == NULL) { cmn_err (CE_WARN, "Can't allocate a DMA buffer for device %d\n", dev); ddi_dma_free_handle (&dmap->dmabuf_dma_handle); return OSS_ENOMEM; } DDB (cmn_err (CE_CONT, "Allocated DMA memory, addr=%x, len=%d\n", (int) dmap->dmabuf, (int) dmap->buffsize)); dmap->dma_parms.orig_buf = (caddr_t) dmap->dmabuf; if ((err = ddi_dma_addr_bind_handle (dmap->dmabuf_dma_handle, NULL, (char *) dmap->dmabuf, dmap->buffsize, flags | DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &dmap->dma_parms.cookie, &ncookies)) != DDI_SUCCESS) { cmn_err (CE_WARN, "DMA address setup failed (%d)\n", err); return OSS_EIO; } dmap->dmabuf = (unsigned char *) ((((unsigned long) dmap->dmabuf) + 4095) & ~4095); dmap->dmabuf_phys = (dmap->dma_parms.cookie.dmac_address + 4095) & ~4095; desc = PMALLOC (osdev, sizeof (contig_desc)); if (desc == NULL) return OSS_ENOMEM; desc->osdev = osdev; desc->next = NULL; desc->is_special = 1; desc->first_addr = dmap->dmabuf; desc->last_addr = dmap->dmabuf + dmap->buffsize - 1; desc->physaddr = dmap->dmabuf_phys; desc->orig_buf = dmap->dma_parms.orig_buf; if (contig_list != NULL) desc->next = contig_list; /* HW_PRINTF(("Alloc DMA: %x-%x, ph=%x\n", desc->first_addr, desc->last_addr, desc->physaddr)); */ contig_list = desc; return 0; } /*ARGSUSED*/ void oss_free_dmabuf (int dev, dmap_p dmap) { int err; if (dmap->dmabuf == NULL) return; if ((err = ddi_dma_unbind_handle (dmap->dmabuf_dma_handle)) != DDI_SUCCESS) cmn_err (CE_WARN, "Failed to free DMA handle (%d)\n", err); #if defined(sparc) ddi_mem_free (dmap->dma_parms.orig_buf); #else ddi_dma_mem_free (&dmap->dma_parms.dma_acc_handle); #endif ddi_dma_free_handle (&dmap->dmabuf_dma_handle); dmap->dmabuf = NULL; dmap->dmabuf_phys = 0; }
Interrupt management
static u_int oss_intr (caddr_t arg) /* Global interrupt handler */ { oss_device_t *osdev = (oss_device_t *) arg; int serviced; #ifdef MUTEX_CHECKS int x = inside_intr; inside_intr = 1; /* For mutex debugging only */ #endif if (osdev == NULL) { #ifdef MUTEX_CHECKS inside_intr = x; #endif return DDI_INTR_UNCLAIMED; } osdev->intrcount++; UP_STATUS (0x01); serviced = osdev->tophalf_handler (osdev); DOWN_STATUS (0x01); if (serviced == 0) return DDI_INTR_UNCLAIMED; osdev->ackcount++; if (osdev->bottomhalf_handler == NULL) return DDI_INTR_CLAIMED; #if 0 if (osdev->intr_is_hilevel) { /* TODO: Schedule the bottom half handler */ } else #endif osdev->bottomhalf_handler (osdev); #ifdef MUTEX_CHECKS inside_intr = x; #endif return DDI_INTR_CLAIMED; } int oss_register_interrupts (oss_device_t * osdev, int intrnum, oss_tophalf_handler_t top, oss_bottomhalf_handler_t bottom) { int err; ddi_idevice_cookie_t ic; if (intrnum != 0) { cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum, osdev->name); return OSS_EINVAL; } if (osdev == NULL) { cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n"); return OSS_EINVAL; } if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL) { cmn_err (CE_WARN, "Interrupts already registered for %s\n", osdev->name); return OSS_EINVAL; } if (top == NULL) { cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name); return OSS_EINVAL; } osdev->tophalf_handler = top; osdev->bottomhalf_handler = bottom; osdev->intr_is_hilevel = ddi_intr_hilevel (osdev->dip, intrnum); if (osdev->intr_is_hilevel) { if (bottom == NULL) { cmn_err (CE_WARN, "The driver for %s doesn't support hilevel interrupts\n", osdev->name); return OSS_EINVAL; } DDB (cmn_err (CE_NOTE, "Using hilevel intr for %s\n", osdev->name)); /* TODO: Fix hilevel intr handling */ cmn_err (CE_WARN, "Hilevel interrupts are not supported yet.\n"); return OSS_EINVAL; } ddi_get_iblock_cookie (osdev->dip, intrnum, &osdev->iblock_cookie); if ((err = ddi_add_intr (osdev->dip, intrnum, NULL, &ic, oss_intr, (caddr_t) osdev)) != DDI_SUCCESS) { cmn_err (CE_WARN, "ddi_add_intr() failed, error=%d\n", err); return OSS_EIO; } return 0; } void oss_unregister_interrupts (oss_device_t * osdev) { ddi_remove_intr (osdev->dip, 0, osdev->iblock_cookie); osdev->tophalf_handler = NULL; osdev->bottomhalf_handler = NULL; } /*ARGSUSED*/ int oss_register_device (oss_device_t * osdev, const char *name) { if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL) { cmn_err (CE_WARN, "Cannot allocate memory for device name\n"); osdev->name = "Unknown device"; } strcpy (osdev->name, name); return 0; } int oss_disable_device (oss_device_t * osdev) { int i;
This routine should check if the device is ready to be unloaded (no devices are in use). If the device cannot be unloaded this routine must return OSS_EBUSY.
If the device can be unloaded then disable any timers or other features that may cause the device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. However the interrupt handler should still stay enabled. The low level driver will call oss_unregister_interrupts() after it has cleared the interrupt enable register.
if (osdev->refcount > 0 || open_devices > 0) { cmn_err (CE_CONT, "Refcount %d (%s) , open_devices %d\n", osdev->refcount, osdev->nick, open_devices); if (open_devices > 0) { int i; for (i = 0; i < oss_num_cdevs; i++) if (oss_cdevs[i]->open_count) { cmn_err (CE_CONT, "%s is opened\n", oss_cdevs[i]->name); } } return OSS_EBUSY; } //cmn_err(CE_CONT, "oss_disable_device %s\n", osdev->nick);
Now mark all devices unavailable (for the time being)
for (i = 0; i < num_mixers; i++) if (mixer_devs[i]->osdev == osdev) { mixer_devs[i]->unloaded = 1; } for (i = 0; i < num_mididevs; i++) { if (midi_devs[i]->osdev == osdev) { midi_devs[i]->unloaded = 1; } } for (i = 0; i < num_audio_engines; i++) if (audio_engines[i]->osdev == osdev) { audio_uninit_device (i); } return 0; } /*ARGSUSED*/ void oss_unregister_device (oss_device_t * osdev) {
Notice! The driver calling this routine (the owner of the osdev parameter) has already uninitialized itself. Do not do any actions that may call this driver directly or indirectly.
Force reload of all drivers if any application tries to open any of the devices.
do_forceload = 1; } void oss_reserve_device (oss_device_t * osdev) { osdev->refcount++; } void oss_unreserve_device (oss_device_t * osdev, int decrement) { osdev->refcount -= decrement; }
Wait queue support
/*ARGSUSED*/ oss_wait_queue_t * oss_create_wait_queue (oss_device_t * osdev, const char *name) { oss_wait_queue_t *wq; if ((wq = PMALLOC (NULL, sizeof (*wq))) == NULL) return NULL; cv_init (&wq->cv, NULL, CV_DRIVER, NULL); return wq; } void * oss_get_osid (oss_device_t * osdev) { return osdev->osid; } /*ARGSUSED*/ void oss_reset_wait_queue (oss_wait_queue_t * wq) { /* NOP */ } void oss_remove_wait_queue (oss_wait_queue_t * wq) { cv_destroy (&wq->cv); } /*ARGSUSED*/ int oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks, oss_native_word * flags, unsigned int *status) {
oss_sleep will return 0 if timeout occurred and 1 otherwise. The WK_SIGNAL bit will be reported on status if a signal was caught.
*status = 0; if (ticks > 0) { int res; if ((res = cv_timedwait_sig (&wq->cv, mutex, ddi_get_lbolt () + ticks)) == 0) *status |= WK_SIGNAL; /* Got signal */ if (res < 0) /* Timeout */ return 0; } else { if (cv_wait_sig (&wq->cv, mutex) == 0) *status |= WK_SIGNAL; /* Got signal */ } return 1; } /*ARGSUSED*/ int oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex, oss_native_word * flags, oss_poll_event_t * ev) { ev->php = &wq->ph; wq->pollevents |= ev->events; return 0; } /*ARGSUSED*/ void oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex, oss_native_word * flags, short events) { cv_broadcast (&wq->cv); if (wq->pollevents & events) { wq->pollevents &= ~events; MUTEX_EXIT_IRQRESTORE (*mutex, *flags); pollwakeup (&wq->ph, events); MUTEX_ENTER_IRQDISABLE (*mutex, *flags); } } #ifdef MUTEX_CHECKS void debug_mutex_init (oss_mutex_t * mutex, void *dummy, int typ, void *arg, char *file, int line) { memset (mutex, 0, sizeof (mutex)); mutex_init (&mutex->mu, dummy, typ, arg); } void debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line) { mutex_destroy (&mutex->mu); } void debug_mutex_enter (oss_mutex_t * mutex, char *file, int line) { if (mutex_owned (&mutex->mu)) { cmn_err (CE_NOTE, "%s:%d: Re-entrant mutex (%s:%d %d)\n", file, line, mutex->file, mutex->line, mutex->busy_flags); return; } mutex->file = file; mutex->line = line; mutex_enter (&mutex->mu); } void debug_mutex_exit (oss_mutex_t * mutex, char *file, int line) { if (!mutex_owned (&mutex->mu)) { cmn_err (CE_NOTE, "Mutex not owned %s:%d\n", file, line); } else mutex_exit (&mutex->mu); mutex->file = NULL; mutex->line = 0; } #endif int oss_get_procinfo(int what) { switch (what) { case OSS_GET_PROCINFO_UID: return ddi_get_cred()->cr_uid; break; case OSS_GET_PROCINFO_GID: return ddi_get_cred()->cr_gid; break; #if 0 case OSS_GET_PROCINFO_PGID: return ddi_get_cred()->cr_pgid; break; #endif } return OSS_EINVAL; }