/*****************************************************************************/

/*
 *	xfsmmixer_main.c  -- kernel soundcard radio modem driver mixer utility.
 *
 *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 *
 * History:
 *   0.1  14.12.96  Started
 *   0.2  11.05.97  introduced hdrvcomm.h
 */

/*****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/if.h>
#include <forms.h>
#include "hdrvcomm.h"
#include "xfsmmixer.h"

/* ---------------------------------------------------------------------- */

static char *progname;
static unsigned int mixdevice;

/* ---------------------------------------------------------------------- */

static FD_mixer_ad1848 *fd_mixer_ad1848;
static FD_mixer_ct1335 *fd_mixer_ct1335;
static FD_mixer_ct1345 *fd_mixer_ct1345;
static FD_mixer_ct1745 *fd_mixer_ct1745;

/* ---------------------------------------------------------------------- */

static int do_mix_ioctl(int cmd, struct sm_mixer_data *mixdat)
{
	struct sm_ioctl par;
	int i;

	par.cmd = cmd;
	par.data.mix = *mixdat;
	i = hdrvc_sm_ioctl(cmd, &par);
	*mixdat = par.data.mix;
	return i;
}

/* ---------------------------------------------------------------------- */

static unsigned char get_mixer_reg(unsigned char addr)
{
	int i;
	struct sm_mixer_data mixdat;

	mixdat.reg = addr;
	if ((i = do_mix_ioctl(SMCTL_GETMIXER, &mixdat)) < 0) {
		perror("do_mix_ioctl: SMCTL_GETMIXER");
		exit(1);
	}
	if (!i)
		fprintf(stderr, "warning: mixer device %u register %u not "
			"accessible for reading!\n", mixdat.mixer_type,
			addr);
	if (mixdat.mixer_type != mixdevice) {
		fprintf(stderr, "error: mixer type changed !?\n");
		exit(2);
	}
	return mixdat.data;
}

/* ---------------------------------------------------------------------- */

static void set_mixer_reg(unsigned char addr, unsigned char data)
{
	struct sm_mixer_data mixdat;

	mixdat.reg = addr;
	mixdat.data = data;
	mixdat.mixer_type = mixdevice;
	if (do_mix_ioctl(SMCTL_SETMIXER, &mixdat) < 0) {
		perror("do_mix_ioctl: SMCTL_SETMIXER");
		exit(1);
	}
}

/* ---------------------------------------------------------------------- */

void update_ad1848(FL_OBJECT *ob, long data)
{
	unsigned char mdata;
	double mval;

	mdata = 0;
	if (fl_get_button(fd_mixer_ad1848->srcl_line))
		mdata = 0x00;
	else if (fl_get_button(fd_mixer_ad1848->srcl_aux1))
		mdata = 0x40;
	else if (fl_get_button(fd_mixer_ad1848->srcl_mic))
		mdata = 0x80;
	else if (fl_get_button(fd_mixer_ad1848->srcl_dac))
		mdata = 0xc0;
	mval = fl_get_slider_value(fd_mixer_ad1848->inl);
	if (mdata == 0x80 && mval >= 20) {
		mval -= 20;
		mdata |= 0x20;
	}
	mval *= 0.666666;
	if (mval > 15) 
		mval = 15;
	mdata |= (int)mval;
	set_mixer_reg(0x00, mdata);
	mdata = 0;
	if (fl_get_button(fd_mixer_ad1848->srcr_line))
		mdata = 0x00;
	else if (fl_get_button(fd_mixer_ad1848->srcr_aux1))
		mdata = 0x40;
	else if (fl_get_button(fd_mixer_ad1848->srcr_mic))
		mdata = 0x80;
	else if (fl_get_button(fd_mixer_ad1848->srcr_dac))
		mdata = 0xc0;
	mval = fl_get_slider_value(fd_mixer_ad1848->inr);
	if (mdata == 0x80 && mval >= 20) {
		mval -= 20;
		mdata |= 0x20;
	}
	mval *= 0.666666;
	if (mval > 15) 
		mval = 15;
	mdata |= (int)mval;
	set_mixer_reg(0x01, mdata);
	set_mixer_reg(0x02, 0x80);
	set_mixer_reg(0x03, 0x80);
	set_mixer_reg(0x04, 0x80);
	set_mixer_reg(0x05, 0x80);
	mval = fl_get_slider_value(fd_mixer_ad1848->outl);
	if (mval < -95)
		set_mixer_reg(0x06, 0x80);
	else 
		set_mixer_reg(0x06, mval * (-0.66666666));
	mval = fl_get_slider_value(fd_mixer_ad1848->outr);
	if (mval < -95)
		set_mixer_reg(0x07, 0x80);
	else 
		set_mixer_reg(0x07, mval * (-0.66666666));
	set_mixer_reg(0x0d, 0x00);
}

