summaryrefslogtreecommitdiffstats
path: root/winsup/testsuite/winsup.api/devdsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/testsuite/winsup.api/devdsp.c')
-rw-r--r--winsup/testsuite/winsup.api/devdsp.c618
1 files changed, 618 insertions, 0 deletions
diff --git a/winsup/testsuite/winsup.api/devdsp.c b/winsup/testsuite/winsup.api/devdsp.c
new file mode 100644
index 000000000..81d664ada
--- /dev/null
+++ b/winsup/testsuite/winsup.api/devdsp.c
@@ -0,0 +1,618 @@
+/* devdsp.c: Device tests for /dev/dsp
+
+ Copyright 2004 Red Hat, Inc
+
+ Written by Gerd Spalink (Gerd.Spalink@t-online.de)
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* Conventions used here:
+ We use the libltp framework
+ 1. Any unexpected behaviour leads to an exit with nonzero exit status
+ 2. Unexpected behaviour from /dev/dsp results in an exit status of TFAIL
+ 3. Unexpected behaviour from OS (malloc, fork, waitpid...) result
+ in an exit status of TBROK */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/soundcard.h>
+#include <math.h>
+#include <errno.h>
+#include "test.h" /* use libltp framework */
+
+static const char wavfile_okay[] =
+ {
+#include "devdsp_okay.h" /* a sound sample */
+ };
+
+/* Globals required by libltp */
+const char *TCID = "devdsp"; /* set test case identifier */
+int TST_TOTAL = 32;
+
+/* Prototypes */
+void sinegen (void *wave, int rate, int bits, int len, int stride);
+void sinegenw (int freq, int samprate, short *value, int len, int stride);
+void sinegenb (int freq, int samprate, unsigned char *value, int len,
+ int stride);
+void playtest (int fd, int rate, int stereo, int bits);
+void rectest (int fd, int rate, int stereo, int bits);
+void rwtest (int fd, int rate, int stereo, int bits);
+void setpars (int fd, int rate, int stereo, int bits);
+void forkplaytest (void);
+void forkrectest (void);
+void recordingtest (void);
+void playbacktest (void);
+void monitortest (void);
+void ioctltest (void);
+void playwavtest (void);
+void syncwithchild (pid_t pid, int expected_exit_status);
+void cleanup (void);
+
+static int expect_child_failure = 0;
+
+/* Sampling rates we want to test */
+static const int rates[] = { 44100, 22050, 8000 };
+
+/* Combinations of stereo/bits we want to test */
+struct sb
+{
+ int stereo;
+ int bits;
+};
+static const struct sb sblut[] = { {0, 8}, {0, 16}, {1, 8}, {1, 16} };
+
+int
+main (int argc, char *argv[])
+{
+ /* tst_brkm(TBROK, cleanup, "see if it breaks all right"); */
+ ioctltest ();
+ playbacktest ();
+ recordingtest ();
+ monitortest ();
+ forkplaytest ();
+ forkrectest ();
+ playwavtest ();
+ tst_exit ();
+ /* NOTREACHED */
+ return 0;
+}
+
+/* test some extra ioctls */
+void
+ioctltest (void)
+{
+ int audio1;
+ int ioctl_par;
+
+ audio1 = open ("/dev/dsp", O_WRONLY);
+ if (audio1 < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "open W: %s", strerror (errno));
+ }
+ setpars (audio1, 44100, 1, 16);
+ /* Note: block size may depend on parameters */
+ if (ioctl (audio1, SNDCTL_DSP_GETBLKSIZE, &ioctl_par) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "ioctl GETBLKSIZE: %s", strerror (errno));
+ }
+ tst_resm (TPASS, "ioctl get buffer size=%d", ioctl_par);
+ if (ioctl (audio1, SNDCTL_DSP_GETFMTS, &ioctl_par) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "ioctl GETFMTS: %s", strerror (errno));
+ }
+ tst_resm (TPASS, "ioctl get formats=%08x", ioctl_par);
+ if (close (audio1) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+}
+
+/* test write / play */
+void
+playbacktest (void)
+{
+ int audio1, audio2;
+ int rate, k;
+
+ audio1 = open ("/dev/dsp", O_WRONLY);
+ if (audio1 < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Error open /dev/dsp W: %s",
+ strerror (errno));
+ }
+ audio2 = open ("/dev/dsp", O_WRONLY);
+ if (audio2 >= 0)
+ {
+ tst_brkm (TFAIL, cleanup,
+ "Second open /dev/dsp W succeeded, but is expected to fail");
+ }
+ if (errno != EBUSY)
+ {
+ tst_brkm (TFAIL, cleanup, "Expected EBUSY here, exit: %s",
+ strerror (errno));
+ }
+ for (rate = 0; rate < sizeof (rates) / sizeof (int); rate++)
+ for (k = 0; k < sizeof (sblut) / sizeof (struct sb); k++)
+ {
+ playtest (audio1, rates[rate], sblut[k].stereo, sblut[k].bits);
+ tst_resm (TPASS, "Play bits=%2d stereo=%d rate=%5d",
+ sblut[k].bits, sblut[k].stereo, rates[rate]);
+ }
+ if (close (audio1) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+}
+
+/* test read / record */
+void
+recordingtest (void)
+{
+ int audio1, audio2;
+ int rate, k;
+ /* test read / record */
+ audio1 = open ("/dev/dsp", O_RDONLY);
+ if (audio1 < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Error open /dev/dsp R: %s",
+ strerror (errno));
+ }
+ audio2 = open ("/dev/dsp", O_RDONLY);
+ if (audio2 >= 0)
+ {
+ tst_brkm (TFAIL, cleanup,
+ "Second open /dev/dsp R succeeded, but is expected to fail");
+ }
+ if (errno != EBUSY)
+ {
+ tst_brkm (TFAIL, cleanup, "Expected EBUSY here, exit: %s",
+ strerror (errno));
+ }
+ for (rate = 0; rate < sizeof (rates) / sizeof (int); rate++)
+ for (k = 0; k < sizeof (sblut) / sizeof (struct sb); k++)
+ {
+ rectest (audio1, rates[rate], sblut[k].stereo, sblut[k].bits);
+ tst_resm (TPASS, "Record bits=%2d stereo=%d rate=%5d",
+ sblut[k].bits, sblut[k].stereo, rates[rate]);
+ }
+ if (close (audio1) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+}
+
+/* simultaneous read/write */
+void
+monitortest (void)
+{
+ int fd;
+
+ fd = open ("/dev/dsp", O_RDWR);
+ if (fd < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "open RW: %s", strerror (errno));
+ }
+ rwtest (fd, 44100, 1, 16);
+ tst_resm (TPASS, "Record+Play rate=44100, stereo, 16 bits");
+ if (close (fd) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+}
+
+void
+forkrectest (void)
+{
+ int pid;
+ int fd;
+
+ fd = open ("/dev/dsp", O_RDONLY);
+ if (fd < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Error open /dev/dsp R: %s",
+ strerror (errno));
+ }
+ pid = fork ();
+ if (pid < 0)
+ {
+ tst_brkm (TBROK, cleanup, "Fork failed: %s", strerror (errno));
+ }
+ if (pid)
+ {
+ tst_resm (TINFO, "forked, child PID=%d", pid);
+ sleep (1);
+ tst_resm (TINFO, "parent records..");
+ rectest (fd, 22050, 1, 16);
+ tst_resm (TINFO, "parent done");
+ syncwithchild (pid, 0);
+ }
+ else
+ { /* child */
+ tst_resm (TINFO, "child records..");
+ rectest (fd, 44100, 1, 16);
+ tst_resm (TINFO, "child done");
+ fflush (stdout);
+ exit (0); /* implicit close */
+ }
+ tst_resm (TPASS, "child records after fork");
+ /* fork again, but now we have done a read before,
+ * so the child is expected to fail
+ */
+ pid = fork ();
+ if (pid < 0)
+ {
+ tst_brkm (TBROK, cleanup, "Fork failed: %s", strerror (errno));
+ }
+ if (pid)
+ {
+ tst_resm (TINFO, "forked, child PID=%d", pid);
+ tst_resm (TINFO, "parent records again ..");
+ rectest (fd, 22050, 1, 16);
+ tst_resm (TINFO, "parent done");
+ syncwithchild (pid, TFAIL); /* expecting error exit */
+ }
+ else
+ { /* child */
+ expect_child_failure = 1;
+ tst_resm (TINFO, "child trying to record (should fail)..");
+ rectest (fd, 44100, 1, 16);
+ /* NOTREACHED */
+ tst_resm (TINFO, "child done");
+ fflush (stdout);
+ exit (0); /* implicit close */
+ }
+ if (close (fd) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+ tst_resm (TPASS, "child cannot record if parent is already recording");
+}
+
+void
+forkplaytest (void)
+{
+ int pid;
+ int fd;
+
+ fd = open ("/dev/dsp", O_WRONLY);
+ if (fd < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Error open /dev/dsp R: %s",
+ strerror (errno));
+ }
+ pid = fork ();
+ if (pid < 0)
+ {
+ tst_brkm (TBROK, cleanup, "Fork failed: %s", strerror (errno));
+ }
+ if (pid)
+ {
+ tst_resm (TINFO, "forked, child PID=%d", pid);
+ sleep (1);
+ tst_resm (TINFO, "parent plays..");
+ playtest (fd, 22050, 0, 8);
+ tst_resm (TINFO, "parent done");
+ syncwithchild (pid, 0);
+ }
+ else
+ { /* child */
+ tst_resm (TINFO, "child plays..");
+ playtest (fd, 44100, 1, 16);
+ tst_resm (TINFO, "child done");
+ fflush (stdout);
+ exit (0); /* implicit close */
+ }
+ tst_resm (TPASS, "child plays after fork");
+ /* fork again, but now we have done a write before,
+ * so the child is expected to fail
+ */
+ pid = fork ();
+ if (pid < 0)
+ {
+ tst_brkm (TBROK, cleanup, "Fork failed");
+ }
+ if (pid)
+ {
+ tst_resm (TINFO, "forked, child PID=%d", pid);
+ tst_resm (TINFO, "parent plays again..");
+ playtest (fd, 22050, 0, 8);
+ tst_resm (TINFO, "parent done");
+ syncwithchild (pid, TFAIL); /* expected failure */
+ }
+ else
+ { /* child */
+ expect_child_failure = 1;
+ tst_resm (TINFO, "child trying to play (should fail)..");
+ playtest (fd, 44100, 1, 16);
+ /* NOTREACHED */
+ tst_resm (TINFO, "child done");
+ fflush (stdout);
+ exit (0); /* implicit close */
+ }
+ if (close (fd) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+ tst_resm (TPASS, "child cannot play if parent is already playing");
+}
+
+void
+playtest (int fd, int rate, int stereo, int bits)
+{ /* Play sine waves, always 0.25 sec */
+ void *wave;
+ int n, c, b;
+ int size;
+ if (stereo)
+ c = 2;
+ else
+ c = 1;
+ if (bits == 8)
+ b = 1;
+ else
+ b = 2;
+ size = rate / 4 * c * b;
+
+ wave = malloc (size);
+ if (wave == NULL)
+ {
+ tst_brkm (TBROK, cleanup, "Malloc failed, exit");
+ }
+ setpars (fd, rate, stereo, bits);
+ sinegen (wave, rate, bits, rate / 4, c);
+
+ if ((n = write (fd, wave, size)) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "write: %s", strerror (errno));
+ }
+ if (n != size)
+ {
+ tst_brkm (TFAIL, cleanup, "Wrote %d, expected %d; exit", n, size);
+ }
+ free (wave);
+}
+
+void
+rectest (int fd, int rate, int stereo, int bits)
+{
+ void *wave;
+ int n, c, b;
+ int size;
+ if (stereo)
+ c = 2;
+ else
+ c = 1;
+ if (bits == 8)
+ b = 1;
+ else
+ b = 2;
+ size = rate / 4 * c * b;
+
+ wave = malloc (size);
+ if (wave == NULL)
+ {
+ tst_brkm (TBROK, cleanup, "Malloc failed, exit");
+ }
+ setpars (fd, rate, stereo, bits);
+ if ((n = read (fd, wave, size)) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "read: %s", strerror (errno));
+ }
+ if (n != size)
+ {
+ tst_brkm (TFAIL, cleanup, "Read n=%d (%d expected); exit", n, size);
+ }
+ if ((n = read (fd, wave, size)) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "read: %s", strerror (errno));
+ }
+ if (n != size)
+ {
+ tst_brkm (TFAIL, cleanup, "Read n=%d (%d expected); exit", n, size);
+ }
+ free (wave);
+}
+
+void
+rwtest (int fd, int rate, int stereo, int bits)
+{
+ int pid;
+ void *wave;
+ int n, c, b;
+ int size;
+ if (stereo)
+ c = 2;
+ else
+ c = 1;
+ if (bits == 8)
+ b = 1;
+ else
+ b = 2;
+ size = rate / 4 * c * b;
+
+ wave = malloc (size);
+ if (wave == NULL)
+ {
+ tst_brkm (TBROK, cleanup, "Malloc failed, exit");
+ }
+ setpars (fd, rate, stereo, bits);
+ pid = fork ();
+ if (pid < 0)
+ {
+ tst_brkm (TBROK, cleanup, "Fork failed: %s", strerror (errno));
+ }
+ if (pid)
+ {
+ tst_resm (TINFO, "forked, child PID=%d parent records", pid);
+ if ((n = read (fd, wave, size)) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "read: %s", strerror (errno));
+ }
+ if (n != size)
+ {
+ tst_brkm (TFAIL, cleanup, "Read n=%d (%d expected)", n, size);
+ }
+ free (wave);
+ syncwithchild (pid, 0);
+ }
+ else
+ { /* child */
+ tst_resm (TINFO, "child plays");
+ sinegen (wave, rate, bits, rate / 4, c);
+ if ((n = write (fd, wave, size)) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "child write: %s", strerror (errno));
+ }
+ if (n != size)
+ {
+ tst_brkm (TFAIL, cleanup, "child write n=%d OK (%d expected)", n,
+ size);
+ }
+ free (wave);
+ exit (0);
+ }
+}
+
+void
+setpars (int fd, int rate, int stereo, int bits)
+{
+ int ioctl_par = 0;
+
+ if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &bits) < 0)
+ {
+ if (expect_child_failure)
+ { /* Note: Don't print this to stderr because we expect failures here
+ * for the some cases after fork()
+ */
+ tst_resm (TINFO, "ioctl SNDCTL_DSP_SAMPLESIZE: %s",
+ strerror (errno));
+ exit (TFAIL);
+ }
+ else
+ {
+ tst_brkm (TFAIL, cleanup, "ioctl SNDCTL_DSP_SAMPLESIZE: %s",
+ strerror (errno));
+ }
+ }
+ if (ioctl (fd, SNDCTL_DSP_STEREO, &stereo) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "ioctl SNDCTL_DSP_STEREO: %s",
+ strerror (errno));
+ }
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "ioctl SNDCTL_DSP_SPEED: %s",
+ strerror (errno));
+ }
+ if (ioctl (fd, SNDCTL_DSP_SYNC, &ioctl_par) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "ioctl SNDCTL_DSP_SYNC: %s",
+ strerror (errno));
+ }
+}
+
+void
+syncwithchild (pid_t pid, int expected_exit_status)
+{
+ int status;
+
+ if (waitpid (pid, &status, 0) != pid)
+ {
+ tst_brkm (TBROK, cleanup, "Wait for child: %s", strerror (errno));
+ }
+ if (!WIFEXITED (status))
+ {
+ tst_brkm (TBROK, cleanup, "Child had abnormal exit");
+ }
+ if (WEXITSTATUS (status) != expected_exit_status)
+ {
+ tst_brkm (TBROK, cleanup, "Child had exit status != 0");
+ }
+}
+
+void
+sinegen (void *wave, int rate, int bits, int len, int stride)
+{
+ if (bits == 8)
+ {
+ sinegenb (1000, rate, (unsigned char *) wave, len, stride);
+ if (stride == 2)
+ sinegenb (800, rate, (unsigned char *) wave + 1, len, stride);
+ }
+ else
+ {
+ sinegenw (1000, rate, (short *) wave, len, stride);
+ if (stride == 2)
+ sinegenw (800, rate, (short *) wave + 1, len, stride);
+ }
+}
+
+void
+sinegenw (int freq, int samprate, short *value, int len, int stride)
+{
+ double phase, incr;
+
+ phase = 0.0;
+ incr = M_PI * 2.0 * (double) freq / (double) samprate;
+ while (len-- > 0)
+ {
+ *value = (short) floor (0.5 + 32766.5 * sin (phase));
+ value += stride;
+ phase += incr;
+ }
+}
+
+void
+sinegenb (int freq, int samprate, unsigned char *value, int len, int stride)
+{
+ double phase, incr;
+
+ phase = 0.0;
+ incr = M_PI * 2.0 * (double) freq / (double) samprate;
+ while (len-- > 0)
+ {
+ *value = (unsigned char) floor (128.5 + 126.5 * sin (phase));
+ value += stride;
+ phase += incr;
+ }
+}
+
+void
+playwavtest (void)
+{
+ int audio;
+ int size = sizeof (wavfile_okay);
+ int n;
+ audio = open ("/dev/dsp", O_WRONLY);
+ if (audio < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Error open /dev/dsp W: %s",
+ strerror (errno));
+ }
+ if ((n = write (audio, wavfile_okay, size)) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "write: %s", strerror (errno));
+ }
+ if (n != size)
+ {
+ tst_brkm (TFAIL, cleanup, "Wrote %d, expected %d; exit", n, size);
+ }
+ if (close (audio) < 0)
+ {
+ tst_brkm (TFAIL, cleanup, "Close audio: %s", strerror (errno));
+ }
+ tst_resm (TPASS, "Set parameters from wave file header");
+}
+
+void
+cleanup (void)
+{
+}