/* $Id: icnctrl.c,v 1.6 2007/03/05 18:04:35 keil Exp $

 * ICN-ISDN driver for Linux. (Control-Utility)
 *
 * Copyright 1994,95 by Fritz Elfert (fritz@isdn4linux.de)
 * Copyright 1995 Thinking Objects Software GmbH Wuerzburg
 *
 * This file is part of Isdn4Linux.
 *
 * 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, 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.
 *
 * $Log: icnctrl.c,v $
 * Revision 1.6  2007/03/05 18:04:35  keil
 * Small fixes to avoid warnings with newer gcc
 *
 * Revision 1.5  2002/01/31 18:55:45  paul
 * #include <stdlib.h> for prototypes against warnings.
 *
 * Revision 1.4  1999/09/06 08:03:24  fritz
 * Changed my mail-address.
 *
 * Revision 1.3  1997/06/21 14:38:22  fritz
 * Added option for enabling only one channel in leased mode.
 *
 * Revision 1.2  1997/05/17 12:23:29  fritz
 * Corrected some Copyright notes to refer to GPL.
 *
 * Revision 1.1  1997/02/17 00:08:53  fritz
 * New CVS tree
 *
 * Revision 1.2  1996/11/22 15:04:25  fritz
 * Minor cosmetix fix.
 *
 * Revision 1.1.1.1  1996/10/08 10:03:12  fritz
 * First Import.
 *
 * Revision 1.6  1995/12/18  18:25:24  fritz
 * Support for ICN-4B Cards.
 *
 * Revision 1.5  1995/10/29  21:44:02  fritz
 * Changed to support DriverId's.
 * Added support for leased lines.
 *
 * Revision 1.4  1995/04/23  13:43:02  fritz
 * Changed Copyright.
 *
 * Revision 1.3  1995/02/01  11:03:26  fritz
 * Added Messages, when loading bootcode and protocol.
 *
 * Revision 1.2  1995/01/09  07:41:59  fritz
 * Added GPL-Notice
 *
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <linux/isdn.h>
#ifdef __DEBUGVAR__
#define __KERNEL__
#endif
#include <icn.h>

#ifdef __DEBUGVAR__
typedef struct dbgstruct {
	ulong card;
	ulong dev;
} dbgstruct;
dbgstruct dbg;
#endif

int mem_fd;

char *cmd;
char *ctrldev;
int arg_ofs;

#if 0
/* #ifdef _POSIX_MAPPED_FILES */
#define DO_VIA_MMAP
#else
#include <malloc.h>
#endif

void
usage()
{
	fprintf(stderr, "usage: %s [-d driverId] io [mmio port]             (get/set hw-io)\n", cmd);
#ifdef __DEBUGVAR__
	fprintf(stderr, "   or: %s [-d driverId] dump                       (dump driver data)\n", cmd);
#endif
	fprintf(stderr, "   or: %s [-d driverId] add <portbase> [id1 [id2]] (Add a new card\n", cmd);
	fprintf(stderr, "   or: %s [-d driverId] load <bootcode> <protocol> (load firmware)\n", cmd);
	fprintf(stderr, "   or: %s [-d driverId] leased <1|2|on|off>         (Switch interface,\n",cmd);
    fprintf(stderr, "                                                    Channel n\n");
	fprintf(stderr, "                                                    into Leased-Line-Mode)\n");
	exit(-1);
}

#ifdef __DEBUGVAR__
u_char *
mapmem(ulong where, long size)
{
	u_char *addr;
#ifdef DO_VIA_MMAP
	ulong realw;
	long ofs;
#endif

	if ((mem_fd = open("/dev/mem", O_RDWR)) < 0) {
		perror("open /dev/mem");
		exit(1);
	}
#ifdef DO_VIA_MMAP
	size = ((size / PAGE_SIZE) + 2) * PAGE_SIZE;
	realw = PAGE_ALIGN(where) - PAGE_SIZE;
	ofs = where - realw;
	printf("%08lx - %08lx = %08x\n", where, realw, ofs);
	addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED,
		    mem_fd, realw);
	if ((int) addr < 0) {
		perror("mmap");
		exit(1);
	}
	return (addr + ofs);
#else
	addr = malloc(size);
	lseek(mem_fd, where, SEEK_SET);
	read(mem_fd, addr, size);
	return addr;
#endif
}

void
dump_dev(icn_dev * p)
{
	printf("dev struct at:          %p\n", p);
	printf("phys. dev struct at:    0x%08lx\n", dbg.dev);
	printf("shmem:                  %p\n", p->shmem);
	printf("mvalid:                 %d\n", p->mvalid);
	printf("channel:                %d\n", p->channel);
	printf("mcard:                  %p\n", p->mcard);
	printf("chanlock:               %d\n", p->chanlock);
	printf("firstload:              %d\n", p->firstload);
}

void
qdump(ulong queue)
{
	struct sk_buff_head *p;

	p = (struct sk_buff_head *) mapmem(queue, sizeof(struct sk_buff_head));
	printf("  len:                  %d\n", p->qlen);
}

void
dump_card(icn_card * p)
{
	printf("card struct at:         %p\n", p);
	printf("phys. card struct at:   0x%08lx\n", dbg.card);
	printf("next:                   %p\n", p->next);
	printf("other:                  %p\n", p->other);
	printf("port:                   0x%04x\n", p->port);
	printf("myid:                   %d\n", p->myid);
	printf("rvalid:                 %d\n", p->rvalid);
	printf("leased:                 %d\n", p->leased);
	printf("flags:                  0x%04x\n", p->flags);
	printf("doubleS0:               %d\n", p->doubleS0);
	printf("secondhalf:             %d\n", p->secondhalf);
	printf("fw_rev:                 %d\n", p->fw_rev);
	printf("ptype:                  %d\n", p->ptype);
	printf("st_timer\n");
	printf("rb_timer\n");
	printf("rcvbuf\n");
	printf("rcvidx[0]:              %d\n", p->rcvidx[0]);
	printf("rcvidx[1]:              %d\n", p->rcvidx[1]);
	printf("l2_proto[0]             %d\n", p->l2_proto[0]);
	printf("l2_proto[1]             %d\n", p->l2_proto[1]);
	printf("interface:\n");
	printf("iptr:                   %d\n", p->iptr);
	printf("imsg:                   %s\n", p->imsg);
	printf("msg_buf:                %p\n", p->msg_buf);
	printf("msg_buf_write:          %p\n", p->msg_buf_write);
	printf("msg_buf_read:           %p\n", p->msg_buf_read);
	printf("msg_buf_end:            %p\n", p->msg_buf_end);
	printf("sndcount[0]:            %d\n", p->sndcount[0]);
	printf("sndcount[1]:            %d\n", p->sndcount[1]);
	printf("spqueue[0]:             %p\n", &p->spqueue[0]);
	qdump((ulong) & p->spqueue[0]);
	printf("spqueue[1]:             %p\n", &p->spqueue[1]);
	qdump((ulong) & p->spqueue[1]);
	printf("regname:                %s\n", p->regname);
	printf("xmit_lock[0]:           %d\n", p->xmit_lock[0]);
	printf("xmit_lock[1]:           %d\n", p->xmit_lock[1]);
}
#endif