/* ---------------------------------------------------------------------- */

void update_ct1335(FL_OBJECT *ob, long data)
{
	double mval;

	mval = fl_get_slider_value(fd_mixer_ct1335->out) * (7.0 / 46.0);
	set_mixer_reg(0x02, ((((int)(mval)) + 7) << 1));
	set_mixer_reg(0x06, 0x00);
	set_mixer_reg(0x08, 0x00);
	set_mixer_reg(0x0a, 0x06);
}

/* ---------------------------------------------------------------------- */

void update_ct1345(FL_OBJECT *ob, long data)
{
	unsigned char mdata;
	double mval;

	set_mixer_reg(0x04, 0xee);
	set_mixer_reg(0x0a, 0x00);
	mdata = 0x20;
	if (fl_get_button(fd_mixer_ct1345->src_mic))
		mdata = 0x20;
	else if (fl_get_button(fd_mixer_ct1345->src_cd))
		mdata = 0x22;
	else if (fl_get_button(fd_mixer_ct1345->src_line))
		mdata = 0x26;
	set_mixer_reg(0x0c, mdata);
	set_mixer_reg(0x0e, 0x20);
	mval = fl_get_slider_value(fd_mixer_ct1345->outl) * (7.0 / 46.0);
	mdata = (((int)(mval))+7) << 5;
	mval = fl_get_slider_value(fd_mixer_ct1345->outr) * (7.0 / 46.0);
	mdata |= (((int)(mval))+7) << 1;
	set_mixer_reg(0x22, mdata);
	set_mixer_reg(0x26, 0x00);
	set_mixer_reg(0x28, 0x00);
	set_mixer_reg(0x2e, 0x00);
}

/* ---------------------------------------------------------------------- */

