162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * The Virtual DVB test driver serves as a reference DVB driver and helps
462306a36Sopenharmony_ci * validate the existing APIs in the media subsystem. It can also aid
562306a36Sopenharmony_ci * developers working on userspace applications.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2020 Daniel W. S. Almeida
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/math64.h>
1362306a36Sopenharmony_ci#include <linux/printk.h>
1462306a36Sopenharmony_ci#include <linux/ratelimit.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "vidtv_common.h"
1862306a36Sopenharmony_ci#include "vidtv_ts.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic u32 vidtv_ts_write_pcr_bits(u8 *to, u32 to_offset, u64 pcr)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	/* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */
2362306a36Sopenharmony_ci	u64 div;
2462306a36Sopenharmony_ci	u64 rem;
2562306a36Sopenharmony_ci	u8 *buf = to + to_offset;
2662306a36Sopenharmony_ci	u64 pcr_low;
2762306a36Sopenharmony_ci	u64 pcr_high;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	div = div64_u64_rem(pcr, 300, &rem);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	pcr_low = rem; /* pcr_low = pcr % 300 */
3262306a36Sopenharmony_ci	pcr_high = div; /* pcr_high = pcr / 300 */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	*buf++ = pcr_high >> 25;
3562306a36Sopenharmony_ci	*buf++ = pcr_high >> 17;
3662306a36Sopenharmony_ci	*buf++ = pcr_high >>  9;
3762306a36Sopenharmony_ci	*buf++ = pcr_high >>  1;
3862306a36Sopenharmony_ci	*buf++ = pcr_high <<  7 | pcr_low >> 8 | 0x7e;
3962306a36Sopenharmony_ci	*buf++ = pcr_low;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return 6;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_civoid vidtv_ts_inc_cc(u8 *continuity_counter)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	++*continuity_counter;
4762306a36Sopenharmony_ci	if (*continuity_counter > TS_CC_MAX_VAL)
4862306a36Sopenharmony_ci		*continuity_counter = 0;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciu32 vidtv_ts_null_write_into(struct null_packet_write_args args)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 nbytes = 0;
5462306a36Sopenharmony_ci	struct vidtv_mpeg_ts ts_header = {};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	ts_header.sync_byte          = TS_SYNC_BYTE;
5762306a36Sopenharmony_ci	ts_header.bitfield           = cpu_to_be16(TS_NULL_PACKET_PID);
5862306a36Sopenharmony_ci	ts_header.payload            = 1;
5962306a36Sopenharmony_ci	ts_header.continuity_counter = *args.continuity_counter;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* copy TS header */
6262306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args.dest_buf,
6362306a36Sopenharmony_ci			       args.dest_offset + nbytes,
6462306a36Sopenharmony_ci			       args.buf_sz,
6562306a36Sopenharmony_ci			       &ts_header,
6662306a36Sopenharmony_ci			       sizeof(ts_header));
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	vidtv_ts_inc_cc(args.continuity_counter);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* fill the rest with empty data */
7162306a36Sopenharmony_ci	nbytes += vidtv_memset(args.dest_buf,
7262306a36Sopenharmony_ci			       args.dest_offset + nbytes,
7362306a36Sopenharmony_ci			       args.buf_sz,
7462306a36Sopenharmony_ci			       TS_FILL_BYTE,
7562306a36Sopenharmony_ci			       TS_PACKET_LEN - nbytes);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* we should have written exactly _one_ 188byte packet */
7862306a36Sopenharmony_ci	if (nbytes != TS_PACKET_LEN)
7962306a36Sopenharmony_ci		pr_warn_ratelimited("Expected exactly %d bytes, got %d\n",
8062306a36Sopenharmony_ci				    TS_PACKET_LEN,
8162306a36Sopenharmony_ci				    nbytes);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return nbytes;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciu32 vidtv_ts_pcr_write_into(struct pcr_write_args args)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	u32 nbytes = 0;
8962306a36Sopenharmony_ci	struct vidtv_mpeg_ts ts_header = {};
9062306a36Sopenharmony_ci	struct vidtv_mpeg_ts_adaption ts_adap = {};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	ts_header.sync_byte     = TS_SYNC_BYTE;
9362306a36Sopenharmony_ci	ts_header.bitfield      = cpu_to_be16(args.pid);
9462306a36Sopenharmony_ci	ts_header.scrambling    = 0;
9562306a36Sopenharmony_ci	/* cc is not incremented, but it is needed. see 13818-1 clause 2.4.3.3 */
9662306a36Sopenharmony_ci	ts_header.continuity_counter = *args.continuity_counter;
9762306a36Sopenharmony_ci	ts_header.payload            = 0;
9862306a36Sopenharmony_ci	ts_header.adaptation_field   = 1;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* 13818-1 clause 2.4.3.5 */
10162306a36Sopenharmony_ci	ts_adap.length = 183;
10262306a36Sopenharmony_ci	ts_adap.PCR    = 1;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* copy TS header */
10562306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args.dest_buf,
10662306a36Sopenharmony_ci			       args.dest_offset + nbytes,
10762306a36Sopenharmony_ci			       args.buf_sz,
10862306a36Sopenharmony_ci			       &ts_header,
10962306a36Sopenharmony_ci			       sizeof(ts_header));
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* write the adap after the TS header */
11262306a36Sopenharmony_ci	nbytes += vidtv_memcpy(args.dest_buf,
11362306a36Sopenharmony_ci			       args.dest_offset + nbytes,
11462306a36Sopenharmony_ci			       args.buf_sz,
11562306a36Sopenharmony_ci			       &ts_adap,
11662306a36Sopenharmony_ci			       sizeof(ts_adap));
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* write the PCR optional */
11962306a36Sopenharmony_ci	nbytes += vidtv_ts_write_pcr_bits(args.dest_buf,
12062306a36Sopenharmony_ci					  args.dest_offset + nbytes,
12162306a36Sopenharmony_ci					  args.pcr);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	nbytes += vidtv_memset(args.dest_buf,
12462306a36Sopenharmony_ci			       args.dest_offset + nbytes,
12562306a36Sopenharmony_ci			       args.buf_sz,
12662306a36Sopenharmony_ci			       TS_FILL_BYTE,
12762306a36Sopenharmony_ci			       TS_PACKET_LEN - nbytes);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* we should have written exactly _one_ 188byte packet */
13062306a36Sopenharmony_ci	if (nbytes != TS_PACKET_LEN)
13162306a36Sopenharmony_ci		pr_warn_ratelimited("Expected exactly %d bytes, got %d\n",
13262306a36Sopenharmony_ci				    TS_PACKET_LEN,
13362306a36Sopenharmony_ci				    nbytes);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return nbytes;
13662306a36Sopenharmony_ci}
137