int
main(int argc, char **argv)
{
	int fd;
	FILE *code;
	int mmio,
	 port;
	int ac;
	int a;
	u_char buf[0x20000];
	isdn_ioctl_struct ioctl_s;
	icn_cdef newcard;

	cmd = strrchr(argv[0], '/');
	cmd = (cmd == NULL) ? argv[0] : cmd + 1;
	if (argc > 1) {
		if (!strcmp(argv[1], "-d")) {
			strcpy(ioctl_s.drvid, argv[2]);
			arg_ofs = 3;
		} else {
			ioctl_s.drvid[0] = '\0';
			arg_ofs = 1;
		}
	} else
		usage();
	ac = argc - (arg_ofs - 1);
	fd = open("/dev/isdnctrl", O_RDWR);
	if (fd < 0) {
		perror(ctrldev);
		exit(-1);
	}
#ifdef __DEBUGVAR__
	if (!strcmp(argv[arg_ofs], "dump")) {
		char *p;
		ioctl_s.arg = (ulong) & dbg;
		if ((ioctl(fd, ICN_IOCTL_DEBUGVAR + IIOCDRVCTL, &ioctl_s)) < 0) {
			perror("ioctl DEBUGVAR");
			exit(-1);
		}
		close(fd);
		p = mapmem(dbg.dev, sizeof(icn_dev));
		dump_dev((icn_dev *) p);
		p = mapmem(dbg.card, sizeof(icn_card));
		dump_card((icn_card *) p);
		return 0;
	}
#endif
	if (!strcmp(argv[arg_ofs], "leased")) {
		if (ac == 3) {
			ioctl_s.arg = 0;
			if ((!strcmp(argv[arg_ofs + 1], "on")) ||
			    (!strcmp(argv[arg_ofs + 1], "2"))  ||
			    (!strcmp(argv[arg_ofs + 1], "yes")))
				ioctl_s.arg = 3;
			if (!strcmp(argv[arg_ofs + 1], "1"))
				ioctl_s.arg = 1;
			if ((ioctl(fd, ICN_IOCTL_LEASEDCFG + IIOCDRVCTL, &ioctl_s)) < 0) {
				perror("ioctl LEASEDCFG");
				exit(-1);
			}
			close(fd);
			return 0;
		}
	}
	if (!strcmp(argv[arg_ofs], "add")) {
		if (ac >= 3) {
			sscanf(argv[arg_ofs + 1], "%i", &a);
			if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
			    || a == 0x340 || a == 0x350 || a == 0x360 ||
			    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
			    || a == 0x348 || a == 0x358 || a == 0x368) {
				ioctl_s.arg = (unsigned long) &newcard;
				newcard.port = a;
				newcard.id1[0] = 0;
				newcard.id2[0] = 0;
				if (ac >= 4)
					strncpy(newcard.id1, argv[arg_ofs + 2], sizeof(newcard.id1) - 1);
				if (ac == 5)
					strncpy(newcard.id2, argv[arg_ofs + 3], sizeof(newcard.id2) - 1);
				if ((ioctl(fd, ICN_IOCTL_ADDCARD + IIOCDRVCTL, &ioctl_s)) < 0) {
					perror("ioctl ADDCARD");
					exit(-1);
				}
				close(fd);
				return 0;
			}
		}
	}
	if (!strcmp(argv[arg_ofs], "io")) {
		if (ac == 4) {
			if (sscanf(argv[arg_ofs + 1], "%i", &mmio) != 1)
				usage();
			if (sscanf(argv[arg_ofs + 2], "%i", &port) != 1)
				usage();
			ioctl_s.arg = mmio;
			if ((mmio = ioctl(fd, ICN_IOCTL_SETMMIO + IIOCDRVCTL, &ioctl_s)) < 0) {
				perror("ioctl SETMMIO");
				exit(-1);
			}
			ioctl_s.arg = port;
			if ((port = ioctl(fd, ICN_IOCTL_SETPORT + IIOCDRVCTL, &ioctl_s)) < 0) {
				perror("ioctl SETPORT");
				exit(-1);
			}
		}
		if ((mmio = ioctl(fd, ICN_IOCTL_GETMMIO + IIOCDRVCTL, &ioctl_s)) < 0) {
			perror("ioctl GETMMIO");
			exit(-1);
		}
		if ((port = ioctl(fd, ICN_IOCTL_GETPORT + IIOCDRVCTL, &ioctl_s)) < 0) {
			perror("ioctl GETPORT");
			exit(-1);
		}
		printf("Memory-mapped io at 0x%08lx, port 0x%03x\n",
		       (unsigned long) mmio, (unsigned short) port);
		close(fd);
		return 0;
	}
	if (!strcmp(argv[arg_ofs], "load")) {
		int needed;
		switch (ioctl(fd, ICN_IOCTL_GETDOUBLE + IIOCDRVCTL, &ioctl_s)) {
			case 0:
				needed = 4;
				break;
			case 1:
				needed = 5;
				break;
			default:
				perror("ioctl GETDOUBLE");
				exit(-1);
		}
		if (ac == needed) {
			if (!(code = fopen(argv[arg_ofs + 1], "r"))) {
				perror(argv[arg_ofs + 1]);
				exit(-1);
			}
			if (fread(buf, 4096, 1, code) < 1) {
				fprintf(stderr, "Read error on %s\n", argv[arg_ofs + 1]);
				exit(-1);
			}
			printf("Loading Bootcode %s ... ", argv[arg_ofs + 1]);
			fflush(stdout);
			ioctl_s.arg = (ulong) buf;
			if (ioctl(fd, ICN_IOCTL_LOADBOOT + IIOCDRVCTL, &ioctl_s) < 0) {
				perror("\nioctl LOADBOOT");
				exit(-1);
			}
			fclose(code);
			printf("done\n");
			if (!(code = fopen(argv[arg_ofs + 2], "r"))) {
				perror(argv[arg_ofs + 2]);
				exit(-1);
			}
			if (fread(buf, 65536, 1, code) < 1) {
				fprintf(stderr, "Read error on %s\n", argv[arg_ofs + 2]);
				exit(-1);
			}
			fclose(code);
			if (needed == 5) {
				if (!(code = fopen(argv[arg_ofs + 3], "r"))) {
					perror(argv[arg_ofs + 3]);
					exit(-1);
				}
				if (fread(buf + 65536, 65536, 1, code) < 1) {
					fprintf(stderr, "Read error on %s\n", argv[arg_ofs + 3]);
					exit(-1);
				}
				fclose(code);
				printf("Loading Protocols %s\n", argv[arg_ofs + 2]);
				printf("              and %s ... ", argv[arg_ofs + 3]);
			} else
				printf("Loading Protocol %s ... ", argv[arg_ofs + 2]);
			fflush(stdout);
			ioctl_s.arg = (ulong) buf;
			if (ioctl(fd, ICN_IOCTL_LOADPROTO + IIOCDRVCTL, &ioctl_s) < 0) {
				perror("\nioctl LOADPROTO");
				exit(-1);
			}
			printf("done\n");
			close(fd);
			return 0;
		}
		usage();
	}
	usage();
	return 0;
}
