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-2007.
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. Please contact sales@opensound.com for further info.
#include "oss_config.h" #include "midi_core.h" #include <oss_pci.h> #include <Drivers.h> #include <KernelExport.h> #include <driver_settings.h>
The BeOS and Haiku kernels are preemptible, therefore we must ensure safe access to global variables. Such variables are marked by GM. Other protected variables: - oss_cdevs - mixer_devs XXX: use a benaphore ??
#if 0 // spinlock static oss_mutex_t osscore_mutex; #define CORE_LOCK_VAR \ oss_native_word osscore_mutex_flags #define CORE_LOCK_INIT() \ MUTEX_INIT (osdev, osscore_mutex, MH_TOP) #define CORE_LOCK_CLEANUP() \ MUTEX_CLEANUP (osscore_mutex) #define LOCK_CORE() \ MUTEX_ENTER_IRQDISABLE (osscore_mutex, osscore_mutex_flags) #define UNLOCK_CORE() \ MUTEX_EXIT_IRQRESTORE (osscore_mutex, osscore_mutex_flags) #else // benaphore typedef struct benaphore { sem_id sem; int32 count; } benaphore; benaphore osscore_benaphore; #define CORE_LOCK_VAR \ int dummy_ocl #define CORE_LOCK_INIT() \ osscore_benaphore.count = 1; \ osscore_benaphore.sem = create_sem(0, "OSS CORE LOCK") #define CORE_LOCK_CLEANUP() \ delete_sem(osscore_benaphore.sem) #define LOCK_CORE() \ { \ if (atomic_add(&osscore_benaphore.count, -1) <= 0) \ acquire_sem(osscore_benaphore.sem); \ } #define UNLOCK_CORE() \ { \ if (atomic_add(&osscore_benaphore.count, 1) < 0) \ release_sem(osscore_benaphore.sem); \ } #endif #define DEBUG_IRQ 1 #if DEBUG_IRQ vint32 irq_count = 0; #endif volatile int oss_open_devices = 0; #define MAX_CARDS 16 int oss_num_cards = 0; /* GM */ static oss_device_t *cards[MAX_CARDS]; /* GM */ static int oss_expired = 0; extern int vmix_disabled; //static struct fileinfo files[OSS_MAX_CDEVS]; //static volatile int open_count[OSS_MAX_CDEVS] = { 0 }; //static volatile int open_devices = 0; /* GM */ pci_module_info *gPCI = NULL; static char **gDeviceNames = NULL; // buffer for all names device_hooks oss_driver_hooks;
Table for permanently allocated memory (to be freed by std_op(UNLOAD))
#define MAX_MEMBLOCKS 4096 static void *memblocks[MAX_MEMBLOCKS]; /* GM */ static int nmemblocks = 0; /* GM */ void * oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit, oss_native_word * phaddr) { status_t err; area_id id; void *p = NULL; uint32 lock = B_CONTIGUOUS; physical_entry pent[1]; *phaddr = 0; switch (memlimit) { case MEMLIMIT_ISA: case MEMLIMIT_28BITS: case MEMLIMIT_30BITS: case MEMLIMIT_31BITS: /* no known way to force a physical address limit other than <16M */ lock = B_LOMEM; break; case MEMLIMIT_32BITS: lock = B_CONTIGUOUS; break; default: cmn_err (CE_WARN, "Bad DMA memlimit for %s\n", osdev->nick); } /* round up to page size */ size += B_PAGE_SIZE - 1; size &= ~(B_PAGE_SIZE - 1); if ((err = id = create_area(OSS_CONTIG_AREA_NAME, &p, B_ANY_KERNEL_ADDRESS, size, lock, 0)) < B_OK) { cmn_err (CE_WARN, "create_area() failed\n"); return NULL; } if ((err = get_memory_map(p, size, pent, 1)) < B_OK) { cmn_err (CE_WARN, "get_memory_map() failed\n"); delete_area(id); return NULL; } //XXX:DEBUG *phaddr = (oss_native_word)pent[0].address; dprintf("oss_contig_malloc: area %d @ va %p, pa %p, sz %d\n", id, p, (void *)(*phaddr), size); return p; } void oss_contig_free (oss_device_t * osdev, void *p, int sz) { area_id id; if (p == NULL) return; id = area_for(p); if (id < B_OK) return; #ifdef MEMDEBUG { area_info ai; if ((get_area_info(id, &ai) < B_OK) || strncmp(ai.name, OSS_CONTIG_AREA_NAME)) { cmn_err (CE_NOTE, "oss_contig_free: bad area (%ld)!\n", id); return; } } #endif delete_area(id); } int __oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags, oss_uint64_t maxaddr, int direction) { void *tmpbuf; 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; tmpbuf = CONTIG_MALLOC (dmap->osdev, size, maxaddr, &phaddr, NULL); if (tmpbuf == NULL) return OSS_ENOMEM; dmap->dmabuf = tmpbuf; dmap->buffsize = size; dmap->dmabuf_phys = phaddr; return 0; } void oss_free_dmabuf (int dev, dmap_p dmap) { if (dmap->dmabuf == NULL) return; CONTIG_FREE (dmap->osdev, dmap->dmabuf, dmap->buffsize, NULL); dmap->dmabuf = NULL; dmap->buffsize = 0; dmap->dmabuf_phys = 0; } oss_native_word oss_virt_to_bus (void *addr) { physical_entry pent[2]; status_t err; if (addr == NULL) return 0; /* XXX: ROUNDUP(B_PAGE_SIZE) ? */ if ((err = get_memory_map(addr, 1, pent, 2)) < 1) { cmn_err (CE_WARN, "Virtual address %x not mapped\n", (int) addr); return 0; } //XXX:which??? //return (oss_native_word)pent[0].address; return (oss_native_word)(gPCI->ram_address(pent[0].address)); } void * oss_pmalloc (size_t sz) { void *tmp; tmp = KERNEL_MALLOC (sz); if (nmemblocks < MAX_MEMBLOCKS) memblocks[nmemblocks++] = tmp; return tmp; } 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 OSS_EIO; } uio->ptr = buf; uio->resid = count; uio->kernel_space = is_kernel; uio->rw = rw; return 0; } int oss_uiomove (void *address, size_t nbytes, enum uio_rw rwflag, uio_t * uio) { int err = EFAULT; FENTRY(); if (rwflag != uio->rw) { oss_cmn_err (CE_WARN, "uiomove: Bad direction\n"); goto err; } if (uio->resid < nbytes) { oss_cmn_err (CE_WARN, "uiomove: Bad count %d (%d)\n", nbytes, uio->resid); goto err; } if (uio->kernel_space) goto err; switch (rwflag) { case UIO_READ: //XXX:user_memcpy... memcpy (uio->ptr, address, nbytes); break; case UIO_WRITE: //XXX:user_memcpy... memcpy (address, uio->ptr, nbytes); break; } uio->resid -= nbytes; uio->ptr += nbytes; err = B_OK; err: FEXITR(err); return err; } void oss_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; if (level == CE_CONT) { sprintf (tmp, s, a[0], a[1], a[2], a[3], a[4], a[5], NULL, NULL, NULL, NULL); dprintf ("%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); dprintf ("%s", tmp); } va_end (ap); }
Sleep/wakeup
struct oss_wait_queue * oss_create_wait_queue (oss_device_t * osdev, const char *name) { struct oss_wait_queue *wq; status_t err; FENTRYA(", %s", name); if ((wq = malloc (sizeof (*wq))) == NULL) { oss_cmn_err (CE_WARN, "malloc(%d) failed (wq)\n", sizeof (*wq)); return NULL; } sprintf(wq->name, OSS_WQ_SEM_NAME "%-20s", name); err = wq->sem = create_sem(0, wq->name); if (err < B_OK) { free(wq); oss_cmn_err (CE_WARN, "create_sem() failed (wq)\n"); return NULL; } return wq; } void oss_reset_wait_queue (struct oss_wait_queue *wq) { sem_info si; status_t err; FENTRY(); wq->flags = 0; err = create_sem(0, wq->name); if (err >= 0) { /* replace with the new one */ delete_sem(wq->sem); wq->sem = err; } FEXIT(); } void oss_remove_wait_queue (struct oss_wait_queue *wq) { FENTRY(); delete_sem(wq->sem); free (wq); } int oss_sleep (struct oss_wait_queue *wq, oss_mutex_t * mutex, int ticks, oss_native_word * flags, unsigned int *status) { bigtime_t timeout = B_INFINITE_TIMEOUT; uint32 semflags = B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT; int result = 0; FENTRYA("(%s), , %d, , ", wq->name, ticks); *status = 0; if (wq == NULL) return 0; #ifdef B_WAKE_ON_TIMEOUT // Dano only; sure it's what we want ? if (wq->flags & WK_WAKEUP) semflags |= B_WAKE_ON_TIMEOUT; #endif wq->flags = 0; MUTEX_EXIT_IRQRESTORE(*mutex, *flags); if (ticks > 0) timeout = ticks * 1000000LL / OSS_HZ; result = acquire_sem_etc (wq->sem, 1, semflags, timeout); //dprintf("oss_sleep:acquire_sem(s:%ld, 1, %x, %Ld): 0x%08lx\n", wq->sem, semflags, timeout, result); MUTEX_ENTER_IRQDISABLE (*mutex, *flags); if (result == EINTR) /* Signal received */ { *status |= WK_SIGNAL; return 1; } if (result == B_TIMED_OUT) //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) { FENTRYA("(%s), , , , ", wq->name); dprintf("oss:UNIMPLEMENTED:%s\n", __FUNCTION__); MUTEX_EXIT_IRQRESTORE(*mutex, *flags); //poll_wait ((struct file *) ev->file, &wq->wq, (struct wait *) ev->wait); MUTEX_ENTER_IRQDISABLE (*mutex, *flags); return 0; } void oss_wakeup (struct oss_wait_queue *wq, oss_mutex_t * mutex, oss_native_word * flags, short events) { FENTRYA("(%s), , %x, , ", wq->name, events); if (wq == NULL) return; wq->flags |= WK_WAKEUP; MUTEX_EXIT_IRQRESTORE(*mutex, *flags); //dprintf("oss_wakeup:release_sem(s:%ld)\n", wq->sem); release_sem_etc (wq->sem, 1, B_DO_NOT_RESCHEDULE); //XXX:handle select here MUTEX_ENTER_IRQDISABLE (*mutex, *flags); } unsigned long oss_get_time (void) { return (unsigned long) (system_time() / (1000000 / OSS_HZ)); } typedef struct tmout_desc { struct timer timer; /* MUST be first */ volatile int active; int timestamp; void (*func) (void *); void *arg; } tmout_desc_t; static volatile int next_id = 0; #define MAX_TMOUTS 128 tmout_desc_t tmouts[MAX_TMOUTS] = { {0} }; int timeout_random = 0x12123400; int32 oss_timer_callback (struct timer *timer) { tmout_desc_t *tmout = (tmout_desc_t *)timer; int ix; void *arg; timeout_random++; if (!tmout->active) return; arg = tmout->arg; tmout->active = 0; tmout->timestamp = 0; tmout->func (arg); return B_HANDLED_INTERRUPT;//B_INVOKE_SCHEDULER ?; } timeout_id_t oss_timeout (void (*func) (void *), void *arg, unsigned long long ticks) { tmout_desc_t *tmout = NULL; bigtime_t period; 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); period = ticks * 1000000LL / OSS_HZ; add_timer (&tmout->timer, oss_timer_callback, period, B_ONE_SHOT_RELATIVE_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) cancel_timer (&tmout->timer); tmout->active = 0; tmout->timestamp = 0; } caddr_t oss_map_pci_mem (oss_device_t * osdev, int nr, int phaddr, int size) { status_t err; void *va = NULL; FENTRYA("%p,%d,%u,%d", osdev, nr, phaddr, size); //XXX:align phaddr ? /* round up to page size */ size += B_PAGE_SIZE - 1; size &= ~(B_PAGE_SIZE - 1); err = map_physical_memory(OSS_PCI_AREA_NAME, (void *)phaddr, size, B_ANY_KERNEL_BLOCK_ADDRESS, 0, &va); if (err < B_OK) va = NULL; FEXITR((uint32)va); return (caddr_t)va; } void oss_unmap_pci_mem (void *addr) { area_id id; if (addr == NULL) return; id = area_for(addr); if (id < B_OK) return; #ifdef MEMDEBUG { area_info ai; if ((get_area_info(id, &ai) < B_OK) || strncmp(ai.name, OSS_PCI_AREA_NAME)) { cmn_err (CE_NOTE, "oss_unmap_pci_mem: bad area (%ld)!\n", id); return; } } #endif delete_area(id); } 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) { if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) return PCIBIOS_FAILED; *val = (unsigned char)gPCI->read_pci_config (osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, (uchar)where, 1); 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 || osdev->dip == NULL) return PCIBIOS_FAILED; *val = (unsigned short)gPCI->read_pci_config (osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, (uchar)where, 2); return PCIBIOS_SUCCESSFUL; } int pci_read_config_dword (oss_device_t * osdev, offset_t where, unsigned int *val) { if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) return PCIBIOS_FAILED; *val = (unsigned int)gPCI->read_pci_config (osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, (uchar)where, 4); return PCIBIOS_SUCCESSFUL; } int pci_write_config_byte (oss_device_t * osdev, offset_t where, unsigned char val) { if (osdev->dev_type != DRV_PCI || osdev->dip == NULL) return PCIBIOS_FAILED; gPCI->write_pci_config (osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, (uchar)where, 1, 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 || osdev->dip == NULL) return PCIBIOS_FAILED; gPCI->write_pci_config (osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, (uchar)where, 2, 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 || osdev->dip == NULL) return PCIBIOS_FAILED; gPCI->write_pci_config (osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, (uchar)where, 4, val); return PCIBIOS_SUCCESSFUL; } #ifdef MUTEX_CHECKS static int oss_context = 0; /* 0=user context, 1=interrupt context */ #endif static int32 ossintr (void *idata) { oss_device_t *osdev = idata; oss_native_word flags; //dprintf("oss:intr(%ld)!\n", osdev->irq); #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 B_UNHANDLED_INTERRUPT; } if (osdev->bottomhalf_handler != NULL) osdev->bottomhalf_handler (osdev); MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags); #ifdef MUTEX_CHECKS oss_context = saved_context; #endif return B_HANDLED_INTERRUPT; } int oss_register_interrupts (oss_device_t * osdev, int intrnum, oss_tophalf_handler_t top, oss_bottomhalf_handler_t bottom) { unsigned char pci_irq_line; int err; FENTRYA(", %d, , ", intrnum); 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; } // could probably use osdev->dip->pciinfo... if (pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line) > 0) return OSS_EIO; osdev->irq = pci_irq_line; osdev->tophalf_handler = top; osdev->bottomhalf_handler = bottom; err = install_io_interrupt_handler (pci_irq_line, ossintr, osdev, 0); dprintf("install_io_interrupt_handler (%d, %p, %p, 0) = 0x%08lx\n", pci_irq_line, ossintr, osdev, err); if (err < B_OK) { cmn_err (CE_WARN, "install_io_interrupt_handler failed for %s\n", osdev->nick); osdev->irq = -1; osdev->tophalf_handler = NULL; osdev->bottomhalf_handler = NULL; return err; } #if DEBUG_IRQ atomic_add(&irq_count, 1); #endif return 0; } void oss_unregister_interrupts (oss_device_t * osdev) { status_t err = B_OK; FENTRY(); dprintf("remove_io_interrupt_handler (%d, %p, %p)\n", osdev->irq, ossintr, osdev); if (osdev->irq >= 0) err = remove_io_interrupt_handler (osdev->irq, ossintr, osdev); if (err < B_OK) cmn_err (CE_WARN, "Error removing interrupt index (%d) for %s: %s\n", osdev->irq, osdev->name, strerror(err)); #if DEBUG_IRQ atomic_add(&irq_count, -1); #endif osdev->irq = -1; } int oss_register_device (oss_device_t * osdev, const char *name) { static int dev_instance = 0; FENTRYA(", %s", name); DDB (cmn_err (CE_CONT, "OSS device %d is %s\n", dev_instance++, 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); FEXITR(0); return 0; } int oss_disable_device (oss_device_t * osdev) { int i; CORE_LOCK_VAR; FENTRY();
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.
LOCK_CORE(); if (osdev->refcount > 0 || oss_open_devices > 0) { UNLOCK_CORE(); cmn_err (CE_CONT, "Refcount %d, open_devices %d\n", osdev->refcount, oss_open_devices); 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) { UNLOCK_CORE(); //needed until a benaphore is used. audio_uninit_device (i); LOCK_CORE(); } UNLOCK_CORE(); FEXIT(); return 0; } void oss_unregister_device (oss_device_t * osdev) { FENTRY();
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; FEXIT(); } void oss_reserve_device (oss_device_t * osdev) { CORE_LOCK_VAR; //XXX:use atomic_add() ? LOCK_CORE(); osdev->refcount++; UNLOCK_CORE(); } void oss_unreserve_device (oss_device_t * osdev, int decrement) { CORE_LOCK_VAR; //XXX:use atomic_add() ? LOCK_CORE(); osdev->refcount--; if (osdev->refcount < 0) osdev->refcount = 0; UNLOCK_CORE(); } void * oss_get_osid (oss_device_t * osdev) { // return osdev->osid; return osdev->dip; return NULL; // XXX:TODO } int oss_get_procinfo(int what) { switch (what) { case OSS_GET_PROCINFO_UID: return getuid(); break; } return OSS_EINVAL; } 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; off_t region_size; CORE_LOCK_VAR; FENTRYA(", %d, %d, %s, %s", dev_type, instance, nick, handle); if (handle == NULL) handle = nick;
Don't accept any more drivers if expired
if (oss_expired && oss_num_cards > 0) return NULL; LOCK_CORE(); for (i = 0; dip && (i < oss_num_cards); i++) { if (cards[i] == NULL) continue; if (cards[i]->available) continue; if (cards[i]->dip == dip) { osdev = cards[i]; break; } } UNLOCK_CORE(); if (osdev == NULL) { if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL) { cmn_err (CE_WARN, "osdev_create: Out of memory\n"); return NULL; } LOCK_CORE(); if (oss_num_cards >= MAX_CARDS) { UNLOCK_CORE(); cmn_err (CE_PANIC, "Too many OSS devices. At most %d permitted.\n", MAX_CARDS); return NULL; } memset (osdev, 0, sizeof (*osdev)); osdev->cardnum = oss_num_cards; cards[oss_num_cards++] = osdev; UNLOCK_CORE(); } osdev->dip = dip; //osdev->osid = dip; osdev->unloaded = 0; 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: /* NOP */ #ifdef __HAIKU__ if (gPCI->reserve_device(osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, "oss", osdev) != B_OK) { cmn_err (CE_WARN, "Could not reserve PCI device\n"); /* XXX: CLEANUP! */ return NULL; } #endif break; case DRV_VIRTUAL: 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; 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); } FEXIT(); return osdev; } oss_device_t * osdev_clone (oss_device_t * orig_osdev, int new_instance) { oss_device_t *osdev; FENTRYA(", %d", new_instance); 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); FEXIT(); return osdev; } void osdev_delete (oss_device_t * osdev) { int i; CORE_LOCK_VAR; FENTRY(); if (osdev == NULL) return; osdev->available=0; switch (osdev->dev_type) { case DRV_PCI: /* NOP */ //pci_config_teardown (&osdev->pci_config_handle); //osdev->pci_config_handle = NULL; #ifdef __HAIKU__ gPCI->unreserve_device(osdev->dip->pciinfo.bus, osdev->dip->pciinfo.device, osdev->dip->pciinfo.function, "oss", osdev); #endif break; }
Mark all minor nodes for this module as invalid.
LOCK_CORE(); 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"); } UNLOCK_CORE(); MUTEX_CLEANUP (osdev->mutex); osdev->unloaded = 1; FEXIT(); } 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; CORE_LOCK_VAR; FENTRYA(", %s, %d, %d, , %d", name, dev_class, instance, flags); if (dev_class != OSS_DEV_STATUS) if (oss_expired && instance > 0) return;
Find if this dev_class&instance already exists (after previous module detach).
LOCK_CORE(); 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]; //dprintf("oss:reusing cdev[%d]\n", num); 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; break; } UNLOCK_CORE(); if (cdev == NULL) { /* must alloc before locking, the rest must be atomic */ if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL) { cmn_err (CE_WARN, "Cannot allocate character device desc.\n"); return; } LOCK_CORE(); 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"); UNLOCK_CORE(); 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; //dprintf("oss:reusing cdev[%d]: @%p\n", num, cdev); UNLOCK_CORE(); }
Export the device only if name != NULL
#if 0 if (name != NULL) { char tmp[64], *s; char *dev_type = "oss_sysdev"; //XXX: maybe do something ?? } #endif FEXIT(); } int oss_get_cardinfo (int cardnum, oss_card_info * ci) { CORE_LOCK_VAR;
Print information about a 'card' in a format suitable for /dev/sndstat
LOCK_CORE(); if (cardnum < 0 || cardnum >= oss_num_cards) { UNLOCK_CORE(); 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; UNLOCK_CORE(); return 0; } /* XXX: major/minors don't exist in BeOS, WTF */ int oss_find_minor (int dev_class, int instance) { int i, minor = -1; CORE_LOCK_VAR; LOCK_CORE(); 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) { minor = i; break; } UNLOCK_CORE(); return minor; } #ifdef MUTEX_CHECKS void debug_mutex_init (oss_mutex_t * mutex, char *file, int line) { memset (mutex, 0, sizeof (mutex)); mutex->lock = 0; mutex->owner = -1; } void debug_mutex_destroy (oss_mutex_t * mutex, char *file, int line) { if (find_thread(NULL) == mutex->owner) { cmn_err (CE_NOTE, "%s:%d: mutex still owned (%d)\n", file, line, mutex->owner); } if (mutex->lock) { cmn_err (CE_NOTE, "%s:%d: mutex still locked (%d)\n", file, line, mutex->lock); } } void debug_mutex_enter (oss_mutex_t * mutex, char *file, int line, oss_native_word *flags) { if (find_thread(NULL) == mutex->owner) { 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->busy_flags = flags ? CNTX_INTR : CNTX_USER; if (flags) *flags = (oss_native_word) disable_interrupts(); acquire_spinlock (&mutex->lock); } void debug_mutex_exit (oss_mutex_t * mutex, char *file, int line, oss_native_word *flags) { if (find_thread(NULL) != mutex->owner) { cmn_err (CE_NOTE, "Mutex not owned %s:%d\n", file, line); } else { release_spinlock(&mutex->lock); if (flags) restore_interrupts((cpu_status)*flags); } mutex->owner = -1; mutex->file = NULL; mutex->line = 0; mutex->busy_flags = 0; } #endif void oss_load_options (oss_device_t * osdev, oss_option_map_t map[]) { char name[32] = OSS_CONFIG_FILE_PREFIX "core"; void *handle; const char *valstr; int i; if (osdev == NULL) return; /* if not core module, take the module name */ if (strcmp(osdev->modname, "oss")) strncpy (name, osdev->modname, 16); dprintf("oss_load_options(): %s\n", name); handle = load_driver_settings (name); /* not there */ if (handle == NULL) return; for (i = 0; map[i].name != NULL; i++) { /* discard given without value */ if ((valstr = get_driver_parameter (handle, map[i].name, NULL, NULL)) != NULL) { int base = 10; if (!strncmp (valstr, "0x", 2)) { base = 16; valstr += 2; } *map[i].ptr = (int)strtol (valstr, NULL, base); } } unload_driver_settings (handle); } #ifdef DEBUG_KDLCMD static int kdl_oss_dump_cards(int argc, char **argv) { int i; kprintf("oss_num_cards = %d\n", oss_num_cards); for (i = 0; i < oss_num_cards; i++) { if (cards[i] == NULL) continue; kprintf("oss_cards[%d] = {\n", i); kprintf(" cardnum= %d\n", cards[i]->cardnum); kprintf(" dev_type= %d\n", cards[i]->dev_type); kprintf(" instance= %d\n", cards[i]->instance); kprintf(" unloaded= %d\n", cards[i]->unloaded); kprintf(" name= %s\n", cards[i]->name); kprintf(" nick= %-16s\n", cards[i]->nick); kprintf(" modname= %-32s\n", cards[i]->modname); kprintf(" handle= %-32s\n", cards[i]->handle); kprintf(" num_audio_engines= %d\n", cards[i]->num_audio_engines); kprintf(" num_audioplay= %d\n", cards[i]->num_audioplay); kprintf(" num_audiorec= %d\n", cards[i]->num_audiorec); kprintf(" num_audioduplex= %d\n", cards[i]->num_audioduplex); kprintf(" num_mididevs= %d\n", cards[i]->num_mididevs); kprintf(" num_mixerdevs= %d\n", cards[i]->num_mixerdevs); kprintf(" refcount= %d\n", cards[i]->refcount); kprintf(" irq= %d\n", cards[i]->irq); kprintf("}\n"); } return 0; } static int kdl_oss_dump_cdevs(int argc, char **argv) { int i; kprintf("oss_num_cdevs = %d\n", oss_num_cdevs); for (i = 0; i < oss_num_cdevs; i++) { if (oss_cdevs[i] == NULL) continue; kprintf("oss_cdevs[%d] = {\n", i); kprintf(" dev_class= %d\n", oss_cdevs[i]->dev_class); kprintf(" instance= %d\n", oss_cdevs[i]->instance); kprintf(" name= %-32s\n", oss_cdevs[i]->name); kprintf(" osdev= %p\n", oss_cdevs[i]->osdev); //kprintf(" opencount= %d\n", open_count[i]); kprintf("}\n"); } return 0; } #endif
Driver entry point routines
status_t init_osscore (void) { int err = 0; oss_device_t *osdev; FENTRY(); #ifdef LICENSED_VERSION /*WRITEME*/ #endif add_debugger_command("oss_dump_cards", &kdl_oss_dump_cards, "Dump the OSS cards[] array."); add_debugger_command("oss_dump_cdevs", &kdl_oss_dump_cdevs, "Dump the OSS cdevs[] array."); //MUTEX_INIT (osdev, osscore_mutex, MH_TOP); CORE_LOCK_INIT(); if ((osdev = osdev_create (NULL, DRV_VIRTUAL, 0, "oss", NULL)) == NULL) { cmn_err (CE_WARN, "Creating osdev failed\n"); FEXITR(ENOMEM); return OSS_ENOMEM; } oss_load_options (osdev, oss_global_options); oss_common_init (osdev); oss_register_device (osdev, "OSS core services"); FEXIT(); return 0; } status_t uninit_osscore (void) { int i; static int already_unloaded = 0; FENTRY(); if (oss_open_devices > 0) return EBUSY; if (already_unloaded) return 0; already_unloaded = 1; oss_unload_drivers (); //LOCK_CORE(); for (i = 0; i < nmemblocks; i++) KERNEL_FREE (memblocks[i]); nmemblocks = 0; CORE_LOCK_CLEANUP(); //MUTEX_CLEANUP (osscore_mutex); remove_debugger_command("oss_dump_cdevs", &kdl_oss_dump_cdevs); remove_debugger_command("oss_dump_cards", &kdl_oss_dump_cards); #if DEBUG_IRQ dprintf("oss: %ld irq handlers left\n", irq_count); #endif return 0; } const char ** oss_publish_devices(void) { int i, j; char *name; CORE_LOCK_VAR; FENTRY(); LOCK_CORE(); gDeviceNames = realloc(gDeviceNames, (oss_num_cdevs + 1) * sizeof(char *) + (oss_num_cdevs * DEVICE_NAME_LEN)); if (gDeviceNames == NULL) { UNLOCK_CORE(); FEXIT(); return NULL; } name = (char *)(&gDeviceNames[oss_num_cdevs + 1]); for (i = 0; i < oss_num_cdevs+1; i++) gDeviceNames[i] = NULL; //dprintf("oss_num_cdevs = %d\n", oss_num_cdevs); for (i = 0, j = 0; i < oss_num_cdevs; i++) { oss_cdev_t *cdev = oss_cdevs[i]; if (cdev && cdev->d) { strcpy(name, DEVICE_PREFIX); strncat(name, cdev->name, 32-1); //dprintf("oss: publishing %s\n", name); gDeviceNames[j++] = name; name += DEVICE_NAME_LEN; } } UNLOCK_CORE(); FEXIT(); return (const char**)gDeviceNames; } device_hooks *oss_get_driver_hooks (void) { return &oss_driver_hooks; } status_t oss_load_drivers (void) { oss_drv_module_info *drv; char module[256]; size_t modulesz = sizeof(module); void *cookie; status_t err = ENOENT; FENTRY(); cookie = open_module_list(OSS_MODULES_PREFIX); if (cookie == NULL) goto err1; while (read_next_module_name(cookie, module, &modulesz) >= B_OK) { err = get_module(module, (module_info **)&drv); if (err >= B_OK) { if (drv->driver_probe() < B_OK) put_module(module); } modulesz = sizeof(module); } err = B_OK; err2: close_module_list(cookie); err1: FEXITR(err); return B_OK; } status_t oss_probe_pci (void) { FENTRY(); //XXX:TODO:remove me //ali5455_probe(); //atiaudio_probe(); FEXIT(); return B_OK; } static status_t unload_driver (const char *nick) { oss_drv_module_info *drv; char module[256]; status_t err = B_OK; int i; CORE_LOCK_VAR; FENTRYA("%s", nick); /* skip ourselves */ if (!strcmp(nick, "oss")) goto err1; sprintf(module, "%s%s%s", OSS_MODULES_PREFIX, nick, OSS_MODULES_SUFFIX); err = get_module(module, (module_info **)&drv); if (err < B_OK) goto err1; err = EBUSY; LOCK_CORE(); /* detach all osdevs for this driver */ for (i = 0; i < oss_num_cards; i++) { oss_device_t *osdev = cards[i]; if (!osdev) continue; if (strncmp(osdev->modname, nick, 32)) continue; UNLOCK_CORE(); err = drv->driver_detach(osdev); if (err < B_OK) goto err2; LOCK_CORE(); //cards[i] = NULL; // XXX: not sure... } UNLOCK_CORE(); err = B_OK; put_module(module); /* twice */ err2: put_module(module); err1: FEXITR(err); return err; } status_t oss_unload_all_drivers(void) { char module[256]; status_t err = B_OK; int i; CORE_LOCK_VAR; FENTRY();
which should delete them.
LOCK_CORE(); for (i = 0; i < oss_num_cards; i++) { if (!cards[i]) continue; if (cards[i]->unloaded) continue; UNLOCK_CORE(); err = unload_driver(cards[i]->modname); if (err < B_OK) break; LOCK_CORE(); } UNLOCK_CORE(); FEXITR(err); return err; } static status_t oss_stdops(int32 op, ...) { status_t err; switch (op) { case B_MODULE_INIT: //vmix_disabled = 1; /* disable vmix */ err = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI); if (err >= B_OK) { //XXX:call init_osscore() here return err; put_module(B_PCI_MODULE_NAME); } return err; case B_MODULE_UNINIT: free(gDeviceNames); put_module(B_PCI_MODULE_NAME); return B_OK; } return B_ERROR; } oss_core_module_info gOSSCoreModule = { { OSS_CORE_MODULE_NAME, /*B_KEEP_LOADED*/0, oss_stdops, }, init_osscore, uninit_osscore, oss_publish_devices, oss_get_driver_hooks, oss_probe_pci, oss_load_drivers, oss_unload_all_drivers, };
extern oss_drv_module_info gModule_oss_ali5455; extern oss_drv_module_info gModule_oss_atiaudio; extern oss_drv_module_info gModule_oss_audigyls; //extern oss_drv_module_info gModule_oss_audiocs; extern oss_drv_module_info gModule_oss_audioloop; extern oss_drv_module_info gModule_oss_audiopci; extern oss_drv_module_info gModule_oss_cmi878x; extern oss_drv_module_info gModule_oss_cmpci; extern oss_drv_module_info gModule_oss_cs4281; extern oss_drv_module_info gModule_oss_cs461x; extern oss_drv_module_info gModule_oss_digi96; extern oss_drv_module_info gModule_oss_emu10k1x; extern oss_drv_module_info gModule_oss_envy24; extern oss_drv_module_info gModule_oss_envy24ht; extern oss_drv_module_info gModule_oss_fmedia; extern oss_drv_module_info gModule_oss_geode; extern oss_drv_module_info gModule_oss_hdaudio; extern oss_drv_module_info gModule_oss_ich; //extern oss_drv_module_info gModule_oss_imux; //extern oss_drv_module_info gModule_oss_midiloop; //extern oss_drv_module_info gModule_oss_midimix; //extern oss_drv_module_info gModule_oss_sadasupport; extern oss_drv_module_info gModule_oss_sblive; extern oss_drv_module_info gModule_oss_sbpci; extern oss_drv_module_info gModule_oss_sbxfi; extern oss_drv_module_info gModule_oss_solo; extern oss_drv_module_info gModule_oss_trident; //extern oss_drv_module_info gModule_oss_usb; //extern oss_drv_module_info gModule_oss_userdev; extern oss_drv_module_info gModule_oss_via823x; extern oss_drv_module_info gModule_oss_via97; extern oss_drv_module_info gModule_oss_ymf7xx; //extern oss_drv_module_info gModule_osscore;
module_info *modules[] = { (module_info *)&gOSSCoreModule, //for f in kernel/drv/*; do echo " (module_info *)&gModule_$(basename $f),"; done >> kernel/OS/BeOS/os_beos.c (module_info *)&gModule_oss_ali5455, (module_info *)&gModule_oss_atiaudio, (module_info *)&gModule_oss_audigyls, //(module_info *)&gModule_oss_audiocs, //(module_info *)&gModule_oss_audioloop, (module_info *)&gModule_oss_audiopci, (module_info *)&gModule_oss_cmi878x, (module_info *)&gModule_oss_cmpci, (module_info *)&gModule_oss_cs4281, (module_info *)&gModule_oss_cs461x, (module_info *)&gModule_oss_digi96, (module_info *)&gModule_oss_emu10k1x, (module_info *)&gModule_oss_envy24, (module_info *)&gModule_oss_envy24ht, (module_info *)&gModule_oss_fmedia, (module_info *)&gModule_oss_geode, (module_info *)&gModule_oss_hdaudio, (module_info *)&gModule_oss_ich, //(module_info *)&gModule_oss_imux, //(module_info *)&gModule_oss_midiloop, //(module_info *)&gModule_oss_midimix, //(module_info *)&gModule_oss_sadasupport, (module_info *)&gModule_oss_sblive, (module_info *)&gModule_oss_sbpci, (module_info *)&gModule_oss_sbxfi, (module_info *)&gModule_oss_solo, (module_info *)&gModule_oss_trident, //(module_info *)&gModule_oss_usb, //(module_info *)&gModule_oss_userdev, (module_info *)&gModule_oss_via823x, (module_info *)&gModule_oss_via97, (module_info *)&gModule_oss_ymf7xx, // (module_info *)&gModule_osscore, }
#if 0 /* OLD
(module_info *)&gModule_oss_ali5455, (module_info *)&gModule_oss_allegro, (module_info *)&gModule_oss_als300, (module_info *)&gModule_oss_als4000, (module_info *)&gModule_oss_apci97, (module_info *)&gModule_oss_atiaudio, (module_info *)&gModule_oss_audigyls, //(module_info *)&gModule_oss_audioloop, (module_info *)&gModule_oss_audiopci, (module_info *)&gModule_oss_cmi8788, (module_info *)&gModule_oss_cmpci, (module_info *)&gModule_oss_cs4280, (module_info *)&gModule_oss_cs4281, (module_info *)&gModule_oss_digi32, (module_info *)&gModule_oss_digi96, (module_info *)&gModule_oss_emu10k1x, (module_info *)&gModule_oss_envy24, (module_info *)&gModule_oss_envy24ht, (module_info *)&gModule_oss_fm801, (module_info *)&gModule_oss_geode, (module_info *)&gModule_oss_hdaudio, (module_info *)&gModule_oss_ich, #ifdef ENABLE_IMUX (module_info *)&gModule_oss_imux, #endif (module_info *)&gModule_oss_maestro, (module_info *)&gModule_oss_neomagic, (module_info *)&gModule_oss_s3vibes, (module_info *)&gModule_oss_sblive, #ifdef ENABLE_SOFTOSS //(module_info *)&gModule_oss_softoss, #endif (module_info *)&gModule_oss_solo, (module_info *)&gModule_oss_trident, (module_info *)&gModule_oss_via8233, (module_info *)&gModule_oss_via97, (module_info *)&gModule_oss_vortex, (module_info *)&gModule_oss_ymf7xx, #endif NULL };
Driver hooks
typedef struct ossdev_cookie { int minor; /* index into cdevs[] */ oss_cdev_t *cdev; struct fileinfo file; } ossdev_cookie_t; static int find_cdev(const char *name) { int i, which = -1; CORE_LOCK_VAR; // if (strlen(name) < strlen(DEVICE_PREFIX)) // return -1; name += strlen(DEVICE_PREFIX); LOCK_CORE(); for (i = 0; i < oss_num_cdevs; i++) { oss_cdev_t *cdev = oss_cdevs[i]; //dprintf("oss:find_cdev: cdev %p, ->d %p, %s <> %s\n", cdev, cdev?cdev->d:NULL, cdev?cdev->name:"-", name); if (cdev && cdev->d && !strncmp(cdev->name, name, 32)) { which = i; break; } } UNLOCK_CORE(); return which; } static status_t ossdrv_open(const char *name, uint32 oflags, void **cookie) { ossdev_cookie_t *c; status_t err; int dev, tmpdev; oss_cdev_t *cdev; CORE_LOCK_VAR; FENTRYA("%s, %ld, ", name, oflags); dev = find_cdev(name); err = ENOENT; if (dev < 0) goto err1; err = ENXIO; if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL) goto err1; DDB (cmn_err (CE_CONT, "oss_cdev_open(%d): %s, class=%d, instance=%d\n", dev, cdev->name, cdev->dev_class, cdev->instance)); err = ENODEV; if (cdev->d->open == NULL) goto err1; err = ENOMEM; c = malloc(sizeof(ossdev_cookie_t)); if (!c) goto err1; switch (oflags & O_RWMASK) { case O_RDONLY: c->file.mode = OPEN_READ; break; case O_WRONLY: c->file.mode = OPEN_WRITE; break; case O_RDWR: c->file.mode = OPEN_READWRITE; break; default: err = EINVAL; goto err2; } //c->file.flags = oflags; c->file.acc_flags = oflags; tmpdev = -1; err = cdev->d->open (cdev->instance, cdev->dev_class, &c->file, 0, 0, &tmpdev); if (err < 0) goto err3; if (tmpdev > -1) dev = tmpdev; c->minor = dev; c->cdev = cdev = oss_cdevs[dev]; //XXX:locking LOCK_CORE(); oss_open_devices++; //open_count[dev]++; UNLOCK_CORE(); *cookie = c; FEXITR(B_OK); return B_OK; err3: err2: free(c); err1: FEXITR(err); return err; } static status_t ossdrv_close(void *cookie) { FENTRY(); FEXIT(); return B_OK; } static status_t ossdrv_freecookie(ossdev_cookie_t *cookie) { oss_cdev_t *cdev = cookie->cdev; int dev = cookie->minor; CORE_LOCK_VAR; status_t err; FENTRY(); err = ENXIO; if (dev >= OSS_MAX_CDEVS) goto err; #if 0 // not safe (not locked) err = B_OK; if (open_count[dev] == 0) /* Not opened */ goto err; #endif err = ENXIO; if (cdev != oss_cdevs[dev]) goto err; //in close or free ?? cdev->d->close (cdev->instance, &cookie->file); LOCK_CORE(); oss_open_devices--; //open_count[dev]--; UNLOCK_CORE(); free(cookie); err = B_OK; err: FEXITR(err); return B_OK; }
as long as the type field is printable ascii (>32). Lucky are we.
static status_t ossdrv_ioctl(ossdev_cookie_t *cookie, uint32 op, void *buffer, size_t length) { oss_cdev_t *cdev = cookie->cdev; status_t err = ENXIO; uint32 cmd = op; int len = 0; char buf[4096]; FENTRYA(", %lx, , %ld", op, length); if ((cdev != oss_cdevs[cookie->minor]) || cdev->d->ioctl == NULL) goto err1; if (cmd & (SIOC_OUT | SIOC_IN)) { len = (cmd >> 16) & SIOCPARM_MASK; if (len < 0) len = 0; if (len > sizeof (buf)) { cmn_err (CE_WARN, "Bad ioctl buffer size %d\n", len); err = EFAULT; goto err1; } if ((cmd & SIOC_IN) && len > 0) { memcpy (buf, buffer, len); //return EFAULT; } } err = cdev->d->ioctl (cdev->instance, &cookie->file, cmd, (ioctl_arg) buf); if ((cmd & SIOC_OUT) && len > 0) { memcpy (buffer, buf, len); //return EFAULT; } err1: FEXITR(err); return err; } static status_t ossdrv_read(ossdev_cookie_t *cookie, off_t pos, void *buffer, size_t *_length) { oss_cdev_t *cdev = cookie->cdev; uio_t uio; int count = *_length; int err; FENTRYA(", %Ld, , %ld", pos, *_length); err = ENXIO; if ((cdev != oss_cdevs[cookie->minor]) || cdev->d->read == NULL) goto err; //files[dev].acc_flags = uiop->uio_fmode; if ((err = oss_create_uio (&uio, buffer, count, UIO_READ, 0)) < 0) goto err; err = cdev->d->read (cdev->instance, &cookie->file, &uio, count);
if (err < 0) goto err;
_length = err; err = B_OK; FEXITR(err); return err;
err: _length = 0; FEXITR(err); return err;
}static status_t ossdrv_write(ossdev_cookie_t *cookie, off_t pos, const void *buffer, size_t *_length) { oss_cdev_t *cdev = cookie->cdev; uio_t uio; int count = *_length; int err; FENTRYA(", %Ld, , %ld", pos, *_length); }
err = ENXIO; if ((cdev != oss_cdevs[cookie->minor]) || cdev->d->write == NULL) goto err; //files[dev].acc_flags = uiop->uio_fmode; if ((err = oss_create_uio (&uio, (void *)buffer, count, UIO_WRITE, 0)) < 0) goto err;
err = cdev->d->write (cdev->instance, &cookie->file, &uio, count); //*_length = (err > 0) ? err : 0; if (err < 0) goto err;
_length = err; err = B_OK; FEXITR(err); return err;
err: _length = 0; FEXITR(err); return err;
device_hooks oss_driver_hooks = { &ossdrv_open, &ossdrv_close, (device_free_hook)&ossdrv_freecookie, (device_control_hook)&ossdrv_ioctl, (device_read_hook)&ossdrv_read, (device_write_hook)&ossdrv_write, /* Leave select/deselect/readv/writev undefined. The kernel will use its own default implementation. The basic hooks above this line MUST be defined, however. }
NULL, NULL, NULL, NULL };