1 /*
2 * libdvbfe - a DVB frontend library
3 *
4 * Copyright (C) 2005 Andrew de Quincey (adq_dvb@lidskialf.net)
5 * Copyright (C) 2005 Manu Abraham <abraham.manu@gmail.com>
6 * Copyright (C) 2005 Kenneth Aafloy (kenneth@linuxtv.org)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #define _GNU_SOURCE
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <sys/param.h>
28 #include <sys/ioctl.h>
29 #include <sys/time.h>
30 #include <sys/poll.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <linux/dvb/frontend.h>
36 #include <libdvbmisc/dvbmisc.h>
37 #include "dvbfe.h"
38
39 int verbose = 0;
40
41 static int dvbfe_spectral_inversion_to_kapi[][2] =
42 {
43 { DVBFE_INVERSION_OFF, INVERSION_OFF },
44 { DVBFE_INVERSION_ON, INVERSION_ON },
45 { DVBFE_INVERSION_AUTO, INVERSION_AUTO },
46 { -1, -1 }
47 };
48
49 static int dvbfe_code_rate_to_kapi[][2] =
50 {
51 { DVBFE_FEC_NONE, FEC_NONE },
52 { DVBFE_FEC_1_2, FEC_1_2 },
53 { DVBFE_FEC_2_3, FEC_2_3 },
54 { DVBFE_FEC_3_4, FEC_3_4 },
55 { DVBFE_FEC_4_5, FEC_4_5 },
56 { DVBFE_FEC_5_6, FEC_5_6 },
57 { DVBFE_FEC_6_7, FEC_6_7 },
58 { DVBFE_FEC_7_8, FEC_7_8 },
59 { DVBFE_FEC_8_9, FEC_8_9 },
60 { DVBFE_FEC_AUTO, FEC_AUTO },
61 { -1, -1 }
62 };
63
64 static int dvbfe_dvbt_const_to_kapi[][2] =
65 {
66 { DVBFE_DVBT_CONST_QPSK, FE_QPSK },
67 { DVBFE_DVBT_CONST_QAM_16, QAM_16 },
68 { DVBFE_DVBT_CONST_QAM_32, QAM_32 },
69 { DVBFE_DVBT_CONST_QAM_64, QAM_64 },
70 { DVBFE_DVBT_CONST_QAM_128, QAM_128 },
71 { DVBFE_DVBT_CONST_QAM_256, QAM_256 },
72 { DVBFE_DVBT_CONST_AUTO, QAM_AUTO },
73 { -1, -1 }
74 };
75
76 static int dvbfe_dvbc_mod_to_kapi[][2] =
77 {
78 { DVBFE_DVBC_MOD_QAM_16, QAM_16 },
79 { DVBFE_DVBC_MOD_QAM_32, QAM_32 },
80 { DVBFE_DVBC_MOD_QAM_64, QAM_64 },
81 { DVBFE_DVBC_MOD_QAM_128, QAM_128 },
82 { DVBFE_DVBC_MOD_QAM_256, QAM_256 },
83 { DVBFE_DVBC_MOD_AUTO, QAM_AUTO },
84 { -1, -1 }
85 };
86
87 static int dvbfe_atsc_mod_to_kapi[][2] =
88 {
89 { DVBFE_ATSC_MOD_QAM_64, QAM_64 },
90 { DVBFE_ATSC_MOD_QAM_256, QAM_256 },
91 { DVBFE_ATSC_MOD_VSB_8, VSB_8 },
92 { DVBFE_ATSC_MOD_VSB_16, VSB_16 },
93 { DVBFE_ATSC_MOD_AUTO, QAM_AUTO },
94 { -1, -1 }
95 };
96
97 static int dvbfe_dvbt_transmit_mode_to_kapi[][2] =
98 {
99 { DVBFE_DVBT_TRANSMISSION_MODE_2K, TRANSMISSION_MODE_2K },
100 { DVBFE_DVBT_TRANSMISSION_MODE_8K, TRANSMISSION_MODE_8K },
101 { DVBFE_DVBT_TRANSMISSION_MODE_AUTO, TRANSMISSION_MODE_AUTO },
102 { -1, -1 }
103 };
104
105 static int dvbfe_dvbt_bandwidth_to_kapi[][2] =
106 {
107 { DVBFE_DVBT_BANDWIDTH_8_MHZ, BANDWIDTH_8_MHZ },
108 { DVBFE_DVBT_BANDWIDTH_7_MHZ, BANDWIDTH_7_MHZ },
109 { DVBFE_DVBT_BANDWIDTH_6_MHZ, BANDWIDTH_6_MHZ },
110 { DVBFE_DVBT_BANDWIDTH_AUTO, BANDWIDTH_AUTO },
111 { -1, -1 }
112 };
113
114 static int dvbfe_dvbt_guard_interval_to_kapi[][2] =
115 {
116 { DVBFE_DVBT_GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_32},
117 { DVBFE_DVBT_GUARD_INTERVAL_1_16, GUARD_INTERVAL_1_16},
118 { DVBFE_DVBT_GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_8},
119 { DVBFE_DVBT_GUARD_INTERVAL_1_4, GUARD_INTERVAL_1_4},
120 { DVBFE_DVBT_GUARD_INTERVAL_AUTO, GUARD_INTERVAL_AUTO},
121 { -1, -1 }
122 };
123
124 static int dvbfe_dvbt_hierarchy_to_kapi[][2] =
125 {
126 { DVBFE_DVBT_HIERARCHY_NONE, HIERARCHY_NONE },
127 { DVBFE_DVBT_HIERARCHY_1, HIERARCHY_1 },
128 { DVBFE_DVBT_HIERARCHY_2, HIERARCHY_2 },
129 { DVBFE_DVBT_HIERARCHY_4, HIERARCHY_4 },
130 { DVBFE_DVBT_HIERARCHY_AUTO, HIERARCHY_AUTO },
131 { -1, -1 }
132 };
133
134
lookupval(int val,int reverse,int table[][2])135 static int lookupval(int val, int reverse, int table[][2])
136 {
137 int i =0;
138
139 while(table[i][0] != -1) {
140 if (!reverse) {
141 if (val == table[i][0]) {
142 return table[i][1];
143 }
144 } else {
145 if (val == table[i][1]) {
146 return table[i][0];
147 }
148 }
149 i++;
150 }
151
152 return -1;
153 }
154
155
156 struct dvbfe_handle {
157 int fd;
158 enum dvbfe_type type;
159 char *name;
160 };
161
dvbfe_open(int adapter,int frontend,int readonly)162 struct dvbfe_handle *dvbfe_open(int adapter, int frontend, int readonly)
163 {
164 char filename[PATH_MAX+1];
165 struct dvbfe_handle *fehandle;
166 int fd;
167 struct dvb_frontend_info info;
168
169 // flags
170 int flags = O_RDWR;
171 if (readonly) {
172 flags = O_RDONLY;
173 }
174
175 // open it (try normal /dev structure first)
176 sprintf(filename, "/dev/dvb/adapter%i/frontend%i", adapter, frontend);
177 if ((fd = open(filename, flags)) < 0) {
178 // if that failed, try a flat /dev structure
179 sprintf(filename, "/dev/dvb%i.frontend%i", adapter, frontend);
180 if ((fd = open(filename, flags)) < 0) {
181 return NULL;
182 }
183 }
184
185 // determine fe type
186 if (ioctl(fd, FE_GET_INFO, &info)) {
187 close(fd);
188 return NULL;
189 }
190
191 // setup structure
192 fehandle = (struct dvbfe_handle*) malloc(sizeof(struct dvbfe_handle));
193 memset(fehandle, 0, sizeof(struct dvbfe_handle));
194 fehandle->fd = fd;
195 switch(info.type) {
196 case FE_QPSK:
197 fehandle->type = DVBFE_TYPE_DVBS;
198 break;
199
200 case FE_QAM:
201 fehandle->type = DVBFE_TYPE_DVBC;
202 break;
203
204 case FE_OFDM:
205 fehandle->type = DVBFE_TYPE_DVBT;
206 break;
207
208 case FE_ATSC:
209 fehandle->type = DVBFE_TYPE_ATSC;
210 break;
211 }
212 fehandle->name = strndup(info.name, sizeof(info.name));
213
214 // done
215 return fehandle;
216 }
217
dvbfe_close(struct dvbfe_handle * fehandle)218 void dvbfe_close(struct dvbfe_handle *fehandle)
219 {
220 close(fehandle->fd);
221 free(fehandle->name);
222 free(fehandle);
223 }
224
dvbfe_get_info(struct dvbfe_handle * fehandle,enum dvbfe_info_mask querymask,struct dvbfe_info * result,enum dvbfe_info_querytype querytype,int timeout)225 extern int dvbfe_get_info(struct dvbfe_handle *fehandle,
226 enum dvbfe_info_mask querymask,
227 struct dvbfe_info *result,
228 enum dvbfe_info_querytype querytype,
229 int timeout)
230 {
231 int returnval = 0;
232 struct dvb_frontend_event kevent;
233 int ok = 0;
234
235 result->name = fehandle->name;
236 result->type = fehandle->type;
237
238 switch(querytype) {
239 case DVBFE_INFO_QUERYTYPE_IMMEDIATE:
240 if (querymask & DVBFE_INFO_LOCKSTATUS) {
241 if (!ioctl(fehandle->fd, FE_READ_STATUS, &kevent.status)) {
242 returnval |= DVBFE_INFO_LOCKSTATUS;
243 }
244 }
245 if (querymask & DVBFE_INFO_FEPARAMS) {
246 if (!ioctl(fehandle->fd, FE_GET_FRONTEND, &kevent.parameters)) {
247 returnval |= DVBFE_INFO_FEPARAMS;
248 }
249 }
250 break;
251
252 case DVBFE_INFO_QUERYTYPE_LOCKCHANGE:
253 {
254 struct pollfd pollfd;
255 pollfd.fd = fehandle->fd;
256 pollfd.events = POLLIN | POLLERR;
257
258 ok = 1;
259 if (poll(&pollfd, 1, timeout) < 0)
260 ok = 0;
261 if (pollfd.revents & POLLERR)
262 ok = 0;
263 if (!(pollfd.revents & POLLIN))
264 ok = 0;
265 }
266
267 if (ok &&
268 ((querymask & DVBFE_INFO_LOCKSTATUS) ||
269 (querymask & DVBFE_INFO_FEPARAMS))) {
270 if (!ioctl(fehandle->fd, FE_GET_EVENT, &kevent)) {
271 if (querymask & DVBFE_INFO_LOCKSTATUS)
272 returnval |= DVBFE_INFO_LOCKSTATUS;
273 if (querymask & DVBFE_INFO_FEPARAMS)
274 returnval |= DVBFE_INFO_FEPARAMS;
275 }
276 }
277 break;
278 }
279
280 if (returnval & DVBFE_INFO_LOCKSTATUS) {
281 result->signal = kevent.status & FE_HAS_SIGNAL ? 1 : 0;
282 result->carrier = kevent.status & FE_HAS_CARRIER ? 1 : 0;
283 result->viterbi = kevent.status & FE_HAS_VITERBI ? 1 : 0;
284 result->sync = kevent.status & FE_HAS_SYNC ? 1 : 0;
285 result->lock = kevent.status & FE_HAS_LOCK ? 1 : 0;
286 }
287
288 if (returnval & DVBFE_INFO_FEPARAMS) {
289 result->feparams.frequency = kevent.parameters.frequency;
290 result->feparams.inversion = lookupval(kevent.parameters.inversion, 1, dvbfe_spectral_inversion_to_kapi);
291 switch(fehandle->type) {
292 case FE_QPSK:
293 result->feparams.u.dvbs.symbol_rate = kevent.parameters.u.qpsk.symbol_rate;
294 result->feparams.u.dvbs.fec_inner =
295 lookupval(kevent.parameters.u.qpsk.fec_inner, 1, dvbfe_code_rate_to_kapi);
296 break;
297
298 case FE_QAM:
299 result->feparams.u.dvbc.symbol_rate = kevent.parameters.u.qam.symbol_rate;
300 result->feparams.u.dvbc.fec_inner =
301 lookupval(kevent.parameters.u.qam.fec_inner, 1, dvbfe_code_rate_to_kapi);
302 result->feparams.u.dvbc.modulation =
303 lookupval(kevent.parameters.u.qam.modulation, 1, dvbfe_dvbc_mod_to_kapi);
304 break;
305
306 case FE_OFDM:
307 result->feparams.u.dvbt.bandwidth =
308 lookupval(kevent.parameters.u.ofdm.bandwidth, 1, dvbfe_dvbt_bandwidth_to_kapi);
309 result->feparams.u.dvbt.code_rate_HP =
310 lookupval(kevent.parameters.u.ofdm.code_rate_HP, 1, dvbfe_code_rate_to_kapi);
311 result->feparams.u.dvbt.code_rate_LP =
312 lookupval(kevent.parameters.u.ofdm.code_rate_LP, 1, dvbfe_code_rate_to_kapi);
313 result->feparams.u.dvbt.constellation =
314 lookupval(kevent.parameters.u.ofdm.constellation, 1, dvbfe_dvbt_const_to_kapi);
315 result->feparams.u.dvbt.transmission_mode =
316 lookupval(kevent.parameters.u.ofdm.transmission_mode, 1, dvbfe_dvbt_transmit_mode_to_kapi);
317 result->feparams.u.dvbt.guard_interval =
318 lookupval(kevent.parameters.u.ofdm.guard_interval, 1, dvbfe_dvbt_guard_interval_to_kapi);
319 result->feparams.u.dvbt.hierarchy_information =
320 lookupval(kevent.parameters.u.ofdm.hierarchy_information, 1, dvbfe_dvbt_hierarchy_to_kapi);
321 break;
322
323 case FE_ATSC:
324 result->feparams.u.atsc.modulation =
325 lookupval(kevent.parameters.u.vsb.modulation, 1, dvbfe_atsc_mod_to_kapi);
326 break;
327 }
328 }
329
330 if (querymask & DVBFE_INFO_BER) {
331 if (!ioctl(fehandle->fd, FE_READ_BER, &result->ber))
332 returnval |= DVBFE_INFO_BER;
333 }
334 if (querymask & DVBFE_INFO_SIGNAL_STRENGTH) {
335 if (!ioctl(fehandle->fd, FE_READ_SIGNAL_STRENGTH, &result->signal_strength))
336 returnval |= DVBFE_INFO_SIGNAL_STRENGTH;
337 }
338 if (querymask & DVBFE_INFO_SNR) {
339 if (!ioctl(fehandle->fd, FE_READ_SNR, &result->snr))
340 returnval |= DVBFE_INFO_SNR;
341 }
342 if (querymask & DVBFE_INFO_UNCORRECTED_BLOCKS) {
343 if (!ioctl(fehandle->fd, FE_READ_UNCORRECTED_BLOCKS, &result->ucblocks))
344 returnval |= DVBFE_INFO_UNCORRECTED_BLOCKS;
345 }
346
347 // done
348 return returnval;
349 }
350
dvbfe_set(struct dvbfe_handle * fehandle,struct dvbfe_parameters * params,int timeout)351 int dvbfe_set(struct dvbfe_handle *fehandle,
352 struct dvbfe_parameters *params,
353 int timeout)
354 {
355 struct dvb_frontend_parameters kparams;
356 int res;
357 struct timeval endtime;
358 fe_status_t status;
359
360 kparams.frequency = params->frequency;
361 kparams.inversion = lookupval(params->inversion, 0, dvbfe_spectral_inversion_to_kapi);
362 switch(fehandle->type) {
363 case FE_QPSK:
364 kparams.u.qpsk.symbol_rate = params->u.dvbs.symbol_rate;
365 kparams.u.qpsk.fec_inner = lookupval(params->u.dvbs.fec_inner, 0, dvbfe_code_rate_to_kapi);
366 break;
367
368 case FE_QAM:
369 kparams.u.qam.symbol_rate = params->u.dvbc.symbol_rate;
370 kparams.u.qam.fec_inner = lookupval(params->u.dvbc.fec_inner, 0, dvbfe_code_rate_to_kapi);
371 kparams.u.qam.modulation = lookupval(params->u.dvbc.modulation, 0, dvbfe_dvbc_mod_to_kapi);
372 break;
373
374 case FE_OFDM:
375 kparams.u.ofdm.bandwidth = lookupval(params->u.dvbt.bandwidth, 0, dvbfe_dvbt_bandwidth_to_kapi);
376 kparams.u.ofdm.code_rate_HP = lookupval(params->u.dvbt.code_rate_HP, 0, dvbfe_code_rate_to_kapi);
377 kparams.u.ofdm.code_rate_LP = lookupval(params->u.dvbt.code_rate_LP, 0, dvbfe_code_rate_to_kapi);
378 kparams.u.ofdm.constellation = lookupval(params->u.dvbt.constellation, 0, dvbfe_dvbt_const_to_kapi);
379 kparams.u.ofdm.transmission_mode =
380 lookupval(params->u.dvbt.transmission_mode, 0, dvbfe_dvbt_transmit_mode_to_kapi);
381 kparams.u.ofdm.guard_interval =
382 lookupval(params->u.dvbt.guard_interval, 0, dvbfe_dvbt_guard_interval_to_kapi);
383 kparams.u.ofdm.hierarchy_information =
384 lookupval(params->u.dvbt.hierarchy_information, 0, dvbfe_dvbt_hierarchy_to_kapi);
385 break;
386
387 case FE_ATSC:
388 kparams.u.vsb.modulation = lookupval(params->u.atsc.modulation, 0, dvbfe_atsc_mod_to_kapi);
389 break;
390
391 default:
392 return -EINVAL;
393 }
394
395 // set it and check for error
396 res = ioctl(fehandle->fd, FE_SET_FRONTEND, &kparams);
397 if (res)
398 return res;
399
400 // 0 => return immediately
401 if (timeout == 0) {
402 return 0;
403 }
404
405 /* calculate timeout */
406 if (timeout > 0) {
407 gettimeofday(&endtime, NULL);
408 timeout *= 1000;
409 endtime.tv_sec += timeout / 1000000;
410 endtime.tv_usec += timeout % 1000000;
411 }
412
413 /* wait for a lock */
414 while(1) {
415 /* has it locked? */
416 if (!ioctl(fehandle->fd, FE_READ_STATUS, &status)) {
417 if (status & FE_HAS_LOCK) {
418 break;
419 }
420 }
421
422 /* check for timeout */
423 if (timeout > 0) {
424 struct timeval curtime;
425 gettimeofday(&curtime, NULL);
426 if ((curtime.tv_sec > endtime.tv_sec) ||
427 ((curtime.tv_sec == endtime.tv_sec) && (curtime.tv_usec >= endtime.tv_usec))) {
428 break;
429 }
430 }
431
432 /* delay for a bit */
433 usleep(100000);
434 }
435
436 /* exit */
437 if (status & FE_HAS_LOCK)
438 return 0;
439 return -ETIMEDOUT;
440 }
441
dvbfe_get_pollfd(struct dvbfe_handle * handle)442 int dvbfe_get_pollfd(struct dvbfe_handle *handle)
443 {
444 return handle->fd;
445 }
446
dvbfe_set_22k_tone(struct dvbfe_handle * fehandle,enum dvbfe_sec_tone_mode tone)447 int dvbfe_set_22k_tone(struct dvbfe_handle *fehandle, enum dvbfe_sec_tone_mode tone)
448 {
449 int ret = 0;
450
451 switch (tone) {
452 case DVBFE_SEC_TONE_OFF:
453 ret = ioctl(fehandle->fd, FE_SET_TONE, SEC_TONE_OFF);
454 break;
455 case DVBFE_SEC_TONE_ON:
456 ret = ioctl(fehandle->fd, FE_SET_TONE, SEC_TONE_ON);
457 break;
458 default:
459 print(verbose, ERROR, 1, "Invalid command !");
460 break;
461 }
462 if (ret == -1)
463 print(verbose, ERROR, 1, "IOCTL failed !");
464
465 return ret;
466 }
467
dvbfe_set_tone_data_burst(struct dvbfe_handle * fehandle,enum dvbfe_sec_mini_cmd minicmd)468 int dvbfe_set_tone_data_burst(struct dvbfe_handle *fehandle, enum dvbfe_sec_mini_cmd minicmd)
469 {
470 int ret = 0;
471
472 switch (minicmd) {
473 case DVBFE_SEC_MINI_A:
474 ret = ioctl(fehandle->fd, FE_DISEQC_SEND_BURST, SEC_MINI_A);
475 break;
476 case DVBFE_SEC_MINI_B:
477 ret = ioctl(fehandle->fd, FE_DISEQC_SEND_BURST, SEC_MINI_B);
478 break;
479 default:
480 print(verbose, ERROR, 1, "Invalid command");
481 break;
482 }
483 if (ret == -1)
484 print(verbose, ERROR, 1, "IOCTL failed");
485
486 return ret;
487 }
488
dvbfe_set_voltage(struct dvbfe_handle * fehandle,enum dvbfe_sec_voltage voltage)489 int dvbfe_set_voltage(struct dvbfe_handle *fehandle, enum dvbfe_sec_voltage voltage)
490 {
491 int ret = 0;
492
493 switch (voltage) {
494 case DVBFE_SEC_VOLTAGE_OFF:
495 ret = ioctl(fehandle->fd, FE_SET_VOLTAGE, SEC_VOLTAGE_OFF);
496 break;
497 case DVBFE_SEC_VOLTAGE_13:
498 ret = ioctl(fehandle->fd, FE_SET_VOLTAGE, SEC_VOLTAGE_13);
499 break;
500 case DVBFE_SEC_VOLTAGE_18:
501 ret = ioctl(fehandle->fd, FE_SET_VOLTAGE, SEC_VOLTAGE_18);
502 break;
503 default:
504 print(verbose, ERROR, 1, "Invalid command");
505 break;
506 }
507 if (ret == -1)
508 print(verbose, ERROR, 1, "IOCTL failed");
509
510 return ret;
511 }
512
dvbfe_set_high_lnb_voltage(struct dvbfe_handle * fehandle,int on)513 int dvbfe_set_high_lnb_voltage(struct dvbfe_handle *fehandle, int on)
514 {
515 switch (on) {
516 case 0:
517 ioctl(fehandle->fd, FE_ENABLE_HIGH_LNB_VOLTAGE, 0);
518 break;
519 default:
520 ioctl(fehandle->fd, FE_ENABLE_HIGH_LNB_VOLTAGE, 1);
521 break;
522 }
523 return 0;
524 }
525
dvbfe_do_dishnetworks_legacy_command(struct dvbfe_handle * fehandle,unsigned int cmd)526 int dvbfe_do_dishnetworks_legacy_command(struct dvbfe_handle *fehandle, unsigned int cmd)
527 {
528 int ret = 0;
529
530 ret = ioctl(fehandle->fd, FE_DISHNETWORK_SEND_LEGACY_CMD, cmd);
531 if (ret == -1)
532 print(verbose, ERROR, 1, "IOCTL failed");
533
534 return ret;
535 }
536
dvbfe_do_diseqc_command(struct dvbfe_handle * fehandle,uint8_t * data,uint8_t len)537 int dvbfe_do_diseqc_command(struct dvbfe_handle *fehandle, uint8_t *data, uint8_t len)
538 {
539 int ret = 0;
540 struct dvb_diseqc_master_cmd diseqc_message;
541
542 if (len > 6)
543 return -EINVAL;
544
545 diseqc_message.msg_len = len;
546 memcpy(diseqc_message.msg, data, len);
547
548 ret = ioctl(fehandle->fd, FE_DISEQC_SEND_MASTER_CMD, &diseqc_message);
549 if (ret == -1)
550 print(verbose, ERROR, 1, "IOCTL failed");
551
552 return ret;
553 }
554
dvbfe_diseqc_read(struct dvbfe_handle * fehandle,int timeout,unsigned char * buf,unsigned int len)555 int dvbfe_diseqc_read(struct dvbfe_handle *fehandle, int timeout, unsigned char *buf, unsigned int len)
556 {
557 struct dvb_diseqc_slave_reply reply;
558 int result;
559
560 if (len > 4)
561 len = 4;
562
563 reply.timeout = timeout;
564 reply.msg_len = len;
565
566 if ((result = ioctl(fehandle->fd, FE_DISEQC_RECV_SLAVE_REPLY, reply)) != 0)
567 return result;
568
569 if (reply.msg_len < len)
570 len = reply.msg_len;
571 memcpy(buf, reply.msg, len);
572
573 return len;
574 }
575