• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Vidtv serves as a reference DVB driver and helps validate the existing APIs
4  * in the media subsystem. It can also aid developers working on userspace
5  * applications.
6  *
7  * This file contains the code for a 'channel' abstraction.
8  *
9  * When vidtv boots, it will create some hardcoded channels.
10  * Their services will be concatenated to populate the SDT.
11  * Their programs will be concatenated to populate the PAT
12  * Their events will be concatenated to populate the EIT
13  * For each program in the PAT, a PMT section will be created
14  * The PMT section for a channel will be assigned its streams.
15  * Every stream will have its corresponding encoder polled to produce TS packets
16  * These packets may be interleaved by the mux and then delivered to the bridge
17  *
18  *
19  * Copyright (C) 2020 Daniel W. S. Almeida
20  */
21 
22 #include <linux/dev_printk.h>
23 #include <linux/ratelimit.h>
24 #include <linux/slab.h>
25 #include <linux/types.h>
26 
27 #include "vidtv_channel.h"
28 #include "vidtv_common.h"
29 #include "vidtv_encoder.h"
30 #include "vidtv_mux.h"
31 #include "vidtv_psi.h"
32 #include "vidtv_s302m.h"
33 
vidtv_channel_encoder_destroy(struct vidtv_encoder * e)34 static void vidtv_channel_encoder_destroy(struct vidtv_encoder *e)
35 {
36 	struct vidtv_encoder *tmp = NULL;
37 	struct vidtv_encoder *curr = e;
38 
39 	while (curr) {
40 		/* forward the call to the derived type */
41 		tmp = curr;
42 		curr = curr->next;
43 		tmp->destroy(tmp);
44 	}
45 }
46 
47 #define ENCODING_ISO8859_15 "\x0b"
48 #define TS_NIT_PID	0x10
49 
50 /*
51  * init an audio only channel with a s302m encoder
52  */
53 struct vidtv_channel
vidtv_channel_s302m_init(struct vidtv_channel * head,u16 transport_stream_id)54 *vidtv_channel_s302m_init(struct vidtv_channel *head, u16 transport_stream_id)
55 {
56 	const __be32 s302m_fid              = cpu_to_be32(VIDTV_S302M_FORMAT_IDENTIFIER);
57 	char *event_text = ENCODING_ISO8859_15 "Bagatelle No. 25 in A minor for solo piano, also known as F\xfcr Elise, composed by Ludwig van Beethoven";
58 	char *event_name = ENCODING_ISO8859_15 "Ludwig van Beethoven: F\xfcr Elise";
59 	struct vidtv_s302m_encoder_init_args encoder_args = {};
60 	char *iso_language_code = ENCODING_ISO8859_15 "eng";
61 	char *provider = ENCODING_ISO8859_15 "LinuxTV.org";
62 	char *name = ENCODING_ISO8859_15 "Beethoven";
63 	const u16 s302m_es_pid              = 0x111; /* packet id for the ES */
64 	const u16 s302m_program_pid         = 0x101; /* packet id for PMT*/
65 	const u16 s302m_service_id          = 0x880;
66 	const u16 s302m_program_num         = 0x880;
67 	const u16 s302m_beethoven_event_id  = 1;
68 	struct vidtv_channel *s302m;
69 
70 	s302m = kzalloc(sizeof(*s302m), GFP_KERNEL);
71 	if (!s302m)
72 		return NULL;
73 
74 	s302m->name = kstrdup(name, GFP_KERNEL);
75 	if (!s302m->name)
76 		goto free_s302m;
77 
78 	s302m->service = vidtv_psi_sdt_service_init(NULL, s302m_service_id, false, true);
79 	if (!s302m->service)
80 		goto free_name;
81 
82 	s302m->service->descriptor = (struct vidtv_psi_desc *)
83 				     vidtv_psi_service_desc_init(NULL,
84 								 DIGITAL_RADIO_SOUND_SERVICE,
85 								 name,
86 								 provider);
87 	if (!s302m->service->descriptor)
88 		goto free_service;
89 
90 	s302m->transport_stream_id = transport_stream_id;
91 
92 	s302m->program = vidtv_psi_pat_program_init(NULL,
93 						    s302m_service_id,
94 						    s302m_program_pid);
95 	if (!s302m->program)
96 		goto free_service;
97 
98 	s302m->program_num = s302m_program_num;
99 
100 	s302m->streams = vidtv_psi_pmt_stream_init(NULL,
101 						   STREAM_PRIVATE_DATA,
102 						   s302m_es_pid);
103 	if (!s302m->streams)
104 		goto free_program;
105 
106 	s302m->streams->descriptor = (struct vidtv_psi_desc *)
107 				     vidtv_psi_registration_desc_init(NULL,
108 								      s302m_fid,
109 								      NULL,
110 								      0);
111 	if (!s302m->streams->descriptor)
112 		goto free_streams;
113 
114 	encoder_args.es_pid = s302m_es_pid;
115 
116 	s302m->encoders = vidtv_s302m_encoder_init(encoder_args);
117 	if (!s302m->encoders)
118 		goto free_streams;
119 
120 	s302m->events = vidtv_psi_eit_event_init(NULL, s302m_beethoven_event_id);
121 	if (!s302m->events)
122 		goto free_encoders;
123 	s302m->events->descriptor = (struct vidtv_psi_desc *)
124 				    vidtv_psi_short_event_desc_init(NULL,
125 								    iso_language_code,
126 								    event_name,
127 								    event_text);
128 	if (!s302m->events->descriptor)
129 		goto free_events;
130 
131 	if (head) {
132 		while (head->next)
133 			head = head->next;
134 
135 		head->next = s302m;
136 	}
137 
138 	return s302m;
139 
140 free_events:
141 	vidtv_psi_eit_event_destroy(s302m->events);
142 free_encoders:
143 	vidtv_s302m_encoder_destroy(s302m->encoders);
144 free_streams:
145 	vidtv_psi_pmt_stream_destroy(s302m->streams);
146 free_program:
147 	vidtv_psi_pat_program_destroy(s302m->program);
148 free_service:
149 	vidtv_psi_sdt_service_destroy(s302m->service);
150 free_name:
151 	kfree(s302m->name);
152 free_s302m:
153 	kfree(s302m);
154 
155 	return NULL;
156 }
157 
158 static struct vidtv_psi_table_eit_event
vidtv_channel_eit_event_cat_into_new(struct vidtv_mux * m)159 *vidtv_channel_eit_event_cat_into_new(struct vidtv_mux *m)
160 {
161 	/* Concatenate the events */
162 	const struct vidtv_channel *cur_chnl = m->channels;
163 	struct vidtv_psi_table_eit_event *curr = NULL;
164 	struct vidtv_psi_table_eit_event *head = NULL;
165 	struct vidtv_psi_table_eit_event *tail = NULL;
166 	struct vidtv_psi_desc *desc = NULL;
167 	u16 event_id;
168 
169 	if (!cur_chnl)
170 		return NULL;
171 
172 	while (cur_chnl) {
173 		curr = cur_chnl->events;
174 
175 		if (!curr)
176 			dev_warn_ratelimited(m->dev,
177 					     "No events found for channel %s\n",
178 					     cur_chnl->name);
179 
180 		while (curr) {
181 			event_id = be16_to_cpu(curr->event_id);
182 			tail = vidtv_psi_eit_event_init(tail, event_id);
183 			if (!tail) {
184 				vidtv_psi_eit_event_destroy(head);
185 				return NULL;
186 			}
187 
188 			desc = vidtv_psi_desc_clone(curr->descriptor);
189 			vidtv_psi_desc_assign(&tail->descriptor, desc);
190 
191 			if (!head)
192 				head = tail;
193 
194 			curr = curr->next;
195 		}
196 
197 		cur_chnl = cur_chnl->next;
198 	}
199 
200 	return head;
201 }
202 
203 static struct vidtv_psi_table_sdt_service
vidtv_channel_sdt_serv_cat_into_new(struct vidtv_mux * m)204 *vidtv_channel_sdt_serv_cat_into_new(struct vidtv_mux *m)
205 {
206 	/* Concatenate the services */
207 	const struct vidtv_channel *cur_chnl = m->channels;
208 
209 	struct vidtv_psi_table_sdt_service *curr = NULL;
210 	struct vidtv_psi_table_sdt_service *head = NULL;
211 	struct vidtv_psi_table_sdt_service *tail = NULL;
212 
213 	struct vidtv_psi_desc *desc = NULL;
214 	u16 service_id;
215 
216 	if (!cur_chnl)
217 		return NULL;
218 
219 	while (cur_chnl) {
220 		curr = cur_chnl->service;
221 
222 		if (!curr)
223 			dev_warn_ratelimited(m->dev,
224 					     "No services found for channel %s\n",
225 					     cur_chnl->name);
226 
227 		while (curr) {
228 			service_id = be16_to_cpu(curr->service_id);
229 			tail = vidtv_psi_sdt_service_init(tail,
230 							  service_id,
231 							  curr->EIT_schedule,
232 							  curr->EIT_present_following);
233 			if (!tail)
234 				goto free;
235 
236 			desc = vidtv_psi_desc_clone(curr->descriptor);
237 			if (!desc)
238 				goto free_tail;
239 			vidtv_psi_desc_assign(&tail->descriptor, desc);
240 
241 			if (!head)
242 				head = tail;
243 
244 			curr = curr->next;
245 		}
246 
247 		cur_chnl = cur_chnl->next;
248 	}
249 
250 	return head;
251 
252 free_tail:
253 	vidtv_psi_sdt_service_destroy(tail);
254 free:
255 	vidtv_psi_sdt_service_destroy(head);
256 	return NULL;
257 }
258 
259 static struct vidtv_psi_table_pat_program*
vidtv_channel_pat_prog_cat_into_new(struct vidtv_mux * m)260 vidtv_channel_pat_prog_cat_into_new(struct vidtv_mux *m)
261 {
262 	/* Concatenate the programs */
263 	const struct vidtv_channel *cur_chnl = m->channels;
264 	struct vidtv_psi_table_pat_program *curr = NULL;
265 	struct vidtv_psi_table_pat_program *head = NULL;
266 	struct vidtv_psi_table_pat_program *tail = NULL;
267 	u16 serv_id;
268 	u16 pid;
269 
270 	if (!cur_chnl)
271 		return NULL;
272 
273 	while (cur_chnl) {
274 		curr = cur_chnl->program;
275 
276 		if (!curr)
277 			dev_warn_ratelimited(m->dev,
278 					     "No programs found for channel %s\n",
279 					     cur_chnl->name);
280 
281 		while (curr) {
282 			serv_id = be16_to_cpu(curr->service_id);
283 			pid = vidtv_psi_get_pat_program_pid(curr);
284 			tail = vidtv_psi_pat_program_init(tail,
285 							  serv_id,
286 							  pid);
287 			if (!tail) {
288 				vidtv_psi_pat_program_destroy(head);
289 				return NULL;
290 			}
291 
292 			if (!head)
293 				head = tail;
294 
295 			curr = curr->next;
296 		}
297 
298 		cur_chnl = cur_chnl->next;
299 	}
300 	/* Add the NIT table */
301 	vidtv_psi_pat_program_init(tail, 0, TS_NIT_PID);
302 
303 	return head;
304 }
305 
306 /*
307  * Match channels to their respective PMT sections, then assign the
308  * streams
309  */
310 static void
vidtv_channel_pmt_match_sections(struct vidtv_channel * channels,struct vidtv_psi_table_pmt ** sections,u32 nsections)311 vidtv_channel_pmt_match_sections(struct vidtv_channel *channels,
312 				 struct vidtv_psi_table_pmt **sections,
313 				 u32 nsections)
314 {
315 	struct vidtv_psi_table_pmt *curr_section = NULL;
316 	struct vidtv_psi_table_pmt_stream *head = NULL;
317 	struct vidtv_psi_table_pmt_stream *tail = NULL;
318 	struct vidtv_psi_table_pmt_stream *s = NULL;
319 	struct vidtv_channel *cur_chnl = channels;
320 	struct vidtv_psi_desc *desc = NULL;
321 	u16 e_pid; /* elementary stream pid */
322 	u16 curr_id;
323 	u32 j;
324 
325 	while (cur_chnl) {
326 		for (j = 0; j < nsections; ++j) {
327 			curr_section = sections[j];
328 
329 			if (!curr_section)
330 				continue;
331 
332 			curr_id = be16_to_cpu(curr_section->header.id);
333 
334 			/* we got a match */
335 			if (curr_id == cur_chnl->program_num) {
336 				s = cur_chnl->streams;
337 
338 				/* clone the streams for the PMT */
339 				while (s) {
340 					e_pid = vidtv_psi_pmt_stream_get_elem_pid(s);
341 					tail = vidtv_psi_pmt_stream_init(tail,
342 									 s->type,
343 									 e_pid);
344 
345 					if (!head)
346 						head = tail;
347 
348 					desc = vidtv_psi_desc_clone(s->descriptor);
349 					vidtv_psi_desc_assign(&tail->descriptor,
350 							      desc);
351 
352 					s = s->next;
353 				}
354 
355 				vidtv_psi_pmt_stream_assign(curr_section, head);
356 				break;
357 			}
358 		}
359 
360 		cur_chnl = cur_chnl->next;
361 	}
362 }
363 
364 static void
vidtv_channel_destroy_service_list(struct vidtv_psi_desc_service_list_entry * e)365 vidtv_channel_destroy_service_list(struct vidtv_psi_desc_service_list_entry *e)
366 {
367 	struct vidtv_psi_desc_service_list_entry *tmp;
368 
369 	while (e) {
370 		tmp = e;
371 		e = e->next;
372 		kfree(tmp);
373 	}
374 }
375 
376 static struct vidtv_psi_desc_service_list_entry
vidtv_channel_build_service_list(struct vidtv_psi_table_sdt_service * s)377 *vidtv_channel_build_service_list(struct vidtv_psi_table_sdt_service *s)
378 {
379 	struct vidtv_psi_desc_service_list_entry *curr_e = NULL;
380 	struct vidtv_psi_desc_service_list_entry *head_e = NULL;
381 	struct vidtv_psi_desc_service_list_entry *prev_e = NULL;
382 	struct vidtv_psi_desc *desc = s->descriptor;
383 	struct vidtv_psi_desc_service *s_desc;
384 
385 	while (s) {
386 		while (desc) {
387 			if (s->descriptor->type != SERVICE_DESCRIPTOR)
388 				goto next_desc;
389 
390 			s_desc = (struct vidtv_psi_desc_service *)desc;
391 
392 			curr_e = kzalloc(sizeof(*curr_e), GFP_KERNEL);
393 			if (!curr_e) {
394 				vidtv_channel_destroy_service_list(head_e);
395 				return NULL;
396 			}
397 
398 			curr_e->service_id = s->service_id;
399 			curr_e->service_type = s_desc->service_type;
400 
401 			if (!head_e)
402 				head_e = curr_e;
403 			if (prev_e)
404 				prev_e->next = curr_e;
405 
406 			prev_e = curr_e;
407 
408 next_desc:
409 			desc = desc->next;
410 		}
411 		s = s->next;
412 	}
413 	return head_e;
414 }
415 
vidtv_channel_si_init(struct vidtv_mux * m)416 int vidtv_channel_si_init(struct vidtv_mux *m)
417 {
418 	struct vidtv_psi_desc_service_list_entry *service_list = NULL;
419 	struct vidtv_psi_table_pat_program *programs = NULL;
420 	struct vidtv_psi_table_sdt_service *services = NULL;
421 	struct vidtv_psi_table_eit_event *events = NULL;
422 
423 	m->si.pat = vidtv_psi_pat_table_init(m->transport_stream_id);
424 	if (!m->si.pat)
425 		return -ENOMEM;
426 
427 	m->si.sdt = vidtv_psi_sdt_table_init(m->network_id,
428 					     m->transport_stream_id);
429 	if (!m->si.sdt)
430 		goto free_pat;
431 
432 	programs = vidtv_channel_pat_prog_cat_into_new(m);
433 	if (!programs)
434 		goto free_sdt;
435 	services = vidtv_channel_sdt_serv_cat_into_new(m);
436 	if (!services)
437 		goto free_programs;
438 
439 	events = vidtv_channel_eit_event_cat_into_new(m);
440 	if (!events)
441 		goto free_services;
442 
443 	/* look for a service descriptor for every service */
444 	service_list = vidtv_channel_build_service_list(services);
445 	if (!service_list)
446 		goto free_events;
447 
448 	/* use these descriptors to build the NIT */
449 	m->si.nit = vidtv_psi_nit_table_init(m->network_id,
450 					     m->transport_stream_id,
451 					     m->network_name,
452 					     service_list);
453 	if (!m->si.nit)
454 		goto free_service_list;
455 
456 	m->si.eit = vidtv_psi_eit_table_init(m->network_id,
457 					     m->transport_stream_id,
458 					     programs->service_id);
459 	if (!m->si.eit)
460 		goto free_nit;
461 
462 	/* assemble all programs and assign to PAT */
463 	vidtv_psi_pat_program_assign(m->si.pat, programs);
464 
465 	/* assemble all services and assign to SDT */
466 	vidtv_psi_sdt_service_assign(m->si.sdt, services);
467 
468 	/* assemble all events and assign to EIT */
469 	vidtv_psi_eit_event_assign(m->si.eit, events);
470 
471 	m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat,
472 								     m->pcr_pid);
473 	if (!m->si.pmt_secs)
474 		goto free_eit;
475 
476 	vidtv_channel_pmt_match_sections(m->channels,
477 					 m->si.pmt_secs,
478 					 m->si.pat->num_pmt);
479 
480 	vidtv_channel_destroy_service_list(service_list);
481 
482 	return 0;
483 
484 free_eit:
485 	vidtv_psi_eit_table_destroy(m->si.eit);
486 free_nit:
487 	vidtv_psi_nit_table_destroy(m->si.nit);
488 free_service_list:
489 	vidtv_channel_destroy_service_list(service_list);
490 free_events:
491 	vidtv_psi_eit_event_destroy(events);
492 free_services:
493 	vidtv_psi_sdt_service_destroy(services);
494 free_programs:
495 	vidtv_psi_pat_program_destroy(programs);
496 free_sdt:
497 	vidtv_psi_sdt_table_destroy(m->si.sdt);
498 free_pat:
499 	vidtv_psi_pat_table_destroy(m->si.pat);
500 	return 0;
501 }
502 
vidtv_channel_si_destroy(struct vidtv_mux * m)503 void vidtv_channel_si_destroy(struct vidtv_mux *m)
504 {
505 	u32 i;
506 
507 	for (i = 0; i < m->si.pat->num_pmt; ++i)
508 		vidtv_psi_pmt_table_destroy(m->si.pmt_secs[i]);
509 
510 	vidtv_psi_pat_table_destroy(m->si.pat);
511 
512 	kfree(m->si.pmt_secs);
513 	vidtv_psi_sdt_table_destroy(m->si.sdt);
514 	vidtv_psi_nit_table_destroy(m->si.nit);
515 	vidtv_psi_eit_table_destroy(m->si.eit);
516 }
517 
vidtv_channels_init(struct vidtv_mux * m)518 int vidtv_channels_init(struct vidtv_mux *m)
519 {
520 	/* this is the place to add new 'channels' for vidtv */
521 	m->channels = vidtv_channel_s302m_init(NULL, m->transport_stream_id);
522 
523 	if (!m->channels)
524 		return -ENOMEM;
525 
526 	return 0;
527 }
528 
vidtv_channels_destroy(struct vidtv_mux * m)529 void vidtv_channels_destroy(struct vidtv_mux *m)
530 {
531 	struct vidtv_channel *curr = m->channels;
532 	struct vidtv_channel *tmp = NULL;
533 
534 	while (curr) {
535 		kfree(curr->name);
536 		vidtv_psi_sdt_service_destroy(curr->service);
537 		vidtv_psi_pat_program_destroy(curr->program);
538 		vidtv_psi_pmt_stream_destroy(curr->streams);
539 		vidtv_channel_encoder_destroy(curr->encoders);
540 		vidtv_psi_eit_event_destroy(curr->events);
541 
542 		tmp = curr;
543 		curr = curr->next;
544 		kfree(tmp);
545 	}
546 }
547