Google
 

Open Sound System
The Hitchhiker's Guide to OSS 4.1 Internals

Do you have problems with sound/audio application development? Don't panic! Click here for help!

SCO_SV/os_sco.c

Operating system abstraction functions for SCO OpenServer/UnixWare

Description




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>
#include <sys/exec.h>
#include <sys/user.h>
#include <errno.h>


MAX_CARDS must be larger than life. The system will panic if there are more sound devices (cards os sound chips) than MAX_CARDS.

#define MAX_CARDS 32

static oss_device_t *cards[MAX_CARDS];
int oss_num_cards = 0;
static int oss_expired = 0;

volatile int oss_open_devices = 0;

static bcb_t *oss_bcb;


Driver information structures, for drv_attach().

static int oss_config (cfg_func_t func, void *idata, rm_key_t key);

static const drvops_t oss_ops = {
  oss_config,
  oss_open,
  oss_close,
  oss_devinfo,
  oss_biostart,
  oss_ioctl,
  NULL,				/* drvctl */
  NULL				/* mmap */
};

static const drvinfo_t oss_drvinfo = {
  &oss_ops,
  "osscore",
  D_MP,				/* MP-safe */
  NULL,				/* Not a STREAMS driver */
  10				/* Must match $maxchan in Node file */
};

static physreq_t *physreq_isa = NULL;	/* 24 bits */
static physreq_t *physreq_28bit = NULL;
static physreq_t *physreq_30bit = NULL;
static physreq_t *physreq_31bit = NULL;
static physreq_t *physreq_32bit = NULL;

#ifdef MEMDEBUG
typedef struct
{
  void *addr;
  int size;
  char file[40];
  int line;
} mem_block_t;

#define MAX_MEMBLOCKS 1024

static mem_block_t memblocks[MAX_MEMBLOCKS];
static int num_memblocks = 0;
#endif

#ifdef MEMDEBUG
void *
oss_kmem_alloc (size_t size, int flags, char *file, int line)
#else
void *
oss_kmem_alloc (size_t size, int flags)
#endif
{

This routine allocates a memory block and stores length of it in the beginning. This length information can be used when later unallocating the memory.


  char *ptr;
  uint64_t *len;

  ptr = kmem_zalloc (size + sizeof (uint64_t), flags);
#ifdef MEMDEBUG
  cmn_err (CE_CONT, "kmalloc(%d, %s:%d)=%x\n", size, file, line, ptr);
#endif

  if (ptr == NULL)
    return NULL;

  len = (uint64_t *) ptr;

  ptr += sizeof (uint64_t);
  *len = size + sizeof (uint64_t);

#ifdef MEMDEBUG
#if 1
  {
    int i;
    for (i = 0; i < num_memblocks; i++)
      if (memblocks[i].addr == NULL)
	{
	  memblocks[i].addr = ptr;
	  memblocks[i].size = size;
	  strncpy (memblocks[i].file, file, 39);
	  memblocks[i].line = line;
	  return ptr;
	}
  }
#endif

  if (num_memblocks < MAX_MEMBLOCKS)
    {
      memblocks[num_memblocks].addr = ptr;
      memblocks[num_memblocks].size = size;
      strncpy (memblocks[num_memblocks].file, file, 39);
      memblocks[num_memblocks].line = line;
      num_memblocks++;
    }
#endif

  return ptr;
}

void
#ifdef MEMDEBUG
oss_kmem_free (void *addr, char *file, int line)
#else
oss_kmem_free (void *addr)
#endif
{
  uint64_t *len;
  int i;

  char *ptr = addr;

  if (addr == NULL)
    return;

  ptr -= sizeof (uint64_t);

  len = (uint64_t *) ptr;

  kmem_free (ptr, *len);
#ifdef MEMDEBUG
  {
    int i;

    for (i = 0; i < num_memblocks; i++)
      if (addr == memblocks[i].addr)
	{
	  memblocks[i].addr = NULL;
	  return;
	}
  }
  cmn_err (CE_WARN, "Bad kfree(%x, %s:%d)\n", addr, file, line);
#endif
}


Table for permanently allocated memory (to be freed by _fini)

#define OSS_MAX_MEM 	1024
void *oss_mem_blocks[OSS_MAX_MEM];
int oss_nblocks = 0;

void *
#ifdef MEMDEBUG
oss_pmalloc (size_t size, char *file, int line)
#else
oss_pmalloc (size_t size)
#endif
{
  void *mem_ptr;
#ifdef MEMDEBUG
  mem_ptr = (oss_mem_blocks[oss_nblocks] =
	     oss_kmem_alloc (size, KM_SLEEP, file, line));
#else
  mem_ptr = (oss_mem_blocks[oss_nblocks] = KERNEL_MALLOC (size));
#endif

  if (oss_nblocks <= OSS_MAX_MEM)
    oss_nblocks++;
  else
    cmn_err (CE_NOTE, "Out of mem blocks\n");
  return mem_ptr;
}

