Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/sound/core/oss/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 86 kB image not shown  

Quelle  pcm_oss.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Digital Audio (PCM) abstract layer / OSS compatible
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 */


#if 0
#define PLUGIN_DEBUG
#endif
#if 0
#define OSS_DEBUG
#endif

#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/math64.h>
#include <linux/string.h>
#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "pcm_plugin.h"
#include <sound/info.h>
#include <linux/soundcard.h>
#include <sound/initval.h>
#include <sound/mixer_oss.h>

#define OSS_ALSAEMULVER  _SIOR ('M', 249, int)

static int dsp_map[SNDRV_CARDS];
static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
static bool nonblock_open = 1;

MODULE_AUTHOR("Jaroslav Kysela , Abramo Bagnara ");
MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
MODULE_LICENSE("GPL");
module_param_array(dsp_map, int, NULL, 0444);
MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device.");
module_param_array(adsp_map, int, NULL, 0444);
MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device.");
module_param(nonblock_open, bool, 0644);
MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);

static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file);
static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file);
static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file);

/*
 * helper functions to process hw_params
 */

static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin)
{
 int changed = 0;
 if (i->min < min) {
  i->min = min;
  i->openmin = openmin;
  changed = 1;
 } else if (i->min == min && !i->openmin && openmin) {
  i->openmin = 1;
  changed = 1;
 }
 if (i->integer) {
  if (i->openmin) {
   i->min++;
   i->openmin = 0;
  }
 }
 if (snd_interval_checkempty(i)) {
  snd_interval_none(i);
  return -EINVAL;
 }
 return changed;
}

static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax)
{
 int changed = 0;
 if (i->max > max) {
  i->max = max;
  i->openmax = openmax;
  changed = 1;
 } else if (i->max == max && !i->openmax && openmax) {
  i->openmax = 1;
  changed = 1;
 }
 if (i->integer) {
  if (i->openmax) {
   i->max--;
   i->openmax = 0;
  }
 }
 if (snd_interval_checkempty(i)) {
  snd_interval_none(i);
  return -EINVAL;
 }
 return changed;
}

static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
{
 struct snd_interval t;
 t.empty = 0;
 t.min = t.max = val;
 t.openmin = t.openmax = 0;
 t.integer = 1;
 return snd_interval_refine(i, &t);
}

/**
 * snd_pcm_hw_param_value_min
 * @params: the hw_params instance
 * @var: parameter to retrieve
 * @dir: pointer to the direction (-1,0,1) or NULL
 *
 * Return the minimum value for field PAR.
 */

static unsigned int
snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
      snd_pcm_hw_param_t var, int *dir)
{
 if (hw_is_mask(var)) {
  if (dir)
   *dir = 0;
  return snd_mask_min(hw_param_mask_c(params, var));
 }
 if (hw_is_interval(var)) {
  const struct snd_interval *i = hw_param_interval_c(params, var);
  if (dir)
   *dir = i->openmin;
  return snd_interval_min(i);
 }
 return -EINVAL;
}

/**
 * snd_pcm_hw_param_value_max
 * @params: the hw_params instance
 * @var: parameter to retrieve
 * @dir: pointer to the direction (-1,0,1) or NULL
 *
 * Return the maximum value for field PAR.
 */

static int
snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
      snd_pcm_hw_param_t var, int *dir)
{
 if (hw_is_mask(var)) {
  if (dir)
   *dir = 0;
  return snd_mask_max(hw_param_mask_c(params, var));
 }
 if (hw_is_interval(var)) {
  const struct snd_interval *i = hw_param_interval_c(params, var);
  if (dir)
   *dir = - (int) i->openmax;
  return snd_interval_max(i);
 }
 return -EINVAL;
}

static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,
      snd_pcm_hw_param_t var,
      const struct snd_mask *val)
{
 int changed;
 changed = snd_mask_refine(hw_param_mask(params, var), val);
 if (changed > 0) {
  params->cmask |= 1 << var;
  params->rmask |= 1 << var;
 }
 return changed;
}

static int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm,
     struct snd_pcm_hw_params *params,
     snd_pcm_hw_param_t var,
     const struct snd_mask *val)
{
 int changed = _snd_pcm_hw_param_mask(params, var, val);
 if (changed < 0)
  return changed;
 if (params->rmask) {
  int err = snd_pcm_hw_refine(pcm, params);
  if (err < 0)
   return err;
 }
 return 0;
}

static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
     snd_pcm_hw_param_t var, unsigned int val,
     int dir)
{
 int changed;
 int open = 0;
 if (dir) {
  if (dir > 0) {
   open = 1;
  } else if (dir < 0) {
   if (val > 0) {
    open = 1;
    val--;
   }
  }
 }
 if (hw_is_mask(var))
  changed = snd_mask_refine_min(hw_param_mask(params, var),
           val + !!open);
 else if (hw_is_interval(var))
  changed = snd_interval_refine_min(hw_param_interval(params, var),
        val, open);
 else
  return -EINVAL;
 if (changed > 0) {
  params->cmask |= 1 << var;
  params->rmask |= 1 << var;
 }
 return changed;
}

/**
 * snd_pcm_hw_param_min
 * @pcm: PCM instance
 * @params: the hw_params instance
 * @var: parameter to retrieve
 * @val: minimal value
 * @dir: pointer to the direction (-1,0,1) or NULL
 *
 * Inside configuration space defined by PARAMS remove from PAR all 
 * values < VAL. Reduce configuration space accordingly.
 * Return new minimum or -EINVAL if the configuration space is empty
 */

static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm,
    struct snd_pcm_hw_params *params,
    snd_pcm_hw_param_t var, unsigned int val,
    int *dir)
{
 int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
 if (changed < 0)
  return changed;
 if (params->rmask) {
  int err = snd_pcm_hw_refine(pcm, params);
  if (err < 0)
   return err;
 }
 return snd_pcm_hw_param_value_min(params, var, dir);
}

static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,
     snd_pcm_hw_param_t var, unsigned int val,
     int dir)
{
 int changed;
 int open = 0;
 if (dir) {
  if (dir < 0) {
   open = 1;
  } else if (dir > 0) {
   open = 1;
   val++;
  }
 }
 if (hw_is_mask(var)) {
  if (val == 0 && open) {
   snd_mask_none(hw_param_mask(params, var));
   changed = -EINVAL;
  } else
   changed = snd_mask_refine_max(hw_param_mask(params, var),
            val - !!open);
 } else if (hw_is_interval(var))
  changed = snd_interval_refine_max(hw_param_interval(params, var),
        val, open);
 else
  return -EINVAL;
 if (changed > 0) {
  params->cmask |= 1 << var;
  params->rmask |= 1 << var;
 }
 return changed;
}

/**
 * snd_pcm_hw_param_max
 * @pcm: PCM instance
 * @params: the hw_params instance
 * @var: parameter to retrieve
 * @val: maximal value
 * @dir: pointer to the direction (-1,0,1) or NULL
 *
 * Inside configuration space defined by PARAMS remove from PAR all 
 *  values >= VAL + 1. Reduce configuration space accordingly.
 *  Return new maximum or -EINVAL if the configuration space is empty
 */

static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm,
    struct snd_pcm_hw_params *params,
    snd_pcm_hw_param_t var, unsigned int val,
    int *dir)
{
 int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
 if (changed < 0)
  return changed;
 if (params->rmask) {
  int err = snd_pcm_hw_refine(pcm, params);
  if (err < 0)
   return err;
 }
 return snd_pcm_hw_param_value_max(params, var, dir);
}

static int boundary_sub(int a, int adir,
   int b, int bdir,
   int *c, int *cdir)
{
 adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);
 bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);
 *c = a - b;
 *cdir = adir - bdir;
 if (*cdir == -2) {
  (*c)--;
 } else if (*cdir == 2) {
  (*c)++;
 }
 return 0;
}

