patch-2.1.77 linux/drivers/sound/audio.c

Next file: linux/drivers/sound/dmabuf.c
Previous file: linux/drivers/sound/ad1848.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.76/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
@@ -11,6 +11,9 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ */
 
 #include <linux/config.h>
 
@@ -345,197 +348,157 @@
 }
 
 int audio_ioctl(int dev, struct fileinfo *file_must_not_be_used,
-	    unsigned int cmd, caddr_t arg)
+		unsigned int cmd, caddr_t arg)
 {
-	int val;
+	int val, info, count;
+	unsigned long flags;
+	struct dma_buffparms *dmap;
 
 	dev = dev >> 4;
 
-	if (((cmd >> 8) & 0xff) == 'C')
-	{
+	if (((cmd >> 8) & 0xff) == 'C')	{
 		if (audio_devs[dev]->coproc)	/* Coprocessor ioctl */
 			return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
 		/* else
-			printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
+		        printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
 		return -ENXIO;
-	}
-	else switch (cmd)
-	{
-		case SNDCTL_DSP_SYNC:
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return 0;
-
-			if (audio_devs[dev]->dmap_out->fragment_size == 0)
-				return 0;
-			sync_output(dev);
-			DMAbuf_sync(dev);
-			DMAbuf_reset(dev);
+	} else switch (cmd) {
+	case SNDCTL_DSP_SYNC:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 			return 0;
-
-		case SNDCTL_DSP_POST:
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return 0;
-			if (audio_devs[dev]->dmap_out->fragment_size == 0)
-				return 0;
-			audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
-			sync_output(dev);
-			dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0);
+		if (audio_devs[dev]->dmap_out->fragment_size == 0)
 			return 0;
+		sync_output(dev);
+		DMAbuf_sync(dev);
+		DMAbuf_reset(dev);
+		return 0;
 
-		case SNDCTL_DSP_RESET:
-			audio_mode[dev] = AM_NONE;
-			DMAbuf_reset(dev);
+	case SNDCTL_DSP_POST:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 			return 0;
+		if (audio_devs[dev]->dmap_out->fragment_size == 0)
+			return 0;
+		audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
+		sync_output(dev);
+		dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0);
+		return 0;
 
-		case SNDCTL_DSP_GETFMTS:
-			return (*(int *) arg = audio_devs[dev]->format_mask);
-
-		case SNDCTL_DSP_SETFMT:
-			val = *(int *) arg;
-			return (*(int *) arg = set_format(dev, val));
-
-		case SNDCTL_DSP_GETISPACE:
-			if (!(audio_devs[dev]->open_mode & OPEN_READ))
-				return 0;
-			if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-				return -EBUSY;
-
-			{
-				audio_buf_info  info;
-
-				int err = dma_ioctl(dev, cmd, (caddr_t) & info);
-
-				if (err < 0)
-					return err;
-
-				memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-				return 0;
-			}
-
-		case SNDCTL_DSP_GETOSPACE:
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				  return -EPERM;
-			if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-				  return -EBUSY;
-
-			{
-				audio_buf_info  info;
-
-				int err = dma_ioctl(dev, cmd, (caddr_t) & info);
-
-				if (err < 0)
-					return err;
+	case SNDCTL_DSP_RESET:
+		audio_mode[dev] = AM_NONE;
+		DMAbuf_reset(dev);
+		return 0;
 
-				memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-				return 0;
-			}
+	case SNDCTL_DSP_GETFMTS:
+		val = audio_devs[dev]->format_mask;
+		return __put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETFMT:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = set_format(dev, val);
+		return __put_user(val, (int *)arg);
 
-		case SNDCTL_DSP_NONBLOCK:
-			dev_nblock[dev] = 1;
+	case SNDCTL_DSP_GETISPACE:
+		if (!(audio_devs[dev]->open_mode & OPEN_READ))
 			return 0;
+		if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+			return -EBUSY;
+		return dma_ioctl(dev, cmd, arg);
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EPERM;
+		if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+			return -EBUSY;
+		return dma_ioctl(dev, cmd, arg);
+		
+	case SNDCTL_DSP_NONBLOCK:
+		dev_nblock[dev] = 1;
+		return 0;
 
-		case SNDCTL_DSP_GETCAPS:
-		{
-			int info = 1;	/* Revision level of this ioctl() */
-
+	case SNDCTL_DSP_GETCAPS:
+			info = 1 | DSP_CAP_MMAP;	/* Revision level of this ioctl() */
 			if (audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode == OPEN_READWRITE)
-					info |= DSP_CAP_DUPLEX;
-
+			    audio_devs[dev]->open_mode == OPEN_READWRITE)
+				info |= DSP_CAP_DUPLEX;
 			if (audio_devs[dev]->coproc)
 				info |= DSP_CAP_COPROC;
-
 			if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
 				info |= DSP_CAP_BATCH;
-
 			if (audio_devs[dev]->d->trigger)	/* Supports SETTRIGGER */
 				info |= DSP_CAP_TRIGGER;
-
-			info |= DSP_CAP_MMAP;
-
-			memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-			return 0;
-		}
-
-		case SOUND_PCM_WRITE_RATE:
-			val = *(int *) arg;
-			return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, val));
-
-		case SOUND_PCM_READ_RATE:
-			return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, 0));
-
-		case SNDCTL_DSP_STEREO:
-		{
-			int n;
-
-			n = *(int *) arg;
-			if (n > 1)
-			{
-/*				printk(KERN_DENUG "sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n);*/
-				return -EINVAL;
-			}
-			if (n < 0)
-				return -EINVAL;
-
-			return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, n + 1) - 1);
-		}
-
-		case SOUND_PCM_WRITE_CHANNELS:
-			val = *(int *) arg;
-			return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, val));
-
-		case SOUND_PCM_READ_CHANNELS:
-			return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, 0));
-
-		case SOUND_PCM_READ_BITS:
-			return (*(int *) arg = audio_devs[dev]->d->set_bits(dev, 0));
-
-		case SNDCTL_DSP_SETDUPLEX:
-			if (audio_devs[dev]->open_mode != OPEN_READWRITE)
-				return -EPERM;
-			if (audio_devs[dev]->flags & DMA_DUPLEX)
-				return 0;
-			else
-				return -EIO;
-
-		case SNDCTL_DSP_PROFILE:
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
-			if (audio_devs[dev]->open_mode & OPEN_READ)
-				audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
-			return 0;
-
-		case SNDCTL_DSP_GETODELAY:
-		{
-			int count;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return -EINVAL;
-			if (!(dmap->flags & DMA_ALLOC_DONE))
-				return *(int *) arg = 0;
-
-			save_flags (flags);
-			cli ();
-			/* Compute number of bytes that have been played */
-			count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
-			if (count < dmap->fragment_size && dmap->qhead != 0)
-				count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-			count += dmap->byte_counter;
-
-			/* Substract current count from the number of bytes written by app */
-			count = dmap->user_counter - count;
-			if (count < 0)
-				count = 0;
-			restore_flags (flags);
-
-			return *(int *) arg = count;
-		}
-		break;
-
-		default:
-			return dma_ioctl(dev, cmd, arg);
+			return __put_user(info, (int *)arg);
+			
+	case SOUND_PCM_WRITE_RATE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = audio_devs[dev]->d->set_speed(dev, val);
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_RATE:
+		val = audio_devs[dev]->d->set_speed(dev, 0);
+		return __put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_STEREO:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (val > 1 || val < 0)
+			return -EINVAL;
+		val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_WRITE_CHANNELS:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		val = audio_devs[dev]->d->set_channels(dev, val);
+		return __put_user(val, (int *)arg);
+
+	case SOUND_PCM_READ_CHANNELS:
+		val = audio_devs[dev]->d->set_channels(dev, 0);
+		return __put_user(val, (int *)arg);
+		
+	case SOUND_PCM_READ_BITS:
+		val = audio_devs[dev]->d->set_bits(dev, 0);
+		return __put_user(val, (int *)arg);
+
+	case SNDCTL_DSP_SETDUPLEX:
+		if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+			return -EPERM;
+		return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
+
+	case SNDCTL_DSP_PROFILE:
+		if (__get_user(val, (int *)arg))
+			return -EFAULT;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			audio_devs[dev]->dmap_out->applic_profile = val;
+		if (audio_devs[dev]->open_mode & OPEN_READ)
+			audio_devs[dev]->dmap_in->applic_profile = val;
+		return 0;
+		
+	case SNDCTL_DSP_GETODELAY:
+		dmap = audio_devs[dev]->dmap_out;
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+		if (!(dmap->flags & DMA_ALLOC_DONE))
+			return __put_user(0, (int *)arg);
+		
+		save_flags (flags);
+		cli();
+		/* Compute number of bytes that have been played */
+		count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
+		if (count < dmap->fragment_size && dmap->qhead != 0)
+			count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
+		count += dmap->byte_counter;
+		
+		/* Substract current count from the number of bytes written by app */
+		count = dmap->user_counter - count;
+		if (count < 0)
+			count = 0;
+		restore_flags (flags);
+		return __put_user(count, (int *)arg);
+		
+	default:
+		return dma_ioctl(dev, cmd, arg);
 	}
 }
 
