1 /*
2  * Copyright (c) 2013 - Mauro Carvalho Chehab <mchehab@kernel.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation version 2.1 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16  * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
17  *
18  * Based on ETSI EN 300 468 V1.11.1 (2010-04)
19  */
20 
21 #include <libdvbv5/desc_extension.h>
22 #include <libdvbv5/desc_t2_delivery.h>
23 #include <libdvbv5/dvb-fe.h>
24 
25 #if __GNUC__ >= 9
26 #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
27 #endif
28 
dvb_desc_t2_delivery_init(struct dvb_v5_fe_parms * parms,const uint8_t * buf,struct dvb_extension_descriptor * ext,void * desc)29 int dvb_desc_t2_delivery_init(struct dvb_v5_fe_parms *parms,
30 			       const uint8_t *buf,
31 			       struct dvb_extension_descriptor *ext,
32 			       void *desc)
33 {
34 	struct dvb_desc_t2_delivery *d = desc;
35 	unsigned char *p = (unsigned char *) buf;
36 	size_t desc_len = ext->length - 1, len, len2;
37 	int i, pos = 0;
38 
39 	len = offsetof(struct dvb_desc_t2_delivery, bitfield);
40 	len2 = offsetof(struct dvb_desc_t2_delivery, centre_frequency);
41 
42 	if (desc_len < len) {
43 		dvb_logwarn("T2 delivery descriptor is too small");
44 		return -1;
45 	}
46 	if (desc_len < len2) {
47 		memcpy(d, buf, len);
48 		bswap16(d->system_id);
49 
50 		/* It is valid to have length == 4 */
51 		if (desc_len == len)
52 			return 0;
53 
54 		dvb_logwarn("T2 delivery descriptor is truncated");
55 		return -2;
56 	}
57 	memcpy(d, buf, len2);
58 	bswap16(d->system_id);
59 	bswap16(d->bitfield);
60 	p += len2;
61 
62 	while (desc_len - (p - buf)) {
63 		if (desc_len - (p - buf) < sizeof(uint16_t)) {
64 			dvb_logwarn("T2 delivery descriptor is truncated");
65 			return -2;
66 		}
67 
68 		d->cell = realloc(d->cell, (d->num_cell + 1) * sizeof(*d->cell));
69 		if (!d->cell) {
70 			dvb_logerr("%s: out of memory", __func__);
71 			return -3;
72 		}
73 
74 		d->cell[d->num_cell].cell_id = *(uint16_t *)p;
75 		bswap16(d->cell[d->num_cell].cell_id);
76 		p += sizeof(uint16_t);
77 
78 		if (d->tfs_flag) {
79 			d->cell[d->num_cell].num_freqs = *p;
80 			p++;
81 		}
82 		else
83 			d->cell[d->num_cell].num_freqs = 1;
84 
85 		d->frequency_loop_length += d->cell[d->num_cell].num_freqs;
86 		d->centre_frequency = realloc(d->centre_frequency,
87 					      d->frequency_loop_length * sizeof(*d->centre_frequency));
88 		if (!d->centre_frequency) {
89 			dvb_logerr("%s: out of memory", __func__);
90 			return -3;
91 		}
92 
93 		d->cell[d->num_cell].centre_frequency = &d->centre_frequency[pos];
94 
95 		memcpy(&d->centre_frequency[pos], p, sizeof(*d->centre_frequency) * d->cell[d->num_cell].num_freqs);
96 		p += sizeof(*d->centre_frequency) * d->cell[d->num_cell].num_freqs;
97 
98 		for (i = 0; i < d->cell[d->num_cell].num_freqs; i++) {
99 			bswap32(d->centre_frequency[pos]);
100 			pos++;
101 		}
102 
103 		/* Handle subcel frequency table */
104 		d->cell[d->num_cell].subcel_length = *p;
105 		d->cell[d->num_cell].subcel = NULL;
106 
107 		p++;
108 
109 		if (d->cell[d->num_cell].subcel_length) {
110 			d->cell[d->num_cell].subcel = calloc(d->cell[d->num_cell].subcel_length,
111 							     sizeof (*d->cell[d->num_cell].subcel));
112 
113 			if (!d->cell[d->num_cell].subcel) {
114 				dvb_logerr("%s: out of memory", __func__);
115 				return -3;
116 			}
117 		}
118 
119 		for (i = 0; i < d->cell[d->num_cell].subcel_length; i++) {
120 			if (desc_len - (p - buf) < sizeof(uint8_t) + sizeof(uint32_t)) {
121 				dvb_logwarn("T2 delivery descriptor is truncated");
122 				return -2;
123 			}
124 			d->cell[d->num_cell].subcel[i].cell_id_extension = *p;
125 			p++;
126 
127 			// Add transposer_frequency at centre_frequency table
128 			d->frequency_loop_length++;
129 			d->centre_frequency = realloc(d->centre_frequency,
130 						      d->frequency_loop_length * sizeof(*d->centre_frequency));
131 			memcpy(&d->centre_frequency[pos], p, sizeof(*d->centre_frequency));
132 			bswap32(d->centre_frequency[pos]);
133 			d->cell[d->num_cell].subcel[i].transposer_frequency = d->centre_frequency[pos];
134 			pos++;
135 
136 			p += sizeof(*d->centre_frequency);
137 		}
138 		d->num_cell++;
139 	}
140 
141 	return 0;
142 }
143 
dvb_desc_t2_delivery_print(struct dvb_v5_fe_parms * parms,const struct dvb_extension_descriptor * ext,const void * desc)144 void dvb_desc_t2_delivery_print(struct dvb_v5_fe_parms *parms,
145 				const struct dvb_extension_descriptor *ext,
146 				const void *desc)
147 {
148 	const struct dvb_desc_t2_delivery *d = desc;
149 	int i, j, k;
150 
151 	dvb_loginfo("|           plp_id                    0x%04x", d->plp_id);
152 	dvb_loginfo("|           system_id                 0x%04x", d->system_id);
153 
154 	if (ext->length - 1 <= 4)
155 		return;
156 
157 	dvb_loginfo("|           tfs_flag                  %d", d->tfs_flag);
158 	dvb_loginfo("|           other_frequency_flag      %d", d->other_frequency_flag);
159 	dvb_loginfo("|           transmission_mode         %s (%d)",
160 		    fe_transmission_mode_name[dvbt2_transmission_mode[d->transmission_mode]], d->transmission_mode);
161 	dvb_loginfo("|           guard_interval            %s (%d)",
162 		    fe_guard_interval_name[dvbt2_interval[d->guard_interval]], d->guard_interval );
163 	dvb_loginfo("|           reserved                  %d", d->reserved);
164 	dvb_loginfo("|           bandwidth                 %.2f MHz (%d)",
165 		    dvbt2_bw[d->bandwidth]/ 1000000., d->bandwidth);
166 	dvb_loginfo("|           SISO/MISO mode            %s", siso_miso[d->SISO_MISO]);
167 
168 	/*
169 	 * All frequencies are also at the per-cell data. Yet, as the flat
170 	 * frequency table is what's used to add new transponders for DVB-T2
171 	 * scan, let's display the flat table too
172 	 */
173 	for (i = 0; i < d->frequency_loop_length; i++)
174 		dvb_loginfo("|           frequency[%d]              %.5f MHz", i, d->centre_frequency[i] / 100000.);
175 
176 
177 	for (i = 0; i < d->num_cell; i++) {
178 		struct dvb_desc_t2_delivery_cell *cell = &d->cell[i];
179 		dvb_loginfo("|           Cell ID                   0x%04x", cell->cell_id);
180 		for (j = 0; j < cell->num_freqs; j++) {
181 			dvb_loginfo("|              centre frequency[%d]    %.5f MHz", j, cell->centre_frequency[j] / 100000.);
182 
183 			for (k = 0; k < cell->subcel_length; k++) {
184 				struct dvb_desc_t2_delivery_subcell *subcel = &cell->subcel[k];
185 				dvb_loginfo("|           |- subcell        %d", subcel->cell_id_extension);
186 				dvb_loginfo("|              |- transposer  %.5f MHz", subcel->transposer_frequency / 100000.);
187 			}
188 		}
189 	}
190 }
191 
dvb_desc_t2_delivery_free(const void * desc)192 void dvb_desc_t2_delivery_free(const void *desc)
193 {
194 	const struct dvb_desc_t2_delivery *d = desc;
195 	int i;
196 
197 	if (d->centre_frequency)
198 		free(d->centre_frequency);
199 
200 	if (d->cell) {
201 		for (i = 0; i < d->num_cell; i++)
202 			if (d->cell[i].subcel)
203 				free(d->cell[i].subcel);
204 		free (d->cell);
205 	}
206 
207 	// No need to free d->subcell, as it is always NULL
208 }
209 
210 const unsigned dvbt2_bw[] = {
211 	[0] =  8000000,
212 	[1] =  7000000,
213 	[2] =  6000000,
214 	[3] =  5000000,
215 	[4] = 10000000,
216 	[5] =  1712000,
217 	[6 ...15] = 0,		/* Reserved */
218 };
219 const uint32_t dvbt2_interval[] = {
220 	[0] = GUARD_INTERVAL_1_32,
221 	[1] = GUARD_INTERVAL_1_16,
222 	[2] = GUARD_INTERVAL_1_8,
223 	[3] = GUARD_INTERVAL_1_4,
224 	[4] = GUARD_INTERVAL_1_128,
225 	[5] = GUARD_INTERVAL_19_128,
226 	[6] = GUARD_INTERVAL_19_256,
227 	[7 ...15] = GUARD_INTERVAL_AUTO /* Reserved */
228 };
229 const unsigned dvbt2_transmission_mode[] = {
230 	[0] = TRANSMISSION_MODE_2K,
231 	[1] = TRANSMISSION_MODE_8K,
232 	[2] = TRANSMISSION_MODE_4K,
233 	[3] = TRANSMISSION_MODE_1K,
234 	[4] = TRANSMISSION_MODE_16K,
235 	[5] = TRANSMISSION_MODE_32K,
236 	[6 ...7] = TRANSMISSION_MODE_AUTO,	/* Reserved */
237 };
238 const char *siso_miso[4] = {
239 	[0] = "Single Input, Single Output (SISO)",
240 	[1] = "Multiple Input, Single Output (MISO)",
241 	[2 ...3] = "reserved",
242 };
243