static int boundary_lt(unsigned int a, int adir,
         unsigned int b, int bdir)
{
 if (adir < 0) {
  a--;
  adir = 1;
 } else if (adir > 0)
  adir = 1;
 if (bdir < 0) {
  b--;
  bdir = 1;
 } else if (bdir > 0)
  bdir = 1;
 return a < b || (a == b && adir < bdir);
}

/* Return 1 if min is nearer to best than max */
static int boundary_nearer(int min, int mindir,
      int best, int bestdir,
      int max, int maxdir)
{
 int dmin, dmindir;
 int dmax, dmaxdir;
 boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);
 boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);
 return boundary_lt(dmin, dmindir, dmax, dmaxdir);
}

/**
 * snd_pcm_hw_param_near
 * @pcm: PCM instance
 * @params: the hw_params instance
 * @var: parameter to retrieve
 * @best: value to set
 * @dir: pointer to the direction (-1,0,1) or NULL
 *
 * Inside configuration space defined by PARAMS set PAR to the available value
 * nearest to VAL. Reduce configuration space accordingly.
 * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS,
 * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.
 * Return the value found.
  */

static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
     struct snd_pcm_hw_params *params,
     snd_pcm_hw_param_t var, unsigned int best,
     int *dir)
{
 struct snd_pcm_hw_params *save __free(kfree) = NULL;
 int v;
 unsigned int saved_min;
 int last = 0;
 int min, max;
 int mindir, maxdir;
 int valdir = dir ? *dir : 0;
 /* FIXME */
 if (best > INT_MAX)
  best = INT_MAX;
 min = max = best;
 mindir = maxdir = valdir;
 if (maxdir > 0)
  maxdir = 0;
 else if (maxdir == 0)
  maxdir = -1;
 else {
  maxdir = 1;
  max--;
 }
 save = kmalloc(sizeof(*save), GFP_KERNEL);
 if (save == NULL)
  return -ENOMEM;
 *save = *params;
 saved_min = min;
 min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
 if (min >= 0) {
  struct snd_pcm_hw_params *params1 __free(kfree) = NULL;
  if (max < 0)
   goto _end;
  if ((unsigned int)min == saved_min && mindir == valdir)
   goto _end;
  params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
  if (params1 == NULL)
   return -ENOMEM;
  *params1 = *save;
  max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
  if (max < 0)
   goto _end;
  if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
   *params = *params1;
   last = 1;
  }
 } else {
  *params = *save;
  max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
  if (max < 0)
   return max;
  last = 1;
 }
 _end:
 if (last)
  v = snd_pcm_hw_param_last(pcm, params, var, dir);
 else
  v = snd_pcm_hw_param_first(pcm, params, var, dir);
 return v;
}

static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
     snd_pcm_hw_param_t var, unsigned int val,
     int dir)
{
 int changed;
 if (hw_is_mask(var)) {
  struct snd_mask *m = hw_param_mask(params, var);
  if (val == 0 && dir < 0) {
   changed = -EINVAL;
   snd_mask_none(m);
  } else {
   if (dir > 0)
    val++;
   else if (dir < 0)
    val--;
   changed = snd_mask_refine_set(hw_param_mask(params, var), val);
  }
 } else if (hw_is_interval(var)) {
  struct snd_interval *i = hw_param_interval(params, var);
  if (val == 0 && dir < 0) {
   changed = -EINVAL;
   snd_interval_none(i);
  } else if (dir == 0)
   changed = snd_interval_refine_set(i, val);
  else {
   struct snd_interval t;
   t.openmin = 1;
   t.openmax = 1;
   t.empty = 0;
   t.integer = 0;
   if (dir < 0) {
    t.min = val - 1;
    t.max = val;
   } else {
    t.min = val;
    t.max = val+1;
   }
   changed = snd_interval_refine(i, &t);
  }
 } else
  return -EINVAL;
 if (changed > 0) {
  params->cmask |= 1 << var;
  params->rmask |= 1 << var;
 }
 return changed;
}

/**
 * snd_pcm_hw_param_set
 * @pcm: PCM instance
 * @params: the hw_params instance
 * @var: parameter to retrieve
 * @val: value to set
 * @dir: pointer to the direction (-1,0,1) or NULL
 *
 * Inside configuration space defined by PARAMS remove from PAR all 
 * values != VAL. Reduce configuration space accordingly.
 *  Return VAL or -EINVAL if the configuration space is empty
 */

static int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm,
    struct snd_pcm_hw_params *params,
    snd_pcm_hw_param_t var, unsigned int val,
    int dir)
{
 int changed = _snd_pcm_hw_param_set(params, var, val, dir);
 if (changed < 0)
  return changed;
 if (params->rmask) {
  int err = snd_pcm_hw_refine(pcm, params);
  if (err < 0)
   return err;
 }
 return snd_pcm_hw_param_value(params, var, NULL);
}

static int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,
     snd_pcm_hw_param_t var)
{
 int changed;
 changed = snd_interval_setinteger(hw_param_interval(params, var));
 if (changed > 0) {
  params->cmask |= 1 << var;
  params->rmask |= 1 << var;
 }
 return changed;
}
 
/*
 * plugin
 */


#ifdef CONFIG_SND_PCM_OSS_PLUGINS
static int snd_pcm_oss_plugin_clear(struct snd_pcm_substream *substream)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_pcm_plugin *plugin, *next;
 
 plugin = runtime->oss.plugin_first;
 while (plugin) {
  next = plugin->next;
  snd_pcm_plugin_free(plugin);
  plugin = next;
 }
 runtime->oss.plugin_first = runtime->oss.plugin_last = NULL;
 return 0;
}

static int snd_pcm_plugin_insert(struct snd_pcm_plugin *plugin)
{
 struct snd_pcm_runtime *runtime = plugin->plug->runtime;
 plugin->next = runtime->oss.plugin_first;
 plugin->prev = NULL;
 if (runtime->oss.plugin_first) {
  runtime->oss.plugin_first->prev = plugin;
  runtime->oss.plugin_first = plugin;
 } else {
  runtime->oss.plugin_last =
  runtime->oss.plugin_first = plugin;
 }
 return 0;
}

int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin)
{
 struct snd_pcm_runtime *runtime = plugin->plug->runtime;
 plugin->next = NULL;
 plugin->prev = runtime->oss.plugin_last;
 if (runtime->oss.plugin_last) {
  runtime->oss.plugin_last->next = plugin;
  runtime->oss.plugin_last = plugin;
 } else {
  runtime->oss.plugin_last =
  runtime->oss.plugin_first = plugin;
 }
 return 0;
}
#endif /* CONFIG_SND_PCM_OSS_PLUGINS */

static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 long buffer_size = snd_pcm_lib_buffer_bytes(substream);
 long bytes = frames_to_bytes(runtime, frames);
 if (buffer_size == runtime->oss.buffer_bytes)
  return bytes;
#if BITS_PER_LONG >= 64
 return runtime->oss.buffer_bytes * bytes / buffer_size;
#else
 {
  u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
  return div_u64(bsize, buffer_size);
 }
#endif
}

static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 long buffer_size = snd_pcm_lib_buffer_bytes(substream);
 if (buffer_size == runtime->oss.buffer_bytes)
  return bytes_to_frames(runtime, bytes);
 return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
}

static inline
snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
{
 return runtime->hw_ptr_interrupt;
}

/* define extended formats in the recent OSS versions (if any) */
/* linear formats */
#define AFMT_S32_LE      0x00001000
#define AFMT_S32_BE      0x00002000
#define AFMT_S24_LE      0x00008000
#define AFMT_S24_BE      0x00010000
#define AFMT_S24_PACKED  0x00040000

/* other supported formats */
#define AFMT_FLOAT       0x00004000
#define AFMT_SPDIF_RAW   0x00020000

/* unsupported formats */
#define AFMT_AC3         0x00000400
#define AFMT_VORBIS      0x00000800