@@ -676,14 +639,13 @@
 	dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
 }
 
-static int dma_subdivide(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
 {
-	if (fact == 0)
-	{
+	if (fact == 0) {
 		fact = dmap->subdivision;
 		if (fact == 0)
 			fact = 1;
-		return (*(int *) arg = fact);
+		return fact;
 	}
 	if (dmap->subdivision != 0 || dmap->fragment_size)	/* Too late to change */
 		return -EINVAL;
@@ -695,10 +657,10 @@
 		return -EINVAL;
 
 	dmap->subdivision = fact;
-	return (*(int *) arg = fact);
+	return fact;
 }
 
-static int dma_set_fragment(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
 {
 	int bytes, count;
 
@@ -747,311 +709,223 @@
 		dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
 
 	dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
-	if (arg)
-		return (*(int *) arg = bytes | ((count - 1) << 16));
-	else
-		return 0;
+	return bytes | ((count - 1) << 16);
 }
 
 int dma_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-
 	struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
 	struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
-
-	switch (cmd)
-	{
-
-		case SNDCTL_DSP_SUBDIVIDE:
-		{
-			int             fact;
-			int             ret = 0;
-
-			fact = *(int *) arg;
-
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				ret = dma_subdivide(dev, dmap_out, arg, fact);
-			if (ret < 0)
-				 return ret;
-
-			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
-				(audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode & OPEN_READ))
-					ret = dma_subdivide(dev, dmap_in, arg, fact);
-
+	struct dma_buffparms *dmap;
+	audio_buf_info info;
+	count_info cinfo;
+	int fact, ret, changed, bits, count, err;
+	unsigned long flags;
+
+	switch (cmd) {
+	case SNDCTL_DSP_SUBDIVIDE:
+		ret = 0;
+		if (__get_user(fact, (int *)arg))
+			return -EFAULT;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			ret = dma_subdivide(dev, dmap_out, fact);
+		if (ret < 0)
 			return ret;
-		}
-		break;
-
-		case SNDCTL_DSP_GETISPACE:
-		case SNDCTL_DSP_GETOSPACE:
-		{
-			struct dma_buffparms *dmap = dmap_out;
-
-			audio_buf_info *info = (audio_buf_info *) arg;
-
-			if (cmd == SNDCTL_DSP_GETISPACE &&
-				!(audio_devs[dev]->open_mode & OPEN_READ))
-					return -EINVAL;
-
-			if (cmd == SNDCTL_DSP_GETOSPACE &&
-				!(audio_devs[dev]->open_mode & OPEN_WRITE))
-					return -EINVAL;
-
-			if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
-				dmap = dmap_in;
-
-			if (dmap->mapping_flags & DMA_MAP_MAPPED)
-				return -EINVAL;
-
-			if (!(dmap->flags & DMA_ALLOC_DONE))
-				reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
-
-			info->fragstotal = dmap->nbufs;
+		if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+		    (audio_devs[dev]->flags & DMA_DUPLEX &&
+		     audio_devs[dev]->open_mode & OPEN_READ))
+			ret = dma_subdivide(dev, dmap_in, fact);
+		if (ret < 0)
+			return ret;
+		return __put_user(ret, (int *)arg);
 
-			if (cmd == SNDCTL_DSP_GETISPACE)
-				info->fragments = dmap->qlen;
-			else
-			{
-				if (!DMAbuf_space_in_queue(dev))
-					info->fragments = 0;
-				else
-				{
-					info->fragments = DMAbuf_space_in_queue(dev);
-					if (audio_devs[dev]->d->local_qlen)
-					{
-						int tmp = audio_devs[dev]->d->local_qlen(dev);
-
-						if (tmp && info->fragments)
-							tmp--;	/*
-								 * This buffer has been counted twice
-								 */
-						info->fragments -= tmp;
-					}
+	case SNDCTL_DSP_GETISPACE:
+	case SNDCTL_DSP_GETOSPACE:
+		dmap = dmap_out;
+		if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
+			return -EINVAL;
+		if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+		if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+			dmap = dmap_in;
+		if (dmap->mapping_flags & DMA_MAP_MAPPED)
+			return -EINVAL;
+		if (!(dmap->flags & DMA_ALLOC_DONE))
+			reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+		info.fragstotal = dmap->nbufs;
+		if (cmd == SNDCTL_DSP_GETISPACE)
+			info.fragments = dmap->qlen;
+		else {
+			if (!DMAbuf_space_in_queue(dev))
+				info.fragments = 0;
+			else {
+				info.fragments = DMAbuf_space_in_queue(dev);
+				if (audio_devs[dev]->d->local_qlen) {
+					int tmp = audio_devs[dev]->d->local_qlen(dev);
+					if (tmp && info.fragments)
+						tmp--;	/*
+							 * This buffer has been counted twice
+							 */
+					info.fragments -= tmp;
 				}
 			}
-
-			if (info->fragments < 0)
-				info->fragments = 0;
-			else if (info->fragments > dmap->nbufs)
-				info->fragments = dmap->nbufs;
-
-			info->fragsize = dmap->fragment_size;
-			info->bytes = info->fragments * dmap->fragment_size;
-
-			if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
-				info->bytes -= dmap->counts[dmap->qhead];
-			else
-			{
-				info->fragments = info->bytes / dmap->fragment_size;
-				info->bytes -= dmap->user_counter % dmap->fragment_size;
-			}
 		}
-		return 0;
-
-		case SNDCTL_DSP_SETTRIGGER:
-		{
-			unsigned long flags;
-
-			int bits;
-			int changed;
-
-			bits = *(int *) arg;
-			bits &= audio_devs[dev]->open_mode;
-
-			if (audio_devs[dev]->d->trigger == NULL)
-				return -EINVAL;
-
-			if (!(audio_devs[dev]->flags & DMA_DUPLEX))
-				if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
-				{
-					/* printk(KERN_WARNING "Sound: Device doesn't have full duplex capability\n");*/
-					return -EINVAL;
-				}
-			save_flags(flags);
-			cli();
-			changed = audio_devs[dev]->enable_bits ^ bits;
-
-			if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
-			{
-				int err;
-
-				reorganize_buffers(dev, dmap_in, 1);
-
-				if ((err = audio_devs[dev]->d->prepare_for_input(dev,
-					dmap_in->fragment_size, dmap_in->nbufs)) < 0)
-						return -err;
-
-				dmap_in->dma_mode = DMODE_INPUT;
-				audio_devs[dev]->enable_bits = bits;
-				DMAbuf_activate_recording(dev, dmap_in);
-			}
-			
-			if ((changed & bits) & PCM_ENABLE_OUTPUT &&
-				(dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
-				audio_devs[dev]->go)
-			{
-
-				if (!(dmap_out->flags & DMA_ALLOC_DONE))
-				{
-					reorganize_buffers(dev, dmap_out, 0);
-				}
-				dmap_out->dma_mode = DMODE_OUTPUT;
-				audio_devs[dev]->enable_bits = bits;
-				dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
-				DMAbuf_launch_output(dev, dmap_out);
-			}
+		if (info.fragments < 0)
+				info.fragments = 0;
+		else if (info.fragments > dmap->nbufs)
+			info.fragments = dmap->nbufs;
+
+		info.fragsize = dmap->fragment_size;
+		info.bytes = info.fragments * dmap->fragment_size;
+
+		if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+			info.bytes -= dmap->counts[dmap->qhead];
+		else {
+			info.fragments = info.bytes / dmap->fragment_size;
+			info.bytes -= dmap->user_counter % dmap->fragment_size;
+		}
+		return __copy_to_user(arg, &info, sizeof(info));
+
+	case SNDCTL_DSP_SETTRIGGER:
+		if (__get_user(bits, (int *)arg))
+			return -EFAULT;
+		bits &= audio_devs[dev]->open_mode;
+		if (audio_devs[dev]->d->trigger == NULL)
+			return -EINVAL;
+		if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
+		    (bits & PCM_ENABLE_OUTPUT))
+			return -EINVAL;
+		save_flags(flags);
+		cli();
+		changed = audio_devs[dev]->enable_bits ^ bits;
+		if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) {
+			reorganize_buffers(dev, dmap_in, 1);
+			if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+					     dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+				return -err;
+			dmap_in->dma_mode = DMODE_INPUT;
 			audio_devs[dev]->enable_bits = bits;
-			
-			if (changed && audio_devs[dev]->d->trigger)
-			{
-				audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
-			}
-			restore_flags(flags);
-		}
-		/* Falls through... */
-
-		case SNDCTL_DSP_GETTRIGGER:
-			return (*(int *) arg = audio_devs[dev]->enable_bits);
-
-		case SNDCTL_DSP_SETSYNCRO:
-
-			if (!audio_devs[dev]->d->trigger)
-				return -EINVAL;
-
-			audio_devs[dev]->d->trigger(dev, 0);
-			audio_devs[dev]->go = 0;
-			return 0;
-			break;
-
-		case SNDCTL_DSP_GETIPTR:
-		{
-			count_info      info;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = dmap_in;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_READ))
-				return -EINVAL;
-
-			save_flags(flags);
-			cli();
-			info.bytes = dmap->byte_counter;
-			info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_INPUT) & ~3;
-			if (info.ptr < dmap->fragment_size && dmap->qtail != 0)
-				info.bytes += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-
-			info.blocks = dmap->qlen;
-			info.bytes += info.ptr;
-			memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-
-			if (dmap->mapping_flags & DMA_MAP_MAPPED)
-				dmap->qlen = 0;	/* Reset interrupt counter */
-			restore_flags(flags);
-			return 0;
-		}
-		break;
-
-		case SNDCTL_DSP_GETOPTR:
-		{
-			count_info      info;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = dmap_out;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return -EINVAL;
-
-			save_flags(flags);
-			cli();
-			info.bytes = dmap->byte_counter;
-			info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT) & ~3;
-			if (info.ptr < dmap->fragment_size && dmap->qhead != 0)
-				info.bytes += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-			info.blocks = dmap->qlen;
-			info.bytes += info.ptr;
-			memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
-
-			if (dmap->mapping_flags & DMA_MAP_MAPPED)
-				dmap->qlen = 0;	/* Reset interrupt counter */
-
-			restore_flags(flags);
-			return 0;
-		}
-		break;
-
-		case SNDCTL_DSP_GETODELAY:
-		{
-			int count;
-			unsigned long   flags;
-			struct dma_buffparms *dmap = dmap_out;
-
-			if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-				return -EINVAL;
-			if (!(dmap->flags & DMA_ALLOC_DONE))
-				return (*(int *) arg = 0);
-
-			save_flags(flags);
-			cli();
-			/* Compute number of bytes that have been played */
-			count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
-			if (count < dmap->fragment_size && dmap->qhead != 0)
-				count += dmap->bytes_in_use;	/* Pointer wrap not handled yet */
-			count += dmap->byte_counter;
-
-			/* Substract current count from the number of bytes written by app */
-			count = dmap->user_counter - count;
-			if (count < 0)
-        			count = 0;
-			restore_flags (flags);
-
-			return (*(int *) arg = count);
+			DMAbuf_activate_recording(dev, dmap_in);
 		}
-
-		case SNDCTL_DSP_POST:
-			if (audio_devs[dev]->dmap_out->qlen > 0)
-				if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
-					DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
-			return 0;
-
-		case SNDCTL_DSP_GETBLKSIZE:
-		{
-			int             fragment_size;
-			struct dma_buffparms *dmap = dmap_out;
-
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				reorganize_buffers(dev, dmap_out,
-					(audio_devs[dev]->open_mode == OPEN_READ));
-			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
-				(audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode & OPEN_READ))
-					reorganize_buffers(dev, dmap_in,
-						(audio_devs[dev]->open_mode == OPEN_READ));
-			if (audio_devs[dev]->open_mode == OPEN_READ)
-					dmap = dmap_in;
-			fragment_size = dmap->fragment_size;
-			return (*(int *) arg = fragment_size);
+		if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+		    (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+		    audio_devs[dev]->go) {
+			if (!(dmap_out->flags & DMA_ALLOC_DONE))
+				reorganize_buffers(dev, dmap_out, 0);
+			dmap_out->dma_mode = DMODE_OUTPUT;
+			audio_devs[dev]->enable_bits = bits;
+			dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+			DMAbuf_launch_output(dev, dmap_out);
 		}
+		audio_devs[dev]->enable_bits = bits;
+		if (changed && audio_devs[dev]->d->trigger)
+			audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
+		restore_flags(flags);
+		/* Falls through... */
 
-		case SNDCTL_DSP_SETFRAGMENT:
-		{
-			int  fact;
-			int  ret = 0;
+	case SNDCTL_DSP_GETTRIGGER:
+		ret = audio_devs[dev]->enable_bits;
+		return __put_user(ret, (int *)arg);
+
+	case SNDCTL_DSP_SETSYNCRO:
+		if (!audio_devs[dev]->d->trigger)
+			return -EINVAL;
+		audio_devs[dev]->d->trigger(dev, 0);
+		audio_devs[dev]->go = 0;
+		return 0;
 
-			fact = *(int *) arg;
-			if (audio_devs[dev]->open_mode & OPEN_WRITE)
-				ret = dma_set_fragment(dev, dmap_out, arg, fact);
-			if (ret < 0)
-				return ret;
-
-			if (audio_devs[dev]->open_mode != OPEN_WRITE ||
-				(audio_devs[dev]->flags & DMA_DUPLEX &&
-				audio_devs[dev]->open_mode & OPEN_READ))
-				ret = dma_set_fragment(dev, dmap_in, arg, fact);
+	case SNDCTL_DSP_GETIPTR:
+		if (!(audio_devs[dev]->open_mode & OPEN_READ))
+			return -EINVAL;
+		save_flags(flags);
+		cli();
+		cinfo.bytes = dmap_in->byte_counter;
+		cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
+		if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
+			cinfo.bytes += dmap_in->bytes_in_use;	/* Pointer wrap not handled yet */
+		cinfo.blocks = dmap_in->qlen;
+		cinfo.bytes += cinfo.ptr;
+		if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
+			dmap_in->qlen = 0;	/* Reset interrupt counter */
+		restore_flags(flags);
+		return __copy_to_user(arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETOPTR:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+
+		save_flags(flags);
+		cli();
+		cinfo.bytes = dmap_out->byte_counter;
+		cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
+		if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
+			cinfo.bytes += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+		cinfo.blocks = dmap_out->qlen;
+		cinfo.bytes += cinfo.ptr;
+		if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
+			dmap_out->qlen = 0;	/* Reset interrupt counter */
+		restore_flags(flags);
+		return __copy_to_user(arg, &cinfo, sizeof(cinfo));
+
+	case SNDCTL_DSP_GETODELAY:
+		if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+			return -EINVAL;
+		if (!(dmap_out->flags & DMA_ALLOC_DONE))
+			return __put_user(0, (int *)arg);
+		save_flags(flags);
+		cli();
+		/* Compute number of bytes that have been played */
+		count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
+		if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
+			count += dmap_out->bytes_in_use;	/* Pointer wrap not handled yet */
+		count += dmap_out->byte_counter;
+		/* Substract current count from the number of bytes written by app */
+		count = dmap_out->user_counter - count;
+		if (count < 0)
+			count = 0;
+		restore_flags (flags);
+		return __put_user(count, (int *)arg);
+
+	case SNDCTL_DSP_POST:
+		if (audio_devs[dev]->dmap_out->qlen > 0)
+			if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+				DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
+		return 0;
 
+	case SNDCTL_DSP_GETBLKSIZE:
+		dmap = dmap_out;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
+		if (audio_devs[dev]->open_mode == OPEN_READ ||
+		    (audio_devs[dev]->flags & DMA_DUPLEX &&
+		     audio_devs[dev]->open_mode & OPEN_READ))
+			reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
+		if (audio_devs[dev]->open_mode == OPEN_READ)
+			dmap = dmap_in;
+		ret = dmap->fragment_size;
+		return __put_user(ret, (int *)arg);
+
+	case SNDCTL_DSP_SETFRAGMENT:
+		ret = 0;
+		if (__get_user(fact, (int *)arg))
+			return -EFAULT;
+		if (audio_devs[dev]->open_mode & OPEN_WRITE)
+			ret = dma_set_fragment(dev, dmap_out, fact);
+		if (ret < 0)
 			return ret;
-		}
-		break;
+		if (audio_devs[dev]->open_mode == OPEN_READ ||
+		    (audio_devs[dev]->flags & DMA_DUPLEX &&
+		     audio_devs[dev]->open_mode & OPEN_READ))
+			ret = dma_set_fragment(dev, dmap_in, fact);
+		if (ret < 0)
+			return ret;
+		if (!arg) /* don't know what this is good for, but preserve old semantics */
+			return 0;
+		return __put_user(ret, (int *)arg);
 
-		default:
-			return audio_devs[dev]->d->ioctl(dev, cmd, arg);
+	default:
+		if (!audio_devs[dev]->d->ioctl)
+			return -EINVAL;
+		return audio_devs[dev]->d->ioctl(dev, cmd, arg);
 	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov