1 /*
2 * Tvheadend - Network Scanner
3 *
4 * Copyright (C) 2014 Adam Sutton
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "input.h"
21
22 /******************************************************************************
23 * Timer
24 *****************************************************************************/
25
26 /* Notify */
27 static void
mpegts_network_scan_notify(mpegts_mux_t * mm)28 mpegts_network_scan_notify ( mpegts_mux_t *mm )
29 {
30 idnode_notify_changed(&mm->mm_id);
31 idnode_notify_changed(&mm->mm_network->mn_id);
32 }
33
34 static int
mm_cmp(mpegts_mux_t * a,mpegts_mux_t * b)35 mm_cmp ( mpegts_mux_t *a, mpegts_mux_t *b )
36 {
37 int r = b->mm_scan_weight - a->mm_scan_weight;
38 if (r == 0) {
39 int64_t l = b->mm_start_monoclock - a->mm_start_monoclock;
40 if (l == 0)
41 return mpegts_mux_compare(a, b);
42 r = l > 0 ? 1 : -1;
43 }
44 return r;
45 }
46
47 void
mpegts_network_scan_timer_cb(void * p)48 mpegts_network_scan_timer_cb ( void *p )
49 {
50 mpegts_network_t *mn = p;
51 mpegts_mux_t *mm, *nxt = NULL;
52 int r;
53
54 /* Process Q */
55 for (mm = TAILQ_FIRST(&mn->mn_scan_pend); mm != NULL; mm = nxt) {
56 nxt = TAILQ_NEXT(mm, mm_scan_link);
57 assert(mm->mm_scan_state == MM_SCAN_STATE_PEND || mm->mm_scan_state == MM_SCAN_STATE_ACTIVE);
58
59 /* Don't try to subscribe already tuned muxes */
60 if (mm->mm_active) continue;
61
62 /* Attempt to tune */
63 r = mpegts_mux_subscribe(mm, NULL, "scan", mm->mm_scan_weight,
64 mm->mm_scan_flags |
65 SUBSCRIPTION_ONESHOT |
66 SUBSCRIPTION_TABLES);
67
68 /* Started */
69 if (!r) {
70 assert(mm->mm_scan_state == MM_SCAN_STATE_ACTIVE);
71 continue;
72 }
73 assert(mm->mm_scan_state == MM_SCAN_STATE_PEND);
74
75 /* No free tuners - stop */
76 if (r == SM_CODE_NO_FREE_ADAPTER || r == SM_CODE_NO_ADAPTERS)
77 break;
78
79 /* No valid tuners (subtly different, might be able to tuner a later
80 * mux)
81 */
82 if (r == SM_CODE_NO_VALID_ADAPTER && mm->mm_is_enabled(mm))
83 continue;
84
85 /* Failed */
86 TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
87 if (mm->mm_scan_result != MM_SCAN_FAIL) {
88 mm->mm_scan_result = MM_SCAN_FAIL;
89 idnode_changed(&mm->mm_id);
90 }
91 mm->mm_scan_state = MM_SCAN_STATE_IDLE;
92 mm->mm_scan_weight = 0;
93 mpegts_network_scan_notify(mm);
94 }
95
96 /* Re-arm timer. Really this is just a safety measure as we'd normally
97 * expect the timer to be forcefully triggered on finish of a mux scan
98 */
99 mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(120));
100 }
101
102 /******************************************************************************
103 * Mux transition
104 *****************************************************************************/
105
106 /* Finished */
107 static inline void
mpegts_network_scan_mux_done0(mpegts_mux_t * mm,mpegts_mux_scan_result_t result,int weight)108 mpegts_network_scan_mux_done0
109 ( mpegts_mux_t *mm, mpegts_mux_scan_result_t result, int weight )
110 {
111 mpegts_network_t *mn = mm->mm_network;
112 mpegts_mux_scan_state_t state = mm->mm_scan_state;
113
114 /* prevent double del: */
115 /* mpegts_mux_stop -> mpegts_network_scan_mux_cancel */
116 mm->mm_scan_state = MM_SCAN_STATE_IDLE;
117 mpegts_mux_unsubscribe_by_name(mm, "scan");
118 mm->mm_scan_state = state;
119
120 if (state == MM_SCAN_STATE_PEND) {
121 if (weight || mn->mn_idlescan) {
122 if (!weight)
123 mm->mm_scan_weight = SUBSCRIPTION_PRIO_SCAN_IDLE;
124 TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
125 TAILQ_INSERT_SORTED_R(&mn->mn_scan_pend, mpegts_mux_queue,
126 mm, mm_scan_link, mm_cmp);
127 mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(10));
128 weight = 0;
129 } else {
130 mpegts_network_scan_queue_del(mm);
131 }
132 } else {
133 if (!weight && mn->mn_idlescan)
134 weight = SUBSCRIPTION_PRIO_SCAN_IDLE;
135 mpegts_network_scan_queue_del(mm);
136 }
137
138 if (result != MM_SCAN_NONE && mm->mm_scan_result != result) {
139 mm->mm_scan_result = result;
140 idnode_changed(&mm->mm_id);
141 }
142
143 /* Re-enable? */
144 if (weight > 0)
145 mpegts_network_scan_queue_add(mm, weight, mm->mm_scan_flags, 10);
146 }
147
148 /* Failed - couldn't start */
149 void
mpegts_network_scan_mux_fail(mpegts_mux_t * mm)150 mpegts_network_scan_mux_fail ( mpegts_mux_t *mm )
151 {
152 mpegts_network_scan_mux_done0(mm, MM_SCAN_FAIL, 0);
153 }
154
155 /* Completed succesfully */
156 void
mpegts_network_scan_mux_done(mpegts_mux_t * mm)157 mpegts_network_scan_mux_done ( mpegts_mux_t *mm )
158 {
159 mm->mm_scan_flags = 0;
160 mpegts_network_scan_mux_done0(mm, MM_SCAN_OK, 0);
161 }
162
163 /* Partially completed (not all tables were read) */
164 void
mpegts_network_scan_mux_partial(mpegts_mux_t * mm)165 mpegts_network_scan_mux_partial ( mpegts_mux_t *mm )
166 {
167 mpegts_network_scan_mux_done0(mm, MM_SCAN_PARTIAL, 0);
168 }
169
170 /* Interrupted (re-add) */
171 void
mpegts_network_scan_mux_cancel(mpegts_mux_t * mm,int reinsert)172 mpegts_network_scan_mux_cancel ( mpegts_mux_t *mm, int reinsert )
173 {
174 if (mm->mm_scan_state != MM_SCAN_STATE_ACTIVE)
175 return;
176
177 if (!reinsert)
178 mm->mm_scan_flags = 0;
179
180 mpegts_network_scan_mux_done0(mm, MM_SCAN_NONE,
181 reinsert ? mm->mm_scan_weight : 0);
182 }
183
184 /* Mux has been started */
185 void
mpegts_network_scan_mux_active(mpegts_mux_t * mm)186 mpegts_network_scan_mux_active ( mpegts_mux_t *mm )
187 {
188 mpegts_network_t *mn = mm->mm_network;
189 if (mm->mm_scan_state != MM_SCAN_STATE_PEND)
190 return;
191 mm->mm_scan_state = MM_SCAN_STATE_ACTIVE;
192 mm->mm_scan_init = 0;
193 TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
194 TAILQ_INSERT_TAIL(&mn->mn_scan_active, mm, mm_scan_link);
195 }
196
197 /******************************************************************************
198 * Mux queue handling
199 *****************************************************************************/
200
201 void
mpegts_network_scan_queue_del(mpegts_mux_t * mm)202 mpegts_network_scan_queue_del ( mpegts_mux_t *mm )
203 {
204 mpegts_network_t *mn = mm->mm_network;
205 char buf[384];
206 if (mm->mm_scan_state == MM_SCAN_STATE_ACTIVE) {
207 TAILQ_REMOVE(&mn->mn_scan_active, mm, mm_scan_link);
208 } else if (mm->mm_scan_state == MM_SCAN_STATE_PEND) {
209 TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
210 }
211 mpegts_mux_nice_name(mm, buf, sizeof(buf));
212 tvhdebug(LS_MPEGTS, "removing mux %s from scan queue", buf);
213 mm->mm_scan_state = MM_SCAN_STATE_IDLE;
214 mm->mm_scan_weight = 0;
215 mtimer_disarm(&mm->mm_scan_timeout);
216 mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, 0);
217 mpegts_network_scan_notify(mm);
218 }
219
220 void
mpegts_network_scan_queue_add(mpegts_mux_t * mm,int weight,int flags,int delay)221 mpegts_network_scan_queue_add
222 ( mpegts_mux_t *mm, int weight, int flags, int delay )
223 {
224 int reload = 0;
225 char buf[384];
226 mpegts_network_t *mn = mm->mm_network;
227
228 if (!mm->mm_is_enabled(mm)) return;
229
230 if (weight <= 0) return;
231
232 if (weight > mm->mm_scan_weight) {
233 mm->mm_scan_weight = weight;
234 reload = 1;
235 }
236
237 /* Already active */
238 if (mm->mm_scan_state == MM_SCAN_STATE_ACTIVE)
239 return;
240
241 /* Remove entry (or ignore) */
242 if (mm->mm_scan_state == MM_SCAN_STATE_PEND) {
243 if (!reload)
244 return;
245 TAILQ_REMOVE(&mn->mn_scan_pend, mm, mm_scan_link);
246 }
247
248 mpegts_mux_nice_name(mm, buf, sizeof(buf));
249 tvhdebug(LS_MPEGTS, "adding mux %s to scan queue weight %d flags %04X",
250 buf, weight, flags);
251
252 /* Add new entry */
253 mm->mm_scan_state = MM_SCAN_STATE_PEND;
254 mm->mm_scan_flags |= flags;
255 if (mm->mm_scan_flags == 0)
256 mm->mm_scan_flags = SUBSCRIPTION_IDLESCAN;
257 TAILQ_INSERT_SORTED_R(&mn->mn_scan_pend, mpegts_mux_queue,
258 mm, mm_scan_link, mm_cmp);
259 mtimer_arm_rel(&mn->mn_scan_timer, mpegts_network_scan_timer_cb, mn, sec2mono(delay));
260 mpegts_network_scan_notify(mm);
261 }
262
263 /******************************************************************************
264 * Bouquet helper
265 *****************************************************************************/
266
267 #if ENABLE_MPEGTS_DVB
268 static ssize_t
startswith(const char * str,const char * start)269 startswith( const char *str, const char *start )
270 {
271 size_t len = strlen(start);
272 if (!strncmp(str, start, len))
273 return len;
274 return -1;
275 }
276 #endif
277
278 void
mpegts_mux_bouquet_rescan(const char * src,const char * extra)279 mpegts_mux_bouquet_rescan ( const char *src, const char *extra )
280 {
281 #if ENABLE_MPEGTS_DVB
282 mpegts_network_t *mn;
283 mpegts_mux_t *mm;
284 ssize_t l;
285 const idclass_t *ic;
286 uint32_t freq;
287 int satpos;
288 #endif
289
290 if (!src)
291 return;
292 #if ENABLE_MPEGTS_DVB
293 if ((l = startswith(src, "dvb-bouquet://dvbs,")) > 0) {
294 uint32_t tsid, nbid;
295 src += l;
296 if ((satpos = dvb_sat_position_from_str(src)) == INT_MAX)
297 return;
298 while (*src && *src != ',')
299 src++;
300 if (sscanf(src, ",%x,%x", &tsid, &nbid) != 2)
301 return;
302 LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
303 LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
304 if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class) &&
305 mm->mm_tsid == tsid &&
306 ((dvb_mux_t *)mm)->lm_tuning.u.dmc_fe_qpsk.orbital_pos == satpos)
307 mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
308 return;
309 }
310 if ((l = startswith(src, "dvb-bouquet://dvbt,")) > 0) {
311 uint32_t tsid, nbid;
312 if (sscanf(src, "%x,%x", &tsid, &nbid) != 2)
313 return;
314 ic = &dvb_mux_dvbt_class;
315 goto tsid_lookup;
316 }
317 if ((l = startswith(src, "dvb-bouquet://dvbc,")) > 0) {
318 uint32_t tsid, nbid;
319 if (sscanf(src, "%x,%x", &tsid, &nbid) != 2)
320 return;
321 ic = &dvb_mux_dvbc_class;
322 tsid_lookup:
323 LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
324 LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
325 if (idnode_is_instance(&mm->mm_id, ic) &&
326 mm->mm_tsid == tsid)
327 mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
328 return;
329 }
330 if ((l = startswith(src, "dvb-bskyb://dvbs,")) > 0 ||
331 (l = startswith(src, "dvb-freesat://dvbs,")) > 0) {
332 if ((satpos = dvb_sat_position_from_str(src + l)) == INT_MAX)
333 return;
334 /* a bit tricky, but we don't have other info */
335 if (!extra)
336 return;
337 freq = strtod(extra, NULL) * 1000;
338
339 LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
340 LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
341 if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class) &&
342 deltaU32(((dvb_mux_t *)mm)->lm_tuning.dmc_fe_freq, freq) < 2000 &&
343 ((dvb_mux_t *)mm)->lm_tuning.u.dmc_fe_qpsk.orbital_pos == satpos)
344 mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
345 return;
346 }
347 if ((l = startswith(src, "dvb-fastscan://")) > 0) {
348 uint32_t pid, symbol, dvbs2;
349 char pol[2];
350 pol[1] = '\0';
351 src += l;
352
353 if ((l = startswith(src, "DVBS2,")) > 0 || (l = startswith(src, "DVBS-2,")) > 0)
354 dvbs2 = 1;
355 else if ((l = startswith(src, "DVBS,")) > 0 || (l = startswith(src, "DVB-S,")) > 0)
356 dvbs2 = 0;
357 else
358 return;
359 src += l;
360
361 if ((satpos = dvb_sat_position_from_str(src)) == INT_MAX)
362 return;
363 while (*src && *src != ',')
364 src++;
365 if (sscanf(src, ",%u,%c,%u,%u", &freq, &pol[0], &symbol, &pid) != 4)
366 return;
367
368 // search for fastscan mux
369 LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
370 LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link)
371 if (idnode_is_instance(&mm->mm_id, &dvb_mux_dvbs_class) &&
372 deltaU32(((dvb_mux_t *)mm)->lm_tuning.dmc_fe_freq, freq) < 2000 &&
373 ((dvb_mux_t *)mm)->lm_tuning.u.dmc_fe_qpsk.polarisation == dvb_str2pol(pol) &&
374 ((dvb_mux_t *)mm)->lm_tuning.u.dmc_fe_qpsk.orbital_pos == satpos)
375 {
376 char buf[256];
377 mpegts_mux_nice_name(mm, buf, sizeof(buf));
378 tvhinfo(LS_MPEGTS, "fastscan mux found '%s', set scan state 'PENDING'", buf);
379 mpegts_mux_scan_state_set(mm, MM_SCAN_STATE_PEND);
380 return;
381 }
382 tvhinfo(LS_MPEGTS, "fastscan mux not found, position:%i, frequency:%i, polarisation:%c", satpos, freq, pol[0]);
383
384 // fastscan mux not found, try to add it automatically
385 LIST_FOREACH(mn, &mpegts_network_all, mn_global_link)
386 if (mn->mn_satpos != INT_MAX && mn->mn_satpos == satpos)
387 {
388 dvb_mux_conf_t *mux;
389 mpegts_mux_t *mm = NULL;
390
391 mux = malloc(sizeof(dvb_mux_conf_t));
392 dvb_mux_conf_init(mn, mux, dvbs2 ? DVB_SYS_DVBS2 : DVB_SYS_DVBS);
393 mux->dmc_fe_freq = freq;
394 mux->u.dmc_fe_qpsk.symbol_rate = symbol;
395 mux->u.dmc_fe_qpsk.polarisation = dvb_str2pol(pol);
396 mux->u.dmc_fe_qpsk.orbital_pos = satpos;
397 mux->u.dmc_fe_qpsk.fec_inner = DVB_FEC_AUTO;
398 mux->dmc_fe_modulation = dvbs2 ? DVB_MOD_PSK_8 : DVB_MOD_QPSK;
399 mux->dmc_fe_rolloff = DVB_ROLLOFF_AUTO;
400
401 mm = (mpegts_mux_t*)dvb_mux_create0((dvb_network_t*)mn,
402 MPEGTS_ONID_NONE,
403 MPEGTS_TSID_NONE,
404 mux, NULL, NULL);
405 if (mm) {
406 char buf[256];
407 idnode_changed(&mm->mm_id);
408 mn->mn_display_name(mn, buf, sizeof(buf));
409 tvhinfo(LS_MPEGTS, "fastscan mux add to network '%s'", buf);
410 }
411 free(mux);
412 }
413 return;
414 }
415 #endif
416 }
417
418 /******************************************************************************
419 * Subsystem setup / tear down
420 *****************************************************************************/
421
422 void
mpegts_network_scan_init(void)423 mpegts_network_scan_init ( void )
424 {
425 }
426
427 void
mpegts_network_scan_done(void)428 mpegts_network_scan_done ( void )
429 {
430 }
431
432 /******************************************************************************
433 * Editor Configuration
434 *
435 * vim:sts=2:ts=2:sw=2:et
436 *****************************************************************************/
437