static snd_pcm_format_t snd_pcm_oss_format_from(int format)
{
 switch (format) {
 case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
 case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW;
 case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM;
 case AFMT_U8:  return SNDRV_PCM_FORMAT_U8;
 case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE;
 case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE;
 case AFMT_S8:  return SNDRV_PCM_FORMAT_S8;
 case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE;
 case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE;
 case AFMT_MPEG:  return SNDRV_PCM_FORMAT_MPEG;
 case AFMT_S32_LE: return SNDRV_PCM_FORMAT_S32_LE;
 case AFMT_S32_BE: return SNDRV_PCM_FORMAT_S32_BE;
 case AFMT_S24_LE: return SNDRV_PCM_FORMAT_S24_LE;
 case AFMT_S24_BE: return SNDRV_PCM_FORMAT_S24_BE;
 case AFMT_S24_PACKED: return SNDRV_PCM_FORMAT_S24_3LE;
 case AFMT_FLOAT: return SNDRV_PCM_FORMAT_FLOAT;
 case AFMT_SPDIF_RAW: return SNDRV_PCM_FORMAT_IEC958_SUBFRAME;
 default:  return SNDRV_PCM_FORMAT_U8;
 }
}

static int snd_pcm_oss_format_to(snd_pcm_format_t format)
{
 switch (format) {
 case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
 case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW;
 case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;
 case SNDRV_PCM_FORMAT_U8:  return AFMT_U8;
 case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE;
 case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE;
 case SNDRV_PCM_FORMAT_S8:  return AFMT_S8;
 case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE;
 case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE;
 case SNDRV_PCM_FORMAT_MPEG:  return AFMT_MPEG;
 case SNDRV_PCM_FORMAT_S32_LE: return AFMT_S32_LE;
 case SNDRV_PCM_FORMAT_S32_BE: return AFMT_S32_BE;
 case SNDRV_PCM_FORMAT_S24_LE: return AFMT_S24_LE;
 case SNDRV_PCM_FORMAT_S24_BE: return AFMT_S24_BE;
 case SNDRV_PCM_FORMAT_S24_3LE: return AFMT_S24_PACKED;
 case SNDRV_PCM_FORMAT_FLOAT: return AFMT_FLOAT;
 case SNDRV_PCM_FORMAT_IEC958_SUBFRAME: return AFMT_SPDIF_RAW;
 default:   return -EINVAL;
 }
}

static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, 
       struct snd_pcm_hw_params *oss_params,
       struct snd_pcm_hw_params *slave_params)
{
 ssize_t s;
 ssize_t oss_buffer_size;
 ssize_t oss_period_size, oss_periods;
 ssize_t min_period_size, max_period_size;
 struct snd_pcm_runtime *runtime = substream->runtime;
 size_t oss_frame_size;

 oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
    params_channels(oss_params) / 8;

 oss_buffer_size = snd_pcm_hw_param_value_max(slave_params,
           SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
           NULL);
 if (oss_buffer_size <= 0)
  return -EINVAL;
 oss_buffer_size = snd_pcm_plug_client_size(substream,
         oss_buffer_size * oss_frame_size);
 if (oss_buffer_size <= 0)
  return -EINVAL;
 oss_buffer_size = rounddown_pow_of_two(oss_buffer_size);
 if (atomic_read(&substream->mmap_count)) {
  if (oss_buffer_size > runtime->oss.mmap_bytes)
   oss_buffer_size = runtime->oss.mmap_bytes;
 }

 if (substream->oss.setup.period_size > 16)
  oss_period_size = substream->oss.setup.period_size;
 else if (runtime->oss.fragshift) {
  oss_period_size = 1 << runtime->oss.fragshift;
  if (oss_period_size > oss_buffer_size / 2)
   oss_period_size = oss_buffer_size / 2;
 } else {
  int sd;
  size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8;

  oss_period_size = oss_buffer_size;
  do {
   oss_period_size /= 2;
  } while (oss_period_size > bytes_per_sec);
  if (runtime->oss.subdivision == 0) {
   sd = 4;
   if (oss_period_size / sd > 4096)
    sd *= 2;
   if (oss_period_size / sd < 4096)
    sd = 1;
  } else
   sd = runtime->oss.subdivision;
  oss_period_size /= sd;
  if (oss_period_size < 16)
   oss_period_size = 16;
 }

 min_period_size = snd_pcm_plug_client_size(substream,
         snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
 if (min_period_size > 0) {
  min_period_size *= oss_frame_size;
  min_period_size = roundup_pow_of_two(min_period_size);
  if (oss_period_size < min_period_size)
   oss_period_size = min_period_size;
 }

 max_period_size = snd_pcm_plug_client_size(substream,
         snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
 if (max_period_size > 0) {
  max_period_size *= oss_frame_size;
  max_period_size = rounddown_pow_of_two(max_period_size);
  if (oss_period_size > max_period_size)
   oss_period_size = max_period_size;
 }

 oss_periods = oss_buffer_size / oss_period_size;

 if (substream->oss.setup.periods > 1)
  oss_periods = substream->oss.setup.periods;

 s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
 if (s > 0 && runtime->oss.maxfrags && s > runtime->oss.maxfrags)
  s = runtime->oss.maxfrags;
 if (oss_periods > s)
  oss_periods = s;

 s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
 if (s < 2)
  s = 2;
 if (oss_periods < s)
  oss_periods = s;

 while (oss_period_size * oss_periods > oss_buffer_size)
  oss_period_size /= 2;

 if (oss_period_size < 16)
  return -EINVAL;

 /* don't allocate too large period; 1MB period must be enough */
 if (oss_period_size > 1024 * 1024)
  return -ENOMEM;

 runtime->oss.period_bytes = oss_period_size;
 runtime->oss.period_frames = 1;
 runtime->oss.periods = oss_periods;
 return 0;
}

static int choose_rate(struct snd_pcm_substream *substream,
         struct snd_pcm_hw_params *params, unsigned int best_rate)
{
 const struct snd_interval *it;
 struct snd_pcm_hw_params *save __free(kfree) = NULL;
 unsigned int rate, prev;

 save = kmalloc(sizeof(*save), GFP_KERNEL);
 if (save == NULL)
  return -ENOMEM;
 *save = *params;
 it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE);

 /* try multiples of the best rate */
 rate = best_rate;
 for (;;) {
  if (it->max < rate || (it->max == rate && it->openmax))
   break;
  if (it->min < rate || (it->min == rate && !it->openmin)) {
   int ret;
   ret = snd_pcm_hw_param_set(substream, params,
         SNDRV_PCM_HW_PARAM_RATE,
         rate, 0);
   if (ret == (int)rate)
    return rate;
   *params = *save;
  }
  prev = rate;
  rate += best_rate;
  if (rate <= prev)
   break;
 }

 /* not found, use the nearest rate */
 return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
}

/* parameter locking: returns immediately if tried during streaming */
static int lock_params(struct snd_pcm_runtime *runtime)
{
 if (mutex_lock_interruptible(&runtime->oss.params_lock))
  return -ERESTARTSYS;
 if (atomic_read(&runtime->oss.rw_ref)) {
  mutex_unlock(&runtime->oss.params_lock);
  return -EBUSY;
 }
 return 0;
}

static void unlock_params(struct snd_pcm_runtime *runtime)
{
 mutex_unlock(&runtime->oss.params_lock);
}

static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream)
{
 struct snd_pcm_runtime *runtime = substream->runtime;

 kvfree(runtime->oss.buffer);
 runtime->oss.buffer = NULL;
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
 snd_pcm_oss_plugin_clear(substream);
#endif
}

