11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/sound/oss/dmasound/dmasound_core.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for
61da177e4SLinus Torvalds  *  Linux/m68k
71da177e4SLinus Torvalds  *  Extended to support Power Macintosh for Linux/ppc by Paul Mackerras
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  (c) 1995 by Michael Schlueter & Michael Marte
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *  Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
121da177e4SLinus Torvalds  *  interface and the u-law to signed byte conversion.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
151da177e4SLinus Torvalds  *  /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
161da177e4SLinus Torvalds  *  to thank:
171da177e4SLinus Torvalds  *    - Michael Schlueter for initial ideas and documentation on the MFP and
181da177e4SLinus Torvalds  *	the DMA sound hardware.
191da177e4SLinus Torvalds  *    - Therapy? for their CD 'Troublegum' which really made me rock.
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  *  /dev/sndstat is based on code by Hannu Savolainen, the author of the
221da177e4SLinus Torvalds  *  VoxWare family of drivers.
231da177e4SLinus Torvalds  *
241da177e4SLinus Torvalds  *  This file is subject to the terms and conditions of the GNU General Public
251da177e4SLinus Torvalds  *  License.  See the file COPYING in the main directory of this archive
261da177e4SLinus Torvalds  *  for more details.
271da177e4SLinus Torvalds  *
281da177e4SLinus Torvalds  *  History:
291da177e4SLinus Torvalds  *
301da177e4SLinus Torvalds  *	1995/8/25	First release
311da177e4SLinus Torvalds  *
321da177e4SLinus Torvalds  *	1995/9/02	Roman Hodek:
331da177e4SLinus Torvalds  *			  - Fixed atari_stram_alloc() call, the timer
341da177e4SLinus Torvalds  *			    programming and several race conditions
351da177e4SLinus Torvalds  *	1995/9/14	Roman Hodek:
361da177e4SLinus Torvalds  *			  - After some discussion with Michael Schlueter,
371da177e4SLinus Torvalds  *			    revised the interrupt disabling
381da177e4SLinus Torvalds  *			  - Slightly speeded up U8->S8 translation by using
391da177e4SLinus Torvalds  *			    long operations where possible
401da177e4SLinus Torvalds  *			  - Added 4:3 interpolation for /dev/audio
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  *	1995/9/20	Torsten Scherer:
431da177e4SLinus Torvalds  *			  - Fixed a bug in sq_write and changed /dev/audio
441da177e4SLinus Torvalds  *			    converting to play at 12517Hz instead of 6258Hz.
451da177e4SLinus Torvalds  *
461da177e4SLinus Torvalds  *	1995/9/23	Torsten Scherer:
471da177e4SLinus Torvalds  *			  - Changed sq_interrupt() and sq_play() to pre-program
481da177e4SLinus Torvalds  *			    the DMA for another frame while there's still one
491da177e4SLinus Torvalds  *			    running. This allows the IRQ response to be
501da177e4SLinus Torvalds  *			    arbitrarily delayed and playing will still continue.
511da177e4SLinus Torvalds  *
521da177e4SLinus Torvalds  *	1995/10/14	Guenther Kelleter, Torsten Scherer:
531da177e4SLinus Torvalds  *			  - Better support for Falcon audio (the Falcon doesn't
541da177e4SLinus Torvalds  *			    raise an IRQ at the end of a frame, but at the
551da177e4SLinus Torvalds  *			    beginning instead!). uses 'if (codec_dma)' in lots
561da177e4SLinus Torvalds  *			    of places to simply switch between Falcon and TT
571da177e4SLinus Torvalds  *			    code.
581da177e4SLinus Torvalds  *
591da177e4SLinus Torvalds  *	1995/11/06	Torsten Scherer:
601da177e4SLinus Torvalds  *			  - Started introducing a hardware abstraction scheme
611da177e4SLinus Torvalds  *			    (may perhaps also serve for Amigas?)
621da177e4SLinus Torvalds  *			  - Can now play samples at almost all frequencies by
631da177e4SLinus Torvalds  *			    means of a more generalized expand routine
641da177e4SLinus Torvalds  *			  - Takes a good deal of care to cut data only at
651da177e4SLinus Torvalds  *			    sample sizes
661da177e4SLinus Torvalds  *			  - Buffer size is now a kernel runtime option
671da177e4SLinus Torvalds  *			  - Implemented fsync() & several minor improvements
681da177e4SLinus Torvalds  *			Guenther Kelleter:
691da177e4SLinus Torvalds  *			  - Useful hints and bug fixes
701da177e4SLinus Torvalds  *			  - Cross-checked it for Falcons
711da177e4SLinus Torvalds  *
721da177e4SLinus Torvalds  *	1996/3/9	Geert Uytterhoeven:
731da177e4SLinus Torvalds  *			  - Support added for Amiga, A-law, 16-bit little
741da177e4SLinus Torvalds  *			    endian.
751da177e4SLinus Torvalds  *			  - Unification to drivers/sound/dmasound.c.
761da177e4SLinus Torvalds  *
771da177e4SLinus Torvalds  *	1996/4/6	Martin Mitchell:
781da177e4SLinus Torvalds  *			  - Updated to 1.3 kernel.
791da177e4SLinus Torvalds  *
801da177e4SLinus Torvalds  *	1996/6/13       Topi Kanerva:
811da177e4SLinus Torvalds  *			  - Fixed things that were broken (mainly the amiga
821da177e4SLinus Torvalds  *			    14-bit routines)
831da177e4SLinus Torvalds  *			  - /dev/sndstat shows now the real hardware frequency
841da177e4SLinus Torvalds  *			  - The lowpass filter is disabled by default now
851da177e4SLinus Torvalds  *
861da177e4SLinus Torvalds  *	1996/9/25	Geert Uytterhoeven:
871da177e4SLinus Torvalds  *			  - Modularization
881da177e4SLinus Torvalds  *
891da177e4SLinus Torvalds  *	1998/6/10	Andreas Schwab:
901da177e4SLinus Torvalds  *			  - Converted to use sound_core
911da177e4SLinus Torvalds  *
921da177e4SLinus Torvalds  *	1999/12/28	Richard Zidlicky:
931da177e4SLinus Torvalds  *			  - Added support for Q40
941da177e4SLinus Torvalds  *
951da177e4SLinus Torvalds  *	2000/2/27	Geert Uytterhoeven:
961da177e4SLinus Torvalds  *			  - Clean up and split the code into 4 parts:
971da177e4SLinus Torvalds  *			      o dmasound_core: machine-independent code
981da177e4SLinus Torvalds  *			      o dmasound_atari: Atari TT and Falcon support
991da177e4SLinus Torvalds  *			      o dmasound_awacs: Apple PowerMac support
1001da177e4SLinus Torvalds  *			      o dmasound_paula: Amiga support
1011da177e4SLinus Torvalds  *
1021da177e4SLinus Torvalds  *	2000/3/25	Geert Uytterhoeven:
1031da177e4SLinus Torvalds  *			  - Integration of dmasound_q40
1041da177e4SLinus Torvalds  *			  - Small clean ups
1051da177e4SLinus Torvalds  *
1061da177e4SLinus Torvalds  *	2001/01/26 [1.0] Iain Sandoe
1071da177e4SLinus Torvalds  *			  - make /dev/sndstat show revision & edition info.
1081da177e4SLinus Torvalds  *			  - since dmasound.mach.sq_setup() can fail on pmac
1091da177e4SLinus Torvalds  *			    its type has been changed to int and the returns
1101da177e4SLinus Torvalds  *			    are checked.
1111da177e4SLinus Torvalds  *		   [1.1]  - stop missing translations from being called.
1121da177e4SLinus Torvalds  *	2001/02/08 [1.2]  - remove unused translation tables & move machine-
1131da177e4SLinus Torvalds  *			    specific tables to low-level.
1141da177e4SLinus Torvalds  *			  - return correct info. for SNDCTL_DSP_GETFMTS.
1151da177e4SLinus Torvalds  *		   [1.3]  - implement SNDCTL_DSP_GETCAPS fully.
1161da177e4SLinus Torvalds  *		   [1.4]  - make /dev/sndstat text length usage deterministic.
1171da177e4SLinus Torvalds  *			  - make /dev/sndstat call to low-level
1181da177e4SLinus Torvalds  *			    dmasound.mach.state_info() pass max space to ll driver.
1191da177e4SLinus Torvalds  *			  - tidy startup banners and output info.
1201da177e4SLinus Torvalds  *		   [1.5]  - tidy up a little (removed some unused #defines in
1211da177e4SLinus Torvalds  *			    dmasound.h)
1221da177e4SLinus Torvalds  *			  - fix up HAS_RECORD conditionalisation.
1231da177e4SLinus Torvalds  *			  - add record code in places it is missing...
1241da177e4SLinus Torvalds  *			  - change buf-sizes to bytes to allow < 1kb for pmac
1251da177e4SLinus Torvalds  *			    if user param entry is < 256 the value is taken to
1261da177e4SLinus Torvalds  *			    be in kb > 256 is taken to be in bytes.
1271da177e4SLinus Torvalds  *			  - make default buff/frag params conditional on
1281da177e4SLinus Torvalds  *			    machine to allow smaller values for pmac.
1291da177e4SLinus Torvalds  *			  - made the ioctls, read & write comply with the OSS
1301da177e4SLinus Torvalds  *			    rules on setting params.
1311da177e4SLinus Torvalds  *			  - added parsing of _setup() params for record.
1321da177e4SLinus Torvalds  *	2001/04/04 [1.6]  - fix bug where sample rates higher than maximum were
1331da177e4SLinus Torvalds  *			    being reported as OK.
1341da177e4SLinus Torvalds  *			  - fix open() to return -EBUSY as per OSS doc. when
1351da177e4SLinus Torvalds  *			    audio is in use - this is independent of O_NOBLOCK.
1361da177e4SLinus Torvalds  *			  - fix bug where SNDCTL_DSP_POST was blocking.
1371da177e4SLinus Torvalds  */
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds  /* Record capability notes 30/01/2001:
1401da177e4SLinus Torvalds   * At present these observations apply only to pmac LL driver (the only one
1411da177e4SLinus Torvalds   * that can do record, at present).  However, if other LL drivers for machines
1421da177e4SLinus Torvalds   * with record are added they may apply.
1431da177e4SLinus Torvalds   *
1441da177e4SLinus Torvalds   * The fragment parameters for the record and play channels are separate.
1451da177e4SLinus Torvalds   * However, if the driver is opened O_RDWR there is no way (in the current OSS
1461da177e4SLinus Torvalds   * API) to specify their values independently for the record and playback
1471da177e4SLinus Torvalds   * channels.  Since the only common factor between the input & output is the
1481da177e4SLinus Torvalds   * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and
1491da177e4SLinus Torvalds   * /dev/dspY O_RDONLY.  The input & output channels could then have different
1501da177e4SLinus Torvalds   * characteristics (other than the first that sets sample rate claiming the
1511da177e4SLinus Torvalds   * right to set it for ever).  As it stands, the format, channels, number of
1521da177e4SLinus Torvalds   * bits & sample rate are assumed to be common.  In the future perhaps these
1531da177e4SLinus Torvalds   * should be the responsibility of the LL driver - and then if a card really
1541da177e4SLinus Torvalds   * does not share items between record & playback they can be specified
1551da177e4SLinus Torvalds   * separately.
1561da177e4SLinus Torvalds */
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds /* Thread-safeness of shared_resources notes: 31/01/2001
1591da177e4SLinus Torvalds  * If the user opens O_RDWR and then splits record & play between two threads
1601da177e4SLinus Torvalds  * both of which inherit the fd - and then starts changing things from both
1611da177e4SLinus Torvalds  * - we will have difficulty telling.
1621da177e4SLinus Torvalds  *
1631da177e4SLinus Torvalds  * It's bad application coding - but ...
1641da177e4SLinus Torvalds  * TODO: think about how to sort this out... without bogging everything down in
1651da177e4SLinus Torvalds  * semaphores.
1661da177e4SLinus Torvalds  *
1671da177e4SLinus Torvalds  * Similarly, the OSS spec says "all changes to parameters must be between
1681da177e4SLinus Torvalds  * open() and the first read() or write(). - and a bit later on (by
1691da177e4SLinus Torvalds  * implication) "between SNDCTL_DSP_RESET and the first read() or write() after
1701da177e4SLinus Torvalds  * it".  If the app is multi-threaded and this rule is broken between threads
1711da177e4SLinus Torvalds  * we will have trouble spotting it - and the fault will be rather obscure :-(
1721da177e4SLinus Torvalds  *
1731da177e4SLinus Torvalds  * We will try and put out at least a kmsg if we see it happen... but I think
1741da177e4SLinus Torvalds  * it will be quite hard to trap it with an -EXXX return... because we can't
1751da177e4SLinus Torvalds  * see the fault until after the damage is done.
1761da177e4SLinus Torvalds */
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds #include <linux/module.h>
1791da177e4SLinus Torvalds #include <linux/slab.h>
1801da177e4SLinus Torvalds #include <linux/sound.h>
1811da177e4SLinus Torvalds #include <linux/init.h>
1821da177e4SLinus Torvalds #include <linux/soundcard.h>
1831da177e4SLinus Torvalds #include <linux/poll.h>
184645ef9efSArnd Bergmann #include <linux/mutex.h>
185174cd4b1SIngo Molnar #include <linux/sched/signal.h>
1861da177e4SLinus Torvalds 
1877c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds #include "dmasound.h"
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds #define DMASOUND_CORE_REVISION 1
1921da177e4SLinus Torvalds #define DMASOUND_CORE_EDITION 6
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds     /*
1951da177e4SLinus Torvalds      *  Declarations
1961da177e4SLinus Torvalds      */
1971da177e4SLinus Torvalds 
198645ef9efSArnd Bergmann static DEFINE_MUTEX(dmasound_core_mutex);
1991da177e4SLinus Torvalds int dmasound_catchRadius = 0;
2008d3b33f6SRusty Russell module_param(dmasound_catchRadius, int, 0);
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;
2038d3b33f6SRusty Russell module_param(numWriteBufs, int, 0);
2041da177e4SLinus Torvalds static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ;	/* in bytes */
2058d3b33f6SRusty Russell module_param(writeBufSize, int, 0);
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds static int sq_unit = -1;
2101da177e4SLinus Torvalds static int mixer_unit = -1;
2111da177e4SLinus Torvalds static int state_unit = -1;
2121da177e4SLinus Torvalds static int irq_installed;
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds /* control over who can modify resources shared between play/record */
215aeb5d727SAl Viro static fmode_t shared_resource_owner;
2161da177e4SLinus Torvalds static int shared_resources_initialised;
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds     /*
2191da177e4SLinus Torvalds      *  Mid level stuff
2201da177e4SLinus Torvalds      */
2211da177e4SLinus Torvalds 
22270edc800SThomas Gleixner struct sound_settings dmasound = {
22370edc800SThomas Gleixner 	.lock = __SPIN_LOCK_UNLOCKED(dmasound.lock)
22470edc800SThomas Gleixner };
2251da177e4SLinus Torvalds 
sound_silence(void)2261da177e4SLinus Torvalds static inline void sound_silence(void)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	dmasound.mach.silence(); /* _MUST_ stop DMA */
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
sound_set_format(int format)2311da177e4SLinus Torvalds static inline int sound_set_format(int format)
2321da177e4SLinus Torvalds {
2331da177e4SLinus Torvalds 	return dmasound.mach.setFormat(format);
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 
sound_set_speed(int speed)2371da177e4SLinus Torvalds static int sound_set_speed(int speed)
2381da177e4SLinus Torvalds {
2391da177e4SLinus Torvalds 	if (speed < 0)
2401da177e4SLinus Torvalds 		return dmasound.soft.speed;
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 	/* trap out-of-range speed settings.
2431da177e4SLinus Torvalds 	   at present we allow (arbitrarily) low rates - using soft
2441da177e4SLinus Torvalds 	   up-conversion - but we can't allow > max because there is
2451da177e4SLinus Torvalds 	   no soft down-conversion.
2461da177e4SLinus Torvalds 	*/
2471da177e4SLinus Torvalds 	if (dmasound.mach.max_dsp_speed &&
2481da177e4SLinus Torvalds 	   (speed > dmasound.mach.max_dsp_speed))
2491da177e4SLinus Torvalds 		speed = dmasound.mach.max_dsp_speed ;
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	dmasound.soft.speed = speed;
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	if (dmasound.minDev == SND_DEV_DSP)
2541da177e4SLinus Torvalds 		dmasound.dsp.speed = dmasound.soft.speed;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	return dmasound.soft.speed;
2571da177e4SLinus Torvalds }
2581da177e4SLinus Torvalds 
sound_set_stereo(int stereo)2591da177e4SLinus Torvalds static int sound_set_stereo(int stereo)
2601da177e4SLinus Torvalds {
2611da177e4SLinus Torvalds 	if (stereo < 0)
2621da177e4SLinus Torvalds 		return dmasound.soft.stereo;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	stereo = !!stereo;    /* should be 0 or 1 now */
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	dmasound.soft.stereo = stereo;
2671da177e4SLinus Torvalds 	if (dmasound.minDev == SND_DEV_DSP)
2681da177e4SLinus Torvalds 		dmasound.dsp.stereo = stereo;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	return stereo;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds 
sound_copy_translate(TRANS * trans,const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)2731da177e4SLinus Torvalds static ssize_t sound_copy_translate(TRANS *trans, const u_char __user *userPtr,
2741da177e4SLinus Torvalds 				    size_t userCount, u_char frame[],
2751da177e4SLinus Torvalds 				    ssize_t *frameUsed, ssize_t frameLeft)
2761da177e4SLinus Torvalds {
2771da177e4SLinus Torvalds 	ssize_t (*ct_func)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 	switch (dmasound.soft.format) {
2801da177e4SLinus Torvalds 	    case AFMT_MU_LAW:
2811da177e4SLinus Torvalds 		ct_func = trans->ct_ulaw;
2821da177e4SLinus Torvalds 		break;
2831da177e4SLinus Torvalds 	    case AFMT_A_LAW:
2841da177e4SLinus Torvalds 		ct_func = trans->ct_alaw;
2851da177e4SLinus Torvalds 		break;
2861da177e4SLinus Torvalds 	    case AFMT_S8:
2871da177e4SLinus Torvalds 		ct_func = trans->ct_s8;
2881da177e4SLinus Torvalds 		break;
2891da177e4SLinus Torvalds 	    case AFMT_U8:
2901da177e4SLinus Torvalds 		ct_func = trans->ct_u8;
2911da177e4SLinus Torvalds 		break;
2921da177e4SLinus Torvalds 	    case AFMT_S16_BE:
2931da177e4SLinus Torvalds 		ct_func = trans->ct_s16be;
2941da177e4SLinus Torvalds 		break;
2951da177e4SLinus Torvalds 	    case AFMT_U16_BE:
2961da177e4SLinus Torvalds 		ct_func = trans->ct_u16be;
2971da177e4SLinus Torvalds 		break;
2981da177e4SLinus Torvalds 	    case AFMT_S16_LE:
2991da177e4SLinus Torvalds 		ct_func = trans->ct_s16le;
3001da177e4SLinus Torvalds 		break;
3011da177e4SLinus Torvalds 	    case AFMT_U16_LE:
3021da177e4SLinus Torvalds 		ct_func = trans->ct_u16le;
3031da177e4SLinus Torvalds 		break;
3041da177e4SLinus Torvalds 	    default:
3051da177e4SLinus Torvalds 		return 0;
3061da177e4SLinus Torvalds 	}
3071da177e4SLinus Torvalds 	/* if the user has requested a non-existent translation don't try
3081da177e4SLinus Torvalds 	   to call it but just return 0 bytes moved
3091da177e4SLinus Torvalds 	*/
3101da177e4SLinus Torvalds 	if (ct_func)
3111da177e4SLinus Torvalds 		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
3121da177e4SLinus Torvalds 	return 0;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds     /*
3161da177e4SLinus Torvalds      *  /dev/mixer abstraction
3171da177e4SLinus Torvalds      */
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds static struct {
3201da177e4SLinus Torvalds     int busy;
3211da177e4SLinus Torvalds     int modify_counter;
3221da177e4SLinus Torvalds } mixer;
3231da177e4SLinus Torvalds 
mixer_open(struct inode * inode,struct file * file)3241da177e4SLinus Torvalds static int mixer_open(struct inode *inode, struct file *file)
3251da177e4SLinus Torvalds {
326645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
32790dc763fSArnd Bergmann 	if (!try_module_get(dmasound.mach.owner)) {
328645ef9efSArnd Bergmann 		mutex_unlock(&dmasound_core_mutex);
3291da177e4SLinus Torvalds 		return -ENODEV;
33090dc763fSArnd Bergmann 	}
3311da177e4SLinus Torvalds 	mixer.busy = 1;
332645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
3331da177e4SLinus Torvalds 	return 0;
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
mixer_release(struct inode * inode,struct file * file)3361da177e4SLinus Torvalds static int mixer_release(struct inode *inode, struct file *file)
3371da177e4SLinus Torvalds {
338645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
3391da177e4SLinus Torvalds 	mixer.busy = 0;
3401da177e4SLinus Torvalds 	module_put(dmasound.mach.owner);
341645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
3421da177e4SLinus Torvalds 	return 0;
3431da177e4SLinus Torvalds }
344d209974cSArnd Bergmann 
mixer_ioctl(struct file * file,u_int cmd,u_long arg)345d209974cSArnd Bergmann static int mixer_ioctl(struct file *file, u_int cmd, u_long arg)
3461da177e4SLinus Torvalds {
3471da177e4SLinus Torvalds 	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
3481da177e4SLinus Torvalds 	    mixer.modify_counter++;
3491da177e4SLinus Torvalds 	switch (cmd) {
3501da177e4SLinus Torvalds 	    case OSS_GETVERSION:
3511da177e4SLinus Torvalds 		return IOCTL_OUT(arg, SOUND_VERSION);
3521da177e4SLinus Torvalds 	    case SOUND_MIXER_INFO:
3531da177e4SLinus Torvalds 		{
3541da177e4SLinus Torvalds 		    mixer_info info;
3551da177e4SLinus Torvalds 		    memset(&info, 0, sizeof(info));
35675b1a8f9SJoe Perches 		    strscpy(info.id, dmasound.mach.name2, sizeof(info.id));
35775b1a8f9SJoe Perches 		    strscpy(info.name, dmasound.mach.name2, sizeof(info.name));
3581da177e4SLinus Torvalds 		    info.modify_counter = mixer.modify_counter;
3591da177e4SLinus Torvalds 		    if (copy_to_user((void __user *)arg, &info, sizeof(info)))
3601da177e4SLinus Torvalds 			    return -EFAULT;
3611da177e4SLinus Torvalds 		    return 0;
3621da177e4SLinus Torvalds 		}
3631da177e4SLinus Torvalds 	}
3641da177e4SLinus Torvalds 	if (dmasound.mach.mixer_ioctl)
3651da177e4SLinus Torvalds 	    return dmasound.mach.mixer_ioctl(cmd, arg);
3661da177e4SLinus Torvalds 	return -EINVAL;
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
mixer_unlocked_ioctl(struct file * file,u_int cmd,u_long arg)369d209974cSArnd Bergmann static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
370d209974cSArnd Bergmann {
371d209974cSArnd Bergmann 	int ret;
372d209974cSArnd Bergmann 
373645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
374d209974cSArnd Bergmann 	ret = mixer_ioctl(file, cmd, arg);
375645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
376d209974cSArnd Bergmann 
377d209974cSArnd Bergmann 	return ret;
378d209974cSArnd Bergmann }
379d209974cSArnd Bergmann 
3809c2e08c5SArjan van de Ven static const struct file_operations mixer_fops =
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
3831da177e4SLinus Torvalds 	.llseek		= no_llseek,
384d209974cSArnd Bergmann 	.unlocked_ioctl	= mixer_unlocked_ioctl,
3852022ca0aSArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
3861da177e4SLinus Torvalds 	.open		= mixer_open,
3871da177e4SLinus Torvalds 	.release	= mixer_release,
3881da177e4SLinus Torvalds };
3891da177e4SLinus Torvalds 
mixer_init(void)3901da177e4SLinus Torvalds static void mixer_init(void)
3911da177e4SLinus Torvalds {
3921da177e4SLinus Torvalds 	mixer_unit = register_sound_mixer(&mixer_fops, -1);
3931da177e4SLinus Torvalds 	if (mixer_unit < 0)
3941da177e4SLinus Torvalds 		return;
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	mixer.busy = 0;
3971da177e4SLinus Torvalds 	dmasound.treble = 0;
3981da177e4SLinus Torvalds 	dmasound.bass = 0;
3991da177e4SLinus Torvalds 	if (dmasound.mach.mixer_init)
4001da177e4SLinus Torvalds 	    dmasound.mach.mixer_init();
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 
4041da177e4SLinus Torvalds     /*
4051da177e4SLinus Torvalds      *  Sound queue stuff, the heart of the driver
4061da177e4SLinus Torvalds      */
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds struct sound_queue dmasound_write_sq;
4091da177e4SLinus Torvalds static void sq_reset_output(void) ;
4101da177e4SLinus Torvalds 
sq_allocate_buffers(struct sound_queue * sq,int num,int size)4111da177e4SLinus Torvalds static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds 	int i;
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	if (sq->buffers)
4161da177e4SLinus Torvalds 		return 0;
4171da177e4SLinus Torvalds 	sq->numBufs = num;
4181da177e4SLinus Torvalds 	sq->bufSize = size;
4196da2ec56SKees Cook 	sq->buffers = kmalloc_array (num, sizeof(char *), GFP_KERNEL);
4201da177e4SLinus Torvalds 	if (!sq->buffers)
4211da177e4SLinus Torvalds 		return -ENOMEM;
4221da177e4SLinus Torvalds 	for (i = 0; i < num; i++) {
4231da177e4SLinus Torvalds 		sq->buffers[i] = dmasound.mach.dma_alloc(size, GFP_KERNEL);
4241da177e4SLinus Torvalds 		if (!sq->buffers[i]) {
4251da177e4SLinus Torvalds 			while (i--)
4261da177e4SLinus Torvalds 				dmasound.mach.dma_free(sq->buffers[i], size);
4271da177e4SLinus Torvalds 			kfree(sq->buffers);
4281da177e4SLinus Torvalds 			sq->buffers = NULL;
4291da177e4SLinus Torvalds 			return -ENOMEM;
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds 	}
4321da177e4SLinus Torvalds 	return 0;
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
sq_release_buffers(struct sound_queue * sq)4351da177e4SLinus Torvalds static void sq_release_buffers(struct sound_queue *sq)
4361da177e4SLinus Torvalds {
4371da177e4SLinus Torvalds 	int i;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	if (sq->buffers) {
4401da177e4SLinus Torvalds 		for (i = 0; i < sq->numBufs; i++)
4411da177e4SLinus Torvalds 			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
4421da177e4SLinus Torvalds 		kfree(sq->buffers);
4431da177e4SLinus Torvalds 		sq->buffers = NULL;
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 
sq_setup(struct sound_queue * sq)4481da177e4SLinus Torvalds static int sq_setup(struct sound_queue *sq)
4491da177e4SLinus Torvalds {
4501da177e4SLinus Torvalds 	int (*setup_func)(void) = NULL;
4511da177e4SLinus Torvalds 	int hard_frame ;
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	if (sq->locked) { /* are we already set? - and not changeable */
4541da177e4SLinus Torvalds #ifdef DEBUG_DMASOUND
4551da177e4SLinus Torvalds printk("dmasound_core: tried to sq_setup a locked queue\n") ;
4561da177e4SLinus Torvalds #endif
4571da177e4SLinus Torvalds 		return -EINVAL ;
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 	sq->locked = 1 ; /* don't think we have a race prob. here _check_ */
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	/* make sure that the parameters are set up
4621da177e4SLinus Torvalds 	   This should have been done already...
4631da177e4SLinus Torvalds 	*/
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	dmasound.mach.init();
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	/* OK.  If the user has set fragment parameters explicitly, then we
4681da177e4SLinus Torvalds 	   should leave them alone... as long as they are valid.
4691da177e4SLinus Torvalds 	   Invalid user fragment params can occur if we allow the whole buffer
4701da177e4SLinus Torvalds 	   to be used when the user requests the fragments sizes (with no soft
4711da177e4SLinus Torvalds 	   x-lation) and then the user subsequently sets a soft x-lation that
4721da177e4SLinus Torvalds 	   requires increased internal buffering.
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	   Othwerwise (if the user did not set them) OSS says that we should
4751da177e4SLinus Torvalds 	   select frag params on the basis of 0.5 s output & 0.1 s input
4761da177e4SLinus Torvalds 	   latency. (TODO.  For now we will copy in the defaults.)
4771da177e4SLinus Torvalds 	*/
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	if (sq->user_frags <= 0) {
4801da177e4SLinus Torvalds 		sq->max_count = sq->numBufs ;
4811da177e4SLinus Torvalds 		sq->max_active = sq->numBufs ;
4821da177e4SLinus Torvalds 		sq->block_size = sq->bufSize;
4831da177e4SLinus Torvalds 		/* set up the user info */
4841da177e4SLinus Torvalds 		sq->user_frags = sq->numBufs ;
4851da177e4SLinus Torvalds 		sq->user_frag_size = sq->bufSize ;
4861da177e4SLinus Torvalds 		sq->user_frag_size *=
4871da177e4SLinus Torvalds 			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
4881da177e4SLinus Torvalds 		sq->user_frag_size /=
4891da177e4SLinus Torvalds 			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
4901da177e4SLinus Torvalds 	} else {
4911da177e4SLinus Torvalds 		/* work out requested block size */
4921da177e4SLinus Torvalds 		sq->block_size = sq->user_frag_size ;
4931da177e4SLinus Torvalds 		sq->block_size *=
4941da177e4SLinus Torvalds 			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
4951da177e4SLinus Torvalds 		sq->block_size /=
4961da177e4SLinus Torvalds 			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
4971da177e4SLinus Torvalds 		/* the user wants to write frag-size chunks */
4981da177e4SLinus Torvalds 		sq->block_size *= dmasound.hard.speed ;
4991da177e4SLinus Torvalds 		sq->block_size /= dmasound.soft.speed ;
5001da177e4SLinus Torvalds 		/* this only works for size values which are powers of 2 */
5011da177e4SLinus Torvalds 		hard_frame =
5021da177e4SLinus Torvalds 			(dmasound.hard.size * (dmasound.hard.stereo+1))/8 ;
5031da177e4SLinus Torvalds 		sq->block_size +=  (hard_frame - 1) ;
5041da177e4SLinus Torvalds 		sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */
5051da177e4SLinus Torvalds 		/* let's just check for obvious mistakes */
5061da177e4SLinus Torvalds 		if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {
5071da177e4SLinus Torvalds #ifdef DEBUG_DMASOUND
5081da177e4SLinus Torvalds printk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;
5091da177e4SLinus Torvalds #endif
5101da177e4SLinus Torvalds 			sq->block_size = sq->bufSize ;
5111da177e4SLinus Torvalds 		}
5121da177e4SLinus Torvalds 		if ( sq->user_frags <= sq->numBufs ) {
5131da177e4SLinus Torvalds 			sq->max_count = sq->user_frags ;
5141da177e4SLinus Torvalds 			/* if user has set max_active - then use it */
5151da177e4SLinus Torvalds 			sq->max_active = (sq->max_active <= sq->max_count) ?
5161da177e4SLinus Torvalds 				sq->max_active : sq->max_count ;
5171da177e4SLinus Torvalds 		} else {
5181da177e4SLinus Torvalds #ifdef DEBUG_DMASOUND
5191da177e4SLinus Torvalds printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
5201da177e4SLinus Torvalds #endif
5211da177e4SLinus Torvalds 			sq->max_count =
5221da177e4SLinus Torvalds 			sq->max_active = sq->numBufs ;
5231da177e4SLinus Torvalds 		}
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds 	sq->front = sq->count = sq->rear_size = 0;
5261da177e4SLinus Torvalds 	sq->syncing = 0;
5271da177e4SLinus Torvalds 	sq->active = 0;
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds 	if (sq == &write_sq) {
5301da177e4SLinus Torvalds 	    sq->rear = -1;
5311da177e4SLinus Torvalds 	    setup_func = dmasound.mach.write_sq_setup;
5321da177e4SLinus Torvalds 	}
5331da177e4SLinus Torvalds 	if (setup_func)
5341da177e4SLinus Torvalds 	    return setup_func();
5351da177e4SLinus Torvalds 	return 0 ;
5361da177e4SLinus Torvalds }
5371da177e4SLinus Torvalds 
sq_play(void)5381da177e4SLinus Torvalds static inline void sq_play(void)
5391da177e4SLinus Torvalds {
5401da177e4SLinus Torvalds 	dmasound.mach.play();
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
sq_write(struct file * file,const char __user * src,size_t uLeft,loff_t * ppos)5431da177e4SLinus Torvalds static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
5441da177e4SLinus Torvalds 			loff_t *ppos)
5451da177e4SLinus Torvalds {
5461da177e4SLinus Torvalds 	ssize_t uWritten = 0;
5471da177e4SLinus Torvalds 	u_char *dest;
5481da177e4SLinus Torvalds 	ssize_t uUsed = 0, bUsed, bLeft;
5491da177e4SLinus Torvalds 	unsigned long flags ;
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds 	/* ++TeSche: Is something like this necessary?
5521da177e4SLinus Torvalds 	 * Hey, that's an honest question! Or does any other part of the
5531da177e4SLinus Torvalds 	 * filesystem already checks this situation? I really don't know.
5541da177e4SLinus Torvalds 	 */
5551da177e4SLinus Torvalds 	if (uLeft == 0)
5561da177e4SLinus Torvalds 		return 0;
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	/* implement any changes we have made to the soft/hard params.
5591da177e4SLinus Torvalds 	   this is not satisfactory really, all we have done up to now is to
5601da177e4SLinus Torvalds 	   say what we would like - there hasn't been any real checking of capability
5611da177e4SLinus Torvalds 	*/
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	if (shared_resources_initialised == 0) {
5641da177e4SLinus Torvalds 		dmasound.mach.init() ;
5651da177e4SLinus Torvalds 		shared_resources_initialised = 1 ;
5661da177e4SLinus Torvalds 	}
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	/* set up the sq if it is not already done. This may seem a dumb place
5691da177e4SLinus Torvalds 	   to do it - but it is what OSS requires.  It means that write() can
5701da177e4SLinus Torvalds 	   return memory allocation errors.  To avoid this possibility use the
5711da177e4SLinus Torvalds 	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the
5721da177e4SLinus Torvalds 	   params you want to change) - these ioctls also force the setup.
5731da177e4SLinus Torvalds 	*/
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 	if (write_sq.locked == 0) {
5761da177e4SLinus Torvalds 		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;
5771da177e4SLinus Torvalds 		uWritten = 0 ;
5781da177e4SLinus Torvalds 	}
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds /* FIXME: I think that this may be the wrong behaviour when we get strapped
5811da177e4SLinus Torvalds 	for time and the cpu is close to being (or actually) behind in sending data.
5821da177e4SLinus Torvalds 	- because we've lost the time that the N samples, already in the buffer,
5831da177e4SLinus Torvalds 	would have given us to get here with the next lot from the user.
5841da177e4SLinus Torvalds */
5851da177e4SLinus Torvalds 	/* The interrupt doesn't start to play the last, incomplete frame.
5861da177e4SLinus Torvalds 	 * Thus we can append to it without disabling the interrupts! (Note
5871da177e4SLinus Torvalds 	 * also that write_sq.rear isn't affected by the interrupt.)
5881da177e4SLinus Torvalds 	 */
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:
5911da177e4SLinus Torvalds 	   this will mimic the behaviour of syncing and allow the sq_play() to
5921da177e4SLinus Torvalds 	   queue a partial fragment.  Since sq_play() may/will be called from
5931da177e4SLinus Torvalds 	   the IRQ handler - at least on Pmac we have to deal with it.
5941da177e4SLinus Torvalds 	   The strategy - possibly not optimum - is to kill _POST status if we
5951da177e4SLinus Torvalds 	   get here.  This seems, at least, reasonable - in the sense that POST
5961da177e4SLinus Torvalds 	   is supposed to indicate that we might not write before the queue
5971da177e4SLinus Torvalds 	   is drained - and if we get here in time then it does not apply.
5981da177e4SLinus Torvalds 	*/
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds 	spin_lock_irqsave(&dmasound.lock, flags);
6011da177e4SLinus Torvalds 	write_sq.syncing &= ~2 ; /* take out POST status */
6021da177e4SLinus Torvalds 	spin_unlock_irqrestore(&dmasound.lock, flags);
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 	if (write_sq.count > 0 &&
6051da177e4SLinus Torvalds 	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
6061da177e4SLinus Torvalds 		dest = write_sq.buffers[write_sq.rear];
6071da177e4SLinus Torvalds 		bUsed = write_sq.rear_size;
6081da177e4SLinus Torvalds 		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
6091da177e4SLinus Torvalds 					     dest, &bUsed, bLeft);
6101da177e4SLinus Torvalds 		if (uUsed <= 0)
6111da177e4SLinus Torvalds 			return uUsed;
6121da177e4SLinus Torvalds 		src += uUsed;
6131da177e4SLinus Torvalds 		uWritten += uUsed;
6141da177e4SLinus Torvalds 		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
6151da177e4SLinus Torvalds 		write_sq.rear_size = bUsed;
6161da177e4SLinus Torvalds 	}
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 	while (uLeft) {
6191a1e0a80SArnd Bergmann 		DEFINE_WAIT(wait);
6201a1e0a80SArnd Bergmann 
6211da177e4SLinus Torvalds 		while (write_sq.count >= write_sq.max_active) {
6221a1e0a80SArnd Bergmann 			prepare_to_wait(&write_sq.action_queue, &wait, TASK_INTERRUPTIBLE);
6231da177e4SLinus Torvalds 			sq_play();
6241a1e0a80SArnd Bergmann 			if (write_sq.non_blocking) {
6251a1e0a80SArnd Bergmann 				finish_wait(&write_sq.action_queue, &wait);
6261da177e4SLinus Torvalds 				return uWritten > 0 ? uWritten : -EAGAIN;
6271a1e0a80SArnd Bergmann 			}
6281a1e0a80SArnd Bergmann 			if (write_sq.count < write_sq.max_active)
6291a1e0a80SArnd Bergmann 				break;
6301a1e0a80SArnd Bergmann 
6311a1e0a80SArnd Bergmann 			schedule_timeout(HZ);
6321a1e0a80SArnd Bergmann 			if (signal_pending(current)) {
6331a1e0a80SArnd Bergmann 				finish_wait(&write_sq.action_queue, &wait);
6341da177e4SLinus Torvalds 				return uWritten > 0 ? uWritten : -EINTR;
6351da177e4SLinus Torvalds 			}
6361a1e0a80SArnd Bergmann 		}
6371a1e0a80SArnd Bergmann 
6381a1e0a80SArnd Bergmann 		finish_wait(&write_sq.action_queue, &wait);
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 		/* Here, we can avoid disabling the interrupt by first
6411da177e4SLinus Torvalds 		 * copying and translating the data, and then updating
6421da177e4SLinus Torvalds 		 * the write_sq variables. Until this is done, the interrupt
6431da177e4SLinus Torvalds 		 * won't see the new frame and we can work on it
6441da177e4SLinus Torvalds 		 * undisturbed.
6451da177e4SLinus Torvalds 		 */
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds 		dest = write_sq.buffers[(write_sq.rear+1) % write_sq.max_count];
6481da177e4SLinus Torvalds 		bUsed = 0;
6491da177e4SLinus Torvalds 		bLeft = write_sq.block_size;
6501da177e4SLinus Torvalds 		uUsed = sound_copy_translate(dmasound.trans_write, src, uLeft,
6511da177e4SLinus Torvalds 					     dest, &bUsed, bLeft);
6521da177e4SLinus Torvalds 		if (uUsed <= 0)
6531da177e4SLinus Torvalds 			break;
6541da177e4SLinus Torvalds 		src += uUsed;
6551da177e4SLinus Torvalds 		uWritten += uUsed;
6561da177e4SLinus Torvalds 		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
6571da177e4SLinus Torvalds 		if (bUsed) {
6581da177e4SLinus Torvalds 			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
6591da177e4SLinus Torvalds 			write_sq.rear_size = bUsed;
6601da177e4SLinus Torvalds 			write_sq.count++;
6611da177e4SLinus Torvalds 		}
6621da177e4SLinus Torvalds 	} /* uUsed may have been 0 */
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	sq_play();
6651da177e4SLinus Torvalds 
6661da177e4SLinus Torvalds 	return uUsed < 0? uUsed: uWritten;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds 
sq_poll(struct file * file,struct poll_table_struct * wait)669680ef72aSAl Viro static __poll_t sq_poll(struct file *file, struct poll_table_struct *wait)
6701da177e4SLinus Torvalds {
671680ef72aSAl Viro 	__poll_t mask = 0;
6721da177e4SLinus Torvalds 	int retVal;
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 	if (write_sq.locked == 0) {
6751da177e4SLinus Torvalds 		if ((retVal = sq_setup(&write_sq)) < 0)
6761da177e4SLinus Torvalds 			return retVal;
6771da177e4SLinus Torvalds 		return 0;
6781da177e4SLinus Torvalds 	}
6791da177e4SLinus Torvalds 	if (file->f_mode & FMODE_WRITE )
6801da177e4SLinus Torvalds 		poll_wait(file, &write_sq.action_queue, wait);
6811da177e4SLinus Torvalds 	if (file->f_mode & FMODE_WRITE)
6821da177e4SLinus Torvalds 		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
683a9a08845SLinus Torvalds 			mask |= EPOLLOUT | EPOLLWRNORM;
6841da177e4SLinus Torvalds 	return mask;
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
sq_init_waitqueue(struct sound_queue * sq)6881da177e4SLinus Torvalds static inline void sq_init_waitqueue(struct sound_queue *sq)
6891da177e4SLinus Torvalds {
6901da177e4SLinus Torvalds 	init_waitqueue_head(&sq->action_queue);
6911da177e4SLinus Torvalds 	init_waitqueue_head(&sq->open_queue);
6921da177e4SLinus Torvalds 	init_waitqueue_head(&sq->sync_queue);
6931da177e4SLinus Torvalds 	sq->busy = 0;
6941da177e4SLinus Torvalds }
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds #if 0 /* blocking open() */
6971da177e4SLinus Torvalds static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
698aeb5d727SAl Viro 			      fmode_t mode)
6991da177e4SLinus Torvalds {
7001da177e4SLinus Torvalds 	if (file->f_mode & mode) {
7011da177e4SLinus Torvalds 		sq->busy = 0; /* CHECK: IS THIS OK??? */
7021da177e4SLinus Torvalds 		WAKE_UP(sq->open_queue);
7031da177e4SLinus Torvalds 	}
7041da177e4SLinus Torvalds }
7051da177e4SLinus Torvalds #endif
7061da177e4SLinus Torvalds 
sq_open2(struct sound_queue * sq,struct file * file,fmode_t mode,int numbufs,int bufsize)707aeb5d727SAl Viro static int sq_open2(struct sound_queue *sq, struct file *file, fmode_t mode,
7081da177e4SLinus Torvalds 		    int numbufs, int bufsize)
7091da177e4SLinus Torvalds {
7101da177e4SLinus Torvalds 	int rc = 0;
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds 	if (file->f_mode & mode) {
7131da177e4SLinus Torvalds 		if (sq->busy) {
7141da177e4SLinus Torvalds #if 0 /* blocking open() */
7151da177e4SLinus Torvalds 			rc = -EBUSY;
7161da177e4SLinus Torvalds 			if (file->f_flags & O_NONBLOCK)
7171da177e4SLinus Torvalds 				return rc;
7181da177e4SLinus Torvalds 			rc = -EINTR;
7191a1e0a80SArnd Bergmann 			if (wait_event_interruptible(sq->open_queue, !sq->busy))
7201da177e4SLinus Torvalds 				return rc;
7211da177e4SLinus Torvalds 			rc = 0;
7221da177e4SLinus Torvalds #else
7231da177e4SLinus Torvalds 			/* OSS manual says we will return EBUSY regardless
7241da177e4SLinus Torvalds 			   of O_NOBLOCK.
7251da177e4SLinus Torvalds 			*/
7261da177e4SLinus Torvalds 			return -EBUSY ;
7271da177e4SLinus Torvalds #endif
7281da177e4SLinus Torvalds 		}
7291da177e4SLinus Torvalds 		sq->busy = 1; /* Let's play spot-the-race-condition */
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds 		/* allocate the default number & size of buffers.
7321da177e4SLinus Torvalds 		   (i.e. specified in _setup() or as module params)
7331da177e4SLinus Torvalds 		   can't be changed at the moment - but _could_ be perhaps
7341da177e4SLinus Torvalds 		   in the setfragments ioctl.
7351da177e4SLinus Torvalds 		*/
7361da177e4SLinus Torvalds 		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {
7371da177e4SLinus Torvalds #if 0 /* blocking open() */
7381da177e4SLinus Torvalds 			sq_wake_up(sq, file, mode);
7391da177e4SLinus Torvalds #else
7401da177e4SLinus Torvalds 			sq->busy = 0 ;
7411da177e4SLinus Torvalds #endif
7421da177e4SLinus Torvalds 			return rc;
7431da177e4SLinus Torvalds 		}
7441da177e4SLinus Torvalds 
7454b30fbdeSAl Viro 		sq->non_blocking = file->f_flags & O_NONBLOCK;
7461da177e4SLinus Torvalds 	}
7471da177e4SLinus Torvalds 	return rc;
7481da177e4SLinus Torvalds }
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds #define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
7511da177e4SLinus Torvalds #if 0 /* blocking open() */
7521da177e4SLinus Torvalds #define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
7531da177e4SLinus Torvalds #endif
7541da177e4SLinus Torvalds #define write_sq_release_buffers()	sq_release_buffers(&write_sq)
7551da177e4SLinus Torvalds #define write_sq_open(file)	\
7561da177e4SLinus Torvalds 	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
7571da177e4SLinus Torvalds 
sq_open(struct inode * inode,struct file * file)7581da177e4SLinus Torvalds static int sq_open(struct inode *inode, struct file *file)
7591da177e4SLinus Torvalds {
7601da177e4SLinus Torvalds 	int rc;
7611da177e4SLinus Torvalds 
762645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
76390dc763fSArnd Bergmann 	if (!try_module_get(dmasound.mach.owner)) {
764645ef9efSArnd Bergmann 		mutex_unlock(&dmasound_core_mutex);
7651da177e4SLinus Torvalds 		return -ENODEV;
76690dc763fSArnd Bergmann 	}
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds 	rc = write_sq_open(file); /* checks the f_mode */
7691da177e4SLinus Torvalds 	if (rc)
7701da177e4SLinus Torvalds 		goto out;
7711da177e4SLinus Torvalds 	if (file->f_mode & FMODE_READ) {
7721da177e4SLinus Torvalds 		/* TODO: if O_RDWR, release any resources grabbed by write part */
7731da177e4SLinus Torvalds 		rc = -ENXIO ; /* I think this is what is required by open(2) */
7741da177e4SLinus Torvalds 		goto out;
7751da177e4SLinus Torvalds 	}
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	if (dmasound.mach.sq_open)
7781da177e4SLinus Torvalds 	    dmasound.mach.sq_open(file->f_mode);
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	/* CHECK whether this is sensible - in the case that dsp0 could be opened
7811da177e4SLinus Torvalds 	  O_RDONLY and dsp1 could be opened O_WRONLY
7821da177e4SLinus Torvalds 	*/
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 	dmasound.minDev = iminor(inode) & 0x0f;
7851da177e4SLinus Torvalds 
7861da177e4SLinus Torvalds 	/* OK. - we should make some attempt at consistency. At least the H'ware
7871da177e4SLinus Torvalds 	   options should be set with a valid mode.  We will make it that the LL
7881da177e4SLinus Torvalds 	   driver must supply defaults for hard & soft params.
7891da177e4SLinus Torvalds 	*/
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds 	if (shared_resource_owner == 0) {
7921da177e4SLinus Torvalds 		/* you can make this AFMT_U8/mono/8K if you want to mimic old
7931da177e4SLinus Torvalds 		   OSS behaviour - while we still have soft translations ;-) */
7941da177e4SLinus Torvalds 		dmasound.soft = dmasound.mach.default_soft ;
7951da177e4SLinus Torvalds 		dmasound.dsp = dmasound.mach.default_soft ;
7961da177e4SLinus Torvalds 		dmasound.hard = dmasound.mach.default_hard ;
7971da177e4SLinus Torvalds 	}
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds #ifndef DMASOUND_STRICT_OSS_COMPLIANCE
8001da177e4SLinus Torvalds 	/* none of the current LL drivers can actually do this "native" at the moment
8011da177e4SLinus Torvalds 	   OSS does not really require us to supply /dev/audio if we can't do it.
8021da177e4SLinus Torvalds 	*/
8031da177e4SLinus Torvalds 	if (dmasound.minDev == SND_DEV_AUDIO) {
8041da177e4SLinus Torvalds 		sound_set_speed(8000);
8051da177e4SLinus Torvalds 		sound_set_stereo(0);
8061da177e4SLinus Torvalds 		sound_set_format(AFMT_MU_LAW);
8071da177e4SLinus Torvalds 	}
8081da177e4SLinus Torvalds #endif
809645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
8101da177e4SLinus Torvalds 	return 0;
8111da177e4SLinus Torvalds  out:
8121da177e4SLinus Torvalds 	module_put(dmasound.mach.owner);
813645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
8141da177e4SLinus Torvalds 	return rc;
8151da177e4SLinus Torvalds }
8161da177e4SLinus Torvalds 
sq_reset_output(void)8171da177e4SLinus Torvalds static void sq_reset_output(void)
8181da177e4SLinus Torvalds {
8191da177e4SLinus Torvalds 	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */
8201da177e4SLinus Torvalds 	write_sq.active = 0;
8211da177e4SLinus Torvalds 	write_sq.count = 0;
8221da177e4SLinus Torvalds 	write_sq.rear_size = 0;
8231da177e4SLinus Torvalds 	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/
8241da177e4SLinus Torvalds 	write_sq.front = 0 ;
8251da177e4SLinus Torvalds 	write_sq.rear = -1 ; /* same as for set-up */
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 	/* OK - we can unlock the parameters and fragment settings */
8281da177e4SLinus Torvalds 	write_sq.locked = 0 ;
8291da177e4SLinus Torvalds 	write_sq.user_frags = 0 ;
8301da177e4SLinus Torvalds 	write_sq.user_frag_size = 0 ;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds 
sq_reset(void)8331da177e4SLinus Torvalds static void sq_reset(void)
8341da177e4SLinus Torvalds {
8351da177e4SLinus Torvalds 	sq_reset_output() ;
8361da177e4SLinus Torvalds 	/* we could consider resetting the shared_resources_owner here... but I
8371da177e4SLinus Torvalds 	   think it is probably still rather non-obvious to application writer
8381da177e4SLinus Torvalds 	*/
8391da177e4SLinus Torvalds 
8401da177e4SLinus Torvalds 	/* we release everything else though */
8411da177e4SLinus Torvalds 	shared_resources_initialised = 0 ;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
sq_fsync(void)844fca660beSAl Viro static int sq_fsync(void)
8451da177e4SLinus Torvalds {
8461da177e4SLinus Torvalds 	int rc = 0;
8471da177e4SLinus Torvalds 	int timeout = 5;
8481da177e4SLinus Torvalds 
8491da177e4SLinus Torvalds 	write_sq.syncing |= 1;
8501da177e4SLinus Torvalds 	sq_play();	/* there may be an incomplete frame waiting */
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 	while (write_sq.active) {
8531a1e0a80SArnd Bergmann 		wait_event_interruptible_timeout(write_sq.sync_queue,
8541a1e0a80SArnd Bergmann 						 !write_sq.active, HZ);
8551da177e4SLinus Torvalds 		if (signal_pending(current)) {
8561da177e4SLinus Torvalds 			/* While waiting for audio output to drain, an
8571da177e4SLinus Torvalds 			 * interrupt occurred.  Stop audio output immediately
8581da177e4SLinus Torvalds 			 * and clear the queue. */
8591da177e4SLinus Torvalds 			sq_reset_output();
8601da177e4SLinus Torvalds 			rc = -EINTR;
8611da177e4SLinus Torvalds 			break;
8621da177e4SLinus Torvalds 		}
8631da177e4SLinus Torvalds 		if (!--timeout) {
8641da177e4SLinus Torvalds 			printk(KERN_WARNING "dmasound: Timeout draining output\n");
8651da177e4SLinus Torvalds 			sq_reset_output();
8661da177e4SLinus Torvalds 			rc = -EIO;
8671da177e4SLinus Torvalds 			break;
8681da177e4SLinus Torvalds 		}
8691da177e4SLinus Torvalds 	}
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	/* flag no sync regardless of whether we had a DSP_POST or not */
8721da177e4SLinus Torvalds 	write_sq.syncing = 0 ;
8731da177e4SLinus Torvalds 	return rc;
8741da177e4SLinus Torvalds }
8751da177e4SLinus Torvalds 
sq_release(struct inode * inode,struct file * file)8761da177e4SLinus Torvalds static int sq_release(struct inode *inode, struct file *file)
8771da177e4SLinus Torvalds {
8781da177e4SLinus Torvalds 	int rc = 0;
8791da177e4SLinus Torvalds 
880645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	if (file->f_mode & FMODE_WRITE) {
8831da177e4SLinus Torvalds 		if (write_sq.busy)
884fca660beSAl Viro 			rc = sq_fsync();
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 		sq_reset_output() ; /* make sure dma is stopped and all is quiet */
8871da177e4SLinus Torvalds 		write_sq_release_buffers();
8881da177e4SLinus Torvalds 		write_sq.busy = 0;
8891da177e4SLinus Torvalds 	}
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 	if (file->f_mode & shared_resource_owner) { /* it's us that has them */
8921da177e4SLinus Torvalds 		shared_resource_owner = 0 ;
8931da177e4SLinus Torvalds 		shared_resources_initialised = 0 ;
8941da177e4SLinus Torvalds 		dmasound.hard = dmasound.mach.default_hard ;
8951da177e4SLinus Torvalds 	}
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 	module_put(dmasound.mach.owner);
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds #if 0 /* blocking open() */
9001da177e4SLinus Torvalds 	/* Wake up a process waiting for the queue being released.
9011da177e4SLinus Torvalds 	 * Note: There may be several processes waiting for a call
9021da177e4SLinus Torvalds 	 * to open() returning. */
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 	/* Iain: hmm I don't understand this next comment ... */
9051da177e4SLinus Torvalds 	/* There is probably a DOS atack here. They change the mode flag. */
9061da177e4SLinus Torvalds 	/* XXX add check here,*/
9071da177e4SLinus Torvalds 	read_sq_wake_up(file); /* checks f_mode */
9081da177e4SLinus Torvalds 	write_sq_wake_up(file); /* checks f_mode */
9091da177e4SLinus Torvalds #endif /* blocking open() */
9101da177e4SLinus Torvalds 
911645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	return rc;
9141da177e4SLinus Torvalds }
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds /* here we see if we have a right to modify format, channels, size and so on
9171da177e4SLinus Torvalds    if no-one else has claimed it already then we do...
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds    TODO: We might change this to mask O_RDWR such that only one or the other channel
9201da177e4SLinus Torvalds    is the owner - if we have problems.
9211da177e4SLinus Torvalds */
9221da177e4SLinus Torvalds 
shared_resources_are_mine(fmode_t md)923aeb5d727SAl Viro static int shared_resources_are_mine(fmode_t md)
9241da177e4SLinus Torvalds {
9251da177e4SLinus Torvalds 	if (shared_resource_owner)
926aeb5d727SAl Viro 		return (shared_resource_owner & md) != 0;
9271da177e4SLinus Torvalds 	else {
9281da177e4SLinus Torvalds 		shared_resource_owner = md ;
9291da177e4SLinus Torvalds 		return 1 ;
9301da177e4SLinus Torvalds 	}
9311da177e4SLinus Torvalds }
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds /* if either queue is locked we must deny the right to change shared params
9341da177e4SLinus Torvalds */
9351da177e4SLinus Torvalds 
queues_are_quiescent(void)9361da177e4SLinus Torvalds static int queues_are_quiescent(void)
9371da177e4SLinus Torvalds {
9381da177e4SLinus Torvalds 	if (write_sq.locked)
9391da177e4SLinus Torvalds 		return 0 ;
9401da177e4SLinus Torvalds 	return 1 ;
9411da177e4SLinus Torvalds }
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds /* check and set a queue's fragments per user's wishes...
9441da177e4SLinus Torvalds    we will check against the pre-defined literals and the actual sizes.
9451da177e4SLinus Torvalds    This is a bit fraught - because soft translations can mess with our
9461da177e4SLinus Torvalds    buffer requirements *after* this call - OSS says "call setfrags first"
9471da177e4SLinus Torvalds */
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds /* It is possible to replace all the -EINVAL returns with an override that
9501da177e4SLinus Torvalds    just puts the allowable value in.  This may be what many OSS apps require
9511da177e4SLinus Torvalds */
9521da177e4SLinus Torvalds 
set_queue_frags(struct sound_queue * sq,int bufs,int size)9531da177e4SLinus Torvalds static int set_queue_frags(struct sound_queue *sq, int bufs, int size)
9541da177e4SLinus Torvalds {
9551da177e4SLinus Torvalds 	if (sq->locked) {
9561da177e4SLinus Torvalds #ifdef DEBUG_DMASOUND
9571da177e4SLinus Torvalds printk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;
9581da177e4SLinus Torvalds #endif
9591da177e4SLinus Torvalds 		return -EINVAL ;
9601da177e4SLinus Torvalds 	}
9611da177e4SLinus Torvalds 
9621da177e4SLinus Torvalds 	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))
9631da177e4SLinus Torvalds 		return -EINVAL ;
9641da177e4SLinus Torvalds 	size = (1<<size) ; /* now in bytes */
9651da177e4SLinus Torvalds 	if (size > sq->bufSize)
9661da177e4SLinus Torvalds 		return -EINVAL ; /* this might still not work */
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds 	if (bufs <= 0)
9691da177e4SLinus Torvalds 		return -EINVAL ;
9701da177e4SLinus Torvalds 	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */
9711da177e4SLinus Torvalds 		bufs = sq->numBufs ;
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds 	/* there is, currently, no way to specify max_active separately
9741da177e4SLinus Torvalds 	   from max_count.  This could be a LL driver issue - I guess
9751da177e4SLinus Torvalds 	   if there is a requirement for these values to be different then
9761da177e4SLinus Torvalds 	  we will have to pass that info. up to this level.
9771da177e4SLinus Torvalds 	*/
9781da177e4SLinus Torvalds 	sq->user_frags =
9791da177e4SLinus Torvalds 	sq->max_active = bufs ;
9801da177e4SLinus Torvalds 	sq->user_frag_size = size ;
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	return 0 ;
9831da177e4SLinus Torvalds }
9841da177e4SLinus Torvalds 
sq_ioctl(struct file * file,u_int cmd,u_long arg)985d209974cSArnd Bergmann static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
9861da177e4SLinus Torvalds {
9871da177e4SLinus Torvalds 	int val, result;
9881da177e4SLinus Torvalds 	u_long fmt;
9891da177e4SLinus Torvalds 	int data;
9901da177e4SLinus Torvalds 	int size, nbufs;
9911da177e4SLinus Torvalds 	audio_buf_info info;
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	switch (cmd) {
9941da177e4SLinus Torvalds 	case SNDCTL_DSP_RESET:
9951da177e4SLinus Torvalds 		sq_reset();
9961da177e4SLinus Torvalds 		return 0;
9971da177e4SLinus Torvalds 	case SNDCTL_DSP_GETFMTS:
9981da177e4SLinus Torvalds 		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
9991da177e4SLinus Torvalds 		return IOCTL_OUT(arg, fmt);
10001da177e4SLinus Torvalds 	case SNDCTL_DSP_GETBLKSIZE:
10011da177e4SLinus Torvalds 		/* this should tell the caller about bytes that the app can
10021da177e4SLinus Torvalds 		   read/write - the app doesn't care about our internal buffers.
10031da177e4SLinus Torvalds 		   We force sq_setup() here as per OSS 1.1 (which should
10041da177e4SLinus Torvalds 		   compute the values necessary).
10051da177e4SLinus Torvalds 		   Since there is no mechanism to specify read/write separately, for
10061da177e4SLinus Torvalds 		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite
10071da177e4SLinus Torvalds 		   the read_sq ones.
10081da177e4SLinus Torvalds 		*/
10091da177e4SLinus Torvalds 		size = 0 ;
10101da177e4SLinus Torvalds 		if (file->f_mode & FMODE_WRITE) {
10111da177e4SLinus Torvalds 			if ( !write_sq.locked )
10121da177e4SLinus Torvalds 				sq_setup(&write_sq) ;
10131da177e4SLinus Torvalds 			size = write_sq.user_frag_size ;
10141da177e4SLinus Torvalds 		}
10151da177e4SLinus Torvalds 		return IOCTL_OUT(arg, size);
10161da177e4SLinus Torvalds 	case SNDCTL_DSP_POST:
10171da177e4SLinus Torvalds 		/* all we are going to do is to tell the LL that any
10181da177e4SLinus Torvalds 		   partial frags can be queued for output.
10191da177e4SLinus Torvalds 		   The LL will have to clear this flag when last output
10201da177e4SLinus Torvalds 		   is queued.
10211da177e4SLinus Torvalds 		*/
10221da177e4SLinus Torvalds 		write_sq.syncing |= 0x2 ;
10231da177e4SLinus Torvalds 		sq_play() ;
10241da177e4SLinus Torvalds 		return 0 ;
10251da177e4SLinus Torvalds 	case SNDCTL_DSP_SYNC:
10261da177e4SLinus Torvalds 		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
10271da177e4SLinus Torvalds 		   except that it waits for output to finish before resetting
102825985edcSLucas De Marchi 		   everything - read, however, is killed immediately.
10291da177e4SLinus Torvalds 		*/
10301da177e4SLinus Torvalds 		result = 0 ;
10311da177e4SLinus Torvalds 		if (file->f_mode & FMODE_WRITE) {
1032fca660beSAl Viro 			result = sq_fsync();
10331da177e4SLinus Torvalds 			sq_reset_output() ;
10341da177e4SLinus Torvalds 		}
10351da177e4SLinus Torvalds 		/* if we are the shared resource owner then release them */
10361da177e4SLinus Torvalds 		if (file->f_mode & shared_resource_owner)
10371da177e4SLinus Torvalds 			shared_resources_initialised = 0 ;
10381da177e4SLinus Torvalds 		return result ;
10391da177e4SLinus Torvalds 	case SOUND_PCM_READ_RATE:
10401da177e4SLinus Torvalds 		return IOCTL_OUT(arg, dmasound.soft.speed);
10411da177e4SLinus Torvalds 	case SNDCTL_DSP_SPEED:
10421da177e4SLinus Torvalds 		/* changing this on the fly will have weird effects on the sound.
10431da177e4SLinus Torvalds 		   Where there are rate conversions implemented in soft form - it
10441da177e4SLinus Torvalds 		   will cause the _ctx_xxx() functions to be substituted.
10451da177e4SLinus Torvalds 		   However, there doesn't appear to be any reason to dis-allow it from
10461da177e4SLinus Torvalds 		   a driver pov.
10471da177e4SLinus Torvalds 		*/
10481da177e4SLinus Torvalds 		if (shared_resources_are_mine(file->f_mode)) {
10491da177e4SLinus Torvalds 			IOCTL_IN(arg, data);
10501da177e4SLinus Torvalds 			data = sound_set_speed(data) ;
10511da177e4SLinus Torvalds 			shared_resources_initialised = 0 ;
10521da177e4SLinus Torvalds 			return IOCTL_OUT(arg, data);
10531da177e4SLinus Torvalds 		} else
10541da177e4SLinus Torvalds 			return -EINVAL ;
10551da177e4SLinus Torvalds 		break ;
10561da177e4SLinus Torvalds 	/* OSS says these next 4 actions are undefined when the device is
10571da177e4SLinus Torvalds 	   busy/active - we will just return -EINVAL.
10581da177e4SLinus Torvalds 	   To be allowed to change one - (a) you have to own the right
10591da177e4SLinus Torvalds 	    (b) the queue(s) must be quiescent
10601da177e4SLinus Torvalds 	*/
10611da177e4SLinus Torvalds 	case SNDCTL_DSP_STEREO:
10621da177e4SLinus Torvalds 		if (shared_resources_are_mine(file->f_mode) &&
10631da177e4SLinus Torvalds 		    queues_are_quiescent()) {
10641da177e4SLinus Torvalds 			IOCTL_IN(arg, data);
10651da177e4SLinus Torvalds 			shared_resources_initialised = 0 ;
10661da177e4SLinus Torvalds 			return IOCTL_OUT(arg, sound_set_stereo(data));
10671da177e4SLinus Torvalds 		} else
10681da177e4SLinus Torvalds 			return -EINVAL ;
10691da177e4SLinus Torvalds 		break ;
10701da177e4SLinus Torvalds 	case SOUND_PCM_WRITE_CHANNELS:
10711da177e4SLinus Torvalds 		if (shared_resources_are_mine(file->f_mode) &&
10721da177e4SLinus Torvalds 		    queues_are_quiescent()) {
10731da177e4SLinus Torvalds 			IOCTL_IN(arg, data);
10741da177e4SLinus Torvalds 			/* the user might ask for 20 channels, we will return 1 or 2 */
10751da177e4SLinus Torvalds 			shared_resources_initialised = 0 ;
10761da177e4SLinus Torvalds 			return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
10771da177e4SLinus Torvalds 		} else
10781da177e4SLinus Torvalds 			return -EINVAL ;
10791da177e4SLinus Torvalds 		break ;
10801da177e4SLinus Torvalds 	case SNDCTL_DSP_SETFMT:
10811da177e4SLinus Torvalds 		if (shared_resources_are_mine(file->f_mode) &&
10821da177e4SLinus Torvalds 		    queues_are_quiescent()) {
10831da177e4SLinus Torvalds 		    	int format;
10841da177e4SLinus Torvalds 			IOCTL_IN(arg, data);
10851da177e4SLinus Torvalds 			shared_resources_initialised = 0 ;
10861da177e4SLinus Torvalds 			format = sound_set_format(data);
10871da177e4SLinus Torvalds 			result = IOCTL_OUT(arg, format);
10881da177e4SLinus Torvalds 			if (result < 0)
10891da177e4SLinus Torvalds 				return result;
10901da177e4SLinus Torvalds 			if (format != data && data != AFMT_QUERY)
10911da177e4SLinus Torvalds 				return -EINVAL;
10921da177e4SLinus Torvalds 			return 0;
10931da177e4SLinus Torvalds 		} else
10941da177e4SLinus Torvalds 			return -EINVAL ;
10951da177e4SLinus Torvalds 	case SNDCTL_DSP_SUBDIVIDE:
10961da177e4SLinus Torvalds 		return -EINVAL ;
10971da177e4SLinus Torvalds 	case SNDCTL_DSP_SETFRAGMENT:
10981da177e4SLinus Torvalds 		/* we can do this independently for the two queues - with the
10991da177e4SLinus Torvalds 		   proviso that for fds opened O_RDWR we cannot separate the
11001da177e4SLinus Torvalds 		   actions and both queues will be set per the last call.
11011da177e4SLinus Torvalds 		   NOTE: this does *NOT* actually set the queue up - merely
11021da177e4SLinus Torvalds 		   registers our intentions.
11031da177e4SLinus Torvalds 		*/
11041da177e4SLinus Torvalds 		IOCTL_IN(arg, data);
11051da177e4SLinus Torvalds 		result = 0 ;
11061da177e4SLinus Torvalds 		nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
11071da177e4SLinus Torvalds 		size = data & 0xffff;
11081da177e4SLinus Torvalds 		if (file->f_mode & FMODE_WRITE) {
11091da177e4SLinus Torvalds 			result = set_queue_frags(&write_sq, nbufs, size) ;
11101da177e4SLinus Torvalds 			if (result)
11111da177e4SLinus Torvalds 				return result ;
11121da177e4SLinus Torvalds 		}
11131da177e4SLinus Torvalds 		/* NOTE: this return value is irrelevant - OSS specifically says that
11141da177e4SLinus Torvalds 		   the value is 'random' and that the user _must_ check the actual
11151da177e4SLinus Torvalds 		   frags values using SNDCTL_DSP_GETBLKSIZE or similar */
11161da177e4SLinus Torvalds 		return IOCTL_OUT(arg, data);
11171da177e4SLinus Torvalds 	case SNDCTL_DSP_GETOSPACE:
11181da177e4SLinus Torvalds 		/*
11191da177e4SLinus Torvalds 		*/
11201da177e4SLinus Torvalds 		if (file->f_mode & FMODE_WRITE) {
11211da177e4SLinus Torvalds 			if ( !write_sq.locked )
11221da177e4SLinus Torvalds 				sq_setup(&write_sq) ;
11231da177e4SLinus Torvalds 			info.fragments = write_sq.max_active - write_sq.count;
11241da177e4SLinus Torvalds 			info.fragstotal = write_sq.max_active;
11251da177e4SLinus Torvalds 			info.fragsize = write_sq.user_frag_size;
11261da177e4SLinus Torvalds 			info.bytes = info.fragments * info.fragsize;
11271da177e4SLinus Torvalds 			if (copy_to_user((void __user *)arg, &info, sizeof(info)))
11281da177e4SLinus Torvalds 				return -EFAULT;
11291da177e4SLinus Torvalds 			return 0;
11301da177e4SLinus Torvalds 		} else
11311da177e4SLinus Torvalds 			return -EINVAL ;
11321da177e4SLinus Torvalds 		break ;
11331da177e4SLinus Torvalds 	case SNDCTL_DSP_GETCAPS:
11341da177e4SLinus Torvalds 		val = dmasound.mach.capabilities & 0xffffff00;
11351da177e4SLinus Torvalds 		return IOCTL_OUT(arg,val);
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	default:
1138d209974cSArnd Bergmann 		return mixer_ioctl(file, cmd, arg);
11391da177e4SLinus Torvalds 	}
11401da177e4SLinus Torvalds 	return -EINVAL;
11411da177e4SLinus Torvalds }
11421da177e4SLinus Torvalds 
sq_unlocked_ioctl(struct file * file,u_int cmd,u_long arg)1143d209974cSArnd Bergmann static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
1144d209974cSArnd Bergmann {
1145d209974cSArnd Bergmann 	int ret;
1146d209974cSArnd Bergmann 
1147645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
1148d209974cSArnd Bergmann 	ret = sq_ioctl(file, cmd, arg);
1149645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
1150d209974cSArnd Bergmann 
1151d209974cSArnd Bergmann 	return ret;
1152d209974cSArnd Bergmann }
1153d209974cSArnd Bergmann 
11549c2e08c5SArjan van de Ven static const struct file_operations sq_fops =
11551da177e4SLinus Torvalds {
11561da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
11571da177e4SLinus Torvalds 	.llseek		= no_llseek,
11581da177e4SLinus Torvalds 	.write		= sq_write,
11591da177e4SLinus Torvalds 	.poll		= sq_poll,
1160d209974cSArnd Bergmann 	.unlocked_ioctl	= sq_unlocked_ioctl,
11612022ca0aSArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
11621da177e4SLinus Torvalds 	.open		= sq_open,
11631da177e4SLinus Torvalds 	.release	= sq_release,
11641da177e4SLinus Torvalds };
11651da177e4SLinus Torvalds 
sq_init(void)11661da177e4SLinus Torvalds static int sq_init(void)
11671da177e4SLinus Torvalds {
1168d8b3966eSAl Viro 	const struct file_operations *fops = &sq_fops;
11691da177e4SLinus Torvalds 
1170d8b3966eSAl Viro 	sq_unit = register_sound_dsp(fops, -1);
11711da177e4SLinus Torvalds 	if (sq_unit < 0) {
11721da177e4SLinus Torvalds 		printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
11731da177e4SLinus Torvalds 		return sq_unit ;
11741da177e4SLinus Torvalds 	}
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	write_sq_init_waitqueue();
11771da177e4SLinus Torvalds 
11781da177e4SLinus Torvalds 	/* These parameters will be restored for every clean open()
11791da177e4SLinus Torvalds 	 * in the case of multiple open()s (e.g. dsp0 & dsp1) they
11801da177e4SLinus Torvalds 	 * will be set so long as the shared resources have no owner.
11811da177e4SLinus Torvalds 	 */
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 	if (shared_resource_owner == 0) {
11841da177e4SLinus Torvalds 		dmasound.soft = dmasound.mach.default_soft ;
11851da177e4SLinus Torvalds 		dmasound.hard = dmasound.mach.default_hard ;
11861da177e4SLinus Torvalds 		dmasound.dsp = dmasound.mach.default_soft ;
11871da177e4SLinus Torvalds 		shared_resources_initialised = 0 ;
11881da177e4SLinus Torvalds 	}
11891da177e4SLinus Torvalds 	return 0 ;
11901da177e4SLinus Torvalds }
11911da177e4SLinus Torvalds 
11921da177e4SLinus Torvalds 
11931da177e4SLinus Torvalds     /*
11941da177e4SLinus Torvalds      *  /dev/sndstat
11951da177e4SLinus Torvalds      */
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds /* we allow more space for record-enabled because there are extra output lines.
11981da177e4SLinus Torvalds    the number here must include the amount we are prepared to give to the low-level
11991da177e4SLinus Torvalds    driver.
12001da177e4SLinus Torvalds */
12011da177e4SLinus Torvalds 
12021da177e4SLinus Torvalds #define STAT_BUFF_LEN 768
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds /* this is how much space we will allow the low-level driver to use
12051da177e4SLinus Torvalds    in the stat buffer.  Currently, 2 * (80 character line + <NL>).
12061da177e4SLinus Torvalds    We do not police this (it is up to the ll driver to be honest).
12071da177e4SLinus Torvalds */
12081da177e4SLinus Torvalds 
12091da177e4SLinus Torvalds #define LOW_LEVEL_STAT_ALLOC 162
12101da177e4SLinus Torvalds 
12111da177e4SLinus Torvalds static struct {
12121da177e4SLinus Torvalds     int busy;
12131da177e4SLinus Torvalds     char buf[STAT_BUFF_LEN];	/* state.buf should not overflow! */
12141da177e4SLinus Torvalds     int len, ptr;
12151da177e4SLinus Torvalds } state;
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds /* publish this function for use by low-level code, if required */
12181da177e4SLinus Torvalds 
get_afmt_string(int afmt)12190a1b42dbSAdrian Bunk static char *get_afmt_string(int afmt)
12201da177e4SLinus Torvalds {
12211da177e4SLinus Torvalds         switch(afmt) {
12221da177e4SLinus Torvalds             case AFMT_MU_LAW:
12231da177e4SLinus Torvalds                 return "mu-law";
12241da177e4SLinus Torvalds             case AFMT_A_LAW:
12251da177e4SLinus Torvalds                 return "A-law";
12261da177e4SLinus Torvalds             case AFMT_U8:
12271da177e4SLinus Torvalds                 return "unsigned 8 bit";
12281da177e4SLinus Torvalds             case AFMT_S8:
12291da177e4SLinus Torvalds                 return "signed 8 bit";
12301da177e4SLinus Torvalds             case AFMT_S16_BE:
12311da177e4SLinus Torvalds                 return "signed 16 bit BE";
12321da177e4SLinus Torvalds             case AFMT_U16_BE:
12331da177e4SLinus Torvalds                 return "unsigned 16 bit BE";
12341da177e4SLinus Torvalds             case AFMT_S16_LE:
12351da177e4SLinus Torvalds                 return "signed 16 bit LE";
12361da177e4SLinus Torvalds             case AFMT_U16_LE:
12371da177e4SLinus Torvalds                 return "unsigned 16 bit LE";
12381da177e4SLinus Torvalds 	    case 0:
12391da177e4SLinus Torvalds 		return "format not set" ;
12401da177e4SLinus Torvalds             default:
12411da177e4SLinus Torvalds                 break ;
12421da177e4SLinus Torvalds         }
12431da177e4SLinus Torvalds         return "ERROR: Unsupported AFMT_XXXX code" ;
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds 
state_open(struct inode * inode,struct file * file)12461da177e4SLinus Torvalds static int state_open(struct inode *inode, struct file *file)
12471da177e4SLinus Torvalds {
12481da177e4SLinus Torvalds 	char *buffer = state.buf;
12491da177e4SLinus Torvalds 	int len = 0;
125090dc763fSArnd Bergmann 	int ret;
12511da177e4SLinus Torvalds 
1252645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
125390dc763fSArnd Bergmann 	ret = -EBUSY;
12541da177e4SLinus Torvalds 	if (state.busy)
125590dc763fSArnd Bergmann 		goto out;
12561da177e4SLinus Torvalds 
125790dc763fSArnd Bergmann 	ret = -ENODEV;
12581da177e4SLinus Torvalds 	if (!try_module_get(dmasound.mach.owner))
125990dc763fSArnd Bergmann 		goto out;
126090dc763fSArnd Bergmann 
12611da177e4SLinus Torvalds 	state.ptr = 0;
12621da177e4SLinus Torvalds 	state.busy = 1;
12631da177e4SLinus Torvalds 
12641da177e4SLinus Torvalds 	len += sprintf(buffer+len, "%sDMA sound driver rev %03d :\n",
12651da177e4SLinus Torvalds 		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
12661da177e4SLinus Torvalds 		((dmasound.mach.version>>8) & 0x0f));
12671da177e4SLinus Torvalds 	len += sprintf(buffer+len,
12681da177e4SLinus Torvalds 		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
12691da177e4SLinus Torvalds 		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
12701da177e4SLinus Torvalds 		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds 	/* call the low-level module to fill in any stat info. that it has
12731da177e4SLinus Torvalds 	   if present.  Maximum buffer usage is specified.
12741da177e4SLinus Torvalds 	*/
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 	if (dmasound.mach.state_info)
12771da177e4SLinus Torvalds 		len += dmasound.mach.state_info(buffer+len,
12781da177e4SLinus Torvalds 			(size_t) LOW_LEVEL_STAT_ALLOC) ;
12791da177e4SLinus Torvalds 
12801da177e4SLinus Torvalds 	/* make usage of the state buffer as deterministic as poss.
12811da177e4SLinus Torvalds 	   exceptional conditions could cause overrun - and this is flagged as
12821da177e4SLinus Torvalds 	   a kernel error.
12831da177e4SLinus Torvalds 	*/
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds 	/* formats and settings */
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 	len += sprintf(buffer+len,"\t\t === Formats & settings ===\n") ;
12881da177e4SLinus Torvalds 	len += sprintf(buffer+len,"Parameter %20s%20s\n","soft","hard") ;
12891da177e4SLinus Torvalds 	len += sprintf(buffer+len,"Format   :%20s%20s\n",
12901da177e4SLinus Torvalds 		get_afmt_string(dmasound.soft.format),
12911da177e4SLinus Torvalds 		get_afmt_string(dmasound.hard.format));
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 	len += sprintf(buffer+len,"Samp Rate:%14d s/sec%14d s/sec\n",
12941da177e4SLinus Torvalds 		       dmasound.soft.speed, dmasound.hard.speed);
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds 	len += sprintf(buffer+len,"Channels :%20s%20s\n",
12971da177e4SLinus Torvalds 		       dmasound.soft.stereo ? "stereo" : "mono",
12981da177e4SLinus Torvalds 		       dmasound.hard.stereo ? "stereo" : "mono" );
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds 	/* sound queue status */
13011da177e4SLinus Torvalds 
13021da177e4SLinus Torvalds 	len += sprintf(buffer+len,"\t\t === Sound Queue status ===\n");
13031da177e4SLinus Torvalds 	len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
13041da177e4SLinus Torvalds 	len += sprintf(buffer+len,"%9s:%8d%6d\n",
13051da177e4SLinus Torvalds 		"write", write_sq.numBufs, write_sq.bufSize) ;
13061da177e4SLinus Torvalds 	len += sprintf(buffer+len,
13071da177e4SLinus Torvalds 		"Current  : MaxFrg FragSiz MaxAct Frnt Rear "
13081da177e4SLinus Torvalds 		"Cnt RrSize A B S L  xruns\n") ;
13091da177e4SLinus Torvalds 	len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
13101da177e4SLinus Torvalds 		"write", write_sq.max_count, write_sq.block_size,
13111da177e4SLinus Torvalds 		write_sq.max_active, write_sq.front, write_sq.rear,
13121da177e4SLinus Torvalds 		write_sq.count, write_sq.rear_size, write_sq.active,
13131da177e4SLinus Torvalds 		write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
13141da177e4SLinus Torvalds #ifdef DEBUG_DMASOUND
13151da177e4SLinus Torvalds printk("dmasound: stat buffer used %d bytes\n", len) ;
13161da177e4SLinus Torvalds #endif
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds 	if (len >= STAT_BUFF_LEN)
13191da177e4SLinus Torvalds 		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	state.len = len;
132290dc763fSArnd Bergmann 	ret = 0;
132390dc763fSArnd Bergmann out:
1324645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
132590dc763fSArnd Bergmann 	return ret;
13261da177e4SLinus Torvalds }
13271da177e4SLinus Torvalds 
state_release(struct inode * inode,struct file * file)13281da177e4SLinus Torvalds static int state_release(struct inode *inode, struct file *file)
13291da177e4SLinus Torvalds {
1330645ef9efSArnd Bergmann 	mutex_lock(&dmasound_core_mutex);
13311da177e4SLinus Torvalds 	state.busy = 0;
13321da177e4SLinus Torvalds 	module_put(dmasound.mach.owner);
1333645ef9efSArnd Bergmann 	mutex_unlock(&dmasound_core_mutex);
13341da177e4SLinus Torvalds 	return 0;
13351da177e4SLinus Torvalds }
13361da177e4SLinus Torvalds 
state_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)13371da177e4SLinus Torvalds static ssize_t state_read(struct file *file, char __user *buf, size_t count,
13381da177e4SLinus Torvalds 			  loff_t *ppos)
13391da177e4SLinus Torvalds {
13401da177e4SLinus Torvalds 	int n = state.len - state.ptr;
13411da177e4SLinus Torvalds 	if (n > count)
13421da177e4SLinus Torvalds 		n = count;
13431da177e4SLinus Torvalds 	if (n <= 0)
13441da177e4SLinus Torvalds 		return 0;
13451da177e4SLinus Torvalds 	if (copy_to_user(buf, &state.buf[state.ptr], n))
13461da177e4SLinus Torvalds 		return -EFAULT;
13471da177e4SLinus Torvalds 	state.ptr += n;
13481da177e4SLinus Torvalds 	return n;
13491da177e4SLinus Torvalds }
13501da177e4SLinus Torvalds 
13519c2e08c5SArjan van de Ven static const struct file_operations state_fops = {
13521da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
13531da177e4SLinus Torvalds 	.llseek		= no_llseek,
13541da177e4SLinus Torvalds 	.read		= state_read,
13551da177e4SLinus Torvalds 	.open		= state_open,
13561da177e4SLinus Torvalds 	.release	= state_release,
13571da177e4SLinus Torvalds };
13581da177e4SLinus Torvalds 
state_init(void)13591da177e4SLinus Torvalds static int state_init(void)
13601da177e4SLinus Torvalds {
13611da177e4SLinus Torvalds 	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
13621da177e4SLinus Torvalds 	if (state_unit < 0)
13631da177e4SLinus Torvalds 		return state_unit ;
13641da177e4SLinus Torvalds 	state.busy = 0;
13651da177e4SLinus Torvalds 	return 0 ;
13661da177e4SLinus Torvalds }
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds     /*
13701da177e4SLinus Torvalds      *  Config & Setup
13711da177e4SLinus Torvalds      *
13721da177e4SLinus Torvalds      *  This function is called by _one_ chipset-specific driver
13731da177e4SLinus Torvalds      */
13741da177e4SLinus Torvalds 
dmasound_init(void)13751da177e4SLinus Torvalds int dmasound_init(void)
13761da177e4SLinus Torvalds {
13771da177e4SLinus Torvalds 	int res ;
13789dd7c463SRandy Dunlap 
13791da177e4SLinus Torvalds 	if (irq_installed)
13801da177e4SLinus Torvalds 		return -EBUSY;
13811da177e4SLinus Torvalds 
13821da177e4SLinus Torvalds 	/* Set up sound queue, /dev/audio and /dev/dsp. */
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 	/* Set default settings. */
13851da177e4SLinus Torvalds 	if ((res = sq_init()) < 0)
13861da177e4SLinus Torvalds 		return res ;
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds 	/* Set up /dev/sndstat. */
13891da177e4SLinus Torvalds 	if ((res = state_init()) < 0)
13901da177e4SLinus Torvalds 		return res ;
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 	/* Set up /dev/mixer. */
13931da177e4SLinus Torvalds 	mixer_init();
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 	if (!dmasound.mach.irqinit()) {
13961da177e4SLinus Torvalds 		printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
13971da177e4SLinus Torvalds 		return -ENODEV;
13981da177e4SLinus Torvalds 	}
13991da177e4SLinus Torvalds 	irq_installed = 1;
14001da177e4SLinus Torvalds 
14011da177e4SLinus Torvalds 	printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
14021da177e4SLinus Torvalds 		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
14031da177e4SLinus Torvalds 		((dmasound.mach.version>>8) & 0x0f));
14041da177e4SLinus Torvalds 	printk(KERN_INFO
14051da177e4SLinus Torvalds 		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
14061da177e4SLinus Torvalds 		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
14071da177e4SLinus Torvalds 		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
14081da177e4SLinus Torvalds 	printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
14091da177e4SLinus Torvalds 		numWriteBufs, writeBufSize) ;
14101da177e4SLinus Torvalds 	return 0;
14111da177e4SLinus Torvalds }
14121da177e4SLinus Torvalds 
dmasound_deinit(void)14131da177e4SLinus Torvalds void dmasound_deinit(void)
14141da177e4SLinus Torvalds {
14151da177e4SLinus Torvalds 	if (irq_installed) {
14161da177e4SLinus Torvalds 		sound_silence();
14171da177e4SLinus Torvalds 		dmasound.mach.irqcleanup();
14181da177e4SLinus Torvalds 		irq_installed = 0;
14191da177e4SLinus Torvalds 	}
14201da177e4SLinus Torvalds 
14211da177e4SLinus Torvalds 	write_sq_release_buffers();
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 	if (mixer_unit >= 0)
14241da177e4SLinus Torvalds 		unregister_sound_mixer(mixer_unit);
14251da177e4SLinus Torvalds 	if (state_unit >= 0)
14261da177e4SLinus Torvalds 		unregister_sound_special(state_unit);
14271da177e4SLinus Torvalds 	if (sq_unit >= 0)
14281da177e4SLinus Torvalds 		unregister_sound_dsp(sq_unit);
14291da177e4SLinus Torvalds }
14301da177e4SLinus Torvalds 
dmasound_setup(char * str)1431*357ad4d8SMiles Chen static int __maybe_unused dmasound_setup(char *str)
14321da177e4SLinus Torvalds {
14331da177e4SLinus Torvalds 	int ints[6], size;
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds 	str = get_options(str, ARRAY_SIZE(ints), ints);
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds 	/* check the bootstrap parameter for "dmasound=" */
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds 	/* FIXME: other than in the most naive of cases there is no sense in these
14401da177e4SLinus Torvalds 	 *	  buffers being other than powers of two.  This is not checked yet.
14411da177e4SLinus Torvalds 	 */
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds 	switch (ints[0]) {
14441da177e4SLinus Torvalds 	case 3:
14451da177e4SLinus Torvalds 		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
14461da177e4SLinus Torvalds 			printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
14471da177e4SLinus Torvalds 		else
14481da177e4SLinus Torvalds 			catchRadius = ints[3];
1449c0dbbdadSGustavo A. R. Silva 		fallthrough;
14501da177e4SLinus Torvalds 	case 2:
14511da177e4SLinus Torvalds 		if (ints[1] < MIN_BUFFERS)
14521da177e4SLinus Torvalds 			printk("dmasound_setup: invalid number of buffers, using default = %d\n", numWriteBufs);
14531da177e4SLinus Torvalds 		else
14541da177e4SLinus Torvalds 			numWriteBufs = ints[1];
1455c0dbbdadSGustavo A. R. Silva 		fallthrough;
14561da177e4SLinus Torvalds 	case 1:
14571da177e4SLinus Torvalds 		if ((size = ints[2]) < 256) /* check for small buffer specs */
14581da177e4SLinus Torvalds 			size <<= 10 ;
14591da177e4SLinus Torvalds                 if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
14601da177e4SLinus Torvalds                         printk("dmasound_setup: invalid write buffer size, using default = %d\n", writeBufSize);
14611da177e4SLinus Torvalds                 else
14621da177e4SLinus Torvalds                         writeBufSize = size;
14631da177e4SLinus Torvalds 	case 0:
14641da177e4SLinus Torvalds 		break;
14651da177e4SLinus Torvalds 	default:
14661da177e4SLinus Torvalds 		printk("dmasound_setup: invalid number of arguments\n");
14671da177e4SLinus Torvalds 		return 0;
14681da177e4SLinus Torvalds 	}
14691da177e4SLinus Torvalds 	return 1;
14701da177e4SLinus Torvalds }
14711da177e4SLinus Torvalds 
14721da177e4SLinus Torvalds __setup("dmasound=", dmasound_setup);
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds     /*
14751da177e4SLinus Torvalds      *  Conversion tables
14761da177e4SLinus Torvalds      */
14771da177e4SLinus Torvalds 
14781da177e4SLinus Torvalds #ifdef HAS_8BIT_TABLES
14791da177e4SLinus Torvalds /* 8 bit mu-law */
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds char dmasound_ulaw2dma8[] = {
14821da177e4SLinus Torvalds 	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
14831da177e4SLinus Torvalds 	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
14841da177e4SLinus Torvalds 	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
14851da177e4SLinus Torvalds 	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
14861da177e4SLinus Torvalds 	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
14871da177e4SLinus Torvalds 	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
14881da177e4SLinus Torvalds 	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
14891da177e4SLinus Torvalds 	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
14901da177e4SLinus Torvalds 	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
14911da177e4SLinus Torvalds 	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
14921da177e4SLinus Torvalds 	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
14931da177e4SLinus Torvalds 	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
14941da177e4SLinus Torvalds 	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
14951da177e4SLinus Torvalds 	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
14961da177e4SLinus Torvalds 	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
14971da177e4SLinus Torvalds 	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
14981da177e4SLinus Torvalds 	125,	121,	117,	113,	109,	105,	101,	97,
14991da177e4SLinus Torvalds 	93,	89,	85,	81,	77,	73,	69,	65,
15001da177e4SLinus Torvalds 	62,	60,	58,	56,	54,	52,	50,	48,
15011da177e4SLinus Torvalds 	46,	44,	42,	40,	38,	36,	34,	32,
15021da177e4SLinus Torvalds 	30,	29,	28,	27,	26,	25,	24,	23,
15031da177e4SLinus Torvalds 	22,	21,	20,	19,	18,	17,	16,	15,
15041da177e4SLinus Torvalds 	15,	14,	14,	13,	13,	12,	12,	11,
15051da177e4SLinus Torvalds 	11,	10,	10,	9,	9,	8,	8,	7,
15061da177e4SLinus Torvalds 	7,	7,	6,	6,	6,	6,	5,	5,
15071da177e4SLinus Torvalds 	5,	5,	4,	4,	4,	4,	3,	3,
15081da177e4SLinus Torvalds 	3,	3,	3,	3,	2,	2,	2,	2,
15091da177e4SLinus Torvalds 	2,	2,	2,	2,	1,	1,	1,	1,
15101da177e4SLinus Torvalds 	1,	1,	1,	1,	1,	1,	1,	1,
15111da177e4SLinus Torvalds 	0,	0,	0,	0,	0,	0,	0,	0,
15121da177e4SLinus Torvalds 	0,	0,	0,	0,	0,	0,	0,	0,
15131da177e4SLinus Torvalds 	0,	0,	0,	0,	0,	0,	0,	0
15141da177e4SLinus Torvalds };
15151da177e4SLinus Torvalds 
15161da177e4SLinus Torvalds /* 8 bit A-law */
15171da177e4SLinus Torvalds 
15181da177e4SLinus Torvalds char dmasound_alaw2dma8[] = {
15191da177e4SLinus Torvalds 	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
15201da177e4SLinus Torvalds 	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
15211da177e4SLinus Torvalds 	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
15221da177e4SLinus Torvalds 	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
15231da177e4SLinus Torvalds 	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
15241da177e4SLinus Torvalds 	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
15251da177e4SLinus Torvalds 	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
15261da177e4SLinus Torvalds 	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
15271da177e4SLinus Torvalds 	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
15281da177e4SLinus Torvalds 	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
15291da177e4SLinus Torvalds 	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
15301da177e4SLinus Torvalds 	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
15311da177e4SLinus Torvalds 	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
15321da177e4SLinus Torvalds 	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
15331da177e4SLinus Torvalds 	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
15341da177e4SLinus Torvalds 	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
15351da177e4SLinus Torvalds 	21,	20,	23,	22,	17,	16,	19,	18,
15361da177e4SLinus Torvalds 	29,	28,	31,	30,	25,	24,	27,	26,
15371da177e4SLinus Torvalds 	10,	10,	11,	11,	8,	8,	9,	9,
15381da177e4SLinus Torvalds 	14,	14,	15,	15,	12,	12,	13,	13,
15391da177e4SLinus Torvalds 	86,	82,	94,	90,	70,	66,	78,	74,
15401da177e4SLinus Torvalds 	118,	114,	126,	122,	102,	98,	110,	106,
15411da177e4SLinus Torvalds 	43,	41,	47,	45,	35,	33,	39,	37,
15421da177e4SLinus Torvalds 	59,	57,	63,	61,	51,	49,	55,	53,
15431da177e4SLinus Torvalds 	1,	1,	1,	1,	1,	1,	1,	1,
15441da177e4SLinus Torvalds 	1,	1,	1,	1,	1,	1,	1,	1,
15451da177e4SLinus Torvalds 	0,	0,	0,	0,	0,	0,	0,	0,
15461da177e4SLinus Torvalds 	0,	0,	0,	0,	0,	0,	0,	0,
15471da177e4SLinus Torvalds 	5,	5,	5,	5,	4,	4,	4,	4,
15481da177e4SLinus Torvalds 	7,	7,	7,	7,	6,	6,	6,	6,
15491da177e4SLinus Torvalds 	2,	2,	2,	2,	2,	2,	2,	2,
15501da177e4SLinus Torvalds 	3,	3,	3,	3,	3,	3,	3,	3
15511da177e4SLinus Torvalds };
15521da177e4SLinus Torvalds #endif /* HAS_8BIT_TABLES */
15531da177e4SLinus Torvalds 
15541da177e4SLinus Torvalds     /*
15551da177e4SLinus Torvalds      *  Visible symbols for modules
15561da177e4SLinus Torvalds      */
15571da177e4SLinus Torvalds 
15581da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound);
15591da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound_init);
15601da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound_deinit);
15611da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound_write_sq);
15621da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound_catchRadius);
15631da177e4SLinus Torvalds #ifdef HAS_8BIT_TABLES
15641da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound_ulaw2dma8);
15651da177e4SLinus Torvalds EXPORT_SYMBOL(dmasound_alaw2dma8);
15661da177e4SLinus Torvalds #endif
1567