aboutsummaryrefslogtreecommitdiffstats
path: root/util/szap/czap.c
blob: 9d183f98ec99fd4e907691649bceef11d320ccda (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# This file lists all frequencies used in Western Europe for DVB-T.
# The transmission parameters listed here are the ones generally used in
# Italy, broadcast neworks in other countries do use different parameters.
# Moreover, other countries use a bandwidth of 8 MHz also for Band III
# channels.
#
# Compiled in December 2010 by Marco d'Itri <md@linux.it>.
#
# References:
# http://en.wikipedia.org/wiki/Band_I#Europe
# http://en.wikipedia.org/wiki/Band_III#Europe
# http://en.wikipedia.org/wiki/File:VHF_Usage.svg
# http://en.wikipedia.org/wiki/Television_channel_frequencies

# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy

### VHF - Band III ###
# 5
T 177500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 6
T 184500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 7
T 191500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 8
T 198500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 9
T 205500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 10
T 212500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 11
T 219500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE
# 12
T 226500000 7MHz 2/3 NONE QAM64 8k 1/32 NONE

### UHF - Band IV ###
# 21
T 474000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 22
T 482000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 23
T 490000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 24
T 498000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 25
T 506000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 26
T 514000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 27
T 522000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 28
T 530000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 29
T 538000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 30
T 546000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 31
T 554000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 32
T 562000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 33
T 570000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 34
T 578000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 35
T 586000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 36
T 594000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 37
T 602000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE

### UHF - Band V ###
# 38
T 610000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 39
T 618000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 40
T 626000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 41
T 634000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 42
T 642000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 43
T 650000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 44
T 658000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 45
T 666000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 46
T 674000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 47
T 682000000 8MHz 2/3 NONE QAM64 8k 1/32 NONE
# 48
T 690000000 
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>

#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>


static char FRONTEND_DEV [80];
static char DEMUX_DEV [80];

#define CHANNEL_FILE "/.czap/channels.conf"

#define ERROR(x...)							\
	do {								\
		fprintf(stderr, "ERROR: ");				\
		fprintf(stderr, x);					\
		fprintf (stderr, "\n");					\
	} while (0)

#define PERROR(x...)							\
	do {								\
		fprintf(stderr, "ERROR: ");				\
		fprintf(stderr, x);					\
		fprintf (stderr, " (%s)\n", strerror(errno));		\
	} while (0)


typedef struct {
	char *name;
	int value;
} Param;

static const Param inversion_list[] = {
	{ "INVERSION_OFF", INVERSION_OFF },
	{ "INVERSION_ON", INVERSION_ON },
	{ "INVERSION_AUTO", INVERSION_AUTO }
};

static const Param fec_list[] = {
	{ "FEC_1_2", FEC_1_2 },
	{ "FEC_2_3", FEC_2_3 },
	{ "FEC_3_4", FEC_3_4 },
	{ "FEC_4_5", FEC_4_5 },
	{ "FEC_5_6", FEC_5_6 },
	{ "FEC_6_7", FEC_6_7 },
	{ "FEC_7_8", FEC_7_8 },
	{ "FEC_8_9", FEC_8_9 },
	{ "FEC_AUTO", FEC_AUTO },
	{ "FEC_NONE", FEC_NONE }
};

static const Param modulation_list[] = {
	{ "QAM_16", QAM_16 },
	{ "QAM_32", QAM_32 },
	{ "QAM_64", QAM_64 },
	{ "QAM_128", QAM_128 },
	{ "QAM_256", QAM_256 },
	{ "QAM_AUTO", QAM_AUTO }
};

#define LIST_SIZE(x) sizeof(x)/sizeof(Param)


static
int parse_param(const char *val, const Param * plist, int list_size)
{
	int i;

	for (i = 0; i < list_size; i++) {
		if (strcasecmp(plist[i].name, val) == 0)
			return plist[i].value;
	}
	return -1;
}


static char line_buf[256];
static
char *find_channel(FILE *f, int list_channels, int *chan_no, const char *channel)
{
	size_t l;
	int lno = 0;

	l = channel ? strlen(channel) : 0;
	while (!feof(f)) {
		if (!fgets(line_buf, sizeof(line_buf), f))
			return NULL;
		lno++;
		if (list_channels) {
			printf("%3d %s", lno, line_buf);
		}
		else if (*chan_no) {
			if (*chan_no == lno)
				return line_buf;
		}
		else if ((strncasecmp(channel, line_buf, l) == 0)
				&& (line_buf[l] == ':')) {
			*chan_no = lno;
			return line_buf;
		}
	};

	return NULL;
}


int parse(const char *fname, int list_channels, int chan_no, const char *channel,
	  struct dvb_frontend_parameters *frontend, int *vpid, int *apid)
{
	FILE *f;
	char *chan;
	char *name, *inv, *fec, *mod;

	if ((f = fopen(fname, "r")) == NULL) {
		PERROR("could not open file '%s'", fname);
		return -1;
	}

	chan = find_channel(f, list_channels, &chan_no, channel);
	fclose(f);
	if (list_channels)
		return 0;
	if (!chan) {
		ERROR("could not find channel '%s' in channel list",
		      channel);
		return -2;
	}
	printf("%3d %s", chan_no, chan);

	if ((sscanf(chan, "%a[^:]:%d:%a[^:]:%d:%a[^:]:%a[^:]:%d:%d\n",
				&name, &frontend->frequency,
				&inv, &frontend->u.qam.symbol_rate,
				&fec, &mod, vpid, apid) != 8)
			|| !name || !inv || !fec | !mod) {
		ERROR("cannot parse service data");
		return -3;
	}
	frontend->inversion = parse_param(inv, inversion_list, LIST_SIZE(inversion_list));
	if (frontend->inversion < 0) {
		ERROR("inversion field syntax '%s'", inv);
		return -4;
	}
	frontend->u.qam.fec_inner = parse_param(fec, fec_list, LIST_SIZE(fec_list));
	if (frontend->u.qam.fec_inner < 0) {
		ERROR("FEC field syntax '%s'", fec);
		return -5;
	}
	frontend->u.qam.modulation = parse_param(mod, modulation_list,
			LIST_SIZE(modulation_list));
	if (frontend->u.qam.modulation < 0) {
		ERROR("modulation field syntax '%s'", mod);
		return -6;
	}
	printf("%3d %s: f %d, s %d, i %d, fec %d, qam %d, v %#x, a %#x\n",
			chan_no, name, frontend->frequency, frontend->u.qam.symbol_rate,
			frontend->inversion, frontend->u.qam.fec_inner,
			frontend->u.qam.modulation, *vpid, *apid);
	free(name);
	free(inv);
	free(fec);
	free(mod);

	return 0;
}



static
int set_pesfilter (int fd, int pid, dmx_pes_type_t type, int dvr)
{
	struct dmx_pes_filter_params pesfilter;

	if (pid <= 0 || pid >= 0x1fff)
		return 0;

	pesfilter.pid = pid;
	pesfilter.input = DMX_IN_FRONTEND;
	pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER;
	pesfilter.pes_type = type;
	pesfilter.flags = DMX_IMMEDIATE_START;

	if (ioctl(fd, DMX_SET_PES_FILTER, &pesfilter) < 0) {
		PERROR ("ioctl(DMX_SET_PES_FILTER) for %s PID failed",
			type == DMX_PES_AUDIO ? "Audio" :
			type == DMX_PES_VIDEO ? "Video" : "??");
		return -1;
	}

	return 0;
}

static
int setup_frontend(int fe_fd, struct dvb_frontend_parameters *frontend)
{
	struct dvb_frontend_info fe_info;

	if (ioctl(fe_fd, FE_GET_INFO, &fe_info) < 0) {
		PERROR ("ioctl FE_GET_INFO failed");
		return -1;
	}

	if (fe_info.type != FE_QAM) {
		ERROR ("frontend device is not a QAM (DVB-C) device");
		return -1;
	}

	if (ioctl(fe_fd, FE_SET_FRONTEND, frontend) < 0) {
		PERROR ("ioctl FE_SET_FRONTEND failed");
		return -1;
	}

	return 0;
}


static
int check_frontend (int fe_fd)
{
	fe_status_t status;
	uint16_t snr, signal;
	uint32_t ber, uncorrected_blocks;

	do {
		ioctl(fe_fd, FE_READ_STATUS, &status);
		ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal);
		ioctl(fe_fd, FE_READ_SNR, &snr);
		ioctl(fe_fd, FE_READ_BER, &ber);
		ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks);

		printf ("status %02x | signal %04x | snr %04x | "
			"ber %08x | unc %08x | ",
			status, signal, snr, ber, uncorrected_blocks);

		if (status & FE_HAS_LOCK)
			printf("FE_HAS_LOCK");

		usleep(1000000);

		printf("\n");
	} while (1);

	return 0;
}