/* call with params_lock held */
static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct snd_pcm_hw_params *params, *sparams;
 struct snd_pcm_sw_params *sw_params;
 ssize_t oss_buffer_size, oss_period_size;
 size_t oss_frame_size;
 int err;
 int direct;
 snd_pcm_format_t format, sformat;
 int n;
 const struct snd_mask *sformat_mask;
 struct snd_mask mask;

 if (!runtime->oss.params)
  return 0;
 sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
 params = kmalloc(sizeof(*params), GFP_KERNEL);
 sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
 if (!sw_params || !params || !sparams) {
  err = -ENOMEM;
  goto failure;
 }

 if (atomic_read(&substream->mmap_count))
  direct = 1;
 else
  direct = substream->oss.setup.direct;

 _snd_pcm_hw_params_any(sparams);
 _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
 _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
 snd_mask_none(&mask);
 if (atomic_read(&substream->mmap_count))
  snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
 else {
  snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED);
  if (!direct)
   snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
 }
 err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
 if (err < 0) {
  pcm_dbg(substream->pcm, "No usable accesses\n");
  err = -EINVAL;
  goto failure;
 }

 err = choose_rate(substream, sparams, runtime->oss.rate);
 if (err < 0)
  goto failure;
 err = snd_pcm_hw_param_near(substream, sparams,
        SNDRV_PCM_HW_PARAM_CHANNELS,
        runtime->oss.channels, NULL);
 if (err < 0)
  goto failure;

 format = snd_pcm_oss_format_from(runtime->oss.format);

 sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
 if (direct)
  sformat = format;
 else
  sformat = snd_pcm_plug_slave_format(format, sformat_mask);

 if ((__force int)sformat < 0 ||
     !snd_mask_test_format(sformat_mask, sformat)) {
  pcm_for_each_format(sformat) {
   if (snd_mask_test_format(sformat_mask, sformat) &&
       snd_pcm_oss_format_to(sformat) >= 0)
    goto format_found;
  }
  pcm_dbg(substream->pcm, "Cannot find a format!!!\n");
  err = -EINVAL;
  goto failure;
 }
 format_found:
 err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
 if (err < 0)
  goto failure;

 if (direct) {
  memcpy(params, sparams, sizeof(*params));
 } else {
  _snd_pcm_hw_params_any(params);
  _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
          (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
  _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
          (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0);
  _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
          runtime->oss.channels, 0);
  _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
          runtime->oss.rate, 0);
  pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",
    params_access(params), params_format(params),
    params_channels(params), params_rate(params));
 }
 pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",
   params_access(sparams), params_format(sparams),
   params_channels(sparams), params_rate(sparams));

 oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *
    params_channels(params) / 8;

 err = snd_pcm_oss_period_size(substream, params, sparams);
 if (err < 0)
  goto failure;

 n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
 err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
 if (err < 0)
  goto failure;

 err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
         runtime->oss.periods, NULL);
 if (err < 0)
  goto failure;

 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);

 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams);
 if (err < 0) {
  pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err);
  goto failure;
 }

#ifdef CONFIG_SND_PCM_OSS_PLUGINS
 snd_pcm_oss_plugin_clear(substream);
 if (!direct) {
  /* add necessary plugins */
  err = snd_pcm_plug_format_plugins(substream, params, sparams);
  if (err < 0) {
   pcm_dbg(substream->pcm,
    "snd_pcm_plug_format_plugins failed: %i\n", err);
   goto failure;
  }
  if (runtime->oss.plugin_first) {
   struct snd_pcm_plugin *plugin;
   err = snd_pcm_plugin_build_io(substream, sparams, &plugin);
   if (err < 0) {
    pcm_dbg(substream->pcm,
     "snd_pcm_plugin_build_io failed: %i\n", err);
    goto failure;
   }
   if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
    err = snd_pcm_plugin_append(plugin);
   } else {
    err = snd_pcm_plugin_insert(plugin);
   }
   if (err < 0)
    goto failure;
  }
 }
#endif

 if (runtime->oss.trigger) {
  sw_params->start_threshold = 1;
 } else {
  sw_params->start_threshold = runtime->boundary;
 }
 if (atomic_read(&substream->mmap_count) ||
     substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  sw_params->stop_threshold = runtime->boundary;
 else
  sw_params->stop_threshold = runtime->buffer_size;
 sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
 sw_params->period_step = 1;
 sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
  1 : runtime->period_size;
 if (atomic_read(&substream->mmap_count) ||
     substream->oss.setup.nosilence) {
  sw_params->silence_threshold = 0;
  sw_params->silence_size = 0;
 } else {
  snd_pcm_uframes_t frames;
  frames = runtime->period_size + 16;
  if (frames > runtime->buffer_size)
   frames = runtime->buffer_size;
  sw_params->silence_threshold = frames;
  sw_params->silence_size = frames;
 }

 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params);
 if (err < 0) {
  pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err);
  goto failure;
 }

 runtime->oss.periods = params_periods(sparams);
 oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
 if (oss_period_size < 0) {
  err = -EINVAL;
  goto failure;
 }
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
 if (runtime->oss.plugin_first) {
  err = snd_pcm_plug_alloc(substream, oss_period_size);
  if (err < 0)
   goto failure;
 }
#endif
 oss_period_size = array_size(oss_period_size, oss_frame_size);
 oss_buffer_size = array_size(oss_period_size, runtime->oss.periods);
 if (oss_buffer_size <= 0) {
  err = -EINVAL;
  goto failure;
 }

 runtime->oss.period_bytes = oss_period_size;
 runtime->oss.buffer_bytes = oss_buffer_size;

 pdprintf("oss: period bytes = %i, buffer bytes = %i\n",
   runtime->oss.period_bytes,
   runtime->oss.buffer_bytes);
 pdprintf("slave: period_size = %i, buffer_size = %i\n",
   params_period_size(sparams),
   params_buffer_size(sparams));

 runtime->oss.format = snd_pcm_oss_format_to(params_format(params));
 runtime->oss.channels = params_channels(params);
 runtime->oss.rate = params_rate(params);

 kvfree(runtime->oss.buffer);
 runtime->oss.buffer = kvzalloc(runtime->oss.period_bytes, GFP_KERNEL);
 if (!runtime->oss.buffer) {
  err = -ENOMEM;
  goto failure;
 }

 runtime->oss.params = 0;
 runtime->oss.prepare = 1;
 runtime->oss.buffer_used = 0;
 snd_pcm_runtime_buffer_set_silence(runtime);

 runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);

 err = 0;
failure:
 if (err)
  snd_pcm_oss_release_buffers(substream);
 kfree(sw_params);
 kfree(params);
 kfree(sparams);
 return err;
}

/* this one takes the lock by itself */
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
         bool trylock)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 int err;

 if (trylock) {
  if (!(mutex_trylock(&runtime->oss.params_lock)))
   return -EAGAIN;
 } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
  return -ERESTARTSYS;

 err = snd_pcm_oss_change_params_locked(substream);
 mutex_unlock(&runtime->oss.params_lock);
 return err;
}

static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_file, struct snd_pcm_substream **r_substream)
{
 int idx, err;
 struct snd_pcm_substream *asubstream = NULL, *substream;

 for (idx = 0; idx < 2; idx++) {
  substream = pcm_oss_file->streams[idx];
  if (substream == NULL)
   continue;
  if (asubstream == NULL)
   asubstream = substream;
  if (substream->runtime->oss.params) {
   err = snd_pcm_oss_change_params(substream, false);
   if (err < 0)
    return err;
  }
 }
 if (!asubstream)
  return -EIO;
 if (r_substream)
  *r_substream = asubstream;
 return 0;
}

/* call with params_lock held */
/* NOTE: this always call PREPARE unconditionally no matter whether
 * runtime->oss.prepare is set or not
 */