void update_ct1745(FL_OBJECT *ob, long data)
{
	unsigned char mdata;
	double mval;

	set_mixer_reg(0x3c, 0); /* output src: only voice and pcspk */
	mval = fl_get_slider_value(fd_mixer_ct1745->treble);
	set_mixer_reg(0x44, ((int)mval)<<4); /* treble.l */
	set_mixer_reg(0x45, ((int)mval)<<4); /* treble.r */
	mval = fl_get_slider_value(fd_mixer_ct1745->bass);
	set_mixer_reg(0x46, ((int)mval)<<4); /* bass.l */
	set_mixer_reg(0x47, ((int)mval)<<4); /* bass.r */
	set_mixer_reg(0x3b, 0); /* PC speaker vol -18dB */
	set_mixer_reg(0x43, 1); /* mic: agc off, fixed 20dB gain */
	mval = fl_get_slider_value(fd_mixer_ct1745->outl);
	mdata = 0;
	while (mval > 0) {
		mval -= 6;
		mdata += 0x40;
	}
	set_mixer_reg(0x41, mdata); /* output gain */
	set_mixer_reg(0x30, 0xf8); /* master vol */
	set_mixer_reg(0x32, ((int)(mval * 0.5) + 31) << 3); /* voice vol */
	mval = fl_get_slider_value(fd_mixer_ct1745->outr);
	mdata = 0;
	while (mval > 0) {
		mval -= 6;
		mdata += 0x40;
	}
	set_mixer_reg(0x42, mdata); /* output gain */
	set_mixer_reg(0x31, 0xf8); /* master vol */
	set_mixer_reg(0x33, ((int)(mval * 0.5) + 31) << 3); /* voice vol */
	mval = fl_get_slider_value(fd_mixer_ct1745->inl);
	mdata = 0;
	while (mval > 0) {
		mval -= 6;
		mdata += 0x40;
	}
	set_mixer_reg(0x3f, mdata); /* input gain */
	mdata = ((int)(mval * 0.5) + 31) << 3;
	set_mixer_reg(0x34, mdata); /* midi vol */
	set_mixer_reg(0x36, mdata); /* cd vol */
	set_mixer_reg(0x38, mdata); /* line vol */
	set_mixer_reg(0x3a, mdata); /* mic vol */
	mval = fl_get_slider_value(fd_mixer_ct1745->inr);
	mdata = 0;
	while (mval > 0) {
		mval -= 6;
		mdata += 0x40;
	}
	set_mixer_reg(0x40, mdata); /* input gain */
	mdata = ((int)(mval * 0.5) + 31) << 3;
	set_mixer_reg(0x35, mdata); /* midi vol */
	set_mixer_reg(0x37, mdata); /* cd vol */
	set_mixer_reg(0x39, mdata); /* line vol */
	mdata = 0;
	if (fl_get_button(fd_mixer_ct1745->srcl_mic))
		mdata |= 0x01;
	if (fl_get_button(fd_mixer_ct1745->srcl_cdl))
		mdata |= 0x04;
	if (fl_get_button(fd_mixer_ct1745->srcl_cdr))
		mdata |= 0x02;
	if (fl_get_button(fd_mixer_ct1745->srcl_linel))
		mdata |= 0x10;
	if (fl_get_button(fd_mixer_ct1745->srcl_liner))
		mdata |= 0x08;
	if (fl_get_button(fd_mixer_ct1745->srcl_midil))
		mdata |= 0x40;
	if (fl_get_button(fd_mixer_ct1745->srcl_midir))
		mdata |= 0x20;				
	set_mixer_reg(0x3d, mdata); /* input sources left */
	mdata = 0;
	if (fl_get_button(fd_mixer_ct1745->srcr_mic))
		mdata |= 0x01;
	if (fl_get_button(fd_mixer_ct1745->srcr_cdl))
		mdata |= 0x04;
	if (fl_get_button(fd_mixer_ct1745->srcr_cdr))
		mdata |= 0x02;
	if (fl_get_button(fd_mixer_ct1745->srcr_linel))
		mdata |= 0x10;
	if (fl_get_button(fd_mixer_ct1745->srcr_liner))
		mdata |= 0x08;
	if (fl_get_button(fd_mixer_ct1745->srcr_midil))
		mdata |= 0x40;
	if (fl_get_button(fd_mixer_ct1745->srcr_midir))
		mdata |= 0x20;				
	set_mixer_reg(0x3e, mdata); /* input sources right*/
}

/* ---------------------------------------------------------------------- */

void cb_quit(FL_OBJECT *ob, long data)
{
	exit (0);
}

/* ---------------------------------------------------------------------- */

static const char *usage_str = 
"[-i smif]\n"
"  -i: specify the name of the soundmodem kernel driver interface\n\n";

/* ---------------------------------------------------------------------- */

