[RFC, WIP, v4 07/11] media: vidtv: add MPEG TS common code

From: Daniel W. S. Almeida
Date: Fri May 01 2020 - 23:23:00 EST


From: "Daniel W. S. Almeida" <dwlsalmeida@xxxxxxxxx>

Add code to work with MPEG TS packets, such as TS headers, adaptation
fields, PCR packets and NULL packets.

Signed-off-by: Daniel W. S. Almeida <dwlsalmeida@xxxxxxxxx>
---
drivers/media/test-drivers/vidtv/Makefile | 2 +-
drivers/media/test-drivers/vidtv/vidtv_ts.c | 130 ++++++++++++++++++++
drivers/media/test-drivers/vidtv/vidtv_ts.h | 103 ++++++++++++++++
3 files changed, 234 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/test-drivers/vidtv/vidtv_ts.c
create mode 100644 drivers/media/test-drivers/vidtv/vidtv_ts.h

diff --git a/drivers/media/test-drivers/vidtv/Makefile b/drivers/media/test-drivers/vidtv/Makefile
index 9ea9485d42189..92001bc348615 100644
--- a/drivers/media/test-drivers/vidtv/Makefile
+++ b/drivers/media/test-drivers/vidtv/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0

vidtv_demod-objs := vidtv_common.o
-vidtv_bridge-objs := vidtv_common.o
+vidtv_bridge-objs := vidtv_common.o vidtv_ts.o

