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

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

diff -u --recursive --new-file v1.3.8/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
@@ -26,13 +26,15 @@
  * SUCH DAMAGE.
  *
  */
+/*
+ * Created modular version by Peter Trattler (peter@sbox.tu-graz.ac.at)
+ */
 
 #include "sound_config.h"
 
 #ifdef CONFIGURE_SOUNDCARD
 
 #include <linux/major.h>
-#include <linux/mm.h>
 
 static int      soundcards_installed = 0;	/* Number of installed
 
@@ -44,15 +46,9 @@
 int
 snd_ioctl_return (int *addr, int value)
 {
-  int error;
-
   if (value < 0)
     return value;
 
-  error = verify_area(VERIFY_WRITE, addr, sizeof(int));
-  if (error)
-    return error;
-
   PUT_WORD_TO_USER (addr, 0, value);
   return 0;
 }
@@ -73,6 +69,11 @@
 {
   int             dev;
 
+#ifdef MODULE
+  int             err;
+
+#endif
+
   dev = inode->i_rdev;
   dev = MINOR (dev);
 
@@ -88,7 +89,8 @@
 static int
 sound_open (struct inode *inode, struct file *file)
 {
-  int             dev;
+  int             dev, retval;
+  struct fileinfo tmp_file;
 
   dev = inode->i_rdev;
   dev = MINOR (dev);
@@ -99,16 +101,25 @@
       return RET_ERROR (ENXIO);
     }
 
-  files[dev].mode = 0;
+  tmp_file.mode = 0;
+  tmp_file.filp = file;
 
   if ((file->f_flags & O_ACCMODE) == O_RDWR)
-    files[dev].mode = OPEN_READWRITE;
+    tmp_file.mode = OPEN_READWRITE;
   if ((file->f_flags & O_ACCMODE) == O_RDONLY)
-    files[dev].mode = OPEN_READ;
+    tmp_file.mode = OPEN_READ;
   if ((file->f_flags & O_ACCMODE) == O_WRONLY)
-    files[dev].mode = OPEN_WRITE;
+    tmp_file.mode = OPEN_WRITE;
 
-  return sound_open_sw (dev, &files[dev]);
+  if ((retval = sound_open_sw (dev, &tmp_file)) < 0)
+    return retval;
+
+#ifdef MODULE
+  MOD_INC_USE_COUNT;
+#endif
+
+  memcpy ((char *) &files[dev], (char *) &tmp_file, sizeof (tmp_file));
+  return retval;
 }
 
 static void
@@ -120,6 +131,9 @@
   dev = MINOR (dev);
 
   sound_release_sw (dev, &files[dev]);
+#ifdef MODULE
+  MOD_DEC_USE_COUNT;
+#endif
 }
 
 static int
@@ -131,6 +145,29 @@
   dev = inode->i_rdev;
   dev = MINOR (dev);
 
+  if (cmd & IOC_INOUT)
+    {
+      /*
+         * Have to validate the address given by the process.
+       */
+      int             len, err;
+
+      len = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
+
+      if (cmd & IOC_IN)
+	{
+	  if ((err = verify_area (VERIFY_READ, (void *) arg, len)) < 0)
+	    return err;
+	}
+
+      if (cmd & IOC_OUT)
+	{
+	  if ((err = verify_area (VERIFY_WRITE, (void *) arg, len)) < 0)
+	    return err;
+	}
+
+    }
+
   return sound_ioctl_sw (dev, &files[dev], cmd, arg);
 }
 
@@ -148,6 +185,7 @@
     {
 #ifndef EXCLUDE_SEQUENCER
     case SND_DEV_SEQ:
+    case SND_DEV_SEQ2:
       return sequencer_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
@@ -158,6 +196,14 @@
       break;
 #endif
 
+#ifndef EXCLUDE_AUDIO
+    case SND_DEV_DSP:
+    case SND_DEV_DSP16:
+    case SND_DEV_AUDIO:
+      return audio_select (dev, &files[dev], sel_type, wait);
+      break;
+#endif
+
     default:
       return 0;
     }
@@ -181,7 +227,9 @@
 long
 soundcard_init (long mem_start)
 {
+#ifndef MODULE
   register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+#endif
 
   soundcard_configured = 1;
 
@@ -212,6 +260,89 @@
   return mem_start;
 }
 