int
__oss_alloc_dmabuf (int dev, dmap_p dmap, unsigned int alloc_flags,
		    oss_uint64_t maxaddr, int direction)
{
  void *p;
  oss_native_word phaddr;
  int size = 64 * 1024;
  extern int dma_buffsize;

  if (dma_buffsize > 16 && dma_buffsize <= 128)
    size = dma_buffsize * 1024;

  if (dmap->dmabuf != NULL)
    return 0;


Some applications and virtual drivers need shorter buffer.

  if (dmap->flags & DMAP_SMALLBUF)
    {
      size = SMALL_DMABUF_SIZE;
    }
  else if (dmap->flags & DMAP_MEDIUMBUF)
    {
      size = MEDIUM_DMABUF_SIZE;
    }

  if ((alloc_flags & DMABUF_SIZE_16BITS) && size > 32 * 1024)
    size = 32 * 1024;

  if ((p =
       oss_contig_malloc (audio_engines[dev]->osdev, size, maxaddr,
			  &phaddr)) == NULL)
    return OSS_ENOMEM;

  dmap->dmabuf = p;
  dmap->dmabuf_phys = phaddr;
  dmap->buffsize = size;

  return 0;
}

void
oss_free_dmabuf (int dev, dmap_p dmap)
{
  if (dmap->dmabuf == NULL)
    return;

  oss_contig_free (audio_engines[dev]->osdev, dmap->dmabuf, dmap->buffsize);
  dmap->dmabuf = NULL;
  dmap->dmabuf_phys = 0;
  dmap->buffsize = 0;
}

void *
oss_contig_malloc (oss_device_t * osdev, int size, oss_uint64_t memlimit,
		   oss_native_word * phaddr)
{
  void *p = NULL;
  physreq_t *preqp = physreq_32bit;
  paddr32_t pa;

  *phaddr = 0;

  switch (memlimit)
    {
    case MEMLIMIT_ISA:
      preqp = physreq_isa;
      break;
    case MEMLIMIT_28BITS:
      preqp = physreq_28bit;
      break;
    case MEMLIMIT_30BITS:
      preqp = physreq_30bit;
      break;
    case MEMLIMIT_31BITS:
      preqp = physreq_31bit;
      break;
    case MEMLIMIT_32BITS:
      preqp = physreq_32bit;
      break;

    default:
      cmn_err (CE_WARN, "osscore: Bad DMA memlimit for %s\n", osdev->nick);
    }

  if ((p = kmem_alloc_phys (size, preqp, &pa, 0)) == NULL)
    {
      cmn_err (CE_WARN, "osscore: kmem_alloc_phys() failed\n");
      return NULL;
    }

  *phaddr = pa;
  return p;
}

void
oss_contig_free (oss_device_t * osdev, void *p, int sz)
{
  if (p)
    kmem_free (p, sz);
}


Wait queue support

oss_wait_queue_t *
oss_create_wait_queue (oss_device_t * osdev, const char *name)
{
  oss_wait_queue_t *q;

  if ((q = KERNEL_MALLOC (sizeof (*q))) == NULL)
    {
      cmn_err (CE_WARN, "osscore: Cannot allocate memory for a wait queue\n");
      return NULL;
    }

  memset (q, 0, sizeof (*q));

  if ((q->sv = SV_ALLOC (KM_SLEEP)) == NULL)
    {
      cmn_err (CE_WARN,
	       "osscore: Cannot allocate synchronization variable\n");
      return NULL;
    }

  return q;
}

void
oss_reset_wait_queue (oss_wait_queue_t * wq)
{
  // NOP
}

void
oss_remove_wait_queue (oss_wait_queue_t * wq)
{
  SV_DEALLOC (wq->sv);
  KERNEL_FREE (wq);
}

static void
sleep_timeout (caddr_t arg)
{
  oss_wait_queue_t *wq = (oss_wait_queue_t *) arg;

  SV_BROADCAST (wq->sv, 0);
}

int
oss_sleep (oss_wait_queue_t * wq, oss_mutex_t * mutex, int ticks,
	   oss_native_word * flags, unsigned int *status)
{
  timeout_id_t tid;

  *status = 0;
  wq->flags = 0;

  if (wq == NULL)
    {
      cmn_err (CE_WARN, "osscore: Unallocated wait queue\n");
      *status |= WK_SIGNAL;
      return 1;
    }
  if (ticks > 0)
    tid = timeout (sleep_timeout, wq, ticks);


Note SV_WAIT_SIG() will release the mutex/lock so we must re-acquire it before returning.

#ifdef MUTEX_CHECKS
  // Pop mutex because it will be released by SV_WAIT_SIG() 
  pop_mutex (*mutex, __FILE__, __LINE__);
#endif
  if (!SV_WAIT_SIG (wq->sv, prihi, *mutex))	/* Signal */
    {
      *status |= WK_SIGNAL;
      wq->flags |= WK_WAKEUP;	/* Needed to prevent false timeout messages */
    }

  MUTEX_ENTER_IRQDISABLE (*mutex, *flags);
  if (ticks > 0 && (wq->flags & WK_WAKEUP))
    untimeout (tid);

  return (wq->flags & WK_WAKEUP);
}

int
oss_register_poll (oss_wait_queue_t * wq, oss_mutex_t * mutex,
		   oss_native_word * flags, oss_poll_event_t * ev)
{
  // NOP: DDI8 doesn't support chpoll
}

void
oss_wakeup (oss_wait_queue_t * wq, oss_mutex_t * mutex,
	    oss_native_word * flags, short events)
{
  wq->flags |= WK_WAKEUP;
  SV_BROADCAST (wq->sv, 0);
}

void *
oss_get_osid (oss_device_t * osdev)
{
  return osdev->osid;
}

static int
grow_array(oss_device_t *osdev, oss_cdev_t ***arr, int *size, int element_size, int increment)
{
	oss_cdev_t **old=*arr, **new = *arr;
	int old_size = *size;
	int new_size = *size;
		
	new_size += increment;

	if ((new=PMALLOC(osdev, new_size * element_size))==NULL)
	   return 0;

	memset(new, 0, new_size * element_size);
	if (old != NULL)
	   memcpy(new, old, old_size * element_size);

	*size = new_size;
	*arr = new;

	if (old != NULL)
	   PMFREE(osdev, old);

	return 1;
}

void
oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class,
		    int instance, oss_cdev_drv_t * drv, int flags)
{

oss_install_chrdev creates a character device (minor). However if name==NULL the device will not be exported (made visible to userland clients).


  int i, num;
  oss_cdev_t *cdev = NULL;

  if (dev_class != OSS_DEV_STATUS)
    if (oss_expired && instance > 0)
      return;

Find if this dev_class&instance already exists (after previous module detach).


  for (num = 0; num < oss_num_cdevs; num++)
    if (oss_cdevs[num]->d == NULL)	/* Unloaded driver */
      if (oss_cdevs[num]->dev_class == dev_class
	  && oss_cdevs[num]->instance == instance)
	{
	  cdev = oss_cdevs[num];
	  break;
	}

  if (cdev == NULL)
    {
      if (oss_num_cdevs >= OSS_MAX_CDEVS)
	{
	   if (!grow_array(osdev, &oss_cdevs, &oss_max_cdevs, sizeof(oss_cdev_t*), 100))
	   {
	  	cmn_err (CE_WARN, "Cannot allocate new minor numbers.\n");
	  	return;
	   }
	}

      if ((cdev = PMALLOC (NULL, sizeof (*cdev))) == NULL)
	{
	  cmn_err (CE_WARN, "Cannot allocate character device desc.\n");
	  return;
	}
      num = oss_num_cdevs++;
    }

  memset (cdev, 0, sizeof (*cdev));
  cdev->dev_class = dev_class;
  cdev->instance = instance;
  cdev->d = drv;
  cdev->osdev = osdev;
  if (name != NULL)
    strncpy (cdev->name, name, sizeof (cdev->name) - 1);
  else
    strcpy (cdev->name, "NONE");
  cdev->name[sizeof (cdev->name) - 1] = 0;
  oss_cdevs[num] = cdev;
}

int
oss_find_minor (int dev_class, int instance)
{
  int i;

  for (i = 0; i < oss_num_cdevs; i++)
    if (oss_cdevs[i]->d != NULL && oss_cdevs[i]->dev_class == dev_class
	&& oss_cdevs[i]->instance == instance)
      return i;

  return OSS_EIO;
}

int
oss_get_cardinfo (int cardnum, oss_card_info * ci)
{

Print information about a 'card' in a format suitable for /dev/sndstat


  if (cardnum < 0 || cardnum >= oss_num_cards)
    return OSS_ENXIO;

  if (cards[cardnum]->name != NULL)
    strncpy (ci->longname, cards[cardnum]->name, 128);
  ci->shortname[127] = 0;

  if (cards[cardnum]->nick != NULL)
    strncpy (ci->shortname, cards[cardnum]->nick, 16);
  ci->shortname[15] = 0;

  if (cards[cardnum]->hw_info != NULL)
    strncpy (ci->hw_info, cards[cardnum]->hw_info, sizeof (ci->hw_info) - 1);
  ci->hw_info[sizeof (ci->hw_info) - 1] = 0;

  return 0;
}

void
oss_reserve_device (oss_device_t * osdev)
{
  osdev->refcount++;
}

void
oss_unreserve_device (oss_device_t * osdev, int decrement)
{
  osdev->refcount--;
  if (osdev->refcount < 0)
    osdev->refcount = 0;
}


Some standard C library routines

void *
memset (void *t, int c, size_t l)
{
  int i;

  if (t == NULL)
    return NULL;

  for (i = 0; i < l; i++)
    ((char *) t)[i] = c;

  return t;
}

void *
memcpy (void *t, const void *s, size_t l)
{
  bcopy (s, t, l);
  return t;
}

oss_device_t *
osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick,
	      const char *handle)
{
  oss_device_t *osdev = NULL;
  int i, err;
  caddr_t addr;
  dev_info_t ldip = 0;

  if (dip == NULL)
    dip = &ldip;

  if (handle == NULL)
    handle = nick;


Don't accept any more drivers if expired

  if (oss_expired && oss_num_cards > 0)
    return NULL;


Check if a driver/device was reinserted

  if (dip != &ldip)		/* Not a virtual driver */
    for (i = 0; i < oss_num_cards; i++)
      {
	if (!cards[i]->available && cards[i]->key == *dip)
	  {
	    osdev = cards[i];
	    break;
	  }
      }

  if (osdev == NULL)
    {
      if (oss_num_cards >= MAX_CARDS)
	{
	  cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n",
		   MAX_CARDS);
	  return NULL;
	}

      if ((osdev = PMALLOC (NULL, sizeof (*osdev))) == NULL)
	{
	  cmn_err (CE_WARN, "osdev_create: Out of memory\n");
	  return NULL;
	}

      osdev->cardnum = oss_num_cards;
      cards[oss_num_cards++] = osdev;
    }
  osdev->key = *dip;
  osdev->osid = dip;
  osdev->available = 1;
  osdev->first_mixer = -1;
  osdev->instance = instance;
  osdev->dev_type = dev_type;
  osdev->devc = NULL;
  MUTEX_INIT (osdev, osdev->mutex, MH_GLOBAL);
  sprintf (osdev->nick, "%s%d", nick, instance);
  strcpy (osdev->modname, nick);

  switch (dev_type)
    {
    case DRV_PCI:
      //pci_config_setup (dip, &osdev->pci_config_handle);
      break;

    case DRV_VIRTUAL:
    case DRV_VMIX:
    case DRV_STREAMS:
      /* NOP */
      break;

    case DRV_USB:
      /* NOP */
      break;

    default:
      cmn_err (CE_WARN, "Bad device type\n");
      return NULL;
    }


