diff options
| author | etobi <git@e-tobi.net> | 2013-09-03 09:48:38 +0200 | 
|---|---|---|
| committer | etobi <git@e-tobi.net> | 2013-09-03 09:48:38 +0200 | 
| commit | 6e40287e2f39a80fc72bd8d0fbc1a8334d688c2d (patch) | |
| tree | 024bef311226653bdd1da4fa588becf5098bcff7 /util/scan | |
| download | linux-dvb-apps-6e40287e2f39a80fc72bd8d0fbc1a8334d688c2d.tar.gz | |
Imported Upstream version 1.1.0upstream/1.1.0
Diffstat (limited to '')
49 files changed, 2787 insertions, 0 deletions
| diff --git a/util/scan/Makefile b/util/scan/Makefile new file mode 100644 index 0000000..a82d865 --- /dev/null +++ b/util/scan/Makefile @@ -0,0 +1,21 @@ + +CC = gcc +CFLAGS = -MD -g -Wall -O2 -I../../include -I../lib +LFLAGS = -g -Wall + +OBJS = diseqc.o dump-zap.o dump-vdr.o scan.o ../lib/lnb.o +SRCS = $(OBJS:.o=.c) + +TARGET = scan + +$(TARGET): $(OBJS) +	$(CC) $(LFLAGS) -o $(TARGET) $(OBJS) + +.c.o: +	$(CC) $(CFLAGS) -c $< -o $@ + +clean: +	$(RM) *.o *.d $(TARGET) + +-include $(wildcard *.d) dummy + diff --git a/util/scan/README b/util/scan/README new file mode 100644 index 0000000..a6c1767 --- /dev/null +++ b/util/scan/README @@ -0,0 +1,18 @@ +Hi, + +this is a little channel scan utility to generate szap/tzap/czap compatible  +channel lists. Scan does not do a frequency scan, however, so you must +manually provide the data for tuning to one or more start transponders. +A number of initial-tuning-data files are provided for various dvb-c, dvb-s +and dvb-t networks around the world. If you make a new one feel free to +submit it to the linux-dvb mailing list http://linuxtv.org/mailinglists.xml. + +Basic usage: ./scan dvb-s/Astra-19.2E | tee mychannels.conf + +If you want it to check a specific frequency, tune to that frequency  +(e.g. using szap/tzap/czap) and then use './scan -c'. + +For more scan options see ./scan -h. + +Good luck, +Holger + Johannes diff --git a/util/scan/diseqc.c b/util/scan/diseqc.c new file mode 100644 index 0000000..c763261 --- /dev/null +++ b/util/scan/diseqc.c @@ -0,0 +1,108 @@ +#include <linux/dvb/frontend.h> +#include <sys/ioctl.h> +#include <time.h> + +#include "scan.h" +#include "diseqc.h" + + +struct diseqc_cmd switch_cmds[] = { +	{ { { 0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf2, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf1, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf3, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf4, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf6, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf5, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf7, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf8, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xfa, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xf9, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xfb, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xfc, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xfe, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xfd, 0x00, 0x00 }, 4 }, 0 }, +	{ { { 0xe0, 0x10, 0x38, 0xff, 0x00, 0x00 }, 4 }, 0 } +}; + + +/*--------------------------------------------------------------------------*/ + +static inline +void msleep(uint32_t msec) +{ +	struct timespec req = { msec / 1000, 1000000 * (msec % 1000) }; + +	while (nanosleep(&req, &req)) +		; +} + +#define printf(x...) + + +int diseqc_send_msg (int fd, fe_sec_voltage_t v, struct diseqc_cmd **cmd, +		     fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b) +{ +	int err; + +	if ((err = ioctl(fd, FE_SET_TONE, SEC_TONE_OFF))) +		return err; + +	if ((err = ioctl(fd, FE_SET_VOLTAGE, v))) +		return err; + +	msleep(15); +	while (*cmd) { +		debug("DiSEqC: %02x %02x %02x %02x %02x %02x\n", +		    (*cmd)->cmd.msg[0], (*cmd)->cmd.msg[1], +		    (*cmd)->cmd.msg[2], (*cmd)->cmd.msg[3], +		    (*cmd)->cmd.msg[4], (*cmd)->cmd.msg[5]); + +		if ((err = ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &(*cmd)->cmd))) +			return err; + +		msleep((*cmd)->wait); +		cmd++; +	} + +	//debug(" %s ", v == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : +	//    v == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "???"); + +	//debug(" %s ", b == SEC_MINI_A ? "SEC_MINI_A" : +	//    b == SEC_MINI_B ? "SEC_MINI_B" : "???"); + +	//debug(" %s\n", t == SEC_TONE_ON ? "SEC_TONE_ON" : +	//    t == SEC_TONE_OFF ? "SEC_TONE_OFF" : "???"); + +	msleep(15); + +	if ((err = ioctl(fd, FE_DISEQC_SEND_BURST, b))) +		return err; + +	msleep(15); + +	return ioctl(fd, FE_SET_TONE, t); +} + + +int setup_switch (int frontend_fd, int switch_pos, int voltage_18, int hiband) +{ +	struct diseqc_cmd *cmd[2] = { NULL, NULL }; +	int i = 4 * switch_pos + 2 * hiband + (voltage_18 ? 1 : 0); + +	verbose("DiSEqC: switch pos %i, %sV, %sband (index %d)\n", +	    switch_pos, voltage_18 ? "18" : "13", hiband ? "hi" : "lo", i); + +	if (i < 0 || i >= sizeof(switch_cmds)/sizeof(struct diseqc_cmd)) +		return -EINVAL; + +	cmd[0] = &switch_cmds[i]; + +	return diseqc_send_msg (frontend_fd, +				i % 2 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13, +				cmd, +				(i/2) % 2 ? SEC_TONE_ON : SEC_TONE_OFF, +				(i/4) % 2 ? SEC_MINI_B : SEC_MINI_A); +} + + diff --git a/util/scan/diseqc.h b/util/scan/diseqc.h new file mode 100644 index 0000000..d44d99b --- /dev/null +++ b/util/scan/diseqc.h @@ -0,0 +1,25 @@ +#ifndef __DISEQC_H__ +#define __DISEQC_H__ + +#include <stdint.h> +#include <linux/dvb/frontend.h> + + +struct diseqc_cmd { +	struct dvb_diseqc_master_cmd cmd; +	uint32_t wait; +}; + + +extern int diseqc_send_msg (int fd, fe_sec_voltage_t v, struct diseqc_cmd **cmd, +			    fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b); + + +/** + *   set up the switch to position/voltage/tone + */ +extern int setup_switch (int frontend_fd, int switch_pos, int voltage_18, int freq); + + +#endif + diff --git a/util/scan/dump-vdr.c b/util/scan/dump-vdr.c new file mode 100644 index 0000000..8f86654 --- /dev/null +++ b/util/scan/dump-vdr.c @@ -0,0 +1,161 @@ +#include <stdio.h> +#include "dump-vdr.h" +#include <linux/dvb/frontend.h> + + +static const char *inv_name [] = { +	"0", +	"1", +	"999" +}; + +static const char *fec_name [] = { +	"0", +	"12", +	"23", +	"34", +	"45", +	"56", +	"67", +	"78", +	"89", +	"999" +}; + +static const char *qam_name [] = { +	"0", +	"16", +	"32", +	"64", +	"128", +	"256", +	"999" +}; + + +static const char *bw_name [] = { +	"8", +	"7", +	"6", +	"999" +}; + + +static const char *mode_name [] = { +	"2", +	"8", +	"999" +}; + +static const char *guard_name [] = { +	"32", +	"16", +	"8", +	"4", +	"999" +}; + + +static const char *hierarchy_name [] = { +	"0", +	"1", +	"2", +	"4", +	"999" +}; + +static const char *west_east_flag_name [] = { +	"W", +	"E" +}; + +void vdr_dump_dvb_parameters (FILE *f, fe_type_t type, +		struct dvb_frontend_parameters *p, +		char polarity, int orbital_pos, int we_flag) +{ +	switch (type) { +	case FE_QPSK: +		fprintf (f, "%i:", p->frequency / 1000); +		fprintf (f, "%c:", polarity); +		fprintf (f, "S%i.%i%s:", orbital_pos/10, +			 orbital_pos % 10, west_east_flag_name[we_flag]); +		fprintf (f, "%i:", p->u.qpsk.symbol_rate / 1000); +		break; + +	case FE_QAM: +		fprintf (f, "%i:", p->frequency / 1000000); +		fprintf (f, "M%s:C:", qam_name[p->u.qam.modulation]); +		fprintf (f, "%i:", p->u.qam.symbol_rate / 1000); +		break; + +	case FE_OFDM: +		fprintf (f, "%i:", p->frequency / 1000); +		fprintf (f, "I%s", inv_name[p->inversion]); +		fprintf (f, "B%s", bw_name[p->u.ofdm.bandwidth]); +		fprintf (f, "C%s", fec_name[p->u.ofdm.code_rate_HP]); +		fprintf (f, "D%s", fec_name[p->u.ofdm.code_rate_LP]); +		fprintf (f, "M%s", qam_name[p->u.ofdm.constellation]); +		fprintf (f, "T%s", mode_name[p->u.ofdm.transmission_mode]); +		fprintf (f, "G%s", guard_name[p->u.ofdm.guard_interval]); +		fprintf (f, "Y%s", hierarchy_name[p->u.ofdm.hierarchy_information]); +		fprintf (f, ":T:27500:"); +		break; + +	default: +		; +	}; +} + +void vdr_dump_service_parameter_set (FILE *f, +				 const char *service_name, +				 const char *provider_name, +				 fe_type_t type, +				 struct dvb_frontend_parameters *p, +				 char polarity, +				 int video_pid, +				 int pcr_pid, +				 uint16_t *audio_pid, +                                 int audio_num, +				 int teletext_pid, +				 int scrambled, +				 int ac3_pid, +                                 int service_id, +				 int network_id, +				 int transport_stream_id, +				 int orbital_pos, +				 int we_flag, +				 int dump_provider, +				 int ca_select, +				 int vdr_version, +				 int dump_channum, +				 int channel_num) +{ +        int i; + +	if ((video_pid || audio_pid[0]) && ((ca_select > 0) || ((ca_select == 0) && (scrambled == 0)))) { +		if ((dump_channum == 1) && (channel_num > 0)) +			fprintf(f, ":@%i\n", channel_num); +		if (dump_provider == 1) +			fprintf (f, "%s - ", provider_name); +		fprintf (f, "%s:", service_name); +		vdr_dump_dvb_parameters (f, type, p, polarity, orbital_pos, we_flag); +		if ((pcr_pid != video_pid) && (video_pid > 0)) +			fprintf (f, "%i+%i:", video_pid, pcr_pid); +		else +			fprintf (f, "%i:", video_pid); +		fprintf (f, "%i", audio_pid[0]); +	        for (i = 1; i < audio_num; i++) +			fprintf (f, ",%i", audio_pid[i]); +		if (ac3_pid) +			fprintf (f, ";%i", ac3_pid); +		if (scrambled == 1) scrambled = ca_select; +		if (vdr_version == 2) { +			network_id = 0; +			transport_stream_id = 0; +		}  +		fprintf (f, ":%d:%d:%d:%d:%d:0", teletext_pid, scrambled, +				service_id, network_id, transport_stream_id); +		fprintf (f, "\n"); +	} +} + diff --git a/util/scan/dump-vdr.h b/util/scan/dump-vdr.h new file mode 100644 index 0000000..0602026 --- /dev/null +++ b/util/scan/dump-vdr.h @@ -0,0 +1,38 @@ +#ifndef __DUMP_VDR_H__ +#define __DUMP_VDR_H__ + +#include <stdint.h> +#include <linux/dvb/frontend.h> + +extern +void vdr_dump_dvb_parameters (FILE *f, fe_type_t type, +		struct dvb_frontend_parameters *p, +		char polarity, int orbital_pos, int we_flag); + +extern +void vdr_dump_service_parameter_set (FILE *f, +				 const char *service_name, +				 const char *provider_name, +				 fe_type_t type, +				 struct dvb_frontend_parameters *p, +				 char polarity, +				 int video_pid, +				 int pcr_pid, +				 uint16_t *audio_pid, +                                 int audio_num, +				 int teletext_pid, +				 int scrambled, +				 int ac3_pid, +                                 int service_id, +				 int network_id, +				 int transport_stream_id, +				 int orbital_pos, +				 int we_flag, +				 int dump_provider, +				 int ca_select, +				 int vdr_version, +				 int dump_channum, +				 int channel_num); + +#endif + diff --git a/util/scan/dump-zap.c b/util/scan/dump-zap.c new file mode 100644 index 0000000..fb46a2a --- /dev/null +++ b/util/scan/dump-zap.c @@ -0,0 +1,119 @@ +#include <stdio.h> +#include <linux/dvb/frontend.h> +#include "dump-zap.h" + +static const char *inv_name [] = { +	"INVERSION_OFF", +	"INVERSION_ON", +	"INVERSION_AUTO" +}; + +static const char *fec_name [] = { +	"FEC_NONE", +	"FEC_1_2", +	"FEC_2_3", +	"FEC_3_4", +	"FEC_4_5", +	"FEC_5_6", +	"FEC_6_7", +	"FEC_7_8", +	"FEC_8_9", +	"FEC_AUTO" +}; + + +static const char *qam_name [] = { +	"QPSK", +	"QAM_16", +	"QAM_32", +	"QAM_64", +	"QAM_128", +	"QAM_256", +	"QAM_AUTO" +}; + + +static const char *bw_name [] = { +	"BANDWIDTH_8_MHZ", +	"BANDWIDTH_7_MHZ", +	"BANDWIDTH_6_MHZ", +	"BANDWIDTH_AUTO" +}; + + +static const char *mode_name [] = { +	"TRANSMISSION_MODE_2K", +	"TRANSMISSION_MODE_8K", +	"TRANSMISSION_MODE_AUTO" +}; + +static const char *guard_name [] = { +	"GUARD_INTERVAL_1_32", +	"GUARD_INTERVAL_1_16", +	"GUARD_INTERVAL_1_8", +	"GUARD_INTERVAL_1_4", +	"GUARD_INTERVAL_AUTO" +}; + + +static const char *hierarchy_name [] = { +	"HIERARCHY_NONE", +	"HIERARCHY_1", +	"HIERARCHY_2", +	"HIERARCHY_4", +	"HIERARCHY_AUTO" +}; + + +void zap_dump_dvb_parameters (FILE *f, fe_type_t type, struct dvb_frontend_parameters *p, char polarity, int sat_number) +{ +	switch (type) { +	case FE_QPSK: +		fprintf (f, "%i:", p->frequency / 1000);	/* channels.conf wants MHz */ +		fprintf (f, "%c:", polarity); +		fprintf (f, "%d:", sat_number); +		fprintf (f, "%i", p->u.qpsk.symbol_rate / 1000); /* channels.conf wants kBaud */ +		/*fprintf (f, "%s", fec_name[p->u.qpsk.fec_inner]);*/ +		break; + +	case FE_QAM: +		fprintf (f, "%i:", p->frequency); +		fprintf (f, "%s:", inv_name[p->inversion]); +		fprintf (f, "%i:", p->u.qpsk.symbol_rate); +		fprintf (f, "%s:", fec_name[p->u.qpsk.fec_inner]); +		fprintf (f, "%s", qam_name[p->u.qam.modulation]); +		break; + +	case FE_OFDM: +		fprintf (f, "%i:", p->frequency); +		fprintf (f, "%s:", inv_name[p->inversion]); +		fprintf (f, "%s:", bw_name[p->u.ofdm.bandwidth]); +		fprintf (f, "%s:", fec_name[p->u.ofdm.code_rate_HP]); +		fprintf (f, "%s:", fec_name[p->u.ofdm.code_rate_LP]); +		fprintf (f, "%s:", qam_name[p->u.ofdm.constellation]); +		fprintf (f, "%s:", mode_name[p->u.ofdm.transmission_mode]); +		fprintf (f, "%s:", guard_name[p->u.ofdm.guard_interval]); +		fprintf (f, "%s", hierarchy_name[p->u.ofdm.hierarchy_information]); +		break; + +	default: +		; +	}; +} + +void zap_dump_service_parameter_set (FILE *f,  +				 const char *service_name, +				 fe_type_t type, +				 struct dvb_frontend_parameters *p, +				 char polarity, +				 int sat_number, +				 uint16_t video_pid, +				 uint16_t *audio_pid, +				 uint16_t service_id) +{ +	fprintf (f, "%s:", service_name); +	zap_dump_dvb_parameters (f, type, p, polarity, sat_number); +	fprintf (f, ":%i:%i:%i", video_pid, audio_pid[0], service_id); +	fprintf (f, "\n"); +} + diff --git a/util/scan/dump-zap.h b/util/scan/dump-zap.h new file mode 100644 index 0000000..6763dc2 --- /dev/null +++ b/util/scan/dump-zap.h @@ -0,0 +1,20 @@ +#ifndef __DUMP_ZAP_H__ +#define __DUMP_ZAP_H__ + +#include <stdint.h> +#include <linux/dvb/frontend.h> + +extern void zap_dump_dvb_parameters (FILE *f, fe_type_t type, +		struct dvb_frontend_parameters *t, char polarity, int sat); + +extern void zap_dump_service_parameter_set (FILE *f, +				 const char *service_name, +				 fe_type_t type, +				 struct dvb_frontend_parameters *t, +				 char polarity, int sat, +				 uint16_t video_pid, +				 uint16_t *audio_pid, +				 uint16_t service_id); + +#endif + diff --git a/util/scan/dvb-c/at-Vienna b/util/scan/dvb-c/at-Vienna new file mode 100644 index 0000000..2c3d29c --- /dev/null +++ b/util/scan/dvb-c/at-Vienna @@ -0,0 +1,3 @@ +# Kabel Vienna +# freq sr fec mod +C 377750000 6900000 NONE QAM256 diff --git a/util/scan/dvb-c/ch-unknown b/util/scan/dvb-c/ch-unknown new file mode 100644 index 0000000..a9852d1 --- /dev/null +++ b/util/scan/dvb-c/ch-unknown @@ -0,0 +1,3 @@ +# Kabel Suisse +# freq sr fec mod +C 530000000 6900000 NONE QAM64 diff --git a/util/scan/dvb-c/de-Berlin b/util/scan/dvb-c/de-Berlin new file mode 100644 index 0000000..4a53b74 --- /dev/null +++ b/util/scan/dvb-c/de-Berlin @@ -0,0 +1,4 @@ +# Kabel Berlin +# freq sr fec mod +C 394000000 6900000 NONE QAM64 +C 113000000 6900000 NONE QAM64 diff --git a/util/scan/dvb-c/de-iesy b/util/scan/dvb-c/de-iesy new file mode 100644 index 0000000..a289951 --- /dev/null +++ b/util/scan/dvb-c/de-iesy @@ -0,0 +1,3 @@ +# Kabel iesy +# freq sr fec mod +C 410000000 6900000 NONE QAM64 diff --git a/util/scan/dvb-c/fi-3ktv b/util/scan/dvb-c/fi-3ktv new file mode 100644 index 0000000..55ccfd6 --- /dev/null +++ b/util/scan/dvb-c/fi-3ktv @@ -0,0 +1,3 @@ +# 3KTV +# freq sr fec mod +C 306000000 6875000 NONE QAM64 diff --git a/util/scan/dvb-c/fi-vaasa-oncable b/util/scan/dvb-c/fi-vaasa-oncable new file mode 100644 index 0000000..380ccf7 --- /dev/null +++ b/util/scan/dvb-c/fi-vaasa-oncable @@ -0,0 +1,13 @@ +# OnCable (Finland / Vaasa) +# freq sr fec mod +C 386000000 6875000 NONE QAM64 +C 394000000 6875000 NONE QAM64 +C 143000000 6875000 NONE QAM64 +C 434000000 6875000 NONE QAM64 +C 362000000 6875000 NONE QAM64 +C 418000000 6875000 NONE QAM64 +C 426000000 6875000 NONE QAM64 +C 314000000 6875000 NONE QAM64 +C 410000000 6875000 NONE QAM64 +C 442000000 6875000 NONE QAM64 +C 306000000 6875000 NONE QAM64 diff --git a/util/scan/dvb-s/Astra-19.2E b/util/scan/dvb-s/Astra-19.2E new file mode 100644 index 0000000..74e1b59 --- /dev/null +++ b/util/scan/dvb-s/Astra-19.2E @@ -0,0 +1,3 @@ +# Astra 19.2E SDT info service transponder +# freq pol sr fec +S 12551500 V 22000000 5/6 diff --git a/util/scan/dvb-s/Hispasat-30.0W b/util/scan/dvb-s/Hispasat-30.0W new file mode 100644 index 0000000..2c200b5 --- /dev/null +++ b/util/scan/dvb-s/Hispasat-30.0W @@ -0,0 +1,6 @@ +# Hispasat 30.0W +# freq pol sr fec +S 11539000 V 24500000 5/6 +S 11931000 H 27500000 3/4 +S 12015000 V 27500000 3/4 +S 12567000 H 19850000 3/4 diff --git a/util/scan/dvb-s/Hotbird-13.0E b/util/scan/dvb-s/Hotbird-13.0E new file mode 100644 index 0000000..f2168da --- /dev/null +++ b/util/scan/dvb-s/Hotbird-13.0E @@ -0,0 +1,3 @@ +# EUTELSAT SkyPlex, Hotbird 13E +# freq pol sr fec +S 12539000 H 27500000 3/4 diff --git a/util/scan/dvb-s/PAS-43.0W b/util/scan/dvb-s/PAS-43.0W new file mode 100644 index 0000000..fab84c5 --- /dev/null +++ b/util/scan/dvb-s/PAS-43.0W @@ -0,0 +1,6 @@ +# PAS 6/6B/3R at 43.0W +# freq pol sr fec +S 12578000 H 19850000 3/4 +S 12584000 V 27500000 3/4 +S 12606000 H  6616000 3/4 +S 12665000 H 19850000 7/8 diff --git a/util/scan/dvb-s/Sirius-5.0E b/util/scan/dvb-s/Sirius-5.0E new file mode 100644 index 0000000..ec4e4ea --- /dev/null +++ b/util/scan/dvb-s/Sirius-5.0E @@ -0,0 +1,5 @@ +# Sirius 5.0E +# freq pol sr fec +S 11823000 V 27500000 3/4 +S 11977000 V 27500000 3/4 +S 12054000 V 27500000 3/4 diff --git a/util/scan/dvb-s/Telecom2-8.0W b/util/scan/dvb-s/Telecom2-8.0W new file mode 100644 index 0000000..91678b4 --- /dev/null +++ b/util/scan/dvb-s/Telecom2-8.0W @@ -0,0 +1,4 @@ +# Telecom2 8.0W +# freq pol sr fec +S 11635000 H 6800000 5/6 +S 12687000 V 1879000 3/4 diff --git a/util/scan/dvb-s/Telstar12-15.0W b/util/scan/dvb-s/Telstar12-15.0W new file mode 100644 index 0000000..529b91f --- /dev/null +++ b/util/scan/dvb-s/Telstar12-15.0W @@ -0,0 +1,4 @@ +# Telstar 12 15.0W +# freq pol sr fec +S 12041000 H 3256000 2/3 +S 12520000 V 8700000 1/2 diff --git a/util/scan/dvb-s/Thor-1.0W b/util/scan/dvb-s/Thor-1.0W new file mode 100644 index 0000000..1f8f1d2 --- /dev/null +++ b/util/scan/dvb-s/Thor-1.0W @@ -0,0 +1,8 @@ +# Thor 1.0W +# freq pol sr fec +S 11247000 V 24500000 7/8 +S 11293000 H 24500000 7/8 +S 11325000 H 24500000 7/8 +S 12054000 H 28000000 7/8 +S 12169000 H 28000000 7/8 +S 12226000 V 28000000 7/8 diff --git a/util/scan/dvb-s/Turksat-42.0E b/util/scan/dvb-s/Turksat-42.0E new file mode 100644 index 0000000..1ac7fd8 --- /dev/null +++ b/util/scan/dvb-s/Turksat-42.0E @@ -0,0 +1,4 @@ +# Turksat 42.0E +# freq pol sr fec +S 11594000 H 4557000 5/6 +S 10978000 V 2344000 3/4 diff --git a/util/scan/dvb-t/au-Darwin b/util/scan/dvb-t/au-Darwin new file mode 100644 index 0000000..522bb1d --- /dev/null +++ b/util/scan/dvb-t/au-Darwin @@ -0,0 +1,5 @@ +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 543625000 7MHz 3/4 NONE QAM64 8k 1/16 NONE +T 550500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +T 536625000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +T 557625000 7MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/au-canberra b/util/scan/dvb-t/au-canberra new file mode 100644 index 0000000..5e71b07 --- /dev/null +++ b/util/scan/dvb-t/au-canberra @@ -0,0 +1,12 @@ +# Australia / Canberra / Woden +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +# ABC +T 205625000 7MHz 3/4 3/4 QAM64 8k 1/16 NONE +# Seven +T 177500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +# Nine +T 191625000 7MHz 3/4 NONE QAM64 8k 1/16 NONE +# Ten +T 219500000 7MHz 3/4 1/2 QAM64 8k 1/16 NONE +# SBS +T 543500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/au-sydney_north_shore b/util/scan/dvb-t/au-sydney_north_shore new file mode 100644 index 0000000..0bb6dd4 --- /dev/null +++ b/util/scan/dvb-t/au-sydney_north_shore @@ -0,0 +1,12 @@ +# Australia / Sydney / North Shore (aka Artarmon/Gore Hill) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +# ABC +T 226500000 7MHz 3/4 NONE QAM64 8k 1/16 NONE +# Seven +T 177500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE +# Nine +T 191625000 7MHz 3/4 NONE QAM64 8k 1/16 NONE +# Ten +T 219500000 7MHz 3/4 NONE QAM64 8k 1/16 NONE +# SBS +T 571500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/au-unknown b/util/scan/dvb-t/au-unknown new file mode 100644 index 0000000..9f96d9e --- /dev/null +++ b/util/scan/dvb-t/au-unknown @@ -0,0 +1,3 @@ +# Australia ABC +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 226500000 7MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/de-Berlin b/util/scan/dvb-t/de-Berlin new file mode 100644 index 0000000..416bd11 --- /dev/null +++ b/util/scan/dvb-t/de-Berlin @@ -0,0 +1,5 @@ +# DVB-T Berlin +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 522000000 8MHz 2/3 NONE QAM16 8k 1/8 NONE     # ard / rbb +T 570000000 8MHz 2/3 NONE QAM16 8k 1/8 NONE     # zdf +T 658000000 8MHz 2/3 NONE QAM16 8k 1/8 NONE     # t-systems diff --git a/util/scan/dvb-t/es-Collserola b/util/scan/dvb-t/es-Collserola new file mode 100644 index 0000000..04ffe54 --- /dev/null +++ b/util/scan/dvb-t/es-Collserola @@ -0,0 +1,6 @@ +# DVB-T Collserola (Barcelona) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 650000000 8MHz 2/3 2/3 QAM64 8k 1/32 NONE     # C43: tvc +T 794000000 8MHz 2/3 NONE QAM64 8k 1/4 NONE     # C61: tve, t5, a3, c+ +T 834000000 8MHz 2/3 NONE QAM64 8k 1/4 NONE     # C66: veotv, nettv + diff --git a/util/scan/dvb-t/fi-Espoo b/util/scan/dvb-t/fi-Espoo new file mode 100644 index 0000000..dd0619b --- /dev/null +++ b/util/scan/dvb-t/fi-Espoo @@ -0,0 +1,3 @@ +# Espoo A-mux (Digita Finland) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 562000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/fi-Tampere b/util/scan/dvb-t/fi-Tampere new file mode 100644 index 0000000..7e59894 --- /dev/null +++ b/util/scan/dvb-t/fi-Tampere @@ -0,0 +1,6 @@ +# Tampere DVB-T (Digita Finland) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 578000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE +T 490000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE +T 770000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE + diff --git a/util/scan/dvb-t/fi-Turku b/util/scan/dvb-t/fi-Turku new file mode 100644 index 0000000..5f2d9b4 --- /dev/null +++ b/util/scan/dvb-t/fi-Turku @@ -0,0 +1,3 @@ +# Turku A-mux (Digita Finland) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 714000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/nl-AlphenaandenRijn b/util/scan/dvb-t/nl-AlphenaandenRijn new file mode 100644 index 0000000..f95d3a4 --- /dev/null +++ b/util/scan/dvb-t/nl-AlphenaandenRijn @@ -0,0 +1,7 @@ +# Digitenne (Alphen aan den Rijn, The Netherlands) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy         +T 474000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 578000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 722000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 762000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 818000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE diff --git a/util/scan/dvb-t/nl-Randstad b/util/scan/dvb-t/nl-Randstad new file mode 100644 index 0000000..da1a74d --- /dev/null +++ b/util/scan/dvb-t/nl-Randstad @@ -0,0 +1,7 @@ +# Digitenne (Randstad, The Netherlands) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy         +T 474000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 490000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 578000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 762000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE +T 818000000 8MHz 1/2 NONE QAM64 8k 1/4 NONE diff --git a/util/scan/dvb-t/se-Gavle b/util/scan/dvb-t/se-Gavle new file mode 100644 index 0000000..04fe333 --- /dev/null +++ b/util/scan/dvb-t/se-Gavle @@ -0,0 +1,6 @@ +# Gavle (Senda/Boxer Sweden) +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 674000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE +T 498000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE +T 562000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE +T 706000000 8MHz 2/3 NONE QAM64 8k 1/8 NONE diff --git a/util/scan/dvb-t/uk-BlackHill b/util/scan/dvb-t/uk-BlackHill new file mode 100644 index 0000000..17eae72 --- /dev/null +++ b/util/scan/dvb-t/uk-BlackHill @@ -0,0 +1,3 @@ +# uk BlackHill +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 634167000 8MHz 2/3 NONE QAM64 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-CrystalPalace b/util/scan/dvb-t/uk-CrystalPalace new file mode 100644 index 0000000..9586b25 --- /dev/null +++ b/util/scan/dvb-t/uk-CrystalPalace @@ -0,0 +1,3 @@ +# Crystal Palace +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 505833333 8MHz 3/4 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-Hannington b/util/scan/dvb-t/uk-Hannington new file mode 100644 index 0000000..0bbbfdd --- /dev/null +++ b/util/scan/dvb-t/uk-Hannington @@ -0,0 +1,3 @@ +# Hannington, North Hampshire +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 706000000 8MHz 3/4 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-Oxford b/util/scan/dvb-t/uk-Oxford new file mode 100644 index 0000000..035603e --- /dev/null +++ b/util/scan/dvb-t/uk-Oxford @@ -0,0 +1,3 @@ +# Oxford +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 578000000 8MHz 2/3 NONE QAM64 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-PontopPike b/util/scan/dvb-t/uk-PontopPike new file mode 100644 index 0000000..c24ba92 --- /dev/null +++ b/util/scan/dvb-t/uk-PontopPike @@ -0,0 +1,3 @@ +# Pontop Pike, UK +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 690000000 8MHz 1/2 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-Redruth b/util/scan/dvb-t/uk-Redruth new file mode 100644 index 0000000..84dcb9a --- /dev/null +++ b/util/scan/dvb-t/uk-Redruth @@ -0,0 +1,3 @@ +# Redruth, Cornwall +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 618000000 8MHz 3/4 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-Reigate b/util/scan/dvb-t/uk-Reigate new file mode 100644 index 0000000..add0d8b --- /dev/null +++ b/util/scan/dvb-t/uk-Reigate @@ -0,0 +1,3 @@ +# Reigate +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 554000000 8MHz 2/3 NONE QAM64 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-Rowridge b/util/scan/dvb-t/uk-Rowridge new file mode 100644 index 0000000..c0c72a0 --- /dev/null +++ b/util/scan/dvb-t/uk-Rowridge @@ -0,0 +1,3 @@ +# Rowridge, Isle of Wight +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 489833333 8MHz 3/4 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-SandyHeath b/util/scan/dvb-t/uk-SandyHeath new file mode 100644 index 0000000..05d0474 --- /dev/null +++ b/util/scan/dvb-t/uk-SandyHeath @@ -0,0 +1,3 @@ +# Sandy Heath +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 641833334 8MHz 2/3 NONE QAM64 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-Storeton b/util/scan/dvb-t/uk-Storeton new file mode 100644 index 0000000..5c93ee9 --- /dev/null +++ b/util/scan/dvb-t/uk-Storeton @@ -0,0 +1,3 @@ +# Storeton, Wirral +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 546167000 8MHz 3/4 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/dvb-t/uk-WinterHill b/util/scan/dvb-t/uk-WinterHill new file mode 100644 index 0000000..b000623 --- /dev/null +++ b/util/scan/dvb-t/uk-WinterHill @@ -0,0 +1,3 @@ +# Winter Hill, North-West England +# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy +T 754166670 8MHz 3/4 NONE QAM16 2k 1/32 NONE diff --git a/util/scan/list.h b/util/scan/list.h new file mode 100644 index 0000000..6032c22 --- /dev/null +++ b/util/scan/list.h @@ -0,0 +1,140 @@ +#ifndef _LIST_H +#define _LIST_H + + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { +	struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ +	struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ +	(ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries.  + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, +	struct list_head * prev, +	struct list_head * next) +{ +	next->prev = new; +	new->next = next; +	new->prev = prev; +	prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ +	__list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ +	__list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, +				  struct list_head * next) +{ +	next->prev = prev; +	prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ +	__list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ +	__list_del(entry->prev, entry->next); +	INIT_LIST_HEAD(entry);  +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ +	return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr:	the &struct list_head pointer. + * @type:	the type of the struct this is embedded in. + * @member:	the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ +	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each	-	iterate over a list + * @pos:	the &struct list_head to use as a loop counter. + * @head:	the head for your list. + */ +#define list_for_each(pos, head) \ +	for (pos = (head)->next; pos != (head); pos = pos->next) +        	 +/** + * list_for_each_safe	-	iterate over a list safe against removal of list entry + * @pos:	the &struct list_head to use as a loop counter. + * @n:		another &struct list_head to use as temporary storage + * @head:	the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ +	for (pos = (head)->next, n = pos->next; pos != (head); \ +		pos = n, n = pos->next) + +#endif diff --git a/util/scan/scan.c b/util/scan/scan.c new file mode 100644 index 0000000..30d77a5 --- /dev/null +++ b/util/scan/scan.c @@ -0,0 +1,1928 @@ +/** + *  Simple MPEG parser to achieve network/service information. + * + *  refered standards: + * + *    ETSI EN 300 468 + *    ETSI TR 101 211 + *    ETSI ETR 211 + *    ITU-T H.222.0 + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <assert.h> + +#include <linux/dvb/frontend.h> +#include <linux/dvb/dmx.h> + +#include "list.h" +#include "diseqc.h" +#include "dump-zap.h" +#include "dump-vdr.h" +#include "scan.h" +#include "lnb.h" + + +static char demux_devname[80]; + +static struct dvb_frontend_info fe_info = { +	.type = -1 +}; + +int verbosity = 2; + +static int long_timeout; +static int current_tp_only; +static int get_other_nits; +static int vdr_dump_provider; +static int vdr_dump_channum; +static int ca_select = 1; +static int serv_select = 7; +static int vdr_version = 2; +static struct lnb_types_st lnb_type; + +static enum fe_spectral_inversion spectral_inversion = INVERSION_AUTO; + +enum table_type { +	PAT, +	PMT, +	SDT, +	NIT +}; + +enum format { +        OUTPUT_ZAP, +        OUTPUT_VDR, +	OUTPUT_PIDS +}; +static enum format output_format = OUTPUT_ZAP; + + +enum polarisation { +	POLARISATION_HORIZONTAL     = 0x00, +	POLARISATION_VERTICAL       = 0x01, +	POLARISATION_CIRCULAR_LEFT  = 0x02, +	POLARISATION_CIRCULAR_RIGHT = 0x03 +}; + +enum running_mode { +	RM_NOT_RUNNING = 0x01, +	RM_STARTS_SOON = 0x02, +	RM_PAUSING     = 0x03, +	RM_RUNNING     = 0x04 +}; + +#define AUDIO_CHAN_MAX (32) +#define CA_SYSTEM_ID_MAX (16) + +struct service { +	struct list_head list; +	int transport_stream_id; +	int service_id; +	char *provider_name; +	char *service_name; +	uint16_t pmt_pid; +	uint16_t pcr_pid; +	uint16_t video_pid; +	uint16_t audio_pid[AUDIO_CHAN_MAX]; +	char audio_lang[AUDIO_CHAN_MAX][4]; +	int audio_num; +	uint16_t ca_id[CA_SYSTEM_ID_MAX]; +	int ca_num; +	uint16_t teletext_pid; +	uint16_t subtitling_pid; +	uint16_t ac3_pid; +	unsigned int type         : 8; +	unsigned int scrambled	  : 1; +	enum running_mode running; +	void *priv; +	int channel_num; +}; + +struct transponder { +	struct list_head list; +	struct list_head services; +	int network_id; +	int transport_stream_id; +	enum fe_type type; +	struct dvb_frontend_parameters param; +	enum polarisation polarisation;		/* only for DVB-S */ +	int orbital_pos;			/* only for DVB-S */ +	unsigned int we_flag		  : 1;	/* West/East Flag - only for DVB-S */ +	unsigned int scan_done		  : 1; +	unsigned int last_tuning_failed	  : 1; +	unsigned int other_frequency_flag : 1;	/* DVB-T */ +	int n_other_f; +	uint32_t *other_f;			/* DVB-T freqeuency-list descriptor */ +}; + + +struct section_buf { +	struct list_head list; +	const char *dmx_devname; +	unsigned int run_once  : 1; +	unsigned int segmented : 1;	/* segmented by table_id_ext */ +	int fd; +	int pid; +	int table_id; +	int table_id_ext; +	int section_version_number; +	uint8_t section_done[32]; +	int sectionfilter_done; +	unsigned char buf[1024]; +	time_t timeout; +	time_t start_time; +	time_t running_time; +	struct section_buf *next_seg;	/* this is used to handle +					 * segmented tables (like NIT-other) +					 */ +}; + +static LIST_HEAD(scanned_transponders); +static LIST_HEAD(new_transponders); +static struct transponder *current_tp; + + +static void dump_dvb_parameters (FILE *f, struct transponder *p); + +static void setup_filter (struct section_buf* s, const char *dmx_devname, +		          int pid, int tid, int run_once, int segmented, int timeout); +static void add_filter (struct section_buf *s); + + +/* According to the DVB standards, the combination of network_id and + * transport_stream_id should be unique, but in real life the satellite + * operators and broadcasters don't care enough to coordinate + * the numbering. Thus we identify TPs by frequency (scan handles only + * one satellite at a time). Further complication: Different NITs on + * one satellite sometimes list the same TP with slightly different + * frequencies, so we have to search within some bandwidth. + */ +static struct transponder *alloc_transponder(uint32_t frequency) +{ +	struct transponder *tp = calloc(1, sizeof(*tp)); + +	tp->param.frequency = frequency; +	INIT_LIST_HEAD(&tp->list); +	INIT_LIST_HEAD(&tp->services); +	list_add_tail(&tp->list, &new_transponders); +	return tp; +} + +static int is_same_transponder(uint32_t f1, uint32_t f2) +{ +	uint32_t diff; +	if (f1 == f2) +		return 1; +	diff = (f1 > f2) ? (f1 - f2) : (f2 - f1); +	//FIXME: use symbolrate etc. to estimate bandwidth +	if (diff < 2000) { +		debug("f1 = %u is same TP as f2 = %u\n", f1, f2); +		return 1; +	} +	return 0; +} + +static struct transponder *find_transponder(uint32_t frequency) +{ +	struct list_head *pos; +	struct transponder *tp; + +	list_for_each(pos, &scanned_transponders) { +		tp = list_entry(pos, struct transponder, list); +		if (is_same_transponder(tp->param.frequency, frequency)) +			return tp; +	} +	list_for_each(pos, &new_transponders) { +		tp = list_entry(pos, struct transponder, list); +		if (is_same_transponder(tp->param.frequency, frequency)) +			return tp; +	} +	return NULL; +} + +static void copy_transponder(struct transponder *d, struct transponder *s) +{ +	d->network_id = s->network_id; +	d->transport_stream_id = s->transport_stream_id; +	d->type = s->type; +	memcpy(&d->param, &s->param, sizeof(d->param)); +	d->polarisation = s->polarisation; +	d->orbital_pos = s->orbital_pos; +	d->we_flag = s->we_flag; +	d->scan_done = s->scan_done; +	d->last_tuning_failed = s->last_tuning_failed; +	d->other_frequency_flag = s->other_frequency_flag; +	d->n_other_f = s->n_other_f; +	if (d->n_other_f) { +		d->other_f = calloc(d->n_other_f, sizeof(uint32_t)); +		memcpy(d->other_f, s->other_f, d->n_other_f * sizeof(uint32_t)); +	} +	else +		d->other_f = NULL; +} + +/* service_ids are guaranteed to be unique within one TP + * (the DVB standards say theay should be unique within one + * network, but in real life...) + */ +static struct service *alloc_service(struct transponder *tp, int service_id) +{ +	struct service *s = calloc(1, sizeof(*s)); +	INIT_LIST_HEAD(&s->list); +	s->service_id = service_id; +	list_add_tail(&s->list, &tp->services); +	return s; +} + +static struct service *find_service(struct transponder *tp, int service_id) +{ +	struct list_head *pos; +	struct service *s; + +	list_for_each(pos, &tp->services) { +		s = list_entry(pos, struct service, list); +		if (s->service_id == service_id) +			return s; +	} +	return NULL; +} + + +static void parse_ca_identifier_descriptor (const unsigned char *buf, +				     struct service *s) +{ +	unsigned char len = buf [1]; +	int i; + +	buf += 2; + +	if (len > sizeof(s->ca_id)) { +		len = sizeof(s->ca_id); +		warning("too many CA system ids\n"); +	} +	memcpy(s->ca_id, buf, len); +	for (i = 0; i < len / sizeof(s->ca_id[0]); i++) +		moreverbose("  CA ID 0x%04x\n", s->ca_id[i]); +} + + +static void parse_iso639_language_descriptor (const unsigned char *buf, struct service *s) +{ +	unsigned char len = buf [1]; + +	buf += 2; + +	if (len >= 4) { +		debug("    LANG=%.3s %d\n", buf, buf[3]); +		memcpy(s->audio_lang[s->audio_num], buf, 3); +#if 0 +		/* seems like the audio_type is wrong all over the place */ +		//if (buf[3] == 0) -> normal +		if (buf[3] == 1) +			s->audio_lang[s->audio_num][3] = '!'; /* clean effects (no language) */ +		else if (buf[3] == 2) +			s->audio_lang[s->audio_num][3] = '?'; /* for the hearing impaired */ +		else if (buf[3] == 3) +			s->audio_lang[s->audio_num][3] = '+'; /* visually impaired commentary */ +#endif +	} +} + +static void parse_network_name_descriptor (const unsigned char *buf, void *dummy) +{ +	unsigned char len = buf [1]; + +	info("Network Name '%.*s'\n", len, buf + 2); +} + +static void parse_terrestrial_uk_channel_number (const unsigned char *buf, void *dummy) +{ +	int i, n, channel_num, service_id; +	struct list_head *p1, *p2; +	struct transponder *t; +	struct service *s; + +	// 32 bits per record +	n = buf[1] / 4; +	if (n < 1) +		return; + +	// desc id, desc len, (service id, service number) +	buf += 2; +	for (i = 0; i < n; i++) { +		service_id = (buf[0]<<8)|(buf[1]&0xff); +		channel_num = (buf[2]&0x03<<8)|(buf[3]&0xff); +		debug("Service ID 0x%x has channel number %d ", service_id, channel_num); +		list_for_each(p1, &scanned_transponders) { +			t = list_entry(p1, struct transponder, list); +			list_for_each(p2, &t->services) { +				s = list_entry(p2, struct service, list); +				if (s->service_id == service_id) +					s->channel_num = channel_num; +			} +		} +		buf += 4; +	} +} + + +static long bcd32_to_cpu (const int b0, const int b1, const int b2, const int b3) +{ +	return ((b0 >> 4) & 0x0f) * 10000000 + (b0 & 0x0f) * 1000000 + +	       ((b1 >> 4) & 0x0f) * 100000   + (b1 & 0x0f) * 10000 + +	       ((b2 >> 4) & 0x0f) * 1000     + (b2 & 0x0f) * 100 + +	       ((b3 >> 4) & 0x0f) * 10       + (b3 & 0x0f); +} + + +static const fe_code_rate_t fec_tab [8] = { +	FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4, +	FEC_5_6, FEC_7_8, FEC_NONE, FEC_NONE +}; + + +static const fe_modulation_t qam_tab [6] = { +	QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256 +}; + + +static void parse_cable_delivery_system_descriptor (const unsigned char *buf, +					     struct transponder *t) +{ +	if (!t) { +		warning("cable_delivery_system_descriptor outside transport stream definition (ignored)\n"); +		return; +	} +	t->type = FE_QAM; + +	t->param.frequency = bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]); +	t->param.frequency *= 100; +	t->param.u.qam.fec_inner = fec_tab[buf[12] & 0x07]; +	t->param.u.qam.symbol_rate = 10 * bcd32_to_cpu (buf[9], +							buf[10], +							buf[11], +							buf[12] & 0xf0); +	if ((buf[8] & 0x0f) > 5) +		t->param.u.qam.modulation = QAM_AUTO; +	else +		t->param.u.qam.modulation = qam_tab[buf[8] & 0x0f]; +	t->param.inversion = spectral_inversion; + +	if (verbosity >= 5) { +		debug("0x%#04x/0x%#04x ", t->network_id, t->transport_stream_id); +		dump_dvb_parameters (stderr, t); +		if (t->scan_done) +			dprintf(5, " (done)"); +		if (t->last_tuning_failed) +			dprintf(5, " (tuning failed)"); +		dprintf(5, "\n"); +	} +} + +static void parse_satellite_delivery_system_descriptor (const unsigned char *buf, +						 struct transponder *t) +{ +	if (!t) { +		warning("satellite_delivery_system_descriptor outside transport stream definition (ignored)\n"); +		return; +	} +	t->type = FE_QPSK; +	t->param.frequency = 10 * bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]); +	t->param.u.qpsk.fec_inner = fec_tab[buf[12] & 0x07]; +	t->param.u.qpsk.symbol_rate = 10 * bcd32_to_cpu (buf[9], +							 buf[10], +							 buf[11], +							 buf[12] & 0xf0); + +	t->polarisation = (buf[8] >> 5) & 0x03; +	t->param.inversion = spectral_inversion; + +	t->orbital_pos = bcd32_to_cpu (0x00, 0x00, buf[6], buf[7]); +	t->we_flag = buf[8] >> 7; + +	if (verbosity >= 5) { +		debug("0x%#04x/0x%#04x ", t->network_id, t->transport_stream_id); +		dump_dvb_parameters (stderr, t); +		if (t->scan_done) +			dprintf(5, " (done)"); +		if (t->last_tuning_failed) +			dprintf(5, " (tuning failed)"); +		dprintf(5, "\n"); +	} +} + + +static void parse_terrestrial_delivery_system_descriptor (const unsigned char *buf, +						   struct transponder *t) +{ +	static const fe_modulation_t m_tab [] = { QPSK, QAM_16, QAM_64, QAM_AUTO }; +	static const fe_code_rate_t ofec_tab [8] = { FEC_1_2, FEC_2_3, FEC_3_4, +					       FEC_5_6, FEC_7_8 }; +	struct dvb_ofdm_parameters *o; + +	if (!t) { +		warning("terrestrial_delivery_system_descriptor outside transport stream definition (ignored)\n"); +		return; +	} +	o = &t->param.u.ofdm; +	t->type = FE_OFDM; + +	t->param.frequency = (buf[2] << 24) | (buf[3] << 16); +	t->param.frequency |= (buf[4] << 8) | buf[5]; +	t->param.frequency *= 10; +	t->param.inversion = spectral_inversion; + +	o->bandwidth = BANDWIDTH_8_MHZ + ((buf[6] >> 5) & 0x3); +	o->constellation = m_tab[(buf[7] >> 6) & 0x3]; +	o->hierarchy_information = HIERARCHY_NONE + ((buf[7] >> 3) & 0x3); + +	if ((buf[7] & 0x7) > 4) +		o->code_rate_HP = FEC_AUTO; +	else +		o->code_rate_HP = ofec_tab [buf[7] & 0x7]; + +	if (((buf[8] >> 5) & 0x7) > 4) +		o->code_rate_LP = FEC_AUTO; +	else +		o->code_rate_LP = ofec_tab [(buf[8] >> 5) & 0x7]; + +	o->guard_interval = GUARD_INTERVAL_1_32 + ((buf[8] >> 3) & 0x3); + +	o->transmission_mode = (buf[8] & 0x2) ? +			       TRANSMISSION_MODE_8K : +			       TRANSMISSION_MODE_2K; + +	t->other_frequency_flag = (buf[8] & 0x01); + +	if (verbosity >= 5) { +		debug("0x%#04x/0x%#04x ", t->network_id, t->transport_stream_id); +		dump_dvb_parameters (stderr, t); +		if (t->scan_done) +			dprintf(5, " (done)"); +		if (t->last_tuning_failed) +			dprintf(5, " (tuning failed)"); +		dprintf(5, "\n"); +	} +} + +static void parse_frequency_list_descriptor (const unsigned char *buf, +				      struct transponder *t) +{ +	int n, i; +	typeof(*t->other_f) f; + +	if (!t) { +		warning("frequency_list_descriptor outside transport stream definition (ignored)\n"); +		return; +	} +	if (t->other_f) +		return; + +	n = (buf[1] - 1) / 4; +	if (n < 1 || (buf[2] & 0x03) != 3) +		return; + +	t->other_f = calloc(n, sizeof(*t->other_f)); +	t->n_other_f = n; +	buf += 3; +	for (i = 0; i < n; i++) { +		f = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +		t->other_f[i] = f * 10; +		buf += 4; +	} +} + +static void parse_service_descriptor (const unsigned char *buf, struct service *s) +{ +	unsigned char len; +	unsigned char *src, *dest; + +	s->type = buf[2]; + +	buf += 3; +	len = *buf; +	buf++; + +	if (s->provider_name) +		free (s->provider_name); + +	s->provider_name = malloc (len + 1); +	memcpy (s->provider_name, buf, len); +	s->provider_name[len] = '\0'; + +	/* remove control characters (FIXME: handle short/long name) */ +	/* FIXME: handle character set correctly (e.g. via iconv) +	 * c.f. EN 300 468 annex A */ +	for (src = dest = s->provider_name; *src; src++) +		if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f)) +			*dest++ = *src; +	*dest = '\0'; +	if (!s->provider_name[0]) { +		/* zap zero length names */ +		free (s->provider_name); +		s->provider_name = 0; +	} + +	if (s->service_name) +		free (s->service_name); + +	buf += len; +	len = *buf; +	buf++; + +	s->service_name = malloc (len + 1); +	memcpy (s->service_name, buf, len); +	s->service_name[len] = '\0'; + +	/* remove control characters (FIXME: handle short/long name) */ +	/* FIXME: handle character set correctly (e.g. via iconv) +	 * c.f. EN 300 468 annex A */ +	for (src = dest = s->service_name; *src; src++) +		if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f)) +			*dest++ = *src; +	*dest = '\0'; +	if (!s->service_name[0]) { +		/* zap zero length names */ +		free (s->service_name); +		s->service_name = 0; +	} + +	info("0x%04x 0x%04x: pmt_pid 0x%04x %s -- %s (%s%s)\n", +	    s->transport_stream_id, +	    s->service_id, +	    s->pmt_pid, +	    s->provider_name, s->service_name, +	    s->running == RM_NOT_RUNNING ? "not running" : +	    s->running == RM_STARTS_SOON ? "starts soon" : +	    s->running == RM_PAUSING     ? "pausing" : +	    s->running == RM_RUNNING     ? "running" : "???", +	    s->scrambled ? ", scrambled" : ""); +} + +static int find_descriptor(uint8_t tag, const unsigned char *buf, +		int descriptors_loop_len, +		const unsigned char **desc, int *desc_len) +{ +	while (descriptors_loop_len > 0) { +		unsigned char descriptor_tag = buf[0]; +		unsigned char descriptor_len = buf[1] + 2; + +		if (!descriptor_len) { +			warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag); +			break; +		} + +		if (tag == descriptor_tag) { +			if (desc) +				*desc = buf; +			if (desc_len) +				*desc_len = descriptor_len; +			return 1; +		} + +		buf += descriptor_len; +		descriptors_loop_len -= descriptor_len; +	} +	return 0; +} + +static void parse_descriptors(enum table_type t, const unsigned char *buf, +			      int descriptors_loop_len, void *data) +{ +	while (descriptors_loop_len > 0) { +		unsigned char descriptor_tag = buf[0]; +		unsigned char descriptor_len = buf[1] + 2; + +		if (!descriptor_len) { +			warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag); +			break; +		} + +		switch (descriptor_tag) { +		case 0x0a: +			if (t == PMT) +				parse_iso639_language_descriptor (buf, data); +			break; + +		case 0x40: +			if (t == NIT) +				parse_network_name_descriptor (buf, data); +			break; + +		case 0x43: +			if (t == NIT) +				parse_satellite_delivery_system_descriptor (buf, data); +			break; + +		case 0x44: +			if (t == NIT) +				parse_cable_delivery_system_descriptor (buf, data); +			break; + +		case 0x48: +			if (t == SDT) +				parse_service_descriptor (buf, data); +			break; + +		case 0x53: +			if (t == SDT) +				parse_ca_identifier_descriptor (buf, data); +			break; + +		case 0x5a: +			if (t == NIT) +				parse_terrestrial_delivery_system_descriptor (buf, data); +			break; + +		case 0x62: +			if (t == NIT) +				parse_frequency_list_descriptor (buf, data); +			break; + +		case 0x83: +			/* 0x83 is in the privately defined range of descriptor tags, +			 * so we parse this only if the user says so to avoid +			 * problems when 0x83 is something entirely different... */ +			if (t == NIT && vdr_dump_channum) +				parse_terrestrial_uk_channel_number (buf, data); +			break; + +		default: +			verbosedebug("skip descriptor 0x%02x\n", descriptor_tag); +		}; + +		buf += descriptor_len; +		descriptors_loop_len -= descriptor_len; +	} +} + + +static void parse_pat(const unsigned char *buf, int section_length, +		      int transport_stream_id) +{ +	while (section_length > 0) { +		struct service *s; +		int service_id = (buf[0] << 8) | buf[1]; + +		if (service_id == 0) { +			buf += 4;		/*  skip nit pid entry... */ +			section_length -= 4; +			continue; +		} +		/* SDT might have been parsed first... */ +		s = find_service(current_tp, service_id); +		if (!s) +			s = alloc_service(current_tp, service_id); +		s->pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3]; +		if (!s->priv && s->pmt_pid) { +			s->priv = malloc(sizeof(struct section_buf)); +			setup_filter(s->priv, demux_devname, +				     s->pmt_pid, 0x02, 1, 0, 5); + +			add_filter (s->priv); +		} + +		buf += 4; +		section_length -= 4; +	}; +} + + +static void parse_pmt (const unsigned char *buf, int section_length, int service_id) +{ +	int program_info_len; +	struct service *s; +        char msg_buf[14 * AUDIO_CHAN_MAX + 1]; +        char *tmp; +        int i; + +	s = find_service (current_tp, service_id); +	if (!s) { +		error("PMT for serivce_id 0x%04x was not in PAT\n", service_id); +		return; +	} + +	s->pcr_pid = ((buf[0] & 0x1f) << 8) | buf[1]; + +	program_info_len = ((buf[2] & 0x0f) << 8) | buf[3]; + +	buf += program_info_len + 4; +	section_length -= program_info_len + 4; + +	while (section_length > 0) { +		int ES_info_len = ((buf[3] & 0x0f) << 8) | buf[4]; +		int elementary_pid = ((buf[1] & 0x1f) << 8) | buf[2]; + +		switch (buf[0]) { +		case 0x01: +		case 0x02: +			moreverbose("  VIDEO     : PID 0x%04x\n", elementary_pid); +			if (s->video_pid == 0) +				s->video_pid = elementary_pid; +			break; +		case 0x03: +		case 0x04: +			moreverbose("  AUDIO     : PID 0x%04x\n", elementary_pid); +			if (s->audio_num < AUDIO_CHAN_MAX) { +				s->audio_pid[s->audio_num] = elementary_pid; +				parse_descriptors (PMT, buf + 5, ES_info_len, s); +				s->audio_num++; +			} +			else +				warning("more than %i audio channels, truncating\n", +				     AUDIO_CHAN_MAX); +			break; +		case 0x06: +			if (find_descriptor(0x56, buf + 5, ES_info_len, NULL, NULL)) { +				moreverbose("  TELETEXT  : PID 0x%04x\n", elementary_pid); +				s->teletext_pid = elementary_pid; +				break; +			} +			else if (find_descriptor(0x59, buf + 5, ES_info_len, NULL, NULL)) { +				/* Note: The subtitling descriptor can also signal +				 * teletext subtitling, but then the teletext descriptor +				 * will also be present; so we can be quite confident +				 * that we catch DVB subtitling streams only here, w/o +				 * parsing the descriptor. */ +				moreverbose("  SUBTITLING: PID 0x%04x\n", elementary_pid); +				s->subtitling_pid = elementary_pid; +				break; +			} +			else if (find_descriptor(0x6a, buf + 5, ES_info_len, NULL, NULL)) { +				moreverbose("  AC3       : PID 0x%04x\n", elementary_pid); +				s->ac3_pid = elementary_pid; +				break; +			} +			/* fall through */ +		default: +			moreverbose("  OTHER     : PID 0x%04x TYPE 0x%02x\n", elementary_pid, buf[0]); +		}; + +		buf += ES_info_len + 5; +		section_length -= ES_info_len + 5; +	}; + + +        tmp = msg_buf; +        tmp += sprintf(tmp, "0x%04x (%.4s)", s->audio_pid[0], s->audio_lang[0]); + +	if (s->audio_num > AUDIO_CHAN_MAX) { +		warning("more than %i audio channels: %i, truncating to %i\n", +		      AUDIO_CHAN_MAX, s->audio_num, AUDIO_CHAN_MAX); +		s->audio_num = AUDIO_CHAN_MAX; +	} + +        for (i=1; i<s->audio_num; i++) +                tmp += sprintf(tmp, ", 0x%04x (%.4s)", s->audio_pid[i], s->audio_lang[i]); + +        debug("0x%04x 0x%04x: %s -- %s, pmt_pid 0x%04x, vpid 0x%04x, apid %s\n", +	    s->transport_stream_id, +	    s->service_id, +	    s->provider_name, s->service_name, +	    s->pmt_pid, s->video_pid, msg_buf); +} + + +static void parse_nit (const unsigned char *buf, int section_length, int network_id) +{ +	int descriptors_loop_len = ((buf[0] & 0x0f) << 8) | buf[1]; + +	if (section_length < descriptors_loop_len + 4) +	{ +		warning("section too short: network_id == 0x%04x, section_length == %i, " +		     "descriptors_loop_len == %i\n", +		     network_id, section_length, descriptors_loop_len); +		return; +	} + +	parse_descriptors (NIT, buf + 2, descriptors_loop_len, NULL); + +	section_length -= descriptors_loop_len + 4; +	buf += descriptors_loop_len + 4; + +	while (section_length > 6) { +		int transport_stream_id = (buf[0] << 8) | buf[1]; +		struct transponder *t, tn; + +		descriptors_loop_len = ((buf[4] & 0x0f) << 8) | buf[5]; + +		if (section_length < descriptors_loop_len + 4) +		{ +			warning("section too short: transport_stream_id == 0x%04x, " +			     "section_length == %i, descriptors_loop_len == %i\n", +			     transport_stream_id, section_length, +			     descriptors_loop_len); +			break; +		} + +		debug("transport_stream_id 0x%04x\n", transport_stream_id); + +		memset(&tn, 0, sizeof(tn)); +		tn.type = -1; +		tn.network_id = network_id; +		tn.transport_stream_id = transport_stream_id; + +		parse_descriptors (NIT, buf + 6, descriptors_loop_len, &tn); + +		if (tn.type == fe_info.type) { +			/* only add if develivery_descriptor matches FE type */ +			t = find_transponder(tn.param.frequency); +			if (!t) +				t = alloc_transponder(tn.param.frequency); +			copy_transponder(t, &tn); +		} + +		section_length -= descriptors_loop_len + 6; +		buf += descriptors_loop_len + 6; +	}; +} + + +static void parse_sdt (const unsigned char *buf, int section_length, +		int transport_stream_id) +{ +	buf += 3;	       /*  skip original network id + reserved field */ + +	while (section_length > 4) { +		int service_id = (buf[0] << 8) | buf[1]; +		int descriptors_loop_len = ((buf[3] & 0x0f) << 8) | buf[4]; +		struct service *s; + +		if (section_length < descriptors_loop_len || !descriptors_loop_len) +		{ +			warning("section too short: service_id == 0x%02x, section_length == %i, " +			     "descriptors_loop_len == %i\n", +			     service_id, section_length, +			     descriptors_loop_len); +			break; +		} + +		s = find_service(current_tp, service_id); +		if (!s) +			/* maybe PAT has not yet been parsed... */ +			s = alloc_service(current_tp, service_id); + +		s->running = (buf[3] >> 5) & 0x7; +		s->scrambled = (buf[3] >> 4) & 1; + +		parse_descriptors (SDT, buf + 5, descriptors_loop_len, s); + +		section_length -= descriptors_loop_len + 5; +		buf += descriptors_loop_len + 5; +	}; +} + + +static int get_bit (uint8_t *bitfield, int bit) +{ +	return (bitfield[bit/8] >> (bit % 8)) & 1; +} + +static void set_bit (uint8_t *bitfield, int bit) +{ +	bitfield[bit/8] |= 1 << (bit % 8); +} + + +/** + *   returns 0 when more sections are expected + *	   1 when all sections are read on this pid + *	   -1 on invalid table id + */ +static int parse_section (struct section_buf *s) +{ +	const unsigned char *buf = s->buf; +	int table_id; +	int section_length; +	int table_id_ext; +	int section_version_number; +	int section_number; +	int last_section_number; +	int i; + +	table_id = buf[0]; + +	if (s->table_id != table_id) +		return -1; + +	section_length = (((buf[1] & 0x0f) << 8) | buf[2]) - 11; + +	table_id_ext = (buf[3] << 8) | buf[4]; +	section_version_number = (buf[5] >> 1) & 0x1f; +	section_number = buf[6]; +	last_section_number = buf[7]; + +	if (s->segmented && s->table_id_ext != -1 && s->table_id_ext != table_id_ext) { +		/* find or allocate actual section_buf matching table_id_ext */ +		while (s->next_seg) { +			s = s->next_seg; +			if (s->table_id_ext == table_id_ext) +				break; +		} +		if (s->table_id_ext != table_id_ext) { +			assert(s->next_seg == NULL); +			s->next_seg = calloc(1, sizeof(struct section_buf)); +			s->next_seg->segmented = s->segmented; +			s->next_seg->run_once = s->run_once; +			s->next_seg->timeout = s->timeout; +			s = s->next_seg; +			s->table_id = table_id; +			s->table_id_ext = table_id_ext; +			s->section_version_number = section_version_number; +		} +	} + +	if (s->section_version_number != section_version_number || +			s->table_id_ext != table_id_ext) { +		struct section_buf *next_seg = s->next_seg; + +		if (s->section_version_number != -1 && s->table_id_ext != -1) +			debug("section version_number or table_id_ext changed " +				"%d -> %d / %04x -> %04x\n", +				s->section_version_number, section_version_number, +				s->table_id_ext, table_id_ext); +		s->table_id_ext = table_id_ext; +		s->section_version_number = section_version_number; +		s->sectionfilter_done = 0; +		memset (s->section_done, 0, sizeof(s->section_done)); +		s->next_seg = next_seg; +	} + +	buf += 8; + +	if (!get_bit(s->section_done, section_number)) { +		set_bit (s->section_done, section_number); + +		debug("pid 0x%02x tid 0x%02x table_id_ext 0x%04x, " +		    "%i/%i (version %i)\n", +		    s->pid, table_id, table_id_ext, section_number, +		    last_section_number, section_version_number); + +		switch (table_id) { +		case 0x00: +			verbose("PAT\n"); +			parse_pat (buf, section_length, table_id_ext); +			break; + +		case 0x02: +			verbose("PMT 0x%04x for service 0x%04x\n", s->pid, table_id_ext); +			parse_pmt (buf, section_length, table_id_ext); +			break; + +		case 0x41: +			verbose("////////////////////////////////////////////// NIT other\n"); +		case 0x40: +			verbose("NIT (%s TS)\n", table_id == 0x40 ? "actual":"other"); +			parse_nit (buf, section_length, table_id_ext); +			break; + +		case 0x42: +		case 0x46: +			verbose("SDT (%s TS)\n", table_id == 0x42 ? "actual":"other"); +			parse_sdt (buf, section_length, table_id_ext); +			break; + +		default: +			; +		}; + +		for (i = 0; i <= last_section_number; i++) +			if (get_bit (s->section_done, i) == 0) +				break; + +		if (i > last_section_number) +			s->sectionfilter_done = 1; +	} + +	if (s->segmented) { +		/* always wait for timeout; this is because we don't now how +		 * many segments there are +		 */ +		return 0; +	} +	else if (s->sectionfilter_done) +		return 1; + +	return 0; +} + + +static int read_sections (struct section_buf *s) +{ +	int section_length, count; + +	if (s->sectionfilter_done && !s->segmented) +		return 1; + +	/* the section filter API guarantess that we get one full section +	 * per read(), provided that the buffer is large enough (it is) +	 */ +	if (((count = read (s->fd, s->buf, sizeof(s->buf))) < 0) && errno == EOVERFLOW) +		count = read (s->fd, s->buf, sizeof(s->buf)); +	if (count < 0) { +		errorn("read_sections: read error"); +		return -1; +	} + +	if (count < 4) +		return -1; + +	section_length = ((s->buf[1] & 0x0f) << 8) | s->buf[2]; + +	if (count != section_length + 3) +		return -1; + +	if (parse_section(s) == 1) +		return 1; + +	return 0; +} + + +static LIST_HEAD(running_filters); +static LIST_HEAD(waiting_filters); +static int n_running; +#define MAX_RUNNING 32 +static struct pollfd poll_fds[MAX_RUNNING]; +static struct section_buf* poll_section_bufs[MAX_RUNNING]; + + +static void setup_filter (struct section_buf* s, const char *dmx_devname, +			  int pid, int tid, int run_once, int segmented, int timeout) +{ +	memset (s, 0, sizeof(struct section_buf)); + +	s->fd = -1; +	s->dmx_devname = dmx_devname; +	s->pid = pid; +	s->table_id = tid; + +	s->run_once = run_once; +	s->segmented = segmented; + +	if (long_timeout) +		s->timeout = 5 * timeout; +	else +		s->timeout = timeout; + +	s->table_id_ext = -1; +	s->section_version_number = -1; + +	INIT_LIST_HEAD (&s->list); +} + +static void update_poll_fds(void) +{ +	struct list_head *p; +	struct section_buf* s; +	int i; + +	memset(poll_section_bufs, 0, sizeof(poll_section_bufs)); +	for (i = 0; i < MAX_RUNNING; i++) +		poll_fds[i].fd = -1; +	i = 0; +	list_for_each (p, &running_filters) { +		if (i >= MAX_RUNNING) +			fatal("too many poll_fds\n"); +		s = list_entry (p, struct section_buf, list); +		if (s->fd == -1) +			fatal("s->fd == -1 on running_filters\n"); +		verbosedebug("poll fd %d\n", s->fd); +		poll_fds[i].fd = s->fd; +		poll_fds[i].events = POLLIN; +		poll_fds[i].revents = 0; +		poll_section_bufs[i] = s; +		i++; +	} +	if (i != n_running) +		fatal("n_running is hosed\n"); +} + +static int start_filter (struct section_buf* s) +{ +	struct dmx_sct_filter_params f; + +	if (n_running >= MAX_RUNNING) +		goto err0; +	if ((s->fd = open (s->dmx_devname, O_RDWR | O_NONBLOCK)) < 0) +		goto err0; + +	verbosedebug("start filter pid 0x%04x table_id 0x%02x\n", s->pid, s->table_id); + +	memset(&f, 0, sizeof(f)); + +	f.pid = (uint16_t) s->pid; + +	if (s->table_id < 0x100 && s->table_id > 0) { +		f.filter.filter[0] = (uint8_t) s->table_id; +		f.filter.mask[0]   = 0xff; +	} + +	f.timeout = 0; +	f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; + +	if (ioctl(s->fd, DMX_SET_FILTER, &f) == -1) { +		errorn ("ioctl DMX_SET_FILTER failed"); +		goto err1; +	} + +	s->sectionfilter_done = 0; +	time(&s->start_time); + +	list_del_init (&s->list);  /* might be in waiting filter list */ +	list_add (&s->list, &running_filters); + +	n_running++; +	update_poll_fds(); + +	return 0; + +err1: +	ioctl (s->fd, DMX_STOP); +	close (s->fd); +err0: +	return -1; +} + + +static void stop_filter (struct section_buf *s) +{ +	verbosedebug("stop filter pid 0x%04x\n", s->pid); +	ioctl (s->fd, DMX_STOP); +	close (s->fd); +	s->fd = -1; +	list_del (&s->list); +	s->running_time += time(NULL) - s->start_time; + +	n_running--; +	update_poll_fds(); +} + + +static void add_filter (struct section_buf *s) +{ +	verbosedebug("add filter pid 0x%04x\n", s->pid); +	if (start_filter (s)) +		list_add_tail (&s->list, &waiting_filters); +} + + +static void remove_filter (struct section_buf *s) +{ +	verbosedebug("remove filter pid 0x%04x\n", s->pid); +	stop_filter (s); + +	while (!list_empty(&waiting_filters)) { +		struct list_head *next = waiting_filters.next; +		s = list_entry (next, struct section_buf, list); +		if (start_filter (s)) +			break; +	}; +} + + +static void read_filters (void) +{ +	struct section_buf *s; +	int i, n, done; + +	n = poll(poll_fds, n_running, 1000); +	if (n == -1) +		errorn("poll"); + +	for (i = 0; i < n_running; i++) { +		s = poll_section_bufs[i]; +		if (!s) +			fatal("poll_section_bufs[%d] is NULL\n", i); +		if (poll_fds[i].revents) +			done = read_sections (s) == 1; +		else +			done = 0; /* timeout */ +		if (done || time(NULL) > s->start_time + s->timeout) { +			if (s->run_once) { +				if (done) +					verbosedebug("filter done pid 0x%04x\n", s->pid); +				else +					warning("filter timeout pid 0x%04x\n", s->pid); +				remove_filter (s); +			} +		} +	} +} + + +static int mem_is_zero (const void *mem, int size) +{ +	const char *p = mem; +	unsigned long i; + +	for (i=0; i<size; i++) { +		if (p[i] != 0x00) +			return 0; +	} + +	return 1; +} + + +static int switch_pos = 0; + +static int __tune_to_transponder (int frontend_fd, struct transponder *t) +{ +	struct dvb_frontend_parameters p; +	fe_status_t s; +	int i; + +	current_tp = t; + +	if (mem_is_zero (&t->param, sizeof(struct dvb_frontend_parameters))) +		return -1; + +	memcpy (&p, &t->param, sizeof(struct dvb_frontend_parameters)); + +	if (verbosity >= 1) { +		dprintf(1, ">>> tune to: "); +		dump_dvb_parameters (stderr, t); +		if (t->last_tuning_failed) +			dprintf(1, " (tuning failed)"); +		dprintf(1, "\n"); +	} + +	if (t->type == FE_QPSK) { +		int hiband = 0; + +		if (lnb_type.switch_val && lnb_type.high_val && +			p.frequency >= lnb_type.switch_val) +			hiband = 1; + +		setup_switch (frontend_fd, +			      switch_pos, +			      t->polarisation == POLARISATION_VERTICAL ? 0 : 1, +			      hiband); +		usleep(50000); +		if (hiband) +			p.frequency = abs(p.frequency - lnb_type.high_val); +		else +			p.frequency = abs(p.frequency - lnb_type.low_val); +	} + +	if (ioctl(frontend_fd, FE_SET_FRONTEND, &p) == -1) { +		errorn("Setting frontend parameters failed"); +		return -1; +	} + +	for (i = 0; i < 10; i++) { +		usleep (200000); + +		if (ioctl(frontend_fd, FE_READ_STATUS, &s) == -1) { +			errorn("FE_READ_STATUS failed"); +			return -1; +		} + +		verbose(">>> tuning status == 0x%02x\n", s); + +		if (s & FE_HAS_LOCK) { +			t->last_tuning_failed = 0; +			return 0; +		} +	} + +	warning(">>> tuning failed!!!\n"); + +	t->last_tuning_failed = 1; + +	return -1; +} + +static int tune_to_transponder (int frontend_fd, struct transponder *t) +{ +	/* move TP from "new" to "scanned" list */ +	list_del_init(&t->list); +	list_add_tail(&t->list, &scanned_transponders); +	t->scan_done = 1; + +	if (t->type != fe_info.type) { +		/* ignore cable descriptors in sat NIT and vice versa */ +		t->last_tuning_failed = 1; +		return -1; +	} + +	if (__tune_to_transponder (frontend_fd, t) == 0) +		return 0; + +	return __tune_to_transponder (frontend_fd, t); +} + + +static int tune_to_next_transponder (int frontend_fd) +{ +	struct list_head *pos, *tmp; +	struct transponder *t; + +	list_for_each_safe(pos, tmp, &new_transponders) { +		t = list_entry (pos, struct transponder, list); +retry: +		if (tune_to_transponder (frontend_fd, t) == 0) +			return 0; +		if (t->other_frequency_flag && +				t->other_f && +				t->n_other_f) { +			t->param.frequency = t->other_f[t->n_other_f - 1]; +			t->n_other_f--; +			info("retrying with f=%d\n", t->param.frequency); +			goto retry; +		} +	} +	return -1; +} + +struct strtab { +	const char *str; +	int val; +}; +static int str2enum(const char *str, const struct strtab *tab, int deflt) +{ +	while (tab->str) { +		if (!strcmp(tab->str, str)) +			return tab->val; +		tab++; +	} +	error("invalid enum value '%s'\n", str); +	return deflt; +} + +static enum fe_code_rate str2fec(const char *fec) +{ +	struct strtab fectab[] = { +		{ "NONE", FEC_NONE }, +		{ "1/2",  FEC_1_2 }, +		{ "2/3",  FEC_2_3 }, +		{ "3/4",  FEC_3_4 }, +		{ "4/5",  FEC_4_5 }, +		{ "5/6",  FEC_5_6 }, +		{ "6/7",  FEC_6_7 }, +		{ "7/8",  FEC_7_8 }, +		{ "8/9",  FEC_8_9 }, +		{ "AUTO", FEC_AUTO }, +		{ NULL, 0 } +	}; +	return str2enum(fec, fectab, FEC_AUTO); +} + +static enum fe_modulation str2qam(const char *qam) +{ +	struct strtab qamtab[] = { +		{ "QPSK",   QPSK }, +		{ "QAM16",  QAM_16 }, +		{ "QAM32",  QAM_32 }, +		{ "QAM64",  QAM_64 }, +		{ "QAM128", QAM_128 }, +		{ "QAM256", QAM_256 }, +		{ "AUTO",   QAM_AUTO }, +		{ NULL, 0 } +	}; +	return str2enum(qam, qamtab, QAM_AUTO); +} + +static enum fe_bandwidth str2bandwidth(const char *bw) +{ +	struct strtab bwtab[] = { +		{ "8MHz", BANDWIDTH_8_MHZ }, +		{ "7MHz", BANDWIDTH_7_MHZ }, +		{ "6MHz", BANDWIDTH_6_MHZ }, +		{ "AUTO", BANDWIDTH_AUTO }, +		{ NULL, 0 } +	}; +	return str2enum(bw, bwtab, BANDWIDTH_AUTO); +} + +static enum fe_transmit_mode str2mode(const char *mode) +{ +	struct strtab modetab[] = { +		{ "2k",   TRANSMISSION_MODE_2K }, +		{ "8k",   TRANSMISSION_MODE_8K }, +		{ "AUTO", TRANSMISSION_MODE_AUTO }, +		{ NULL, 0 } +	}; +	return str2enum(mode, modetab, TRANSMISSION_MODE_AUTO); +} + +static enum fe_guard_interval str2guard(const char *guard) +{ +	struct strtab guardtab[] = { +		{ "1/32", GUARD_INTERVAL_1_32 }, +		{ "1/16", GUARD_INTERVAL_1_16 }, +		{ "1/8",  GUARD_INTERVAL_1_8 }, +		{ "1/4",  GUARD_INTERVAL_1_4 }, +		{ "AUTO", GUARD_INTERVAL_AUTO }, +		{ NULL, 0 } +	}; +	return str2enum(guard, guardtab, GUARD_INTERVAL_AUTO); +} + +static enum fe_hierarchy str2hier(const char *hier) +{ +	struct strtab hiertab[] = { +		{ "NONE", HIERARCHY_NONE }, +		{ "1",    HIERARCHY_1 }, +		{ "2",    HIERARCHY_2 }, +		{ "4",    HIERARCHY_4 }, +		{ "AUTO", HIERARCHY_AUTO }, +		{ NULL, 0 } +	}; +	return str2enum(hier, hiertab, HIERARCHY_AUTO); +} + +static int tune_initial (int frontend_fd, const char *initial) +{ +	FILE *inif; +	unsigned int f, sr; +	char buf[200]; +	char pol[20], fec[20], qam[20], bw[20], fec2[20], mode[20], guard[20], hier[20]; +	struct transponder *t; + +	inif = fopen(initial, "r"); +	if (!inif) { +		error("cannot open '%s': %d %m\n", initial, errno); +		return -1; +	} +	while (fgets(buf, sizeof(buf), inif)) { +		if (buf[0] == '#' || buf[0] == '\n') +			; +		else if (sscanf(buf, "S %u %1[HVLR] %u %4s\n", &f, pol, &sr, fec) == 4) { +			t = alloc_transponder(f); +			t->type = FE_QPSK; +			switch(pol[0]) { +				case 'H': +				case 'L': +					t->polarisation = POLARISATION_HORIZONTAL; +					break; +				default: +					t->polarisation = POLARISATION_VERTICAL;; +					break; +			} +			t->param.inversion = spectral_inversion; +			t->param.u.qpsk.symbol_rate = sr; +			t->param.u.qpsk.fec_inner = str2fec(fec); +			info("initial transponder %u %c %u %d\n", +					t->param.frequency, +					pol[0], sr, +					t->param.u.qpsk.fec_inner); +		} +		else if (sscanf(buf, "C %u %u %4s %6s\n", &f, &sr, fec, qam) == 4) { +			t = alloc_transponder(f); +			t->type = FE_QAM; +			t->param.inversion = spectral_inversion; +			t->param.u.qam.symbol_rate = sr; +			t->param.u.qam.fec_inner = str2fec(fec); +			t->param.u.qam.modulation = str2qam(qam); +			info("initial transponder %u %u %d %d\n", +					t->param.frequency, +					sr, +					t->param.u.qam.fec_inner, +					t->param.u.qam.modulation); +		} +		else if (sscanf(buf, "T %u %4s %4s %4s %7s %4s %4s %4s\n", +					&f, bw, fec, fec2, qam, mode, guard, hier) == 8) { +			t = alloc_transponder(f); +			t->type = FE_OFDM; +			t->param.inversion = spectral_inversion; +			t->param.u.ofdm.bandwidth = str2bandwidth(bw); +			t->param.u.ofdm.code_rate_HP = str2fec(fec); +			t->param.u.ofdm.code_rate_LP = str2fec(fec2); +			t->param.u.ofdm.constellation = str2qam(qam); +			t->param.u.ofdm.transmission_mode = str2mode(mode); +			t->param.u.ofdm.guard_interval = str2guard(guard); +			t->param.u.ofdm.hierarchy_information = str2hier(hier); +			info("initial transponder %u %d %d %d %d %d %d %d\n", +					t->param.frequency, +					t->param.u.ofdm.bandwidth, +					t->param.u.ofdm.code_rate_HP, +					t->param.u.ofdm.code_rate_LP, +					t->param.u.ofdm.constellation, +					t->param.u.ofdm.transmission_mode, +					t->param.u.ofdm.guard_interval, +					t->param.u.ofdm.hierarchy_information); +		} +		else +			error("cannot parse'%s'\n", buf); +	} + +	fclose(inif); + +	return tune_to_next_transponder(frontend_fd); +} + + +static void scan_tp (void) +{ +	struct section_buf s0; +	struct section_buf s1; +	struct section_buf s2; +	struct section_buf s3; + +	/** +	 *  filter timeouts > min repetition rates specified in ETR211 +	 */ +	setup_filter (&s0, demux_devname, 0x00, 0x00, 1, 0, 5); /* PAT */ +	setup_filter (&s1, demux_devname, 0x11, 0x42, 1, 0, 5); /* SDT */ + +	add_filter (&s0); +	add_filter (&s1); + +	if (!current_tp_only || output_format != OUTPUT_PIDS) { +		setup_filter (&s2, demux_devname, 0x10, 0x40, 1, 0, 15); /* NIT */ +		add_filter (&s2); +		if (get_other_nits) { +			/* get NIT-others +			 * Note: There is more than one NIT-other: one per +			 * network, separated by the network_id. +			 */ +			setup_filter (&s3, demux_devname, 0x10, 0x41, 1, 1, 15); +			add_filter (&s3); +		} +	} + +	do { +		read_filters (); +	} while (!(list_empty(&running_filters) && +		   list_empty(&waiting_filters))); +} + +static void scan_network (int frontend_fd, const char *initial) +{ +	if (tune_initial (frontend_fd, initial) < 0) { +		error("initial tuning failed\n"); +		return; +	} + +	do { +		scan_tp(); +	} while (tune_to_next_transponder(frontend_fd) == 0); +} + + +static void pids_dump_service_parameter_set(FILE *f, struct service *s) +{ +        int i; + +	fprintf(f, "%-24.24s (0x%04x) %02x: ", s->service_name, s->service_id, s->type); +	if (!s->pcr_pid || (s->type > 2)) +		fprintf(f, "           "); +	else if (s->pcr_pid == s->video_pid) +		fprintf(f, "PCR == V   "); +	else if ((s->audio_num == 1) && (s->pcr_pid == s->audio_pid[0])) +		fprintf(f, "PCR == A   "); +	else +		fprintf(f, "PCR 0x%04x ", s->pcr_pid); +	if (s->video_pid) +		fprintf(f, "V 0x%04x", s->video_pid); +	else +		fprintf(f, "        "); +	if (s->audio_num) +		fprintf(f, " A"); +        for (i = 0; i < s->audio_num; i++) { +		fprintf(f, " 0x%04x", s->audio_pid[i]); +		if (s->audio_lang[i][0]) +			fprintf(f, " (%.3s)", s->audio_lang[i]); +		else if (s->audio_num == 1) +			fprintf(f, "      "); +	} +	if (s->teletext_pid) +		fprintf(f, " TT 0x%04x", s->teletext_pid); +	if (s->ac3_pid) +		fprintf(f, " AC3 0x%04x", s->ac3_pid); +	if (s->subtitling_pid) +		fprintf(f, " SUB 0x%04x", s->subtitling_pid); +	fprintf(f, "\n"); +} + +static char sat_polarisation (struct transponder *t) +{ +	return t->polarisation == POLARISATION_VERTICAL ? 'v' : 'h'; +} + +static int sat_number (struct transponder *t) +{ +	return switch_pos; +} + +static void dump_lists (void) +{ +	struct list_head *p1, *p2; +	struct transponder *t; +	struct service *s; +	int n = 0, i; +	char sn[20]; + +	list_for_each(p1, &scanned_transponders) { +		t = list_entry(p1, struct transponder, list); +		list_for_each(p2, &t->services) { +			n++; +		} +	} +	info("dumping lists (%d services)\n", n); + +	list_for_each(p1, &scanned_transponders) { +		t = list_entry(p1, struct transponder, list); +		list_for_each(p2, &t->services) { +			s = list_entry(p2, struct service, list); + +			if (!s->service_name) { +				/* not in SDT */ +				snprintf(sn, sizeof(sn), "[%04x]", s->service_id); +				s->service_name = strdup(sn); +			} +			/* ':' is field separator in szap and vdr service lists */ +			for (i = 0; s->service_name[i]; i++) { +				if (s->service_name[i] == ':') +					s->service_name[i] = ' '; +			} +			for (i = 0; s->provider_name && s->provider_name[i]; i++) { +				if (s->provider_name[i] == ':') +					s->provider_name[i] = ' '; +			} +			if (s->video_pid && !(serv_select & 1)) +				continue; /* no TV services */ +			if (!s->video_pid && s->audio_num && !(serv_select & 2)) +				continue; /* no radio services */ +			if (!s->video_pid && !s->audio_num && !(serv_select & 4)) +				continue; /* no data/other services */ +			if (s->scrambled && !ca_select) +				continue; /* FTA only */ +			switch (output_format) +			{ +			  case OUTPUT_PIDS: +				pids_dump_service_parameter_set (stdout, s); +				break; +			  case OUTPUT_VDR: +				vdr_dump_service_parameter_set (stdout, +						    s->service_name, +						    s->provider_name, +						    t->type, +						    &t->param, +						    sat_polarisation(t), +						    s->video_pid, +						    s->pcr_pid, +						    s->audio_pid, +						    //FIXME: s->audio_lang +						    s->audio_num, +						    s->teletext_pid, +						    s->scrambled, +						    //FIXME: s->subtitling_pid +						    s->ac3_pid, +						    s->service_id, +						    t->network_id, +						    s->transport_stream_id, +						    t->orbital_pos, +						    t->we_flag, +						    vdr_dump_provider, +						    ca_select, +						    vdr_version, +						    vdr_dump_channum, +						    s->channel_num); +				break; +			  case OUTPUT_ZAP: +				zap_dump_service_parameter_set (stdout, +						    s->service_name, +						    t->type, +						    &t->param, +						    sat_polarisation(t), +						    sat_number(t), +						    s->video_pid, +						    s->audio_pid, +						    s->service_id); +			  default: +				break; +			  } +		} +	} +	info("Done.\n"); +} + +static void handle_sigint(int sig) +{ +	error("interrupted by SIGINT, dumping partial result...\n"); +	dump_lists(); +	exit(2); +} + +static const char *usage = "\n" +	"usage: %s [options...] [-c | initial-tuning-data-file]\n" +	"	scan doesn't do frequency scans, hence it needs initial\n" +	"	tuning data for at least one transponder/channel.\n" +	"	-c	scan on currently tuned transponder only\n" +	"	-v 	verbose (repeat for more)\n" +	"	-q 	quiet (repeat for less)\n" +	"	-a N	use DVB /dev/dvb/adapterN/\n" +	"	-f N	use DVB /dev/dvb/adapter?/frontendN\n" +	"	-d N	use DVB /dev/dvb/adapter?/demuxN\n" +	"	-s N	use DiSEqC switch position N (DVB-S only)\n" +	"	-i N	spectral inversion setting (0: off, 1: on, 2: auto [default])\n" +	"	-n	evaluate NIT-other for full network scan (slow!)\n" +	"	-5	multiply all filter timeouts by factor 5\n" +	"		for non-DVB-compliant section repitition rates\n" +	"	-o fmt	output format: 'zap' (default), 'vdr' or 'pids' (default with -c)\n" +	"	-x N	Conditional Axcess, (default 1)\n" +	"		N=0 gets only FTA channels\n" +	"		N=xxx sets ca field in vdr output to :xxx:\n" +	"	-t N	Service select, Combined bitfield parameter.\n" +	"		1 = TV, 2 = Radio, 4 = Other, (default 7)\n" +	"	-p	for vdr output format: dump provider name\n" +	"	-e N	VDR version, default 2 for VDR-1.2.x\n" +	"		ANYTHING ELSE GIVES NONZERO NIT and TID\n" +	"	-l lnb-type (DVB-S Only) (use -l help to print types) or \n" +	"	-l low[,high[,switch]] in Mhz\n" +	"	-u      UK DVB-T Freeview channel numbering for VDR\n"; + +void +bad_usage(char *pname, int prlnb) +{ +int i; +struct lnb_types_st *lnbp; +char **cp; + +	if (!prlnb) { +		fprintf (stderr, usage, pname); +	} else { +		i = 0; +		fprintf(stderr, "-l <lnb-type> or -l low[,high[,switch]] in Mhz\nwhere <lnb-type> is:\n"); +		while(NULL != (lnbp = lnb_enum(i))) { +			fprintf (stderr, "%s\n", lnbp->name); +			for (cp = lnbp->desc; *cp ; cp++) { +				fprintf (stderr, "   %s\n", *cp); +			} +			i++; +		} +	} +} + +int main (int argc, char **argv) +{ +	char frontend_devname [80]; +	int adapter = 0, frontend = 0, demux = 0; +	int opt, i; +	int frontend_fd; +	int fe_open_mode; +	const char *initial = NULL; + +	/* start with default lnb type */ +	lnb_type = *lnb_enum(0); +	while ((opt = getopt(argc, argv, "5cnpa:f:d:s:o:x:e:t:i:l:vq:u")) != -1) { +		switch (opt) { +		case 'a': +			adapter = strtoul(optarg, NULL, 0); +			break; +		case 'c': +			current_tp_only = 1; +			output_format = OUTPUT_PIDS; +			break; +		case 'n': +			get_other_nits = 1; +			break; +		case 'd': +			demux = strtoul(optarg, NULL, 0); +			break; +		case 'f': +			frontend = strtoul(optarg, NULL, 0); +			break; +		case 'p': +			vdr_dump_provider = 1; +			break; +		case 's': +			switch_pos = strtoul(optarg, NULL, 0); +			break; +		case 'o': +                        if      (strcmp(optarg, "zap") == 0) output_format = OUTPUT_ZAP; +                        else if (strcmp(optarg, "vdr") == 0) output_format = OUTPUT_VDR; +                        else if (strcmp(optarg, "pids") == 0) output_format = OUTPUT_PIDS; +                        else { +				bad_usage(argv[0], 0); +				return -1; +			} +			break; +		case '5': +			long_timeout = 1; +			break; +		case 'x': +			ca_select = strtoul(optarg, NULL, 0); +			break; +		case 'e': +			vdr_version = strtoul(optarg, NULL, 0); +			break; +		case 't': +			serv_select = strtoul(optarg, NULL, 0); +			break; +		case 'i': +			spectral_inversion = strtoul(optarg, NULL, 0); +			break; +		case 'l': +			if (lnb_decode(optarg, &lnb_type) < 0) { +				bad_usage(argv[0], 1); +				return -1; +			} +			break; +		case 'v': +			verbosity++; +			break; +		case 'q': +			if (--verbosity < 0) +				verbosity = 0; +			break; +		case 'u': +			vdr_dump_channum = 1; +			break; +		default: +			bad_usage(argv[0], 0); +			return -1; +		}; +	} + +	if (optind < argc) +		initial = argv[optind]; +	if ((!initial && !current_tp_only) || (initial && current_tp_only) || +			(spectral_inversion > 2)) { +		bad_usage(argv[0], 0); +		return -1; +	} +	lnb_type.low_val *= 1000;	/* convert to kiloherz */ +	lnb_type.high_val *= 1000;	/* convert to kiloherz */ +	lnb_type.switch_val *= 1000;	/* convert to kiloherz */ +	if (switch_pos >= 4) { +		fprintf (stderr, "switch position needs to be < 4!\n"); +		return -1; +	} +	if (initial) +		info("scanning %s\n", initial); + +	snprintf (frontend_devname, sizeof(frontend_devname), +		  "/dev/dvb/adapter%i/frontend%i", adapter, frontend); + +	snprintf (demux_devname, sizeof(demux_devname), +		  "/dev/dvb/adapter%i/demux%i", adapter, demux); +	info("using '%s' and '%s'\n", frontend_devname, demux_devname); + +	for (i = 0; i < MAX_RUNNING; i++) +		poll_fds[i].fd = -1; + +	fe_open_mode = current_tp_only ? O_RDONLY : O_RDWR; +	if ((frontend_fd = open (frontend_devname, fe_open_mode)) < 0) +		fatal("failed to open '%s': %d %m\n", frontend_devname, errno); +	/* determine FE type and caps */ +	if (ioctl(frontend_fd, FE_GET_INFO, &fe_info) == -1) +		fatal("FE_GET_INFO failed: %d %m\n", errno); + +	if ((spectral_inversion == INVERSION_AUTO ) && +	    !(fe_info.caps & FE_CAN_INVERSION_AUTO)) { +		info("Frontend can not do INVERSION_AUTO, trying INVERSION_OFF instead\n"); +		spectral_inversion = INVERSION_OFF; +	} + +	signal(SIGINT, handle_sigint); + +	if (current_tp_only) { +		current_tp = alloc_transponder(0); /* dummy */ +		/* move TP from "new" to "scanned" list */ +		list_del_init(¤t_tp->list); +		list_add_tail(¤t_tp->list, &scanned_transponders); +		current_tp->scan_done = 1; +		scan_tp (); +	} +	else +		scan_network (frontend_fd, initial); + +	close (frontend_fd); + +	dump_lists (); + +	return 0; +} + +static void dump_dvb_parameters (FILE *f, struct transponder *t) +{ +	switch (output_format) { +		case OUTPUT_PIDS: +		case OUTPUT_VDR: +			vdr_dump_dvb_parameters(f, t->type, &t->param, +					sat_polarisation (t), t->orbital_pos, t->we_flag); +			break; +		case OUTPUT_ZAP: +			zap_dump_dvb_parameters (f, t->type, &t->param, +					sat_polarisation (t), sat_number (t)); +			break; +		default: +			break; +	} +} diff --git a/util/scan/scan.h b/util/scan/scan.h new file mode 100644 index 0000000..8209076 --- /dev/null +++ b/util/scan/scan.h @@ -0,0 +1,29 @@ +#ifndef __SCAN_H__ +#define __SCAN_H__ + +#include <stdio.h> +#include <errno.h> + +extern int verbosity; + +#define dprintf(level, fmt...)			\ +	do {					\ +		if (level <= verbosity)		\ +			fprintf(stderr, fmt);	\ +	} while (0) + +#define dpprintf(level, fmt, args...) \ +	dprintf(level, "%s:%d: " fmt, __FUNCTION__, __LINE__ , ##args) + +#define fatal(fmt, args...) do { dpprintf(-1, "FATAL: " fmt , ##args); exit(1); } while(0) +#define error(msg...) dprintf(0, "ERROR: " msg) +#define errorn(msg) dprintf(0, "%s:%d: ERROR: " msg ": %d %m\n", __FUNCTION__, __LINE__, errno) +#define warning(msg...) dprintf(1, "WARNING: " msg) +#define info(msg...) dprintf(2, msg) +#define verbose(msg...) dprintf(3, msg) +#define moreverbose(msg...) dprintf(4, msg) +#define debug(msg...) dpprintf(5, msg) +#define verbosedebug(msg...) dpprintf(6, msg) + +#endif + | 
