3224 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Digital Audio (PCM) abstract layer / OSS compatible4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>5 */6 7#if 08#define PLUGIN_DEBUG9#endif10#if 011#define OSS_DEBUG12#endif13 14#include <linux/init.h>15#include <linux/slab.h>16#include <linux/sched/signal.h>17#include <linux/time.h>18#include <linux/vmalloc.h>19#include <linux/module.h>20#include <linux/math64.h>21#include <linux/string.h>22#include <linux/compat.h>23#include <sound/core.h>24#include <sound/minors.h>25#include <sound/pcm.h>26#include <sound/pcm_params.h>27#include "pcm_plugin.h"28#include <sound/info.h>29#include <linux/soundcard.h>30#include <sound/initval.h>31#include <sound/mixer_oss.h>32 33#define OSS_ALSAEMULVER _SIOR ('M', 249, int)34 35static int dsp_map[SNDRV_CARDS];36static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};37static bool nonblock_open = 1;38 39MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");40MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");41MODULE_LICENSE("GPL");42module_param_array(dsp_map, int, NULL, 0444);43MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device.");44module_param_array(adsp_map, int, NULL, 0444);45MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device.");46module_param(nonblock_open, bool, 0644);47MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");48MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);49MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);50 51static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file);52static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file);53static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file);54 55/*56 * helper functions to process hw_params57 */58static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin)59{60 int changed = 0;61 if (i->min < min) {62 i->min = min;63 i->openmin = openmin;64 changed = 1;65 } else if (i->min == min && !i->openmin && openmin) {66 i->openmin = 1;67 changed = 1;68 }69 if (i->integer) {70 if (i->openmin) {71 i->min++;72 i->openmin = 0;73 }74 }75 if (snd_interval_checkempty(i)) {76 snd_interval_none(i);77 return -EINVAL;78 }79 return changed;80}81 82static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax)83{84 int changed = 0;85 if (i->max > max) {86 i->max = max;87 i->openmax = openmax;88 changed = 1;89 } else if (i->max == max && !i->openmax && openmax) {90 i->openmax = 1;91 changed = 1;92 }93 if (i->integer) {94 if (i->openmax) {95 i->max--;96 i->openmax = 0;97 }98 }99 if (snd_interval_checkempty(i)) {100 snd_interval_none(i);101 return -EINVAL;102 }103 return changed;104}105 106static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)107{108 struct snd_interval t;109 t.empty = 0;110 t.min = t.max = val;111 t.openmin = t.openmax = 0;112 t.integer = 1;113 return snd_interval_refine(i, &t);114}115 116/**117 * snd_pcm_hw_param_value_min118 * @params: the hw_params instance119 * @var: parameter to retrieve120 * @dir: pointer to the direction (-1,0,1) or NULL121 *122 * Return the minimum value for field PAR.123 */124static unsigned int125snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,126 snd_pcm_hw_param_t var, int *dir)127{128 if (hw_is_mask(var)) {129 if (dir)130 *dir = 0;131 return snd_mask_min(hw_param_mask_c(params, var));132 }133 if (hw_is_interval(var)) {134 const struct snd_interval *i = hw_param_interval_c(params, var);135 if (dir)136 *dir = i->openmin;137 return snd_interval_min(i);138 }139 return -EINVAL;140}141 142/**143 * snd_pcm_hw_param_value_max144 * @params: the hw_params instance145 * @var: parameter to retrieve146 * @dir: pointer to the direction (-1,0,1) or NULL147 *148 * Return the maximum value for field PAR.149 */150static int151snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,152 snd_pcm_hw_param_t var, int *dir)153{154 if (hw_is_mask(var)) {155 if (dir)156 *dir = 0;157 return snd_mask_max(hw_param_mask_c(params, var));158 }159 if (hw_is_interval(var)) {160 const struct snd_interval *i = hw_param_interval_c(params, var);161 if (dir)162 *dir = - (int) i->openmax;163 return snd_interval_max(i);164 }165 return -EINVAL;166}167 168static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,169 snd_pcm_hw_param_t var,170 const struct snd_mask *val)171{172 int changed;173 changed = snd_mask_refine(hw_param_mask(params, var), val);174 if (changed > 0) {175 params->cmask |= 1 << var;176 params->rmask |= 1 << var;177 }178 return changed;179}180 181static int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm,182 struct snd_pcm_hw_params *params,183 snd_pcm_hw_param_t var,184 const struct snd_mask *val)185{186 int changed = _snd_pcm_hw_param_mask(params, var, val);187 if (changed < 0)188 return changed;189 if (params->rmask) {190 int err = snd_pcm_hw_refine(pcm, params);191 if (err < 0)192 return err;193 }194 return 0;195}196 197static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,198 snd_pcm_hw_param_t var, unsigned int val,199 int dir)200{201 int changed;202 int open = 0;203 if (dir) {204 if (dir > 0) {205 open = 1;206 } else if (dir < 0) {207 if (val > 0) {208 open = 1;209 val--;210 }211 }212 }213 if (hw_is_mask(var))214 changed = snd_mask_refine_min(hw_param_mask(params, var),215 val + !!open);216 else if (hw_is_interval(var))217 changed = snd_interval_refine_min(hw_param_interval(params, var),218 val, open);219 else220 return -EINVAL;221 if (changed > 0) {222 params->cmask |= 1 << var;223 params->rmask |= 1 << var;224 }225 return changed;226}227 228/**229 * snd_pcm_hw_param_min230 * @pcm: PCM instance231 * @params: the hw_params instance232 * @var: parameter to retrieve233 * @val: minimal value234 * @dir: pointer to the direction (-1,0,1) or NULL235 *236 * Inside configuration space defined by PARAMS remove from PAR all 237 * values < VAL. Reduce configuration space accordingly.238 * Return new minimum or -EINVAL if the configuration space is empty239 */240static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm,241 struct snd_pcm_hw_params *params,242 snd_pcm_hw_param_t var, unsigned int val,243 int *dir)244{245 int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);246 if (changed < 0)247 return changed;248 if (params->rmask) {249 int err = snd_pcm_hw_refine(pcm, params);250 if (err < 0)251 return err;252 }253 return snd_pcm_hw_param_value_min(params, var, dir);254}255 256static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,257 snd_pcm_hw_param_t var, unsigned int val,258 int dir)259{260 int changed;261 int open = 0;262 if (dir) {263 if (dir < 0) {264 open = 1;265 } else if (dir > 0) {266 open = 1;267 val++;268 }269 }270 if (hw_is_mask(var)) {271 if (val == 0 && open) {272 snd_mask_none(hw_param_mask(params, var));273 changed = -EINVAL;274 } else275 changed = snd_mask_refine_max(hw_param_mask(params, var),276 val - !!open);277 } else if (hw_is_interval(var))278 changed = snd_interval_refine_max(hw_param_interval(params, var),279 val, open);280 else281 return -EINVAL;282 if (changed > 0) {283 params->cmask |= 1 << var;284 params->rmask |= 1 << var;285 }286 return changed;287}288 289/**290 * snd_pcm_hw_param_max291 * @pcm: PCM instance292 * @params: the hw_params instance293 * @var: parameter to retrieve294 * @val: maximal value295 * @dir: pointer to the direction (-1,0,1) or NULL296 *297 * Inside configuration space defined by PARAMS remove from PAR all 298 * values >= VAL + 1. Reduce configuration space accordingly.299 * Return new maximum or -EINVAL if the configuration space is empty300 */301static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm,302 struct snd_pcm_hw_params *params,303 snd_pcm_hw_param_t var, unsigned int val,304 int *dir)305{306 int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);307 if (changed < 0)308 return changed;309 if (params->rmask) {310 int err = snd_pcm_hw_refine(pcm, params);311 if (err < 0)312 return err;313 }314 return snd_pcm_hw_param_value_max(params, var, dir);315}316 317static int boundary_sub(int a, int adir,318 int b, int bdir,319 int *c, int *cdir)320{321 adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0);322 bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0);323 *c = a - b;324 *cdir = adir - bdir;325 if (*cdir == -2) {326 (*c)--;327 } else if (*cdir == 2) {328 (*c)++;329 }330 return 0;331}332 333static int boundary_lt(unsigned int a, int adir,334 unsigned int b, int bdir)335{336 if (adir < 0) {337 a--;338 adir = 1;339 } else if (adir > 0)340 adir = 1;341 if (bdir < 0) {342 b--;343 bdir = 1;344 } else if (bdir > 0)345 bdir = 1;346 return a < b || (a == b && adir < bdir);347}348 349/* Return 1 if min is nearer to best than max */350static int boundary_nearer(int min, int mindir,351 int best, int bestdir,352 int max, int maxdir)353{354 int dmin, dmindir;355 int dmax, dmaxdir;356 boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir);357 boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir);358 return boundary_lt(dmin, dmindir, dmax, dmaxdir);359}360 361/**362 * snd_pcm_hw_param_near363 * @pcm: PCM instance364 * @params: the hw_params instance365 * @var: parameter to retrieve366 * @best: value to set367 * @dir: pointer to the direction (-1,0,1) or NULL368 *369 * Inside configuration space defined by PARAMS set PAR to the available value370 * nearest to VAL. Reduce configuration space accordingly.371 * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS,372 * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT.373 * Return the value found.374 */375static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,376 struct snd_pcm_hw_params *params,377 snd_pcm_hw_param_t var, unsigned int best,378 int *dir)379{380 struct snd_pcm_hw_params *save __free(kfree) = NULL;381 int v;382 unsigned int saved_min;383 int last = 0;384 int min, max;385 int mindir, maxdir;386 int valdir = dir ? *dir : 0;387 /* FIXME */388 if (best > INT_MAX)389 best = INT_MAX;390 min = max = best;391 mindir = maxdir = valdir;392 if (maxdir > 0)393 maxdir = 0;394 else if (maxdir == 0)395 maxdir = -1;396 else {397 maxdir = 1;398 max--;399 }400 save = kmalloc(sizeof(*save), GFP_KERNEL);401 if (save == NULL)402 return -ENOMEM;403 *save = *params;404 saved_min = min;405 min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);406 if (min >= 0) {407 struct snd_pcm_hw_params *params1 __free(kfree) = NULL;408 if (max < 0)409 goto _end;410 if ((unsigned int)min == saved_min && mindir == valdir)411 goto _end;412 params1 = kmalloc(sizeof(*params1), GFP_KERNEL);413 if (params1 == NULL)414 return -ENOMEM;415 *params1 = *save;416 max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);417 if (max < 0)418 goto _end;419 if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {420 *params = *params1;421 last = 1;422 }423 } else {424 *params = *save;425 max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);426 if (max < 0)427 return max;428 last = 1;429 }430 _end:431 if (last)432 v = snd_pcm_hw_param_last(pcm, params, var, dir);433 else434 v = snd_pcm_hw_param_first(pcm, params, var, dir);435 return v;436}437 438static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,439 snd_pcm_hw_param_t var, unsigned int val,440 int dir)441{442 int changed;443 if (hw_is_mask(var)) {444 struct snd_mask *m = hw_param_mask(params, var);445 if (val == 0 && dir < 0) {446 changed = -EINVAL;447 snd_mask_none(m);448 } else {449 if (dir > 0)450 val++;451 else if (dir < 0)452 val--;453 changed = snd_mask_refine_set(hw_param_mask(params, var), val);454 }455 } else if (hw_is_interval(var)) {456 struct snd_interval *i = hw_param_interval(params, var);457 if (val == 0 && dir < 0) {458 changed = -EINVAL;459 snd_interval_none(i);460 } else if (dir == 0)461 changed = snd_interval_refine_set(i, val);462 else {463 struct snd_interval t;464 t.openmin = 1;465 t.openmax = 1;466 t.empty = 0;467 t.integer = 0;468 if (dir < 0) {469 t.min = val - 1;470 t.max = val;471 } else {472 t.min = val;473 t.max = val+1;474 }475 changed = snd_interval_refine(i, &t);476 }477 } else478 return -EINVAL;479 if (changed > 0) {480 params->cmask |= 1 << var;481 params->rmask |= 1 << var;482 }483 return changed;484}485 486/**487 * snd_pcm_hw_param_set488 * @pcm: PCM instance489 * @params: the hw_params instance490 * @var: parameter to retrieve491 * @val: value to set492 * @dir: pointer to the direction (-1,0,1) or NULL493 *494 * Inside configuration space defined by PARAMS remove from PAR all 495 * values != VAL. Reduce configuration space accordingly.496 * Return VAL or -EINVAL if the configuration space is empty497 */498static int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm,499 struct snd_pcm_hw_params *params,500 snd_pcm_hw_param_t var, unsigned int val,501 int dir)502{503 int changed = _snd_pcm_hw_param_set(params, var, val, dir);504 if (changed < 0)505 return changed;506 if (params->rmask) {507 int err = snd_pcm_hw_refine(pcm, params);508 if (err < 0)509 return err;510 }511 return snd_pcm_hw_param_value(params, var, NULL);512}513 514static int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,515 snd_pcm_hw_param_t var)516{517 int changed;518 changed = snd_interval_setinteger(hw_param_interval(params, var));519 if (changed > 0) {520 params->cmask |= 1 << var;521 params->rmask |= 1 << var;522 }523 return changed;524}525 526/*527 * plugin528 */529 530#ifdef CONFIG_SND_PCM_OSS_PLUGINS531static int snd_pcm_oss_plugin_clear(struct snd_pcm_substream *substream)532{533 struct snd_pcm_runtime *runtime = substream->runtime;534 struct snd_pcm_plugin *plugin, *next;535 536 plugin = runtime->oss.plugin_first;537 while (plugin) {538 next = plugin->next;539 snd_pcm_plugin_free(plugin);540 plugin = next;541 }542 runtime->oss.plugin_first = runtime->oss.plugin_last = NULL;543 return 0;544}545 546static int snd_pcm_plugin_insert(struct snd_pcm_plugin *plugin)547{548 struct snd_pcm_runtime *runtime = plugin->plug->runtime;549 plugin->next = runtime->oss.plugin_first;550 plugin->prev = NULL;551 if (runtime->oss.plugin_first) {552 runtime->oss.plugin_first->prev = plugin;553 runtime->oss.plugin_first = plugin;554 } else {555 runtime->oss.plugin_last =556 runtime->oss.plugin_first = plugin;557 }558 return 0;559}560 561int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin)562{563 struct snd_pcm_runtime *runtime = plugin->plug->runtime;564 plugin->next = NULL;565 plugin->prev = runtime->oss.plugin_last;566 if (runtime->oss.plugin_last) {567 runtime->oss.plugin_last->next = plugin;568 runtime->oss.plugin_last = plugin;569 } else {570 runtime->oss.plugin_last =571 runtime->oss.plugin_first = plugin;572 }573 return 0;574}575#endif /* CONFIG_SND_PCM_OSS_PLUGINS */576 577static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames)578{579 struct snd_pcm_runtime *runtime = substream->runtime;580 long buffer_size = snd_pcm_lib_buffer_bytes(substream);581 long bytes = frames_to_bytes(runtime, frames);582 if (buffer_size == runtime->oss.buffer_bytes)583 return bytes;584#if BITS_PER_LONG >= 64585 return runtime->oss.buffer_bytes * bytes / buffer_size;586#else587 {588 u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;589 return div_u64(bsize, buffer_size);590 }591#endif592}593 594static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes)595{596 struct snd_pcm_runtime *runtime = substream->runtime;597 long buffer_size = snd_pcm_lib_buffer_bytes(substream);598 if (buffer_size == runtime->oss.buffer_bytes)599 return bytes_to_frames(runtime, bytes);600 return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);601}602 603static inline604snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)605{606 return runtime->hw_ptr_interrupt;607}608 609/* define extended formats in the recent OSS versions (if any) */610/* linear formats */611#define AFMT_S32_LE 0x00001000612#define AFMT_S32_BE 0x00002000613#define AFMT_S24_LE 0x00008000614#define AFMT_S24_BE 0x00010000615#define AFMT_S24_PACKED 0x00040000616 617/* other supported formats */618#define AFMT_FLOAT 0x00004000619#define AFMT_SPDIF_RAW 0x00020000620 621/* unsupported formats */622#define AFMT_AC3 0x00000400623#define AFMT_VORBIS 0x00000800624 625static snd_pcm_format_t snd_pcm_oss_format_from(int format)626{627 switch (format) {628 case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;629 case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW;630 case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM;631 case AFMT_U8: return SNDRV_PCM_FORMAT_U8;632 case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE;633 case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE;634 case AFMT_S8: return SNDRV_PCM_FORMAT_S8;635 case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE;636 case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE;637 case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG;638 case AFMT_S32_LE: return SNDRV_PCM_FORMAT_S32_LE;639 case AFMT_S32_BE: return SNDRV_PCM_FORMAT_S32_BE;640 case AFMT_S24_LE: return SNDRV_PCM_FORMAT_S24_LE;641 case AFMT_S24_BE: return SNDRV_PCM_FORMAT_S24_BE;642 case AFMT_S24_PACKED: return SNDRV_PCM_FORMAT_S24_3LE;643 case AFMT_FLOAT: return SNDRV_PCM_FORMAT_FLOAT;644 case AFMT_SPDIF_RAW: return SNDRV_PCM_FORMAT_IEC958_SUBFRAME;645 default: return SNDRV_PCM_FORMAT_U8;646 }647}648 649static int snd_pcm_oss_format_to(snd_pcm_format_t format)650{651 switch (format) {652 case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;653 case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW;654 case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM;655 case SNDRV_PCM_FORMAT_U8: return AFMT_U8;656 case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE;657 case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE;658 case SNDRV_PCM_FORMAT_S8: return AFMT_S8;659 case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE;660 case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE;661 case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG;662 case SNDRV_PCM_FORMAT_S32_LE: return AFMT_S32_LE;663 case SNDRV_PCM_FORMAT_S32_BE: return AFMT_S32_BE;664 case SNDRV_PCM_FORMAT_S24_LE: return AFMT_S24_LE;665 case SNDRV_PCM_FORMAT_S24_BE: return AFMT_S24_BE;666 case SNDRV_PCM_FORMAT_S24_3LE: return AFMT_S24_PACKED;667 case SNDRV_PCM_FORMAT_FLOAT: return AFMT_FLOAT;668 case SNDRV_PCM_FORMAT_IEC958_SUBFRAME: return AFMT_SPDIF_RAW;669 default: return -EINVAL;670 }671}672 673static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, 674 struct snd_pcm_hw_params *oss_params,675 struct snd_pcm_hw_params *slave_params)676{677 ssize_t s;678 ssize_t oss_buffer_size;679 ssize_t oss_period_size, oss_periods;680 ssize_t min_period_size, max_period_size;681 struct snd_pcm_runtime *runtime = substream->runtime;682 size_t oss_frame_size;683 684 oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *685 params_channels(oss_params) / 8;686 687 oss_buffer_size = snd_pcm_hw_param_value_max(slave_params,688 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,689 NULL);690 if (oss_buffer_size <= 0)691 return -EINVAL;692 oss_buffer_size = snd_pcm_plug_client_size(substream,693 oss_buffer_size * oss_frame_size);694 if (oss_buffer_size <= 0)695 return -EINVAL;696 oss_buffer_size = rounddown_pow_of_two(oss_buffer_size);697 if (atomic_read(&substream->mmap_count)) {698 if (oss_buffer_size > runtime->oss.mmap_bytes)699 oss_buffer_size = runtime->oss.mmap_bytes;700 }701 702 if (substream->oss.setup.period_size > 16)703 oss_period_size = substream->oss.setup.period_size;704 else if (runtime->oss.fragshift) {705 oss_period_size = 1 << runtime->oss.fragshift;706 if (oss_period_size > oss_buffer_size / 2)707 oss_period_size = oss_buffer_size / 2;708 } else {709 int sd;710 size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8;711 712 oss_period_size = oss_buffer_size;713 do {714 oss_period_size /= 2;715 } while (oss_period_size > bytes_per_sec);716 if (runtime->oss.subdivision == 0) {717 sd = 4;718 if (oss_period_size / sd > 4096)719 sd *= 2;720 if (oss_period_size / sd < 4096)721 sd = 1;722 } else723 sd = runtime->oss.subdivision;724 oss_period_size /= sd;725 if (oss_period_size < 16)726 oss_period_size = 16;727 }728 729 min_period_size = snd_pcm_plug_client_size(substream,730 snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));731 if (min_period_size > 0) {732 min_period_size *= oss_frame_size;733 min_period_size = roundup_pow_of_two(min_period_size);734 if (oss_period_size < min_period_size)735 oss_period_size = min_period_size;736 }737 738 max_period_size = snd_pcm_plug_client_size(substream,739 snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));740 if (max_period_size > 0) {741 max_period_size *= oss_frame_size;742 max_period_size = rounddown_pow_of_two(max_period_size);743 if (oss_period_size > max_period_size)744 oss_period_size = max_period_size;745 }746 747 oss_periods = oss_buffer_size / oss_period_size;748 749 if (substream->oss.setup.periods > 1)750 oss_periods = substream->oss.setup.periods;751 752 s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);753 if (s > 0 && runtime->oss.maxfrags && s > runtime->oss.maxfrags)754 s = runtime->oss.maxfrags;755 if (oss_periods > s)756 oss_periods = s;757 758 s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);759 if (s < 2)760 s = 2;761 if (oss_periods < s)762 oss_periods = s;763 764 while (oss_period_size * oss_periods > oss_buffer_size)765 oss_period_size /= 2;766 767 if (oss_period_size < 16)768 return -EINVAL;769 770 /* don't allocate too large period; 1MB period must be enough */771 if (oss_period_size > 1024 * 1024)772 return -ENOMEM;773 774 runtime->oss.period_bytes = oss_period_size;775 runtime->oss.period_frames = 1;776 runtime->oss.periods = oss_periods;777 return 0;778}779 780static int choose_rate(struct snd_pcm_substream *substream,781 struct snd_pcm_hw_params *params, unsigned int best_rate)782{783 const struct snd_interval *it;784 struct snd_pcm_hw_params *save __free(kfree) = NULL;785 unsigned int rate, prev;786 787 save = kmalloc(sizeof(*save), GFP_KERNEL);788 if (save == NULL)789 return -ENOMEM;790 *save = *params;791 it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE);792 793 /* try multiples of the best rate */794 rate = best_rate;795 for (;;) {796 if (it->max < rate || (it->max == rate && it->openmax))797 break;798 if (it->min < rate || (it->min == rate && !it->openmin)) {799 int ret;800 ret = snd_pcm_hw_param_set(substream, params,801 SNDRV_PCM_HW_PARAM_RATE,802 rate, 0);803 if (ret == (int)rate)804 return rate;805 *params = *save;806 }807 prev = rate;808 rate += best_rate;809 if (rate <= prev)810 break;811 }812 813 /* not found, use the nearest rate */814 return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);815}816 817/* parameter locking: returns immediately if tried during streaming */818static int lock_params(struct snd_pcm_runtime *runtime)819{820 if (mutex_lock_interruptible(&runtime->oss.params_lock))821 return -ERESTARTSYS;822 if (atomic_read(&runtime->oss.rw_ref)) {823 mutex_unlock(&runtime->oss.params_lock);824 return -EBUSY;825 }826 return 0;827}828 829static void unlock_params(struct snd_pcm_runtime *runtime)830{831 mutex_unlock(&runtime->oss.params_lock);832}833 834static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream)835{836 struct snd_pcm_runtime *runtime = substream->runtime;837 838 kvfree(runtime->oss.buffer);839 runtime->oss.buffer = NULL;840#ifdef CONFIG_SND_PCM_OSS_PLUGINS841 snd_pcm_oss_plugin_clear(substream);842#endif843}844 845/* call with params_lock held */846static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)847{848 struct snd_pcm_runtime *runtime = substream->runtime;849 struct snd_pcm_hw_params *params, *sparams;850 struct snd_pcm_sw_params *sw_params;851 ssize_t oss_buffer_size, oss_period_size;852 size_t oss_frame_size;853 int err;854 int direct;855 snd_pcm_format_t format, sformat;856 int n;857 const struct snd_mask *sformat_mask;858 struct snd_mask mask;859 860 if (!runtime->oss.params)861 return 0;862 sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);863 params = kmalloc(sizeof(*params), GFP_KERNEL);864 sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);865 if (!sw_params || !params || !sparams) {866 err = -ENOMEM;867 goto failure;868 }869 870 if (atomic_read(&substream->mmap_count))871 direct = 1;872 else873 direct = substream->oss.setup.direct;874 875 _snd_pcm_hw_params_any(sparams);876 _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);877 _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);878 snd_mask_none(&mask);879 if (atomic_read(&substream->mmap_count))880 snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);881 else {882 snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED);883 if (!direct)884 snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);885 }886 err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);887 if (err < 0) {888 pcm_dbg(substream->pcm, "No usable accesses\n");889 err = -EINVAL;890 goto failure;891 }892 893 err = choose_rate(substream, sparams, runtime->oss.rate);894 if (err < 0)895 goto failure;896 err = snd_pcm_hw_param_near(substream, sparams,897 SNDRV_PCM_HW_PARAM_CHANNELS,898 runtime->oss.channels, NULL);899 if (err < 0)900 goto failure;901 902 format = snd_pcm_oss_format_from(runtime->oss.format);903 904 sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT);905 if (direct)906 sformat = format;907 else908 sformat = snd_pcm_plug_slave_format(format, sformat_mask);909 910 if ((__force int)sformat < 0 ||911 !snd_mask_test_format(sformat_mask, sformat)) {912 pcm_for_each_format(sformat) {913 if (snd_mask_test_format(sformat_mask, sformat) &&914 snd_pcm_oss_format_to(sformat) >= 0)915 goto format_found;916 }917 pcm_dbg(substream->pcm, "Cannot find a format!!!\n");918 err = -EINVAL;919 goto failure;920 }921 format_found:922 err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);923 if (err < 0)924 goto failure;925 926 if (direct) {927 memcpy(params, sparams, sizeof(*params));928 } else {929 _snd_pcm_hw_params_any(params);930 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,931 (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);932 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,933 (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0);934 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,935 runtime->oss.channels, 0);936 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,937 runtime->oss.rate, 0);938 pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",939 params_access(params), params_format(params),940 params_channels(params), params_rate(params));941 }942 pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",943 params_access(sparams), params_format(sparams),944 params_channels(sparams), params_rate(sparams));945 946 oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *947 params_channels(params) / 8;948 949 err = snd_pcm_oss_period_size(substream, params, sparams);950 if (err < 0)951 goto failure;952 953 n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);954 err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);955 if (err < 0)956 goto failure;957 958 err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,959 runtime->oss.periods, NULL);960 if (err < 0)961 goto failure;962 963 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);964 965 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams);966 if (err < 0) {967 pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err);968 goto failure;969 }970 971#ifdef CONFIG_SND_PCM_OSS_PLUGINS972 snd_pcm_oss_plugin_clear(substream);973 if (!direct) {974 /* add necessary plugins */975 err = snd_pcm_plug_format_plugins(substream, params, sparams);976 if (err < 0) {977 pcm_dbg(substream->pcm,978 "snd_pcm_plug_format_plugins failed: %i\n", err);979 goto failure;980 }981 if (runtime->oss.plugin_first) {982 struct snd_pcm_plugin *plugin;983 err = snd_pcm_plugin_build_io(substream, sparams, &plugin);984 if (err < 0) {985 pcm_dbg(substream->pcm,986 "snd_pcm_plugin_build_io failed: %i\n", err);987 goto failure;988 }989 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {990 err = snd_pcm_plugin_append(plugin);991 } else {992 err = snd_pcm_plugin_insert(plugin);993 }994 if (err < 0)995 goto failure;996 }997 }998#endif999 1000 if (runtime->oss.trigger) {1001 sw_params->start_threshold = 1;1002 } else {1003 sw_params->start_threshold = runtime->boundary;1004 }1005 if (atomic_read(&substream->mmap_count) ||1006 substream->stream == SNDRV_PCM_STREAM_CAPTURE)1007 sw_params->stop_threshold = runtime->boundary;1008 else1009 sw_params->stop_threshold = runtime->buffer_size;1010 sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;1011 sw_params->period_step = 1;1012 sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?1013 1 : runtime->period_size;1014 if (atomic_read(&substream->mmap_count) ||1015 substream->oss.setup.nosilence) {1016 sw_params->silence_threshold = 0;1017 sw_params->silence_size = 0;1018 } else {1019 snd_pcm_uframes_t frames;1020 frames = runtime->period_size + 16;1021 if (frames > runtime->buffer_size)1022 frames = runtime->buffer_size;1023 sw_params->silence_threshold = frames;1024 sw_params->silence_size = frames;1025 }1026 1027 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params);1028 if (err < 0) {1029 pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err);1030 goto failure;1031 }1032 1033 runtime->oss.periods = params_periods(sparams);1034 oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));1035 if (oss_period_size < 0) {1036 err = -EINVAL;1037 goto failure;1038 }1039#ifdef CONFIG_SND_PCM_OSS_PLUGINS1040 if (runtime->oss.plugin_first) {1041 err = snd_pcm_plug_alloc(substream, oss_period_size);1042 if (err < 0)1043 goto failure;1044 }1045#endif1046 oss_period_size = array_size(oss_period_size, oss_frame_size);1047 oss_buffer_size = array_size(oss_period_size, runtime->oss.periods);1048 if (oss_buffer_size <= 0) {1049 err = -EINVAL;1050 goto failure;1051 }1052 1053 runtime->oss.period_bytes = oss_period_size;1054 runtime->oss.buffer_bytes = oss_buffer_size;1055 1056 pdprintf("oss: period bytes = %i, buffer bytes = %i\n",1057 runtime->oss.period_bytes,1058 runtime->oss.buffer_bytes);1059 pdprintf("slave: period_size = %i, buffer_size = %i\n",1060 params_period_size(sparams),1061 params_buffer_size(sparams));1062 1063 runtime->oss.format = snd_pcm_oss_format_to(params_format(params));1064 runtime->oss.channels = params_channels(params);1065 runtime->oss.rate = params_rate(params);1066 1067 kvfree(runtime->oss.buffer);1068 runtime->oss.buffer = kvzalloc(runtime->oss.period_bytes, GFP_KERNEL);1069 if (!runtime->oss.buffer) {1070 err = -ENOMEM;1071 goto failure;1072 }1073 1074 runtime->oss.params = 0;1075 runtime->oss.prepare = 1;1076 runtime->oss.buffer_used = 0;1077 if (runtime->dma_area)1078 snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));1079 1080 runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);1081 1082 err = 0;1083failure:1084 if (err)1085 snd_pcm_oss_release_buffers(substream);1086 kfree(sw_params);1087 kfree(params);1088 kfree(sparams);1089 return err;1090}1091 1092/* this one takes the lock by itself */1093static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,1094 bool trylock)1095{1096 struct snd_pcm_runtime *runtime = substream->runtime;1097 int err;1098 1099 if (trylock) {1100 if (!(mutex_trylock(&runtime->oss.params_lock)))1101 return -EAGAIN;1102 } else if (mutex_lock_interruptible(&runtime->oss.params_lock))1103 return -ERESTARTSYS;1104 1105 err = snd_pcm_oss_change_params_locked(substream);1106 mutex_unlock(&runtime->oss.params_lock);1107 return err;1108}1109 1110static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_file, struct snd_pcm_substream **r_substream)1111{1112 int idx, err;1113 struct snd_pcm_substream *asubstream = NULL, *substream;1114 1115 for (idx = 0; idx < 2; idx++) {1116 substream = pcm_oss_file->streams[idx];1117 if (substream == NULL)1118 continue;1119 if (asubstream == NULL)1120 asubstream = substream;1121 if (substream->runtime->oss.params) {1122 err = snd_pcm_oss_change_params(substream, false);1123 if (err < 0)1124 return err;1125 }1126 }1127 if (!asubstream)1128 return -EIO;1129 if (r_substream)1130 *r_substream = asubstream;1131 return 0;1132}1133 1134/* call with params_lock held */1135/* NOTE: this always call PREPARE unconditionally no matter whether1136 * runtime->oss.prepare is set or not1137 */1138static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)1139{1140 int err;1141 struct snd_pcm_runtime *runtime = substream->runtime;1142 1143 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);1144 if (err < 0) {1145 pcm_dbg(substream->pcm,1146 "snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");1147 return err;1148 }1149 runtime->oss.prepare = 0;1150 runtime->oss.prev_hw_ptr_period = 0;1151 runtime->oss.period_ptr = 0;1152 runtime->oss.buffer_used = 0;1153 1154 return 0;1155}1156 1157static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)1158{1159 struct snd_pcm_runtime *runtime;1160 int err;1161 1162 runtime = substream->runtime;1163 if (runtime->oss.params) {1164 err = snd_pcm_oss_change_params(substream, false);1165 if (err < 0)1166 return err;1167 }1168 if (runtime->oss.prepare) {1169 if (mutex_lock_interruptible(&runtime->oss.params_lock))1170 return -ERESTARTSYS;1171 err = snd_pcm_oss_prepare(substream);1172 mutex_unlock(&runtime->oss.params_lock);1173 if (err < 0)1174 return err;1175 }1176 return 0;1177}1178 1179/* call with params_lock held */1180static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)1181{1182 struct snd_pcm_runtime *runtime;1183 int err;1184 1185 runtime = substream->runtime;1186 if (runtime->oss.params) {1187 err = snd_pcm_oss_change_params_locked(substream);1188 if (err < 0)1189 return err;1190 }1191 if (runtime->oss.prepare) {1192 err = snd_pcm_oss_prepare(substream);1193 if (err < 0)1194 return err;1195 }1196 return 0;1197}1198 1199static int snd_pcm_oss_capture_position_fixup(struct snd_pcm_substream *substream, snd_pcm_sframes_t *delay)1200{1201 struct snd_pcm_runtime *runtime;1202 snd_pcm_uframes_t frames;1203 int err = 0;1204 1205 while (1) {1206 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, delay);1207 if (err < 0)1208 break;1209 runtime = substream->runtime;1210 if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size)1211 break;1212 /* in case of overrun, skip whole periods like OSS/Linux driver does */1213 /* until avail(delay) <= buffer_size */1214 frames = (*delay - runtime->buffer_size) + runtime->period_size - 1;1215 frames /= runtime->period_size;1216 frames *= runtime->period_size;1217 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_FORWARD, &frames);1218 if (err < 0)1219 break;1220 }1221 return err;1222}1223 1224snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const char *ptr, snd_pcm_uframes_t frames, int in_kernel)1225{1226 struct snd_pcm_runtime *runtime = substream->runtime;1227 int ret;1228 while (1) {1229 if (runtime->state == SNDRV_PCM_STATE_XRUN ||1230 runtime->state == SNDRV_PCM_STATE_SUSPENDED) {1231#ifdef OSS_DEBUG1232 pcm_dbg(substream->pcm,1233 "pcm_oss: write: recovering from %s\n",1234 runtime->state == SNDRV_PCM_STATE_XRUN ?1235 "XRUN" : "SUSPEND");1236#endif1237 ret = snd_pcm_oss_prepare(substream);1238 if (ret < 0)1239 break;1240 }1241 mutex_unlock(&runtime->oss.params_lock);1242 ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,1243 frames, in_kernel);1244 mutex_lock(&runtime->oss.params_lock);1245 if (ret != -EPIPE && ret != -ESTRPIPE)1246 break;1247 /* test, if we can't store new data, because the stream */1248 /* has not been started */1249 if (runtime->state == SNDRV_PCM_STATE_PREPARED)1250 return -EAGAIN;1251 }1252 return ret;1253}1254 1255snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *ptr, snd_pcm_uframes_t frames, int in_kernel)1256{1257 struct snd_pcm_runtime *runtime = substream->runtime;1258 snd_pcm_sframes_t delay;1259 int ret;1260 while (1) {1261 if (runtime->state == SNDRV_PCM_STATE_XRUN ||1262 runtime->state == SNDRV_PCM_STATE_SUSPENDED) {1263#ifdef OSS_DEBUG1264 pcm_dbg(substream->pcm,1265 "pcm_oss: read: recovering from %s\n",1266 runtime->state == SNDRV_PCM_STATE_XRUN ?1267 "XRUN" : "SUSPEND");1268#endif1269 ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);1270 if (ret < 0)1271 break;1272 } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {1273 ret = snd_pcm_oss_prepare(substream);1274 if (ret < 0)1275 break;1276 }1277 ret = snd_pcm_oss_capture_position_fixup(substream, &delay);1278 if (ret < 0)1279 break;1280 mutex_unlock(&runtime->oss.params_lock);1281 ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,1282 frames, in_kernel);1283 mutex_lock(&runtime->oss.params_lock);1284 if (ret == -EPIPE) {1285 if (runtime->state == SNDRV_PCM_STATE_DRAINING) {1286 ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);1287 if (ret < 0)1288 break;1289 }1290 continue;1291 }1292 if (ret != -ESTRPIPE)1293 break;1294 }1295 return ret;1296}1297 1298#ifdef CONFIG_SND_PCM_OSS_PLUGINS1299snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)1300{1301 struct snd_pcm_runtime *runtime = substream->runtime;1302 int ret;1303 while (1) {1304 if (runtime->state == SNDRV_PCM_STATE_XRUN ||1305 runtime->state == SNDRV_PCM_STATE_SUSPENDED) {1306#ifdef OSS_DEBUG1307 pcm_dbg(substream->pcm,1308 "pcm_oss: writev: recovering from %s\n",1309 runtime->state == SNDRV_PCM_STATE_XRUN ?1310 "XRUN" : "SUSPEND");1311#endif1312 ret = snd_pcm_oss_prepare(substream);1313 if (ret < 0)1314 break;1315 }1316 ret = snd_pcm_kernel_writev(substream, bufs, frames);1317 if (ret != -EPIPE && ret != -ESTRPIPE)1318 break;1319 1320 /* test, if we can't store new data, because the stream */1321 /* has not been started */1322 if (runtime->state == SNDRV_PCM_STATE_PREPARED)1323 return -EAGAIN;1324 }1325 return ret;1326}1327 1328snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)1329{1330 struct snd_pcm_runtime *runtime = substream->runtime;1331 int ret;1332 while (1) {1333 if (runtime->state == SNDRV_PCM_STATE_XRUN ||1334 runtime->state == SNDRV_PCM_STATE_SUSPENDED) {1335#ifdef OSS_DEBUG1336 pcm_dbg(substream->pcm,1337 "pcm_oss: readv: recovering from %s\n",1338 runtime->state == SNDRV_PCM_STATE_XRUN ?1339 "XRUN" : "SUSPEND");1340#endif1341 ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);1342 if (ret < 0)1343 break;1344 } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {1345 ret = snd_pcm_oss_prepare(substream);1346 if (ret < 0)1347 break;1348 }1349 ret = snd_pcm_kernel_readv(substream, bufs, frames);1350 if (ret != -EPIPE && ret != -ESTRPIPE)1351 break;1352 }1353 return ret;1354}1355#endif /* CONFIG_SND_PCM_OSS_PLUGINS */1356 1357static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const char *buf, size_t bytes, int in_kernel)1358{1359 struct snd_pcm_runtime *runtime = substream->runtime;1360 snd_pcm_sframes_t frames, frames1;1361#ifdef CONFIG_SND_PCM_OSS_PLUGINS1362 if (runtime->oss.plugin_first) {1363 struct snd_pcm_plugin_channel *channels;1364 size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;1365 if (!in_kernel) {1366 if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes))1367 return -EFAULT;1368 buf = runtime->oss.buffer;1369 }1370 frames = bytes / oss_frame_bytes;1371 frames1 = snd_pcm_plug_client_channels_buf(substream, (char *)buf, frames, &channels);1372 if (frames1 < 0)1373 return frames1;1374 frames1 = snd_pcm_plug_write_transfer(substream, channels, frames1);1375 if (frames1 <= 0)1376 return frames1;1377 bytes = frames1 * oss_frame_bytes;1378 } else1379#endif1380 {1381 frames = bytes_to_frames(runtime, bytes);1382 frames1 = snd_pcm_oss_write3(substream, buf, frames, in_kernel);1383 if (frames1 <= 0)1384 return frames1;1385 bytes = frames_to_bytes(runtime, frames1);1386 }1387 return bytes;1388}1389 1390static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const char __user *buf, size_t bytes)1391{1392 size_t xfer = 0;1393 ssize_t tmp = 0;1394 struct snd_pcm_runtime *runtime = substream->runtime;1395 1396 if (atomic_read(&substream->mmap_count))1397 return -ENXIO;1398 1399 atomic_inc(&runtime->oss.rw_ref);1400 while (bytes > 0) {1401 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {1402 tmp = -ERESTARTSYS;1403 break;1404 }1405 tmp = snd_pcm_oss_make_ready_locked(substream);1406 if (tmp < 0)1407 goto err;1408 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {1409 tmp = bytes;1410 if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)1411 tmp = runtime->oss.period_bytes - runtime->oss.buffer_used;1412 if (tmp > 0) {1413 if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) {1414 tmp = -EFAULT;1415 goto err;1416 }1417 }1418 runtime->oss.buffer_used += tmp;1419 buf += tmp;1420 bytes -= tmp;1421 xfer += tmp;1422 if (substream->oss.setup.partialfrag ||1423 runtime->oss.buffer_used == runtime->oss.period_bytes) {1424 tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, 1425 runtime->oss.buffer_used - runtime->oss.period_ptr, 1);1426 if (tmp <= 0)1427 goto err;1428 runtime->oss.bytes += tmp;1429 runtime->oss.period_ptr += tmp;1430 runtime->oss.period_ptr %= runtime->oss.period_bytes;1431 if (runtime->oss.period_ptr == 0 ||1432 runtime->oss.period_ptr == runtime->oss.buffer_used)1433 runtime->oss.buffer_used = 0;1434 else if ((substream->f_flags & O_NONBLOCK) != 0) {1435 tmp = -EAGAIN;1436 goto err;1437 }1438 }1439 } else {1440 tmp = snd_pcm_oss_write2(substream,1441 (const char __force *)buf,1442 runtime->oss.period_bytes, 0);1443 if (tmp <= 0)1444 goto err;1445 runtime->oss.bytes += tmp;1446 buf += tmp;1447 bytes -= tmp;1448 xfer += tmp;1449 if ((substream->f_flags & O_NONBLOCK) != 0 &&1450 tmp != runtime->oss.period_bytes)1451 tmp = -EAGAIN;1452 }1453 err:1454 mutex_unlock(&runtime->oss.params_lock);1455 if (tmp < 0)1456 break;1457 if (signal_pending(current)) {1458 tmp = -ERESTARTSYS;1459 break;1460 }1461 tmp = 0;1462 }1463 atomic_dec(&runtime->oss.rw_ref);1464 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;1465}1466 1467static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel)1468{1469 struct snd_pcm_runtime *runtime = substream->runtime;1470 snd_pcm_sframes_t frames, frames1;1471#ifdef CONFIG_SND_PCM_OSS_PLUGINS1472 char __user *final_dst = (char __force __user *)buf;1473 if (runtime->oss.plugin_first) {1474 struct snd_pcm_plugin_channel *channels;1475 size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;1476 if (!in_kernel)1477 buf = runtime->oss.buffer;1478 frames = bytes / oss_frame_bytes;1479 frames1 = snd_pcm_plug_client_channels_buf(substream, buf, frames, &channels);1480 if (frames1 < 0)1481 return frames1;1482 frames1 = snd_pcm_plug_read_transfer(substream, channels, frames1);1483 if (frames1 <= 0)1484 return frames1;1485 bytes = frames1 * oss_frame_bytes;1486 if (!in_kernel && copy_to_user(final_dst, buf, bytes))1487 return -EFAULT;1488 } else1489#endif1490 {1491 frames = bytes_to_frames(runtime, bytes);1492 frames1 = snd_pcm_oss_read3(substream, buf, frames, in_kernel);1493 if (frames1 <= 0)1494 return frames1;1495 bytes = frames_to_bytes(runtime, frames1);1496 }1497 return bytes;1498}1499 1500static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __user *buf, size_t bytes)1501{1502 size_t xfer = 0;1503 ssize_t tmp = 0;1504 struct snd_pcm_runtime *runtime = substream->runtime;1505 1506 if (atomic_read(&substream->mmap_count))1507 return -ENXIO;1508 1509 atomic_inc(&runtime->oss.rw_ref);1510 while (bytes > 0) {1511 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {1512 tmp = -ERESTARTSYS;1513 break;1514 }1515 tmp = snd_pcm_oss_make_ready_locked(substream);1516 if (tmp < 0)1517 goto err;1518 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {1519 if (runtime->oss.buffer_used == 0) {1520 tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);1521 if (tmp <= 0)1522 goto err;1523 runtime->oss.bytes += tmp;1524 runtime->oss.period_ptr = tmp;1525 runtime->oss.buffer_used = tmp;1526 }1527 tmp = bytes;1528 if ((size_t) tmp > runtime->oss.buffer_used)1529 tmp = runtime->oss.buffer_used;1530 if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) {1531 tmp = -EFAULT;1532 goto err;1533 }1534 buf += tmp;1535 bytes -= tmp;1536 xfer += tmp;1537 runtime->oss.buffer_used -= tmp;1538 } else {1539 tmp = snd_pcm_oss_read2(substream, (char __force *)buf,1540 runtime->oss.period_bytes, 0);1541 if (tmp <= 0)1542 goto err;1543 runtime->oss.bytes += tmp;1544 buf += tmp;1545 bytes -= tmp;1546 xfer += tmp;1547 }1548 err:1549 mutex_unlock(&runtime->oss.params_lock);1550 if (tmp < 0)1551 break;1552 if (signal_pending(current)) {1553 tmp = -ERESTARTSYS;1554 break;1555 }1556 tmp = 0;1557 }1558 atomic_dec(&runtime->oss.rw_ref);1559 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;1560}1561 1562static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)1563{1564 struct snd_pcm_substream *substream;1565 struct snd_pcm_runtime *runtime;1566 int i;1567 1568 for (i = 0; i < 2; i++) { 1569 substream = pcm_oss_file->streams[i];1570 if (!substream)1571 continue;1572 runtime = substream->runtime;1573 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);1574 mutex_lock(&runtime->oss.params_lock);1575 runtime->oss.prepare = 1;1576 runtime->oss.buffer_used = 0;1577 runtime->oss.prev_hw_ptr_period = 0;1578 runtime->oss.period_ptr = 0;1579 mutex_unlock(&runtime->oss.params_lock);1580 }1581 return 0;1582}1583 1584static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)1585{1586 struct snd_pcm_substream *substream;1587 int err;1588 1589 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];1590 if (substream != NULL) {1591 err = snd_pcm_oss_make_ready(substream);1592 if (err < 0)1593 return err;1594 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);1595 }1596 /* note: all errors from the start action are ignored */1597 /* OSS apps do not know, how to handle them */1598 return 0;1599}1600 1601static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)1602{1603 struct snd_pcm_runtime *runtime;1604 ssize_t result = 0;1605 snd_pcm_state_t state;1606 long res;1607 wait_queue_entry_t wait;1608 1609 runtime = substream->runtime;1610 init_waitqueue_entry(&wait, current);1611 add_wait_queue(&runtime->sleep, &wait);1612#ifdef OSS_DEBUG1613 pcm_dbg(substream->pcm, "sync1: size = %li\n", size);1614#endif1615 while (1) {1616 result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);1617 if (result > 0) {1618 runtime->oss.buffer_used = 0;1619 result = 0;1620 break;1621 }1622 if (result != 0 && result != -EAGAIN)1623 break;1624 result = 0;1625 set_current_state(TASK_INTERRUPTIBLE);1626 scoped_guard(pcm_stream_lock_irq, substream)1627 state = runtime->state;1628 if (state != SNDRV_PCM_STATE_RUNNING) {1629 set_current_state(TASK_RUNNING);1630 break;1631 }1632 res = schedule_timeout(10 * HZ);1633 if (signal_pending(current)) {1634 result = -ERESTARTSYS;1635 break;1636 }1637 if (res == 0) {1638 pcm_err(substream->pcm,1639 "OSS sync error - DMA timeout\n");1640 result = -EIO;1641 break;1642 }1643 }1644 remove_wait_queue(&runtime->sleep, &wait);1645 return result;1646}1647 1648static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)1649{1650 int err = 0;1651 unsigned int saved_f_flags;1652 struct snd_pcm_substream *substream;1653 struct snd_pcm_runtime *runtime;1654 snd_pcm_format_t format;1655 unsigned long width;1656 size_t size;1657 1658 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];1659 if (substream != NULL) {1660 runtime = substream->runtime;1661 if (atomic_read(&substream->mmap_count))1662 goto __direct;1663 atomic_inc(&runtime->oss.rw_ref);1664 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {1665 atomic_dec(&runtime->oss.rw_ref);1666 return -ERESTARTSYS;1667 }1668 err = snd_pcm_oss_make_ready_locked(substream);1669 if (err < 0)1670 goto unlock;1671 format = snd_pcm_oss_format_from(runtime->oss.format);1672 width = snd_pcm_format_physical_width(format);1673 if (runtime->oss.buffer_used > 0) {1674#ifdef OSS_DEBUG1675 pcm_dbg(substream->pcm, "sync: buffer_used\n");1676#endif1677 size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;1678 snd_pcm_format_set_silence(format,1679 runtime->oss.buffer + runtime->oss.buffer_used,1680 size);1681 err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);1682 if (err < 0)1683 goto unlock;1684 } else if (runtime->oss.period_ptr > 0) {1685#ifdef OSS_DEBUG1686 pcm_dbg(substream->pcm, "sync: period_ptr\n");1687#endif1688 size = runtime->oss.period_bytes - runtime->oss.period_ptr;1689 snd_pcm_format_set_silence(format,1690 runtime->oss.buffer,1691 size * 8 / width);1692 err = snd_pcm_oss_sync1(substream, size);1693 if (err < 0)1694 goto unlock;1695 }1696 /*1697 * The ALSA's period might be a bit large than OSS one.1698 * Fill the remain portion of ALSA period with zeros.1699 */1700 size = runtime->control->appl_ptr % runtime->period_size;1701 if (size > 0) {1702 size = runtime->period_size - size;1703 if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)1704 snd_pcm_lib_write(substream, NULL, size);1705 else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)1706 snd_pcm_lib_writev(substream, NULL, size);1707 }1708unlock:1709 mutex_unlock(&runtime->oss.params_lock);1710 atomic_dec(&runtime->oss.rw_ref);1711 if (err < 0)1712 return err;1713 /*1714 * finish sync: drain the buffer1715 */1716 __direct:1717 saved_f_flags = substream->f_flags;1718 substream->f_flags &= ~O_NONBLOCK;1719 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);1720 substream->f_flags = saved_f_flags;1721 if (err < 0)1722 return err;1723 mutex_lock(&runtime->oss.params_lock);1724 runtime->oss.prepare = 1;1725 mutex_unlock(&runtime->oss.params_lock);1726 }1727 1728 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];1729 if (substream != NULL) {1730 err = snd_pcm_oss_make_ready(substream);1731 if (err < 0)1732 return err;1733 runtime = substream->runtime;1734 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);1735 if (err < 0)1736 return err;1737 mutex_lock(&runtime->oss.params_lock);1738 runtime->oss.buffer_used = 0;1739 runtime->oss.prepare = 1;1740 mutex_unlock(&runtime->oss.params_lock);1741 }1742 return 0;1743}1744 1745static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)1746{1747 int idx;1748 1749 for (idx = 1; idx >= 0; --idx) {1750 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];1751 struct snd_pcm_runtime *runtime;1752 int err;1753 1754 if (substream == NULL)1755 continue;1756 runtime = substream->runtime;1757 if (rate < 1000)1758 rate = 1000;1759 else if (rate > 192000)1760 rate = 192000;1761 err = lock_params(runtime);1762 if (err < 0)1763 return err;1764 if (runtime->oss.rate != rate) {1765 runtime->oss.params = 1;1766 runtime->oss.rate = rate;1767 }1768 unlock_params(runtime);1769 }1770 return snd_pcm_oss_get_rate(pcm_oss_file);1771}1772 1773static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file)1774{1775 struct snd_pcm_substream *substream;1776 int err;1777 1778 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);1779 if (err < 0)1780 return err;1781 return substream->runtime->oss.rate;1782}1783 1784static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsigned int channels)1785{1786 int idx;1787 if (channels < 1)1788 channels = 1;1789 if (channels > 128)1790 return -EINVAL;1791 for (idx = 1; idx >= 0; --idx) {1792 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];1793 struct snd_pcm_runtime *runtime;1794 int err;1795 1796 if (substream == NULL)1797 continue;1798 runtime = substream->runtime;1799 err = lock_params(runtime);1800 if (err < 0)1801 return err;1802 if (runtime->oss.channels != channels) {1803 runtime->oss.params = 1;1804 runtime->oss.channels = channels;1805 }1806 unlock_params(runtime);1807 }1808 return snd_pcm_oss_get_channels(pcm_oss_file);1809}1810 1811static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file)1812{1813 struct snd_pcm_substream *substream;1814 int err;1815 1816 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);1817 if (err < 0)1818 return err;1819 return substream->runtime->oss.channels;1820}1821 1822static int snd_pcm_oss_get_block_size(struct snd_pcm_oss_file *pcm_oss_file)1823{1824 struct snd_pcm_substream *substream;1825 int err;1826 1827 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);1828 if (err < 0)1829 return err;1830 return substream->runtime->oss.period_bytes;1831}1832 1833static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)1834{1835 struct snd_pcm_substream *substream;1836 int err;1837 int direct;1838 struct snd_pcm_hw_params *params __free(kfree) = NULL;1839 unsigned int formats = 0;1840 const struct snd_mask *format_mask;1841 int fmt;1842 1843 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);1844 if (err < 0)1845 return err;1846 if (atomic_read(&substream->mmap_count))1847 direct = 1;1848 else1849 direct = substream->oss.setup.direct;1850 if (!direct)1851 return AFMT_MU_LAW | AFMT_U8 |1852 AFMT_S16_LE | AFMT_S16_BE |1853 AFMT_S8 | AFMT_U16_LE |1854 AFMT_U16_BE |1855 AFMT_S32_LE | AFMT_S32_BE |1856 AFMT_S24_LE | AFMT_S24_BE |1857 AFMT_S24_PACKED;1858 params = kmalloc(sizeof(*params), GFP_KERNEL);1859 if (!params)1860 return -ENOMEM;1861 _snd_pcm_hw_params_any(params);1862 err = snd_pcm_hw_refine(substream, params);1863 if (err < 0)1864 return err;1865 format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);1866 for (fmt = 0; fmt < 32; ++fmt) {1867 if (snd_mask_test(format_mask, fmt)) {1868 int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt);1869 if (f >= 0)1870 formats |= f;1871 }1872 }1873 1874 return formats;1875}1876 1877static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)1878{1879 int formats, idx;1880 int err;1881 1882 if (format != AFMT_QUERY) {1883 formats = snd_pcm_oss_get_formats(pcm_oss_file);1884 if (formats < 0)1885 return formats;1886 if (!(formats & format))1887 format = AFMT_U8;1888 for (idx = 1; idx >= 0; --idx) {1889 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];1890 struct snd_pcm_runtime *runtime;1891 if (substream == NULL)1892 continue;1893 runtime = substream->runtime;1894 err = lock_params(runtime);1895 if (err < 0)1896 return err;1897 if (runtime->oss.format != format) {1898 runtime->oss.params = 1;1899 runtime->oss.format = format;1900 }1901 unlock_params(runtime);1902 }1903 }1904 return snd_pcm_oss_get_format(pcm_oss_file);1905}1906 1907static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file)1908{1909 struct snd_pcm_substream *substream;1910 int err;1911 1912 err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);1913 if (err < 0)1914 return err;1915 return substream->runtime->oss.format;1916}1917 1918static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int subdivide)1919{1920 struct snd_pcm_runtime *runtime;1921 1922 runtime = substream->runtime;1923 if (subdivide == 0) {1924 subdivide = runtime->oss.subdivision;1925 if (subdivide == 0)1926 subdivide = 1;1927 return subdivide;1928 }1929 if (runtime->oss.subdivision || runtime->oss.fragshift)1930 return -EINVAL;1931 if (subdivide != 1 && subdivide != 2 && subdivide != 4 &&1932 subdivide != 8 && subdivide != 16)1933 return -EINVAL;1934 runtime->oss.subdivision = subdivide;1935 runtime->oss.params = 1;1936 return subdivide;1937}1938 1939static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int subdivide)1940{1941 int err = -EINVAL, idx;1942 1943 for (idx = 1; idx >= 0; --idx) {1944 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];1945 struct snd_pcm_runtime *runtime;1946 1947 if (substream == NULL)1948 continue;1949 runtime = substream->runtime;1950 err = lock_params(runtime);1951 if (err < 0)1952 return err;1953 err = snd_pcm_oss_set_subdivide1(substream, subdivide);1954 unlock_params(runtime);1955 if (err < 0)1956 return err;1957 }1958 return err;1959}1960 1961static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsigned int val)1962{1963 struct snd_pcm_runtime *runtime;1964 int fragshift;1965 1966 runtime = substream->runtime;1967 if (runtime->oss.subdivision || runtime->oss.fragshift)1968 return -EINVAL;1969 fragshift = val & 0xffff;1970 if (fragshift >= 25) /* should be large enough */1971 return -EINVAL;1972 runtime->oss.fragshift = fragshift;1973 runtime->oss.maxfrags = (val >> 16) & 0xffff;1974 if (runtime->oss.fragshift < 4) /* < 16 */1975 runtime->oss.fragshift = 4;1976 if (runtime->oss.maxfrags < 2)1977 runtime->oss.maxfrags = 2;1978 runtime->oss.params = 1;1979 return 0;1980}1981 1982static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsigned int val)1983{1984 int err = -EINVAL, idx;1985 1986 for (idx = 1; idx >= 0; --idx) {1987 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];1988 struct snd_pcm_runtime *runtime;1989 1990 if (substream == NULL)1991 continue;1992 runtime = substream->runtime;1993 err = lock_params(runtime);1994 if (err < 0)1995 return err;1996 err = snd_pcm_oss_set_fragment1(substream, val);1997 unlock_params(runtime);1998 if (err < 0)1999 return err;2000 }2001 return err;2002}2003 2004static int snd_pcm_oss_nonblock(struct file * file)2005{2006 spin_lock(&file->f_lock);2007 file->f_flags |= O_NONBLOCK;2008 spin_unlock(&file->f_lock);2009 return 0;2010}2011 2012static int snd_pcm_oss_get_caps1(struct snd_pcm_substream *substream, int res)2013{2014 2015 if (substream == NULL) {2016 res &= ~DSP_CAP_DUPLEX;2017 return res;2018 }2019#ifdef DSP_CAP_MULTI2020 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)2021 if (substream->pstr->substream_count > 1)2022 res |= DSP_CAP_MULTI;2023#endif2024 /* DSP_CAP_REALTIME is set all times: */2025 /* all ALSA drivers can return actual pointer in ring buffer */2026#if defined(DSP_CAP_REALTIME) && 02027 {2028 struct snd_pcm_runtime *runtime = substream->runtime;2029 if (runtime->info & (SNDRV_PCM_INFO_BLOCK_TRANSFER|SNDRV_PCM_INFO_BATCH))2030 res &= ~DSP_CAP_REALTIME;2031 }2032#endif2033 return res;2034}2035 2036static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file)2037{2038 int result, idx;2039 2040 result = DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_DUPLEX | DSP_CAP_REALTIME;2041 for (idx = 0; idx < 2; idx++) {2042 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];2043 result = snd_pcm_oss_get_caps1(substream, result);2044 }2045 result |= 0x0001; /* revision - same as SB AWE 64 */2046 return result;2047}2048 2049static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream,2050 snd_pcm_uframes_t hw_ptr)2051{2052 struct snd_pcm_runtime *runtime = substream->runtime;2053 snd_pcm_uframes_t appl_ptr;2054 appl_ptr = hw_ptr + runtime->buffer_size;2055 appl_ptr %= runtime->boundary;2056 runtime->control->appl_ptr = appl_ptr;2057}2058 2059static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int trigger)2060{2061 struct snd_pcm_runtime *runtime;2062 struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;2063 int err, cmd;2064 2065#ifdef OSS_DEBUG2066 pr_debug("pcm_oss: trigger = 0x%x\n", trigger);2067#endif2068 2069 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2070 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2071 2072 if (psubstream) {2073 err = snd_pcm_oss_make_ready(psubstream);2074 if (err < 0)2075 return err;2076 }2077 if (csubstream) {2078 err = snd_pcm_oss_make_ready(csubstream);2079 if (err < 0)2080 return err;2081 }2082 if (psubstream) {2083 runtime = psubstream->runtime;2084 cmd = 0;2085 if (mutex_lock_interruptible(&runtime->oss.params_lock))2086 return -ERESTARTSYS;2087 if (trigger & PCM_ENABLE_OUTPUT) {2088 if (runtime->oss.trigger)2089 goto _skip1;2090 if (atomic_read(&psubstream->mmap_count))2091 snd_pcm_oss_simulate_fill(psubstream,2092 get_hw_ptr_period(runtime));2093 runtime->oss.trigger = 1;2094 runtime->start_threshold = 1;2095 cmd = SNDRV_PCM_IOCTL_START;2096 } else {2097 if (!runtime->oss.trigger)2098 goto _skip1;2099 runtime->oss.trigger = 0;2100 runtime->start_threshold = runtime->boundary;2101 cmd = SNDRV_PCM_IOCTL_DROP;2102 runtime->oss.prepare = 1;2103 }2104 _skip1:2105 mutex_unlock(&runtime->oss.params_lock);2106 if (cmd) {2107 err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);2108 if (err < 0)2109 return err;2110 }2111 }2112 if (csubstream) {2113 runtime = csubstream->runtime;2114 cmd = 0;2115 if (mutex_lock_interruptible(&runtime->oss.params_lock))2116 return -ERESTARTSYS;2117 if (trigger & PCM_ENABLE_INPUT) {2118 if (runtime->oss.trigger)2119 goto _skip2;2120 runtime->oss.trigger = 1;2121 runtime->start_threshold = 1;2122 cmd = SNDRV_PCM_IOCTL_START;2123 } else {2124 if (!runtime->oss.trigger)2125 goto _skip2;2126 runtime->oss.trigger = 0;2127 runtime->start_threshold = runtime->boundary;2128 cmd = SNDRV_PCM_IOCTL_DROP;2129 runtime->oss.prepare = 1;2130 }2131 _skip2:2132 mutex_unlock(&runtime->oss.params_lock);2133 if (cmd) {2134 err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);2135 if (err < 0)2136 return err;2137 }2138 }2139 return 0;2140}2141 2142static int snd_pcm_oss_get_trigger(struct snd_pcm_oss_file *pcm_oss_file)2143{2144 struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;2145 int result = 0;2146 2147 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2148 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2149 if (psubstream && psubstream->runtime && psubstream->runtime->oss.trigger)2150 result |= PCM_ENABLE_OUTPUT;2151 if (csubstream && csubstream->runtime && csubstream->runtime->oss.trigger)2152 result |= PCM_ENABLE_INPUT;2153 return result;2154}2155 2156static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)2157{2158 struct snd_pcm_substream *substream;2159 struct snd_pcm_runtime *runtime;2160 snd_pcm_sframes_t delay;2161 int err;2162 2163 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2164 if (substream == NULL)2165 return -EINVAL;2166 err = snd_pcm_oss_make_ready(substream);2167 if (err < 0)2168 return err;2169 runtime = substream->runtime;2170 if (runtime->oss.params || runtime->oss.prepare)2171 return 0;2172 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);2173 if (err == -EPIPE)2174 delay = 0; /* hack for broken OSS applications */2175 else if (err < 0)2176 return err;2177 return snd_pcm_oss_bytes(substream, delay);2178}2179 2180static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct count_info __user * _info)2181{ 2182 struct snd_pcm_substream *substream;2183 struct snd_pcm_runtime *runtime;2184 snd_pcm_sframes_t delay;2185 int fixup;2186 struct count_info info;2187 int err;2188 2189 if (_info == NULL)2190 return -EFAULT;2191 substream = pcm_oss_file->streams[stream];2192 if (substream == NULL)2193 return -EINVAL;2194 err = snd_pcm_oss_make_ready(substream);2195 if (err < 0)2196 return err;2197 runtime = substream->runtime;2198 if (runtime->oss.params || runtime->oss.prepare) {2199 memset(&info, 0, sizeof(info));2200 if (copy_to_user(_info, &info, sizeof(info)))2201 return -EFAULT;2202 return 0;2203 }2204 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {2205 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);2206 if (err == -EPIPE || err == -ESTRPIPE || (! err && delay < 0)) {2207 err = 0;2208 delay = 0;2209 fixup = 0;2210 } else {2211 fixup = runtime->oss.buffer_used;2212 }2213 } else {2214 err = snd_pcm_oss_capture_position_fixup(substream, &delay);2215 fixup = -runtime->oss.buffer_used;2216 }2217 if (err < 0)2218 return err;2219 info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);2220 if (atomic_read(&substream->mmap_count)) {2221 snd_pcm_sframes_t n;2222 delay = get_hw_ptr_period(runtime);2223 n = delay - runtime->oss.prev_hw_ptr_period;2224 if (n < 0)2225 n += runtime->boundary;2226 info.blocks = n / runtime->period_size;2227 runtime->oss.prev_hw_ptr_period = delay;2228 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)2229 snd_pcm_oss_simulate_fill(substream, delay);2230 info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;2231 } else {2232 delay = snd_pcm_oss_bytes(substream, delay);2233 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {2234 if (substream->oss.setup.buggyptr)2235 info.blocks = (runtime->oss.buffer_bytes - delay - fixup) / runtime->oss.period_bytes;2236 else2237 info.blocks = (delay + fixup) / runtime->oss.period_bytes;2238 info.bytes = (runtime->oss.bytes - delay) & INT_MAX;2239 } else {2240 delay += fixup;2241 info.blocks = delay / runtime->oss.period_bytes;2242 info.bytes = (runtime->oss.bytes + delay) & INT_MAX;2243 }2244 }2245 if (copy_to_user(_info, &info, sizeof(info)))2246 return -EFAULT;2247 return 0;2248}2249 2250static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct audio_buf_info __user *_info)2251{2252 struct snd_pcm_substream *substream;2253 struct snd_pcm_runtime *runtime;2254 snd_pcm_sframes_t avail;2255 int fixup;2256 struct audio_buf_info info;2257 int err;2258 2259 if (_info == NULL)2260 return -EFAULT;2261 substream = pcm_oss_file->streams[stream];2262 if (substream == NULL)2263 return -EINVAL;2264 runtime = substream->runtime;2265 2266 if (runtime->oss.params) {2267 err = snd_pcm_oss_change_params(substream, false);2268 if (err < 0)2269 return err;2270 }2271 2272 info.fragsize = runtime->oss.period_bytes;2273 info.fragstotal = runtime->periods;2274 if (runtime->oss.prepare) {2275 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {2276 info.bytes = runtime->oss.period_bytes * runtime->oss.periods;2277 info.fragments = runtime->oss.periods;2278 } else {2279 info.bytes = 0;2280 info.fragments = 0;2281 }2282 } else {2283 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {2284 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &avail);2285 if (err == -EPIPE || err == -ESTRPIPE || (! err && avail < 0)) {2286 avail = runtime->buffer_size;2287 err = 0;2288 fixup = 0;2289 } else {2290 avail = runtime->buffer_size - avail;2291 fixup = -runtime->oss.buffer_used;2292 }2293 } else {2294 err = snd_pcm_oss_capture_position_fixup(substream, &avail);2295 fixup = runtime->oss.buffer_used;2296 }2297 if (err < 0)2298 return err;2299 info.bytes = snd_pcm_oss_bytes(substream, avail) + fixup;2300 info.fragments = info.bytes / runtime->oss.period_bytes;2301 }2302 2303#ifdef OSS_DEBUG2304 pcm_dbg(substream->pcm,2305 "pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n",2306 info.bytes, info.fragments, info.fragstotal, info.fragsize);2307#endif2308 if (copy_to_user(_info, &info, sizeof(info)))2309 return -EFAULT;2310 return 0;2311}2312 2313static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct buffmem_desc __user * _info)2314{2315 // it won't be probably implemented2316 // pr_debug("TODO: snd_pcm_oss_get_mapbuf\n");2317 return -EINVAL;2318}2319 2320static const char *strip_task_path(const char *path)2321{2322 const char *ptr, *ptrl = NULL;2323 for (ptr = path; *ptr; ptr++) {2324 if (*ptr == '/')2325 ptrl = ptr + 1;2326 }2327 return ptrl;2328}2329 2330static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,2331 const char *task_name,2332 struct snd_pcm_oss_setup *rsetup)2333{2334 struct snd_pcm_oss_setup *setup;2335 2336 guard(mutex)(&pcm->streams[stream].oss.setup_mutex);2337 do {2338 for (setup = pcm->streams[stream].oss.setup_list; setup;2339 setup = setup->next) {2340 if (!strcmp(setup->task_name, task_name))2341 goto out;2342 }2343 } while ((task_name = strip_task_path(task_name)) != NULL);2344 out:2345 if (setup)2346 *rsetup = *setup;2347}2348 2349static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)2350{2351 snd_pcm_oss_release_buffers(substream);2352 substream->oss.oss = 0;2353}2354 2355static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,2356 struct snd_pcm_oss_setup *setup,2357 int minor)2358{2359 struct snd_pcm_runtime *runtime;2360 2361 substream->oss.oss = 1;2362 substream->oss.setup = *setup;2363 if (setup->nonblock)2364 substream->f_flags |= O_NONBLOCK;2365 else if (setup->block)2366 substream->f_flags &= ~O_NONBLOCK;2367 runtime = substream->runtime;2368 runtime->oss.params = 1;2369 runtime->oss.trigger = 1;2370 runtime->oss.rate = 8000;2371 mutex_init(&runtime->oss.params_lock);2372 switch (SNDRV_MINOR_OSS_DEVICE(minor)) {2373 case SNDRV_MINOR_OSS_PCM_8:2374 runtime->oss.format = AFMT_U8;2375 break;2376 case SNDRV_MINOR_OSS_PCM_16:2377 runtime->oss.format = AFMT_S16_LE;2378 break;2379 default:2380 runtime->oss.format = AFMT_MU_LAW;2381 }2382 runtime->oss.channels = 1;2383 runtime->oss.fragshift = 0;2384 runtime->oss.maxfrags = 0;2385 runtime->oss.subdivision = 0;2386 substream->pcm_release = snd_pcm_oss_release_substream;2387 atomic_set(&runtime->oss.rw_ref, 0);2388}2389 2390static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)2391{2392 int cidx;2393 if (!pcm_oss_file)2394 return 0;2395 for (cidx = 0; cidx < 2; ++cidx) {2396 struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];2397 if (substream)2398 snd_pcm_release_substream(substream);2399 }2400 kfree(pcm_oss_file);2401 return 0;2402}2403 2404static int snd_pcm_oss_open_file(struct file *file,2405 struct snd_pcm *pcm,2406 struct snd_pcm_oss_file **rpcm_oss_file,2407 int minor,2408 struct snd_pcm_oss_setup *setup)2409{2410 int idx, err;2411 struct snd_pcm_oss_file *pcm_oss_file;2412 struct snd_pcm_substream *substream;2413 fmode_t f_mode = file->f_mode;2414 2415 if (rpcm_oss_file)2416 *rpcm_oss_file = NULL;2417 2418 pcm_oss_file = kzalloc(sizeof(*pcm_oss_file), GFP_KERNEL);2419 if (pcm_oss_file == NULL)2420 return -ENOMEM;2421 2422 if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&2423 (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))2424 f_mode = FMODE_WRITE;2425 2426 file->f_flags &= ~O_APPEND;2427 for (idx = 0; idx < 2; idx++) {2428 if (setup[idx].disable)2429 continue;2430 if (! pcm->streams[idx].substream_count)2431 continue; /* no matching substream */2432 if (idx == SNDRV_PCM_STREAM_PLAYBACK) {2433 if (! (f_mode & FMODE_WRITE))2434 continue;2435 } else {2436 if (! (f_mode & FMODE_READ))2437 continue;2438 }2439 err = snd_pcm_open_substream(pcm, idx, file, &substream);2440 if (err < 0) {2441 snd_pcm_oss_release_file(pcm_oss_file);2442 return err;2443 }2444 2445 pcm_oss_file->streams[idx] = substream;2446 snd_pcm_oss_init_substream(substream, &setup[idx], minor);2447 }2448 2449 if (!pcm_oss_file->streams[0] && !pcm_oss_file->streams[1]) {2450 snd_pcm_oss_release_file(pcm_oss_file);2451 return -EINVAL;2452 }2453 2454 file->private_data = pcm_oss_file;2455 if (rpcm_oss_file)2456 *rpcm_oss_file = pcm_oss_file;2457 return 0;2458}2459 2460 2461static int snd_task_name(struct task_struct *task, char *name, size_t size)2462{2463 unsigned int idx;2464 2465 if (snd_BUG_ON(!task || !name || size < 2))2466 return -EINVAL;2467 for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)2468 name[idx] = task->comm[idx];2469 name[idx] = '\0';2470 return 0;2471}2472 2473static int snd_pcm_oss_open(struct inode *inode, struct file *file)2474{2475 int err;2476 char task_name[32];2477 struct snd_pcm *pcm;2478 struct snd_pcm_oss_file *pcm_oss_file;2479 struct snd_pcm_oss_setup setup[2];2480 int nonblock;2481 wait_queue_entry_t wait;2482 2483 err = nonseekable_open(inode, file);2484 if (err < 0)2485 return err;2486 2487 pcm = snd_lookup_oss_minor_data(iminor(inode),2488 SNDRV_OSS_DEVICE_TYPE_PCM);2489 if (pcm == NULL) {2490 err = -ENODEV;2491 goto __error1;2492 }2493 err = snd_card_file_add(pcm->card, file);2494 if (err < 0)2495 goto __error1;2496 if (!try_module_get(pcm->card->module)) {2497 err = -EFAULT;2498 goto __error2;2499 }2500 if (snd_task_name(current, task_name, sizeof(task_name)) < 0) {2501 err = -EFAULT;2502 goto __error;2503 }2504 memset(setup, 0, sizeof(setup));2505 if (file->f_mode & FMODE_WRITE)2506 snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK,2507 task_name, &setup[0]);2508 if (file->f_mode & FMODE_READ)2509 snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE,2510 task_name, &setup[1]);2511 2512 nonblock = !!(file->f_flags & O_NONBLOCK);2513 if (!nonblock)2514 nonblock = nonblock_open;2515 2516 init_waitqueue_entry(&wait, current);2517 add_wait_queue(&pcm->open_wait, &wait);2518 mutex_lock(&pcm->open_mutex);2519 while (1) {2520 err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,2521 iminor(inode), setup);2522 if (err >= 0)2523 break;2524 if (err == -EAGAIN) {2525 if (nonblock) {2526 err = -EBUSY;2527 break;2528 }2529 } else2530 break;2531 set_current_state(TASK_INTERRUPTIBLE);2532 mutex_unlock(&pcm->open_mutex);2533 schedule();2534 mutex_lock(&pcm->open_mutex);2535 if (pcm->card->shutdown) {2536 err = -ENODEV;2537 break;2538 }2539 if (signal_pending(current)) {2540 err = -ERESTARTSYS;2541 break;2542 }2543 }2544 remove_wait_queue(&pcm->open_wait, &wait);2545 mutex_unlock(&pcm->open_mutex);2546 if (err < 0)2547 goto __error;2548 snd_card_unref(pcm->card);2549 return err;2550 2551 __error:2552 module_put(pcm->card->module);2553 __error2:2554 snd_card_file_remove(pcm->card, file);2555 __error1:2556 if (pcm)2557 snd_card_unref(pcm->card);2558 return err;2559}2560 2561static int snd_pcm_oss_release(struct inode *inode, struct file *file)2562{2563 struct snd_pcm *pcm;2564 struct snd_pcm_substream *substream;2565 struct snd_pcm_oss_file *pcm_oss_file;2566 2567 pcm_oss_file = file->private_data;2568 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2569 if (substream == NULL)2570 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2571 if (snd_BUG_ON(!substream))2572 return -ENXIO;2573 pcm = substream->pcm;2574 if (!pcm->card->shutdown)2575 snd_pcm_oss_sync(pcm_oss_file);2576 mutex_lock(&pcm->open_mutex);2577 snd_pcm_oss_release_file(pcm_oss_file);2578 mutex_unlock(&pcm->open_mutex);2579 wake_up(&pcm->open_wait);2580 module_put(pcm->card->module);2581 snd_card_file_remove(pcm->card, file);2582 return 0;2583}2584 2585static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)2586{2587 struct snd_pcm_oss_file *pcm_oss_file;2588 int __user *p = (int __user *)arg;2589 int res;2590 2591 pcm_oss_file = file->private_data;2592 if (cmd == OSS_GETVERSION)2593 return put_user(SNDRV_OSS_VERSION, p);2594 if (cmd == OSS_ALSAEMULVER)2595 return put_user(1, p);2596#if IS_REACHABLE(CONFIG_SND_MIXER_OSS)2597 if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */2598 struct snd_pcm_substream *substream;2599 int idx;2600 for (idx = 0; idx < 2; ++idx) {2601 substream = pcm_oss_file->streams[idx];2602 if (substream != NULL)2603 break;2604 }2605 if (snd_BUG_ON(idx >= 2))2606 return -ENXIO;2607 return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);2608 }2609#endif2610 if (((cmd >> 8) & 0xff) != 'P')2611 return -EINVAL;2612#ifdef OSS_DEBUG2613 pr_debug("pcm_oss: ioctl = 0x%x\n", cmd);2614#endif2615 switch (cmd) {2616 case SNDCTL_DSP_RESET:2617 return snd_pcm_oss_reset(pcm_oss_file);2618 case SNDCTL_DSP_SYNC:2619 return snd_pcm_oss_sync(pcm_oss_file);2620 case SNDCTL_DSP_SPEED:2621 if (get_user(res, p))2622 return -EFAULT;2623 res = snd_pcm_oss_set_rate(pcm_oss_file, res);2624 if (res < 0)2625 return res;2626 return put_user(res, p);2627 case SOUND_PCM_READ_RATE:2628 res = snd_pcm_oss_get_rate(pcm_oss_file);2629 if (res < 0)2630 return res;2631 return put_user(res, p);2632 case SNDCTL_DSP_STEREO:2633 if (get_user(res, p))2634 return -EFAULT;2635 res = res > 0 ? 2 : 1;2636 res = snd_pcm_oss_set_channels(pcm_oss_file, res);2637 if (res < 0)2638 return res;2639 return put_user(--res, p);2640 case SNDCTL_DSP_GETBLKSIZE:2641 res = snd_pcm_oss_get_block_size(pcm_oss_file);2642 if (res < 0)2643 return res;2644 return put_user(res, p);2645 case SNDCTL_DSP_SETFMT:2646 if (get_user(res, p))2647 return -EFAULT;2648 res = snd_pcm_oss_set_format(pcm_oss_file, res);2649 if (res < 0)2650 return res;2651 return put_user(res, p);2652 case SOUND_PCM_READ_BITS:2653 res = snd_pcm_oss_get_format(pcm_oss_file);2654 if (res < 0)2655 return res;2656 return put_user(res, p);2657 case SNDCTL_DSP_CHANNELS:2658 if (get_user(res, p))2659 return -EFAULT;2660 res = snd_pcm_oss_set_channels(pcm_oss_file, res);2661 if (res < 0)2662 return res;2663 return put_user(res, p);2664 case SOUND_PCM_READ_CHANNELS:2665 res = snd_pcm_oss_get_channels(pcm_oss_file);2666 if (res < 0)2667 return res;2668 return put_user(res, p);2669 case SOUND_PCM_WRITE_FILTER:2670 case SOUND_PCM_READ_FILTER:2671 return -EIO;2672 case SNDCTL_DSP_POST:2673 return snd_pcm_oss_post(pcm_oss_file);2674 case SNDCTL_DSP_SUBDIVIDE:2675 if (get_user(res, p))2676 return -EFAULT;2677 res = snd_pcm_oss_set_subdivide(pcm_oss_file, res);2678 if (res < 0)2679 return res;2680 return put_user(res, p);2681 case SNDCTL_DSP_SETFRAGMENT:2682 if (get_user(res, p))2683 return -EFAULT;2684 return snd_pcm_oss_set_fragment(pcm_oss_file, res);2685 case SNDCTL_DSP_GETFMTS:2686 res = snd_pcm_oss_get_formats(pcm_oss_file);2687 if (res < 0)2688 return res;2689 return put_user(res, p);2690 case SNDCTL_DSP_GETOSPACE:2691 case SNDCTL_DSP_GETISPACE:2692 return snd_pcm_oss_get_space(pcm_oss_file,2693 cmd == SNDCTL_DSP_GETISPACE ?2694 SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,2695 (struct audio_buf_info __user *) arg);2696 case SNDCTL_DSP_NONBLOCK:2697 return snd_pcm_oss_nonblock(file);2698 case SNDCTL_DSP_GETCAPS:2699 res = snd_pcm_oss_get_caps(pcm_oss_file);2700 if (res < 0)2701 return res;2702 return put_user(res, p);2703 case SNDCTL_DSP_GETTRIGGER:2704 res = snd_pcm_oss_get_trigger(pcm_oss_file);2705 if (res < 0)2706 return res;2707 return put_user(res, p);2708 case SNDCTL_DSP_SETTRIGGER:2709 if (get_user(res, p))2710 return -EFAULT;2711 return snd_pcm_oss_set_trigger(pcm_oss_file, res);2712 case SNDCTL_DSP_GETIPTR:2713 case SNDCTL_DSP_GETOPTR:2714 return snd_pcm_oss_get_ptr(pcm_oss_file,2715 cmd == SNDCTL_DSP_GETIPTR ?2716 SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,2717 (struct count_info __user *) arg);2718 case SNDCTL_DSP_MAPINBUF:2719 case SNDCTL_DSP_MAPOUTBUF:2720 return snd_pcm_oss_get_mapbuf(pcm_oss_file,2721 cmd == SNDCTL_DSP_MAPINBUF ?2722 SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK,2723 (struct buffmem_desc __user *) arg);2724 case SNDCTL_DSP_SETSYNCRO:2725 /* stop DMA now.. */2726 return 0;2727 case SNDCTL_DSP_SETDUPLEX:2728 if (snd_pcm_oss_get_caps(pcm_oss_file) & DSP_CAP_DUPLEX)2729 return 0;2730 return -EIO;2731 case SNDCTL_DSP_GETODELAY:2732 res = snd_pcm_oss_get_odelay(pcm_oss_file);2733 if (res < 0) {2734 /* it's for sure, some broken apps don't check for error codes */2735 put_user(0, p);2736 return res;2737 }2738 return put_user(res, p);2739 case SNDCTL_DSP_PROFILE:2740 return 0; /* silently ignore */2741 default:2742 pr_debug("pcm_oss: unknown command = 0x%x\n", cmd);2743 }2744 return -EINVAL;2745}2746 2747#ifdef CONFIG_COMPAT2748/* all compatible */2749static long snd_pcm_oss_ioctl_compat(struct file *file, unsigned int cmd,2750 unsigned long arg)2751{2752 /*2753 * Everything is compatbile except SNDCTL_DSP_MAPINBUF/SNDCTL_DSP_MAPOUTBUF,2754 * which are not implemented for the native case either2755 */2756 return snd_pcm_oss_ioctl(file, cmd, (unsigned long)compat_ptr(arg));2757}2758#else2759#define snd_pcm_oss_ioctl_compat NULL2760#endif2761 2762static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset)2763{2764 struct snd_pcm_oss_file *pcm_oss_file;2765 struct snd_pcm_substream *substream;2766 2767 pcm_oss_file = file->private_data;2768 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2769 if (substream == NULL)2770 return -ENXIO;2771 substream->f_flags = file->f_flags & O_NONBLOCK;2772#ifndef OSS_DEBUG2773 return snd_pcm_oss_read1(substream, buf, count);2774#else2775 {2776 ssize_t res = snd_pcm_oss_read1(substream, buf, count);2777 pcm_dbg(substream->pcm,2778 "pcm_oss: read %li bytes (returned %li bytes)\n",2779 (long)count, (long)res);2780 return res;2781 }2782#endif2783}2784 2785static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)2786{2787 struct snd_pcm_oss_file *pcm_oss_file;2788 struct snd_pcm_substream *substream;2789 long result;2790 2791 pcm_oss_file = file->private_data;2792 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2793 if (substream == NULL)2794 return -ENXIO;2795 substream->f_flags = file->f_flags & O_NONBLOCK;2796 result = snd_pcm_oss_write1(substream, buf, count);2797#ifdef OSS_DEBUG2798 pcm_dbg(substream->pcm, "pcm_oss: write %li bytes (wrote %li bytes)\n",2799 (long)count, (long)result);2800#endif2801 return result;2802}2803 2804static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream)2805{2806 struct snd_pcm_runtime *runtime = substream->runtime;2807 if (atomic_read(&substream->mmap_count))2808 return runtime->oss.prev_hw_ptr_period !=2809 get_hw_ptr_period(runtime);2810 else2811 return snd_pcm_playback_avail(runtime) >=2812 runtime->oss.period_frames;2813}2814 2815static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)2816{2817 struct snd_pcm_runtime *runtime = substream->runtime;2818 if (atomic_read(&substream->mmap_count))2819 return runtime->oss.prev_hw_ptr_period !=2820 get_hw_ptr_period(runtime);2821 else2822 return snd_pcm_capture_avail(runtime) >=2823 runtime->oss.period_frames;2824}2825 2826static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait)2827{2828 struct snd_pcm_oss_file *pcm_oss_file;2829 __poll_t mask;2830 struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;2831 2832 pcm_oss_file = file->private_data;2833 2834 psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2835 csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2836 2837 mask = 0;2838 if (psubstream != NULL) {2839 struct snd_pcm_runtime *runtime = psubstream->runtime;2840 poll_wait(file, &runtime->sleep, wait);2841 scoped_guard(pcm_stream_lock_irq, psubstream) {2842 if (runtime->state != SNDRV_PCM_STATE_DRAINING &&2843 (runtime->state != SNDRV_PCM_STATE_RUNNING ||2844 snd_pcm_oss_playback_ready(psubstream)))2845 mask |= EPOLLOUT | EPOLLWRNORM;2846 }2847 }2848 if (csubstream != NULL) {2849 struct snd_pcm_runtime *runtime = csubstream->runtime;2850 snd_pcm_state_t ostate;2851 poll_wait(file, &runtime->sleep, wait);2852 scoped_guard(pcm_stream_lock_irq, csubstream) {2853 ostate = runtime->state;2854 if (ostate != SNDRV_PCM_STATE_RUNNING ||2855 snd_pcm_oss_capture_ready(csubstream))2856 mask |= EPOLLIN | EPOLLRDNORM;2857 }2858 if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {2859 struct snd_pcm_oss_file ofile;2860 memset(&ofile, 0, sizeof(ofile));2861 ofile.streams[SNDRV_PCM_STREAM_CAPTURE] = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2862 runtime->oss.trigger = 0;2863 snd_pcm_oss_set_trigger(&ofile, PCM_ENABLE_INPUT);2864 }2865 }2866 2867 return mask;2868}2869 2870static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)2871{2872 struct snd_pcm_oss_file *pcm_oss_file;2873 struct snd_pcm_substream *substream = NULL;2874 struct snd_pcm_runtime *runtime;2875 int err;2876 2877#ifdef OSS_DEBUG2878 pr_debug("pcm_oss: mmap begin\n");2879#endif2880 pcm_oss_file = file->private_data;2881 switch ((area->vm_flags & (VM_READ | VM_WRITE))) {2882 case VM_READ | VM_WRITE:2883 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2884 if (substream)2885 break;2886 fallthrough;2887 case VM_READ:2888 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];2889 break;2890 case VM_WRITE:2891 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];2892 break;2893 default:2894 return -EINVAL;2895 }2896 /* set VM_READ access as well to fix memset() routines that do2897 reads before writes (to improve performance) */2898 vm_flags_set(area, VM_READ);2899 if (substream == NULL)2900 return -ENXIO;2901 runtime = substream->runtime;2902 if (!(runtime->info & SNDRV_PCM_INFO_MMAP_VALID))2903 return -EIO;2904 if (runtime->info & SNDRV_PCM_INFO_INTERLEAVED)2905 runtime->access = SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;2906 else2907 return -EIO;2908 2909 if (runtime->oss.params) {2910 /* use mutex_trylock() for params_lock for avoiding a deadlock2911 * between mmap_lock and params_lock taken by2912 * copy_from/to_user() in snd_pcm_oss_write/read()2913 */2914 err = snd_pcm_oss_change_params(substream, true);2915 if (err < 0)2916 return err;2917 }2918#ifdef CONFIG_SND_PCM_OSS_PLUGINS2919 if (runtime->oss.plugin_first != NULL)2920 return -EIO;2921#endif2922 2923 if (area->vm_pgoff != 0)2924 return -EINVAL;2925 2926 err = snd_pcm_mmap_data(substream, file, area);2927 if (err < 0)2928 return err;2929 runtime->oss.mmap_bytes = area->vm_end - area->vm_start;2930 runtime->silence_threshold = 0;2931 runtime->silence_size = 0;2932#ifdef OSS_DEBUG2933 pr_debug("pcm_oss: mmap ok, bytes = 0x%x\n",2934 runtime->oss.mmap_bytes);2935#endif2936 /* In mmap mode we never stop */2937 runtime->stop_threshold = runtime->boundary;2938 2939 return 0;2940}2941 2942#ifdef CONFIG_SND_VERBOSE_PROCFS2943/*2944 * /proc interface2945 */2946 2947static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,2948 struct snd_info_buffer *buffer)2949{2950 struct snd_pcm_str *pstr = entry->private_data;2951 struct snd_pcm_oss_setup *setup = pstr->oss.setup_list;2952 guard(mutex)(&pstr->oss.setup_mutex);2953 while (setup) {2954 snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",2955 setup->task_name,2956 setup->periods,2957 setup->period_size,2958 setup->disable ? " disable" : "",2959 setup->direct ? " direct" : "",2960 setup->block ? " block" : "",2961 setup->nonblock ? " non-block" : "",2962 setup->partialfrag ? " partial-frag" : "",2963 setup->nosilence ? " no-silence" : "");2964 setup = setup->next;2965 }2966}2967 2968static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr)2969{2970 struct snd_pcm_oss_setup *setup, *setupn;2971 2972 for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;2973 setup; setup = setupn) {2974 setupn = setup->next;2975 kfree(setup->task_name);2976 kfree(setup);2977 }2978 pstr->oss.setup_list = NULL;2979}2980 2981static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,2982 struct snd_info_buffer *buffer)2983{2984 struct snd_pcm_str *pstr = entry->private_data;2985 char line[128], str[32], task_name[32];2986 const char *ptr;2987 int idx1;2988 struct snd_pcm_oss_setup *setup, *setup1, template;2989 2990 while (!snd_info_get_line(buffer, line, sizeof(line))) {2991 guard(mutex)(&pstr->oss.setup_mutex);2992 memset(&template, 0, sizeof(template));2993 ptr = snd_info_get_str(task_name, line, sizeof(task_name));2994 if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {2995 snd_pcm_oss_proc_free_setup_list(pstr);2996 continue;2997 }2998 for (setup = pstr->oss.setup_list; setup; setup = setup->next) {2999 if (!strcmp(setup->task_name, task_name)) {3000 template = *setup;3001 break;3002 }3003 }3004 ptr = snd_info_get_str(str, ptr, sizeof(str));3005 template.periods = simple_strtoul(str, NULL, 10);3006 ptr = snd_info_get_str(str, ptr, sizeof(str));3007 template.period_size = simple_strtoul(str, NULL, 10);3008 for (idx1 = 31; idx1 >= 0; idx1--)3009 if (template.period_size & (1 << idx1))3010 break;3011 for (idx1--; idx1 >= 0; idx1--)3012 template.period_size &= ~(1 << idx1);3013 do {3014 ptr = snd_info_get_str(str, ptr, sizeof(str));3015 if (!strcmp(str, "disable")) {3016 template.disable = 1;3017 } else if (!strcmp(str, "direct")) {3018 template.direct = 1;3019 } else if (!strcmp(str, "block")) {3020 template.block = 1;3021 } else if (!strcmp(str, "non-block")) {3022 template.nonblock = 1;3023 } else if (!strcmp(str, "partial-frag")) {3024 template.partialfrag = 1;3025 } else if (!strcmp(str, "no-silence")) {3026 template.nosilence = 1;3027 } else if (!strcmp(str, "buggy-ptr")) {3028 template.buggyptr = 1;3029 }3030 } while (*str);3031 if (setup == NULL) {3032 setup = kmalloc(sizeof(*setup), GFP_KERNEL);3033 if (! setup) {3034 buffer->error = -ENOMEM;3035 return;3036 }3037 if (pstr->oss.setup_list == NULL)3038 pstr->oss.setup_list = setup;3039 else {3040 for (setup1 = pstr->oss.setup_list;3041 setup1->next; setup1 = setup1->next);3042 setup1->next = setup;3043 }3044 template.task_name = kstrdup(task_name, GFP_KERNEL);3045 if (! template.task_name) {3046 kfree(setup);3047 buffer->error = -ENOMEM;3048 return;3049 }3050 }3051 *setup = template;3052 }3053}3054 3055static void snd_pcm_oss_proc_init(struct snd_pcm *pcm)3056{3057 int stream;3058 for (stream = 0; stream < 2; ++stream) {3059 struct snd_info_entry *entry;3060 struct snd_pcm_str *pstr = &pcm->streams[stream];3061 if (pstr->substream_count == 0)3062 continue;3063 entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root);3064 if (entry) {3065 entry->content = SNDRV_INFO_CONTENT_TEXT;3066 entry->mode = S_IFREG | 0644;3067 entry->c.text.read = snd_pcm_oss_proc_read;3068 entry->c.text.write = snd_pcm_oss_proc_write;3069 entry->private_data = pstr;3070 if (snd_info_register(entry) < 0) {3071 snd_info_free_entry(entry);3072 entry = NULL;3073 }3074 }3075 pstr->oss.proc_entry = entry;3076 }3077}3078 3079static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)3080{3081 int stream;3082 for (stream = 0; stream < 2; ++stream) {3083 struct snd_pcm_str *pstr = &pcm->streams[stream];3084 snd_info_free_entry(pstr->oss.proc_entry);3085 pstr->oss.proc_entry = NULL;3086 snd_pcm_oss_proc_free_setup_list(pstr);3087 }3088}3089#else /* !CONFIG_SND_VERBOSE_PROCFS */3090static inline void snd_pcm_oss_proc_init(struct snd_pcm *pcm)3091{3092}3093static inline void snd_pcm_oss_proc_done(struct snd_pcm *pcm)3094{3095}3096#endif /* CONFIG_SND_VERBOSE_PROCFS */3097 3098/*3099 * ENTRY functions3100 */3101 3102static const struct file_operations snd_pcm_oss_f_reg =3103{3104 .owner = THIS_MODULE,3105 .read = snd_pcm_oss_read,3106 .write = snd_pcm_oss_write,3107 .open = snd_pcm_oss_open,3108 .release = snd_pcm_oss_release,3109 .poll = snd_pcm_oss_poll,3110 .unlocked_ioctl = snd_pcm_oss_ioctl,3111 .compat_ioctl = snd_pcm_oss_ioctl_compat,3112 .mmap = snd_pcm_oss_mmap,3113};3114 3115static void register_oss_dsp(struct snd_pcm *pcm, int index)3116{3117 if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,3118 pcm->card, index, &snd_pcm_oss_f_reg,3119 pcm) < 0) {3120 pcm_err(pcm, "unable to register OSS PCM device %i:%i\n",3121 pcm->card->number, pcm->device);3122 }3123}3124 3125static int snd_pcm_oss_register_minor(struct snd_pcm *pcm)3126{3127 pcm->oss.reg = 0;3128 if (dsp_map[pcm->card->number] == (int)pcm->device) {3129 char name[128];3130 int duplex;3131 register_oss_dsp(pcm, 0);3132 duplex = (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count > 0 && 3133 pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count && 3134 !(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX));3135 sprintf(name, "%s%s", pcm->name, duplex ? " (DUPLEX)" : "");3136#ifdef SNDRV_OSS_INFO_DEV_AUDIO3137 snd_oss_info_register(SNDRV_OSS_INFO_DEV_AUDIO,3138 pcm->card->number,3139 name);3140#endif3141 pcm->oss.reg++;3142 pcm->oss.reg_mask |= 1;3143 }3144 if (adsp_map[pcm->card->number] == (int)pcm->device) {3145 register_oss_dsp(pcm, 1);3146 pcm->oss.reg++;3147 pcm->oss.reg_mask |= 2;3148 }3149 3150 if (pcm->oss.reg)3151 snd_pcm_oss_proc_init(pcm);3152 3153 return 0;3154}3155 3156static int snd_pcm_oss_disconnect_minor(struct snd_pcm *pcm)3157{3158 if (pcm->oss.reg) {3159 if (pcm->oss.reg_mask & 1) {3160 pcm->oss.reg_mask &= ~1;3161 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,3162 pcm->card, 0);3163 }3164 if (pcm->oss.reg_mask & 2) {3165 pcm->oss.reg_mask &= ~2;3166 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,3167 pcm->card, 1);3168 }3169 if (dsp_map[pcm->card->number] == (int)pcm->device) {3170#ifdef SNDRV_OSS_INFO_DEV_AUDIO3171 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);3172#endif3173 }3174 pcm->oss.reg = 0;3175 }3176 return 0;3177}3178 3179static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)3180{3181 snd_pcm_oss_disconnect_minor(pcm);3182 snd_pcm_oss_proc_done(pcm);3183 return 0;3184}3185 3186static struct snd_pcm_notify snd_pcm_oss_notify =3187{3188 .n_register = snd_pcm_oss_register_minor,3189 .n_disconnect = snd_pcm_oss_disconnect_minor,3190 .n_unregister = snd_pcm_oss_unregister_minor,3191};3192 3193static int __init alsa_pcm_oss_init(void)3194{3195 int i;3196 int err;3197 3198 /* check device map table */3199 for (i = 0; i < SNDRV_CARDS; i++) {3200 if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) {3201 pr_err("ALSA: pcm_oss: invalid dsp_map[%d] = %d\n",3202 i, dsp_map[i]);3203 dsp_map[i] = 0;3204 }3205 if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) {3206 pr_err("ALSA: pcm_oss: invalid adsp_map[%d] = %d\n",3207 i, adsp_map[i]);3208 adsp_map[i] = 1;3209 }3210 }3211 err = snd_pcm_notify(&snd_pcm_oss_notify, 0);3212 if (err < 0)3213 return err;3214 return 0;3215}3216 3217static void __exit alsa_pcm_oss_exit(void)3218{3219 snd_pcm_notify(&snd_pcm_oss_notify, 1);3220}3221 3222module_init(alsa_pcm_oss_init)3223module_exit(alsa_pcm_oss_exit)3224