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

/*
 *      eppfpga.c  -- HDLC packet radio modem for EPP using FPGA utility.
 *
 *      Copyright (C) 1998-2000  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.
 *
 */

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

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "getopt.h"
#endif

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif

#include "parport.h"
#include "fpga.h"

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

static unsigned verboselevel = 0;
static unsigned syslogmsg = 0;
static struct adapter_config cfg = { 19666600, 9600, 0, 0, 0, 1, 1, 0, 1 };
static int quit = 0;

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

int lprintf(unsigned vl, const char *format, ...)
{
        va_list ap;
        int r;

        if (vl > verboselevel)
                return 0;
	va_start(ap, format);
#ifdef HAVE_VSYSLOG
        if (syslogmsg) {
		static const int logprio[] = { LOG_ERR, LOG_INFO };
		vsyslog((vl > 1) ? LOG_DEBUG : logprio[vl], format, ap);
		r = 0;
	} else
#endif
                r = vfprintf(stderr, format, ap);
	va_end(ap);
        return r;
}

int tprintf(const char *format, ...)
{
	va_list ap;
	int i;

        va_start(ap, format);
        i = vprintf(format, ap);
        va_end(ap);
	fflush(stdout);
	return i;
}

int idle_callback(unsigned us_delay)
{
	if (us_delay)
		usleep(us_delay);
	return quit;
}

void epp_error(const char *msg)
{
	lprintf(0, msg);
	reset_modem();
	exit(1);
}

static void signal_quit(int signum)
{
	quit = 1;
}

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