Create the device handle

  switch (dev_type)
    {
    case DRV_PCI:
      {
	unsigned int subvendor = 0;
	pci_read_config_dword (osdev, 0x2c, &subvendor);

	sprintf (osdev->handle, "PCI%08x-%d", subvendor, instance);
      }
      break;

#if 0
    case DRV_USB:
      sprintf (osdev->handle, "USB-%s%d", handle, instance);
      break;
#endif

    default:
      sprintf (osdev->handle, "%s%d", handle, instance);
    }

  return osdev;
}

oss_device_t *
osdev_clone (oss_device_t * orig_osdev, int new_instance)
{
  oss_device_t *osdev;

  osdev = PMALLOC (NULL, sizeof (*osdev));
  if (osdev == NULL)
    {
      cmn_err (CE_WARN, "osdev_create: Out of memory\n");
      return NULL;
    }
  memcpy (osdev, orig_osdev, sizeof (*osdev));
  osdev->dev_type = DRV_CLONE;
  osdev->instance = new_instance;
  sprintf (osdev->nick, "%s%d", orig_osdev->modname, new_instance);
  sprintf (osdev->handle, "%s%d", orig_osdev->modname, new_instance);

  return osdev;
}

void
osdev_delete (oss_device_t * osdev)
{
  int i;

  if (osdev == NULL)
    return;

  if (!osdev->available)
    {
      cmn_err (CE_WARN, "device %s, osdev already deleted\n", osdev->nick);
      return;
    }

  osdev->available = 0;

  switch (osdev->dev_type)
    {
    case DRV_PCI:
      break;
    }


Mark all minor nodes for this module as invalid.

  for (i = 0; i < oss_num_cdevs; i++)
    if (oss_cdevs[i]->osdev == osdev)
      {
	oss_cdevs[i]->d = NULL;
	oss_cdevs[i]->osdev = NULL;
	strcpy (oss_cdevs[i]->name, "Removed device");
      }
  MUTEX_CLEANUP (osdev->mutex);
}

int
oss_register_device (oss_device_t * osdev, const char *name)
{
  if ((osdev->name = PMALLOC (NULL, strlen (name) + 1)) == NULL)
    {
      cmn_err (CE_WARN, "Cannot allocate memory for device name\n");
      osdev->name = "Unknown device";
    }
  strcpy (osdev->name, name);
  return 0;
}

int
oss_disable_device (oss_device_t * osdev)
{
  int i;

This routine should check if the device is ready to be unloaded (no devices are in use). If the device cannot be unloaded this routine must return OSS_EBUSY.

If the device can be unloaded then disable any timers or other features that may cause the device to be called. Also mark the audio/midi/mixer/etc devices of this device to be disabled. However the interrupt handler should still stay enabled. The low level driver will call oss_unregister_interrupts() after it has cleared the interrupt enable register.

  if (osdev->refcount > 0)
    {
      return OSS_EBUSY;
    }


Now mark all devices unavailable (for the time being)


  for (i = 0; i < num_mixers; i++)
    if (mixer_devs[i]->osdev == osdev)
      {
	mixer_devs[i]->unloaded = 1;
      }

  for (i = 0; i < num_mididevs; i++)
    {
      if (midi_devs[i]->osdev == osdev)
	{
	  midi_devs[i]->unloaded = 1;
	}
    }

  for (i = 0; i < num_audio_engines; i++)
    if (audio_engines[i]->osdev == osdev)
      {
	audio_uninit_device (i);
      }

  return 0;
}

void
oss_unregister_device (oss_device_t * osdev)
{
}

#ifdef MUTEX_CHECKS
static int oss_context = 0;	/* 0=user context, 1=interrupt context */
#endif

static int
ossintr (void *idata)
{
  oss_device_t *osdev = idata;
  oss_native_word flags;
#ifdef MUTEX_CHECKS
  int saved_context;
  saved_context = oss_context;
  if (oss_context == 1)
    cmn_err (CE_WARN, "Recursive interrupt\n");
  oss_context = 1;
#endif

  MUTEX_ENTER_IRQDISABLE (osdev->mutex, flags);

  if (!osdev->tophalf_handler (osdev))
    {
      MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags);
#ifdef MUTEX_CHECKS
      oss_context = saved_context;
#endif
      return ISTAT_NONE;
    }

  if (osdev->bottomhalf_handler != NULL)
    osdev->bottomhalf_handler (osdev);

  MUTEX_EXIT_IRQRESTORE (osdev->mutex, flags);
#ifdef MUTEX_CHECKS
  oss_context = saved_context;
