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

Next file: linux/drivers/sound/soundmodule.h
Previous file: linux/drivers/sound/sound_timer.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.76/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
@@ -1,5 +1,5 @@
 /*
- * linux/kernel/chr_drv/sound/soundcard.c
+ * linux/drivers/sound/soundcard.c
  *
  * Soundcard driver for Linux
  */
@@ -10,6 +10,11 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+/*
+ * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ *                   integrated sound_switch.c and made /proc/sound (equals to /dev/sndstat,
+ *                   which should disappear in the near future)
+ */
 #include <linux/config.h>
 
 
@@ -27,6 +32,7 @@
 #include <linux/ioport.h>
 #endif				/* __KERNEL__ */
 #include <linux/delay.h>
+#include <linux/proc_fs.h>
 
 #define SOUND_CORE
 
@@ -63,27 +69,342 @@
 #define DMA_MAP_BUSY		2
 
 
+static int in_use = 0;	        /* Total # of open devices */
+unsigned long seq_time = 0;	/* Time for /dev/sequencer */
 
-static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+/*
+ * Table for configurable mixer volume handling
+ */
+static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
+static int num_mixer_volumes = 0;
+
+
+int *load_mixer_volumes(char *name, int *levels, int present)
+{
+	int             i, n;
+
+	for (i = 0; i < num_mixer_volumes; i++)
+		if (strcmp(name, mixer_vols[i].name) == 0)
+		  {
+			  if (present)
+				  mixer_vols[i].num = i;
+			  return mixer_vols[i].levels;
+		  }
+	if (num_mixer_volumes >= MAX_MIXER_DEV)
+	  {
+		  printk("Sound: Too many mixers (%s)\n", name);
+		  return levels;
+	  }
+	n = num_mixer_volumes++;
+
+	strcpy(mixer_vols[n].name, name);
+
+	if (present)
+		mixer_vols[n].num = n;
+	else
+		mixer_vols[n].num = -1;
+
+	for (i = 0; i < 32; i++)
+		mixer_vols[n].levels[i] = levels[i];
+	return mixer_vols[n].levels;
+}
+
+static int set_mixer_levels(caddr_t arg)
 {
-	int             dev;
+        /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
+	mixer_vol_table buf;   
 
-	dev = MINOR(file->f_dentry->d_inode->i_rdev);
+	if (__copy_from_user(&buf, arg, sizeof(buf)))
+		return -EFAULT;
+	load_mixer_volumes(buf.name, buf.levels, 0);
+	return __copy_to_user(arg, &buf, sizeof(buf));
+}
+
+static int get_mixer_levels(caddr_t arg)
+{
+	int n;
+
+	if (__get_user(n, (int *)(&(((mixer_vol_table *)arg)->num))))
+		return -EFAULT;
+	if (n < 0 || n >= num_mixer_volumes)
+		return -EINVAL;
+	return __copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table));
+}
+
+static int sound_proc_get_info(char *buffer, char **start, off_t offset, int length, int inout)
+{
+	int len, i, drv;
+        off_t pos = 0;
+        off_t begin = 0;
+
+#ifdef MODULE
+#define MODULEPROCSTRING "Driver loaded as a module"
+#else
+#define MODULEPROCSTRING "Driver compiled into kernel"
+#endif
+	
+	len = sprintf(buffer, "OSS/Free:" SOUND_VERSION_STRING "\n"
+		      "Load type: " MODULEPROCSTRING "\n"
+		      "Kernel: %s %s %s %s %s\n"
+		      "Config options: %x\n\nInstalled drivers: \n", 
+		      system_utsname.sysname, system_utsname.nodename, system_utsname.release, 
+		      system_utsname.version, system_utsname.machine, SELECTED_SOUND_OPTIONS);
+	
+	for (i = 0; (i < num_sound_drivers) && (pos <= offset + length); i++) {
+		if (!sound_drivers[i].card_type)
+			continue;
+		len += sprintf(buffer + len, "Type %d: %s\n", 
+			       sound_drivers[i].card_type, sound_drivers[i].name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+	len += sprintf(buffer + len, "\nCard config: \n");
+
+	for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) {
+		if (!snd_installed_cards[i].card_type)
+			continue;
+		if (!snd_installed_cards[i].enabled)
+			len += sprintf(buffer + len, "(");
+		if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1)
+			len += sprintf(buffer + len, "%s", sound_drivers[drv].name);
+		if (snd_installed_cards[i].config.io_base)
+			len += sprintf(buffer + len, " at 0x%x", snd_installed_cards[i].config.io_base);
+		if (snd_installed_cards[i].config.irq != 0)
+			len += sprintf(buffer + len, " irq %d", abs(snd_installed_cards[i].config.irq));
+		if (snd_installed_cards[i].config.dma != -1) {
+			len += sprintf(buffer + len, " drq %d", snd_installed_cards[i].config.dma);
+			if (snd_installed_cards[i].config.dma2 != -1)
+				len += sprintf(buffer + len, ",%d", snd_installed_cards[i].config.dma2);
+		}
+		if (!snd_installed_cards[i].enabled)
+			len += sprintf(buffer + len, ")");
+		len += sprintf(buffer + len, "\n");
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+	if (!sound_started)
+		len += sprintf(buffer + len, "\n\n***** Sound driver not started *****\n\n");
+#ifndef CONFIG_AUDIO
+	len += sprintf(buffer + len, "\nAudio devices: NOT ENABLED IN CONFIG\n");
+#else
+	len += sprintf(buffer + len, "\nAudio devices:\n");
+	for (i = 0; (i < num_audiodevs) && (pos <= offset + length); i++) {
+		if (audio_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s%s\n", i, audio_devs[i]->name, 
+			       audio_devs[i]->flags & DMA_DUPLEX ? " (DUPLEX)" : "");
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+#ifndef CONFIG_SEQUENCER
+	len += sprintf(buffer + len, "\nSynth devices: NOT ENABLED IN CONFIG\n");
+#else
+	len += sprintf(buffer + len, "\nSynth devices:\n");
+	for (i = 0; (i < num_synths) && (pos <= offset + length); i++) {
+		if (synth_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, synth_devs[i]->info->name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+#ifndef CONFIG_MIDI
+	len += sprintf(buffer + len, "\nMidi devices: NOT ENABLED IN CONFIG\n");
+#else
+	len += sprintf(buffer + len, "\nMidi devices:\n");
+	for (i = 0; (i < num_midis) && (pos <= offset + length); i++) {
+		if (midi_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, midi_devs[i]->info.name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+#ifdef CONFIG_SEQUENCER
+	len += sprintf(buffer + len, "\nTimers:\n");
+
+	for (i = 0; (i < num_sound_timers) && (pos <= offset + length); i++) {
+		if (sound_timer_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, sound_timer_devs[i]->info.name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+#endif
+
+	len += sprintf(buffer + len, "\nMixers:\n");
+	for (i = 0; (i < num_mixers) && (pos <= offset + length); i++) {
+		if (mixer_devs[i] == NULL)
+			continue;
+		len += sprintf(buffer + len, "%d: %s\n", i, mixer_devs[i]->name);
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+	}
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+        if (len > length) 
+		len = length;
+        return len;
+}
+
+static struct proc_dir_entry proc_root_sound = {
+        PROC_SOUND, 5, "sound",
+        S_IFREG | S_IRUGO, 1, 0, 0,
+        0, NULL, sound_proc_get_info
+};
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+/*
+ * basically copied from fs/proc/generic.c:proc_file_read 
+ * should be removed sometime in the future together with /dev/sndstat
+ * (a symlink /dev/sndstat -> /proc/sound will do as well)
+ */
+static ssize_t sndstat_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+        char    *page;
+        ssize_t retval=0;
+        int     eof=0;
+        ssize_t n, count;
+        char    *start;
+
+        if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+                return -ENOMEM;
+
+        while ((nbytes > 0) && !eof)
+        {
+                count = MIN(PROC_BLOCK_SIZE, nbytes);
+
+                start = NULL;
+		n = sound_proc_get_info(page, &start, *ppos, count, 0);
+		if (n < count)
+			eof = 1;
+                        
+                if (!start) {
+                        /*
+                         * For proc files that are less than 4k
+                         */
+                        start = page + *ppos;
+                        n -= *ppos;
+                        if (n <= 0)
+                                break;
+                        if (n > count)
+                                n = count;
+                }
+                if (n == 0)
+                        break;  /* End of file */
+                if (n < 0) {
+                        if (retval == 0)
+                                retval = n;
+                        break;
+                }
+                
+                n -= copy_to_user(buf, start, n);       /* BUG ??? */
+                if (n == 0) {
+                        if (retval == 0)
+                                retval = -EFAULT;
+                        break;
+                }
+                
+                *ppos += n;     /* Move down the file */
+                nbytes -= n;
+                buf += n;
+                retval += n;
+        }
+        free_page((unsigned long) page);
+        return retval;
+}
+
+
+static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
+	DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+		return sndstat_file_read(file, buf, count, ppos);
+
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_read(dev, &files[dev], buf, count);
+#endif
 
-	return sound_read_sw(dev, &files[dev], buf, count);
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_read(dev, &files[dev], buf, count);
+#endif
+
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		return MIDIbuf_read(dev, &files[dev], buf, count);
+#endif
+
+	default:;
+	}
+	return -EINVAL;
 }
 
 static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-	int             dev;
-
-	dev = MINOR(file->f_dentry->d_inode->i_rdev);
+	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
+	DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
+	switch (dev & 0x0f) {
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_write(dev, &files[dev], buf, count);
+#endif
+
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_write(dev, &files[dev], buf, count);
+#endif
 
-	return sound_write_sw(dev, &files[dev], buf, count);
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		return MIDIbuf_write(dev, &files[dev], buf, count);
+#endif
+	}
+	return -EINVAL;
 }
 
 static long long sound_lseek(struct file *file, long long offset, int orig)
@@ -93,20 +414,17 @@
 
 static int sound_open(struct inode *inode, struct file *file)
 {
-	int             dev, retval;
+	int dev, retval;
 	struct fileinfo tmp_file;
 
-	if (is_unloading)
-	{
-/*		  printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/
-		  return -EBUSY;
+	if (is_unloading) {
+		/* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/
+		return -EBUSY;
 	}
 	dev = MINOR(inode->i_rdev);
-
-	if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS)
-	{
-/*		  printk("SoundCard Error: The soundcard system has not been configured\n");*/
-		  return -ENXIO;
+	if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) {
+		/* printk("SoundCard Error: The soundcard system has not been configured\n");*/
+		return -ENXIO;
 	}
 	tmp_file.mode = 0;
 	tmp_file.flags = file->f_flags;
@@ -117,130 +435,259 @@
 		tmp_file.mode = OPEN_READ;
 	if ((tmp_file.flags & O_ACCMODE) == O_WRONLY)
 		tmp_file.mode = OPEN_WRITE;
+	DEB(printk("sound_open(dev=%d)\n", dev));
+	if ((dev >= SND_NDEVS) || (dev < 0)) {
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+	switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+		break;
+
+	case SND_DEV_CTL:
+		if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers)
+			return -ENXIO;
+		break;
+
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		if ((retval = sequencer_open(dev, &tmp_file)) < 0)
+			return retval;
+		break;
+#endif
 
-	if ((retval = sound_open_sw(dev, &tmp_file)) < 0)
-		return retval;
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		if ((retval = MIDIbuf_open(dev, &tmp_file)) < 0)
+			return retval;
+		break;
+#endif
 
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		if ((retval = audio_open(dev, &tmp_file)) < 0)
+			return retval;
+		break;
+#endif
+
+	default:
+		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		return -ENXIO;
+	}
+	in_use++;
 #ifdef MODULE
 	SOUND_INC_USE_COUNT;
 #endif
-
-	memcpy((char *) &files[dev], (char *) &tmp_file, sizeof(tmp_file));
-	return retval;
+	memcpy(&files[dev], &tmp_file, sizeof(tmp_file));
+	return 0;
 }
 
 static int sound_release(struct inode *inode, struct file *file)
 {
-	int             dev;
-
-	dev = MINOR(inode->i_rdev);
+	int dev = MINOR(inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
+	DEB(printk("sound_release(dev=%d)\n", dev));
+	switch (dev & 0x0f) {
+	case SND_DEV_STATUS:
+	case SND_DEV_CTL:
+		break;
+		
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		sequencer_release(dev, &files[dev]);
+		break;
+#endif
 
-	sound_release_sw(dev, &files[dev]);
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		MIDIbuf_release(dev, &files[dev]);
+		break;
+#endif
+
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		audio_release(dev, &files[dev]);
+		break;
+#endif
+
+	default:
+		printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
+	}
+	in_use--;
 #ifdef MODULE
 	SOUND_DEC_USE_COUNT;
 #endif
 	return 0;
 }
 
-static int sound_ioctl(struct inode *inode, struct file *file,
-	    unsigned int cmd, unsigned long arg)
+static int get_mixer_info(int dev, caddr_t arg)
 {
-	int             dev, err;
-	int             len = 0;
-	int             alloced = 0;
-	char           *ptr = (char *) arg;
+	mixer_info info;
+	int i;
 
-	dev = MINOR(inode->i_rdev);
+	if (dev < 0 || dev >= num_mixers)
+		return -ENXIO;
+	strcpy(info.id, mixer_devs[dev]->id);
+	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+		info.name[i] = mixer_devs[dev]->name[i];
+	info.name[i] = 0;
+	info.modify_counter = mixer_devs[dev]->modify_counter;
+	if (__copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
 
-	files[dev].flags = file->f_flags;
+static int get_old_mixer_info(int dev, caddr_t arg)
+{
+	_old_mixer_info info;
+	int             i;
 
-	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0)
-	{
+	if (dev < 0 || dev >= num_mixers)
+		return -ENXIO;
+	strcpy(info.id, mixer_devs[dev]->id);
+	for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+		info.name[i] = mixer_devs[dev]->name[i];
+	info.name[i] = 0;
+	if (__copy_to_user(arg, &info,  sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+static int sound_mixer_ioctl(int mixdev, unsigned int cmd, caddr_t arg)
+{
+	if (cmd == SOUND_MIXER_INFO)
+		return get_mixer_info(mixdev, arg);
+	if (cmd == SOUND_OLD_MIXER_INFO)
+		return get_old_mixer_info(mixdev, arg);
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		mixer_devs[mixdev]->modify_counter++;
+	if (!mixer_devs[mixdev]->ioctl)
+		return -EINVAL;
+	return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
+}
+
+static int sound_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	int err, len = 0, dtype, mixdev;
+	int dev = MINOR(inode->i_rdev);
+
+	files[dev].flags = file->f_flags;
+	if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
 		/*
 		 * Have to validate the address given by the process.
 		 */
-
 		len = _SIOC_SIZE(cmd);
 		if (len < 1 || len > 65536 || arg == 0)
 			return -EFAULT;
-
-		ptr = vmalloc(len);
-		alloced = 1;
-		if (ptr == NULL)
-			return -EFAULT;
-
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-		{
-			if ((err = verify_area(VERIFY_READ, (void *) arg, len)) < 0)
+			if ((err = verify_area(VERIFY_READ, (void *)arg, len)) < 0)
 				return err;
-			copy_from_user(ptr, (char *) arg, len);
-		}
 		if (_SIOC_DIR(cmd) & _SIOC_READ)
-		{
-			if ((err = verify_area(VERIFY_WRITE, (void *) arg, len)) < 0)
+			if ((err = verify_area(VERIFY_WRITE, (void *)arg, len)) < 0)
 				return err;
-		}
 	}
-	err = sound_ioctl_sw(dev, &files[dev], cmd, (caddr_t) ptr);
+	DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+	if (cmd == OSS_GETVERSION)
+		return __put_user(SOUND_VERSION, (int *)arg);
+	
+	if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0)	/* Mixer ioctl */
+		if ((dev & 0x0f) != SND_DEV_CTL) {
+			dtype = dev & 0x0f;
+			switch (dtype) {
+#ifdef CONFIG_AUDIO
+			case SND_DEV_DSP:
+			case SND_DEV_DSP16:
+			case SND_DEV_AUDIO:
+				mixdev = audio_devs[dev >> 4]->mixer_dev;
+				if (mixdev < 0 || mixdev >= num_mixers)
+					return -ENXIO;
+				return sound_mixer_ioctl(mixdev, cmd, (caddr_t)arg);
+#endif
+				
+			default:
+				return sound_mixer_ioctl(dev, cmd, (caddr_t)arg);
+			}
+		}
+	switch (dev & 0x0f) {
+	case SND_DEV_CTL:
+		if (cmd == SOUND_MIXER_GETLEVELS)
+			return get_mixer_levels((caddr_t)arg);
+		if (cmd == SOUND_MIXER_SETLEVELS)
+			return set_mixer_levels((caddr_t)arg);
+		if (!num_mixers)
+			return -ENXIO;
+		dev = dev >> 4;
+		if (dev >= num_mixers)
+			return -ENXIO;
+		return sound_mixer_ioctl(dev, cmd, (caddr_t)arg);
+		break;
+
+#ifdef CONFIG_SEQUENCER
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+#endif
 
-	if (_SIOC_DIR(cmd) & _SIOC_READ)
-		copy_to_user((char *) arg, ptr, len);
+#ifdef CONFIG_AUDIO
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return audio_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		break;
+#endif
 
-	if (ptr != NULL && alloced)
-		vfree(ptr);
+#ifdef CONFIG_MIDI
+	case SND_DEV_MIDIN:
+		return MIDIbuf_ioctl(dev, &files[dev], cmd, (caddr_t)arg);
+		break;
+#endif
 
-	return ((err < 0) ? err : 0);
+	}
+	return -EINVAL;
 }
 
 static int sound_select(struct inode *inode, struct file *file, int sel_type, poll_table * wait)
 {
-	int             dev;
-
-	dev = MINOR(inode->i_rdev);
+	int dev = MINOR(inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
-
 	DEB(printk("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
-
-	switch (dev & 0x0f)
-	{
+	switch (dev & 0x0f) {
 #if defined(CONFIG_SEQUENCER) || defined(MODULE)
-		case SND_DEV_SEQ:
-		case SND_DEV_SEQ2:
-			return sequencer_select(dev, &files[dev], sel_type, wait);
-			break;
+	case SND_DEV_SEQ:
+	case SND_DEV_SEQ2:
+		return sequencer_select(dev, &files[dev], sel_type, wait);
 #endif
 
 #if defined(CONFIG_MIDI)
-		case SND_DEV_MIDIN:
-			return MIDIbuf_select(dev, &files[dev], sel_type, wait);
-			break;
+	case SND_DEV_MIDIN:
+		return MIDIbuf_select(dev, &files[dev], sel_type, wait);
 #endif
 
 #if defined(CONFIG_AUDIO) || defined(MODULE)
-		case SND_DEV_DSP:
-		case SND_DEV_DSP16:
-		case SND_DEV_AUDIO:
-			return DMAbuf_select(dev >> 4, &files[dev], sel_type, wait);
-			break;
+	case SND_DEV_DSP:
+	case SND_DEV_DSP16:
+	case SND_DEV_AUDIO:
+		return DMAbuf_select(dev >> 4, &files[dev], sel_type, wait);
 #endif
-
-		default:
-			return 0;
 	}
-
 	return 0;
 }
 
 static unsigned int sound_poll(struct file *file, poll_table * wait)
 {
-	struct inode   *inode;
-	int             ret = 0;
+	struct inode *inode;
+	int ret = 0;
 
 	inode = file->f_dentry->d_inode;
-
 	if (sound_select(inode, file, SEL_IN, wait))
 		ret |= POLLIN;
 	if (sound_select(inode, file, SEL_OUT, wait))
@@ -250,11 +697,10 @@
 
 static int sound_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	int             dev, dev_class;
-	unsigned long   size;
+	int dev_class;
+	unsigned long size;
 	struct dma_buffparms *dmap = NULL;
-
-	dev = MINOR(file->f_dentry->d_inode->i_rdev);
+	int dev = MINOR(file->f_dentry->d_inode->i_rdev);
 
 	files[dev].flags = file->f_flags;
 
@@ -418,7 +864,9 @@
 
 	if (sound_nblocks >= 1024)
 		printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
-
+	
+	if (proc_register(&proc_root, &proc_root_sound))
+		printk(KERN_ERR "sound: registering /proc/sound failed\n");
 	return 0;
 }
 
@@ -433,6 +881,8 @@
 	{
 		return;
 	}
+        if (proc_unregister(&proc_root, PROC_SOUND))
+		printk(KERN_ERR "sound: unregistering /proc/sound failed\n");
 	if (chrdev_registered)
 		unregister_chrdev(sound_major, "sound");
 
@@ -779,3 +1229,35 @@
 	}
 	printk("\n");
 }
+
+/*
+ *	Module and lock management
+ */
+ 
+struct notifier_block *sound_locker=(struct notifier_block *)0;
+static int lock_depth = 0;
+
+#define SOUND_INC_USE_COUNT	do { notifier_call_chain(&sound_locker, 1, 0); lock_depth++; } while(0);
+#define SOUND_DEC_USE_COUNT	do { notifier_call_chain(&sound_locker, 0, 0); lock_depth--; } while(0);
+
+/*
+ *	When a sound module is registered we need to bring it to the current
+ *	lock level...
+ */
+ 
+void sound_notifier_chain_register(struct notifier_block *bl)
+{
+	int ct=0;
+	
+	notifier_chain_register(&sound_locker, bl);
+	/*
+	 *	Normalise the lock count by calling the entry directly. We
+	 *	have to call the module as it owns its own use counter
+	 */
+	while(ct<lock_depth)
+	{
+		bl->notifier_call(bl, 1, 0);
+		ct++;
+	}
+}
+

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