/*
 * pcsel.c
 *
 * /dev/pcsp implementation
 *
 * Copyright (C) 1993, 1994  Michael Beck
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/pcsp.h>
 
#define AUDIO		"/dev/pcsp"

int audio, omode = O_WRONLY;

/* trying to define independent ioctl for sounddrivers version 1 and 2+ */
#ifdef SOUND_VERSION
#define IOCTL(a,b,c)            (ioctl(a,b,&c))
#else
#define IOCTL(a,b,c)            (c = ioctl(a,b,c) )
#endif

struct _devices {
	char		*name;
	char		*nick;
	unsigned	code;
} devs[] = {
	{ "Stereo-on-One", "Sto1", SNDCARD_STO1 },
	{ "Mono DAC", "DACm",      SNDCARD_DACM },
	{ "Stereo DAC", "DACs",    SNDCARD_DACS },
	{ "PC-Speaker", "PCSP",    SNDCARD_PCSP }
};

#define LP_NO		3
#define CARD_NO		4

void actual_conf(unsigned long dev, unsigned long port, unsigned long portS)
{
	int i, volume, srate, maxrate, emulation;

	printf("Actual PCSP output device: ");
	for (i = 0; i < CARD_NO; ++i)
		if (devs[i].code == dev) {
			printf("%s ", devs[i].name);
			if (dev == SNDCARD_PCSP) {
				IOCTL(audio, PCSP_GET_VOL, volume);
				printf("\nVolume : %d %%", volume * 100 / 256);
				IOCTL(audio, PCSP_GET_SRATE, srate);
				if (srate > 0)
					printf(", real samplerate : %d Hz\n", srate);
				else
					printf(", Interval Mode, lower bound : %d Hz\n", -srate);
				IOCTL(audio, PCSP_GET_MEASURE, maxrate);
				if (maxrate)
					if (maxrate == -1)
						printf("PCSP is disabled\n");
					else
						printf("Maximum Samplerate is %d Hz\n", maxrate);
			}
			else {
				if (dev != SNDCARD_DACS)
					printf("at lp%ld\n", port);
				else
					printf("at lp%ld & lp%ld\n", port, portS);
			}
			emulation = PCSP_EMULATION_QUERY;
			if (IOCTL(audio, PCSP_SET_EMU_MODE, emulation) < 0)
				printf("Emulation mode is not supported\n");
			else {
				printf("16bit Stereo Emulation ");
				if (emulation == PCSP_EMULATION_ON)
					printf("enabled\n");
				else if (emulation == PCSP_EMULATION_OFF)
					printf("disabled\n");
				else
					printf("status unknown\n");
			}
			break;
		}
	if (i == CARD_NO) {
		printf("unknown device %ld !!!\n", dev);
		exit (EXIT_FAILURE);
	}	
}

unsigned long set_dev(char *devname)
{
	int i;
	int dev = 0;

	for (i = 0; i < CARD_NO; ++i)
		if (! strcasecmp(devname, devs[i].nick)) {
			dev = devs[i].code;
			if (IOCTL(audio, PCSP_SET_DEV, dev) < 0) {
				perror(AUDIO);
				exit(EXIT_FAILURE);
			}
		}
	return dev;
}

