$OpenBSD: patch-linux_a_c,v 1.2 2001/07/05 10:50:30 espie Exp $
--- linux_a.c.orig	Mon May 20 15:09:46 1996
+++ linux_a.c	Thu Jul  5 12:49:08 2001
@@ -37,6 +37,11 @@
 #include <machine/soundcard.h>
 #endif
 
+#ifdef __OpenBSD__
+#include <sys/ioctl.h>
+#include <soundcard.h>
+#endif
+
 #include "config.h"
 #include "output.h"
 #include "controls.h"
@@ -56,7 +61,7 @@ PlayMode dpm = {
   -1,
   {0}, /* default: get all the buffer fragments you can */
   "Linux dsp device", 'd',
-  "/dev/dsp",
+  "/dev/audio",
   open_output,
   close_output,
   output_data,
@@ -71,12 +76,15 @@ PlayMode dpm = {
    then 8-bit unsigned if it fails. If you have a sound device that
    can't handle either, let me know. */
 
+/* Flag for Luigi Rizzo new sound driver (as opposed to VoxWare) */
+static luigi_driver = 0;
+
 static int open_output(void)
 {
   int fd, tmp, i, warnings=0;
   
   /* Open the audio device */
-  fd=open(dpm.name, O_RDWR | O_NDELAY);
+  fd=open(dpm.name, O_WRONLY);
   if (fd<0)
     {
       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
@@ -84,6 +92,23 @@ static int open_output(void)
       return -1;
     }
 
+  /* Figure out if we're running with the Luigi driver or
+     the original VoxWare driver, with code based on dburr/luigi
+     in ports/5607.  It'd be great if we could do this before
+     opening the audio device, but oh well... */
+#if defined(AIOGFMT)	/* only defined in Luigi driver */
+  {
+    snd_chan_param s;
+    int i;
+    i = ioctl(fd, AIOGFMT, &s);
+    if (i != -1)
+      luigi_driver = 1;
+  }
+#endif defined (AIOGFMT)
+
+  ctl->cmsg(CMSG_INFO, VERB_VERBOSE, "Using %s sound driver",
+	    luigi_driver ? "luigi" : "VoxWare");
+
   /* They can't mean these */
   dpm.encoding &= ~(PE_ULAW|PE_BYTESWAP);
 
@@ -92,6 +117,29 @@ static int open_output(void)
      the other one. */
 
   i=tmp=(dpm.encoding & PE_16BIT) ? 16 : 8;
+  if (luigi_driver)
+    {
+      if (dpm.encoding & PE_16BIT) {
+	int fmt = AFMT_S16_LE ;
+
+	if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0 || fmt != AFMT_S16_LE) {
+	    fmt = AFMT_U8 ;
+	    if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0 || fmt != AFMT_U8) {
+		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+		      "%s doesn't support 16- or 8-bit sample width",
+		      dpm.name);
+		close(fd);
+		return -1;
+	    }
+	    ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
+		  "Sample width adjusted to %d bits", tmp);
+	    dpm.encoding ^= PE_16BIT;
+	    warnings = 1;
+	}
+      }
+    }
+  else
+    {
   if (ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &tmp)<0 || tmp!=i)
     {
       /* Try the other one */
@@ -109,6 +157,7 @@ static int open_output(void)
       dpm.encoding ^= PE_16BIT;
       warnings=1;
     }
+    }
   if (dpm.encoding & PE_16BIT)
     dpm.encoding |= PE_SIGNED;
   else
@@ -163,6 +212,8 @@ static int open_output(void)
   /* Set buffer fragments (in extra_param[0]) */
   
   tmp=AUDIO_BUFFER_BITS;
+  if (luigi_driver)
+    tmp += 2;
   if (!(dpm.encoding & PE_MONO)) tmp++;
   if (dpm.encoding & PE_16BIT) tmp++;
   tmp |= (dpm.extra_param[0]<<16);
@@ -189,28 +240,35 @@ static int open_output(void)
   return warnings;
 }
 
+/* output_data comes from Luigi's linux_a.c.  This version seems to allow
+   for partial writes to the sound device, where as the original version
+   doesn't. */
 static void output_data(int32 *buf, int32 count)
 {
+  char *p;
+  int res, l;
+  
   if (!(dpm.encoding & PE_MONO)) count*=2; /* Stereo samples */
   
-  if (dpm.encoding & PE_16BIT)
-    {
+  if (dpm.encoding & PE_16BIT) {
       /* Convert data to signed 16-bit PCM */
       s32tos16(buf, count);
-      
-      /* Write the data out. Linux likes to give an EINTR if you suspend
-	 a program while waiting on a write, so we may need to retry. */
-      while ((-1==write(dpm.fd, buf, count * 2)) && errno==EINTR)
-	;
-    }
-  else
-    {
+      res = count*2;
+  } else {
       /* Convert to 8-bit unsigned and write out. */
       s32tou8(buf, count);
-      
-      while ((-1==write(dpm.fd, buf, count)) && errno==EINTR)
-	;
+      res = count;
+  }
+  for (p = (char *) buf; res > 0; res -= l) {
+again:
+    l = write(dpm.fd, p, res);
+    if (l < 0) {
+	if (errno == EINTR)
+		goto again;
+	return;
     }
+    p += l;
+  }
 }
 
 static void close_output(void)
@@ -220,10 +278,10 @@ static void close_output(void)
 
 static void flush_output(void)
 {
-  ioctl(dpm.fd, SNDCTL_DSP_SYNC);
+  ioctl(dpm.fd, SNDCTL_DSP_SYNC,0);
 }
 
 static void purge_output(void)
 {
-  ioctl(dpm.fd, SNDCTL_DSP_RESET);
+  ioctl(dpm.fd, SNDCTL_DSP_RESET,0);
 }
