mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 01:54:09 +08:00
9cfb4d36c2
It is better to use the higher level dev_foo() than pr_foo() for printks. Change them at vidtv at the more trivial places. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
308 lines
7.5 KiB
C
308 lines
7.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Vidtv serves as a reference DVB driver and helps validate the existing APIs
|
|
* in the media subsystem. It can also aid developers working on userspace
|
|
* applications.
|
|
*
|
|
* This file contains the code for a 'channel' abstraction.
|
|
*
|
|
* When vidtv boots, it will create some hardcoded channels.
|
|
* Their services will be concatenated to populate the SDT.
|
|
* Their programs will be concatenated to populate the PAT
|
|
* For each program in the PAT, a PMT section will be created
|
|
* The PMT section for a channel will be assigned its streams.
|
|
* Every stream will have its corresponding encoder polled to produce TS packets
|
|
* These packets may be interleaved by the mux and then delivered to the bridge
|
|
*
|
|
*
|
|
* Copyright (C) 2020 Daniel W. S. Almeida
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/dev_printk.h>
|
|
#include <linux/ratelimit.h>
|
|
|
|
#include "vidtv_channel.h"
|
|
#include "vidtv_psi.h"
|
|
#include "vidtv_encoder.h"
|
|
#include "vidtv_mux.h"
|
|
#include "vidtv_common.h"
|
|
#include "vidtv_s302m.h"
|
|
|
|
static void vidtv_channel_encoder_destroy(struct vidtv_encoder *e)
|
|
{
|
|
struct vidtv_encoder *curr = e;
|
|
struct vidtv_encoder *tmp = NULL;
|
|
|
|
while (curr) {
|
|
/* forward the call to the derived type */
|
|
tmp = curr;
|
|
curr = curr->next;
|
|
tmp->destroy(tmp);
|
|
}
|
|
}
|
|
|
|
struct vidtv_channel
|
|
*vidtv_channel_s302m_init(struct vidtv_channel *head, u16 transport_stream_id)
|
|
{
|
|
/*
|
|
* init an audio only channel with a s302m encoder
|
|
*/
|
|
const u16 s302m_service_id = 0x880;
|
|
const u16 s302m_program_num = 0x880;
|
|
const u16 s302m_program_pid = 0x101; /* packet id for PMT*/
|
|
const u16 s302m_es_pid = 0x111; /* packet id for the ES */
|
|
const __be32 s302m_fid = cpu_to_be32(VIDTV_S302M_FORMAT_IDENTIFIER);
|
|
|
|
char *name = "S302m: Sine Wave PCM Audio";
|
|
|
|
struct vidtv_channel *s302m = kzalloc(sizeof(*s302m), GFP_KERNEL);
|
|
struct vidtv_s302m_encoder_init_args encoder_args = {};
|
|
|
|
s302m->name = kstrdup(name, GFP_KERNEL);
|
|
|
|
s302m->service = vidtv_psi_sdt_service_init(NULL, s302m_service_id);
|
|
|
|
s302m->service->descriptor = (struct vidtv_psi_desc *)
|
|
vidtv_psi_service_desc_init(NULL,
|
|
DIGITAL_TELEVISION_SERVICE,
|
|
name,
|
|
NULL);
|
|
|
|
s302m->transport_stream_id = transport_stream_id;
|
|
|
|
s302m->program = vidtv_psi_pat_program_init(NULL,
|
|
s302m_service_id,
|
|
s302m_program_pid);
|
|
|
|
s302m->program_num = s302m_program_num;
|
|
|
|
s302m->streams = vidtv_psi_pmt_stream_init(NULL,
|
|
STREAM_PRIVATE_DATA,
|
|
s302m_es_pid);
|
|
|
|
s302m->streams->descriptor = (struct vidtv_psi_desc *)
|
|
vidtv_psi_registration_desc_init(NULL,
|
|
s302m_fid,
|
|
NULL,
|
|
0);
|
|
encoder_args.es_pid = s302m_es_pid;
|
|
|
|
s302m->encoders = vidtv_s302m_encoder_init(encoder_args);
|
|
|
|
if (head) {
|
|
while (head->next)
|
|
head = head->next;
|
|
|
|
head->next = s302m;
|
|
}
|
|
|
|
return s302m;
|
|
}
|
|
|
|
static struct vidtv_psi_table_sdt_service
|
|
*vidtv_channel_sdt_serv_cat_into_new(struct vidtv_mux *m)
|
|
{
|
|
/* Concatenate the services */
|
|
const struct vidtv_channel *cur_chnl = m->channels;
|
|
|
|
struct vidtv_psi_table_sdt_service *curr = NULL;
|
|
struct vidtv_psi_table_sdt_service *head = NULL;
|
|
struct vidtv_psi_table_sdt_service *tail = NULL;
|
|
|
|
struct vidtv_psi_desc *desc = NULL;
|
|
u16 service_id;
|
|
|
|
if (!cur_chnl)
|
|
return NULL;
|
|
|
|
while (cur_chnl) {
|
|
curr = cur_chnl->service;
|
|
|
|
if (!curr)
|
|
dev_warn_ratelimited(m->dev,
|
|
"No services found for channel %s\n", cur_chnl->name);
|
|
|
|
while (curr) {
|
|
service_id = be16_to_cpu(curr->service_id);
|
|
tail = vidtv_psi_sdt_service_init(tail, service_id);
|
|
|
|
desc = vidtv_psi_desc_clone(curr->descriptor);
|
|
vidtv_psi_desc_assign(&tail->descriptor, desc);
|
|
|
|
if (!head)
|
|
head = tail;
|
|
|
|
curr = curr->next;
|
|
}
|
|
|
|
cur_chnl = cur_chnl->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
static struct vidtv_psi_table_pat_program*
|
|
vidtv_channel_pat_prog_cat_into_new(struct vidtv_mux *m)
|
|
{
|
|
/* Concatenate the programs */
|
|
const struct vidtv_channel *cur_chnl = m->channels;
|
|
struct vidtv_psi_table_pat_program *curr = NULL;
|
|
struct vidtv_psi_table_pat_program *head = NULL;
|
|
struct vidtv_psi_table_pat_program *tail = NULL;
|
|
u16 serv_id;
|
|
u16 pid;
|
|
|
|
if (!cur_chnl)
|
|
return NULL;
|
|
|
|
while (cur_chnl) {
|
|
curr = cur_chnl->program;
|
|
|
|
if (!curr)
|
|
dev_warn_ratelimited(m->dev,
|
|
"No programs found for channel %s\n",
|
|
cur_chnl->name);
|
|
|
|
while (curr) {
|
|
serv_id = be16_to_cpu(curr->service_id);
|
|
pid = vidtv_psi_get_pat_program_pid(curr);
|
|
tail = vidtv_psi_pat_program_init(tail,
|
|
serv_id,
|
|
pid);
|
|
|
|
if (!head)
|
|
head = tail;
|
|
|
|
curr = curr->next;
|
|
}
|
|
|
|
cur_chnl = cur_chnl->next;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
static void
|
|
vidtv_channel_pmt_match_sections(struct vidtv_channel *channels,
|
|
struct vidtv_psi_table_pmt **sections,
|
|
u32 nsections)
|
|
{
|
|
/*
|
|
* Match channels to their respective PMT sections, then assign the
|
|
* streams
|
|
*/
|
|
struct vidtv_psi_table_pmt *curr_section = NULL;
|
|
struct vidtv_channel *cur_chnl = channels;
|
|
|
|
struct vidtv_psi_table_pmt_stream *s = NULL;
|
|
struct vidtv_psi_table_pmt_stream *head = NULL;
|
|
struct vidtv_psi_table_pmt_stream *tail = NULL;
|
|
|
|
struct vidtv_psi_desc *desc = NULL;
|
|
u32 j;
|
|
u16 curr_id;
|
|
u16 e_pid; /* elementary stream pid */
|
|
|
|
while (cur_chnl) {
|
|
for (j = 0; j < nsections; ++j) {
|
|
curr_section = sections[j];
|
|
|
|
if (!curr_section)
|
|
continue;
|
|
|
|
curr_id = be16_to_cpu(curr_section->header.id);
|
|
|
|
/* we got a match */
|
|
if (curr_id == cur_chnl->program_num) {
|
|
s = cur_chnl->streams;
|
|
|
|
/* clone the streams for the PMT */
|
|
while (s) {
|
|
e_pid = vidtv_psi_pmt_stream_get_elem_pid(s);
|
|
tail = vidtv_psi_pmt_stream_init(tail,
|
|
s->type,
|
|
e_pid);
|
|
|
|
if (!head)
|
|
head = tail;
|
|
|
|
desc = vidtv_psi_desc_clone(s->descriptor);
|
|
vidtv_psi_desc_assign(&tail->descriptor, desc);
|
|
|
|
s = s->next;
|
|
}
|
|
|
|
vidtv_psi_pmt_stream_assign(curr_section, head);
|
|
break;
|
|
}
|
|
}
|
|
|
|
cur_chnl = cur_chnl->next;
|
|
}
|
|
}
|
|
|
|
void vidtv_channel_si_init(struct vidtv_mux *m)
|
|
{
|
|
struct vidtv_psi_table_pat_program *programs = NULL;
|
|
struct vidtv_psi_table_sdt_service *services = NULL;
|
|
|
|
m->si.pat = vidtv_psi_pat_table_init(m->transport_stream_id);
|
|
|
|
m->si.sdt = vidtv_psi_sdt_table_init(m->transport_stream_id);
|
|
|
|
programs = vidtv_channel_pat_prog_cat_into_new(m);
|
|
services = vidtv_channel_sdt_serv_cat_into_new(m);
|
|
|
|
/* assemble all programs and assign to PAT */
|
|
vidtv_psi_pat_program_assign(m->si.pat, programs);
|
|
|
|
/* assemble all services and assign to SDT */
|
|
vidtv_psi_sdt_service_assign(m->si.sdt, services);
|
|
|
|
m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat, m->pcr_pid);
|
|
|
|
vidtv_channel_pmt_match_sections(m->channels,
|
|
m->si.pmt_secs,
|
|
m->si.pat->programs);
|
|
}
|
|
|
|
void vidtv_channel_si_destroy(struct vidtv_mux *m)
|
|
{
|
|
u32 i;
|
|
u16 num_programs = m->si.pat->programs;
|
|
|
|
vidtv_psi_pat_table_destroy(m->si.pat);
|
|
|
|
for (i = 0; i < num_programs; ++i)
|
|
vidtv_psi_pmt_table_destroy(m->si.pmt_secs[i]);
|
|
|
|
kfree(m->si.pmt_secs);
|
|
vidtv_psi_sdt_table_destroy(m->si.sdt);
|
|
}
|
|
|
|
void vidtv_channels_init(struct vidtv_mux *m)
|
|
{
|
|
/* this is the place to add new 'channels' for vidtv */
|
|
m->channels = vidtv_channel_s302m_init(NULL, m->transport_stream_id);
|
|
}
|
|
|
|
void vidtv_channels_destroy(struct vidtv_mux *m)
|
|
{
|
|
struct vidtv_channel *curr = m->channels;
|
|
struct vidtv_channel *tmp = NULL;
|
|
|
|
while (curr) {
|
|
kfree(curr->name);
|
|
vidtv_psi_sdt_service_destroy(curr->service);
|
|
vidtv_psi_pat_program_destroy(curr->program);
|
|
vidtv_psi_pmt_stream_destroy(curr->streams);
|
|
vidtv_channel_encoder_destroy(curr->encoders);
|
|
|
|
tmp = curr;
|
|
curr = curr->next;
|
|
kfree(tmp);
|
|
}
|
|
}
|