/*
 * dsp_video.c: Collect thermal noise from a sound digitizer with no source plugged in or
 *              a camera with the lens cap on, if the system has enough gain to detect
 *              anything. Implementation of RFC 1750.
 *
 *
 * Copyright (c) 2003, eazy <eazy@ondaquadra.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * *  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <linux/soundcard.h>
#include <errno.h>

#define DSP "/dev/dsp"
#define VIDEO "/dev/video"
#define MAXLEN 255
#define COMPRESS "compress"

int             fd[2];
pid_t           pid;

void
err_quit()
{
	perror("error");
	exit(-1);
}

void
usage(char *prog)
{
        fprintf(stderr, "WARNING: Verify that your audio device have no source plugged in\n");
        fprintf(stderr, "and your camera have the lens cap on!\n\n");

	fprintf(stderr, "Usage: %s <-a [-b bits] [-c channels] [-r rate] | -v>\n", prog);
	fprintf(stderr, "\t-a\tCollect entropy from audio device\n");
	fprintf(stderr, "\t-v\tCollect entropy from video device\n");
	fprintf(stderr, "\t-b\tSet size of the sample (8 bits)\n");
	fprintf(stderr, "\t-c\tSet channels (channels 1)\n");
	fprintf(stderr, "\t-r\tSet sampling rate (8000 Hz)\n");
	exit(-1);
}

void
sigint(int signo)
{
	close(fd[1]);
	if (waitpid(pid, NULL, 0) == -1)
		err_quit();

	fprintf(stderr, "\nDone\n");
	exit(0);
}

int
main(int argc, char **argv)
{
	int             n, dev, opt, bits = 0, channels = 0, rate = 0;
	char            buf[MAXLEN], *device = NULL;
	struct sigaction act;

	opterr = 0;
	while ((opt = getopt(argc, argv, "ab:c:r:v")) != -1)
		switch (opt) {
		case 'a':
			if (!device) {
				device = DSP;
				break;
			} else
				opt = '?';
		case 'b':
			bits = atoi(optarg);
			break;
		case 'c':
			channels = atoi(optarg);
			break;
		case 'r':
			rate = atoi(optarg);
			break;
		case 'v':
			if (!device) {
				device = VIDEO;
				break;
			} else
				opt = '?';
		case 'h':
		case '?':
		default:
			usage(argv[0]);
		}

	if (optind < argc)
		usage(argv[0]);

	if (!device)
		usage(argv[0]);

	if ((dev = open(device, O_RDONLY)) == -1)
		err_quit();

        /*
           Set the sample size in bits, the default is 8 bits
         */
	if (strcmp(device, DSP) == 0 && bits) {
		if (ioctl(dev, SOUND_PCM_WRITE_BITS, &bits) == -1)
			err_quit();
		fprintf(stderr, "bits: %d\n", bits);
	}

        /*
           Set the sampling rate, the default is 8000 Hz
         */
	if (strcmp(device, DSP) == 0 && rate) {
		if (ioctl(dev, SOUND_PCM_WRITE_RATE, &rate) == -1)
			err_quit();
		fprintf(stderr, "rate: %d\n", rate);
	}

        /*
           Set the number of channels, the default is one channel
         */
	if (strcmp(device, DSP) == 0 && channels) {
		if (ioctl(dev, SOUND_PCM_WRITE_CHANNELS, &channels) == -1)
			err_quit();
		fprintf(stderr, "channels: %d\n", channels);
	}

        /*
           Create a pipe for interprocess comunication
         */
	if (pipe(fd) == -1)
		err_quit();

	if ((pid = fork()) == -1)
		err_quit();

        /*
           Create a child process that handle the output of the parent
         */
	else if (pid > 0) {  /* parent */

                /*
                   Close the read end of the pipe
                 */
		close(fd[0]);

		act.sa_handler = sigint;
		sigemptyset(&act.sa_mask);
		act.sa_flags |= SA_RESTART;

                /*
                   Call sigint() signal handler when the user press ^C
                 */
		if (sigaction(SIGINT, &act, NULL) == -1)
			err_quit();

                /*
                   Read the device specified from the command line and write
                   the output to the write end of the pipe
                 */
		while ((n = read(dev, buf, MAXLEN)) > 0)
			if (write(fd[1], buf, n) == -1)
				err_quit();

		if (n == -1)
			err_quit();

		fprintf(stderr, "Unexpected EOF\n");
		exit(-1);

	} else {  /* child */

                /*
                   Close the write end of the pipe
                 */
		close(fd[1]);

                /*
                   Duplicate the read end of the pipe to the stdin
                 */
		if (dup2(fd[0], STDIN_FILENO) == -1)
			err_quit();

                /*
                   Close the old descriptor of the read end of the pipe
                 */
		close(fd[0]);

                /*
                   Execute the compress program that read the input from the
                   read end of the pipe duplicated in the stdin
                 */
		execlp(COMPRESS, COMPRESS, (char *) 0);
		err_quit();

	}
}