#endif

  return ISTAT_ASSERTED;
}

int
oss_register_interrupts (oss_device_t * osdev, int intrnum,
			 oss_tophalf_handler_t top,
			 oss_bottomhalf_handler_t bottom)
{
  int err;

  if (intrnum != 0)
    {
      cmn_err (CE_WARN, "Bad interrupt index (%d) for %s\n", intrnum,
	       osdev->name);
      return OSS_EINVAL;
    }

  if (osdev == NULL)
    {
      cmn_err (CE_WARN, "oss_register_interrupts: Bad osdev\n");
      return OSS_EINVAL;
    }

  if (osdev->tophalf_handler != NULL || osdev->bottomhalf_handler != NULL)
    {
      cmn_err (CE_WARN, "Interrupts already registered for %s\n",
	       osdev->name);
      return OSS_EINVAL;
    }

  if (top == NULL)
    {
      cmn_err (CE_WARN, "Bad interrupt handler for %s\n", osdev->name);
      return OSS_EINVAL;
    }

  osdev->tophalf_handler = top;
  osdev->bottomhalf_handler = bottom;
  if (cm_intr_attach
      (osdev->key, ossintr, osdev, osdev->drvinfo, &osdev->intr_cookie) == 0)
    {
      cmn_err (CE_WARN, "cm_intr_attach failed for %s\n", osdev->nick);
    }

  return 0;
}

void
oss_unregister_interrupts (oss_device_t * osdev)
{
  if (osdev->intr_cookie != NULL)
    cm_intr_detach (osdev->intr_cookie);
  osdev->intr_cookie = NULL;
}

static char *
ksprintn (ul, base, lenp)
     register u_long ul;
     register int base, *lenp;
{				/* A long in base 8, plus NULL. */
  static char buf[sizeof (long) * NBBY / 3 + 2];
  register char *p;

  p = buf;
  do
    {
      *++p = "0123456789abcdef"[ul % base];
    }
  while (ul /= base);
  if (lenp)
    *lenp = p - buf;
  return (p);
}

int
oss_sprintf (char *buf, char *cfmt, ...)
{
  const char *fmt = cfmt;
  register char *p, *bp;
  register int ch, base;
  unsigned long ul;
  int lflag;
  int count = 10;
  va_list ap;

  va_start (ap, fmt);
  for (bp = buf;;)
    {
      while ((ch = *(unsigned char *) fmt++) != '%')
	if ((*bp++ = ch) == '\0')
	  {
	    va_end (ap);
	    return ((bp - buf) - 1);
	  }

      lflag = 0;
    reswitch:
      switch (ch = *(unsigned char *) fmt++)
	{
	case 'l':
	  lflag = 1;
	  goto reswitch;

	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	  goto reswitch;

	case 'c':
	  *bp++ = va_arg (ap, int);
	  break;

	case 's':
	  p = va_arg (ap, char *);
	  while (*bp++ = *p++)
	    ;
	  --bp;
	  break;

	case 'd':
	  ul = lflag ? va_arg (ap, long) : va_arg (ap, int);
	  if ((long) ul < 0)
	    {
	      *bp++ = '-';
	      ul = -(long) ul;
	    }
	  base = 10;
	  goto number;
	  break;

	case 'o':
	  ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int);
	  base = 8;
	  goto number;
	  break;

	case 'u':
	  ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int);
	  base = 10;
	  goto number;
	  break;

	case 'x':
	  ul = lflag ? va_arg (ap, unsigned long) : va_arg (ap, unsigned int);
	  base = 16;
	number:
	  for (p = (char *) ksprintn (ul, base, NULL); ch = *p--;)
	    *bp++ = ch;
	  break;

	default:
	  *bp++ = '%';
	  if (lflag)
	    *bp++ = 'l';
	  /* FALLTHROUGH */

	case '%':
	  *bp++ = ch;
	}
    }

  /* va_end(ap); */
}

static physreq_t *
build_physreq (int bits)
{
  physreq_t *pr;
  long long tmp;

  if ((pr = physreq_alloc (KM_SLEEP)) == NULL)
    return NULL;

  pr->phys_align = 4096;
  pr->phys_boundary = 0;
  pr->phys_dmasize = bits;
  pr->phys_max_scgth = 0;
  pr->phys_flags = PREQ_PHYSCONTIG;

  if (physreq_prep (pr, KM_SLEEP) != B_TRUE)
    {
      cmn_err (CE_WARN, "osscore: physreq_prep failed\n");
    }

  return pr;
}


Driver info device file

static int drvinfo_busy = 0;
static int drvinfo_len = 0, drvinfo_ptr = 0;
static char *drvinfo_buf = NULL;
#define DRVINFO_SIZE 4096

static int
drvinfo_open (int dev, int dev_class, struct fileinfo *file,
	      int recursive, int open_flags, int *redirect)
{
  int i;

  if (drvinfo_busy)
    return OSS_EBUSY;
  drvinfo_busy = 1;

  if ((drvinfo_buf = KERNEL_MALLOC (DRVINFO_SIZE)) == NULL)
    {
      cmn_err (CE_WARN, "Cannot allocate drvinfo buffer\n");
      return OSS_ENOMEM;
    }

  drvinfo_len = 0;
  drvinfo_ptr = 0;

  for (i = 1; i < oss_num_cdevs; i++)
    if (oss_cdevs[i]->name[0] != 'N' || oss_cdevs[i]->name[1] != 'O' || oss_cdevs[i]->name[2] != 'N' || oss_cdevs[i]->name[3] != 'E')	/* Not a dummy placeholder device */
      if (DRVINFO_SIZE - drvinfo_len > 32)
	{
	  char *s = drvinfo_buf + drvinfo_len;

	  drvinfo_len +=
	    oss_sprintf (s, "%s %s %d\n", oss_cdevs[i]->name,
			 oss_cdevs[i]->osdev->nick, i);

	}

  return 0;
}