int main(int argc, char *argv[])
{
	struct sm_mixer_data mixdat;
	unsigned char mdata;
	int ret;
	
	progname = *argv;
	printf("%s: Version 0.2; (C) 1996-1997 by Thomas Sailer HB9JNX/AE4WA\n", *argv);
	fl_initialize(&argc, argv, 0, 0, 0);
	hdrvc_args(&argc, argv, "sm0");
	while ((ret = getopt(argc, argv, "")) != -1) {
		switch (ret) {
		default:
			printf("usage: %s %s", *argv, usage_str);
			exit(-1);
		}
	}
	hdrvc_init();
	/*
	 * set mixer
	 */
	mixdat.reg = 0;
	if (do_mix_ioctl(SMCTL_GETMIXER, &mixdat) < 0) {
		perror("do_mix_ioctl: SMCTL_GETMIXER");
		mixdevice = SM_MIXER_INVALID;
	} else
		mixdevice = mixdat.mixer_type;
	switch (mixdevice) {
	case SM_MIXER_INVALID:
		printf("invalid mixer device\n");
		exit(1);

	case SM_MIXER_AD1848:
	case SM_MIXER_CRYSTAL:
		printf("Mixer device: %s\n", mixdevice == SM_MIXER_CRYSTAL ? 
		       "CS423x" : "AD1848");
		fd_mixer_ad1848 = create_form_mixer_ad1848();
		mdata = get_mixer_reg(0);
		fl_set_slider_step(fd_mixer_ad1848->inl, 1.5);
		fl_set_slider_value(fd_mixer_ad1848->inl, (((mdata & 0xe0) == 0xa0) ? 20 : 0) + 
				    (mdata & 0xf) * 1.5);
		fl_set_button(fd_mixer_ad1848->srcl_line, (mdata & 0xc0) == 0x00);
		fl_set_button(fd_mixer_ad1848->srcl_aux1, (mdata & 0xc0) == 0x40);
		fl_set_button(fd_mixer_ad1848->srcl_mic, (mdata & 0xc0) == 0x80);
		fl_set_button(fd_mixer_ad1848->srcl_dac, (mdata & 0xc0) == 0xc0);
		mdata = get_mixer_reg(1);
		fl_set_slider_step(fd_mixer_ad1848->inr, 1.5);
		fl_set_slider_value(fd_mixer_ad1848->inr, (((mdata & 0xe0) == 0xa0) ? 20 : 0) + 
				    (mdata & 0xf) * 1.5);
		fl_set_button(fd_mixer_ad1848->srcr_line, (mdata & 0xc0) == 0x00);
		fl_set_button(fd_mixer_ad1848->srcr_aux1, (mdata & 0xc0) == 0x40);
		fl_set_button(fd_mixer_ad1848->srcr_mic, (mdata & 0xc0) == 0x80);
		fl_set_button(fd_mixer_ad1848->srcr_dac, (mdata & 0xc0) == 0xc0);
		mdata = get_mixer_reg(6);
		fl_set_slider_step(fd_mixer_ad1848->outl, 1.5);
		fl_set_slider_value(fd_mixer_ad1848->outl, (mdata & 0x80) ? -100 : 
				    (mdata & 0x3f) * -1.5);
		mdata = get_mixer_reg(7);
		fl_set_slider_step(fd_mixer_ad1848->outr, 1.5);
		fl_set_slider_value(fd_mixer_ad1848->outr, (mdata & 0x80) ? -100 : 
				    (mdata & 0x3f) * -1.5);
		fl_show_form(fd_mixer_ad1848->mixer_ad1848, FL_PLACE_CENTER,
			     FL_FULLBORDER, "SoundModem Mixer");
		break;

	case SM_MIXER_CT1335:
		printf("Mixer device: CT1335\n");
		fd_mixer_ct1335 = create_form_mixer_ct1335();
		mdata = get_mixer_reg(0x2);
		fl_set_slider_step(fd_mixer_ct1335->out, 46.0/7.0);
		fl_set_slider_value(fd_mixer_ct1335->out, (((int)((mdata >> 1) & 7)) - 7) * 
				    (46.0/7.0));
		fl_show_form(fd_mixer_ct1335->mixer_ct1335, FL_PLACE_CENTER,
			     FL_FULLBORDER, "SoundModem Mixer");
		break;

	case SM_MIXER_CT1345:
		printf("Mixer device: CT1345\n");
		fd_mixer_ct1345 = create_form_mixer_ct1345();
		mdata = get_mixer_reg(0x22);
		fl_set_slider_step(fd_mixer_ct1345->outl, 46.0/7.0);
		fl_set_slider_value(fd_mixer_ct1345->outl, (((int)((mdata >> 5) & 7)) - 7) * 
				    (46.0/7.0));
		fl_set_slider_step(fd_mixer_ct1345->outr, 46.0/7.0);
		fl_set_slider_value(fd_mixer_ct1345->outr, (((int)((mdata >> 1) & 7)) - 7) * 
				    (46.0/7.0));
		mdata = get_mixer_reg(0xc);
		fl_set_button(fd_mixer_ct1345->src_mic, (mdata & 6) == 0 || (mdata & 6) == 4);
		fl_set_button(fd_mixer_ct1345->src_cd, (mdata & 6) == 2);
		fl_set_button(fd_mixer_ct1345->src_line, (mdata & 6) == 6);
		fl_show_form(fd_mixer_ct1345->mixer_ct1345, FL_PLACE_CENTER,
			     FL_FULLBORDER, "SoundModem Mixer");
		break;

	case SM_MIXER_CT1745:
		printf("Mixer device: CT1745\n");
		fd_mixer_ct1745 = create_form_mixer_ct1745();
		fl_set_slider_step(fd_mixer_ct1745->outl, 2);
		fl_set_slider_value(fd_mixer_ct1745->outl, 
				    ((int)((get_mixer_reg(0x32) >> 3) & 0x1f) - 31) * 2 +
				    ((get_mixer_reg(0x41) >> 6) & 0x3) * 6);
		fl_set_slider_step(fd_mixer_ct1745->outr, 2);
		fl_set_slider_value(fd_mixer_ct1745->outr, 
				    ((int)((get_mixer_reg(0x33) >> 3) & 0x1f) - 31) * 2 +
				    ((get_mixer_reg(0x42) >> 6) & 0x3) * 6);
		fl_set_slider_step(fd_mixer_ct1745->inl, 2);
		fl_set_slider_value(fd_mixer_ct1745->inl,
				    ((int)((get_mixer_reg(0x38) >> 3) & 0x1f) - 31) * 2 +
				    ((get_mixer_reg(0x3f) >> 6) & 0x3) * 6);
		fl_set_slider_step(fd_mixer_ct1745->inr, 2);
		fl_set_slider_value(fd_mixer_ct1745->inr, 
				    ((int)((get_mixer_reg(0x39) >> 3) & 0x1f) - 31) * 2 +
				    ((get_mixer_reg(0x40) >> 6) & 0x3) * 6);
		mdata = get_mixer_reg(0x3d);
		fl_set_button(fd_mixer_ct1745->srcl_mic, !!(mdata & 1));
		fl_set_button(fd_mixer_ct1745->srcl_cdl, !!(mdata & 4));
		fl_set_button(fd_mixer_ct1745->srcl_cdr, !!(mdata & 2));
		fl_set_button(fd_mixer_ct1745->srcl_linel, !!(mdata & 0x10));
		fl_set_button(fd_mixer_ct1745->srcl_liner, !!(mdata & 8));
		fl_set_button(fd_mixer_ct1745->srcl_midil, !!(mdata & 0x40));
		fl_set_button(fd_mixer_ct1745->srcl_midir, !!(mdata & 0x20));
		mdata = get_mixer_reg(0x3e);
		fl_set_button(fd_mixer_ct1745->srcr_mic, !!(mdata & 1));
		fl_set_button(fd_mixer_ct1745->srcr_cdl, !!(mdata & 4));
		fl_set_button(fd_mixer_ct1745->srcr_cdr, !!(mdata & 2));
		fl_set_button(fd_mixer_ct1745->srcr_linel, !!(mdata & 0x10));
		fl_set_button(fd_mixer_ct1745->srcr_liner, !!(mdata & 8));
		fl_set_button(fd_mixer_ct1745->srcr_midil, !!(mdata & 0x40));
		fl_set_button(fd_mixer_ct1745->srcr_midir, !!(mdata & 0x20));
		fl_set_slider_step(fd_mixer_ct1745->treble, 1);
		fl_set_slider_value(fd_mixer_ct1745->treble, 
				    (get_mixer_reg(0x44) >> 4) & 0xf);
		fl_set_slider_step(fd_mixer_ct1745->bass, 1);
		fl_set_slider_value(fd_mixer_ct1745->bass, 
				    (get_mixer_reg(0x46) >> 4) & 0xf);
		fl_show_form(fd_mixer_ct1745->mixer_ct1745, FL_PLACE_CENTER,
			     FL_FULLBORDER, "SoundModem Mixer");
		break;

	default:
		printf("unknown mixer device %d\n", mixdevice);
		exit(1);
	}
	
	for (;;) {
		fl_do_forms();
	}
	exit(0);
}