static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
{
 int err;
 struct snd_pcm_runtime *runtime = substream->runtime;

 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
 if (err < 0) {
  pcm_dbg(substream->pcm,
   "snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
  return err;
 }
 runtime->oss.prepare = 0;
 runtime->oss.prev_hw_ptr_period = 0;
 runtime->oss.period_ptr = 0;
 runtime->oss.buffer_used = 0;

 return 0;
}

static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
{
 struct snd_pcm_runtime *runtime;
 int err;

 runtime = substream->runtime;
 if (runtime->oss.params) {
  err = snd_pcm_oss_change_params(substream, false);
  if (err < 0)
   return err;
 }
 if (runtime->oss.prepare) {
  if (mutex_lock_interruptible(&runtime->oss.params_lock))
   return -ERESTARTSYS;
  err = snd_pcm_oss_prepare(substream);
  mutex_unlock(&runtime->oss.params_lock);
  if (err < 0)
   return err;
 }
 return 0;
}

/* call with params_lock held */
static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
{
 struct snd_pcm_runtime *runtime;
 int err;

 runtime = substream->runtime;
 if (runtime->oss.params) {
  err = snd_pcm_oss_change_params_locked(substream);
  if (err < 0)
   return err;
 }
 if (runtime->oss.prepare) {
  err = snd_pcm_oss_prepare(substream);
  if (err < 0)
   return err;
 }
 return 0;
}

static int snd_pcm_oss_capture_position_fixup(struct snd_pcm_substream *substream, snd_pcm_sframes_t *delay)
{
 struct snd_pcm_runtime *runtime;
 snd_pcm_uframes_t frames;
 int err = 0;

 while (1) {
  err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay);
  if (err < 0)
   break;
  runtime = substream->runtime;
  if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size)
   break;
  /* in case of overrun, skip whole periods like OSS/Linux driver does */
  /* until avail(delay) <= buffer_size */
  frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;
  frames /= runtime->period_size;
  frames *= runtime->period_size;
  err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames);
  if (err < 0)
   break;
 }
 return err;
}

snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 int ret;
 while (1) {
  if (runtime->state == SNDRV_PCM_STATE_XRUN ||
      runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
   pcm_dbg(substream->pcm,
    "pcm_oss: write: recovering from %s\n",
    runtime->state == SNDRV_PCM_STATE_XRUN ?
    "XRUN" : "SUSPEND");
#endif
   ret = snd_pcm_oss_prepare(substream);
   if (ret < 0)
    break;
  }
  mutex_unlock(&runtime->oss.params_lock);
  ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
      frames, in_kernel);
  mutex_lock(&runtime->oss.params_lock);
  if (ret != -EPIPE && ret != -ESTRPIPE)
   break;
  /* test, if we can't store new data, because the stream */
  /* has not been started */
  if (runtime->state == SNDRV_PCM_STATE_PREPARED)
   return -EAGAIN;
 }
 return ret;
}

snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 snd_pcm_sframes_t delay;
 int ret;
 while (1) {
  if (runtime->state == SNDRV_PCM_STATE_XRUN ||
      runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
   pcm_dbg(substream->pcm,
    "pcm_oss: read: recovering from %s\n",
    runtime->state == SNDRV_PCM_STATE_XRUN ?
    "XRUN" : "SUSPEND");
#endif
   ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
   if (ret < 0)
    break;
  } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
   ret = snd_pcm_oss_prepare(substream);
   if (ret < 0)
    break;
  }
  ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
  if (ret < 0)
   break;
  mutex_unlock(&runtime->oss.params_lock);
  ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
      frames, in_kernel);
  mutex_lock(&runtime->oss.params_lock);
  if (ret == -EPIPE) {
   if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
    ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
    if (ret < 0)
     break;
   }
   continue;
  }
  if (ret != -ESTRPIPE)
   break;
 }
 return ret;
}

#ifdef CONFIG_SND_PCM_OSS_PLUGINS
snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 int ret;
 while (1) {
  if (runtime->state == SNDRV_PCM_STATE_XRUN ||
      runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
   pcm_dbg(substream->pcm,
    "pcm_oss: writev: recovering from %s\n",
    runtime->state == SNDRV_PCM_STATE_XRUN ?
    "XRUN" : "SUSPEND");
#endif
   ret = snd_pcm_oss_prepare(substream);
   if (ret < 0)
    break;
  }
  ret = snd_pcm_kernel_writev(substream, bufs, frames);
  if (ret != -EPIPE && ret != -ESTRPIPE)
   break;

  /* test, if we can't store new data, because the stream */
  /* has not been started */
  if (runtime->state == SNDRV_PCM_STATE_PREPARED)
   return -EAGAIN;
 }
 return ret;
}
 
snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 int ret;
 while (1) {
  if (runtime->state == SNDRV_PCM_STATE_XRUN ||
      runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
   pcm_dbg(substream->pcm,
    "pcm_oss: readv: recovering from %s\n",
    runtime->state == SNDRV_PCM_STATE_XRUN ?
    "XRUN" : "SUSPEND");
#endif
   ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
   if (ret < 0)
    break;
  } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
   ret = snd_pcm_oss_prepare(substream);
   if (ret < 0)
    break;
  }
  ret = snd_pcm_kernel_readv(substream, bufs, frames);
  if (ret != -EPIPE && ret != -ESTRPIPE)
   break;
 }
 return ret;
}
#endif /* CONFIG_SND_PCM_OSS_PLUGINS */

static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const char *buf, size_t bytes, int in_kernel)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 snd_pcm_sframes_t frames, frames1;
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
 if (runtime->oss.plugin_first) {
  struct snd_pcm_plugin_channel *channels;
  size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
  if (!in_kernel) {
   if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes))
    return -EFAULT;
   buf = runtime->oss.buffer;
  }
  frames = bytes / oss_frame_bytes;
  frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels);
  if (frames1 < 0)
   return frames1;
  frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1);
  if (frames1 <= 0)
   return frames1;
  bytes = frames1 * oss_frame_bytes;
 } else
#endif
 {
  frames = bytes_to_frames(runtime, bytes);
  frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel);
  if (frames1 <= 0)
   return frames1;
  bytes = frames_to_bytes(runtime, frames1);
 }
 return bytes;
}

static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const char __user *buf, size_t bytes)
{
 size_t xfer = 0;
 ssize_t tmp = 0;
 struct snd_pcm_runtime *runtime = substream->runtime;

 if (atomic_read(&substream->mmap_count))
  return -ENXIO;

 atomic_inc(&runtime->oss.rw_ref);
 while (bytes > 0) {
  if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
   tmp = -ERESTARTSYS;
   break;
  }
  tmp = snd_pcm_oss_make_ready_locked(substream);
  if (tmp < 0)
   goto err;
  if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
   tmp = bytes;
   if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
    tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;
   if (tmp > 0) {
    if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) {
     tmp = -EFAULT;
     goto err;
    }
   }
   runtime->oss.buffer_used += tmp;
   buf += tmp;
   bytes -= tmp;
   xfer += tmp;
   if (substream->oss.setup.partialfrag ||
       runtime->oss.buffer_used == runtime->oss.period_bytes) {
    tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, 
        runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
    if (tmp <= 0)
     goto err;
    runtime->oss.bytes += tmp;
    runtime->oss.period_ptr += tmp;
    runtime->oss.period_ptr %= runtime->oss.period_bytes;
    if (runtime->oss.period_ptr == 0 ||
        runtime->oss.period_ptr == runtime->oss.buffer_used)
     runtime->oss.buffer_used = 0;
    else if ((substream->f_flags & O_NONBLOCK) != 0) {
     tmp = -EAGAIN;
     goto err;
    }
   }
  } else {
   tmp = snd_pcm_oss_write2(substream,
       (const char __force *)buf,
       runtime->oss.period_bytes, 0);
   if (tmp <= 0)
    goto err;
   runtime->oss.bytes += tmp;
   buf += tmp;
   bytes -= tmp;
   xfer += tmp;
   if ((substream->f_flags & O_NONBLOCK) != 0 &&
       tmp != runtime->oss.period_bytes)
    tmp = -EAGAIN;
  }
 err:
  mutex_unlock(&runtime->oss.params_lock);
  if (tmp < 0)
   break;
  if (signal_pending(current)) {
   tmp = -ERESTARTSYS;
   break;
  }
  tmp = 0;
 }
 atomic_dec(&runtime->oss.rw_ref);
 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}