static void
drvinfo_close (int dev, struct fileinfo *file)
{
  KERNEL_FREE (drvinfo_buf);
  drvinfo_buf = NULL;
  drvinfo_len = 0;
  drvinfo_ptr = 0;
  drvinfo_busy = 0;
}

static int
drvinfo_read (int dev, struct fileinfo *file, uio_t * buf, int count)
{

Return at most 'count' bytes from the drvinfo_buf.

  int l, c;

  l = count;
  c = drvinfo_len - drvinfo_ptr;

  if (l > c)
    l = c;
  if (l <= 0)
    return 0;

  if (uiomove (&drvinfo_buf[drvinfo_ptr], l, UIO_READ, buf) != 0)
    return OSS_EFAULT;
  drvinfo_ptr += l;

  return l;
}

static oss_cdev_drv_t drvinfo_cdev_drv = {
  drvinfo_open,
  drvinfo_close,
  drvinfo_read
};

static void
install_drvinfo (oss_device_t * osdev)
{
  oss_install_chrdev (osdev, "ossinfo", OSS_DEV_MISC, 0, &drvinfo_cdev_drv,
		      0);
}


Driver entry point routines


int
_load ()
{
  int err = 0;
  oss_device_t *osdev;
  time_t t;

  if ((err = drv_attach (&oss_drvinfo)) != 0)
    {
      cmn_err (CE_WARN, "drv_attach failed %d\n", err);
      return err;
    }

#ifdef LICENSED_VERSION
  if (drv_getparm (TIME, &t) != 0)
    {
      cmn_err (CE_WARN, "drv_getparm(TIME) failed\n");
      return EBUSY;
    }

  if (!oss_license_handle_time (t))
    {
      cmn_err (CE_WARN, "This version of Open Sound System has expired\n");
      cmn_err (CE_CONT,
	       "Please download the latest version from www.opensound.com\n");
      oss_expired = 1;
    }
#endif

  if ((osdev = osdev_create (NULL, DRV_VIRTUAL, 0, "oss", NULL)) == NULL)
    {
      cmn_err (CE_WARN, "Creating osdev failed\n");
      return ENOMEM;
    }


Allocate physrec structures for various memory ranges. Remember to free them in the _load entry point.

  physreq_isa = build_physreq (24);
  physreq_28bit = build_physreq (28);
  physreq_30bit = build_physreq (30);
  physreq_31bit = build_physreq (31);
  physreq_32bit = build_physreq (32);


Create the BCB structure

  oss_bcb = bcb_alloc (KM_SLEEP);
  oss_bcb->bcb_addrtypes = BA_UIO;
  oss_bcb->bcb_flags = 0;
  oss_bcb->bcb_max_xfer = 0;
  oss_bcb->bcb_granularity = 1;
  oss_bcb->bcb_physreqp = physreq_32bit;
  bcb_prep (oss_bcb, KM_SLEEP);

  install_drvinfo (osdev);
  oss_common_init (osdev);

  oss_register_device (osdev, "OSS core services");

  return 0;
}

int
_unload ()
{
  int i;
  static int already_unloaded = 0;

  if (oss_open_devices > 0)
    return EBUSY;
  drv_detach (&oss_drvinfo);

  if (already_unloaded)
    return 0;
  already_unloaded = 1;

  oss_unload_drivers ();

  physreq_free (physreq_isa);
  physreq_free (physreq_28bit);
  physreq_free (physreq_30bit);
  physreq_free (physreq_31bit);
  physreq_free (physreq_32bit);

  bcb_free (oss_bcb);

  for (i = 0; i < oss_nblocks; i++)
    KERNEL_FREE (oss_mem_blocks[i]);
  oss_nblocks = 0;

  return 0;
}

void
oss_pci_byteswap (oss_device_t * osdev, int mode)
{
  // NOP
}

void
oss_pcie_init (oss_device_t * osdev, int flags)
{
	/* TODO: Should we do something? */
}

int
pci_read_config_byte (oss_device_t * osdev, offset_t where,
		      unsigned char *val)
{
  *val = 0;
  if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) ==
      sizeof (*val))
    return PCIBIOS_SUCCESSFUL;

  return PCIBIOS_FAILED;
}

int
pci_read_config_irq (oss_device_t * osdev, offset_t where, unsigned char *val)
{
  *val = 0;
  if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) ==
      sizeof (*val))
    return PCIBIOS_SUCCESSFUL;

  return PCIBIOS_FAILED;
}


int
pci_read_config_word (oss_device_t * osdev, offset_t where,
		      unsigned short *val)
{
  *val = 0;
#if 1
  if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) ==
      sizeof (*val))
    return PCIBIOS_SUCCESSFUL;
