/*
 *   capiinfo - show capi and controller profile etc.
 *   Copyright (C) 2004  Enrik Berkhan <Enrik.Berkhan@inka.de>
 *
 *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <sys/types.h>
#include <linux/capi.h>
#include <capi20.h>
#include <stdio.h>
#include <ctype.h>

typedef struct profile_info
{
	uint16_t ncontroller;
	uint16_t nbchannel;
	uint32_t global_opts;
	uint32_t b1_protocols;
	uint32_t b2_protocols;
	uint32_t b3_protocols;
	uint8_t reserved[24];
	uint8_t specific[20];
} __attribute__((packed)) profile_info;

typedef struct version_info
{
	uint32_t major;
	uint32_t minor;
	uint32_t manu_major;
	uint32_t manu_minor;
} __attribute__((packed)) version_info;

static const char *global_option[32] = {
	"Internal controller", "External equipment", "Handset", "DTMF", "Supplementary services",
	"Channel allocation", "Parameter B channel operation", "Line interconnect",
	"reserved 8", "reserved 9", "reserved 10", "reserved 11",
	"reserved 12", "reserved 13", "reserved 14", "reserved 15",
	"reserved 16", "reserved 17", "reserved 18", "reserved 19",
	"reserved 20", "reserved 21", "reserved 22", "reserved 23",
	"reserved 24", "reserved 25", "reserved 26", "reserved 27",
	"reserved 28", "reserved 29", "reserved 30", "reserved 31"
};

static const char *b1_proto[32] = {
	"64 kbits/s with HDLC framing", "64 kbits/s transparent operation with byte framing from network",
	"V.110 asynchronous operation with start/stop byte framing", "V.110 synchronous operation with HDLC framing",
	"T.30 modem for Group 3 fax", "64 kbit/s inverted with HDLC framing",
	"56 kbit/s bit-transparent operation with byte framing from network", "Modem with all negotiations",
	"Modem asynchronous operation with start/stop byte framing", "Modem synchronous operation with HDLC framing",
	"reserved 10", "reserved 11",
	"reserved 12", "reserved 13", "reserved 14", "reserved 15",
	"reserved 16", "reserved 17", "reserved 18", "reserved 19",
	"reserved 20", "reserved 21", "reserved 22", "reserved 23",
	"reserved 24", "reserved 25", "reserved 26", "reserved 27",
	"reserved 28", "reserved 29", "reserved 30", "reserved 31"
};

static const char *b2_proto[32] = {
	"ISO 7776 (X.75 SLP)", "Transparent", "SDLC", "LAPD in accordance with Q.921 for D channel X.25 (SAPI 16)",
	"T.30 for Group 3 fax", "Point-to-Point Protocol (PPP)",
	"Transparent (ignoring framing errors of B1 protocol)", "Modem error correction and compression (V.42 bis or MNP5)",
	"ISO 7776 (X.75 SLP) modified supporting V.42 bis compression", "V.120 asynchronous mode",
	"V.120 asynchronous mode supporting V.42 bis", "V.120 bit-transparent mode",
	"LAPD in accordance with Q.921 including free SAPI selection", "reserved 13", "reserved 14", "reserved 15",
	"reserved 16", "reserved 17", "reserved 18", "reserved 19",
	"reserved 20", "reserved 21", "reserved 22", "reserved 23",
	"reserved 24", "reserved 25", "reserved 26", "reserved 27",
	"reserved 28", "reserved 29", "reserved 30", "reserved 31"
};

static const char *b3_proto[32] = {
	"Transparent", "T.90NL with compatibility to T.70NL in accordance with T.90 Appendix II",
	"ISO 8208 (X.25 DTE-DTE)", "X.25 DCE",
	"T.30 for Group 3 fax", "T.30 for Group 3 fax with extensions", "reserved 6", "Modem",
	"reserved 8", "reserved 9", "reserved 10", "reserved 11",
	"reserved 12", "reserved 13", "reserved 14", "reserved 15",
	"reserved 16", "reserved 17", "reserved 18", "reserved 19",
	"reserved 20", "reserved 21", "reserved 22", "reserved 23",
	"reserved 24", "reserved 25", "reserved 26", "reserved 27",
	"reserved 28", "reserved 29", "reserved 30", "reserved 31"

};

void
dump_flags(uint32_t options, const char **names)
{
	int i;
	
	for (i = 0; i < 32; i++) {
		if (options & (1<<i))
			printf("\t\t%s\n", names[i]);
	}
	return;
}

char *
hexdump(unsigned char *buf, size_t len)
{
	static const char hexchar[] = "0123456789ABCDEF";
	static const size_t maxlen = 16;
	static char out[16 * 4 + 1];
	size_t i;

	if (len > maxlen) len = maxlen;
	for (i = 0; i < len; i++) {
		out[i*3 + 0] = hexchar[buf[i] >> 4];
		out[i*3 + 1] = hexchar[buf[i] & 0xf];
		out[i*3 + 2] = ' ';
	}
	for (i = len; i < maxlen; i++) {
		out[i*3 + 0] = out[i*3 + 1] = out[i*3 + 2] = ' ';
	}
	for (i = 0; i < len; i++) {
		out[maxlen*3 + i] = isprint(buf[i]) ? buf[i] : '.';
	}
	for (i = len; i < maxlen; i++) {
		out[maxlen*3 + i] = ' ';
	}
	out[maxlen * 4] = 0;
	return out;
}

int
main(int argc, char **argv) {
	unsigned ret;
	version_info version;
	profile_info profile;
	unsigned char manu[64] = { 0 };
	int i, n;

	if (0 != (ret = capi20_isinstalled())) {
		fprintf(stderr, "CAPI not installed: 0x%X\n", ret);
		return 1;
	}

	if (0 == capi20_get_manufacturer(0, manu)) {
		fprintf(stderr, "capi20_get_manufacturer");
		return 1;
	}
	printf("CAPI manufacturer: %s\n", manu);

	if (0 == capi20_get_version(0, (unsigned char*)&version)) {
		fprintf(stderr, "capi20_get_version");
		return 1;
	}
	printf("CAPI version: %d.%d %d.%d\n", version.major, version.minor,
	       version.manu_major, version.manu_minor);

	if (0 == capi20_get_serial_number(0, (unsigned char*)&manu)) {
		fprintf(stderr, "capi20_get_serial");
		return 1;
	}
	printf("CAPI serial: %s\n", manu);

	if (0 != (ret = capi20_get_profile(0, (unsigned char *)&profile))) {
		fprintf(stderr, "capi20_get_profile: 0x%X\n", ret);
		return 1;
	}
	n = profile.ncontroller;
	printf("\n%d controllers found, enumerating...\n", n);
	
	for (i = 1; i <= n; i++) {
		printf("\nController %d:\n", i);
		if (0 == capi20_get_manufacturer(i, manu)) {
			fprintf(stderr, "capi20_get_manufacturer\n");
			return 1;
		}
		printf("\tmanufacturer: %s\n", manu);

		if (0 == capi20_get_version(i, (unsigned char*)&version)) {
			fprintf(stderr, "capi20_get_version");
			return 1;
		}
		printf("\tCAPI version: %d.%d %d.%d\n", version.major, version.minor,
	               version.manu_major, version.manu_minor);

		if (0 == capi20_get_serial_number(i, (unsigned char*)&manu)) {
			fprintf(stderr, "capi20_get_serial");
			return 1;
		}
		printf("\tCAPI serial: %s\n", manu);

		if (0 != (ret = capi20_get_profile(i, (unsigned char *)&profile))) {
			fprintf(stderr, "capi20_get_profile: 0x%X\n", ret);
			return 1;
		}
		printf("\tfeaturing global options:\n"); dump_flags(profile.global_opts, global_option); puts("");
		printf("\t%d B-channels featuring\n", profile.nbchannel);
		printf("\tB1 protocols:\n"); dump_flags(profile.b1_protocols, b1_proto); puts("");
		printf("\tB2 protocols:\n"); dump_flags(profile.b2_protocols, b2_proto); puts("");
		printf("\tB3 protocols:\n"); dump_flags(profile.b3_protocols, b3_proto); puts("");
		printf("\tmanufacturer specific information:\n");
		printf("\t%s\n", hexdump(((unsigned char*)&profile) + 44, 16));
		printf("\t%s\n", hexdump(((unsigned char*)&profile) + 60, 4));
	}

	return 0;
}