static const char *usage = "\nusage: %s [-a adapter_num] [-f frontend_id] [-d demux_id] [-c conf_file] {<channel name>| -n channel_num}\n"
	"   or: %s [-c conf_file]  -l\n\n";


int main(int argc, char **argv)
{
	struct dvb_frontend_parameters frontend_param;
	char *homedir = getenv("HOME");
	char *confname = NULL;
	char *channel = NULL;
	int adapter = 0, frontend = 0, demux = 0, dvr = 0;
	int vpid, apid;
	int frontend_fd, video_fd, audio_fd;
	int opt, list_channels = 0, chan_no = 0;

        while ((opt = getopt(argc, argv, "ln:hrn:a:f:d:c:")) != -1) {
                switch (opt) {
                case 'a':
                        adapter = strtoul(optarg, NULL, 0);
                        break;
                case 'f':
                        frontend = strtoul(optarg, NULL, 0);
                        break;
                case 'd':
                        demux = strtoul(optarg, NULL, 0);
                        break;
                case 'r':
                        dvr = 1;
                        break;
                case 'l':
                        list_channels = 1;
                        break;
                case 'n':
                        chan_no = strtoul(optarg, NULL, 0);
                        break;
                case 'c':
                        confname = optarg;
                        break;
                case '?':
                case 'h':
                default:
                        fprintf (stderr, usage, argv[0], argv[0]);
                        return -1;
                };
        }
	
        if (optind < argc)
                channel = argv[optind];

        if (!channel && chan_no <= 0 && !list_channels) {
                fprintf (stderr, usage, argv[0], argv[0]);
                return -1;
        }

	if (!homedir)
		ERROR("$HOME not set");

        snprintf (FRONTEND_DEV, sizeof(FRONTEND_DEV),
                  "/dev/dvb/adapter%i/frontend%i", adapter, frontend);

        snprintf (DEMUX_DEV, sizeof(DEMUX_DEV),
                  "/dev/dvb/adapter%i/demux%i", adapter, demux);

	printf ("using '%s' and '%s'\n", FRONTEND_DEV, DEMUX_DEV);

	if (!confname)
	{
		if (!homedir)
			ERROR("$HOME not set");
		confname = malloc(strlen(homedir) + strlen(CHANNEL_FILE) + 1);
		memcpy(confname, homedir, strlen(homedir));
		memcpy(confname + strlen(homedir), CHANNEL_FILE,
		       strlen(CHANNEL_FILE) + 1);
	}
	memset(&frontend_param, 0, sizeof(struct dvb_frontend_parameters));

	if (parse(confname, list_channels, chan_no, channel, &frontend_param, &vpid, &apid))
		return -1;
	if (list_channels)
		return 0;

	if ((frontend_fd = open(FRONTEND_DEV, O_RDWR)) < 0) {
		PERROR("failed opening '%s'", FRONTEND_DEV);
		return -1;
	}

	if (setup_frontend(frontend_fd, &frontend_param) < 0)
		return -1;

	if ((video_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
		PERROR("failed opening '%s'", DEMUX_DEV);
		return -1;
	}

	if (set_pesfilter (video_fd, vpid, DMX_PES_VIDEO, dvr) < 0)
		return -1;

	if ((audio_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
		PERROR("failed opening '%s'", DEMUX_DEV);
		return -1;
	}

	if (set_pesfilter (audio_fd, apid, DMX_PES_AUDIO, dvr) < 0)
		return -1;

	check_frontend (frontend_fd);

	close (audio_fd);
	close (video_fd);
	close (frontend_fd);

	return 0;
}