#else
  switch (where)
    {
    case PCI_VENDOR_ID:
      *val = osdev->vendor;
      return PCIBIOS_SUCCESSFUL;
      break;

    case PCI_DEVICE_ID:
      *val = osdev->product;
      return PCIBIOS_SUCCESSFUL;
      break;

    }

#endif
  return PCIBIOS_FAILED;
}

int
pci_read_config_dword (oss_device_t * osdev, offset_t where,
		       unsigned int *val)
{
  *val = 0;
  if (cm_read_devconfig (osdev->key, where, val, sizeof (*val)) ==
      sizeof (*val))
    return PCIBIOS_SUCCESSFUL;

  return PCIBIOS_FAILED;
}

int
pci_write_config_byte (oss_device_t * osdev, offset_t where,
		       unsigned char val)
{
  if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) ==
      sizeof (val))
    return PCIBIOS_SUCCESSFUL;

  return PCIBIOS_FAILED;
}

int
pci_write_config_word (oss_device_t * osdev, offset_t where,
		       unsigned short val)
{
  if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) ==
      sizeof (val))
    return PCIBIOS_SUCCESSFUL;

  return PCIBIOS_FAILED;
}

int
pci_write_config_dword (oss_device_t * osdev, offset_t where,
			unsigned int val)
{
  if (cm_write_devconfig (osdev->key, where, &val, sizeof (val)) ==
      sizeof (val))
    return PCIBIOS_SUCCESSFUL;

  return PCIBIOS_FAILED;
}


Entry point routines

static int
oss_config (cfg_func_t func, void *idata, rm_key_t key)
{
  return EOPNOTSUPP;
}

int
oss_devinfo (void *idata, channel_t channel, di_parm_t parm, void *valp)
{
  switch (parm)
    {
    case DI_MEDIA:
      break;

    case DI_SIZE:
      break;

    case DI_RBCBP:
      *(bcb_t **) valp = oss_bcb;
      return 0;
      break;

    case DI_WBCBP:
      *(bcb_t **) valp = oss_bcb;
      return 0;
      break;

    case DI_PHYS_HINT:
      break;

    }
  return EOPNOTSUPP;
}

void
oss_biostart (void *idata, channel_t channel, buf_t * bp)
{
  int dev = channel;
  oss_cdev_t *cdev;
  int count = bp->b_un.b_uio->uio_resid;
  int retval;

  if ((cdev = oss_cdevs[dev]) == NULL)
    {
      bioerror (bp, ENXIO);
      biodone (bp);
      return;
    }

  cdev->file.acc_flags = 0;

  if (bp->b_flags & B_READ)
    {
      /* Read operation */
      if (cdev->d->read == NULL)
	{
	  bioerror (bp, ENXIO);
	  biodone (bp);
	  return;
	}
      retval =
	cdev->d->read (cdev->instance, &cdev->file, bp->b_un.b_uio, count);
      if (retval < 0)
	bioerror (bp, -retval);
      else if (retval < count)
	bp->b_resid = count - retval;

      biodone (bp);
      return;
    }

  /* Write operation */
  if (cdev->d->write == NULL)
    {
      bioerror (bp, ENXIO);
      biodone (bp);
      return;
    }
  retval =
    cdev->d->write (cdev->instance, &cdev->file, bp->b_un.b_uio, count);
  if (retval < 0)
    bioerror (bp, -retval);
  else if (retval < count)
    bp->b_resid = count - retval;

  biodone (bp);
}

int
oss_open (void *idata, channel_t * channelp,
	  int open_flags, cred_t * crp, queue_t * q)
{
  oss_device_t *osdev;
  int dev = *channelp, tmpdev;
  oss_cdev_t *cdev;
  int retval;

  osdev = idata;

  if (dev >= OSS_MAX_CDEVS)
    return ENXIO;

  if (dev >= oss_num_cdevs)
    {
      return ENODEV;
    }

  if ((cdev = oss_cdevs[dev]) == NULL || cdev->d == NULL)
    return ENODEV;

  if (cdev->d->open == NULL)
    {
      return ENODEV;
    }

  memset (&cdev->file, 0, sizeof (cdev->file));
  cdev->file.mode = 0;
  cdev->file.acc_flags = open_flags;

  if (open_flags & FREAD && open_flags & FWRITE)
    cdev->file.mode = OPEN_READWRITE;
  else if (open_flags & FREAD)
    cdev->file.mode = OPEN_READ;
  else if (open_flags & FWRITE)
    cdev->file.mode = OPEN_WRITE;

  tmpdev = dev;
  retval =
    cdev->d->open (cdev->instance, cdev->dev_class, &cdev->file, 0, 0, &tmpdev);
  *channelp = tmpdev;
  dev = tmpdev;

  if (retval < 0)
    {
      return -retval;
    }

  oss_open_devices++;
  cdev->open_count++;

  return 0;
}

int
oss_close (void *idata, channel_t channel,
	   int oflags, cred_t * crp, queue_t * q)
{
  oss_cdev_t *cdev;
  int dev;

  dev = channel;

  if (dev >= OSS_MAX_CDEVS)
    return ENXIO;

  if ((cdev = oss_cdevs[dev]) == NULL)
    return ENXIO;

  if (cdev->open_count == 0)    /* Not opened */
    return 0;

  cdev->d->close (cdev->instance, &cdev->file);

  oss_open_devices--;
  cdev->open_count--;
  return 0;
}

