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