static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 snd_pcm_sframes_t frames, frames1;
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
 char __user *final_dst = (char __force __user *)buf;
 if (runtime->oss.plugin_first) {
  struct snd_pcm_plugin_channel *channels;
  size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
  if (!in_kernel)
   buf = runtime->oss.buffer;
  frames = bytes / oss_frame_bytes;
  frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels);
  if (frames1 < 0)
   return frames1;
  frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1);
  if (frames1 <= 0)
   return frames1;
  bytes = frames1 * oss_frame_bytes;
  if (!in_kernel && copy_to_user(final_dst, buf, bytes))
   return -EFAULT;
 } else
#endif
 {
  frames = bytes_to_frames(runtime, bytes);
  frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel);
  if (frames1 <= 0)
   return frames1;
  bytes = frames_to_bytes(runtime, frames1);
 }
 return bytes;
}

static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __user *buf, size_t bytes)
{
 size_t xfer = 0;
 ssize_t tmp = 0;
 struct snd_pcm_runtime *runtime = substream->runtime;

 if (atomic_read(&substream->mmap_count))
  return -ENXIO;

 atomic_inc(&runtime->oss.rw_ref);
 while (bytes > 0) {
  if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
   tmp = -ERESTARTSYS;
   break;
  }
  tmp = snd_pcm_oss_make_ready_locked(substream);
  if (tmp < 0)
   goto err;
  if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
   if (runtime->oss.buffer_used == 0) {
    tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
    if (tmp <= 0)
     goto err;
    runtime->oss.bytes += tmp;
    runtime->oss.period_ptr = tmp;
    runtime->oss.buffer_used = tmp;
   }
   tmp = bytes;
   if ((size_t) tmp > runtime->oss.buffer_used)
    tmp = runtime->oss.buffer_used;
   if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) {
    tmp = -EFAULT;
    goto err;
   }
   buf += tmp;
   bytes -= tmp;
   xfer += tmp;
   runtime->oss.buffer_used -= tmp;
  } else {
   tmp = snd_pcm_oss_read2(substream, (char __force *)buf,
      runtime->oss.period_bytes, 0);
   if (tmp <= 0)
    goto err;
   runtime->oss.bytes += tmp;
   buf += tmp;
   bytes -= tmp;
   xfer += tmp;
  }
 err:
  mutex_unlock(&runtime->oss.params_lock);
  if (tmp < 0)
   break;
  if (signal_pending(current)) {
   tmp = -ERESTARTSYS;
   break;
  }
  tmp = 0;
 }
 atomic_dec(&runtime->oss.rw_ref);
 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}

static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 int i;

 for (i = 0; i < 2; i++) { 
  substream = pcm_oss_file->streams[i];
  if (!substream)
   continue;
  runtime = substream->runtime;
  snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
  mutex_lock(&runtime->oss.params_lock);
  runtime->oss.prepare = 1;
  runtime->oss.buffer_used = 0;
  runtime->oss.prev_hw_ptr_period = 0;
  runtime->oss.period_ptr = 0;
  mutex_unlock(&runtime->oss.params_lock);
 }
 return 0;
}

static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 int err;

 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
 if (substream != NULL) {
  err = snd_pcm_oss_make_ready(substream);
  if (err < 0)
   return err;
  snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
 }
 /* note: all errors from the start action are ignored */
 /* OSS apps do not know, how to handle them */
 return 0;
}

static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
{
 struct snd_pcm_runtime *runtime;
 ssize_t result = 0;
 snd_pcm_state_t state;
 long res;
 wait_queue_entry_t wait;

 runtime = substream->runtime;
 init_waitqueue_entry(&wait, current);
 add_wait_queue(&runtime->sleep, &wait);
#ifdef OSS_DEBUG
 pcm_dbg(substream->pcm, "sync1: size = %li\n", size);
#endif
 while (1) {
  result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
  if (result > 0) {
   runtime->oss.buffer_used = 0;
   result = 0;
   break;
  }
  if (result != 0 && result != -EAGAIN)
   break;
  result = 0;
  set_current_state(TASK_INTERRUPTIBLE);
  scoped_guard(pcm_stream_lock_irq, substream)
   state = runtime->state;
  if (state != SNDRV_PCM_STATE_RUNNING) {
   set_current_state(TASK_RUNNING);
   break;
  }
  res = schedule_timeout(10 * HZ);
  if (signal_pending(current)) {
   result = -ERESTARTSYS;
   break;
  }
  if (res == 0) {
   pcm_err(substream->pcm,
    "OSS sync error - DMA timeout\n");
   result = -EIO;
   break;
  }
 }
 remove_wait_queue(&runtime->sleep, &wait);
 return result;
}

static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
{
 int err = 0;
 unsigned int saved_f_flags;
 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 snd_pcm_format_t format;
 unsigned long width;
 size_t size;

 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
 if (substream != NULL) {
  runtime = substream->runtime;
  if (atomic_read(&substream->mmap_count))
   goto __direct;
  atomic_inc(&runtime->oss.rw_ref);
  if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
   atomic_dec(&runtime->oss.rw_ref);
   return -ERESTARTSYS;
  }
  err = snd_pcm_oss_make_ready_locked(substream);
  if (err < 0)
   goto unlock;
  format = snd_pcm_oss_format_from(runtime->oss.format);
  width = snd_pcm_format_physical_width(format);
  if (runtime->oss.buffer_used > 0) {
#ifdef OSS_DEBUG
   pcm_dbg(substream->pcm, "sync: buffer_used\n");
#endif
   size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
   snd_pcm_format_set_silence(format,
         runtime->oss.buffer + runtime->oss.buffer_used,
         size);
   err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
   if (err < 0)
    goto unlock;
  } else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG
   pcm_dbg(substream->pcm, "sync: period_ptr\n");
#endif
   size = runtime->oss.period_bytes - runtime->oss.period_ptr;
   snd_pcm_format_set_silence(format,
         runtime->oss.buffer,
         size * 8 / width);
   err = snd_pcm_oss_sync1(substream, size);
   if (err < 0)
    goto unlock;
  }
  /*
 * The ALSA's period might be a bit large than OSS one.
 * Fill the remain portion of ALSA period with zeros.
 */

  size = runtime->control->appl_ptr % runtime->period_size;
  if (size > 0) {
   size = runtime->period_size - size;
   if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
    snd_pcm_lib_write(substream, NULL, size);
   else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
    snd_pcm_lib_writev(substream, NULL, size);
  }
unlock:
  mutex_unlock(&runtime->oss.params_lock);
  atomic_dec(&runtime->oss.rw_ref);
  if (err < 0)
   return err;
  /*
 * finish sync: drain the buffer
 */

       __direct:
  saved_f_flags = substream->f_flags;
  substream->f_flags &= ~O_NONBLOCK;
  err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
  substream->f_flags = saved_f_flags;
  if (err < 0)
   return err;
  mutex_lock(&runtime->oss.params_lock);
  runtime->oss.prepare = 1;
  mutex_unlock(&runtime->oss.params_lock);
 }

 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
 if (substream != NULL) {
  err = snd_pcm_oss_make_ready(substream);
  if (err < 0)
   return err;
  runtime = substream->runtime;
  err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
  if (err < 0)
   return err;
  mutex_lock(&runtime->oss.params_lock);
  runtime->oss.buffer_used = 0;
  runtime->oss.prepare = 1;
  mutex_unlock(&runtime->oss.params_lock);
 }
 return 0;
}

static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
{
 int idx;

 for (idx = 1; idx >= 0; --idx) {
  struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
  struct snd_pcm_runtime *runtime;
  int err;

  if (substream == NULL)
   continue;
  runtime = substream->runtime;
  if (rate < 1000)
   rate = 1000;
  else if (rate > 192000)
   rate = 192000;
  err = lock_params(runtime);
  if (err < 0)
   return err;
  if (runtime->oss.rate != rate) {
   runtime->oss.params = 1;
   runtime->oss.rate = rate;
  }
  unlock_params(runtime);
 }
 return snd_pcm_oss_get_rate(pcm_oss_file);
}

