brintos

brintos / linux-shallow public Read only

0
0
Text · 86.4 KiB · 4683b91 Raw
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