]> git.sesse.net Git - vlc/blob - modules/access/dtv/linux.c
DTV: pass FEC parameters as integers internally
[vlc] / modules / access / dtv / linux.c
1 /**
2  * @file linux.c
3  * @brief Linux DVB API version 5
4  */
5 /*****************************************************************************
6  * Copyright © 2011 Rémi Denis-Courmont
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 License
10  * as published by the Free Software Foundation; either version 2.1
11  * 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
16  * GNU 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 St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <vlc_common.h>
28
29 #include <errno.h>
30 #include <assert.h>
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <unistd.h>
34 #include <sys/ioctl.h>
35 #include <linux/dvb/version.h>
36 #include <linux/dvb/frontend.h>
37 #include <linux/dvb/dmx.h>
38
39 #include "dtv/dtv.h"
40
41 #ifndef O_SEARCH
42 # define O_SEARCH O_RDONLY
43 #endif
44
45 #define DVBv5(minor) \
46         (DVB_API_VERSION > 5 \
47      || (DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= (minor)))
48 #if !DVBv5(0)
49 # error Linux DVB kernel headers version 2.6.28 or later required.
50 #endif
51
52 /** Opens the device directory for the specified DVB adapter */
53 static int dvb_open_adapter (uint8_t adapter)
54 {
55     char dir[20];
56
57     snprintf (dir, sizeof (dir), "/dev/dvb/adapter%"PRIu8, adapter);
58     return open (dir, O_SEARCH|O_DIRECTORY|O_CLOEXEC);
59 }
60
61 /** Opens the DVB device node of the specified type */
62 static int dvb_open_node (int dirfd, uint8_t dev, const char *type, int flags)
63 {
64     int fd;
65     char path[strlen (type) + 4];
66
67     snprintf (path, sizeof (path), "%s%"PRIu8, type, dev);
68     fd = openat (dirfd, path, flags|O_CLOEXEC);
69     if (fd != -1)
70         fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
71     return fd;
72 }
73
74 typedef struct
75 {
76     int vlc;
77     int linux_;
78 } dvb_int_map_t;
79
80 static int icmp (const void *a, const void *b)
81 {
82     int key = (intptr_t)a;
83     const dvb_int_map_t *entry = b;
84     return key - entry->vlc;
85 }
86
87 /** Maps a VLC config integer to a Linux DVB enum value */
88 static int dvb_parse_int (int i, const dvb_int_map_t *map, size_t n, int def)
89 {
90     const void *k = (const void *)(intptr_t)i;
91     const dvb_int_map_t *p = bsearch (k, map, n, sizeof (*map), icmp);
92     return (p != NULL) ? p->linux_ : def;
93 }
94
95 typedef const struct
96 {
97     char vlc[8];
98     int linux_;
99 } dvb_str_map_t;
100
101 static int scmp (const void *a, const void *b)
102 {
103     const char *key = a;
104     dvb_str_map_t *entry = b;
105     return strcmp (key, entry->vlc);
106 }
107
108 /** Maps a VLC config string to a Linux DVB enum value */
109 static int dvb_parse_str (const char *str, const dvb_str_map_t *map, size_t n,
110                           int def)
111 {
112     if (str != NULL)
113     {
114         const dvb_str_map_t *p = bsearch (str, map, n, sizeof (*map), scmp);
115         if (p != NULL)
116             def = p->linux_;
117     }
118     return def;
119 }
120
121 /*** Modulations ***/
122 static int dvb_parse_modulation (const char *str, int def)
123 {
124     static const dvb_str_map_t mods[] =
125     {
126         { "128QAM",  QAM_128  },
127         { "16APSK", APSK_16   },
128         { "16QAM",   QAM_16   },
129         { "16VSB",   VSB_16   },
130         { "256QAM",  QAM_256  },
131         { "32APSK", APSK_32   },
132         { "32QAM",   QAM_32   },
133         { "64QAM",   QAM_64   },
134         { "8PSK",    PSK_8    }, 
135         { "8VSB",    VSB_8    },
136         { "DQPSK", DQPSK      },
137         { "QAM",     QAM_AUTO },
138         { "QPSK",   QPSK      },
139     };
140     return dvb_parse_str (str, mods, sizeof (mods) / sizeof (*mods), def);
141 }
142
143 static int dvb_parse_fec (uint32_t fec)
144 {
145     static const dvb_int_map_t rates[] =
146     {
147         { 0,             FEC_NONE },
148         { VLC_FEC(1,2),  FEC_1_2  },
149         // TODO: 1/3
150         // TODO: 1/4
151         { VLC_FEC(2,3),  FEC_2_3  },
152         { VLC_FEC(3,4),  FEC_3_4  },
153         { VLC_FEC(3,5),  FEC_3_5  },
154         { VLC_FEC(4,5),  FEC_4_5  },
155         { VLC_FEC(5,6),  FEC_5_6  },
156         { VLC_FEC(6,7),  FEC_6_7  },
157         { VLC_FEC(7,8),  FEC_7_8  },
158         { VLC_FEC(8,9),  FEC_8_9  },
159         { VLC_FEC(9,10), FEC_9_10 },
160         { VLC_FEC_AUTO,  FEC_AUTO },
161     };
162     return dvb_parse_int (fec, rates, sizeof (rates) / sizeof (*rates),
163                           FEC_AUTO);
164 }
165
166
167 struct dvb_device
168 {
169     vlc_object_t *obj;
170     int frontend;
171     int demux;
172 #ifndef USE_DMX
173 # define MAX_PIDS 256
174     int dir;
175     uint8_t dev_id;
176     struct
177     {
178         int fd;
179         uint16_t pid;
180     } pids[MAX_PIDS];
181 #endif
182     int ca;
183     struct dvb_frontend_info info;
184     bool budget;
185     //size_t buffer_size;
186 };
187
188 /**
189  * Opens the DVB tuner
190  */
191 dvb_device_t *dvb_open (vlc_object_t *obj, bool tune)
192 {
193     uint8_t adapter = var_InheritInteger (obj, "dvb-adapter");
194     uint8_t device  = var_InheritInteger (obj, "dvb-device");
195
196     int dirfd = dvb_open_adapter (adapter);
197     if (dirfd == -1)
198     {
199         msg_Err (obj, "cannot access adapter %"PRIu8": %m", adapter);
200         return NULL;
201     }
202
203     dvb_device_t *d = malloc (sizeof (*d));
204     if (unlikely(d == NULL))
205     {
206         close (dirfd);
207         return NULL;
208     }
209
210     d->obj = obj;
211     d->frontend = -1;
212     d->ca = -1;
213     d->budget = var_InheritBool (obj, "dvb-budget-mode");
214
215 #ifndef USE_DMX
216     if (d->budget)
217 #endif
218     {
219        d->demux = dvb_open_node (dirfd, device, "demux", O_RDONLY);
220        if (d->demux == -1)
221        {
222            msg_Err (obj, "cannot access demultiplexer: %m");
223            free (d);
224            close (dirfd);
225            return NULL;
226        }
227
228        if (ioctl (d->demux, DMX_SET_BUFFER_SIZE, 1 << 20) < 0)
229            msg_Warn (obj, "cannot expand demultiplexing buffer: %m");
230
231        /* We need to filter at least one PID. The tap for TS demultiplexing
232         * cannot be configured otherwise. So add the PAT. */
233         struct dmx_pes_filter_params param;
234
235         param.pid = d->budget ? 0x2000 : 0x000;
236         param.input = DMX_IN_FRONTEND;
237         param.output = DMX_OUT_TSDEMUX_TAP;
238         param.pes_type = DMX_PES_OTHER;
239         param.flags = DMX_IMMEDIATE_START;
240         if (ioctl (d->demux, DMX_SET_PES_FILTER, &param) < 0)
241         {
242             msg_Err (obj, "cannot setup TS demultiplexer: %m");
243             goto error;
244         }
245 #ifndef USE_DMX
246     }
247     else
248     {
249         d->dir = fcntl (dirfd, F_DUPFD_CLOEXEC);
250         d->dev_id = device;
251
252         for (size_t i = 0; i < MAX_PIDS; i++)
253             d->pids[i].pid = d->pids[i].fd = -1;
254         d->demux = dvb_open_node (d->dir, device, "dvr", O_RDONLY);
255         if (d->demux == -1)
256         {
257             msg_Err (obj, "cannot access DVR: %m");
258             free (d);
259             close (dirfd);
260             return NULL;
261         }
262 #endif
263     }
264
265     if (tune)
266     {
267         d->frontend = dvb_open_node (dirfd, device, "frontend", O_RDWR);
268         if (d->frontend == -1)
269         {
270             msg_Err (obj, "cannot access frontend %"PRIu8
271                      " of adapter %"PRIu8": %m", device, adapter);
272             goto error;
273         }
274
275         if (ioctl (d->frontend, FE_GET_INFO, &d->info) < 0)
276         {
277             msg_Err (obj, "cannot get frontend info: %m");
278             goto error;
279         }
280
281         msg_Dbg (obj, "using frontend: %s", d->info.name);
282         msg_Dbg (obj, " type %u, capabilities 0x%08X", d->info.type,
283                  d->info.caps);
284
285         d->ca = dvb_open_node (dirfd, device, "ca", O_RDWR);
286         if (d->ca == -1)
287             msg_Dbg (obj, "conditional access module not available (%m)");
288
289     }
290     close (dirfd);
291     return d;
292
293 error:
294     close (dirfd);
295     dvb_close (d);
296     return NULL;
297 }
298
299 void dvb_close (dvb_device_t *d)
300 {
301 #ifndef USE_DMX
302     if (!d->budget)
303     {
304         close (d->dir);
305         for (size_t i = 0; i < MAX_PIDS; i++)
306             if (d->pids[i].fd != -1)
307                 close (d->pids[i].fd);
308     }
309 #endif
310     if (d->ca != -1)
311         close (d->ca);
312     if (d->frontend != -1)
313         close (d->frontend);
314     close (d->demux);
315     free (d);
316 }
317
318 /**
319  * Reads TS data from the tuner.
320  * @return number of bytes read, 0 on EOF, -1 if no data (yet).
321  */
322 ssize_t dvb_read (dvb_device_t *d, void *buf, size_t len)
323 {
324     struct pollfd ufd[2];
325     int n;
326
327     ufd[0].fd = d->demux;
328     ufd[0].events = POLLIN;
329     if (d->frontend != -1)
330     {
331         ufd[1].fd = d->frontend;
332         ufd[1].events = POLLIN;
333         n = 2;
334     }
335     else
336         n = 1;
337
338     if (poll (ufd, n, 500 /* FIXME */) < 0)
339         return -1;
340
341     if (d->frontend != -1 && ufd[1].revents)
342     {
343         struct dvb_frontend_event ev;
344
345         if (ioctl (d->frontend, FE_GET_EVENT, &ev) < 0)
346         {
347             if (errno == EOVERFLOW)
348             {
349                 msg_Err (d->obj, "cannot dequeue events fast enough!");
350                 return -1;
351             }
352             msg_Err (d->obj, "cannot dequeue frontend event: %m");
353             return 0;
354         }
355
356         msg_Dbg (d->obj, "frontend status: 0x%02X", (unsigned)ev.status);
357     }
358
359     if (ufd[0].revents)
360     {
361         ssize_t val = read (d->demux, buf, len);
362         if (val == -1 && (errno != EAGAIN && errno != EINTR))
363         {
364             if (errno == EOVERFLOW)
365             {
366                 msg_Err (d->obj, "cannot demux data fast enough!");
367                 return -1;
368             }
369             msg_Err (d->obj, "cannot demux: %m");
370             return 0;
371         }
372         return val;
373     }
374
375     return -1;
376 }
377
378 int dvb_add_pid (dvb_device_t *d, uint16_t pid)
379 {
380     if (d->budget)
381         return 0;
382 #ifdef USE_DMX
383     if (pid == 0 || ioctl (d->demux, DMX_ADD_PID, &pid) >= 0)
384         return 0;
385 #else
386     for (size_t i = 0; i < MAX_PIDS; i++)
387     {
388         if (d->pids[i].pid == pid)
389             return 0;
390         if (d->pids[i].fd != -1)
391             continue;
392
393         int fd = dvb_open_node (d->dir, d->dev_id, "demux", O_RDONLY);
394         if (fd == -1)
395             goto error;
396
397        /* We need to filter at least one PID. The tap for TS demultiplexing
398         * cannot be configured otherwise. So add the PAT. */
399         struct dmx_pes_filter_params param;
400
401         param.pid = pid;
402         param.input = DMX_IN_FRONTEND;
403         param.output = DMX_OUT_TS_TAP;
404         param.pes_type = DMX_PES_OTHER;
405         param.flags = DMX_IMMEDIATE_START;
406         if (ioctl (fd, DMX_SET_PES_FILTER, &param) < 0)
407         {
408             close (fd);
409             goto error;
410         }
411         d->pids[i].fd = fd;
412         d->pids[i].pid = pid;
413         return 0;
414     }
415     errno = EMFILE;
416 error:
417 #endif
418     msg_Err (d->obj, "cannot add PID 0x%04"PRIu16": %m", pid);
419     return -1;
420 }
421
422 void dvb_remove_pid (dvb_device_t *d, uint16_t pid)
423 {
424     if (d->budget)
425         return;
426 #ifdef USE_DMX
427     if (pid != 0)
428         ioctl (d->demux, DMX_REMOVE_PID, &pid);
429 #else
430     for (size_t i = 0; i < MAX_PIDS; i++)
431     {
432         if (d->pids[i].pid == pid)
433         {
434             close (d->pids[i].fd);
435             d->pids[i].pid = d->pids[i].fd = -1;
436             return;
437         }
438     }
439 #endif
440 }
441
442 const delsys_t *dvb_guess_system (dvb_device_t *d)
443 {
444     assert (d->frontend != -1);
445
446     //bool v2 = d->info.caps & FE_CAN_2G_MODULATION;
447
448     switch (d->info.type)
449     {
450         case FE_QPSK: return /*v2 ? &dvbs2 :*/ &dvbs;
451         case FE_QAM:  return &dvbc;
452         case FE_OFDM: return &dvbt;
453         case FE_ATSC: return &atsc;
454     }
455     return NULL;
456 }
457
458 float dvb_get_signal_strength (dvb_device_t *d)
459 {
460     uint16_t strength;
461
462     if (ioctl (d->frontend, FE_READ_SIGNAL_STRENGTH, &strength) < 0)
463         return 0.;
464     return strength / 65535.;
465 }
466
467 float dvb_get_snr (dvb_device_t *d)
468 {
469     uint16_t snr;
470
471     if (ioctl (d->frontend, FE_READ_SNR, &snr) < 0)
472         return 0.;
473     return snr / 65535.;
474 }
475
476 static int dvb_vset_props (dvb_device_t *d, size_t n, va_list ap)
477 {
478     struct dtv_property buf[n], *prop = buf;
479     struct dtv_properties props = { .num = n, .props = buf };
480
481     memset (prop, 0, sizeof (prop));
482
483     while (n > 0)
484     {
485         prop->cmd = va_arg (ap, uint32_t);
486         prop->u.data = va_arg (ap, uint32_t);
487         msg_Dbg (d->obj, "setting property %"PRIu32" to %"PRIu32,
488                  prop->cmd, prop->u.data);
489         prop++;
490         n--;
491     }
492
493     if (ioctl (d->frontend, FE_SET_PROPERTY, &props) < 0)
494     {
495         msg_Err (d->obj, "cannot set frontend tuning parameters: %m");
496         return -1;
497     }
498     return 0;
499 }
500
501 static int dvb_set_props (dvb_device_t *d, size_t n, ...)
502 {
503     va_list ap;
504     int ret;
505
506     va_start (ap, n);
507     ret = dvb_vset_props (d, n, ap);
508     va_end (ap);
509     return ret;
510 }
511
512 static int dvb_set_prop (dvb_device_t *d, uint32_t prop, uint32_t val)
513 {
514     return dvb_set_props (d, 1, prop, val);
515 }
516
517 int dvb_set_inversion (dvb_device_t *d, int v)
518 {
519     switch (v)
520     {
521         case 0:  v = INVERSION_OFF;  break;
522         case 1:  v = INVERSION_ON;   break;
523         default: v = INVERSION_AUTO; break;
524     }
525     return dvb_set_prop (d, DTV_INVERSION, v);
526 }
527
528 int dvb_tune (dvb_device_t *d)
529 {
530     return dvb_set_prop (d, DTV_TUNE, 0 /* dummy */);
531 }
532
533
534 /*** DVB-C ***/
535 int dvb_set_dvbc (dvb_device_t *d, uint32_t freq, const char *modstr,
536                   uint32_t srate, uint32_t fec)
537 {
538     unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
539     fec = dvb_parse_fec (fec);
540
541     return dvb_set_props (d, 6, DTV_CLEAR, 0,
542                           DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_AC,
543                           DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
544                           DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec);
545 }
546
547
548 /*** DVB-S ***/
549 static unsigned dvb_parse_polarization (char pol)
550 {
551     static const dvb_int_map_t tab[5] = {
552         { 0,   SEC_VOLTAGE_OFF },
553         { 'H', SEC_VOLTAGE_18  },
554         { 'L', SEC_VOLTAGE_18  },
555         { 'R', SEC_VOLTAGE_13  },
556         { 'V', SEC_VOLTAGE_13  },
557     };
558     return dvb_parse_int (pol, tab, 5, SEC_VOLTAGE_OFF);
559 }
560
561 int dvb_set_sec (dvb_device_t *d, uint32_t freq, char pol,
562                  uint32_t lowf, uint32_t highf, uint32_t switchf)
563 {
564     /* Always try to configure high voltage, but only warn on enable failure */
565     int val = var_InheritBool (d->obj, "dvb-high-voltage");
566     if (ioctl (d->frontend, FE_ENABLE_HIGH_LNB_VOLTAGE, &val) < 0 && val)
567         msg_Err (d->obj, "cannot enable high LNB voltage: %m");
568
569     /* Windows BDA exposes a higher-level API covering LNB oscillators.
570      * So lets pretend this is platform-specific stuff and do it here. */
571     if (!lowf)
572     {   /* Default oscillator frequencies */
573         static const struct
574         {
575              uint16_t min, max, low, high;
576         } tab[] =
577         {    /*  min    max    low   high */
578              { 10700, 13250,  9750, 10600 }, /* Ku band */
579              {  4500,  4800,  5950,     0 }, /* C band (high) */
580              {  3400,  4200,  5150,     0 }, /* C band (low) */
581              {  2500,  2700,  3650,     0 }, /* S band */
582              {   950,  2150,     0,     0 }, /* adjusted IF (L band) */
583         };
584         uint_fast16_t mhz = freq / 1000;
585
586         for (size_t i = 0; i < sizeof (tab) / sizeof (tab[0]); i++)
587              if (mhz >= tab[i].min && mhz <= tab[i].max)
588              {
589                  lowf = tab[i].low * 1000;
590                  highf = tab[i].high * 1000;
591                  goto known;
592              }
593
594         msg_Err (d->obj, "no known band for frequency %u kHz", freq);
595 known:
596         msg_Dbg (d->obj, "selected LNB low: %u kHz, LNB high: %u kHz",
597                  lowf, highf);
598     }
599
600     /* Use high oscillator frequency? */
601     bool high = highf != 0 && freq > switchf;
602
603     freq -= high ? highf : lowf;
604     if ((int32_t)freq < 0)
605         freq *= -1;
606     assert (freq < 0x7fffffff);
607
608     int tone;
609     switch (var_InheritInteger (d->obj, "dvb-tone"))
610     {
611         case 0:  tone = SEC_TONE_OFF; break;
612         case 1:  tone = SEC_TONE_ON;  break;
613         default: tone = high ? SEC_TONE_ON : SEC_TONE_OFF;
614     }
615
616     /*** LNB selection / DiSEqC ***/
617     unsigned voltage = dvb_parse_polarization (pol);
618     if (dvb_set_props (d, 2, DTV_TONE, SEC_TONE_OFF, DTV_VOLTAGE, voltage))
619         return -1;
620
621     unsigned satno = var_InheritInteger (d->obj, "dvb-satno");
622     if (satno > 0)
623     {
624         /* DiSEqC 1.0 */
625 #undef msleep /* we know what we are doing! */
626         struct dvb_diseqc_master_cmd cmd;
627
628         satno = (satno - 1) & 3;
629         cmd.msg[0] = 0xE0; /* framing: master, no reply, 1st TX */
630         cmd.msg[1] = 0x10; /* address: all LNB/switch */
631         cmd.msg[2] = 0x38; /* command: Write Port Group 0 */
632         cmd.msg[3] = 0xF0  /* data[0]: clear all bits */
633                    | (satno << 2) /* LNB (A, B, C or D) */
634                    | ((voltage == SEC_VOLTAGE_18) << 1) /* polarization */
635                    | (tone == SEC_TONE_ON); /* option */
636         cmd.msg[4] = cmd.msg[5] = 0; /* unused */
637         msleep (15000); /* wait 15 ms before DiSEqC command */
638         if (ioctl (d->frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd) < 0)
639         {
640             msg_Err (d->obj, "cannot send DiSEqC command: %m");
641             return -1;
642         }
643         msleep (54000 + 15000);
644
645         /* Mini-DiSEqC */
646         satno &= 1;
647         if (ioctl (d->frontend, FE_DISEQC_SEND_BURST,
648                    satno ? SEC_MINI_B : SEC_MINI_A) < 0)
649         {
650             msg_Err (d->obj, "cannot send Mini-DiSEqC tone burst: %m");
651             return -1;
652         }
653         msleep (15000);
654     }
655
656     /* Continuous tone (to select high oscillator frequency) */
657     return dvb_set_props (d, 2, DTV_FREQUENCY, freq, DTV_TONE, tone);
658 }
659
660 int dvb_set_dvbs (dvb_device_t *d, uint32_t freq, uint32_t srate, uint32_t fec)
661 {
662     fec = dvb_parse_fec (fec);
663
664     return dvb_set_props (d, 5, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBS,
665                           DTV_FREQUENCY, freq, DTV_SYMBOL_RATE, srate,
666                           DTV_INNER_FEC, fec);
667 }
668
669 int dvb_set_dvbs2 (dvb_device_t *d, uint32_t freq, const char *modstr,
670                    uint32_t srate, uint32_t fec, int pilot, int rolloff)
671 {
672     unsigned mod = dvb_parse_modulation (modstr, QPSK);
673     fec = dvb_parse_fec (fec);
674
675     switch (pilot)
676     {
677         case 0:  pilot = PILOT_OFF;  break;
678         case 1:  pilot = PILOT_ON;   break;
679         default: pilot = PILOT_AUTO; break;
680     }
681
682     switch (rolloff)
683     {
684         case 20: rolloff = ROLLOFF_20;  break;
685         case 25: rolloff = ROLLOFF_25;  break;
686         case 35: rolloff = ROLLOFF_35;  break;
687         default: rolloff = PILOT_AUTO; break;
688     }
689
690     return dvb_set_props (d, 8, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBS2,
691                           DTV_FREQUENCY, freq, DTV_MODULATION, mod,
692                           DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec,
693                           DTV_PILOT, pilot, DTV_ROLLOFF, rolloff);
694 }
695
696
697 /*** DVB-T ***/
698 static int dvb_parse_transmit_mode (int i)
699 {
700     static const dvb_int_map_t tab[] = {
701         { -1, TRANSMISSION_MODE_AUTO },
702         {  2, TRANSMISSION_MODE_2K   },
703 #if DVBv5(1)
704         {  4, TRANSMISSION_MODE_4K   },
705 #endif
706         {  8, TRANSMISSION_MODE_8K   },
707 #if 0
708         { 16, TRANSMISSION_MODE_16K  },
709         { 32, TRANSMISSION_MODE_32K  },
710 #endif
711     };
712     return dvb_parse_int (i, tab, sizeof (tab) / sizeof (*tab),
713                           TRANSMISSION_MODE_AUTO);
714 }
715
716 static int dvb_parse_guard (uint32_t guard)
717 {
718     static const dvb_int_map_t tab[] = {
719       /*{ VLC_GUARD(1,128),  GUARD_INTERVAL_1_128 },*/
720         { VLC_GUARD(1,16),   GUARD_INTERVAL_1_16 },
721         { VLC_GUARD(1,32),   GUARD_INTERVAL_1_32 },
722         { VLC_GUARD(1,4),    GUARD_INTERVAL_1_4 },
723         { VLC_GUARD(1,8),    GUARD_INTERVAL_1_8 },
724       /*{ VLC_GUARD(19,128), GUARD_INTERVAL_19_128 },*/
725       /*{ VLC_GUARD(9,256),  GUARD_INTERVAL_9_256 },*/
726         { VLC_GUARD_AUTO,    GUARD_INTERVAL_AUTO },
727     };
728     return dvb_parse_int (guard, tab, sizeof (tab) / sizeof (*tab),
729                           GUARD_INTERVAL_AUTO);
730 }
731
732 static int dvb_parse_hierarchy (int i)
733 {
734     static const dvb_int_map_t tab[] = {
735         { HIERARCHY_AUTO, -1 },
736         { HIERARCHY_NONE,  0 },
737         { HIERARCHY_1,     1 },
738         { HIERARCHY_2,     2 },
739         { HIERARCHY_4,     4 },
740     };
741     return dvb_parse_int (i, tab, sizeof (tab) / sizeof (*tab),
742                           HIERARCHY_AUTO);
743 }
744
745 int dvb_set_dvbt (dvb_device_t *d, uint32_t freq, const char *modstr,
746                   uint32_t fec_hp, uint32_t fec_lp, uint32_t bandwidth,
747                   int transmit_mode, uint32_t guard, int hierarchy)
748 {
749     uint32_t mod = dvb_parse_modulation (modstr, QAM_AUTO);
750     fec_hp = dvb_parse_fec (fec_hp);
751     fec_lp = dvb_parse_fec (fec_lp);
752     bandwidth *= 1000000;
753     transmit_mode = dvb_parse_transmit_mode (transmit_mode);
754     guard = dvb_parse_guard (guard);
755     hierarchy = dvb_parse_hierarchy (hierarchy);
756
757     return dvb_set_props (d, 10, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBT,
758                           DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
759                           DTV_CODE_RATE_HP, fec_hp, DTV_CODE_RATE_LP, fec_lp,
760                           DTV_BANDWIDTH_HZ, bandwidth,
761                           DTV_TRANSMISSION_MODE, transmit_mode,
762                           DTV_GUARD_INTERVAL, guard,
763                           DTV_HIERARCHY, hierarchy);
764 }
765
766
767 /*** ATSC ***/
768 int dvb_set_atsc (dvb_device_t *d, uint32_t freq, const char *modstr)
769 {
770     unsigned mod = dvb_parse_modulation (modstr, VSB_8);
771
772     return dvb_set_props (d, 4, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_ATSC,
773                           DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod);
774 }
775
776 int dvb_set_cqam (dvb_device_t *d, uint32_t freq, const char *modstr)
777 {
778     unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
779
780     return dvb_set_props (d, 4, DTV_CLEAR, 0,
781                           DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_B,
782                           DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod);
783 }