+#ifdef MODULE
+static unsigned long irqs = 0;
+void            snd_release_irq (int);
+static int      module_sound_mem_init (void);
+static void     module_sound_mem_release (void);
+
+static void
+free_all_irqs (void)
+{
+  int             i;
+
+  for (i = 0; i < 31; i++)
+    if (irqs & (1ul << i))
+      snd_release_irq (i);
+  irqs = 0;
+}
+
+char            kernel_version[] = UTS_RELEASE;
+
+static long     memory_pool = 0;
+static int      memsize = 70 * 1024;
+static int      debugmem = 0;	/* switched off by default */
+
+int
+init_module (void)
+{
+  long            lastbyte;
+  int             err;
+
+  printk ("sound: made modular by Peter Trattler (peter@sbox.tu-graz.ac.at)\n");
+  err = register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
+  if (err)
+    {
+      printk ("sound: driver already loaded/included in kernel\n");
+      return err;
+    }
+  memory_pool = (long) kmalloc (memsize, GFP_KERNEL);
+  if (memory_pool == 0l)
+    {
+      unregister_chrdev (SOUND_MAJOR, "sound");
+      return -ENOMEM;
+    }
+  lastbyte = soundcard_init (memory_pool);
+  if (lastbyte > memory_pool + memsize)
+    {
+      printk ("sound: Not enough memory; use : 'insmod sound.o memsize=%ld'\n",
+	      lastbyte - memory_pool);
+      kfree ((void *) memory_pool);
+      unregister_chrdev (SOUND_MAJOR, "sound");
+      free_all_irqs ();
+      return -ENOMEM;
+    }
+  err = module_sound_mem_init ();
+  if (err)
+    {
+      module_sound_mem_release ();
+      kfree ((void *) memory_pool);
+      unregister_chrdev (SOUND_MAJOR, "sound");
+      free_all_irqs ();
+      return err;
+    }
+  if (lastbyte < memory_pool + memsize)
+    printk ("sound: (Suggestion) too much memory; use : 'insmod sound.o memsize=%ld'\n",
+	    lastbyte - memory_pool);
+  return 0;
+}
+
+void
+cleanup_module (void)
+{
+  if (MOD_IN_USE)
+    printk ("sound: module busy -- remove delayed\n");
+  else
+    {
+      kfree ((void *) memory_pool);
+      unregister_chrdev (SOUND_MAJOR, "sound");
+      free_all_irqs ();
+      module_sound_mem_release ();
+    }
+}
+
+#endif
+
 void
 tenmicrosec (void)
 {
@@ -222,22 +353,19 @@
 }
 
 int
-snd_set_irq_handler (int interrupt_level, void (*hndlr) (int, struct pt_regs *))
+snd_set_irq_handler (int interrupt_level, INT_HANDLER_PROTO (), char *name)
 {
   int             retcode;
 
-  retcode = request_irq(interrupt_level, hndlr,
-#ifdef SND_SA_INTERRUPT
-	SA_INTERRUPT,
-#else
-	0,
-#endif
-	"sound");
-
+  retcode = request_irq (interrupt_level, hndlr, SA_INTERRUPT, name);
   if (retcode < 0)
     {
       printk ("Sound: IRQ%d already in use\n", interrupt_level);
     }
+#ifdef MODULE
+  else
+    irqs |= (1ul << interrupt_level);
+#endif
 
   return retcode;
 }
@@ -245,6 +373,9 @@
 void
 snd_release_irq (int vect)
 {
+#ifdef MODULE
+  irqs &= ~(1ul << vect);
+#endif
   free_irq (vect);
 }
 
@@ -286,6 +417,152 @@
     return 0;
 }
 
