File Index
Files and directories
Sign in
FILE

vidtv_channel.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/test-drivers/vidtv/vidtv_channel.c C
  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(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
140free_events:
141	vidtv_psi_eit_event_destroy(s302m->events);
142free_encoders:
143	vidtv_s302m_encoder_destroy(s302m->encoders);
144free_streams:
145	vidtv_psi_pmt_stream_destroy(s302m->streams);
146free_program:
147	vidtv_psi_pat_program_destroy(s302m->program);
148free_service:
149	vidtv_psi_sdt_service_destroy(s302m->service);
150free_name:
151	kfree(s302m->name);
152free_s302m:
153	kfree(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(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
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(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
252free_tail:
253	vidtv_psi_sdt_service_destroy(tail);
254free:
255	vidtv_psi_sdt_service_destroy(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(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 */
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(tail,
342									 s->type,
343									 e_pid);
344					if (!tail) {
345						vidtv_psi_pmt_stream_destroy(head);
346						return;
347					}
348
349					if (!head)
350						head = tail;
351
352					desc = vidtv_psi_desc_clone(s->descriptor);
353					vidtv_psi_desc_assign(&tail->descriptor,
354							      desc);
355
356					s = s->next;
357				}
358
359				vidtv_psi_pmt_stream_assign(curr_section, head);
360				break;
361			}
362		}
363
364		cur_chnl = cur_chnl->next;
365	}
366}
367
368static void
369vidtv_channel_destroy_service_list(struct vidtv_psi_desc_service_list_entry *e)
370{
371	struct vidtv_psi_desc_service_list_entry *tmp;
372
373	while (e) {
374		tmp = e;
375		e = e->next;
376		kfree(tmp);
377	}
378}
379
380static struct vidtv_psi_desc_service_list_entry
381*vidtv_channel_build_service_list(struct vidtv_psi_table_sdt_service *s)
382{
383	struct vidtv_psi_desc_service_list_entry *curr_e = NULL;
384	struct vidtv_psi_desc_service_list_entry *head_e = NULL;
385	struct vidtv_psi_desc_service_list_entry *prev_e = NULL;
386	struct vidtv_psi_desc *desc = s->descriptor;
387	struct vidtv_psi_desc_service *s_desc;
388
389	while (s) {
390		while (desc) {
391			if (s->descriptor->type != SERVICE_DESCRIPTOR)
392				goto next_desc;
393
394			s_desc = (struct vidtv_psi_desc_service *)desc;
395
396			curr_e = kzalloc(sizeof(*curr_e), GFP_KERNEL);
397			if (!curr_e) {
398				vidtv_channel_destroy_service_list(head_e);
399				return NULL;
400			}
401
402			curr_e->service_id = s->service_id;
403			curr_e->service_type = s_desc->service_type;
404
405			if (!head_e)
406				head_e = curr_e;
407			if (prev_e)
408				prev_e->next = curr_e;
409
410			prev_e = curr_e;
411
412next_desc:
413			desc = desc->next;
414		}
415		s = s->next;
416	}
417	return head_e;
418}
419
420int vidtv_channel_si_init(struct vidtv_mux *m)
421{
422	struct vidtv_psi_desc_service_list_entry *service_list = NULL;
423	struct vidtv_psi_table_pat_program *programs = NULL;
424	struct vidtv_psi_table_sdt_service *services = NULL;
425	struct vidtv_psi_table_eit_event *events = NULL;
426
427	m->si.pat = vidtv_psi_pat_table_init(m->transport_stream_id);
428	if (!m->si.pat)
429		return -ENOMEM;
430
431	m->si.sdt = vidtv_psi_sdt_table_init(m->network_id,
432					     m->transport_stream_id);
433	if (!m->si.sdt)
434		goto free_pat;
435
436	programs = vidtv_channel_pat_prog_cat_into_new(m);
437	if (!programs)
438		goto free_sdt;
439	services = vidtv_channel_sdt_serv_cat_into_new(m);
440	if (!services)
441		goto free_programs;
442
443	events = vidtv_channel_eit_event_cat_into_new(m);
444	if (!events)
445		goto free_services;
446
447	/* look for a service descriptor for every service */
448	service_list = vidtv_channel_build_service_list(services);
449	if (!service_list)
450		goto free_events;
451
452	/* use these descriptors to build the NIT */
453	m->si.nit = vidtv_psi_nit_table_init(m->network_id,
454					     m->transport_stream_id,
455					     m->network_name,
456					     service_list);
457	if (!m->si.nit)
458		goto free_service_list;
459
460	m->si.eit = vidtv_psi_eit_table_init(m->network_id,
461					     m->transport_stream_id,
462					     programs->service_id);
463	if (!m->si.eit)
464		goto free_nit;
465
466	/* assemble all programs and assign to PAT */
467	vidtv_psi_pat_program_assign(m->si.pat, programs);
468	programs = NULL;
469
470	/* assemble all services and assign to SDT */
471	vidtv_psi_sdt_service_assign(m->si.sdt, services);
472	services = NULL;
473
474	/* assemble all events and assign to EIT */
475	vidtv_psi_eit_event_assign(m->si.eit, events);
476	events = NULL;
477
478	m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat,
479								     m->pcr_pid);
480	if (!m->si.pmt_secs)
481		goto free_eit;
482
483	vidtv_channel_pmt_match_sections(m->channels,
484					 m->si.pmt_secs,
485					 m->si.pat->num_pmt);
486
487	vidtv_channel_destroy_service_list(service_list);
488
489	return 0;
490
491free_eit:
492	vidtv_psi_eit_table_destroy(m->si.eit);
493free_nit:
494	vidtv_psi_nit_table_destroy(m->si.nit);
495free_service_list:
496	vidtv_channel_destroy_service_list(service_list);
497free_events:
498	vidtv_psi_eit_event_destroy(events);
499free_services:
500	vidtv_psi_sdt_service_destroy(services);
501free_programs:
502	vidtv_psi_pat_program_destroy(programs);
503free_sdt:
504	vidtv_psi_sdt_table_destroy(m->si.sdt);
505free_pat:
506	vidtv_psi_pat_table_destroy(m->si.pat);
507	return -EINVAL;
508}
509
510void vidtv_channel_si_destroy(struct vidtv_mux *m)
511{
512	u32 i;
513
514	for (i = 0; i < m->si.pat->num_pmt; ++i)
515		vidtv_psi_pmt_table_destroy(m->si.pmt_secs[i]);
516
517	vidtv_psi_pat_table_destroy(m->si.pat);
518
519	kfree(m->si.pmt_secs);
520	vidtv_psi_sdt_table_destroy(m->si.sdt);
521	vidtv_psi_nit_table_destroy(m->si.nit);
522	vidtv_psi_eit_table_destroy(m->si.eit);
523}
524
525int vidtv_channels_init(struct vidtv_mux *m)
526{
527	/* this is the place to add new 'channels' for vidtv */
528	m->channels = vidtv_channel_s302m_init(NULL, m->transport_stream_id);
529
530	if (!m->channels)
531		return -ENOMEM;
532
533	return 0;
534}
535
536void vidtv_channels_destroy(struct vidtv_mux *m)
537{
538	struct vidtv_channel *curr = m->channels;
539	struct vidtv_channel *tmp = NULL;
540
541	while (curr) {
542		kfree(curr->name);
543		vidtv_psi_sdt_service_destroy(curr->service);
544		vidtv_psi_pat_program_destroy(curr->program);
545		vidtv_psi_pmt_stream_destroy(curr->streams);
546		vidtv_channel_encoder_destroy(curr->encoders);
547		vidtv_psi_eit_event_destroy(curr->events);
548
549		tmp = curr;
550		curr = curr->next;
551		kfree(tmp);
552	}
553}
~/ Read-only file preview.