⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dmabuf.c

📁 freebsd v4.4内核源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * sound/dmabuf.c * * The DMA buffer manager for digitized voice applications * * Copyright by Hannu Savolainen 1993, 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */#include <i386/isa/sound/sound_config.h>#ifdef CONFIGURE_SOUNDCARD#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]);static struct dma_buffparms dmaps[MAX_AUDIO_DEV] ={  {0}};				/*				   * Primitive way to allocate				   * such a large array.				   * Needs dynamic run-time alloction.				 */static int space_in_queue (int);static void reorganize_buffers (int);static void dma_init_buffers (int);static voidreorganize_buffers (int dev){  /*   * This routine breaks the physical device buffers to logical ones.   */  struct dma_buffparms *dmap = audio_devs[dev]->dmap;  struct audio_operations *dsp_dev = audio_devs[dev];  unsigned        i, p, n;  unsigned        sr, nc, sz, bsz;  if (dmap->fragment_size == 0)    {				/* Compute the fragment size using the default algorithm */      sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);      nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);      sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);      if (sr < 1 || nc < 1 || sz < 1)	{	  printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",		  dev, sr, nc, sz);	  sr = DSP_DEFAULT_SPEED;	  nc = 1;	  sz = 8;	}      sz = sr * nc * sz;      sz /= 8;			/* #bits -> #bytes */      /*         * Compute a buffer size for time not exeeding 1 second.         * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds         * of sound (using the current speed, sample size and #channels).       */      bsz = dsp_dev->buffsize;      while (bsz > sz)	bsz /= 2;      if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize)	bsz /= 2;		/* Needs at least 2 buffers */      if (dmap->subdivision == 0)	/* Not already set */	dmap->subdivision = 1;	/* Init to default value */      else	bsz /= dmap->subdivision;      if (bsz < 16)	bsz = 16;		/* Just a sanity check */      while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS)	bsz *= 2;      dmap->fragment_size = bsz;    }  else    {      /*         * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or         * the buffer sice computation has already been done.       */      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))	dmap->fragment_size = (audio_devs[dev]->buffsize / 2);      bsz = dmap->fragment_size;    }  bsz &= ~0x03;			/* Force size which is multiple of 4 bytes */  /*   * Now computing addresses for the logical buffers   */  n = 0;  for (i = 0; i < dmap->raw_count &&       n < dmap->max_fragments &&       n < MAX_SUB_BUFFERS; i++)    {      p = 0;      while ((p + bsz) <= dsp_dev->buffsize &&	     n < dmap->max_fragments &&	     n < MAX_SUB_BUFFERS)	{	  dmap->buf[n] = dmap->raw_buf[i] + p;	  dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p;	  p += bsz;	  n++;	}    }  dmap->nbufs = n;  dmap->bytes_in_use = n * bsz;  for (i = 0; i < dmap->nbufs; i++)    {      dmap->counts[i] = 0;    }  dmap->flags |= DMA_ALLOC_DONE;}static voiddma_init_buffers (int dev){  struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev];  RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);  dmap->flags = DMA_BUSY;	/* Other flags off */  dmap->qlen = dmap->qhead = dmap->qtail = 0;  dmap->nbufs = 1;  dmap->bytes_in_use = audio_devs[dev]->buffsize;  dmap->dma_mode = DMODE_NONE;}intDMAbuf_open (int dev, int mode){  int             retval;  struct dma_buffparms *dmap = NULL;  if (dev >= num_audiodevs)    {      printk ("PCM device %d not installed.\n", dev);      return RET_ERROR (ENXIO);    }  if (!audio_devs[dev])    {      printk ("PCM device %d not initialized\n", dev);      return RET_ERROR (ENXIO);    }  dmap = audio_devs[dev]->dmap = &dmaps[dev];  if (dmap->flags & DMA_BUSY)    return RET_ERROR (EBUSY);#ifdef USE_RUNTIME_DMAMEM  dmap->raw_buf[0] = NULL;  sound_dma_malloc (dev);#endif  if (dmap->raw_buf[0] == NULL)    return RET_ERROR (ENOSPC);	/* Memory allocation failed during boot */  if ((retval = audio_devs[dev]->open (dev, mode)) < 0)    return retval;  dmap->open_mode = mode;  dmap->subdivision = dmap->underrun_count = 0;  dmap->fragment_size = 0;  dmap->max_fragments = 65536;	/* Just a large value */  dma_init_buffers (dev);  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);  audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);  return 0;}static voiddma_reset (int dev){  int             retval;  unsigned long   flags;  DISABLE_INTR (flags);  audio_devs[dev]->reset (dev);  audio_devs[dev]->close (dev);  if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0)    printk ("Sound: Reset failed - Can't reopen device\n");  RESTORE_INTR (flags);  dma_init_buffers (dev);  reorganize_buffers (dev);}static intdma_sync (int dev){  unsigned long   flags;  if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)    {      DISABLE_INTR (flags);      while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])	     && audio_devs[dev]->dmap->qlen)	{	  DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);	  if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))	    {	      RESTORE_INTR (flags);	      return audio_devs[dev]->dmap->qlen;	    }	}      RESTORE_INTR (flags);      /*       * Some devices such as GUS have huge amount of on board RAM for the       * audio data. We have to wait until the device has finished playing.       */      DISABLE_INTR (flags);      if (audio_devs[dev]->local_qlen)	/* Device has hidden buffers */	{	  while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))		 && audio_devs[dev]->local_qlen (dev))	    {	      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ);	    }	}      RESTORE_INTR (flags);    }  return audio_devs[dev]->dmap->qlen;}intDMAbuf_release (int dev, int mode){  unsigned long   flags;  if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))      && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT))    {      dma_sync (dev);    }#ifdef USE_RUNTIME_DMAMEM  sound_dma_free (dev);#endif  DISABLE_INTR (flags);  audio_devs[dev]->reset (dev);  audio_devs[dev]->close (dev);  audio_devs[dev]->dmap->dma_mode = DMODE_NONE;  audio_devs[dev]->dmap->flags &= ~DMA_BUSY;  RESTORE_INTR (flags);  return 0;}intDMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock){  unsigned long   flags;  int             err = EIO;  struct dma_buffparms *dmap = audio_devs[dev]->dmap;  DISABLE_INTR (flags);  if (!dmap->qlen)    {      if (dmap->flags & DMA_RESTART)	{	  dma_reset (dev);	  dmap->flags &= ~DMA_RESTART;	}      if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */	{	  dma_sync (dev);	  dma_reset (dev);	  dmap->dma_mode = DMODE_NONE;	}      if (!(dmap->flags & DMA_ALLOC_DONE))	reorganize_buffers (dev);      if (!dmap->dma_mode)	{	  int             err;	  if ((err = audio_devs[dev]->prepare_for_input (dev,				     dmap->fragment_size, dmap->nbufs)) < 0)	    {	      RESTORE_INTR (flags);	      return err;	    }	  dmap->dma_mode = DMODE_INPUT;	}      if (!(dmap->flags & DMA_ACTIVE))	{	  audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],					dmap->fragment_size, 0,				 !(audio_devs[dev]->flags & DMA_AUTOMODE) ||					!(dmap->flags & DMA_STARTED));	  dmap->flags |= DMA_ACTIVE | DMA_STARTED;	}      if (dontblock)	{	  RESTORE_INTR (flags);#if defined(__FreeBSD__)	  return RET_ERROR (EWOULDBLOCK);#else	  return RET_ERROR (EAGAIN);#endif	}      /* Wait for the next block */      DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);      if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))	{	  printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");	  dma_reset (dev);	  err = EIO;	  SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);	}      else	err = EINTR;    }  RESTORE_INTR (flags);  if (!dmap->qlen)    return RET_ERROR (err);  *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]];  *len = dmap->fragment_size - dmap->counts[dmap->qhead];  return dmap->qhead;}intDMAbuf_rmchars (int dev, int buff_no, int c){  struct dma_buffparms *dmap = audio_devs[dev]->dmap;  int             p = dmap->counts[dmap->qhead] + c;  if (p >= dmap->fragment_size)    {				/* This buffer is completely empty */      dmap->counts[dmap->qhead] = 0;      if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)	printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n",		dev, dmap->qlen, dmap->nbufs);      dmap->qlen--;      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;    }  else    dmap->counts[dmap->qhead] = p;  return 0;}intDMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local){  struct dma_buffparms *dmap = audio_devs[dev]->dmap;  switch (cmd)    {    case SNDCTL_DSP_RESET:      dma_reset (dev);      return 0;      break;    case SNDCTL_DSP_SYNC:      dma_sync (dev);      dma_reset (dev);      return 0;      break;    case SNDCTL_DSP_GETBLKSIZE:      if (!(dmap->flags & DMA_ALLOC_DONE))	reorganize_buffers (dev);      return IOCTL_OUT (arg, dmap->fragment_size);      break;    case SNDCTL_DSP_SETBLKSIZE:      {        int size = IOCTL_IN(arg);                if(!(dmap->flags & DMA_ALLOC_DONE) && size)          {            dmap->fragment_size = size;	    return 0;          }        else          return RET_ERROR (EINVAL);  /* Too late to change */      }      break;    case SNDCTL_DSP_SUBDIVIDE:      {	int             fact = IOCTL_IN (arg);	if (fact == 0)	  {	    fact = dmap->subdivision;	    if (fact == 0)	      fact = 1;	    return IOCTL_OUT (arg, fact);	  }	if (dmap->subdivision != 0 ||	    dmap->fragment_size)	/* Loo late to change */	  return RET_ERROR (EINVAL);	if (fact > MAX_REALTIME_FACTOR)	  return RET_ERROR (EINVAL);	if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)	  return RET_ERROR (EINVAL);	dmap->subdivision = fact;	return IOCTL_OUT (arg, fact);      }      break;    case SNDCTL_DSP_SETFRAGMENT:      {	int             fact = IOCTL_IN (arg);	int             bytes, count;	if (fact == 0)	  return RET_ERROR (EIO);	if (dmap->subdivision != 0 ||	    dmap->fragment_size)	/* Loo late to change */	  return RET_ERROR (EINVAL);	bytes = fact & 0xffff;	count = (fact >> 16) & 0xffff;	if (count == 0)	  count = MAX_SUB_BUFFERS;	if (bytes < 7 || bytes > 17)	/* <64 || > 128k */	  return RET_ERROR (EINVAL);	if (count < 2)	  return RET_ERROR (EINVAL);	dmap->fragment_size = (1 << bytes);	dmap->max_fragments = count;	if (dmap->fragment_size > audio_devs[dev]->buffsize)	  dmap->fragment_size = audio_devs[dev]->buffsize;	if (dmap->fragment_size == audio_devs[dev]->buffsize &&	    audio_devs[dev]->flags & DMA_AUTOMODE)	  dmap->fragment_size /= 2;	/* Needs at least 2 buffers */	dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */	return IOCTL_OUT (arg, bytes | (count << 16));      }      break;    case SNDCTL_DSP_GETISPACE:    case SNDCTL_DSP_GETOSPACE:      if (!local)	return RET_ERROR (EINVAL);      else	{	  audio_buf_info *info = (audio_buf_info *) arg;	  if (!(dmap->flags & DMA_ALLOC_DONE))	    reorganize_buffers (dev);	  info->fragstotal = dmap->nbufs;	  if (cmd == SNDCTL_DSP_GETISPACE)	    info->fragments = dmap->qlen;	  else	    {	      if (!space_in_queue (dev))		info->fragments = 0;	      else		{		  info->fragments = dmap->nbufs - dmap->qlen;		  if (audio_devs[dev]->local_qlen)		    {		      int             tmp = audio_devs[dev]->local_qlen (dev);		      if (tmp & info->fragments)			tmp--;	/*				   * This buffer has been counted twice				 */		      info->fragments -= tmp;		    }		}	    }	  if (info->fragments < 0)	    info->fragments = 0;	  else if (info->fragments > dmap->nbufs)	    info->fragments = dmap->nbufs;	  info->fragsize = dmap->fragment_size;	  info->bytes = info->fragments * dmap->fragment_size;	  if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)	    info->bytes -= dmap->counts[dmap->qhead];	}      return 0;    default:      return audio_devs[dev]->ioctl (dev, cmd, arg, local);    }}static intspace_in_queue (int dev){  int             len, max, tmp;  struct dma_buffparms *dmap = audio_devs[dev]->dmap;  if (dmap->qlen >= dmap->nbufs)	/* No space at all */    return 0;  /*     * Verify that there are no more pending buffers than the limit

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -