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
34static 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 */
53struct vidtv_channel
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(size: sizeof(*s302m), GFP_KERNEL);
71 if (!s302m)
72 return NULL;
73
74 s302m->name = kstrdup(s: name, GFP_KERNEL);
75 if (!s302m->name)
76 goto free_s302m;
77
78 s302m->service = vidtv_psi_sdt_service_init(NULL, service_id: s302m_service_id, eit_schedule: false, eit_present_following: 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 service_type: DIGITAL_RADIO_SOUND_SERVICE,
85 service_name: name,
86 provider_name: 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 service_id: s302m_service_id,
94 program_map_pid: 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_type: STREAM_PRIVATE_DATA,
102 es_pid: 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 format_id: s302m_fid,
109 NULL,
110 additional_info_len: 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(args: encoder_args);
117 if (!s302m->encoders)
118 goto free_streams;
119
120 s302m->events = vidtv_psi_eit_event_init(NULL, event_id: 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 text: 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
140free_events:
141 vidtv_psi_eit_event_destroy(e: s302m->events);
142free_encoders:
143 vidtv_s302m_encoder_destroy(encoder: s302m->encoders);
144free_streams:
145 vidtv_psi_pmt_stream_destroy(s: s302m->streams);
146free_program:
147 vidtv_psi_pat_program_destroy(p: s302m->program);
148free_service:
149 vidtv_psi_sdt_service_destroy(service: s302m->service);
150free_name:
151 kfree(objp: s302m->name);
152free_s302m:
153 kfree(objp: s302m);
154
155 return NULL;
156}
157
158static struct vidtv_psi_table_eit_event
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(head: tail, event_id);
183 if (!tail) {
184 vidtv_psi_eit_event_destroy(e: head);
185 return NULL;
186 }
187
188 desc = vidtv_psi_desc_clone(desc: curr->descriptor);
189 vidtv_psi_desc_assign(to: &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
203static struct vidtv_psi_table_sdt_service
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(head: tail,
230 service_id,
231 eit_schedule: curr->EIT_schedule,
232 eit_present_following: curr->EIT_present_following);
233 if (!tail)
234 goto free;
235
236 desc = vidtv_psi_desc_clone(desc: curr->descriptor);
237 if (!desc)
238 goto free_tail;
239 vidtv_psi_desc_assign(to: &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
252free_tail:
253 vidtv_psi_sdt_service_destroy(service: tail);
254free:
255 vidtv_psi_sdt_service_destroy(service: head);
256 return NULL;
257}
258
259static struct vidtv_psi_table_pat_program*
260vidtv_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(p: curr);
284 tail = vidtv_psi_pat_program_init(head: tail,
285 service_id: serv_id,
286 program_map_pid: pid);
287 if (!tail) {
288 vidtv_psi_pat_program_destroy(p: 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(head: tail, service_id: 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 */
310static void
311vidtv_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(head: tail,
342 stream_type: s->type,
343 es_pid: e_pid);
344
345 if (!head)
346 head = tail;
347
348 desc = vidtv_psi_desc_clone(desc: s->descriptor);
349 vidtv_psi_desc_assign(to: &tail->descriptor,
350 desc);
351
352 s = s->next;
353 }
354
355 vidtv_psi_pmt_stream_assign(pmt: curr_section, s: head);
356 break;
357 }
358 }
359
360 cur_chnl = cur_chnl->next;
361 }
362}
363
364static void
365vidtv_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(objp: tmp);
373 }
374}
375
376static struct vidtv_psi_desc_service_list_entry
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(size: sizeof(*curr_e), GFP_KERNEL);
393 if (!curr_e) {
394 vidtv_channel_destroy_service_list(e: 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
408next_desc:
409 desc = desc->next;
410 }
411 s = s->next;
412 }
413 return head_e;
414}
415
416int 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(transport_stream_id: m->transport_stream_id);
424 if (!m->si.pat)
425 return -ENOMEM;
426
427 m->si.sdt = vidtv_psi_sdt_table_init(network_id: m->network_id,
428 transport_stream_id: 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(s: 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(network_id: m->network_id,
450 transport_stream_id: m->transport_stream_id,
451 network_name: 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(network_id: m->network_id,
457 transport_stream_id: m->transport_stream_id,
458 service_id: 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(pat: m->si.pat, p: programs);
464
465 /* assemble all services and assign to SDT */
466 vidtv_psi_sdt_service_assign(sdt: m->si.sdt, service: services);
467
468 /* assemble all events and assign to EIT */
469 vidtv_psi_eit_event_assign(eit: m->si.eit, e: events);
470
471 m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(pat: m->si.pat,
472 pcr_pid: m->pcr_pid);
473 if (!m->si.pmt_secs)
474 goto free_eit;
475
476 vidtv_channel_pmt_match_sections(channels: m->channels,
477 sections: m->si.pmt_secs,
478 nsections: m->si.pat->num_pmt);
479
480 vidtv_channel_destroy_service_list(e: service_list);
481
482 return 0;
483
484free_eit:
485 vidtv_psi_eit_table_destroy(eit: m->si.eit);
486free_nit:
487 vidtv_psi_nit_table_destroy(nit: m->si.nit);
488free_service_list:
489 vidtv_channel_destroy_service_list(e: service_list);
490free_events:
491 vidtv_psi_eit_event_destroy(e: events);
492free_services:
493 vidtv_psi_sdt_service_destroy(service: services);
494free_programs:
495 vidtv_psi_pat_program_destroy(p: programs);
496free_sdt:
497 vidtv_psi_sdt_table_destroy(sdt: m->si.sdt);
498free_pat:
499 vidtv_psi_pat_table_destroy(p: m->si.pat);
500 return 0;
501}
502
503void 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(pmt: m->si.pmt_secs[i]);
509
510 vidtv_psi_pat_table_destroy(p: m->si.pat);
511
512 kfree(objp: m->si.pmt_secs);
513 vidtv_psi_sdt_table_destroy(sdt: m->si.sdt);
514 vidtv_psi_nit_table_destroy(nit: m->si.nit);
515 vidtv_psi_eit_table_destroy(eit: m->si.eit);
516}
517
518int 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, transport_stream_id: m->transport_stream_id);
522
523 if (!m->channels)
524 return -ENOMEM;
525
526 return 0;
527}
528
529void 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(objp: curr->name);
536 vidtv_psi_sdt_service_destroy(service: curr->service);
537 vidtv_psi_pat_program_destroy(p: curr->program);
538 vidtv_psi_pmt_stream_destroy(s: curr->streams);
539 vidtv_channel_encoder_destroy(e: curr->encoders);
540 vidtv_psi_eit_event_destroy(e: curr->events);
541
542 tmp = curr;
543 curr = curr->next;
544 kfree(objp: tmp);
545 }
546}
547

source code of linux/drivers/media/test-drivers/vidtv/vidtv_channel.c