+#ifdef MODULE
+
+#ifdef KMALLOC_DMA_BROKEN
+#define KMALLOC_MEM_REGIONS 20
+
+static char    *dma_list[KMALLOC_MEM_REGIONS];
+static int      dma_last = 0;
+inline void
+add_to_dma_list (char *adr)
+{
+  dma_list[dma_last++] = adr;
+}
+
+#endif
+
+static int
+module_sound_mem_init (void)
+{
+  int             dev, ret = 0;
+  unsigned long   dma_pagesize;
+  char           *start_addr, *end_addr;
+  struct dma_buffparms *dmap;
+
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan >= 0)
+      {
+	dmap = audio_devs[dev]->dmap;
+	if (audio_devs[dev]->flags & DMA_AUTOMODE)
+	  audio_devs[dev]->buffcount = 1;
+
+	if (audio_devs[dev]->dmachan > 3)
+	  dma_pagesize = 131072;	/* 16bit dma: 128k */
+	else
+	  dma_pagesize = 65536;	/* 8bit dma: 64k */
+	if (debugmem)
+	  printk ("sound: dma-page-size %lu\n", dma_pagesize);
+	/* More sanity checks */
+
+	if (audio_devs[dev]->buffsize > dma_pagesize)
+	  audio_devs[dev]->buffsize = dma_pagesize;
+	audio_devs[dev]->buffsize &= 0xfffff000;	/* Truncate to n*4k */
+	if (audio_devs[dev]->buffsize < 4096)
+	  audio_devs[dev]->buffsize = 4096;
+	if (debugmem)
+	  printk ("sound: buffsize %lu\n", audio_devs[dev]->buffsize);
+	/* Now allocate the buffers */
+	for (dmap->raw_count = 0; dmap->raw_count < audio_devs[dev]->buffcount;
+	     dmap->raw_count++)
+	  {
+#ifdef KMALLOC_DMA_BROKEN
+	    start_addr = kmalloc (audio_devs[dev]->buffsize, GFP_KERNEL);
+	    if (start_addr)
+	      {
+		if (debugmem)
+		  printk ("sound: trying 0x%lx for DMA\n", (long) start_addr);
+		if (valid_dma_page ((unsigned long) start_addr,
+				    audio_devs[dev]->buffsize,
+				    dma_pagesize))
+		  add_to_dma_list (start_addr);
+		else
+		  {
+		    kfree (start_addr);
+		    start_addr = kmalloc (audio_devs[dev]->buffsize * 2,
+					  GFP_KERNEL);	/* what a waste :-( */
+		    if (start_addr)
+		      {
+			if (debugmem)
+			  printk ("sound: failed; trying 0x%lx aligned to",
+				  (long) start_addr);
+			add_to_dma_list (start_addr);
+			/* now align it to the next dma-page boundary */
+			start_addr = (char *) (((long) start_addr
+						+ dma_pagesize - 1)
+					       & ~(dma_pagesize - 1));
+			if (debugmem)
+			  printk (" 0x%lx\n", (long) start_addr);
+		      }
+		  }
+	      }
+#else
+	    start_addr = kmalloc (audio_devs[dev]->buffsize,
+				  GFP_DMA | GFP_KERNEL);
+#endif
+	    if (start_addr == NULL)
+	      ret = -ENOMEM;	/* Can't stop the loop in this case, because
+				   * ...->raw_buf [...] must be initilized
+				   * to valid values (at least to NULL)
+				 */
+	    else
+	      {
+		/* make some checks */
+		end_addr = start_addr + audio_devs[dev]->buffsize - 1;
+		if (debugmem)
+		  printk ("sound: start 0x%lx, end 0x%lx\n",
+			  (long) start_addr, (long) end_addr);
+		/* now check if it fits into the same dma-pagesize */
+		if (((long) start_addr & ~(dma_pagesize - 1))
+		    != ((long) end_addr & ~(dma_pagesize - 1))
+		    || end_addr >= (char *) (16 * 1024 * 1024))
+		  {
+		    printk (
+			     "sound: kmalloc returned invalid address 0x%lx for %ld Bytes DMA-buffer\n",
+			     (long) start_addr,
+			     audio_devs[dev]->buffsize);
+		    ret = -EFAULT;
+		  }
+	      }
+	    dmap->raw_buf[dmap->raw_count] = start_addr;
+	    dmap->raw_buf_phys[dmap->raw_count] = (unsigned long) start_addr;
+	  }
+      }
+  return ret;
+}
+
+static void
+module_sound_mem_release (void)
+{
+#ifdef KMALLOC_DMA_BROKEN
+  int             i;
+
+  for (i = 0; i < dma_last; i++)
+    {
+      if (debugmem)
+	printk ("sound: freeing 0x%lx\n", (long) dma_list[i]);
+      kfree (dma_list[i]);
+    }
+#else
+  int             dev, i;
+
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan >= 0)
+      {
+	for (i = 0; i < audio_devs[dev]->buffcount; i++)
+	  if (audio_devs[dev]->dmap->raw_buf[i])
+	    {
+	      if (debugmem)
+		printk ("sound: freeing 0x%lx\n",
+			(long) (audio_devs[dev]->dmap->raw_buf[i]));
+	      kfree (audio_devs[dev]->dmap->raw_buf[i]);
+	    }
+      }
+#endif
+}
+
+#else /* !MODULE */
+
 void
 sound_mem_init (void)
 {
@@ -309,7 +586,7 @@
 	  audio_devs[dev]->buffcount = 1;
 
 	if (audio_devs[dev]->dmachan > 3 && audio_devs[dev]->buffsize > 65536)
-	  dma_pagesize = 131072;/* 128k */
+	  dma_pagesize = 131072;	/* 128k */
 	else
 	  dma_pagesize = 65536;
 
@@ -347,6 +624,8 @@
       }				/* for dev */
 }
 
+#endif /* !MODULE */
+
 #endif
 
 #else
@@ -365,5 +644,14 @@
 {
   /* Dummy version */
 }
+
+#ifdef MODULE
+static int
+module_sound_mem_init (void)
+{
+  return 0;			/* no error */
+}
+
+#endif
 
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this