1 /*
2 	dvbscan utility
3 
4 	Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 
15 	GNU General Public License for more details.
16 
17 	You should have received a copy of the GNU General Public License
18 	along with this program; if not, write to the Free Software
19 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <time.h>
27 #include <libdvbsec/dvbsec_cfg.h>
28 #include <libdvbcfg/dvbcfg_scanfile.h>
29 #include <libdvbapi/dvbdemux.h>
30 #include "dvbscan.h"
31 
32 
33 #define OUTPUT_TYPE_RAW 		1
34 #define OUTPUT_TYPE_CHANNELS 		2
35 #define OUTPUT_TYPE_VDR12 		3
36 #define OUTPUT_TYPE_VDR13 		4
37 
38 #define SERVICE_FILTER_TV		1
39 #define SERVICE_FILTER_RADIO		2
40 #define SERVICE_FILTER_OTHER		4
41 #define SERVICE_FILTER_ENCRYPTED	8
42 
43 #define TIMEOUT_WAIT_LOCK		2
44 
45 
46 // transponders we have yet to scan
47 static struct transponder *toscan = NULL;
48 static struct transponder *toscan_end = NULL;
49 
50 // transponders we have scanned
51 static struct transponder *scanned = NULL;
52 static struct transponder *scanned_end = NULL;
53 
54 
usage(void)55 static void usage(void)
56 {
57 	static const char *_usage = "\n"
58 		" dvbscan: A digital tv channel scanning utility\n"
59 		" Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)\n\n"
60 		" usage: dvbscan <options> as follows:\n"
61 		" -h			help\n"
62 		" -adapter <id>		adapter to use (default 0)\n"
63 		" -frontend <id>	frontend to use (default 0)\n"
64 		" -demux <id>		demux to use (default 0)\n"
65 		" -secfile <filename>	Optional sec.conf file.\n"
66 		" -secid <secid>	ID of the SEC configuration to use, one of:\n"
67 		"			 * UNIVERSAL (default) - Europe, 10800 to 11800 MHz and 11600 to 12700 Mhz,\n"
68 		" 						 Dual LO, loband 9750, hiband 10600 MHz.\n"
69 		"			 * DBS - Expressvu, North America, 12200 to 12700 MHz, Single LO, 11250 MHz.\n"
70 		"			 * STANDARD - 10945 to 11450 Mhz, Single LO, 10000 Mhz.\n"
71 		"			 * ENHANCED - Astra, 10700 to 11700 MHz, Single LO, 9750 MHz.\n"
72 		"			 * C-BAND - Big Dish, 3700 to 4200 MHz, Single LO, 5150 Mhz.\n"
73 		"			 * C-MULTI - Big Dish - Multipoint LNBf, 3700 to 4200 MHz,\n"
74 		"						Dual LO, H:5150MHz, V:5750MHz.\n"
75 		"			 * One of the sec definitions from the secfile if supplied\n"
76 		" -satpos <position>	Specify DISEQC switch position for DVB-S.\n"
77 		" -inversion <on|off|auto> Specify inversion (default: auto) (note: this option is ignored).\n"
78 		" -uk-ordering 		Use UK DVB-T channel ordering if present (note: this option is ignored).\n"
79 		" -timeout <secs>	Specify filter timeout to use (standard specced values will be used by default)\n"
80 		" -filter <filter>	Specify service filter, a comma seperated list of the following tokens:\n"
81 		" 			 (If no filter is supplied, all services will be output)\n"
82 		"			 * tv - Output TV channels\n"
83 		"			 * radio - Output radio channels\n"
84 		"			 * other - Output other channels\n"
85 		"			 * encrypted - Output encrypted channels\n"
86 		" -out raw <filename>|-	Output in raw format to <filename> or stdout\n"
87 		"      channels <filename>|-  Output in channels.conf format to <filename> or stdout.\n"
88 		"      vdr12 <filename>|- Output in vdr 1.2.x format to <filename> or stdout.\n"
89 		"      vdr13 <filename>|- Output in vdr 1.3.x format to <filename> or stdout.\n"
90 		"			Note: this option is ignored.\n"
91 		" <initial scan file>\n";
92 	fprintf(stderr, "%s\n", _usage);
93 
94 	exit(1);
95 }
96 
97 
scan_load_callback(struct dvbcfg_scanfile * channel,void * private_data)98 static int scan_load_callback(struct dvbcfg_scanfile *channel, void *private_data)
99 {
100 	struct dvbfe_info *feinfo = (struct dvbfe_info *) private_data;
101 
102 	if (channel->fe_type != feinfo->type)
103 		return 0;
104 
105 	struct transponder *t = new_transponder();
106 	append_transponder(t, &toscan, &toscan_end);
107 	memcpy(&t->params, &channel->fe_params, sizeof(struct dvbfe_parameters));
108 
109 	add_frequency(t, t->params.frequency);
110 	t->params.frequency = 0;
111 
112 	return 0;
113 }
114 
main(int argc,char * argv[])115 int main(int argc, char *argv[])
116 {
117 	uint32_t i;
118 	int argpos = 1;
119 	int adapter_id = 0;
120 	int frontend_id = 0;
121 	int demux_id = 0;
122 	char *secfile = NULL;
123 	char *secid = NULL;
124 	int satpos = 0;
125 	int service_filter = -1;
126 	int timeout = 5;
127 	char *scan_filename = NULL;
128 	struct dvbsec_config sec;
129 	int valid_sec = 0;
130 
131 	while(argpos != argc) {
132 		if (!strcmp(argv[argpos], "-h")) {
133 			usage();
134 		} else if (!strcmp(argv[argpos], "-adapter")) {
135 			if ((argc - argpos) < 2)
136 				usage();
137 			if (sscanf(argv[argpos+1], "%i", &adapter_id) != 1)
138 				usage();
139 			argpos+=2;
140 		} else if (!strcmp(argv[argpos], "-frontend")) {
141 			if ((argc - argpos) < 2)
142 				usage();
143 			if (sscanf(argv[argpos+1], "%i", &frontend_id) != 1)
144 				usage();
145 			argpos+=2;
146 		} else if (!strcmp(argv[argpos], "-demux")) {
147 			if ((argc - argpos) < 2)
148 				usage();
149 			if (sscanf(argv[argpos+1], "%i", &demux_id) != 1)
150 				usage();
151 			argpos+=2;
152 		} else if (!strcmp(argv[argpos], "-secfile")) {
153 			if ((argc - argpos) < 2)
154 				usage();
155 			secfile = argv[argpos+1];
156 			argpos+=2;
157 		} else if (!strcmp(argv[argpos], "-secid")) {
158 			if ((argc - argpos) < 2)
159 				usage();
160 			secid = argv[argpos+1];
161 			argpos+=2;
162 		} else if (!strcmp(argv[argpos], "-satpos")) {
163 			if ((argc - argpos) < 2)
164 				usage();
165 			if (sscanf(argv[argpos+1], "%i", &satpos) != 1)
166 				usage();
167 			argpos+=2;
168 		} else if (!strcmp(argv[argpos], "-inversion")) {
169 			if ((argc - argpos) < 2)
170 				usage();
171 			argpos+=2;
172 		} else if (!strcmp(argv[argpos], "-uk-ordering")) {
173 			if ((argc - argpos) < 1)
174 				usage();
175 		} else if (!strcmp(argv[argpos], "-timeout")) {
176 			if ((argc - argpos) < 2)
177 				usage();
178 			if (sscanf(argv[argpos+1], "%i", &timeout) != 1)
179 				usage();
180 			argpos+=2;
181 		} else if (!strcmp(argv[argpos], "-filter")) {
182 			if ((argc - argpos) < 2)
183 				usage();
184 			service_filter = 0;
185 			if (!strstr(argv[argpos+1], "tv")) {
186 				service_filter |= SERVICE_FILTER_TV;
187 			}
188  			if (!strstr(argv[argpos+1], "radio")) {
189 				service_filter |= SERVICE_FILTER_RADIO;
190  			}
191 			if (!strstr(argv[argpos+1], "other")) {
192 				service_filter |= SERVICE_FILTER_OTHER;
193 			}
194 			if (!strstr(argv[argpos+1], "encrypted")) {
195 				service_filter |= SERVICE_FILTER_ENCRYPTED;
196 			}
197 			argpos+=2;
198 		} else if (!strcmp(argv[argpos], "-out")) {
199 			if ((argc - argpos) < 3)
200 				usage();
201 		} else {
202 			if ((argc - argpos) != 1)
203 				usage();
204 			scan_filename = argv[argpos];
205 			argpos++;
206 		}
207 	}
208 
209 	// open the frontend & get its type
210 	struct dvbfe_handle *fe = dvbfe_open(adapter_id, frontend_id, 0);
211 	if (fe == NULL) {
212 		fprintf(stderr, "Failed to open frontend\n");
213 		exit(1);
214 	}
215 	struct dvbfe_info feinfo;
216 	if (dvbfe_get_info(fe, 0, &feinfo, DVBFE_INFO_QUERYTYPE_IMMEDIATE, 0) != 0) {
217 		fprintf(stderr, "Failed to query frontend\n");
218 		exit(1);
219 	}
220 
221 	// default SEC with a DVBS card
222 	if ((secid == NULL) && (feinfo.type == DVBFE_TYPE_DVBS))
223 		secid = "UNIVERSAL";
224 
225 	// look up SECID if one was supplied
226 	if (secid != NULL) {
227 		if (dvbsec_cfg_find(secfile, secid, &sec)) {
228 			fprintf(stderr, "Unable to find suitable sec/lnb configuration for channel\n");
229 			exit(1);
230 		}
231 		valid_sec = 1;
232 	}
233 
234 	// load the initial scan file
235 	FILE *scan_file = fopen(scan_filename, "r");
236 	if (scan_file == NULL) {
237 		fprintf(stderr, "Could not open scan file %s\n", scan_filename);
238 		exit(1);
239 	}
240 	if (dvbcfg_scanfile_parse(scan_file, scan_load_callback, &feinfo) < 0) {
241 		fprintf(stderr, "Could not parse scan file %s\n", scan_filename);
242 		exit(1);
243 	}
244 	fclose(scan_file);
245 
246 	// main scan loop
247 	while(toscan) {
248 		// get the first item on the toscan list
249 		struct transponder *tmp = first_transponder(&toscan, &toscan_end);
250 
251 		// have we already seen this transponder?
252 		if (seen_transponder(tmp, scanned)) {
253 			free_transponder(tmp);
254 			continue;
255 		}
256 
257 		// do we have a valid SEC configuration?
258 		struct dvbsec_config *psec = NULL;
259 		if (valid_sec)
260 			psec = &sec;
261 
262 		// tune it
263 		int tuned_ok = 0;
264 		for(i=0; i < tmp->frequency_count; i++) {
265 			tmp->params.frequency = tmp->frequencies[i];
266 			if (dvbsec_set(fe,
267 					psec,
268 					tmp->polarization,
269 					(satpos & 0x01) ? DISEQC_SWITCH_B : DISEQC_SWITCH_A,
270 					(satpos & 0x02) ? DISEQC_SWITCH_B : DISEQC_SWITCH_A,
271 					&tmp->params,
272 					0)) {
273 				fprintf(stderr, "Failed to set frontend\n");
274 				exit(1);
275 			}
276 
277 			// wait for lock
278 			time_t starttime = time(NULL);
279 			while((time(NULL) - starttime) < TIMEOUT_WAIT_LOCK) {
280 				if (dvbfe_get_info(fe, DVBFE_INFO_LOCKSTATUS, &feinfo,
281 				    			DVBFE_INFO_QUERYTYPE_IMMEDIATE, 0) !=
282 					DVBFE_INFO_QUERYTYPE_IMMEDIATE) {
283 					fprintf(stderr, "Unable to query frontend status\n");
284 					exit(1);
285 				}
286 				if (feinfo.lock) {
287 					tuned_ok = 1;
288 					break;
289 				}
290 				usleep(100000);
291 			}
292 		}
293 		if (!tuned_ok) {
294 			free_transponder(tmp);
295 			continue;
296 		}
297 
298 		// scan it
299 		switch(feinfo.type) {
300 		case DVBFE_TYPE_DVBS:
301 		case DVBFE_TYPE_DVBC:
302 		case DVBFE_TYPE_DVBT:
303 			dvbscan_scan_dvb(fe);
304 			break;
305 
306 		case DVBFE_TYPE_ATSC:
307 			dvbscan_scan_atsc(fe);
308 			break;
309 		}
310 
311 		// add to scanned list.
312 		append_transponder(tmp, &scanned, &scanned_end);
313 	}
314 
315 	// FIXME: output the data
316 
317 	return 0;
318 }
319 
create_section_filter(int adapter,int demux,uint16_t pid,uint8_t table_id)320 int create_section_filter(int adapter, int demux, uint16_t pid, uint8_t table_id)
321 {
322 	int demux_fd = -1;
323 	uint8_t filter[18];
324 	uint8_t mask[18];
325 
326 	// open the demuxer
327 	if ((demux_fd = dvbdemux_open_demux(adapter, demux, 0)) < 0) {
328 		return -1;
329 	}
330 
331 	// create a section filter
332 	memset(filter, 0, sizeof(filter));
333 	memset(mask, 0, sizeof(mask));
334 	filter[0] = table_id;
335 	mask[0] = 0xFF;
336 	if (dvbdemux_set_section_filter(demux_fd, pid, filter, mask, 1, 1)) {
337 		close(demux_fd);
338 		return -1;
339 	}
340 
341 	// done
342 	return demux_fd;
343 }
344