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  * 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
16  *
17  *
18  * Copyright (C) 2020 Daniel W. S. Almeida
19  */
20 
21 #include <linux/types.h>
22 #include <linux/slab.h>
23 #include <linux/dev_printk.h>
24 #include <linux/ratelimit.h>
25 
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"
32 
33 static void vidtv_channel_encoder_destroy(struct vidtv_encoder *e)
34 {
35 	struct vidtv_encoder *curr = e;
36 	struct vidtv_encoder *tmp = NULL;
37 
38 	while (curr) {
39 		/* forward the call to the derived type */
40 		tmp = curr;
41 		curr = curr->next;
42 		tmp->destroy(tmp);
43 	}
44 }
45 
46 #define ENCODING_ISO8859_15 "\x0b"
47 
48 struct vidtv_channel
49 *vidtv_channel_s302m_init(struct vidtv_channel *head, u16 transport_stream_id)
50 {
51 	/*
52 	 * init an audio only channel with a s302m encoder
53 	 */
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);
59 
60 	char *name = ENCODING_ISO8859_15 "Beethoven";
61 	char *provider = ENCODING_ISO8859_15 "LinuxTV.org";
62 
63 	struct vidtv_channel *s302m = kzalloc(sizeof(*s302m), GFP_KERNEL);
64 	struct vidtv_s302m_encoder_init_args encoder_args = {};
65 
66 	s302m->name = kstrdup(name, GFP_KERNEL);
67 
68 	s302m->service = vidtv_psi_sdt_service_init(NULL, s302m_service_id);
69 
70 	s302m->service->descriptor = (struct vidtv_psi_desc *)
71 				     vidtv_psi_service_desc_init(NULL,
72 								 DIGITAL_TELEVISION_SERVICE,
73 								 name,
74 								 provider);
75 
76 	s302m->transport_stream_id = transport_stream_id;
77 
78 	s302m->program = vidtv_psi_pat_program_init(NULL,
79 						    s302m_service_id,
80 						    s302m_program_pid);
81 
82 	s302m->program_num = s302m_program_num;
83 
84 	s302m->streams = vidtv_psi_pmt_stream_init(NULL,
85 						   STREAM_PRIVATE_DATA,
86 						   s302m_es_pid);
87 
88 	s302m->streams->descriptor = (struct vidtv_psi_desc *)
89 				     vidtv_psi_registration_desc_init(NULL,
90 								      s302m_fid,
91 								      NULL,
92 								      0);
93 	encoder_args.es_pid = s302m_es_pid;
94 
95 	s302m->encoders = vidtv_s302m_encoder_init(encoder_args);
96 
97 	if (head) {
98 		while (head->next)
99 			head = head->next;
100 
101 		head->next = s302m;
102 	}
103 
104 	return s302m;
105 }
106 
107 static struct vidtv_psi_table_sdt_service
108 *vidtv_channel_sdt_serv_cat_into_new(struct vidtv_mux *m)
109 {
110 	/* Concatenate the services */
111 	const struct vidtv_channel *cur_chnl = m->channels;
112 
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;
116 
117 	struct vidtv_psi_desc *desc = NULL;
118 	u16 service_id;
119 
120 	if (!cur_chnl)
121 		return NULL;
122 
123 	while (cur_chnl) {
124 		curr = cur_chnl->service;
125 
126 		if (!curr)
127 			dev_warn_ratelimited(m->dev,
128 					     "No services found for channel %s\n", cur_chnl->name);
129 
130 		while (curr) {
131 			service_id = be16_to_cpu(curr->service_id);
132 			tail = vidtv_psi_sdt_service_init(tail, service_id);
133 
134 			desc = vidtv_psi_desc_clone(curr->descriptor);
135 			vidtv_psi_desc_assign(&tail->descriptor, desc);
136 
137 			if (!head)
138 				head = tail;
139 
140 			curr = curr->next;
141 		}
142 
143 		cur_chnl = cur_chnl->next;
144 	}
145 
146 	return head;
147 }
148 
149 static struct vidtv_psi_table_pat_program*
150 vidtv_channel_pat_prog_cat_into_new(struct vidtv_mux *m)
151 {
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;
157 	u16 serv_id;
158 	u16 pid;
159 
160 	if (!cur_chnl)
161 		return NULL;
162 
163 	while (cur_chnl) {
164 		curr = cur_chnl->program;
165 
166 		if (!curr)
167 			dev_warn_ratelimited(m->dev,
168 					     "No programs found for channel %s\n",
169 					     cur_chnl->name);
170 
171 		while (curr) {
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,
175 							  serv_id,
176 							  pid);
177 
178 			if (!head)
179 				head = tail;
180 
181 			curr = curr->next;
182 		}
183 
184 		cur_chnl = cur_chnl->next;
185 	}
186 
187 	return head;
188 }
189 
190 static void
191 vidtv_channel_pmt_match_sections(struct vidtv_channel *channels,
192 				 struct vidtv_psi_table_pmt **sections,
193 				 u32 nsections)
194 {
195 	/*
196 	 * Match channels to their respective PMT sections, then assign the
197 	 * streams
198 	 */
199 	struct vidtv_psi_table_pmt *curr_section = NULL;
200 	struct vidtv_channel *cur_chnl = channels;
201 
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;
205 
206 	struct vidtv_psi_desc *desc = NULL;
207 	u32 j;
208 	u16 curr_id;
209 	u16 e_pid; /* elementary stream pid */
210 
211 	while (cur_chnl) {
212 		for (j = 0; j < nsections; ++j) {
213 			curr_section = sections[j];
214 
215 			if (!curr_section)
216 				continue;
217 
218 			curr_id = be16_to_cpu(curr_section->header.id);
219 
220 			/* we got a match */
221 			if (curr_id == cur_chnl->program_num) {
222 				s = cur_chnl->streams;
223 
224 				/* clone the streams for the PMT */
225 				while (s) {
226 					e_pid = vidtv_psi_pmt_stream_get_elem_pid(s);
227 					tail = vidtv_psi_pmt_stream_init(tail,
228 									 s->type,
229 									 e_pid);
230 
231 					if (!head)
232 						head = tail;
233 
234 					desc = vidtv_psi_desc_clone(s->descriptor);
235 					vidtv_psi_desc_assign(&tail->descriptor, desc);
236 
237 					s = s->next;
238 				}
239 
240 				vidtv_psi_pmt_stream_assign(curr_section, head);
241 				break;
242 			}
243 		}
244 
245 		cur_chnl = cur_chnl->next;
246 	}
247 }
248 
249 void vidtv_channel_si_init(struct vidtv_mux *m)
250 {
251 	struct vidtv_psi_table_pat_program *programs = NULL;
252 	struct vidtv_psi_table_sdt_service *services = NULL;
253 
254 	m->si.pat = vidtv_psi_pat_table_init(m->transport_stream_id);
255 
256 	m->si.sdt = vidtv_psi_sdt_table_init(m->transport_stream_id);
257 
258 	programs = vidtv_channel_pat_prog_cat_into_new(m);
259 	services = vidtv_channel_sdt_serv_cat_into_new(m);
260 
261 	/* assemble all programs and assign to PAT */
262 	vidtv_psi_pat_program_assign(m->si.pat, programs);
263 
264 	/* assemble all services and assign to SDT */
265 	vidtv_psi_sdt_service_assign(m->si.sdt, services);
266 
267 	m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat, m->pcr_pid);
268 
269 	vidtv_channel_pmt_match_sections(m->channels,
270 					 m->si.pmt_secs,
271 					 m->si.pat->programs);
272 }
273 
274 void vidtv_channel_si_destroy(struct vidtv_mux *m)
275 {
276 	u32 i;
277 	u16 num_programs = m->si.pat->programs;
278 
279 	vidtv_psi_pat_table_destroy(m->si.pat);
280 
281 	for (i = 0; i < num_programs; ++i)
282 		vidtv_psi_pmt_table_destroy(m->si.pmt_secs[i]);
283 
284 	kfree(m->si.pmt_secs);
285 	vidtv_psi_sdt_table_destroy(m->si.sdt);
286 }
287 
288 void vidtv_channels_init(struct vidtv_mux *m)
289 {
290 	/* this is the place to add new 'channels' for vidtv */
291 	m->channels = vidtv_channel_s302m_init(NULL, m->transport_stream_id);
292 }
293 
294 void vidtv_channels_destroy(struct vidtv_mux *m)
295 {
296 	struct vidtv_channel *curr = m->channels;
297 	struct vidtv_channel *tmp = NULL;
298 
299 	while (curr) {
300 		kfree(curr->name);
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);
305 
306 		tmp = curr;
307 		curr = curr->next;
308 		kfree(tmp);
309 	}
310 }
311