obj-$(CONFIG_DVB_VIDTV) += vidtv_tuner.o vidtv_demod.o vidtv_bridge.o
diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.c b/drivers/media/test-drivers/vidtv/vidtv_ts.c
new file mode 100644
index 0000000000000..f545c45c0fe7c
--- /dev/null
+++ b/drivers/media/test-drivers/vidtv/vidtv_ts.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The Virtual DVB test driver 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.
+ *
+ * Written by Daniel W. S. Almeida <dwlsalmeida@xxxxxxxxx>
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include "vidtv_ts.h"
+#include "vidtv_common.h"
+
+static u32 vidtv_ts_write_pcr_bits(u8 *buf, u64 pcr)
+{
+ /* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */
+ u64 pcr_low = pcr % 300, pcr_high = pcr / 300;
+
+ *buf++ = pcr_high >> 25;
+ *buf++ = pcr_high >> 17;
+ *buf++ = pcr_high >> 9;
+ *buf++ = pcr_high >> 1;
+ *buf++ = pcr_high << 7 | pcr_low >> 8 | 0x7e;
+ *buf++ = pcr_low;
+
+ return 6;
+}
+
+void vidtv_ts_inc_cc(u8 *continuity_counter)
+{
+ ++*continuity_counter;
+ if (*continuity_counter > TS_CC_MAX_VAL)
+ *continuity_counter = 0;
+}
+
+u32 vidtv_ts_null_write_into(struct null_packet_write_args args)
+{
+ u32 nbytes = 0;
+ struct vidtv_mpeg_ts ts_header = {0};
+
+ ts_header.sync_byte = TS_SYNC_BYTE;
+ ts_header.pid = TS_NULL_PACKET_PID;
+ ts_header.payload = 1;
+ ts_header.continuity_counter = *args.continuity_counter;
+
+ cpu_to_be16s(&ts_header.bitfield);
+
+ /* copy TS header */
+ nbytes += vidtv_memcpy(args.dest_buf + args.dest_offset + nbytes,
+ &ts_header,
+ sizeof(ts_header),
+ args.dest_offset + nbytes,
+ args.buf_sz);
+
+ be16_to_cpus(&ts_header.bitfield);
+
+ vidtv_ts_inc_cc(args.continuity_counter);
+
+ /* fill the rest with empty data */
+ nbytes += vidtv_memset(args.dest_buf + args.dest_offset + nbytes,
+ 0xff,
+ TS_PACKET_LEN - nbytes,
+ args.dest_offset + nbytes,
+ args.buf_sz);
+
+ /* we should have written exactly _one_ 188byte packet */
+ WARN_ON(nbytes != TS_PACKET_LEN);
+
+ return nbytes;
+}
+
+u32 vidtv_ts_pcr_write_into(struct pcr_write_args args)
+{
+ u32 nbytes = 0;
+ struct vidtv_mpeg_ts ts_header = {0};
+ struct vidtv_mpeg_ts_adaption ts_adap = {0};
+
+ ts_header.sync_byte = TS_SYNC_BYTE;
+ ts_header.tei = 0;
+ ts_header.payload_start = 0;
+ ts_header.pid = args.pid;
+ ts_header.priority = 0;
+ ts_header.scrambling = 0;
+ /* cc is not incremented, see 13818-1 clause 2.4.3.3 */
+ ts_header.continuity_counter = *args.continuity_counter;
+ ts_header.payload = 0;
+ ts_header.adaptation_field = 1;
+
+ /* 13818-1 clause 2.4.3.5 */
+ ts_adap.length = 183;
+ ts_adap.PCR = 1;
+
+ cpu_to_be16s(&ts_header.bitfield);
+
+ /* copy TS header */
+ nbytes += vidtv_memcpy(args.dest_buf + args.dest_offset + nbytes,
+ &ts_header,
+ sizeof(ts_header),
+ args.dest_offset + nbytes,
+ args.buf_sz);
+
+ be16_to_cpus(&ts_header.bitfield);
+
+ /* write the adap after the TS header */
+ nbytes += vidtv_memcpy(args.dest_buf + args.dest_offset + nbytes,
+ &ts_adap,
+ sizeof(ts_adap),
+ args.dest_offset + nbytes,
+ args.buf_sz);
+
+ /* write the PCR optional */
+ cpu_to_be64s(&args.pcr);
+ nbytes += vidtv_ts_write_pcr_bits(args.dest_buf +
+ args.dest_offset +
+ nbytes,
+ args.pcr);
+ be64_to_cpus(&args.pcr);
+
+ nbytes += vidtv_memset(args.dest_buf + args.dest_offset + nbytes,
+ 0xff,
+ TS_PACKET_LEN - nbytes,
+ args.dest_offset + nbytes,
+ args.buf_sz);
+
+ /* we should have written exactly _one_ 188byte packet */
+ WARN_ON(nbytes != TS_PACKET_LEN);
+
+ return nbytes;
+}
diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.h b/drivers/media/test-drivers/vidtv/vidtv_ts.h
new file mode 100644
index 0000000000000..2c07bddc46119
--- /dev/null
+++ b/drivers/media/test-drivers/vidtv/vidtv_ts.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The Virtual DVB test driver 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.
+ *
+ * Written by Daniel W. S. Almeida <dwlsalmeida@xxxxxxxxx>
+ */
+
+#ifndef VIDTV_TS_H
+#define VIDTV_TS_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define TS_SYNC_BYTE 0x47
+#define TS_PACKET_LEN 188
+#define TS_PAYLOAD_LEN 184
+#define TS_NULL_PACKET_PID 0x1fff
+#define TS_CC_MAX_VAL 0x0f /* 4 bits */
+#define TS_LAST_VALID_PID 8191
+
+struct vidtv_mpeg_ts_adaption {
+ u8 length;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 extension:1;
+ u8 private_data:1;
+ u8 splicing_point:1;
+ u8 OPCR:1;
+ u8 PCR:1;
+ u8 priority:1;
+ u8 random_access:1;
+ u8 discontinued:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 discontinued:1;
+ u8 random_access:1;
+ u8 priority:1;
+ u8 PCR:1;
+ u8 OPCR:1;
+ u8 splicing_point:1;
+ u8 private_data:1;
+ u8 extension:1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ } __packed;
+ u8 data[];
+} __packed;
+
+struct vidtv_mpeg_ts {
+ u8 sync_byte;
+ union {
+ u16 bitfield;
+ struct {
+ u16 pid:13;
+ u16 priority:1;
+ u16 payload_start:1;
+ u16 tei:1;
+ } __packed;
+ } __packed;
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ u8 continuity_counter:4;
+ u8 payload:1;
+ u8 adaptation_field:1;
+ u8 scrambling:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ u8 scrambling:2;
+ u8 adaptation_field:1;
+ u8 payload:1;
+ u8 continuity_counter:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ } __packed;
+ struct vidtv_mpeg_ts_adaption adaption[];
+} __packed;
+
+struct pcr_write_args {
+ void *dest_buf; /* The buffer to write into */
+ u32 dest_offset; /* The byte offset into the buffer */
+ u16 pid; /* the TS PID for the PCR packets */
+ u32 buf_sz; /* protect against overflow when this field is not zero */
+ u8 *continuity_counter;
+ u64 pcr; /* A sample from the system clock */
+};
+
+struct null_packet_write_args {
+ void *dest_buf;/* The buffer to write into */
+ u32 dest_offset;/* The byte offset into the buffer */
+ u32 buf_sz; /* protect against overflow when this field is not zero */
+ u8 *continuity_counter;
+};
+
+/* Increment the continuity counter */
+void vidtv_ts_inc_cc(u8 *continuity_counter);
+
+u32 vidtv_ts_null_write_into(struct null_packet_write_args args);
+
+u32 vidtv_ts_pcr_write_into(struct pcr_write_args args);
+
+#endif //VIDTV_TS_H
--
2.26.2