1f90cf607SDaniel W. S. Almeida // SPDX-License-Identifier: GPL-2.0 2f90cf607SDaniel W. S. Almeida /* 3f90cf607SDaniel W. S. Almeida * Vidtv serves as a reference DVB driver and helps validate the existing APIs 4f90cf607SDaniel W. S. Almeida * in the media subsystem. It can also aid developers working on userspace 5f90cf607SDaniel W. S. Almeida * applications. 6f90cf607SDaniel W. S. Almeida * 7f90cf607SDaniel W. S. Almeida * This file contains the multiplexer logic for TS packets from different 8f90cf607SDaniel W. S. Almeida * elementary streams 9f90cf607SDaniel W. S. Almeida * 10f90cf607SDaniel W. S. Almeida * Loosely based on libavcodec/mpegtsenc.c 11f90cf607SDaniel W. S. Almeida * 12f90cf607SDaniel W. S. Almeida * Copyright (C) 2020 Daniel W. S. Almeida 13f90cf607SDaniel W. S. Almeida */ 14f90cf607SDaniel W. S. Almeida 158922e393SMauro Carvalho Chehab #include <linux/delay.h> 168922e393SMauro Carvalho Chehab #include <linux/dev_printk.h> 17f90cf607SDaniel W. S. Almeida #include <linux/jiffies.h> 18f90cf607SDaniel W. S. Almeida #include <linux/kernel.h> 19f90cf607SDaniel W. S. Almeida #include <linux/math64.h> 208922e393SMauro Carvalho Chehab #include <linux/ratelimit.h> 218922e393SMauro Carvalho Chehab #include <linux/slab.h> 228922e393SMauro Carvalho Chehab #include <linux/types.h> 238922e393SMauro Carvalho Chehab #include <linux/vmalloc.h> 2482d00a1aSMauro Carvalho Chehab 25f90cf607SDaniel W. S. Almeida #include "vidtv_channel.h" 26f90cf607SDaniel W. S. Almeida #include "vidtv_common.h" 278922e393SMauro Carvalho Chehab #include "vidtv_encoder.h" 288922e393SMauro Carvalho Chehab #include "vidtv_mux.h" 298922e393SMauro Carvalho Chehab #include "vidtv_pes.h" 30f90cf607SDaniel W. S. Almeida #include "vidtv_psi.h" 318922e393SMauro Carvalho Chehab #include "vidtv_ts.h" 32f90cf607SDaniel W. S. Almeida 33f90cf607SDaniel W. S. Almeida static struct vidtv_mux_pid_ctx 34f90cf607SDaniel W. S. Almeida *vidtv_mux_get_pid_ctx(struct vidtv_mux *m, u16 pid) 35f90cf607SDaniel W. S. Almeida { 36f90cf607SDaniel W. S. Almeida struct vidtv_mux_pid_ctx *ctx; 37f90cf607SDaniel W. S. Almeida 38f90cf607SDaniel W. S. Almeida hash_for_each_possible(m->pid_ctx, ctx, h, pid) 39f90cf607SDaniel W. S. Almeida if (ctx->pid == pid) 40f90cf607SDaniel W. S. Almeida return ctx; 41f90cf607SDaniel W. S. Almeida return NULL; 42f90cf607SDaniel W. S. Almeida } 43f90cf607SDaniel W. S. Almeida 44f90cf607SDaniel W. S. Almeida static struct vidtv_mux_pid_ctx 45f90cf607SDaniel W. S. Almeida *vidtv_mux_create_pid_ctx_once(struct vidtv_mux *m, u16 pid) 46f90cf607SDaniel W. S. Almeida { 47f90cf607SDaniel W. S. Almeida struct vidtv_mux_pid_ctx *ctx; 48f90cf607SDaniel W. S. Almeida 49f90cf607SDaniel W. S. Almeida ctx = vidtv_mux_get_pid_ctx(m, pid); 50f90cf607SDaniel W. S. Almeida if (ctx) 513be80379SMauro Carvalho Chehab return ctx; 52f90cf607SDaniel W. S. Almeida 53f90cf607SDaniel W. S. Almeida ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 543be80379SMauro Carvalho Chehab if (!ctx) 553be80379SMauro Carvalho Chehab return NULL; 563be80379SMauro Carvalho Chehab 57f90cf607SDaniel W. S. Almeida ctx->pid = pid; 58f90cf607SDaniel W. S. Almeida ctx->cc = 0; 59f90cf607SDaniel W. S. Almeida hash_add(m->pid_ctx, &ctx->h, pid); 60f90cf607SDaniel W. S. Almeida 61f90cf607SDaniel W. S. Almeida return ctx; 62f90cf607SDaniel W. S. Almeida } 63f90cf607SDaniel W. S. Almeida 64f90cf607SDaniel W. S. Almeida static void vidtv_mux_pid_ctx_destroy(struct vidtv_mux *m) 65f90cf607SDaniel W. S. Almeida { 66f90cf607SDaniel W. S. Almeida struct vidtv_mux_pid_ctx *ctx; 67f90cf607SDaniel W. S. Almeida struct hlist_node *tmp; 68a8bd461cSMauro Carvalho Chehab int bkt; 69f90cf607SDaniel W. S. Almeida 70f90cf607SDaniel W. S. Almeida hash_for_each_safe(m->pid_ctx, bkt, tmp, ctx, h) { 71f90cf607SDaniel W. S. Almeida hash_del(&ctx->h); 72f90cf607SDaniel W. S. Almeida kfree(ctx); 73f90cf607SDaniel W. S. Almeida } 74f90cf607SDaniel W. S. Almeida } 75f90cf607SDaniel W. S. Almeida 763be80379SMauro Carvalho Chehab static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m) 773be80379SMauro Carvalho Chehab { 783be80379SMauro Carvalho Chehab struct vidtv_psi_table_pat_program *p = m->si.pat->program; 793be80379SMauro Carvalho Chehab u16 pid; 803be80379SMauro Carvalho Chehab 813be80379SMauro Carvalho Chehab hash_init(m->pid_ctx); 823be80379SMauro Carvalho Chehab /* push the pcr pid ctx */ 833be80379SMauro Carvalho Chehab if (!vidtv_mux_create_pid_ctx_once(m, m->pcr_pid)) 843be80379SMauro Carvalho Chehab return -ENOMEM; 853be80379SMauro Carvalho Chehab /* push the NULL packet pid ctx */ 863be80379SMauro Carvalho Chehab if (!vidtv_mux_create_pid_ctx_once(m, TS_NULL_PACKET_PID)) 873be80379SMauro Carvalho Chehab goto free; 883be80379SMauro Carvalho Chehab /* push the PAT pid ctx */ 893be80379SMauro Carvalho Chehab if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_PAT_PID)) 903be80379SMauro Carvalho Chehab goto free; 913be80379SMauro Carvalho Chehab /* push the SDT pid ctx */ 923be80379SMauro Carvalho Chehab if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_SDT_PID)) 933be80379SMauro Carvalho Chehab goto free; 943be80379SMauro Carvalho Chehab /* push the NIT pid ctx */ 953be80379SMauro Carvalho Chehab if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_NIT_PID)) 963be80379SMauro Carvalho Chehab goto free; 973be80379SMauro Carvalho Chehab /* push the EIT pid ctx */ 983be80379SMauro Carvalho Chehab if (!vidtv_mux_create_pid_ctx_once(m, VIDTV_EIT_PID)) 993be80379SMauro Carvalho Chehab goto free; 1003be80379SMauro Carvalho Chehab 1013be80379SMauro Carvalho Chehab /* add a ctx for all PMT sections */ 1023be80379SMauro Carvalho Chehab while (p) { 1033be80379SMauro Carvalho Chehab pid = vidtv_psi_get_pat_program_pid(p); 1043be80379SMauro Carvalho Chehab vidtv_mux_create_pid_ctx_once(m, pid); 1053be80379SMauro Carvalho Chehab p = p->next; 1063be80379SMauro Carvalho Chehab } 1073be80379SMauro Carvalho Chehab 1083be80379SMauro Carvalho Chehab return 0; 1093be80379SMauro Carvalho Chehab 1103be80379SMauro Carvalho Chehab free: 1113be80379SMauro Carvalho Chehab vidtv_mux_pid_ctx_destroy(m); 1123be80379SMauro Carvalho Chehab return -ENOMEM; 1133be80379SMauro Carvalho Chehab } 1143be80379SMauro Carvalho Chehab 115f90cf607SDaniel W. S. Almeida static void vidtv_mux_update_clk(struct vidtv_mux *m) 116f90cf607SDaniel W. S. Almeida { 117f90cf607SDaniel W. S. Almeida /* call this at every thread iteration */ 118f90cf607SDaniel W. S. Almeida u64 elapsed_time; 119f90cf607SDaniel W. S. Almeida 120880a8fc0SMauro Carvalho Chehab m->timing.past_jiffies = m->timing.current_jiffies; 121f90cf607SDaniel W. S. Almeida m->timing.current_jiffies = get_jiffies_64(); 122f90cf607SDaniel W. S. Almeida 123f90cf607SDaniel W. S. Almeida elapsed_time = jiffies_to_usecs(m->timing.current_jiffies - 124f90cf607SDaniel W. S. Almeida m->timing.past_jiffies); 125f90cf607SDaniel W. S. Almeida 126f90cf607SDaniel W. S. Almeida /* update the 27Mhz clock proportionally to the elapsed time */ 127f90cf607SDaniel W. S. Almeida m->timing.clk += (CLOCK_UNIT_27MHZ / USEC_PER_SEC) * elapsed_time; 128f90cf607SDaniel W. S. Almeida } 129f90cf607SDaniel W. S. Almeida 130f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_push_si(struct vidtv_mux *m) 131f90cf607SDaniel W. S. Almeida { 132c857b065SMauro Carvalho Chehab struct vidtv_psi_pat_write_args pat_args = { 133c857b065SMauro Carvalho Chehab .buf = m->mux_buf, 134c857b065SMauro Carvalho Chehab .buf_sz = m->mux_buf_sz, 135c857b065SMauro Carvalho Chehab .pat = m->si.pat, 136c857b065SMauro Carvalho Chehab }; 137c857b065SMauro Carvalho Chehab struct vidtv_psi_pmt_write_args pmt_args = { 138c857b065SMauro Carvalho Chehab .buf = m->mux_buf, 139c857b065SMauro Carvalho Chehab .buf_sz = m->mux_buf_sz, 140c857b065SMauro Carvalho Chehab .pcr_pid = m->pcr_pid, 141c857b065SMauro Carvalho Chehab }; 142c857b065SMauro Carvalho Chehab struct vidtv_psi_sdt_write_args sdt_args = { 143c857b065SMauro Carvalho Chehab .buf = m->mux_buf, 144c857b065SMauro Carvalho Chehab .buf_sz = m->mux_buf_sz, 145c857b065SMauro Carvalho Chehab .sdt = m->si.sdt, 146c857b065SMauro Carvalho Chehab }; 147c857b065SMauro Carvalho Chehab struct vidtv_psi_nit_write_args nit_args = { 148c857b065SMauro Carvalho Chehab .buf = m->mux_buf, 149c857b065SMauro Carvalho Chehab .buf_sz = m->mux_buf_sz, 150c857b065SMauro Carvalho Chehab .nit = m->si.nit, 151c857b065SMauro Carvalho Chehab 152c857b065SMauro Carvalho Chehab }; 153c857b065SMauro Carvalho Chehab struct vidtv_psi_eit_write_args eit_args = { 154c857b065SMauro Carvalho Chehab .buf = m->mux_buf, 155c857b065SMauro Carvalho Chehab .buf_sz = m->mux_buf_sz, 156c857b065SMauro Carvalho Chehab .eit = m->si.eit, 157c857b065SMauro Carvalho Chehab }; 158a8bd461cSMauro Carvalho Chehab u32 initial_offset = m->mux_buf_offset; 159a8bd461cSMauro Carvalho Chehab struct vidtv_mux_pid_ctx *pat_ctx; 160a8bd461cSMauro Carvalho Chehab struct vidtv_mux_pid_ctx *pmt_ctx; 161a8bd461cSMauro Carvalho Chehab struct vidtv_mux_pid_ctx *sdt_ctx; 162a8bd461cSMauro Carvalho Chehab struct vidtv_mux_pid_ctx *nit_ctx; 163a8bd461cSMauro Carvalho Chehab struct vidtv_mux_pid_ctx *eit_ctx; 164a8bd461cSMauro Carvalho Chehab u32 nbytes; 165f90cf607SDaniel W. S. Almeida u16 pmt_pid; 166f90cf607SDaniel W. S. Almeida u32 i; 167f90cf607SDaniel W. S. Almeida 168f90cf607SDaniel W. S. Almeida pat_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_PAT_PID); 169f90cf607SDaniel W. S. Almeida sdt_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_SDT_PID); 170c2f78f0cSDaniel W. S. Almeida nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID); 1717a7899f6SDaniel W. S. Almeida eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID); 172f90cf607SDaniel W. S. Almeida 173f90cf607SDaniel W. S. Almeida pat_args.offset = m->mux_buf_offset; 174f90cf607SDaniel W. S. Almeida pat_args.continuity_counter = &pat_ctx->cc; 175f90cf607SDaniel W. S. Almeida 176f90cf607SDaniel W. S. Almeida m->mux_buf_offset += vidtv_psi_pat_write_into(pat_args); 177f90cf607SDaniel W. S. Almeida 178039b7caeSMauro Carvalho Chehab for (i = 0; i < m->si.pat->num_pmt; ++i) { 179f90cf607SDaniel W. S. Almeida pmt_pid = vidtv_psi_pmt_get_pid(m->si.pmt_secs[i], 180f90cf607SDaniel W. S. Almeida m->si.pat); 181f90cf607SDaniel W. S. Almeida 182f90cf607SDaniel W. S. Almeida if (pmt_pid > TS_LAST_VALID_PID) { 1839cfb4d36SMauro Carvalho Chehab dev_warn_ratelimited(m->dev, 1849cfb4d36SMauro Carvalho Chehab "PID: %d not found\n", pmt_pid); 185f90cf607SDaniel W. S. Almeida continue; 186f90cf607SDaniel W. S. Almeida } 187f90cf607SDaniel W. S. Almeida 188f90cf607SDaniel W. S. Almeida pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid); 189f90cf607SDaniel W. S. Almeida 190f90cf607SDaniel W. S. Almeida pmt_args.offset = m->mux_buf_offset; 191f90cf607SDaniel W. S. Almeida pmt_args.pmt = m->si.pmt_secs[i]; 192f90cf607SDaniel W. S. Almeida pmt_args.pid = pmt_pid; 193f90cf607SDaniel W. S. Almeida pmt_args.continuity_counter = &pmt_ctx->cc; 194f90cf607SDaniel W. S. Almeida 195f90cf607SDaniel W. S. Almeida /* write each section into buffer */ 196f90cf607SDaniel W. S. Almeida m->mux_buf_offset += vidtv_psi_pmt_write_into(pmt_args); 197f90cf607SDaniel W. S. Almeida } 198f90cf607SDaniel W. S. Almeida 199f90cf607SDaniel W. S. Almeida sdt_args.offset = m->mux_buf_offset; 200f90cf607SDaniel W. S. Almeida sdt_args.continuity_counter = &sdt_ctx->cc; 201f90cf607SDaniel W. S. Almeida 202f90cf607SDaniel W. S. Almeida m->mux_buf_offset += vidtv_psi_sdt_write_into(sdt_args); 203f90cf607SDaniel W. S. Almeida 204c2f78f0cSDaniel W. S. Almeida nit_args.offset = m->mux_buf_offset; 205c2f78f0cSDaniel W. S. Almeida nit_args.continuity_counter = &nit_ctx->cc; 206c2f78f0cSDaniel W. S. Almeida 207c2f78f0cSDaniel W. S. Almeida m->mux_buf_offset += vidtv_psi_nit_write_into(nit_args); 208c2f78f0cSDaniel W. S. Almeida 2097a7899f6SDaniel W. S. Almeida eit_args.offset = m->mux_buf_offset; 2107a7899f6SDaniel W. S. Almeida eit_args.continuity_counter = &eit_ctx->cc; 2117a7899f6SDaniel W. S. Almeida 2127a7899f6SDaniel W. S. Almeida m->mux_buf_offset += vidtv_psi_eit_write_into(eit_args); 2137a7899f6SDaniel W. S. Almeida 214f90cf607SDaniel W. S. Almeida nbytes = m->mux_buf_offset - initial_offset; 215f90cf607SDaniel W. S. Almeida 216f90cf607SDaniel W. S. Almeida m->num_streamed_si++; 217f90cf607SDaniel W. S. Almeida 218f90cf607SDaniel W. S. Almeida return nbytes; 219f90cf607SDaniel W. S. Almeida } 220f90cf607SDaniel W. S. Almeida 221f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_push_pcr(struct vidtv_mux *m) 222f90cf607SDaniel W. S. Almeida { 223f90cf607SDaniel W. S. Almeida struct pcr_write_args args = {}; 224f90cf607SDaniel W. S. Almeida struct vidtv_mux_pid_ctx *ctx; 225f90cf607SDaniel W. S. Almeida u32 nbytes = 0; 226f90cf607SDaniel W. S. Almeida 227f90cf607SDaniel W. S. Almeida ctx = vidtv_mux_get_pid_ctx(m, m->pcr_pid); 228f90cf607SDaniel W. S. Almeida args.dest_buf = m->mux_buf; 229f90cf607SDaniel W. S. Almeida args.pid = m->pcr_pid; 230f90cf607SDaniel W. S. Almeida args.buf_sz = m->mux_buf_sz; 231f90cf607SDaniel W. S. Almeida args.continuity_counter = &ctx->cc; 232f90cf607SDaniel W. S. Almeida 233f90cf607SDaniel W. S. Almeida /* the 27Mhz clock will feed both parts of the PCR bitfield */ 234f90cf607SDaniel W. S. Almeida args.pcr = m->timing.clk; 235f90cf607SDaniel W. S. Almeida 236f90cf607SDaniel W. S. Almeida nbytes += vidtv_ts_pcr_write_into(args); 237f90cf607SDaniel W. S. Almeida m->mux_buf_offset += nbytes; 238f90cf607SDaniel W. S. Almeida 239f90cf607SDaniel W. S. Almeida m->num_streamed_pcr++; 240f90cf607SDaniel W. S. Almeida 241f90cf607SDaniel W. S. Almeida return nbytes; 242f90cf607SDaniel W. S. Almeida } 243f90cf607SDaniel W. S. Almeida 244f90cf607SDaniel W. S. Almeida static bool vidtv_mux_should_push_pcr(struct vidtv_mux *m) 245f90cf607SDaniel W. S. Almeida { 246f90cf607SDaniel W. S. Almeida u64 next_pcr_at; 247f90cf607SDaniel W. S. Almeida 248f90cf607SDaniel W. S. Almeida if (m->num_streamed_pcr == 0) 249f90cf607SDaniel W. S. Almeida return true; 250f90cf607SDaniel W. S. Almeida 251f90cf607SDaniel W. S. Almeida next_pcr_at = m->timing.start_jiffies + 252f90cf607SDaniel W. S. Almeida usecs_to_jiffies(m->num_streamed_pcr * 253f90cf607SDaniel W. S. Almeida m->timing.pcr_period_usecs); 254f90cf607SDaniel W. S. Almeida 255f90cf607SDaniel W. S. Almeida return time_after64(m->timing.current_jiffies, next_pcr_at); 256f90cf607SDaniel W. S. Almeida } 257f90cf607SDaniel W. S. Almeida 258f90cf607SDaniel W. S. Almeida static bool vidtv_mux_should_push_si(struct vidtv_mux *m) 259f90cf607SDaniel W. S. Almeida { 260f90cf607SDaniel W. S. Almeida u64 next_si_at; 261f90cf607SDaniel W. S. Almeida 262f90cf607SDaniel W. S. Almeida if (m->num_streamed_si == 0) 263f90cf607SDaniel W. S. Almeida return true; 264f90cf607SDaniel W. S. Almeida 265f90cf607SDaniel W. S. Almeida next_si_at = m->timing.start_jiffies + 266f90cf607SDaniel W. S. Almeida usecs_to_jiffies(m->num_streamed_si * 267f90cf607SDaniel W. S. Almeida m->timing.si_period_usecs); 268f90cf607SDaniel W. S. Almeida 269f90cf607SDaniel W. S. Almeida return time_after64(m->timing.current_jiffies, next_si_at); 270f90cf607SDaniel W. S. Almeida } 271f90cf607SDaniel W. S. Almeida 272f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_packetize_access_units(struct vidtv_mux *m, 273f90cf607SDaniel W. S. Almeida struct vidtv_encoder *e) 274f90cf607SDaniel W. S. Almeida { 275163d72a2SMauro Carvalho Chehab struct pes_write_args args = { 276163d72a2SMauro Carvalho Chehab .dest_buf = m->mux_buf, 277163d72a2SMauro Carvalho Chehab .dest_buf_sz = m->mux_buf_sz, 278163d72a2SMauro Carvalho Chehab .pid = be16_to_cpu(e->es_pid), 279163d72a2SMauro Carvalho Chehab .encoder_id = e->id, 280163d72a2SMauro Carvalho Chehab .stream_id = be16_to_cpu(e->stream_id), 281163d72a2SMauro Carvalho Chehab .send_pts = true, /* forbidden value '01'... */ 282163d72a2SMauro Carvalho Chehab .send_dts = false, /* ...for PTS_DTS flags */ 283163d72a2SMauro Carvalho Chehab }; 284f90cf607SDaniel W. S. Almeida struct vidtv_access_unit *au = e->access_units; 285a8bd461cSMauro Carvalho Chehab u32 initial_offset = m->mux_buf_offset; 286163d72a2SMauro Carvalho Chehab struct vidtv_mux_pid_ctx *pid_ctx; 287a8bd461cSMauro Carvalho Chehab u32 nbytes = 0; 288a8bd461cSMauro Carvalho Chehab u8 *buf = NULL; 289f90cf607SDaniel W. S. Almeida 290163d72a2SMauro Carvalho Chehab /* see SMPTE 302M clause 6.4 */ 291163d72a2SMauro Carvalho Chehab if (args.encoder_id == S302M) { 292163d72a2SMauro Carvalho Chehab args.send_dts = false; 293f90cf607SDaniel W. S. Almeida args.send_pts = true; 294163d72a2SMauro Carvalho Chehab } 295163d72a2SMauro Carvalho Chehab 296163d72a2SMauro Carvalho Chehab pid_ctx = vidtv_mux_create_pid_ctx_once(m, be16_to_cpu(e->es_pid)); 297163d72a2SMauro Carvalho Chehab args.continuity_counter = &pid_ctx->cc; 298f90cf607SDaniel W. S. Almeida 299f90cf607SDaniel W. S. Almeida while (au) { 300f90cf607SDaniel W. S. Almeida buf = e->encoder_buf + au->offset; 301f90cf607SDaniel W. S. Almeida args.from = buf; 302f90cf607SDaniel W. S. Almeida args.access_unit_len = au->nbytes; 303f90cf607SDaniel W. S. Almeida args.dest_offset = m->mux_buf_offset; 304f90cf607SDaniel W. S. Almeida args.pts = au->pts; 305a61d7d19SMauro Carvalho Chehab args.pcr = m->timing.clk; 306f90cf607SDaniel W. S. Almeida 307163d72a2SMauro Carvalho Chehab m->mux_buf_offset += vidtv_pes_write_into(&args); 308f90cf607SDaniel W. S. Almeida 309f90cf607SDaniel W. S. Almeida au = au->next; 310f90cf607SDaniel W. S. Almeida } 311f90cf607SDaniel W. S. Almeida 312f90cf607SDaniel W. S. Almeida /* 313f90cf607SDaniel W. S. Almeida * clear the encoder state once the ES data has been written to the mux 314f90cf607SDaniel W. S. Almeida * buffer 315f90cf607SDaniel W. S. Almeida */ 316f90cf607SDaniel W. S. Almeida e->clear(e); 317f90cf607SDaniel W. S. Almeida 318f90cf607SDaniel W. S. Almeida nbytes = m->mux_buf_offset - initial_offset; 319f90cf607SDaniel W. S. Almeida return nbytes; 320f90cf607SDaniel W. S. Almeida } 321f90cf607SDaniel W. S. Almeida 322f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_poll_encoders(struct vidtv_mux *m) 323f90cf607SDaniel W. S. Almeida { 324f90cf607SDaniel W. S. Almeida struct vidtv_channel *cur_chnl = m->channels; 325f90cf607SDaniel W. S. Almeida struct vidtv_encoder *e = NULL; 326a8bd461cSMauro Carvalho Chehab u32 nbytes = 0; 327a8bd461cSMauro Carvalho Chehab u32 au_nbytes; 328f90cf607SDaniel W. S. Almeida 329f90cf607SDaniel W. S. Almeida while (cur_chnl) { 330f90cf607SDaniel W. S. Almeida e = cur_chnl->encoders; 331f90cf607SDaniel W. S. Almeida 332f90cf607SDaniel W. S. Almeida while (e) { 33337b288f5SDaniel W. S. Almeida e->encode(e); 334f90cf607SDaniel W. S. Almeida /* get the TS packets into the mux buffer */ 335f90cf607SDaniel W. S. Almeida au_nbytes = vidtv_mux_packetize_access_units(m, e); 336f90cf607SDaniel W. S. Almeida nbytes += au_nbytes; 337f90cf607SDaniel W. S. Almeida m->mux_buf_offset += au_nbytes; 338f90cf607SDaniel W. S. Almeida /* grab next encoder */ 339f90cf607SDaniel W. S. Almeida e = e->next; 340f90cf607SDaniel W. S. Almeida } 341f90cf607SDaniel W. S. Almeida 342f90cf607SDaniel W. S. Almeida /* grab the next channel */ 343f90cf607SDaniel W. S. Almeida cur_chnl = cur_chnl->next; 344f90cf607SDaniel W. S. Almeida } 345f90cf607SDaniel W. S. Almeida 346f90cf607SDaniel W. S. Almeida return nbytes; 347f90cf607SDaniel W. S. Almeida } 348f90cf607SDaniel W. S. Almeida 349f90cf607SDaniel W. S. Almeida static u32 vidtv_mux_pad_with_nulls(struct vidtv_mux *m, u32 npkts) 350f90cf607SDaniel W. S. Almeida { 351ec3eda53SMauro Carvalho Chehab struct null_packet_write_args args = { 352ec3eda53SMauro Carvalho Chehab .dest_buf = m->mux_buf, 353ec3eda53SMauro Carvalho Chehab .buf_sz = m->mux_buf_sz, 354ec3eda53SMauro Carvalho Chehab .dest_offset = m->mux_buf_offset, 355ec3eda53SMauro Carvalho Chehab }; 356f90cf607SDaniel W. S. Almeida u32 initial_offset = m->mux_buf_offset; 357f90cf607SDaniel W. S. Almeida struct vidtv_mux_pid_ctx *ctx; 358a8bd461cSMauro Carvalho Chehab u32 nbytes; 359a8bd461cSMauro Carvalho Chehab u32 i; 360f90cf607SDaniel W. S. Almeida 361f90cf607SDaniel W. S. Almeida ctx = vidtv_mux_get_pid_ctx(m, TS_NULL_PACKET_PID); 362f90cf607SDaniel W. S. Almeida 363f90cf607SDaniel W. S. Almeida args.continuity_counter = &ctx->cc; 364f90cf607SDaniel W. S. Almeida 365f90cf607SDaniel W. S. Almeida for (i = 0; i < npkts; ++i) { 366f90cf607SDaniel W. S. Almeida m->mux_buf_offset += vidtv_ts_null_write_into(args); 367f90cf607SDaniel W. S. Almeida args.dest_offset = m->mux_buf_offset; 368f90cf607SDaniel W. S. Almeida } 369f90cf607SDaniel W. S. Almeida 370f90cf607SDaniel W. S. Almeida nbytes = m->mux_buf_offset - initial_offset; 371f90cf607SDaniel W. S. Almeida 372f90cf607SDaniel W. S. Almeida /* sanity check */ 373f90cf607SDaniel W. S. Almeida if (nbytes != npkts * TS_PACKET_LEN) 3749cfb4d36SMauro Carvalho Chehab dev_err_ratelimited(m->dev, "%d != %d\n", 3759cfb4d36SMauro Carvalho Chehab nbytes, npkts * TS_PACKET_LEN); 376f90cf607SDaniel W. S. Almeida 377f90cf607SDaniel W. S. Almeida return nbytes; 378f90cf607SDaniel W. S. Almeida } 379f90cf607SDaniel W. S. Almeida 380f90cf607SDaniel W. S. Almeida static void vidtv_mux_clear(struct vidtv_mux *m) 381f90cf607SDaniel W. S. Almeida { 382f90cf607SDaniel W. S. Almeida /* clear the packets currently in the mux */ 383f90cf607SDaniel W. S. Almeida memset(m->mux_buf, 0, m->mux_buf_sz * sizeof(*m->mux_buf)); 384f90cf607SDaniel W. S. Almeida /* point to the beginning of the buffer again */ 385f90cf607SDaniel W. S. Almeida m->mux_buf_offset = 0; 386f90cf607SDaniel W. S. Almeida } 387f90cf607SDaniel W. S. Almeida 38882d00a1aSMauro Carvalho Chehab #define ERR_RATE 10000000 389f90cf607SDaniel W. S. Almeida static void vidtv_mux_tick(struct work_struct *work) 390f90cf607SDaniel W. S. Almeida { 391f90cf607SDaniel W. S. Almeida struct vidtv_mux *m = container_of(work, 392f90cf607SDaniel W. S. Almeida struct vidtv_mux, 393f90cf607SDaniel W. S. Almeida mpeg_thread); 3949cf8572dSMauro Carvalho Chehab struct dtv_frontend_properties *c = &m->fe->dtv_property_cache; 395a8bd461cSMauro Carvalho Chehab u32 tot_bits = 0; 396f90cf607SDaniel W. S. Almeida u32 nbytes; 397f90cf607SDaniel W. S. Almeida u32 npkts; 398f90cf607SDaniel W. S. Almeida 399f90cf607SDaniel W. S. Almeida while (m->streaming) { 400f90cf607SDaniel W. S. Almeida nbytes = 0; 401f90cf607SDaniel W. S. Almeida 402f90cf607SDaniel W. S. Almeida vidtv_mux_update_clk(m); 403f90cf607SDaniel W. S. Almeida 404f90cf607SDaniel W. S. Almeida if (vidtv_mux_should_push_pcr(m)) 405f90cf607SDaniel W. S. Almeida nbytes += vidtv_mux_push_pcr(m); 406f90cf607SDaniel W. S. Almeida 407f90cf607SDaniel W. S. Almeida if (vidtv_mux_should_push_si(m)) 408f90cf607SDaniel W. S. Almeida nbytes += vidtv_mux_push_si(m); 409f90cf607SDaniel W. S. Almeida 410f90cf607SDaniel W. S. Almeida nbytes += vidtv_mux_poll_encoders(m); 41137b288f5SDaniel W. S. Almeida nbytes += vidtv_mux_pad_with_nulls(m, 256); 412f90cf607SDaniel W. S. Almeida 413f90cf607SDaniel W. S. Almeida npkts = nbytes / TS_PACKET_LEN; 414f90cf607SDaniel W. S. Almeida 415f90cf607SDaniel W. S. Almeida /* if the buffer is not aligned there is a bug somewhere */ 416f90cf607SDaniel W. S. Almeida if (nbytes % TS_PACKET_LEN) 4179cfb4d36SMauro Carvalho Chehab dev_err_ratelimited(m->dev, "Misaligned buffer\n"); 418f90cf607SDaniel W. S. Almeida 419f90cf607SDaniel W. S. Almeida if (m->on_new_packets_available_cb) 420f90cf607SDaniel W. S. Almeida m->on_new_packets_available_cb(m->priv, 421f90cf607SDaniel W. S. Almeida m->mux_buf, 422f90cf607SDaniel W. S. Almeida npkts); 423f90cf607SDaniel W. S. Almeida 424f90cf607SDaniel W. S. Almeida vidtv_mux_clear(m); 425f90cf607SDaniel W. S. Almeida 4269cf8572dSMauro Carvalho Chehab /* 4279cf8572dSMauro Carvalho Chehab * Update bytes and packet counts at DVBv5 stats 4289cf8572dSMauro Carvalho Chehab * 4299cf8572dSMauro Carvalho Chehab * For now, both pre and post bit counts are identical, 4309cf8572dSMauro Carvalho Chehab * but post BER count can be lower than pre BER, if the error 4319cf8572dSMauro Carvalho Chehab * correction logic discards packages. 4329cf8572dSMauro Carvalho Chehab */ 43382d00a1aSMauro Carvalho Chehab c->pre_bit_count.stat[0].uvalue = nbytes * 8; 43482d00a1aSMauro Carvalho Chehab c->post_bit_count.stat[0].uvalue = nbytes * 8; 4359cf8572dSMauro Carvalho Chehab c->block_count.stat[0].uvalue += npkts; 4369cf8572dSMauro Carvalho Chehab 43782d00a1aSMauro Carvalho Chehab /* 43882d00a1aSMauro Carvalho Chehab * Even without any visible errors for the user, the pre-BER 43982d00a1aSMauro Carvalho Chehab * stats usually have an error range up to 1E-6. So, 44082d00a1aSMauro Carvalho Chehab * add some random error increment count to it. 44182d00a1aSMauro Carvalho Chehab * 44282d00a1aSMauro Carvalho Chehab * Please notice that this is a poor guy's implementation, 44382d00a1aSMauro Carvalho Chehab * as it will produce one corrected bit error every time 44482d00a1aSMauro Carvalho Chehab * ceil(total bytes / ERR_RATE) is incremented, without 44582d00a1aSMauro Carvalho Chehab * any sort of (pseudo-)randomness. 44682d00a1aSMauro Carvalho Chehab */ 44782d00a1aSMauro Carvalho Chehab tot_bits += nbytes * 8; 44882d00a1aSMauro Carvalho Chehab if (tot_bits > ERR_RATE) { 44982d00a1aSMauro Carvalho Chehab c->pre_bit_error.stat[0].uvalue++; 45082d00a1aSMauro Carvalho Chehab tot_bits -= ERR_RATE; 45182d00a1aSMauro Carvalho Chehab } 45282d00a1aSMauro Carvalho Chehab 453f90cf607SDaniel W. S. Almeida usleep_range(VIDTV_SLEEP_USECS, VIDTV_MAX_SLEEP_USECS); 454f90cf607SDaniel W. S. Almeida } 455f90cf607SDaniel W. S. Almeida } 456f90cf607SDaniel W. S. Almeida 457f90cf607SDaniel W. S. Almeida void vidtv_mux_start_thread(struct vidtv_mux *m) 458f90cf607SDaniel W. S. Almeida { 459f90cf607SDaniel W. S. Almeida if (m->streaming) { 4609cfb4d36SMauro Carvalho Chehab dev_warn_ratelimited(m->dev, "Already streaming. Skipping.\n"); 461f90cf607SDaniel W. S. Almeida return; 462f90cf607SDaniel W. S. Almeida } 463f90cf607SDaniel W. S. Almeida 464f90cf607SDaniel W. S. Almeida m->streaming = true; 465f90cf607SDaniel W. S. Almeida m->timing.start_jiffies = get_jiffies_64(); 466f90cf607SDaniel W. S. Almeida schedule_work(&m->mpeg_thread); 467f90cf607SDaniel W. S. Almeida } 468f90cf607SDaniel W. S. Almeida 469f90cf607SDaniel W. S. Almeida void vidtv_mux_stop_thread(struct vidtv_mux *m) 470f90cf607SDaniel W. S. Almeida { 471f90cf607SDaniel W. S. Almeida if (m->streaming) { 472f90cf607SDaniel W. S. Almeida m->streaming = false; /* thread will quit */ 473f90cf607SDaniel W. S. Almeida cancel_work_sync(&m->mpeg_thread); 474f90cf607SDaniel W. S. Almeida } 475f90cf607SDaniel W. S. Almeida } 476f90cf607SDaniel W. S. Almeida 4779cf8572dSMauro Carvalho Chehab struct vidtv_mux *vidtv_mux_init(struct dvb_frontend *fe, 4789cf8572dSMauro Carvalho Chehab struct device *dev, 4790a33ab16SMauro Carvalho Chehab struct vidtv_mux_init_args *args) 480f90cf607SDaniel W. S. Almeida { 4813be80379SMauro Carvalho Chehab struct vidtv_mux *m; 4823be80379SMauro Carvalho Chehab 4833be80379SMauro Carvalho Chehab m = kzalloc(sizeof(*m), GFP_KERNEL); 4843be80379SMauro Carvalho Chehab if (!m) 4853be80379SMauro Carvalho Chehab return NULL; 486f90cf607SDaniel W. S. Almeida 4879cfb4d36SMauro Carvalho Chehab m->dev = dev; 4889cf8572dSMauro Carvalho Chehab m->fe = fe; 4890a33ab16SMauro Carvalho Chehab m->timing.pcr_period_usecs = args->pcr_period_usecs; 4900a33ab16SMauro Carvalho Chehab m->timing.si_period_usecs = args->si_period_usecs; 491f90cf607SDaniel W. S. Almeida 4920a33ab16SMauro Carvalho Chehab m->mux_rate_kbytes_sec = args->mux_rate_kbytes_sec; 493f90cf607SDaniel W. S. Almeida 4940a33ab16SMauro Carvalho Chehab m->on_new_packets_available_cb = args->on_new_packets_available_cb; 495f90cf607SDaniel W. S. Almeida 4960a33ab16SMauro Carvalho Chehab m->mux_buf = vzalloc(args->mux_buf_sz); 4973be80379SMauro Carvalho Chehab if (!m->mux_buf) 4983be80379SMauro Carvalho Chehab goto free_mux; 4993be80379SMauro Carvalho Chehab 5000a33ab16SMauro Carvalho Chehab m->mux_buf_sz = args->mux_buf_sz; 501f90cf607SDaniel W. S. Almeida 5020a33ab16SMauro Carvalho Chehab m->pcr_pid = args->pcr_pid; 5030a33ab16SMauro Carvalho Chehab m->transport_stream_id = args->transport_stream_id; 5040a33ab16SMauro Carvalho Chehab m->priv = args->priv; 5050a33ab16SMauro Carvalho Chehab m->network_id = args->network_id; 5060a33ab16SMauro Carvalho Chehab m->network_name = kstrdup(args->network_name, GFP_KERNEL); 507880a8fc0SMauro Carvalho Chehab m->timing.current_jiffies = get_jiffies_64(); 508f90cf607SDaniel W. S. Almeida 5090a33ab16SMauro Carvalho Chehab if (args->channels) 5100a33ab16SMauro Carvalho Chehab m->channels = args->channels; 511f90cf607SDaniel W. S. Almeida else 5123be80379SMauro Carvalho Chehab if (vidtv_channels_init(m) < 0) 5133be80379SMauro Carvalho Chehab goto free_mux_buf; 514f90cf607SDaniel W. S. Almeida 515f90cf607SDaniel W. S. Almeida /* will alloc data for pmt_sections after initializing pat */ 5163be80379SMauro Carvalho Chehab if (vidtv_channel_si_init(m) < 0) 5173be80379SMauro Carvalho Chehab goto free_channels; 518f90cf607SDaniel W. S. Almeida 519f90cf607SDaniel W. S. Almeida INIT_WORK(&m->mpeg_thread, vidtv_mux_tick); 520f90cf607SDaniel W. S. Almeida 5213be80379SMauro Carvalho Chehab if (vidtv_mux_pid_ctx_init(m) < 0) 5223be80379SMauro Carvalho Chehab goto free_channel_si; 523f90cf607SDaniel W. S. Almeida 524f90cf607SDaniel W. S. Almeida return m; 5253be80379SMauro Carvalho Chehab 5263be80379SMauro Carvalho Chehab free_channel_si: 5273be80379SMauro Carvalho Chehab vidtv_channel_si_destroy(m); 5283be80379SMauro Carvalho Chehab free_channels: 5293be80379SMauro Carvalho Chehab vidtv_channels_destroy(m); 5303be80379SMauro Carvalho Chehab free_mux_buf: 5313be80379SMauro Carvalho Chehab vfree(m->mux_buf); 5323be80379SMauro Carvalho Chehab free_mux: 5333be80379SMauro Carvalho Chehab kfree(m); 5343be80379SMauro Carvalho Chehab return NULL; 535f90cf607SDaniel W. S. Almeida } 536f90cf607SDaniel W. S. Almeida 537f90cf607SDaniel W. S. Almeida void vidtv_mux_destroy(struct vidtv_mux *m) 538f90cf607SDaniel W. S. Almeida { 539f90cf607SDaniel W. S. Almeida vidtv_mux_stop_thread(m); 540f90cf607SDaniel W. S. Almeida vidtv_mux_pid_ctx_destroy(m); 541f90cf607SDaniel W. S. Almeida vidtv_channel_si_destroy(m); 542f90cf607SDaniel W. S. Almeida vidtv_channels_destroy(m); 543c2f78f0cSDaniel W. S. Almeida kfree(m->network_name); 544f90cf607SDaniel W. S. Almeida vfree(m->mux_buf); 545f90cf607SDaniel W. S. Almeida kfree(m); 546f90cf607SDaniel W. S. Almeida } 547