int main(int argc, char *argv[])
{
        static const struct option long_options[] = {
#ifdef HAVE_PPUSER
                { "ppuser", 1, 0, 0x400 },
                { "ppdev", 1, 0, 0x402 },
#endif
#ifdef HAVE_PPKDRV
                { "ppkdrv", 1, 0, 0x401 },
#endif
#ifdef WIN32
                { "ppgenport", 0, 0, 0x403 },
                { "ppwin", 1, 0, 0x404 },
                { "pplpt", 1, 0, 0x404 },
                { "ppring0", 0, 0, 0x405 },
#endif
		{ "ppforcehwepp", 0, 0, 0x410 },
		{ "ppswemulepp", 0, 0, 0x411 },
		{ "ppswemulecp", 0, 0, 0x412 },
		{ 0, 0, 0, 0 }
        };
	int c, err = 0;
	const struct eppfpgatests *tst = NULL;
	unsigned int iobase = 0x378, ntddkgenport = 0, ntdrv = 0, w9xring0 = 0, ppflags = 0;
	const char *ppuser = NULL, *ppkdrv = NULL, *ppdev = NULL;

	printf("eppfpga v" VERSION " (c) 1998-2000 by Thomas Sailer, HB9JNX/AE4WA\n");
	while ((c = getopt_long(argc, argv, "svp:m:t:", long_options, NULL)) != EOF) {
		switch (c) {
		case 0x400:
			ppuser = optarg;
			ppkdrv = NULL;
			ppdev = NULL;
			ntddkgenport = 0;
                        ntdrv = 0;
                        w9xring0 = 0;
			break;

		case 0x401:
			ppkdrv = optarg;
			ppuser = NULL;
			ppdev = NULL;
			ntddkgenport = 0;
                        ntdrv = 0;
                        w9xring0 = 0;
			break;

		case 0x402:
			ppkdrv = NULL;
			ppuser = NULL;
			ppdev = optarg;
			ntddkgenport = 0;
                        ntdrv = 0;
                        w9xring0 = 0;
			break;

		case 0x403:
			ppkdrv = NULL;
			ppuser = NULL;
			ppdev = NULL;
			ntddkgenport = 1;
                        ntdrv = 0;
                        w9xring0 = 0;
                        break;

		case 0x404:
			ppkdrv = NULL;
			ppuser = NULL;
			ppdev = NULL;
			ntddkgenport = 0;
                        ntdrv = strtoul(optarg, NULL, 0);
                        w9xring0 = 0;
			break;

		case 0x405:
			ppkdrv = NULL;
			ppuser = NULL;
			ppdev = NULL;
			ntddkgenport = 0;
                        ntdrv = 0;
                        w9xring0 = 1;
			break;

		case 0x410:
			ppflags |= PPFLAG_FORCEHWEPP;
			break;

		case 0x411:
			ppflags |= PPFLAG_SWEMULEPP;
			break;

		case 0x412:
			ppflags |= PPFLAG_SWEMULECP;
			break;

#if defined(HAVE_SYSLOG) && defined(HAVE_VSYSLOG)
		case 's':
			if (syslogmsg)
				break;
			openlog("eppfpga", LOG_PID, LOG_USER);
			syslogmsg = 1;
			break;
#endif

		case 'v':
			verboselevel++;
			break;

		case 'p': 
			iobase = strtoul(optarg, NULL, 0);
			if (iobase <= 0 || iobase >= 0x3f8)
				err++;
			ppuser = NULL;
			ppkdrv = NULL;
			break;
			
		case 'm':
			parseconfig(&cfg, optarg);
			break;

		case 't':
			if (tst != NULL) {
				err++;
				break;
			}
			for (tst = eppfpgatests; tst->name && strcmp(optarg, tst->name); tst++);
			if (!tst->name)
				err++;
			break;

		default:
			err++;
			break;
		}
	}
	if (err) {
		lprintf(0, "usage: %s [-v] [-p <portaddr>] [-m <modestr>] [-t <test>]\n"
			"  mode options:\n"
			"    intclk    uses FPGA internal clock; DON'T use if an external osc is on board\n"
			"    extclk    uses the external oscillator; recommended if the internal modem is used\n"
			"    intmodem  uses the internal modem\n"
			"    extmodem  uses a DF9IC type modem connected to the high speed modem connector\n"
			"    fclk=     sets the FPGA clock frequency (default 19.6666 MHz)\n"
			"    bitrate=  sets the bitrate for the FSK modem\n"
			"    loopback  sets an internal modem loopback\n"
			"    extstat   allows extended status readback\n"
			"    noextstat disallows extended status readback\n"
			"  test options:\n", argv[0]);
		for (tst = eppfpgatests; tst->name; tst++)
			lprintf(0, "    %-15s%s\n", tst->name, tst->desc);
		exit(1);
	}
#ifdef HAVE_PPUSER
	if (ppkdrv) {
		if (parport_init_ppkdrv(ppkdrv)) {
			fprintf(stderr, "no kernel interface %s driver found\n", ppkdrv);
			exit(1);
		}
	} else
#endif
#ifdef HAVE_PPUSER
		if (ppdev) {
			if (parport_init_ppdev(ppdev)) {
				fprintf(stderr, "no ppdev driver found at %s\n", ppdev);
				exit(1);
			}
		} else if (ppuser) {
			if (parport_init_ppuser(ppuser)) {
				fprintf(stderr, "no ppuser driver found at %s\n", ppuser);
				exit(1);
			}
		} else
#endif
#ifdef WIN32
                        if (ntdrv) {
                                if (parport_init_win_flags(ntdrv-1, ppflags)) {
                                        fprintf(stderr, "no eppflex.sys/vxd driver found\n");
                                        exit(1);
                                }
                        } else if (ntddkgenport) {
                                if (parport_init_ntddkgenport()) {
                                        fprintf(stderr, "no NTDDK genport.sys driver found\n");
                                        exit(1);
                                }
                        } else if (w9xring0) {
                                if (parport_init_w9xring0_flags(iobase, ppflags)) {
                                        fprintf(stderr, "no parport found at 0x%x\n", iobase);
                                        exit(1);
                                }
                        } else
#endif
                                if (parport_init_direct_flags(iobase, ppflags)) {
                                        fprintf(stderr, "no parport found at 0x%x\n", iobase);
                                        exit(1);
                                }
	if (!tst) {
	  err = abs(adapter_start_epp(&cfg));
	  goto out;
	}
#ifdef SIGHUP
	signal(SIGHUP, signal_quit);
#endif
	signal(SIGINT, signal_quit);
#ifdef SIGQUIT
	signal(SIGQUIT, signal_quit);
#endif
	signal(SIGTERM, signal_quit);
	err = abs(tst->func(&cfg));
 out:
#ifdef WIN32
	if (!ppkdrv && !ppuser && !ppdev && !ntddkgenport && ntdrv)
	    parport_stop_win();
#endif
	exit(err);
}
