1 /*
2  *  TV Input - DVB - Support/Conversion functions
3  *  Copyright (C) 2007 Andreas Öman
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "tvheadend.h"
30 #include "dvb.h"
31 #include "dvb_charset_tables.h"
32 #include "input.h"
33 #include "intlconv.h"
34 #include "lang_str.h"
35 #include "settings.h"
36 
37 static int convert_iso_8859[16] = {
38   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 11, 12, 13
39 };
40 #define convert_utf8   14
41 #define convert_iso6937 15
42 #define convert_ucs2 16
43 #define convert_gb   17
44 
conv_gb(const uint8_t * src,size_t srclen,char * dst,size_t * dstlen)45 static inline size_t conv_gb(const uint8_t *src, size_t srclen,
46                              char *dst, size_t *dstlen)
47 {
48     ssize_t len;
49     len = intlconv_to_utf8(dst, *dstlen, "gb2312", (char *)src, srclen);
50     if (len < 0 || len > *dstlen)
51       return -1;
52     *dstlen -= len;
53     return 0;
54 }
55 
encode_utf8(unsigned int c,char * outb,int outleft)56 static inline int encode_utf8(unsigned int c, char *outb, int outleft)
57 {
58   if (c <= 0x7F && outleft >= 1) {
59     *outb = c;
60     return 1;
61   } else if (c <= 0x7FF && outleft >=2) {
62     *outb++ = ((c >>  6) & 0x1F) | 0xC0;
63     *outb++ = ( c        & 0x3F) | 0x80;
64     return 2;
65   } else if (c <= 0xFFFF && outleft >= 3) {
66     *outb++ = ((c >> 12) & 0x0F) | 0xE0;
67     *outb++ = ((c >>  6) & 0x3F) | 0x80;
68     *outb++ = ( c        & 0x3F) | 0x80;
69     return 3;
70   } else if (c <= 0x10FFFF && outleft >= 4) {
71     *outb++ = ((c >> 18) & 0x07) | 0xF0;
72     *outb++ = ((c >> 12) & 0x3F) | 0x80;
73     *outb++ = ((c >>  6) & 0x3F) | 0x80;
74     *outb++ = ( c        & 0x3F) | 0x80;
75     return 4;
76   } else {
77     return -1;
78   }
79 }
80 
conv_UCS2(const uint8_t * src,size_t srclen,char * dst,size_t * dstlen)81 static inline size_t conv_UCS2(const uint8_t *src, size_t srclen,char *dst, size_t *dstlen)
82 {
83   while (srclen>0 && (*dstlen)>0){
84     uint16_t uc = *src<<8|*(src+1);
85     if (uc >= 0xe080 && uc <= 0xe09f) {
86       // codes 0xe080 - 0xe09f (control codes) are ignored except CR/LF
87       if (uc == 0xe08a) {
88         *dst = '\n';
89         (*dstlen)--;
90         dst++;
91       }
92     } else {
93       int len = encode_utf8(uc, dst, *dstlen);
94       if (len == -1) {
95         errno = E2BIG;
96         return -1;
97       } else {
98         (*dstlen) -= len;
99         dst += len;
100       }
101     }
102     srclen-=2;
103     src+=2;
104   }
105   if (srclen>0) {
106     errno = E2BIG;
107     return -1;
108   }
109   return 0;
110 }
111 
conv_utf8(const uint8_t * src,size_t srclen,char * dst,size_t * dstlen)112 static inline size_t conv_utf8(const uint8_t *src, size_t srclen,
113                                char *dst, size_t *dstlen)
114 {
115   while (srclen>0 && (*dstlen)>0) {
116     *dst = (char) *src;
117     srclen--; (*dstlen)--;
118     src++; dst++;
119   }
120   if (srclen>0) {
121     errno = E2BIG;
122     return -1;
123   }
124   return 0;
125 }
126 
conv_8859(int conv,const uint8_t * src,size_t srclen,char * dst,size_t * dstlen)127 static inline size_t conv_8859(int conv,
128                               const uint8_t *src, size_t srclen,
129                               char *dst, size_t *dstlen)
130 {
131   uint16_t *table = conv_8859_table[conv];
132 
133   while (srclen>0 && (*dstlen)>0) {
134     uint8_t c = *src;
135     if (c <= 0x7f) {
136       // lower half of iso-8859-* is identical to utf-8
137       *dst = (char) *src;
138       (*dstlen)--;
139       dst++;
140     } else if (c <= 0x9f) {
141       // codes 0x80 - 0x9f (control codes) are ignored except CR/LF
142       if (c == 0x8a) {
143         *dst = '\n';
144         (*dstlen)--;
145         dst++;
146       }
147     } else {
148       // map according to character table, skipping
149       // unmapped chars (value 0 in the table)
150       uint16_t uc = table[c-0xa0];
151       if (uc != 0) {
152         int len = encode_utf8(uc, dst, *dstlen);
153         if (len == -1) {
154           errno = E2BIG;
155           return -1;
156         } else {
157           (*dstlen) -= len;
158           dst += len;
159         }
160       }
161     }
162     srclen--;
163     src++;
164   }
165   if (srclen>0) {
166     errno = E2BIG;
167     return -1;
168   }
169   return 0;
170 }
171 
conv_6937(const uint8_t * src,size_t srclen,char * dst,size_t * dstlen)172 static inline size_t conv_6937(const uint8_t *src, size_t srclen,
173                               char *dst, size_t *dstlen)
174 {
175   while (srclen>0 && (*dstlen)>0) {
176     uint8_t c = *src;
177     if (c <= 0x7f) {
178       // lower half of iso6937 is identical to utf-8
179       *dst = (char) *src;
180       (*dstlen)--;
181       dst++;
182     } else if (c <= 0x9f) {
183       // codes 0x80 - 0x9f (control codes) are ignored except CR/LF
184       if (c == 0x8a) {
185         *dst = '\n';
186         (*dstlen)--;
187         dst++;
188       }
189     } else {
190       uint16_t uc;
191       if (c >= 0xc0 && c <= 0xcf) {
192         // map two-byte sequence, skipping illegal combinations.
193         if (srclen<2) {
194           errno = EINVAL;
195           return -1;
196         }
197         srclen--;
198         src++;
199         uint8_t c2 = *src;
200         if (c2 == 0x20) {
201           uc = iso6937_lone_accents[c-0xc0];
202         } else if (c2 >= 0x41 && c2 <= 0x5a) {
203           uc = iso6937_multi_byte[c-0xc0][c2-0x41];
204         } else if (c2 >= 0x61 && c2 <= 0x7a) {
205           uc = iso6937_multi_byte[c-0xc0][c2-0x61+26];
206         } else {
207           uc = 0;
208         }
209       } else {
210         // map according to single character table, skipping
211         // unmapped chars (value 0 in the table)
212         uc = iso6937_single_byte[c-0xa0];
213       }
214       if (uc != 0) {
215         int len = encode_utf8(uc, dst, *dstlen);
216         if (len == -1) {
217           errno = E2BIG;
218           return -1;
219         } else {
220           (*dstlen) -= len;
221           dst += len;
222         }
223       }
224     }
225     srclen--;
226     src++;
227   }
228   if (srclen>0) {
229     errno = E2BIG;
230     return -1;
231   }
232   return 0;
233 }
234 
dvb_convert(int conv,const uint8_t * src,size_t srclen,char * dst,size_t * dstlen)235 static inline size_t dvb_convert(int conv,
236                           const uint8_t *src, size_t srclen,
237                           char *dst, size_t *dstlen)
238 {
239   switch (conv) {
240     case convert_utf8: return conv_utf8(src, srclen, dst, dstlen);
241     case convert_iso6937: return conv_6937(src, srclen, dst, dstlen);
242     case convert_gb: return conv_gb(src,srclen,dst,dstlen);
243     case convert_ucs2:return conv_UCS2(src,srclen,dst,dstlen);
244     default: return conv_8859(conv, src, srclen, dst, dstlen);
245   }
246 }
247 
248 /*
249  * DVB String conversion according to EN 300 468, Annex A
250  * Not all character sets are supported, but it should cover most of them
251  */
252 
253 int
dvb_get_string(char * dst,size_t dstlen,const uint8_t * src,size_t srclen,const char * dvb_charset,dvb_string_conv_t * conv)254 dvb_get_string
255   (char *dst, size_t dstlen, const uint8_t *src, size_t srclen,
256    const char *dvb_charset, dvb_string_conv_t *conv)
257 {
258   int ic = -1;
259   size_t len, outlen;
260   int i, auto_pl_charset = 0;
261 
262   if(srclen < 1) {
263     *dst = 0;
264     return 0;
265   }
266 
267   /* Check custom conversion */
268   while (conv && conv->func) {
269     if (conv->type == src[0])
270       return conv->func(dst, &dstlen, src, srclen);
271     conv++;
272   }
273 
274   // check for automatic polish charset detection
275   if (dvb_charset && strcmp("AUTO_POLISH", dvb_charset) == 0) {
276     auto_pl_charset = 1;
277     dvb_charset = NULL;
278   }
279 
280   // automatic charset detection
281   switch(src[0]) {
282   case 0:
283     *dst = 0; // empty string (confirmed!)
284     return 0;
285 
286   case 0x01 ... 0x0b:
287     if (auto_pl_charset && (src[0] + 4) == 5)
288       ic = convert_iso6937;
289     else
290       ic = convert_iso_8859[src[0] + 4];
291     src++; srclen--;
292     break;
293 
294   case 0x0c ... 0x0f:
295     src++; srclen--;
296     break;
297 
298   case 0x10: /* Table A.4 */
299     if(srclen < 3 || src[1] != 0 || src[2] == 0 || src[2] > 0x0f)
300       return -1;
301 
302     ic = convert_iso_8859[src[2]];
303     src+=3; srclen-=3;
304     break;
305 
306   case 0x11:
307     ic = convert_ucs2;
308     src++; srclen--;
309     break;
310 
311   case 0x13:
312     ic = convert_gb;
313     src++; srclen--;
314     break;
315 
316   case 0x12:
317     src++; srclen--;
318     break;
319 
320   case 0x14:
321     ic = convert_ucs2;
322     src++; srclen--;
323     break;
324 
325   case 0x15:
326     ic = convert_utf8;
327     src++; srclen--;
328     break;
329 
330   case 0x16 ... 0x1f:
331     src++; srclen--;
332     break;
333 
334   default:
335     if (auto_pl_charset)
336       ic = convert_iso_8859[2];
337     else
338       ic = convert_iso6937;
339     break;
340   }
341 
342   // manual charset override
343   if (dvb_charset != NULL && dvb_charset[0] != 0) {
344     if (!strcmp(dvb_charset, "AUTO")) {
345       // ignore
346     } else if (sscanf(dvb_charset, "ISO-8859-%d", &i) > 0 && i > 0 && i < 16) {
347       ic = convert_iso_8859[i];
348     } else if (!strcmp(dvb_charset, "ISO-6937")) {
349       ic = convert_iso6937;
350     } else if (!strcmp(dvb_charset, "UTF-8")) {
351       ic = convert_utf8;
352     } else if (!strcmp(dvb_charset, "GB2312")) {
353       ic = convert_gb;
354     } else if (!strcmp(dvb_charset, "UCS2")) {
355       ic = convert_ucs2;
356     }
357   }
358 
359   if(srclen < 1) {
360     *dst = 0;
361     return 0;
362   }
363 
364   if(ic == -1)
365     return -1;
366 
367   outlen = dstlen - 1;
368 
369   if (dvb_convert(ic, src, srclen, dst, &outlen) == -1)
370     return -1;
371 
372   len = dstlen - outlen - 1;
373   dst[len] = 0;
374   return 0;
375 }
376 
377 
378 int
dvb_get_string_with_len(char * dst,size_t dstlen,const uint8_t * buf,size_t buflen,const char * dvb_charset,dvb_string_conv_t * conv)379 dvb_get_string_with_len(char *dst, size_t dstlen,
380 			const uint8_t *buf, size_t buflen, const char *dvb_charset,
381       dvb_string_conv_t *conv)
382 {
383   int l = buf[0];
384 
385   if(l + 1 > buflen)
386     return -1;
387 
388   if(dvb_get_string(dst, dstlen, buf + 1, l, dvb_charset, conv))
389     return -1;
390 
391   return l + 1;
392 }
393 
394 
395 /**
396  *
397  */
398 void
atsc_utf16_to_utf8(const uint8_t * src,int len,char * buf,int buflen)399 atsc_utf16_to_utf8(const uint8_t *src, int len, char *buf, int buflen)
400 {
401   int i, c, r;
402 
403   for(i = 0; i < len; i++) {
404     c = (src[i * 2 + 0] << 8) | src[i * 2 + 1];
405 
406     if(buflen >= 7) {
407       r = put_utf8(buf, c);
408       buf += r;
409       buflen -= r;
410     }
411   }
412   *buf = 0;
413 }
414 
415 lang_str_t *
atsc_get_string(const uint8_t * src,size_t srclen)416 atsc_get_string
417   (const uint8_t *src, size_t srclen)
418 {
419   lang_str_t *ls = NULL;
420   int i, j, stringcount, segmentcount;
421   int compressiontype, mode, bytecount;
422   char langcode[4];
423   char buf[256];
424 
425   stringcount = src[0];
426   tvhtrace(LS_MPEGTS, "atsc-str: %d strings", stringcount);
427 
428   src++;
429   srclen--;
430 
431   langcode[3] = '\0';
432 
433   for (i = 0; i < stringcount && srclen >= 4; i++) {
434     langcode[0]  = src[0];
435     langcode[1]  = src[1];
436     langcode[2]  = src[2];
437     segmentcount = src[3];
438 
439     tvhtrace(LS_MPEGTS, "atsc-str:  %d: lang '%s', segments %d", i, langcode, segmentcount);
440 
441     src    += 4;
442     srclen -= 4;
443 
444     for (j = 0; j < segmentcount && srclen >= 3; j++) {
445       compressiontype = src[0];
446       mode            = src[1];
447       bytecount       = src[2];
448 
449       src    += 3;
450       srclen -= 3;
451 
452       if (bytecount > srclen)
453         return ls;
454 
455       if (mode == 0 && compressiontype == 0) {
456         tvhtrace(LS_MPEGTS, "atsc-str:    %d: comptype 0x%02x, mode 0x%02x, %d bytes: '%.*s'",
457                  j, compressiontype, mode, bytecount, bytecount, src);
458         memcpy(buf, src, bytecount);
459         buf[bytecount] = '\0';
460         if (ls == NULL)
461           ls = lang_str_create();
462         lang_str_append(ls, buf, langcode);
463       } else {
464         tvhtrace(LS_MPEGTS, "atsc-str:    %d: comptype 0x%02x, mode 0x%02x, %d bytes",
465                  j, compressiontype, mode, bytecount);
466       }
467 
468       /* FIXME: read compressed bytes */
469       src += bytecount; srclen -= bytecount; // skip for now
470     }
471   }
472 
473   return ls;
474 }
475 
476 /*
477  *
478  */
479 
480 static struct strtab dvb_timezone_strtab[] = {
481   { N_("UTC"),        0 },
482   { N_("Local (server) time"), 1 },
483   { N_("UTC- 1"),    -1*60 },
484   { N_("UTC- 2"),    -2*60 },
485   { N_("UTC- 2:30"), -2*60-30 },
486   { N_("UTC- 3"),    -3*60 },
487   { N_("UTC- 3:30"), -3*60-30 },
488   { N_("UTC- 4"),    -4*60 },
489   { N_("UTC- 4:30"), -4*60-30 },
490   { N_("UTC- 5"),    -5*60 },
491   { N_("UTC- 6"),    -6*60 },
492   { N_("UTC- 7"),    -7*60 },
493   { N_("UTC- 8"),    -8*60 },
494   { N_("UTC- 9"),    -9*60 },
495   { N_("UTC- 9:30"), -9*60-30 },
496   { N_("UTC-10"),    -10*60 },
497   { N_("UTC-11"),    -11*60 },
498   { N_("UTC+ 1"),     1*60 },
499   { N_("UTC+ 2"),     2*60 },
500   { N_("UTC+ 3"),     3*60 },
501   { N_("UTC+ 4"),     4*60 },
502   { N_("UTC+ 4:30"),  4*60+30 },
503   { N_("UTC+ 5"),     5*60 },
504   { N_("UTC+ 5:30"),  5*60+30 },
505   { N_("UTC+ 5:45"),  5*60+45 },
506   { N_("UTC+ 6"),     6*60 },
507   { N_("UTC+ 6:30"),  6*60+30 },
508   { N_("UTC+ 7"),     7*60 },
509   { N_("UTC+ 8"),     8*60 },
510   { N_("UTC+ 8:45"),  8*60+45 },
511   { N_("UTC+ 9"),     9*60 },
512   { N_("UTC+ 9:30"),  9*60+30 },
513   { N_("UTC+10"),     10*60 },
514   { N_("UTC+10:30"),  10*60+30 },
515   { N_("UTC+11"),     11*60 },
516   { N_("UTC+12"),     12*60 },
517   { N_("UTC+12:45"),  12*60+45 },
518   { N_("UTC+13"),     13*60 },
519   { N_("UTC+14"),     14*60 }
520 };
521 
522 htsmsg_t *
dvb_timezone_enum(void * p,const char * lang)523 dvb_timezone_enum ( void *p, const char *lang )
524 {
525   return strtab2htsmsg(dvb_timezone_strtab, 1, lang);
526 }
527 
528 /*
529  * DVB time and date functions
530  */
531 
532 time_t
dvb_convert_date(const uint8_t * dvb_buf,int tmzone)533 dvb_convert_date(const uint8_t *dvb_buf, int tmzone)
534 {
535   int i;
536   int year, month, day, hour, min, sec;
537   long int mjd;
538   struct tm dvb_time;
539 
540   mjd = (dvb_buf[0] & 0xff) << 8;
541   mjd += (dvb_buf[1] & 0xff);
542   hour = bcdtoint(dvb_buf[2] & 0xff);
543   min = bcdtoint(dvb_buf[3] & 0xff);
544   sec = bcdtoint(dvb_buf[4] & 0xff);
545   /*
546    * Use the routine specified in ETSI EN 300 468 V1.4.1,
547    * "Specification for Service Information in Digital Video Broadcasting"
548    * to convert from Modified Julian Date to Year, Month, Day.
549    */
550   year = (int) ((mjd - 15078.2) / 365.25);
551   month = (int) ((mjd - 14956.1 - (int) (year * 365.25)) / 30.6001);
552   day = mjd - 14956 - (int) (year * 365.25) - (int) (month * 30.6001);
553   if (month == 14 || month == 15)
554     i = 1;
555   else
556     i = 0;
557   year += i;
558   month = month - 1 - i * 12;
559 
560   dvb_time.tm_sec = sec;
561   dvb_time.tm_min = min;
562   dvb_time.tm_hour = hour;
563   dvb_time.tm_mday = day;
564   dvb_time.tm_mon = month - 1;
565   dvb_time.tm_year = year;
566   dvb_time.tm_isdst = -1;
567   dvb_time.tm_wday = 0;
568   dvb_time.tm_yday = 0;
569 
570   if (tmzone == 0) /* UTC */
571     return timegm(&dvb_time);
572   if (tmzone == 1) /* Local time */
573     return mktime(&dvb_time);
574 
575   /* apply offset */
576   return timegm(&dvb_time) - tmzone * 60;
577 }
578 
579 static time_t _gps_leap_seconds[17] = {
580 	362793600,
581 	394329600,
582 	425865600,
583 	489024000,
584 	567993600,
585 	631152000,
586 	662688000,
587 	709948800,
588 	741484800,
589 	773020800,
590 	820454400,
591 	867715200,
592 	915148800,
593 	1136073600,
594 	1230768000,
595 	1341100800,
596 	1435708800,
597 };
598 
599 time_t
atsc_convert_gpstime(uint32_t gpstime)600 atsc_convert_gpstime(uint32_t gpstime)
601 {
602   int i;
603   time_t out = gpstime + 315964800; // Add Unix - GPS epoch
604 
605   for (i = (sizeof(_gps_leap_seconds)/sizeof(time_t)) - 1; i >= 0; i--) {
606     if (out > _gps_leap_seconds[i]) {
607       out -= i+1;
608       break;
609     }
610   }
611 
612   return out;
613 }
614 
615 /*
616  * DVB API helpers
617  */
618 #if ENABLE_MPEGTS_DVB
619 
620 htsmsg_t *satellites;
621 
622 #define dvb_str2val(p)\
623 const char *dvb_##p##2str (int p)         { return val2str(p, p##tab); }\
624 int         dvb_str2##p   (const char *p) { return str2val(p, p##tab); }
625 
626 #define DVB_EOD -10	/* end-of-data */
627 
dvb_common2str(int p)628 static const char *dvb_common2str(int p)
629 {
630   if (p == 0)
631     return "NONE";
632   if (p == 1)
633     return "AUTO";
634   return NULL;
635 }
636 
dvb_str2common(const char * p)637 static int dvb_str2common(const char *p)
638 {
639   if (strcmp(p, "NONE") == 0)
640     return 0;
641   if (strcmp(p, "AUTO") == 0)
642     return 1;
643   return DVB_EOD;
644 }
645 
dvb_verify(int val,int * table)646 static int dvb_verify(int val, int *table)
647 {
648   while (*table != DVB_EOD) {
649     if (val == *table)
650       return val;
651     table++;
652   }
653   return 0; /* NONE */
654 }
655 
dvb_rolloff2str(int p)656 const char *dvb_rolloff2str(int p)
657 {
658   static __thread char buf[16];
659   const char *res = dvb_common2str(p);
660   if (res)
661     return res;
662   sprintf(buf, "%02i", p / 10);
663   return buf;
664 }
665 
dvb_str2rolloff(const char * p)666 int dvb_str2rolloff(const char *p)
667 {
668   static int rolloff_table[] = {
669     DVB_ROLLOFF_20,
670     DVB_ROLLOFF_25,
671     DVB_ROLLOFF_35,
672     DVB_EOD,
673   };
674   int res = dvb_str2common(p);
675   if (res != DVB_EOD)
676     return res;
677   return dvb_verify(atoi(p) * 10, rolloff_table);
678 }
679 
680 static const struct strtab delsystab[] = {
681   { "NONE",         DVB_SYS_NONE },
682   { "DVB-C",        DVB_SYS_DVBC_ANNEX_A },
683   { "DVBC/ANNEX_A", DVB_SYS_DVBC_ANNEX_A },
684   { "DVBC_ANNEX_A", DVB_SYS_DVBC_ANNEX_A },
685   { "ATSC-C",       DVB_SYS_DVBC_ANNEX_B },
686   { "DVBC/ANNEX_B", DVB_SYS_DVBC_ANNEX_B },
687   { "DVBC_ANNEX_B", DVB_SYS_DVBC_ANNEX_B },
688   { "DVB-C/ANNEX-C",DVB_SYS_DVBC_ANNEX_C },
689   { "DVBC/ANNEX_C", DVB_SYS_DVBC_ANNEX_C },
690   { "DVBC_ANNEX_C", DVB_SYS_DVBC_ANNEX_C },
691   { "DVBC_ANNEX_AC",DVB_SYS_DVBC_ANNEX_A }, /* for compatibility */
692   { "DVB-T",        DVB_SYS_DVBT },
693   { "DVBT",         DVB_SYS_DVBT },
694   { "DVB-T2",       DVB_SYS_DVBT2 },
695   { "DVBT2",        DVB_SYS_DVBT2 },
696   { "DVB-S",        DVB_SYS_DVBS },
697   { "DVBS",         DVB_SYS_DVBS },
698   { "DVB-S2",       DVB_SYS_DVBS2 },
699   { "DVBS2",        DVB_SYS_DVBS2 },
700   { "DVB-H",        DVB_SYS_DVBH },
701   { "DVBH",         DVB_SYS_DVBH },
702   { "ISDB-T",       DVB_SYS_ISDBT },
703   { "ISDBT",        DVB_SYS_ISDBT },
704   { "ISDB-S",       DVB_SYS_ISDBS },
705   { "ISDBS",        DVB_SYS_ISDBS },
706   { "ISDB-C",       DVB_SYS_ISDBC },
707   { "ISDBC",        DVB_SYS_ISDBC },
708   { "ATSC-T",       DVB_SYS_ATSC },
709   { "ATSC",         DVB_SYS_ATSC },
710   { "ATSCM-H",      DVB_SYS_ATSCMH },
711   { "ATSCMH",       DVB_SYS_ATSCMH },
712   { "DTMB",         DVB_SYS_DTMB },
713   { "DMBTH",        DVB_SYS_DTMB },	/* for compatibility */
714   { "CMMB",         DVB_SYS_CMMB },
715   { "DAB",          DVB_SYS_DAB },
716   { "DSS",          DVB_SYS_DSS },
717   { "TURBO",        DVB_SYS_TURBO }
718 };
719 dvb_str2val(delsys);
720 
721 int
dvb_delsys2type(mpegts_network_t * ln,dvb_fe_delivery_system_t delsys)722 dvb_delsys2type ( mpegts_network_t *ln, dvb_fe_delivery_system_t delsys )
723 {
724   switch (delsys) {
725     case DVB_SYS_DVBC_ANNEX_A:
726     case DVB_SYS_DVBC_ANNEX_C:
727       return DVB_TYPE_C;
728     case DVB_SYS_DVBT:
729     case DVB_SYS_DVBT2:
730     case DVB_SYS_TURBO:
731       return DVB_TYPE_T;
732     case DVB_SYS_DVBS:
733     case DVB_SYS_DVBS2:
734       return DVB_TYPE_S;
735     case DVB_SYS_ATSC:
736     case DVB_SYS_ATSCMH:
737       return DVB_TYPE_ATSC_T;
738     case DVB_SYS_DVBC_ANNEX_B:
739       if (ln && idnode_is_instance(&ln->mn_id, &dvb_network_dvbc_class))
740         return DVB_TYPE_C;
741       else
742         return DVB_TYPE_ATSC_C;
743     case DVB_SYS_ISDBT:
744       return DVB_TYPE_ISDB_T;
745     case DVB_SYS_ISDBC:
746       return DVB_TYPE_ISDB_C;
747     case DVB_SYS_ISDBS:
748       return DVB_TYPE_ISDB_S;
749     case DVB_SYS_DAB:
750       return DVB_TYPE_DAB;
751     default:
752       return DVB_TYPE_NONE;
753   }
754 }
755 
dvb_fec2str(int p)756 const char *dvb_fec2str(int p)
757 {
758   static __thread char buf[16];
759   const char *res = dvb_common2str(p);
760   if (res)
761     return res;
762   sprintf(buf, "%i/%i", p / 100, p % 100);
763   return buf;
764 }
765 
dvb_str2fec(const char * p)766 int dvb_str2fec(const char *p)
767 {
768   static int fec_table[] = {
769     DVB_FEC_1_2,
770     DVB_FEC_1_3,
771     DVB_FEC_1_5,
772     DVB_FEC_2_3,
773     DVB_FEC_2_5,
774     DVB_FEC_2_9,
775     DVB_FEC_3_4,
776     DVB_FEC_3_5,
777     DVB_FEC_4_5,
778     DVB_FEC_4_15,
779     DVB_FEC_5_6,
780     DVB_FEC_5_9,
781     DVB_FEC_6_7,
782     DVB_FEC_7_8,
783     DVB_FEC_7_9,
784     DVB_FEC_7_15,
785     DVB_FEC_8_9,
786     DVB_FEC_8_15,
787     DVB_FEC_9_10,
788     DVB_FEC_9_20,
789     DVB_FEC_11_15,
790     DVB_FEC_11_20,
791     DVB_FEC_11_45,
792     DVB_FEC_13_18,
793     DVB_FEC_13_45,
794     DVB_FEC_14_45,
795     DVB_FEC_23_36,
796     DVB_FEC_25_36,
797     DVB_FEC_26_45,
798     DVB_FEC_28_45,
799     DVB_FEC_29_45,
800     DVB_FEC_31_45,
801     DVB_FEC_32_45,
802     DVB_FEC_77_90,
803     DVB_EOD,
804   };
805   int res = dvb_str2common(p);
806   int hi, lo;
807   if (res != DVB_EOD)
808     return res;
809   hi = lo = 0;
810   sscanf(p, "%i/%i", &hi, &lo);
811   return dvb_verify(hi * 100 + lo, fec_table);
812 }
813 
814 static const struct strtab qamtab[] = {
815   { "NONE",      DVB_MOD_NONE },
816   { "AUTO",      DVB_MOD_AUTO },
817   { "QPSK",      DVB_MOD_QPSK },
818   { "QAM4NR",    DVB_MOD_QAM_4_NR },
819   { "QAM/AUTO",  DVB_MOD_QAM_AUTO },
820   { "QAM-AUTO",  DVB_MOD_QAM_AUTO },
821   { "QAM/16",    DVB_MOD_QAM_16 },
822   { "QAM16",     DVB_MOD_QAM_16 },
823   { "QAM/32",    DVB_MOD_QAM_32 },
824   { "QAM32",     DVB_MOD_QAM_32 },
825   { "QAM/64",    DVB_MOD_QAM_64 },
826   { "QAM64",     DVB_MOD_QAM_64 },
827   { "QAM/128",   DVB_MOD_QAM_128 },
828   { "QAM128",    DVB_MOD_QAM_128 },
829   { "QAM/256",   DVB_MOD_QAM_256 },
830   { "QAM256",    DVB_MOD_QAM_256 },
831   { "VSB/8",     DVB_MOD_VSB_8 },
832   { "8VSB",      DVB_MOD_VSB_8 },
833   { "VSB/16",    DVB_MOD_VSB_16 },
834   { "16VSB",     DVB_MOD_VSB_16 },
835   { "PSK/8",     DVB_MOD_PSK_8 },
836   { "8PSK",      DVB_MOD_PSK_8 },
837   { "DQPSK",     DVB_MOD_DQPSK },
838   { "BPSK",      DVB_MOD_BPSK },
839   { "BPSK-S",    DVB_MOD_BPSK_S },
840   { "16APSK",    DVB_MOD_APSK_16 },
841   { "32APSK",    DVB_MOD_APSK_32 },
842   { "64APSK",    DVB_MOD_APSK_64 },
843   { "128APSK",   DVB_MOD_APSK_128 },
844   { "256APSK",   DVB_MOD_APSK_256 },
845   { "8APSK-L",   DVB_MOD_APSK_8_L },
846   { "16APSK-L",  DVB_MOD_APSK_16_L },
847   { "32APSK-L",  DVB_MOD_APSK_32_L },
848   { "64APSK-L",  DVB_MOD_APSK_64_L },
849   { "128APSK-L", DVB_MOD_APSK_128_L },
850   { "256APSK-L", DVB_MOD_APSK_256_L },
851 };
852 dvb_str2val(qam);
853 
dvb_bw2str(int p)854 const char *dvb_bw2str(int p)
855 {
856   static __thread char buf[17];
857   const char *res = dvb_common2str(p);
858   if (res)
859     return res;
860   if (p % 1000)
861     sprintf(buf, "%i.%iMHz", p / 1000, p % 1000);
862   else
863     sprintf(buf, "%iMHz", p / 1000);
864   return buf;
865 }
866 
dvb_str2bw(const char * p)867 int dvb_str2bw(const char *p)
868 {
869   static int bw_table[] = {
870     DVB_BANDWIDTH_1_712_MHZ,
871     DVB_BANDWIDTH_5_MHZ,
872     DVB_BANDWIDTH_6_MHZ,
873     DVB_BANDWIDTH_7_MHZ,
874     DVB_BANDWIDTH_8_MHZ,
875     DVB_BANDWIDTH_10_MHZ,
876     DVB_EOD,
877   };
878   int len, res = dvb_str2common(p);
879   int hi, lo;
880   if (res != DVB_EOD)
881     return res;
882   len = strlen(p);
883   hi = lo = 0;
884   sscanf(p, "%i.%i", &hi, &lo);
885   if (len > 3 && strcmp(&p[len-3], "MHz") == 0)
886     hi = hi * 1000 + lo;
887   return dvb_verify(hi, bw_table);
888 }
889 
890 static const struct strtab invertab[] = {
891   { "NONE",  DVB_INVERSION_UNDEFINED },
892   { "AUTO",  DVB_INVERSION_AUTO },
893   { "ON",    DVB_INVERSION_ON },
894   { "OFF",   DVB_INVERSION_OFF },
895 };
896 dvb_str2val(inver);
897 
898 static const struct strtab modetab[] = {
899   { "NONE",  DVB_TRANSMISSION_MODE_NONE },
900   { "AUTO",  DVB_TRANSMISSION_MODE_AUTO },
901   { "1k",    DVB_TRANSMISSION_MODE_1K },
902   { "2k",    DVB_TRANSMISSION_MODE_2K },
903   { "8k",    DVB_TRANSMISSION_MODE_8K },
904   { "4k",    DVB_TRANSMISSION_MODE_4K },
905   { "16k",   DVB_TRANSMISSION_MODE_16K },
906   { "32k",   DVB_TRANSMISSION_MODE_32K },
907   { "C1",    DVB_TRANSMISSION_MODE_C1 },
908   { "C3780", DVB_TRANSMISSION_MODE_C3780 },
909 };
910 dvb_str2val(mode);
911 
912 static const struct strtab guardtab[] = {
913   { "NONE",   DVB_GUARD_INTERVAL_NONE },
914   { "AUTO",   DVB_GUARD_INTERVAL_AUTO },
915   { "1/4",    DVB_GUARD_INTERVAL_1_4 },
916   { "1/8",    DVB_GUARD_INTERVAL_1_8 },
917   { "1/32",   DVB_GUARD_INTERVAL_1_32 },
918   { "1/16",   DVB_GUARD_INTERVAL_1_16 },
919   { "1/128",  DVB_GUARD_INTERVAL_1_128 },
920   { "19/128", DVB_GUARD_INTERVAL_19_128 },
921   { "19/256", DVB_GUARD_INTERVAL_19_256 },
922   { "PN420",  DVB_GUARD_INTERVAL_PN420 },
923   { "PN595",  DVB_GUARD_INTERVAL_PN595 },
924   { "PN945",  DVB_GUARD_INTERVAL_PN945 },
925 };
926 dvb_str2val(guard);
927 
928 static const struct strtab hiertab[] = {
929   { "NONE", DVB_HIERARCHY_NONE },
930   { "AUTO", DVB_HIERARCHY_AUTO },
931   { "1",    DVB_HIERARCHY_1 },
932   { "2",    DVB_HIERARCHY_2 },
933   { "4",    DVB_HIERARCHY_4 },
934 };
935 dvb_str2val(hier);
936 
937 static const struct strtab poltab[] = {
938   { "V", DVB_POLARISATION_VERTICAL },
939   { "H", DVB_POLARISATION_HORIZONTAL },
940   { "L", DVB_POLARISATION_CIRCULAR_LEFT },
941   { "R", DVB_POLARISATION_CIRCULAR_RIGHT },
942   { "O", DVB_POLARISATION_OFF },
943 };
944 dvb_str2val(pol);
945 
946 static const struct strtab typetab[] = {
947   {"DVB-T",  DVB_TYPE_T},
948   {"DVB-C",  DVB_TYPE_C},
949   {"DVB-S",  DVB_TYPE_S},
950   {"ATSC-T", DVB_TYPE_ATSC_T},
951   {"ATSC-C", DVB_TYPE_ATSC_C},
952   {"ISDB-T", DVB_TYPE_ISDB_T},
953   {"ISDB-C", DVB_TYPE_ISDB_C},
954   {"ISDB-S", DVB_TYPE_ISDB_S},
955   {"DAB",    DVB_TYPE_DAB},
956   {"DVBT",   DVB_TYPE_T},
957   {"DVBC",   DVB_TYPE_C},
958   {"DVBS",   DVB_TYPE_S},
959   {"ATSC",   DVB_TYPE_ATSC_T},
960   {"ATSCT",  DVB_TYPE_ATSC_T},
961   {"ATSCC",  DVB_TYPE_ATSC_C},
962   {"ISDBT",  DVB_TYPE_ISDB_T},
963   {"ISDBC",  DVB_TYPE_ISDB_C},
964   {"ISDBS",  DVB_TYPE_ISDB_S}
965 };
966 dvb_str2val(type);
967 
968 static const struct strtab pilottab[] = {
969   {"NONE", DVB_PILOT_NONE},
970   {"AUTO", DVB_PILOT_AUTO},
971   {"ON",   DVB_PILOT_ON},
972   {"OFF",  DVB_PILOT_OFF}
973 };
974 dvb_str2val(pilot);
975 
976 static const struct strtab plsmodetab[] = {
977   {"ROOT", DVB_PLS_ROOT},
978   {"GOLD", DVB_PLS_GOLD},
979   {"COMBO", DVB_PLS_COMBO},
980 };
981 dvb_str2val(plsmode);
982 #undef dvb_str2val
983 
984 
985 void
dvb_mux_conf_init(mpegts_network_t * ln,dvb_mux_conf_t * dmc,dvb_fe_delivery_system_t delsys)986 dvb_mux_conf_init ( mpegts_network_t *ln,
987                     dvb_mux_conf_t *dmc,
988                     dvb_fe_delivery_system_t delsys )
989 {
990   memset(dmc, 0, sizeof(*dmc));
991   dmc->dmc_fe_type      = dvb_delsys2type(ln, delsys);
992   dmc->dmc_fe_delsys    = delsys;
993   dmc->dmc_fe_inversion = DVB_INVERSION_AUTO;
994   dmc->dmc_fe_pilot     = DVB_PILOT_AUTO;
995   dmc->dmc_fe_stream_id = DVB_NO_STREAM_ID_FILTER;
996   dmc->dmc_fe_pls_mode  = DVB_PLS_GOLD;
997   switch (dmc->dmc_fe_type) {
998   case DVB_TYPE_S:
999     dmc->u.dmc_fe_qpsk.orbital_pos = INT_MAX;
1000     break;
1001   default:
1002     break;
1003   }
1004 }
1005 
1006 
1007 static int
dvb_mux_conf_str_dvbt(dvb_mux_conf_t * dmc,char * buf,size_t bufsize)1008 dvb_mux_conf_str_dvbt ( dvb_mux_conf_t *dmc, char *buf, size_t bufsize )
1009 {
1010   char hp[16];
1011   snprintf(hp, sizeof(hp), "%s", dvb_fec2str(dmc->u.dmc_fe_ofdm.code_rate_HP));
1012   return
1013   snprintf(buf, bufsize,
1014            "%s freq %d bw %s cons %s hier %s code_rate %s:%s guard %s trans %s plp_id %d",
1015            dvb_delsys2str(dmc->dmc_fe_delsys),
1016            dmc->dmc_fe_freq,
1017            dvb_bw2str(dmc->u.dmc_fe_ofdm.bandwidth),
1018            dvb_qam2str(dmc->dmc_fe_modulation),
1019            dvb_hier2str(dmc->u.dmc_fe_ofdm.hierarchy_information),
1020            hp, dvb_fec2str(dmc->u.dmc_fe_ofdm.code_rate_LP),
1021            dvb_guard2str(dmc->u.dmc_fe_ofdm.guard_interval),
1022            dvb_mode2str(dmc->u.dmc_fe_ofdm.transmission_mode),
1023            dmc->dmc_fe_stream_id);
1024 }
1025 
1026 static int
dvb_mux_conf_str_dvbc(dvb_mux_conf_t * dmc,char * buf,size_t bufsize)1027 dvb_mux_conf_str_dvbc ( dvb_mux_conf_t *dmc, char *buf, size_t bufsize )
1028 {
1029   const char *delsys;
1030   if (dmc->dmc_fe_type == DVB_TYPE_C &&
1031       dmc->dmc_fe_delsys == DVB_SYS_DVBC_ANNEX_B)
1032     delsys = "DVB-C/ANNEX_B";
1033   else
1034     delsys = dvb_delsys2str(dmc->dmc_fe_delsys);
1035   return
1036   snprintf(buf, bufsize,
1037            "%s freq %d sym %d mod %s fec %s ds %d plp %d",
1038            delsys,
1039            dmc->dmc_fe_freq,
1040            dmc->u.dmc_fe_qam.symbol_rate,
1041            dvb_qam2str(dmc->dmc_fe_modulation),
1042            dvb_fec2str(dmc->u.dmc_fe_qam.fec_inner),
1043            dmc->dmc_fe_data_slice,
1044            dmc->dmc_fe_stream_id);
1045 }
1046 
1047 static int
dvb_mux_conf_str_dvbs(dvb_mux_conf_t * dmc,char * buf,size_t bufsize)1048 dvb_mux_conf_str_dvbs ( dvb_mux_conf_t *dmc, char *buf, size_t bufsize )
1049 {
1050   const char *pol = dvb_pol2str(dmc->u.dmc_fe_qpsk.polarisation);
1051   const int satpos = dmc->u.dmc_fe_qpsk.orbital_pos;
1052   char satbuf[16];
1053   if (satpos != INT_MAX) {
1054     snprintf(satbuf, sizeof(satbuf), "%d.%d%c ", abs(satpos) / 10, abs(satpos) % 10, satpos < 0 ? 'W' : 'E');
1055   } else {
1056     satbuf[0] = '\0';
1057   }
1058   return
1059   snprintf(buf, bufsize,
1060            "%s %sfreq %d %c sym %d fec %s mod %s roff %s is_id %d pls_mode %s pls_code %d",
1061            dvb_delsys2str(dmc->dmc_fe_delsys),
1062            satbuf,
1063            dmc->dmc_fe_freq,
1064            pol ? pol[0] : 'X',
1065            dmc->u.dmc_fe_qpsk.symbol_rate,
1066            dvb_fec2str(dmc->u.dmc_fe_qpsk.fec_inner),
1067            dvb_qam2str(dmc->dmc_fe_modulation),
1068            dvb_rolloff2str(dmc->dmc_fe_rolloff),
1069            dmc->dmc_fe_stream_id,
1070            dvb_plsmode2str(dmc->dmc_fe_pls_mode),
1071            dmc->dmc_fe_pls_code);
1072 }
1073 
1074 static int
dvb_mux_conf_str_atsc_t(dvb_mux_conf_t * dmc,char * buf,size_t bufsize)1075 dvb_mux_conf_str_atsc_t ( dvb_mux_conf_t *dmc, char *buf, size_t bufsize )
1076 {
1077   return
1078   snprintf(buf, bufsize,
1079            "%s freq %d mod %s",
1080            dvb_delsys2str(dmc->dmc_fe_delsys),
1081            dmc->dmc_fe_freq,
1082            dvb_qam2str(dmc->dmc_fe_modulation));
1083 }
1084 
1085 static int
dvb_mux_conf_str_isdb_t(dvb_mux_conf_t * dmc,char * buf,size_t bufsize)1086 dvb_mux_conf_str_isdb_t ( dvb_mux_conf_t *dmc, char *buf, size_t bufsize )
1087 {
1088   char hp[16];
1089   snprintf(hp, sizeof(hp), "%s", dvb_fec2str(dmc->u.dmc_fe_ofdm.code_rate_HP));
1090   return
1091   snprintf(buf, bufsize,
1092            "%s freq %d bw %s guard %s A (%s,%s,%d,%d) B (%s,%s,%d,%d) C (%s,%s,%d,%d)",
1093            dvb_delsys2str(dmc->dmc_fe_delsys),
1094            dmc->dmc_fe_freq,
1095            dvb_bw2str(dmc->u.dmc_fe_isdbt.bandwidth),
1096            dvb_guard2str(dmc->u.dmc_fe_isdbt.guard_interval),
1097            dvb_fec2str(dmc->u.dmc_fe_isdbt.layers[0].fec),
1098            dvb_qam2str(dmc->u.dmc_fe_isdbt.layers[0].modulation),
1099            dmc->u.dmc_fe_isdbt.layers[0].segment_count,
1100            dmc->u.dmc_fe_isdbt.layers[0].time_interleaving,
1101            dvb_fec2str(dmc->u.dmc_fe_isdbt.layers[1].fec),
1102            dvb_qam2str(dmc->u.dmc_fe_isdbt.layers[1].modulation),
1103            dmc->u.dmc_fe_isdbt.layers[1].segment_count,
1104            dmc->u.dmc_fe_isdbt.layers[1].time_interleaving,
1105            dvb_fec2str(dmc->u.dmc_fe_isdbt.layers[2].fec),
1106            dvb_qam2str(dmc->u.dmc_fe_isdbt.layers[2].modulation),
1107            dmc->u.dmc_fe_isdbt.layers[2].segment_count,
1108            dmc->u.dmc_fe_isdbt.layers[2].time_interleaving);
1109 }
1110 
1111 int
dvb_mux_conf_str(dvb_mux_conf_t * dmc,char * buf,size_t bufsize)1112 dvb_mux_conf_str ( dvb_mux_conf_t *dmc, char *buf, size_t bufsize )
1113 {
1114   switch (dmc->dmc_fe_type) {
1115   case DVB_TYPE_NONE:
1116     return
1117       snprintf(buf, bufsize, "NONE %s", dvb_delsys2str(dmc->dmc_fe_delsys));
1118   case DVB_TYPE_T:
1119     return dvb_mux_conf_str_dvbt(dmc, buf, bufsize);
1120   case DVB_TYPE_C:
1121   case DVB_TYPE_ATSC_C:
1122   case DVB_TYPE_ISDB_C:
1123     return dvb_mux_conf_str_dvbc(dmc, buf, bufsize);
1124   case DVB_TYPE_S:
1125     return dvb_mux_conf_str_dvbs(dmc, buf, bufsize);
1126   case DVB_TYPE_ATSC_T:
1127     return dvb_mux_conf_str_atsc_t(dmc, buf, bufsize);
1128   case DVB_TYPE_ISDB_T:
1129     return dvb_mux_conf_str_isdb_t(dmc, buf, bufsize);
1130   default:
1131     return
1132       snprintf(buf, bufsize, "UNKNOWN MUX CONFIG");
1133   }
1134 }
1135 
1136 const char *
dvb_sat_position_to_str(int position,char * buf,size_t buflen)1137 dvb_sat_position_to_str(int position, char *buf, size_t buflen)
1138 {
1139   const int dec = position % 10;
1140 
1141   if (!buf || !buflen)
1142     return "";
1143   snprintf(buf, buflen, "%d", abs(position / 10));
1144   if (dec)
1145     snprintf(buf + strlen(buf), buflen - strlen(buf), ".%d", abs(dec));
1146   snprintf(buf + strlen(buf), buflen - strlen(buf), "%c", position < 0 ? 'W' : 'E');
1147   return buf;
1148 }
1149 
1150 int
dvb_sat_position_from_str(const char * buf)1151 dvb_sat_position_from_str( const char *buf )
1152 {
1153   const char *s = buf;
1154   int min, maj;
1155   char c;
1156 
1157   if (!buf)
1158     return INT_MAX;
1159   maj = atoi(s);
1160   while (*s && *s != '.')
1161     s++;
1162   min = *s == '.' ? atoi(s + 1) : 0;
1163   if (*s != '.') s = buf;
1164   do {
1165     c = *s++;
1166   } while (c && c != 'W' && c != 'E');
1167   if (!c)
1168     return INT_MAX;
1169   if (maj > 180 || maj < 0)
1170     return INT_MAX;
1171   if (min > 9 || min < 0)
1172     return INT_MAX;
1173   return (maj * 10 + min) * (c == 'W' ? -1 : 1);
1174 }
1175 
1176 uint32_t
dvb_sat_pls(dvb_mux_conf_t * dmc)1177 dvb_sat_pls( dvb_mux_conf_t *dmc )
1178 {
1179   if (dmc->dmc_fe_pls_mode == DVB_PLS_ROOT) {
1180     uint32_t x, g;
1181     const uint32_t root = dmc->dmc_fe_pls_code & 0x3ffff;
1182 
1183     for (g = 0, x = 1; g < 0x3ffff; g++)  {
1184       if (root == x)
1185         return g;
1186       x = (((x ^ (x >> 7)) & 1) << 17) | (x >> 1);
1187     }
1188     return 0x3ffff;
1189   }
1190   return dmc->dmc_fe_pls_code & 0x3ffff;
1191 }
1192 
1193 #endif /* ENABLE_MPEGTS_DVB */
1194 
1195 /**
1196  *
1197  */
dvb_init(void)1198 void dvb_init( void )
1199 {
1200 #if ENABLE_MPEGTS_DVB
1201   satellites = hts_settings_load("satellites");
1202 #endif
1203 }
1204 
dvb_done(void)1205 void dvb_done( void )
1206 {
1207 #if ENABLE_MPEGTS_DVB
1208   htsmsg_destroy(satellites);
1209 #endif
1210 }
1211