static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 int err;
 
 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
 if (err < 0)
  return err;
 return substream->runtime->oss.rate;
}

static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsigned int channels)
{
 int idx;
 if (channels < 1)
  channels = 1;
 if (channels > 128)
  return -EINVAL;
 for (idx = 1; idx >= 0; --idx) {
  struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
  struct snd_pcm_runtime *runtime;
  int err;

  if (substream == NULL)
   continue;
  runtime = substream->runtime;
  err = lock_params(runtime);
  if (err < 0)
   return err;
  if (runtime->oss.channels != channels) {
   runtime->oss.params = 1;
   runtime->oss.channels = channels;
  }
  unlock_params(runtime);
 }
 return snd_pcm_oss_get_channels(pcm_oss_file);
}

static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 int err;
 
 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
 if (err < 0)
  return err;
 return substream->runtime->oss.channels;
}

static int snd_pcm_oss_get_block_size(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 int err;
 
 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
 if (err < 0)
  return err;
 return substream->runtime->oss.period_bytes;
}

static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 int err;
 int direct;
 struct snd_pcm_hw_params *params __free(kfree) = NULL;
 unsigned int formats = 0;
 const struct snd_mask *format_mask;
 int fmt;

 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
 if (err < 0)
  return err;
 if (atomic_read(&substream->mmap_count))
  direct = 1;
 else
  direct = substream->oss.setup.direct;
 if (!direct)
  return AFMT_MU_LAW | AFMT_U8 |
         AFMT_S16_LE | AFMT_S16_BE |
         AFMT_S8 | AFMT_U16_LE |
         AFMT_U16_BE |
   AFMT_S32_LE | AFMT_S32_BE |
   AFMT_S24_LE | AFMT_S24_BE |
   AFMT_S24_PACKED;
 params = kmalloc(sizeof(*params), GFP_KERNEL);
 if (!params)
  return -ENOMEM;
 _snd_pcm_hw_params_any(params);
 err = snd_pcm_hw_refine(substream, params);
 if (err < 0)
  return err;
 format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
 for (fmt = 0; fmt < 32; ++fmt) {
  if (snd_mask_test(format_mask, fmt)) {
   int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt);
   if (f >= 0)
    formats |= f;
  }
 }

 return formats;
}

static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
{
 int formats, idx;
 int err;
 
 if (format != AFMT_QUERY) {
  formats = snd_pcm_oss_get_formats(pcm_oss_file);
  if (formats < 0)
   return formats;
  if (!(formats & format))
   format = AFMT_U8;
  for (idx = 1; idx >= 0; --idx) {
   struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
   struct snd_pcm_runtime *runtime;
   if (substream == NULL)
    continue;
   runtime = substream->runtime;
   err = lock_params(runtime);
   if (err < 0)
    return err;
   if (runtime->oss.format != format) {
    runtime->oss.params = 1;
    runtime->oss.format = format;
   }
   unlock_params(runtime);
  }
 }
 return snd_pcm_oss_get_format(pcm_oss_file);
}

static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 int err;
 
 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
 if (err < 0)
  return err;
 return substream->runtime->oss.format;
}

static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int subdivide)
{
 struct snd_pcm_runtime *runtime;

 runtime = substream->runtime;
 if (subdivide == 0) {
  subdivide = runtime->oss.subdivision;
  if (subdivide == 0)
   subdivide = 1;
  return subdivide;
 }
 if (runtime->oss.subdivision || runtime->oss.fragshift)
  return -EINVAL;
 if (subdivide != 1 && subdivide != 2 && subdivide != 4 &&
     subdivide != 8 && subdivide != 16)
  return -EINVAL;
 runtime->oss.subdivision = subdivide;
 runtime->oss.params = 1;
 return subdivide;
}

static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int subdivide)
{
 int err = -EINVAL, idx;

 for (idx = 1; idx >= 0; --idx) {
  struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
  struct snd_pcm_runtime *runtime;

  if (substream == NULL)
   continue;
  runtime = substream->runtime;
  err = lock_params(runtime);
  if (err < 0)
   return err;
  err = snd_pcm_oss_set_subdivide1(substream, subdivide);
  unlock_params(runtime);
  if (err < 0)
   return err;
 }
 return err;
}

static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsigned int val)
{
 struct snd_pcm_runtime *runtime;
 int fragshift;

 runtime = substream->runtime;
 if (runtime->oss.subdivision || runtime->oss.fragshift)
  return -EINVAL;
 fragshift = val & 0xffff;
 if (fragshift >= 25) /* should be large enough */
  return -EINVAL;
 runtime->oss.fragshift = fragshift;
 runtime->oss.maxfrags = (val >> 16) & 0xffff;
 if (runtime->oss.fragshift < 4)  /* < 16 */
  runtime->oss.fragshift = 4;
 if (runtime->oss.maxfrags < 2)
  runtime->oss.maxfrags = 2;
 runtime->oss.params = 1;
 return 0;
}

static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsigned int val)
{
 int err = -EINVAL, idx;

 for (idx = 1; idx >= 0; --idx) {
  struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
  struct snd_pcm_runtime *runtime;

  if (substream == NULL)
   continue;
  runtime = substream->runtime;
  err = lock_params(runtime);
  if (err < 0)
   return err;
  err = snd_pcm_oss_set_fragment1(substream, val);
  unlock_params(runtime);
  if (err < 0)
   return err;
 }
 return err;
}

static int snd_pcm_oss_nonblock(struct file * file)
{
 spin_lock(&file->f_lock);
 file->f_flags |= O_NONBLOCK;
 spin_unlock(&file->f_lock);
 return 0;
}

static int snd_pcm_oss_get_caps1(struct snd_pcm_substream *substream, int res)
{

 if (substream == NULL) {
  res &= ~DSP_CAP_DUPLEX;
  return res;
 }
#ifdef DSP_CAP_MULTI
 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  if (substream->pstr->substream_count > 1)
   res |= DSP_CAP_MULTI;
#endif
 /* DSP_CAP_REALTIME is set all times: */
 /* all ALSA drivers can return actual pointer in ring buffer */
#if defined(DSP_CAP_REALTIME) && 0
 {
  struct snd_pcm_runtime *runtime = substream->runtime;
  if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))
   res &= ~DSP_CAP_REALTIME;
 }
#endif
 return res;
}

static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file)
{
 int result, idx;
 
 result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME;
 for (idx = 0; idx < 2; idx++) {
  struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
  result = snd_pcm_oss_get_caps1(substream, result);
 }
 result |= 0x0001; /* revision - same as SB AWE 64 */
 return result;
}

static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream,
          snd_pcm_uframes_t hw_ptr)
{
 struct snd_pcm_runtime *runtime = substream->runtime;
 snd_pcm_uframes_t appl_ptr;
 appl_ptr = hw_ptr + runtime->buffer_size;
 appl_ptr %= runtime->boundary;
 runtime->control->appl_ptr = appl_ptr;
}