int main(int argc, char *argv[])
{
  int i;
  int dev, port, portS, arg, stereo, samplerate;
  int volume = 0, srate = 0, emulation = PCSP_EMULATION_QUERY;
  char c, flag, acc, opt;

  audio = open(AUDIO, omode, 0);
  if (audio < 0) {
    perror(AUDIO);
    exit(EXIT_FAILURE);
  }

  IOCTL(audio, PCSP_GET_DEV, dev);
  if (dev != SNDCARD_PCSP) {
    IOCTL(audio, PCSP_GET_PORTS, port);
    portS = port & 0xF0 >> 8;
    port &= 0x0F;
  }
  else {
    port = 1; portS = 2;
  }
  stereo = 2; samplerate = 0;

  acc = 0; opt = 1;
  while ((c = getopt (argc, argv, "b:d:e:p:r:l:s:v:MSh")) != EOF) {
    flag = 1; opt = 0;
    switch (c) {
      case 'b':
	if (! strcasecmp("max", optarg)) {
		IOCTL(audio, PCSP_GET_MEASURE, srate);
		if (srate > 18356)
			srate = 18356;
		break;
	}
	srate = atoi(optarg);
	if (srate < 10000 || srate > 18356) {
          fprintf(stderr, "%s: Real samplerate not in range [10000, 18356]\n", argv[0]);
          exit(EXIT_FAILURE);
        }
        break;
      case 'd': 
        dev = set_dev(optarg); 
        if (! dev) {
          fprintf(stderr, "%s: unknown device '%s'\n", argv[0], optarg);
          exit(EXIT_FAILURE);
        }
        break;
      case 'e':
        if (! strcasecmp("on", optarg))
		emulation = PCSP_EMULATION_ON;
	else if (! strcasecmp("off", optarg))
		emulation = PCSP_EMULATION_OFF;
	else {
	  fprintf(stderr, "%s: unknown keyword '%s'\n", argv[0], optarg);
	  exit(EXIT_FAILURE);
	}
	break;
      case 'p':
      case 'r': flag = 0;
      case 'l':
        arg = atoi(optarg);
        if (arg >= LP_NO)
          fprintf(stderr, "%s: wrong port\n", argv[0]);
        else {
          (flag) ? portS : port = arg;
          acc = 1;
        }
        break;
      case 'M':
        stereo = 0; break;
      case 'S':
        stereo = 1; break;
      case 's':
        samplerate = atoi(optarg); break;
      case 'v':
	volume = atoi(optarg); break;
      case 'h':
      default:
        fprintf(stderr,
         "Usage: %s [-d device] [-p port] [-r rport -l lport] [-s Hz] [-SM]\n", argv[0]);
        fprintf(stderr, "\t\t[-e on|off] [-b real samplerate] [-v volume]\n");
        fprintf(stderr, "\nsupported devices :\n");
        for (i = 0; i < CARD_NO; ++i)
          fprintf(stderr, "  '%s' : %s\n", devs[i].nick, devs[i].name);
        exit(EXIT_FAILURE);
    }
  }
  if (opt) {
    actual_conf(dev, port, portS);
    exit(EXIT_SUCCESS);
  }

  if (acc) {
    if (dev == SNDCARD_DACS && port == portS) {
      fprintf(stderr, "%s: a Stereo DAC need two different ports\n", argv[0]);
      exit(EXIT_FAILURE);
    }
    port |= portS << 8;
    if (IOCTL(audio, PCSP_SET_PORTS, port) < 0) {
      perror(AUDIO);
      exit(EXIT_FAILURE);
    }
  }

  if (stereo != 2)
    if (IOCTL(audio, SNDCTL_DSP_STEREO, stereo) < 0) 
      fprintf(stderr, "%s: can't set stereo\n", argv[0]);

  if (srate)
    if (IOCTL(audio, PCSP_SET_SRATE, srate) < 0)
      fprintf(stderr, "%s: can't set real samplerate for PC-Speaker\n", argv[0]);

  if (samplerate)
    if (IOCTL(audio, SNDCTL_DSP_SPEED, samplerate) < 0)
      fprintf(stderr, "%s: can't set samplerate to %d Hz\n", argv[0], samplerate);

  if (volume > 0) {
    volume = (volume * 256) / 100;
    if (IOCTL(audio, PCSP_SET_VOL, volume) < 0)
      fprintf(stderr, "%s: can't set volume for PC-Speaker\n", argv[0]);
  }

  if (emulation != PCSP_EMULATION_QUERY)
    if (IOCTL(audio, PCSP_SET_EMU_MODE, emulation) < 0)
      fprintf(stderr, "%s: can't set emulation mode\n", argv[0]);

  return (EXIT_SUCCESS);
}

