1 // SPDX-License-Identifier: GPL-2.0
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
7 * This file contains the code for a 'channel' abstraction.
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 * For each program in the PAT, a PMT section will be created
13 * The PMT section for a channel will be assigned its streams.
14 * Every stream will have its corresponding encoder polled to produce TS packets
15 * These packets may be interleaved by the mux and then delivered to the bridge
18 * Copyright (C) 2020 Daniel W. S. Almeida
21 #include <linux/types.h>
22 #include <linux/slab.h>
23 #include <linux/dev_printk.h>
24 #include <linux/ratelimit.h>
26 #include "vidtv_channel.h"
27 #include "vidtv_psi.h"
28 #include "vidtv_encoder.h"
29 #include "vidtv_mux.h"
30 #include "vidtv_common.h"
31 #include "vidtv_s302m.h"
33 static void vidtv_channel_encoder_destroy(struct vidtv_encoder *e)
35 struct vidtv_encoder *curr = e;
36 struct vidtv_encoder *tmp = NULL;
39 /* forward the call to the derived type */
46 #define ENCODING_ISO8859_15 "\x0b"
49 *vidtv_channel_s302m_init(struct vidtv_channel *head, u16 transport_stream_id)
52 * init an audio only channel with a s302m encoder
54 const u16 s302m_service_id = 0x880;
55 const u16 s302m_program_num = 0x880;
56 const u16 s302m_program_pid = 0x101; /* packet id for PMT*/
57 const u16 s302m_es_pid = 0x111; /* packet id for the ES */
58 const __be32 s302m_fid = cpu_to_be32(VIDTV_S302M_FORMAT_IDENTIFIER);
60 char *name = ENCODING_ISO8859_15 "Beethoven";
61 char *provider = ENCODING_ISO8859_15 "LinuxTV.org";
63 struct vidtv_channel *s302m = kzalloc(sizeof(*s302m), GFP_KERNEL);
64 struct vidtv_s302m_encoder_init_args encoder_args = {};
66 s302m->name = kstrdup(name, GFP_KERNEL);
68 s302m->service = vidtv_psi_sdt_service_init(NULL, s302m_service_id);
70 s302m->service->descriptor = (struct vidtv_psi_desc *)
71 vidtv_psi_service_desc_init(NULL,
72 DIGITAL_TELEVISION_SERVICE,
76 s302m->transport_stream_id = transport_stream_id;
78 s302m->program = vidtv_psi_pat_program_init(NULL,
82 s302m->program_num = s302m_program_num;
84 s302m->streams = vidtv_psi_pmt_stream_init(NULL,
88 s302m->streams->descriptor = (struct vidtv_psi_desc *)
89 vidtv_psi_registration_desc_init(NULL,
93 encoder_args.es_pid = s302m_es_pid;
95 s302m->encoders = vidtv_s302m_encoder_init(encoder_args);
107 static struct vidtv_psi_table_sdt_service
108 *vidtv_channel_sdt_serv_cat_into_new(struct vidtv_mux *m)
110 /* Concatenate the services */
111 const struct vidtv_channel *cur_chnl = m->channels;
113 struct vidtv_psi_table_sdt_service *curr = NULL;
114 struct vidtv_psi_table_sdt_service *head = NULL;
115 struct vidtv_psi_table_sdt_service *tail = NULL;
117 struct vidtv_psi_desc *desc = NULL;
124 curr = cur_chnl->service;
127 dev_warn_ratelimited(m->dev,
128 "No services found for channel %s\n", cur_chnl->name);
131 service_id = be16_to_cpu(curr->service_id);
132 tail = vidtv_psi_sdt_service_init(tail, service_id);
134 desc = vidtv_psi_desc_clone(curr->descriptor);
135 vidtv_psi_desc_assign(&tail->descriptor, desc);
143 cur_chnl = cur_chnl->next;
149 static struct vidtv_psi_table_pat_program*
150 vidtv_channel_pat_prog_cat_into_new(struct vidtv_mux *m)
152 /* Concatenate the programs */
153 const struct vidtv_channel *cur_chnl = m->channels;
154 struct vidtv_psi_table_pat_program *curr = NULL;
155 struct vidtv_psi_table_pat_program *head = NULL;
156 struct vidtv_psi_table_pat_program *tail = NULL;
164 curr = cur_chnl->program;
167 dev_warn_ratelimited(m->dev,
168 "No programs found for channel %s\n",
172 serv_id = be16_to_cpu(curr->service_id);
173 pid = vidtv_psi_get_pat_program_pid(curr);
174 tail = vidtv_psi_pat_program_init(tail,
184 cur_chnl = cur_chnl->next;
191 vidtv_channel_pmt_match_sections(struct vidtv_channel *channels,
192 struct vidtv_psi_table_pmt **sections,
196 * Match channels to their respective PMT sections, then assign the
199 struct vidtv_psi_table_pmt *curr_section = NULL;
200 struct vidtv_channel *cur_chnl = channels;
202 struct vidtv_psi_table_pmt_stream *s = NULL;
203 struct vidtv_psi_table_pmt_stream *head = NULL;
204 struct vidtv_psi_table_pmt_stream *tail = NULL;
206 struct vidtv_psi_desc *desc = NULL;
209 u16 e_pid; /* elementary stream pid */
212 for (j = 0; j < nsections; ++j) {
213 curr_section = sections[j];
218 curr_id = be16_to_cpu(curr_section->header.id);
221 if (curr_id == cur_chnl->program_num) {
222 s = cur_chnl->streams;
224 /* clone the streams for the PMT */
226 e_pid = vidtv_psi_pmt_stream_get_elem_pid(s);
227 tail = vidtv_psi_pmt_stream_init(tail,
234 desc = vidtv_psi_desc_clone(s->descriptor);
235 vidtv_psi_desc_assign(&tail->descriptor, desc);
240 vidtv_psi_pmt_stream_assign(curr_section, head);
245 cur_chnl = cur_chnl->next;
249 void vidtv_channel_si_init(struct vidtv_mux *m)
251 struct vidtv_psi_table_pat_program *programs = NULL;
252 struct vidtv_psi_table_sdt_service *services = NULL;
254 m->si.pat = vidtv_psi_pat_table_init(m->transport_stream_id);
256 m->si.sdt = vidtv_psi_sdt_table_init(m->transport_stream_id);
258 programs = vidtv_channel_pat_prog_cat_into_new(m);
259 services = vidtv_channel_sdt_serv_cat_into_new(m);
261 /* assemble all programs and assign to PAT */
262 vidtv_psi_pat_program_assign(m->si.pat, programs);
264 /* assemble all services and assign to SDT */
265 vidtv_psi_sdt_service_assign(m->si.sdt, services);
267 m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat, m->pcr_pid);
269 vidtv_channel_pmt_match_sections(m->channels,
271 m->si.pat->programs);
274 void vidtv_channel_si_destroy(struct vidtv_mux *m)
277 u16 num_programs = m->si.pat->programs;
279 vidtv_psi_pat_table_destroy(m->si.pat);
281 for (i = 0; i < num_programs; ++i)
282 vidtv_psi_pmt_table_destroy(m->si.pmt_secs[i]);
284 kfree(m->si.pmt_secs);
285 vidtv_psi_sdt_table_destroy(m->si.sdt);
288 void vidtv_channels_init(struct vidtv_mux *m)
290 /* this is the place to add new 'channels' for vidtv */
291 m->channels = vidtv_channel_s302m_init(NULL, m->transport_stream_id);
294 void vidtv_channels_destroy(struct vidtv_mux *m)
296 struct vidtv_channel *curr = m->channels;
297 struct vidtv_channel *tmp = NULL;
301 vidtv_psi_sdt_service_destroy(curr->service);
302 vidtv_psi_pat_program_destroy(curr->program);
303 vidtv_psi_pmt_stream_destroy(curr->streams);
304 vidtv_channel_encoder_destroy(curr->encoders);