| 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;
}