1 /*
2  * util functions for various ?zap implementations
3  *
4  * Copyright (C) 2001 Johannes Stezenbach (js@convergence.de)
5  * for convergence integrated media
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
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 <string.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <stdint.h>
27 
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 
33 #include <linux/dvb/frontend.h>
34 #include <linux/dvb/dmx.h>
35 
36 
set_pesfilter(int dmxfd,int pid,int pes_type,int dvr)37 int set_pesfilter(int dmxfd, int pid, int pes_type, int dvr)
38 {
39 	struct dmx_pes_filter_params pesfilter;
40 
41 	/* ignore this pid to allow radio services */
42 	if (pid < 0 || pid >= 0x1fff || (pid == 0 && pes_type != DMX_PES_OTHER))
43 		return 0;
44 
45 	if (dvr) {
46 		int buffersize = 64 * 1024;
47 		if (ioctl(dmxfd, DMX_SET_BUFFER_SIZE, buffersize) == -1)
48 			perror("DMX_SET_BUFFER_SIZE failed");
49 	}
50 
51 	pesfilter.pid = pid;
52 	pesfilter.input = DMX_IN_FRONTEND;
53 	pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER;
54 	pesfilter.pes_type = pes_type;
55 	pesfilter.flags = DMX_IMMEDIATE_START;
56 
57 	if (ioctl(dmxfd, DMX_SET_PES_FILTER, &pesfilter) == -1) {
58 		fprintf(stderr, "DMX_SET_PES_FILTER failed "
59 			"(PID = 0x%04x): %d %m\n", pid, errno);
60 		return -1;
61 	}
62 	return 0;
63 }
64 
65 
get_pmt_pid(char * dmxdev,int sid)66 int get_pmt_pid(char *dmxdev, int sid)
67 {
68 	int patfd, count;
69 	int pmt_pid = 0;
70 	int patread = 0;
71 	int section_length;
72 	unsigned char buft[4096];
73 	unsigned char *buf = buft;
74 	struct dmx_sct_filter_params f;
75 
76 	memset(&f, 0, sizeof(f));
77 	f.pid = 0;
78 	f.filter.filter[0] = 0x00;
79 	f.filter.mask[0] = 0xff;
80 	f.timeout = 0;
81 	f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
82 
83 	if ((patfd = open(dmxdev, O_RDWR)) < 0) {
84 		perror("openening pat demux failed");
85 		return -1;
86 	}
87 
88 	if (ioctl(patfd, DMX_SET_FILTER, &f) == -1) {
89 		perror("ioctl DMX_SET_FILTER failed");
90 		close(patfd);
91 		return -1;
92 	}
93 
94 	while (!patread) {
95 		if (((count = read(patfd, buf, sizeof(buft))) < 0) && errno == EOVERFLOW)
96 			count = read(patfd, buf, sizeof(buft));
97 
98 		if (count < 0) {
99 			perror("read_sections: read error");
100 			close(patfd);
101 			return -1;
102 		}
103 		section_length = ((buf[1] & 0x0f) << 8) | buf[2];
104 		if (count != section_length + 3)
105 			continue;
106 
107 		buf += 8;
108 		section_length -= 8;
109 
110 		patread = 1; /* assumes one section contains the whole pat */
111 		while (section_length > 0) {
112 			int service_id = (buf[0] << 8) | buf[1];
113 
114 			if (service_id == sid) {
115 				pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3];
116 				section_length = 0;
117 			}
118 			buf += 4;
119 			section_length -= 4;
120 		}
121 	}
122 	close(patfd);
123 	return pmt_pid;
124 }
125 
126 char *type_str[] = {
127 	"QPSK",
128 	"QAM",
129 	"OFDM",
130 	"ATSC",
131 };
132 
133 /* to be used with v3 drivers */
check_frontend_v3(int fd,enum fe_type type)134 int check_frontend_v3(int fd, enum fe_type type)
135 {
136 	struct dvb_frontend_info info;
137 	int ret;
138 
139 	ret = ioctl(fd, FE_GET_INFO, &info);
140 	if (ret < 0) {
141 		perror("ioctl FE_GET_INFO failed");
142 		close(fd);
143 		ret = -1;
144 		goto exit;
145 	}
146 	if (info.type != type) {
147 		fprintf(stderr, "Not a valid %s device!\n", type_str[type]);
148 		close(fd);
149 		ret = -EINVAL;
150 		goto exit;
151 	}
152 exit:
153 	return ret;
154 }
155 
156 char *del_str[] = {
157 	"UNDEFINED",
158 	"DVB-C (A)",
159 	"DVB-C (B)",
160 	"DVB-T",
161 	"DSS",
162 	"DVB-S",
163 	"DVB-S2",
164 	"DVB-H",
165 	"ISDB-T",
166 	"ISDB-S",
167 	"ISDB-C",
168 	"ATSC",
169 	"ATSC-M/H",
170 	"DTMB",
171 	"CMMB",
172 	"DAB",
173 	"DVB-T2",
174 	"TURBO",
175 	"QAM (C)",
176 };
177 
map_delivery_mode(fe_type_t * type,enum fe_delivery_system delsys)178 static int map_delivery_mode(fe_type_t *type, enum fe_delivery_system delsys)
179 {
180 	switch (delsys) {
181 	case SYS_DSS:
182 	case SYS_DVBS:
183 	case SYS_DVBS2:
184 	case SYS_TURBO:
185 		*type = FE_QPSK;
186 		break;
187 	case SYS_DVBT:
188 	case SYS_DVBT2:
189 	case SYS_DVBH:
190 	case SYS_ISDBT:
191 		*type = FE_OFDM;
192 		break;
193 	case SYS_DVBC_ANNEX_A:
194 	case SYS_DVBC_ANNEX_C:
195 		*type = FE_QAM;
196 		break;
197 	case SYS_ATSC:
198 	case SYS_DVBC_ANNEX_B:
199 		*type = FE_ATSC;
200 		break;
201 	default:
202 		fprintf(stderr, "Delivery system unsupported, please report to linux-media ML\n");
203 		return -1;
204 	}
205 	return 0;
206 }
207 
get_property(int fd,uint32_t pcmd,uint32_t * len,uint8_t * data)208 int get_property(int fd, uint32_t pcmd, uint32_t *len, uint8_t *data)
209 {
210 	struct dtv_property p, *b;
211 	struct dtv_properties cmd;
212 	int ret;
213 
214 	p.cmd = pcmd;
215 	cmd.num = 1;
216 	cmd.props = &p;
217 	b = &p;
218 
219 	ret = ioctl(fd, FE_GET_PROPERTY, &cmd);
220 	if (ret < 0) {
221 		fprintf(stderr, "FE_SET_PROPERTY returned %d\n", ret);
222 		return -1;
223 	}
224 	memcpy(len, &b->u.buffer.len, sizeof (uint32_t));
225 	memcpy(data, b->u.buffer.data, *len);
226 	return 0;
227 }
228 
set_property(int fd,uint32_t cmd,uint32_t data)229 int set_property(int fd, uint32_t cmd, uint32_t data)
230 {
231 	struct dtv_property p, *b;
232 	struct dtv_properties c;
233 	int ret;
234 
235 	p.cmd = cmd;
236 	c.num = 1;
237 	c.props = &p;
238 	b = &p;
239 	b->u.data = data;
240 	ret = ioctl(fd, FE_SET_PROPERTY, &c);
241 	if (ret < 0) {
242 		fprintf(stderr, "FE_SET_PROPERTY returned %d\n", ret);
243 		return -1;
244 	}
245 	return 0;
246 }
247 
dvbfe_get_delsys(int fd,fe_delivery_system_t * delsys)248 int dvbfe_get_delsys(int fd, fe_delivery_system_t *delsys)
249 {
250 	uint32_t len;
251 	/* Buggy API design */
252 	return get_property(fd, DTV_DELIVERY_SYSTEM, &len, (uint8_t *)delsys);
253 }
254 
dvbfe_set_delsys(int fd,enum fe_delivery_system delsys)255 int dvbfe_set_delsys(int fd, enum fe_delivery_system delsys)
256 {
257 	return set_property(fd, DTV_DELIVERY_SYSTEM, delsys);
258 }
259 
dvbfe_enum_delsys(int fd,uint32_t * len,uint8_t * data)260 int dvbfe_enum_delsys(int fd, uint32_t *len, uint8_t *data)
261 {
262 	return get_property(fd, DTV_ENUM_DELSYS, len, data);
263 }
264 
dvbfe_get_version(int fd,int * major,int * minor)265 int dvbfe_get_version(int fd, int *major, int *minor)
266 {
267 	struct dtv_property p, *b;
268 	struct dtv_properties cmd;
269 	int ret;
270 
271 	p.cmd = DTV_API_VERSION;
272 	cmd.num = 1;
273 	cmd.props = &p;
274 	b = &p;
275 
276 	ret = ioctl(fd, FE_GET_PROPERTY, &cmd);
277 	if (ret < 0) {
278 		fprintf(stderr, "FE_GET_PROPERTY failed, ret=%d\n", ret);
279 		return -1;
280 	}
281 	*major = (b->u.data >> 8) & 0xff;
282 	*minor = b->u.data & 0xff;
283 	return 0;
284 }
285 
check_frontend_multi(int fd,enum fe_type type,uint32_t * mstd)286 int check_frontend_multi(int fd, enum fe_type type, uint32_t *mstd)
287 {
288 	int ret;
289 
290 	enum fe_type delmode;
291 	unsigned int i, valid_delsys = 0;
292 	uint32_t len;
293 	uint8_t data[32];
294 
295 	ret = dvbfe_enum_delsys(fd, &len, data);
296 	if (ret) {
297 		fprintf(stderr, "enum_delsys failed, ret=%d\n", ret);
298 		ret = -EIO;
299 		goto exit;
300 	}
301 	fprintf(stderr, "\t FE_CAN { ");
302 	for (i = 0; i < len; i++) {
303 		if (i < len - 1)
304 			fprintf(stderr, "%s + ", del_str[data[i]]);
305 		else
306 			fprintf(stderr, "%s", del_str[data[i]]);
307 	}
308 	fprintf(stderr, " }\n");
309 	/* check whether frontend can support our delivery */
310 	for (i = 0; i < len; i++) {
311 		map_delivery_mode(&delmode, data[i]);
312 		if (type == delmode) {
313 			valid_delsys = 1;
314 			ret = 0;
315 			break;
316 		}
317 	}
318 	if (!valid_delsys) {
319 		fprintf(stderr, "Not a valid %s device!\n", type_str[type]);
320 		ret = -EINVAL;
321 		goto exit;
322 	}
323 	*mstd = len; /* mstd has supported delsys count */
324 exit:
325 	return ret;
326 }
327 
check_frontend(int fd,enum fe_type type,uint32_t * mstd)328 int check_frontend(int fd, enum fe_type type, uint32_t *mstd)
329 {
330 	int major, minor, ret;
331 
332 	ret = dvbfe_get_version(fd, &major, &minor);
333 	if (ret)
334 		goto exit;
335 	fprintf(stderr, "Version: %d.%d  ", major, minor);
336 	if ((major == 5) && (minor > 8)) {
337 		ret = check_frontend_multi(fd, type, mstd);
338 		if (ret)
339 			goto exit;
340 	} else {
341 		ret = check_frontend_v3(fd, type);
342 		if (ret)
343 			goto exit;
344 	}
345 exit:
346 	return ret;
347 }
348