ACPI: acpi_drivers.h: Update the kernel doc
[linux-2.6-microblaze.git] / drivers / media / test-drivers / vidtv / vidtv_channel.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  * 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 }