linux/drivers/media/test-drivers/vidtv/vidtv_channel.c
<<
>>
Prefs
   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
 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 void
 365vidtv_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_entry
 377*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