int
oss_ioctl (void *idata, channel_t channel, int cmd, void *arg,
	   int oflags, cred_t * crp, int *rvalp)
{
  int dev = channel;
  int retval;
  int len = 0;
  char localbuf[256];		/* All frequently used ioctl calls fit in 256 bytes */
  char *buf = localbuf, *auxbuf = NULL;
  oss_cdev_t *cdev;

  if ((cdev = oss_cdevs[dev]) == NULL || cdev->d->ioctl == NULL)
    return *rvalp = ENXIO;

  if (oss_expired)
    return *rvalp = ENODEV;

  if (cmd & (SIOC_OUT | SIOC_IN))
    {

      len = __SIOC_SIZE (cmd);
      if (len < 0)
	len = 0;
      if (len > sizeof (localbuf))
	{

For the few largest ioctl calls we need to allocate an off-stack buffer because otherwise the kernel stack will overflow. This approach is slower but fortunately "sane" applications don't use these ioctl calls too frequently.

	  if ((auxbuf = KERNEL_MALLOC (len)) == NULL)
	    {
	      cmn_err (CE_WARN,
		       "Failed to allocate an ioctl buffer of %d bytes\n",
		       len);
	      return *rvalp = ENOMEM;
	    }
	  buf = auxbuf;
	}

      if ((cmd & SIOC_IN) && len > 0)
	{
	  if (copyin ((char *) arg, buf, len) == -1)
	    {
	      if (auxbuf != NULL)
		KERNEL_FREE (auxbuf);
	      return *rvalp = EFAULT;
	    }
	}

    }

  retval = cdev->d->ioctl (cdev->instance, &cdev->file, cmd, (ioctl_arg) buf);

  if ((cmd & SIOC_OUT) && len > 0)
    {
      if (copyout (buf, (char *) arg, len) == -1)
	{
	  if (auxbuf != NULL)
	    KERNEL_FREE (auxbuf);
	  return *rvalp = EFAULT;
	}
    }

  *rvalp = (((retval) < 0) ? -(retval) : 0);

  if (auxbuf != NULL)
    KERNEL_FREE (auxbuf);

  return *rvalp;
}

#ifdef MUTEX_CHECKS

typedef struct
{
  int active;
  void *mutex;
  const char *filename;
  int line;
  int context;
} mutex_debug_t;

static mutex_debug_t mutex_tab[1024];
static int n_mutexes = 0;

void
push_mutex (void *mutex, const char *file, int line)
{
  int i, n = -1;
//cmn_err(CE_CONT, "Push mutex %08x, %s:%d, context=%d\n", mutex, file, line, oss_context);

  for (i = 0; n == -1 && i < n_mutexes; i++)
    if (!mutex_tab[i].active)
      n = i;
    else
      {
	if (mutex_tab[i].mutex == mutex
	    && mutex_tab[i].context == oss_context)
	  {
	    cmn_err (CE_NOTE, "Mutex %08x already held\n", mutex);
	    cmn_err (CE_CONT, "  Locked by %s:%d\n", mutex_tab[i].filename,
		     mutex_tab[i].line);
	    cmn_err (CE_CONT, "  Acquire by %s:%d\n", file, line);
	  }
      }

  if (n == -1)
    {
      if (n_mutexes >= 1024)
	{
	  cmn_err (CE_NOTE, "Mutex debug table full\n");
	  return;
	}
      n = n_mutexes++;
    }

  mutex_tab[n].active = 1;
  mutex_tab[n].mutex = mutex;
  mutex_tab[n].filename = file;
  mutex_tab[n].line = line;
  mutex_tab[n].context = oss_context;
}

void
pop_mutex (void *mutex, const char *file, int line)
{
  int i;

//cmn_err(CE_CONT, "Pop mutex %08x, %s:%d, context=%d\n", mutex, file, line, oss_context);
  for (i = 0; i < n_mutexes; i++)
    if (mutex_tab[i].active && mutex_tab[i].mutex == mutex
	&& mutex_tab[i].context == oss_context)
      {
	mutex_tab[i].active = 0;
	mutex_tab[i].filename = file;
	mutex_tab[i].line = line;
	return;
      }

  cmn_err (CE_NOTE, "Mutex %08x not locked (%s:%d), context=%d\n",
	   mutex, file, line, oss_context);
  for (i = 0; i < n_mutexes; i++)
    if (mutex_tab[i].active == 0 && mutex_tab[i].mutex == mutex)
      {
	cmn_err (CE_CONT, "  Previous unlock at %s:%d, context=%d\n",
		 mutex_tab[i].filename, mutex_tab[i].line,
		 mutex_tab[i].context);
      }
}

void
print_mutexes (void)
{
  int i, n = 0;

  for (i = 0; i < n_mutexes; i++)
    if (mutex_tab[i].active)
      n++;

  cmn_err (CE_CONT, "%d mutexes held\n", n);

  for (i = 0; i < n_mutexes; i++)
    if (mutex_tab[i].active)
      cmn_err (CE_CONT, "   %08x %s:%d\n", mutex_tab[i].mutex,
	       mutex_tab[i].filename, mutex_tab[i].line);
}
#endif

int
oss_get_procinfo(int what)
{
	// TODO

	return OSS_EINVAL;
}

Copyright (C) 4Front Technologies, 2007. All rights reserved.

Back to index OSS web site


Copyright (C) 4Front Technologies, 2007. All rights reserved.
Back to index OSS web site