static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int trigger)
{
 struct snd_pcm_runtime *runtime;
 struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;
 int err, cmd;

#ifdef OSS_DEBUG
 pr_debug("pcm_oss: trigger = 0x%x\n", trigger);
#endif
 
 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];

 if (psubstream) {
  err = snd_pcm_oss_make_ready(psubstream);
  if (err < 0)
   return err;
 }
 if (csubstream) {
  err = snd_pcm_oss_make_ready(csubstream);
  if (err < 0)
   return err;
 }
       if (psubstream) {
        runtime = psubstream->runtime;
  cmd = 0;
  if (mutex_lock_interruptible(&runtime->oss.params_lock))
   return -ERESTARTSYS;
  if (trigger & PCM_ENABLE_OUTPUT) {
   if (runtime->oss.trigger)
    goto _skip1;
   if (atomic_read(&psubstream->mmap_count))
    snd_pcm_oss_simulate_fill(psubstream,
      get_hw_ptr_period(runtime));
   runtime->oss.trigger = 1;
   runtime->start_threshold = 1;
   cmd = SNDRV_PCM_IOCTL_START;
  } else {
   if (!runtime->oss.trigger)
    goto _skip1;
   runtime->oss.trigger = 0;
   runtime->start_threshold = runtime->boundary;
   cmd = SNDRV_PCM_IOCTL_DROP;
   runtime->oss.prepare = 1;
  }
 _skip1:
  mutex_unlock(&runtime->oss.params_lock);
  if (cmd) {
   err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
   if (err < 0)
    return err;
  }
 }
 if (csubstream) {
        runtime = csubstream->runtime;
  cmd = 0;
  if (mutex_lock_interruptible(&runtime->oss.params_lock))
   return -ERESTARTSYS;
  if (trigger & PCM_ENABLE_INPUT) {
   if (runtime->oss.trigger)
    goto _skip2;
   runtime->oss.trigger = 1;
   runtime->start_threshold = 1;
   cmd = SNDRV_PCM_IOCTL_START;
  } else {
   if (!runtime->oss.trigger)
    goto _skip2;
   runtime->oss.trigger = 0;
   runtime->start_threshold = runtime->boundary;
   cmd = SNDRV_PCM_IOCTL_DROP;
   runtime->oss.prepare = 1;
  }
 _skip2:
  mutex_unlock(&runtime->oss.params_lock);
  if (cmd) {
   err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
   if (err < 0)
    return err;
  }
 }
 return 0;
}

static int snd_pcm_oss_get_trigger(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;
 int result = 0;

 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
 if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger)
  result |= PCM_ENABLE_OUTPUT;
 if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger)
  result |= PCM_ENABLE_INPUT;
 return result;
}

static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)
{
 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 snd_pcm_sframes_t delay;
 int err;

 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
 if (substream == NULL)
  return -EINVAL;
 err = snd_pcm_oss_make_ready(substream);
 if (err < 0)
  return err;
 runtime = substream->runtime;
 if (runtime->oss.params || runtime->oss.prepare)
  return 0;
 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
 if (err == -EPIPE)
  delay = 0; /* hack for broken OSS applications */
 else if (err < 0)
  return err;
 return snd_pcm_oss_bytes(substream, delay);
}

static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct count_info __user * _info)

 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 snd_pcm_sframes_t delay;
 int fixup;
 struct count_info info;
 int err;

 if (_info == NULL)
  return -EFAULT;
 substream = pcm_oss_file->streams[stream];
 if (substream == NULL)
  return -EINVAL;
 err = snd_pcm_oss_make_ready(substream);
 if (err < 0)
  return err;
 runtime = substream->runtime;
 if (runtime->oss.params || runtime->oss.prepare) {
  memset(&info, 0, sizeof(info));
  if (copy_to_user(_info, &info, sizeof(info)))
   return -EFAULT;
  return 0;
 }
 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
  err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
  if (err == -EPIPE || err == -ESTRPIPE || (! err && delay < 0)) {
   err = 0;
   delay = 0;
   fixup = 0;
  } else {
   fixup = runtime->oss.buffer_used;
  }
 } else {
  err = snd_pcm_oss_capture_position_fixup(substream, &delay);
  fixup = -runtime->oss.buffer_used;
 }
 if (err < 0)
  return err;
 info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
 if (atomic_read(&substream->mmap_count)) {
  snd_pcm_sframes_t n;
  delay = get_hw_ptr_period(runtime);
  n = delay - runtime->oss.prev_hw_ptr_period;
  if (n < 0)
   n += runtime->boundary;
  info.blocks = n / runtime->period_size;
  runtime->oss.prev_hw_ptr_period = delay;
  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
   snd_pcm_oss_simulate_fill(substream, delay);
  info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
 } else {
  delay = snd_pcm_oss_bytes(substream, delay);
  if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
   if (substream->oss.setup.buggyptr)
    info.blocks = (runtime->oss.buffer_bytes - delay - fixup) / runtime->oss.period_bytes;
   else
    info.blocks = (delay + fixup) / runtime->oss.period_bytes;
   info.bytes = (runtime->oss.bytes - delay) & INT_MAX;
  } else {
   delay += fixup;
   info.blocks = delay / runtime->oss.period_bytes;
   info.bytes = (runtime->oss.bytes + delay) & INT_MAX;
  }
 }
 if (copy_to_user(_info, &info, sizeof(info)))
  return -EFAULT;
 return 0;
}

static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct audio_buf_info __user *_info)
{
 struct snd_pcm_substream *substream;
 struct snd_pcm_runtime *runtime;
 snd_pcm_sframes_t avail;
 int fixup;
 struct audio_buf_info info;
 int err;

 if (_info == NULL)
  return -EFAULT;
 substream = pcm_oss_file->streams[stream];
 if (substream == NULL)
  return -EINVAL;
 runtime = substream->runtime;

 if (runtime->oss.params) {
  err = snd_pcm_oss_change_params(substream, false);
  if (err < 0)
   return err;
 }

 info.fragsize = runtime->oss.period_bytes;
 info.fragstotal = runtime->periods;
 if (runtime->oss.prepare) {
  if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
   info.bytes = runtime->oss.period_bytes * runtime->oss.periods;
   info.fragments = runtime->oss.periods;
  } else {
   info.bytes = 0;
   info.fragments = 0;
  }
 } else {
  if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
   err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &avail);
   if (err == -EPIPE || err == -ESTRPIPE || (! err && avail < 0)) {
    avail = runtime->buffer_size;
    err = 0;
    fixup = 0;
   } else {
    avail = runtime->buffer_size - avail;
    fixup = -runtime->oss.buffer_used;
   }
  } else {
   err = snd_pcm_oss_capture_position_fixup(substream, &avail);
   fixup = runtime->oss.buffer_used;
  }
  if (err < 0)
   return err;
  info.bytes = snd_pcm_oss_bytes(substream, avail) + fixup;
  info.fragments = info.bytes / runtime->oss.period_bytes;
 }

#ifdef OSS_DEBUG
 pcm_dbg(substream->pcm,
  "pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n",
  info.bytes, info.fragments, info.fragstotal, info.fragsize);
#endif
 if (copy_to_user(_info, &info, sizeof(info)))
  return -EFAULT;
 return 0;
}

static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct buffmem_desc __user * _info)
{
 // it won't be probably implemented
 // pr_debug("TODO: snd_pcm_oss_get_mapbuf\n");
 return -EINVAL;
}

static const char *strip_task_path(const char *path)
{
 const char *ptr, *ptrl = NULL;
 for (ptr = path; *ptr; ptr++) {
  if (*ptr == '/')
   ptrl = ptr + 1;
 }
 return ptrl;
}

static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
          const char *task_name,
          struct snd_pcm_oss_setup *rsetup)
{
 struct snd_pcm_oss_setup *setup;

 guard(mutex)(&pcm->streams[stream].oss.setup_mutex);
 do {
  for (setup = pcm->streams[stream].oss.setup_list; setup;
       setup = setup->next) {
   if (!strcmp(setup->task_name, task_name))
    goto out;
  }
 } while ((task_name = strip_task_path(task_name)) != NULL);
 out:
 if (setup)
  *rsetup = *setup;
}

static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
{
 snd_pcm_oss_release_buffers(substream);
 substream->oss.oss = 0;
}

static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
           struct snd_pcm_oss_setup *setup,
           int minor)
{
 struct snd_pcm_runtime *runtime;

 substream->oss.oss = 1;
 substream->oss.setup = *setup;
 if (setup->nonblock)
  substream->f_flags |= O_NONBLOCK;
 else if (setup->block)
  substream->f_flags &= ~O_NONBLOCK;
 runtime = substream->runtime;
 runtime->oss.params = 1;
 runtime->oss.trigger = 1;
 runtime->oss.rate = 8000;
 mutex_init(&runtime->oss.params_lock);
 switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
 case SNDRV_MINOR_OSS_PCM_8:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.