brintos

brintos / linux-shallow public Read only

0
0
Text · 13.2 KiB · 7838e62 Raw
547 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Vidtv serves as a reference DVB driver and helps validate the existing APIs4 * in the media subsystem. It can also aid developers working on userspace5 * 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 PAT12 * Their events will be concatenated to populate the EIT13 * For each program in the PAT, a PMT section will be created14 * The PMT section for a channel will be assigned its streams.15 * Every stream will have its corresponding encoder polled to produce TS packets16 * These packets may be interleaved by the mux and then delivered to the bridge17 *18 *19 * Copyright (C) 2020 Daniel W. S. Almeida20 */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	0x1049 50/*51 * init an audio only channel with a s302m encoder52 */53struct vidtv_channel54*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_event159*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_service204*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 the308 * streams309 */310static void311vidtv_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 364static void365vidtv_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 376static struct vidtv_psi_desc_service_list_entry377*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 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(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 484free_eit:485	vidtv_psi_eit_table_destroy(m->si.eit);486free_nit:487	vidtv_psi_nit_table_destroy(m->si.nit);488free_service_list:489	vidtv_channel_destroy_service_list(service_list);490free_events:491	vidtv_psi_eit_event_destroy(events);492free_services:493	vidtv_psi_sdt_service_destroy(services);494free_programs:495	vidtv_psi_pat_program_destroy(programs);496free_sdt:497	vidtv_psi_sdt_table_destroy(m->si.sdt);498free_pat:499	vidtv_psi_pat_table_destroy(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(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 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, 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(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