18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file contains the logic to work with MPEG Program-Specific Information.
48c2ecf20Sopenharmony_ci * These are defined both in ISO/IEC 13818-1 (systems) and ETSI EN 300 468.
58c2ecf20Sopenharmony_ci * PSI is carried in the form of table structures, and although each table might
68c2ecf20Sopenharmony_ci * technically be broken into one or more sections, we do not do this here,
78c2ecf20Sopenharmony_ci * hence 'table' and 'section' are interchangeable for vidtv.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 2020 Daniel W. S. Almeida
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/bcd.h>
158c2ecf20Sopenharmony_ci#include <linux/crc32.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/ktime.h>
188c2ecf20Sopenharmony_ci#include <linux/printk.h>
198c2ecf20Sopenharmony_ci#include <linux/ratelimit.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/string.h>
228c2ecf20Sopenharmony_ci#include <linux/string.h>
238c2ecf20Sopenharmony_ci#include <linux/time.h>
248c2ecf20Sopenharmony_ci#include <linux/types.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "vidtv_common.h"
278c2ecf20Sopenharmony_ci#include "vidtv_psi.h"
288c2ecf20Sopenharmony_ci#include "vidtv_ts.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define CRC_SIZE_IN_BYTES 4
318c2ecf20Sopenharmony_ci#define MAX_VERSION_NUM 32
328c2ecf20Sopenharmony_ci#define INITIAL_CRC 0xffffffff
338c2ecf20Sopenharmony_ci#define ISO_LANGUAGE_CODE_LEN 3
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const u32 CRC_LUT[256] = {
368c2ecf20Sopenharmony_ci	/* from libdvbv5 */
378c2ecf20Sopenharmony_ci	0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
388c2ecf20Sopenharmony_ci	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
398c2ecf20Sopenharmony_ci	0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
408c2ecf20Sopenharmony_ci	0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
418c2ecf20Sopenharmony_ci	0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
428c2ecf20Sopenharmony_ci	0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
438c2ecf20Sopenharmony_ci	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
448c2ecf20Sopenharmony_ci	0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
458c2ecf20Sopenharmony_ci	0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
468c2ecf20Sopenharmony_ci	0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
478c2ecf20Sopenharmony_ci	0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
488c2ecf20Sopenharmony_ci	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
498c2ecf20Sopenharmony_ci	0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
508c2ecf20Sopenharmony_ci	0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
518c2ecf20Sopenharmony_ci	0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
528c2ecf20Sopenharmony_ci	0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
538c2ecf20Sopenharmony_ci	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
548c2ecf20Sopenharmony_ci	0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
558c2ecf20Sopenharmony_ci	0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
568c2ecf20Sopenharmony_ci	0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
578c2ecf20Sopenharmony_ci	0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
588c2ecf20Sopenharmony_ci	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
598c2ecf20Sopenharmony_ci	0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
608c2ecf20Sopenharmony_ci	0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
618c2ecf20Sopenharmony_ci	0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
628c2ecf20Sopenharmony_ci	0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
638c2ecf20Sopenharmony_ci	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
648c2ecf20Sopenharmony_ci	0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
658c2ecf20Sopenharmony_ci	0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
668c2ecf20Sopenharmony_ci	0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
678c2ecf20Sopenharmony_ci	0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
688c2ecf20Sopenharmony_ci	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
698c2ecf20Sopenharmony_ci	0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
708c2ecf20Sopenharmony_ci	0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
718c2ecf20Sopenharmony_ci	0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
728c2ecf20Sopenharmony_ci	0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
738c2ecf20Sopenharmony_ci	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
748c2ecf20Sopenharmony_ci	0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
758c2ecf20Sopenharmony_ci	0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
768c2ecf20Sopenharmony_ci	0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
778c2ecf20Sopenharmony_ci	0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
788c2ecf20Sopenharmony_ci	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
798c2ecf20Sopenharmony_ci	0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic u32 dvb_crc32(u32 crc, u8 *data, u32 len)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	/* from libdvbv5 */
858c2ecf20Sopenharmony_ci	while (len--)
868c2ecf20Sopenharmony_ci		crc = (crc << 8) ^ CRC_LUT[((crc >> 24) ^ *data++) & 0xff];
878c2ecf20Sopenharmony_ci	return crc;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void vidtv_psi_update_version_num(struct vidtv_psi_table_header *h)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	h->version++;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic u16 vidtv_psi_get_sec_len(struct vidtv_psi_table_header *h)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u16 mask;
988c2ecf20Sopenharmony_ci	u16 ret;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	mask = GENMASK(11, 0);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ret = be16_to_cpu(h->bitfield) & mask;
1038c2ecf20Sopenharmony_ci	return ret;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ciu16 vidtv_psi_get_pat_program_pid(struct vidtv_psi_table_pat_program *p)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	u16 mask;
1098c2ecf20Sopenharmony_ci	u16 ret;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	mask = GENMASK(12, 0);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	ret = be16_to_cpu(p->bitfield) & mask;
1148c2ecf20Sopenharmony_ci	return ret;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ciu16 vidtv_psi_pmt_stream_get_elem_pid(struct vidtv_psi_table_pmt_stream *s)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u16 mask;
1208c2ecf20Sopenharmony_ci	u16 ret;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	mask = GENMASK(12, 0);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	ret = be16_to_cpu(s->bitfield) & mask;
1258c2ecf20Sopenharmony_ci	return ret;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void vidtv_psi_set_desc_loop_len(__be16 *bitfield, u16 new_len,
1298c2ecf20Sopenharmony_ci					u8 desc_len_nbits)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	__be16 new;
1328c2ecf20Sopenharmony_ci	u16 mask;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mask = GENMASK(15, desc_len_nbits);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	new = cpu_to_be16((be16_to_cpu(*bitfield) & mask) | new_len);
1378c2ecf20Sopenharmony_ci	*bitfield = new;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void vidtv_psi_set_sec_len(struct vidtv_psi_table_header *h, u16 new_len)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	u16 old_len = vidtv_psi_get_sec_len(h);
1438c2ecf20Sopenharmony_ci	__be16 new;
1448c2ecf20Sopenharmony_ci	u16 mask;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mask = GENMASK(15, 13);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	new = cpu_to_be16((be16_to_cpu(h->bitfield) & mask) | new_len);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (old_len > MAX_SECTION_LEN)
1518c2ecf20Sopenharmony_ci		pr_warn_ratelimited("section length: %d > %d, old len was %d\n",
1528c2ecf20Sopenharmony_ci				    new_len,
1538c2ecf20Sopenharmony_ci				    MAX_SECTION_LEN,
1548c2ecf20Sopenharmony_ci				    old_len);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	h->bitfield = new;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/*
1608c2ecf20Sopenharmony_ci * Packetize PSI sections into TS packets:
1618c2ecf20Sopenharmony_ci * push a TS header (4bytes) every 184 bytes
1628c2ecf20Sopenharmony_ci * manage the continuity_counter
1638c2ecf20Sopenharmony_ci * add stuffing (i.e. padding bytes) after the CRC
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic u32 vidtv_psi_ts_psi_write_into(struct psi_write_args *args)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct vidtv_mpeg_ts ts_header = {
1688c2ecf20Sopenharmony_ci		.sync_byte = TS_SYNC_BYTE,
1698c2ecf20Sopenharmony_ci		.bitfield = cpu_to_be16((args->new_psi_section << 14) | args->pid),
1708c2ecf20Sopenharmony_ci		.scrambling = 0,
1718c2ecf20Sopenharmony_ci		.payload = 1,
1728c2ecf20Sopenharmony_ci		.adaptation_field = 0, /* no adaptation field */
1738c2ecf20Sopenharmony_ci	};
1748c2ecf20Sopenharmony_ci	u32 nbytes_past_boundary = (args->dest_offset % TS_PACKET_LEN);
1758c2ecf20Sopenharmony_ci	bool aligned = (nbytes_past_boundary == 0);
1768c2ecf20Sopenharmony_ci	u32 remaining_len = args->len;
1778c2ecf20Sopenharmony_ci	u32 payload_write_len = 0;
1788c2ecf20Sopenharmony_ci	u32 payload_offset = 0;
1798c2ecf20Sopenharmony_ci	u32 nbytes = 0;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (!args->crc && !args->is_crc)
1828c2ecf20Sopenharmony_ci		pr_warn_ratelimited("Missing CRC for chunk\n");
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (args->crc)
1858c2ecf20Sopenharmony_ci		*args->crc = dvb_crc32(*args->crc, args->from, args->len);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (args->new_psi_section && !aligned) {
1888c2ecf20Sopenharmony_ci		pr_warn_ratelimited("Cannot write a new PSI section in a misaligned buffer\n");
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		/* forcibly align and hope for the best */
1918c2ecf20Sopenharmony_ci		nbytes += vidtv_memset(args->dest_buf,
1928c2ecf20Sopenharmony_ci				       args->dest_offset + nbytes,
1938c2ecf20Sopenharmony_ci				       args->dest_buf_sz,
1948c2ecf20Sopenharmony_ci				       TS_FILL_BYTE,
1958c2ecf20Sopenharmony_ci				       TS_PACKET_LEN - nbytes_past_boundary);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	while (remaining_len) {
1998c2ecf20Sopenharmony_ci		nbytes_past_boundary = (args->dest_offset + nbytes) % TS_PACKET_LEN;
2008c2ecf20Sopenharmony_ci		aligned = (nbytes_past_boundary == 0);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		if (aligned) {
2038c2ecf20Sopenharmony_ci			/* if at a packet boundary, write a new TS header */
2048c2ecf20Sopenharmony_ci			ts_header.continuity_counter = *args->continuity_counter;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci			nbytes += vidtv_memcpy(args->dest_buf,
2078c2ecf20Sopenharmony_ci					       args->dest_offset + nbytes,
2088c2ecf20Sopenharmony_ci					       args->dest_buf_sz,
2098c2ecf20Sopenharmony_ci					       &ts_header,
2108c2ecf20Sopenharmony_ci					       sizeof(ts_header));
2118c2ecf20Sopenharmony_ci			/*
2128c2ecf20Sopenharmony_ci			 * This will trigger a discontinuity if the buffer is full,
2138c2ecf20Sopenharmony_ci			 * effectively dropping the packet.
2148c2ecf20Sopenharmony_ci			 */
2158c2ecf20Sopenharmony_ci			vidtv_ts_inc_cc(args->continuity_counter);
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		/* write the pointer_field in the first byte of the payload */
2198c2ecf20Sopenharmony_ci		if (args->new_psi_section)
2208c2ecf20Sopenharmony_ci			nbytes += vidtv_memset(args->dest_buf,
2218c2ecf20Sopenharmony_ci					       args->dest_offset + nbytes,
2228c2ecf20Sopenharmony_ci					       args->dest_buf_sz,
2238c2ecf20Sopenharmony_ci					       0x0,
2248c2ecf20Sopenharmony_ci					       1);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* write as much of the payload as possible */
2278c2ecf20Sopenharmony_ci		nbytes_past_boundary = (args->dest_offset + nbytes) % TS_PACKET_LEN;
2288c2ecf20Sopenharmony_ci		payload_write_len = min(TS_PACKET_LEN - nbytes_past_boundary, remaining_len);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		nbytes += vidtv_memcpy(args->dest_buf,
2318c2ecf20Sopenharmony_ci				       args->dest_offset + nbytes,
2328c2ecf20Sopenharmony_ci				       args->dest_buf_sz,
2338c2ecf20Sopenharmony_ci				       args->from + payload_offset,
2348c2ecf20Sopenharmony_ci				       payload_write_len);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		/* 'payload_write_len' written from a total of 'len' requested*/
2378c2ecf20Sopenharmony_ci		remaining_len -= payload_write_len;
2388c2ecf20Sopenharmony_ci		payload_offset += payload_write_len;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/*
2428c2ecf20Sopenharmony_ci	 * fill the rest of the packet if there is any remaining space unused
2438c2ecf20Sopenharmony_ci	 */
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	nbytes_past_boundary = (args->dest_offset + nbytes) % TS_PACKET_LEN;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (args->is_crc)
2488c2ecf20Sopenharmony_ci		nbytes += vidtv_memset(args->dest_buf,
2498c2ecf20Sopenharmony_ci				       args->dest_offset + nbytes,
2508c2ecf20Sopenharmony_ci				       args->dest_buf_sz,
2518c2ecf20Sopenharmony_ci				       TS_FILL_BYTE,
2528c2ecf20Sopenharmony_ci				       TS_PACKET_LEN - nbytes_past_boundary);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return nbytes;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic u32 table_section_crc32_write_into(struct crc32_write_args *args)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct psi_write_args psi_args = {
2608c2ecf20Sopenharmony_ci		.dest_buf           = args->dest_buf,
2618c2ecf20Sopenharmony_ci		.from               = &args->crc,
2628c2ecf20Sopenharmony_ci		.len                = CRC_SIZE_IN_BYTES,
2638c2ecf20Sopenharmony_ci		.dest_offset        = args->dest_offset,
2648c2ecf20Sopenharmony_ci		.pid                = args->pid,
2658c2ecf20Sopenharmony_ci		.new_psi_section    = false,
2668c2ecf20Sopenharmony_ci		.continuity_counter = args->continuity_counter,
2678c2ecf20Sopenharmony_ci		.is_crc             = true,
2688c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->dest_buf_sz,
2698c2ecf20Sopenharmony_ci	};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* the CRC is the last entry in the section */
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return vidtv_psi_ts_psi_write_into(&psi_args);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void vidtv_psi_desc_chain(struct vidtv_psi_desc *head, struct vidtv_psi_desc *desc)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	if (head) {
2798c2ecf20Sopenharmony_ci		while (head->next)
2808c2ecf20Sopenharmony_ci			head = head->next;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		head->next = desc;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistruct vidtv_psi_desc_service *vidtv_psi_service_desc_init(struct vidtv_psi_desc *head,
2878c2ecf20Sopenharmony_ci							   enum service_type service_type,
2888c2ecf20Sopenharmony_ci							   char *service_name,
2898c2ecf20Sopenharmony_ci							   char *provider_name)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service *desc;
2928c2ecf20Sopenharmony_ci	u32 service_name_len = service_name ? strlen(service_name) : 0;
2938c2ecf20Sopenharmony_ci	u32 provider_name_len = provider_name ? strlen(provider_name) : 0;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
2968c2ecf20Sopenharmony_ci	if (!desc)
2978c2ecf20Sopenharmony_ci		return NULL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	desc->type = SERVICE_DESCRIPTOR;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	desc->length = sizeof_field(struct vidtv_psi_desc_service, service_type)
3028c2ecf20Sopenharmony_ci		       + sizeof_field(struct vidtv_psi_desc_service, provider_name_len)
3038c2ecf20Sopenharmony_ci		       + provider_name_len
3048c2ecf20Sopenharmony_ci		       + sizeof_field(struct vidtv_psi_desc_service, service_name_len)
3058c2ecf20Sopenharmony_ci		       + service_name_len;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	desc->service_type = service_type;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	desc->service_name_len = service_name_len;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (service_name && service_name_len) {
3128c2ecf20Sopenharmony_ci		desc->service_name = kstrdup(service_name, GFP_KERNEL);
3138c2ecf20Sopenharmony_ci		if (!desc->service_name)
3148c2ecf20Sopenharmony_ci			goto free_desc;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	desc->provider_name_len = provider_name_len;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (provider_name && provider_name_len) {
3208c2ecf20Sopenharmony_ci		desc->provider_name = kstrdup(provider_name, GFP_KERNEL);
3218c2ecf20Sopenharmony_ci		if (!desc->provider_name)
3228c2ecf20Sopenharmony_ci			goto free_desc_service_name;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	vidtv_psi_desc_chain(head, (struct vidtv_psi_desc *)desc);
3268c2ecf20Sopenharmony_ci	return desc;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cifree_desc_service_name:
3298c2ecf20Sopenharmony_ci	if (service_name && service_name_len)
3308c2ecf20Sopenharmony_ci		kfree(desc->service_name);
3318c2ecf20Sopenharmony_cifree_desc:
3328c2ecf20Sopenharmony_ci	kfree(desc);
3338c2ecf20Sopenharmony_ci	return NULL;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistruct vidtv_psi_desc_registration
3378c2ecf20Sopenharmony_ci*vidtv_psi_registration_desc_init(struct vidtv_psi_desc *head,
3388c2ecf20Sopenharmony_ci				  __be32 format_id,
3398c2ecf20Sopenharmony_ci				  u8 *additional_ident_info,
3408c2ecf20Sopenharmony_ci				  u32 additional_info_len)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_registration *desc;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc) + sizeof(format_id) + additional_info_len, GFP_KERNEL);
3458c2ecf20Sopenharmony_ci	if (!desc)
3468c2ecf20Sopenharmony_ci		return NULL;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	desc->type = REGISTRATION_DESCRIPTOR;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	desc->length = sizeof_field(struct vidtv_psi_desc_registration, format_id)
3518c2ecf20Sopenharmony_ci		       + additional_info_len;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	desc->format_id = format_id;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (additional_ident_info && additional_info_len)
3568c2ecf20Sopenharmony_ci		memcpy(desc->additional_identification_info,
3578c2ecf20Sopenharmony_ci		       additional_ident_info,
3588c2ecf20Sopenharmony_ci		       additional_info_len);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	vidtv_psi_desc_chain(head, (struct vidtv_psi_desc *)desc);
3618c2ecf20Sopenharmony_ci	return desc;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistruct vidtv_psi_desc_network_name
3658c2ecf20Sopenharmony_ci*vidtv_psi_network_name_desc_init(struct vidtv_psi_desc *head, char *network_name)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	u32 network_name_len = network_name ? strlen(network_name) : 0;
3688c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_network_name *desc;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
3718c2ecf20Sopenharmony_ci	if (!desc)
3728c2ecf20Sopenharmony_ci		return NULL;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	desc->type = NETWORK_NAME_DESCRIPTOR;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	desc->length = network_name_len;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (network_name && network_name_len) {
3798c2ecf20Sopenharmony_ci		desc->network_name = kstrdup(network_name, GFP_KERNEL);
3808c2ecf20Sopenharmony_ci		if (!desc->network_name) {
3818c2ecf20Sopenharmony_ci			kfree(desc);
3828c2ecf20Sopenharmony_ci			return NULL;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	vidtv_psi_desc_chain(head, (struct vidtv_psi_desc *)desc);
3878c2ecf20Sopenharmony_ci	return desc;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistruct vidtv_psi_desc_service_list
3918c2ecf20Sopenharmony_ci*vidtv_psi_service_list_desc_init(struct vidtv_psi_desc *head,
3928c2ecf20Sopenharmony_ci				  struct vidtv_psi_desc_service_list_entry *entry)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list_entry *curr_e = NULL;
3958c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list_entry *head_e = NULL;
3968c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list_entry *prev_e = NULL;
3978c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list *desc;
3988c2ecf20Sopenharmony_ci	u16 length = 0;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
4018c2ecf20Sopenharmony_ci	if (!desc)
4028c2ecf20Sopenharmony_ci		return NULL;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	desc->type = SERVICE_LIST_DESCRIPTOR;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	while (entry) {
4078c2ecf20Sopenharmony_ci		curr_e = kzalloc(sizeof(*curr_e), GFP_KERNEL);
4088c2ecf20Sopenharmony_ci		if (!curr_e) {
4098c2ecf20Sopenharmony_ci			while (head_e) {
4108c2ecf20Sopenharmony_ci				curr_e = head_e;
4118c2ecf20Sopenharmony_ci				head_e = head_e->next;
4128c2ecf20Sopenharmony_ci				kfree(curr_e);
4138c2ecf20Sopenharmony_ci			}
4148c2ecf20Sopenharmony_ci			kfree(desc);
4158c2ecf20Sopenharmony_ci			return NULL;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		curr_e->service_id = entry->service_id;
4198c2ecf20Sopenharmony_ci		curr_e->service_type = entry->service_type;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		length += sizeof(struct vidtv_psi_desc_service_list_entry) -
4228c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_desc_service_list_entry *);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		if (!head_e)
4258c2ecf20Sopenharmony_ci			head_e = curr_e;
4268c2ecf20Sopenharmony_ci		if (prev_e)
4278c2ecf20Sopenharmony_ci			prev_e->next = curr_e;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		prev_e = curr_e;
4308c2ecf20Sopenharmony_ci		entry = entry->next;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	desc->length = length;
4348c2ecf20Sopenharmony_ci	desc->service_list = head_e;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	vidtv_psi_desc_chain(head, (struct vidtv_psi_desc *)desc);
4378c2ecf20Sopenharmony_ci	return desc;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistruct vidtv_psi_desc_short_event
4418c2ecf20Sopenharmony_ci*vidtv_psi_short_event_desc_init(struct vidtv_psi_desc *head,
4428c2ecf20Sopenharmony_ci				 char *iso_language_code,
4438c2ecf20Sopenharmony_ci				 char *event_name,
4448c2ecf20Sopenharmony_ci				 char *text)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	u32 iso_len =  iso_language_code ? strlen(iso_language_code) : 0;
4478c2ecf20Sopenharmony_ci	u32 event_name_len = event_name ? strlen(event_name) : 0;
4488c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_short_event *desc;
4498c2ecf20Sopenharmony_ci	u32 text_len =  text ? strlen(text) : 0;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
4528c2ecf20Sopenharmony_ci	if (!desc)
4538c2ecf20Sopenharmony_ci		return NULL;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	desc->type = SHORT_EVENT_DESCRIPTOR;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	desc->length = ISO_LANGUAGE_CODE_LEN +
4588c2ecf20Sopenharmony_ci		       sizeof_field(struct vidtv_psi_desc_short_event, event_name_len) +
4598c2ecf20Sopenharmony_ci		       event_name_len +
4608c2ecf20Sopenharmony_ci		       sizeof_field(struct vidtv_psi_desc_short_event, text_len) +
4618c2ecf20Sopenharmony_ci		       text_len;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	desc->event_name_len = event_name_len;
4648c2ecf20Sopenharmony_ci	desc->text_len = text_len;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (iso_len != ISO_LANGUAGE_CODE_LEN)
4678c2ecf20Sopenharmony_ci		iso_language_code = "eng";
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	desc->iso_language_code = kstrdup(iso_language_code, GFP_KERNEL);
4708c2ecf20Sopenharmony_ci	if (!desc->iso_language_code)
4718c2ecf20Sopenharmony_ci		goto free_desc;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (event_name && event_name_len) {
4748c2ecf20Sopenharmony_ci		desc->event_name = kstrdup(event_name, GFP_KERNEL);
4758c2ecf20Sopenharmony_ci		if (!desc->event_name)
4768c2ecf20Sopenharmony_ci			goto free_desc_language_code;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (text && text_len) {
4808c2ecf20Sopenharmony_ci		desc->text = kstrdup(text, GFP_KERNEL);
4818c2ecf20Sopenharmony_ci		if (!desc->text)
4828c2ecf20Sopenharmony_ci			goto free_desc_event_name;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	vidtv_psi_desc_chain(head, (struct vidtv_psi_desc *)desc);
4868c2ecf20Sopenharmony_ci	return desc;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cifree_desc_event_name:
4898c2ecf20Sopenharmony_ci	if (event_name && event_name_len)
4908c2ecf20Sopenharmony_ci		kfree(desc->event_name);
4918c2ecf20Sopenharmony_cifree_desc_language_code:
4928c2ecf20Sopenharmony_ci	kfree(desc->iso_language_code);
4938c2ecf20Sopenharmony_cifree_desc:
4948c2ecf20Sopenharmony_ci	kfree(desc);
4958c2ecf20Sopenharmony_ci	return NULL;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistruct vidtv_psi_desc *vidtv_psi_desc_clone(struct vidtv_psi_desc *desc)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_network_name *desc_network_name;
5018c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list *desc_service_list;
5028c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_short_event  *desc_short_event;
5038c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service *service;
5048c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *head = NULL;
5058c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *prev = NULL;
5068c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *curr = NULL;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	while (desc) {
5098c2ecf20Sopenharmony_ci		switch (desc->type) {
5108c2ecf20Sopenharmony_ci		case SERVICE_DESCRIPTOR:
5118c2ecf20Sopenharmony_ci			service = (struct vidtv_psi_desc_service *)desc;
5128c2ecf20Sopenharmony_ci			curr = (struct vidtv_psi_desc *)
5138c2ecf20Sopenharmony_ci			       vidtv_psi_service_desc_init(head,
5148c2ecf20Sopenharmony_ci							   service->service_type,
5158c2ecf20Sopenharmony_ci							   service->service_name,
5168c2ecf20Sopenharmony_ci							   service->provider_name);
5178c2ecf20Sopenharmony_ci		break;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		case NETWORK_NAME_DESCRIPTOR:
5208c2ecf20Sopenharmony_ci			desc_network_name = (struct vidtv_psi_desc_network_name *)desc;
5218c2ecf20Sopenharmony_ci			curr = (struct vidtv_psi_desc *)
5228c2ecf20Sopenharmony_ci			       vidtv_psi_network_name_desc_init(head,
5238c2ecf20Sopenharmony_ci								desc_network_name->network_name);
5248c2ecf20Sopenharmony_ci		break;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		case SERVICE_LIST_DESCRIPTOR:
5278c2ecf20Sopenharmony_ci			desc_service_list = (struct vidtv_psi_desc_service_list *)desc;
5288c2ecf20Sopenharmony_ci			curr = (struct vidtv_psi_desc *)
5298c2ecf20Sopenharmony_ci			       vidtv_psi_service_list_desc_init(head,
5308c2ecf20Sopenharmony_ci								desc_service_list->service_list);
5318c2ecf20Sopenharmony_ci		break;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		case SHORT_EVENT_DESCRIPTOR:
5348c2ecf20Sopenharmony_ci			desc_short_event = (struct vidtv_psi_desc_short_event *)desc;
5358c2ecf20Sopenharmony_ci			curr = (struct vidtv_psi_desc *)
5368c2ecf20Sopenharmony_ci			       vidtv_psi_short_event_desc_init(head,
5378c2ecf20Sopenharmony_ci							       desc_short_event->iso_language_code,
5388c2ecf20Sopenharmony_ci							       desc_short_event->event_name,
5398c2ecf20Sopenharmony_ci							       desc_short_event->text);
5408c2ecf20Sopenharmony_ci		break;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		case REGISTRATION_DESCRIPTOR:
5438c2ecf20Sopenharmony_ci		default:
5448c2ecf20Sopenharmony_ci			curr = kzalloc(sizeof(*desc) + desc->length, GFP_KERNEL);
5458c2ecf20Sopenharmony_ci			if (!curr)
5468c2ecf20Sopenharmony_ci				return NULL;
5478c2ecf20Sopenharmony_ci			memcpy(curr, desc, sizeof(*desc) + desc->length);
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		if (!curr)
5518c2ecf20Sopenharmony_ci			return NULL;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		curr->next = NULL;
5548c2ecf20Sopenharmony_ci		if (!head)
5558c2ecf20Sopenharmony_ci			head = curr;
5568c2ecf20Sopenharmony_ci		if (prev)
5578c2ecf20Sopenharmony_ci			prev->next = curr;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		prev = curr;
5608c2ecf20Sopenharmony_ci		desc = desc->next;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return head;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_civoid vidtv_psi_desc_destroy(struct vidtv_psi_desc *desc)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list_entry *sl_entry_tmp = NULL;
5698c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list_entry *sl_entry = NULL;
5708c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *curr = desc;
5718c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *tmp  = NULL;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	while (curr) {
5748c2ecf20Sopenharmony_ci		tmp  = curr;
5758c2ecf20Sopenharmony_ci		curr = curr->next;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		switch (tmp->type) {
5788c2ecf20Sopenharmony_ci		case SERVICE_DESCRIPTOR:
5798c2ecf20Sopenharmony_ci			kfree(((struct vidtv_psi_desc_service *)tmp)->provider_name);
5808c2ecf20Sopenharmony_ci			kfree(((struct vidtv_psi_desc_service *)tmp)->service_name);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci			break;
5838c2ecf20Sopenharmony_ci		case REGISTRATION_DESCRIPTOR:
5848c2ecf20Sopenharmony_ci			/* nothing to do */
5858c2ecf20Sopenharmony_ci			break;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		case NETWORK_NAME_DESCRIPTOR:
5888c2ecf20Sopenharmony_ci			kfree(((struct vidtv_psi_desc_network_name *)tmp)->network_name);
5898c2ecf20Sopenharmony_ci			break;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		case SERVICE_LIST_DESCRIPTOR:
5928c2ecf20Sopenharmony_ci			sl_entry = ((struct vidtv_psi_desc_service_list *)tmp)->service_list;
5938c2ecf20Sopenharmony_ci			while (sl_entry) {
5948c2ecf20Sopenharmony_ci				sl_entry_tmp = sl_entry;
5958c2ecf20Sopenharmony_ci				sl_entry = sl_entry->next;
5968c2ecf20Sopenharmony_ci				kfree(sl_entry_tmp);
5978c2ecf20Sopenharmony_ci			}
5988c2ecf20Sopenharmony_ci			break;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		case SHORT_EVENT_DESCRIPTOR:
6018c2ecf20Sopenharmony_ci			kfree(((struct vidtv_psi_desc_short_event *)tmp)->iso_language_code);
6028c2ecf20Sopenharmony_ci			kfree(((struct vidtv_psi_desc_short_event *)tmp)->event_name);
6038c2ecf20Sopenharmony_ci			kfree(((struct vidtv_psi_desc_short_event *)tmp)->text);
6048c2ecf20Sopenharmony_ci		break;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		default:
6078c2ecf20Sopenharmony_ci			pr_warn_ratelimited("Possible leak: not handling descriptor type %d\n",
6088c2ecf20Sopenharmony_ci					    tmp->type);
6098c2ecf20Sopenharmony_ci			break;
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci		kfree(tmp);
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic u16
6178c2ecf20Sopenharmony_cividtv_psi_desc_comp_loop_len(struct vidtv_psi_desc *desc)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	u32 length = 0;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (!desc)
6228c2ecf20Sopenharmony_ci		return 0;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	while (desc) {
6258c2ecf20Sopenharmony_ci		length += sizeof_field(struct vidtv_psi_desc, type);
6268c2ecf20Sopenharmony_ci		length += sizeof_field(struct vidtv_psi_desc, length);
6278c2ecf20Sopenharmony_ci		length += desc->length; /* from 'length' field until the end of the descriptor */
6288c2ecf20Sopenharmony_ci		desc    = desc->next;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return length;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_civoid vidtv_psi_desc_assign(struct vidtv_psi_desc **to,
6358c2ecf20Sopenharmony_ci			   struct vidtv_psi_desc *desc)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	if (desc == *to)
6388c2ecf20Sopenharmony_ci		return;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (*to)
6418c2ecf20Sopenharmony_ci		vidtv_psi_desc_destroy(*to);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	*to = desc;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_civoid vidtv_pmt_desc_assign(struct vidtv_psi_table_pmt *pmt,
6478c2ecf20Sopenharmony_ci			   struct vidtv_psi_desc **to,
6488c2ecf20Sopenharmony_ci			   struct vidtv_psi_desc *desc)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	vidtv_psi_desc_assign(to, desc);
6518c2ecf20Sopenharmony_ci	vidtv_psi_pmt_table_update_sec_len(pmt);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (vidtv_psi_get_sec_len(&pmt->header) > MAX_SECTION_LEN)
6548c2ecf20Sopenharmony_ci		vidtv_psi_desc_assign(to, NULL);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	vidtv_psi_update_version_num(&pmt->header);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_civoid vidtv_sdt_desc_assign(struct vidtv_psi_table_sdt *sdt,
6608c2ecf20Sopenharmony_ci			   struct vidtv_psi_desc **to,
6618c2ecf20Sopenharmony_ci			   struct vidtv_psi_desc *desc)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	vidtv_psi_desc_assign(to, desc);
6648c2ecf20Sopenharmony_ci	vidtv_psi_sdt_table_update_sec_len(sdt);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (vidtv_psi_get_sec_len(&sdt->header) > MAX_SECTION_LEN)
6678c2ecf20Sopenharmony_ci		vidtv_psi_desc_assign(to, NULL);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	vidtv_psi_update_version_num(&sdt->header);
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic u32 vidtv_psi_desc_write_into(struct desc_write_args *args)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	struct psi_write_args psi_args = {
6758c2ecf20Sopenharmony_ci		.dest_buf           = args->dest_buf,
6768c2ecf20Sopenharmony_ci		.from               = &args->desc->type,
6778c2ecf20Sopenharmony_ci		.pid                = args->pid,
6788c2ecf20Sopenharmony_ci		.new_psi_section    = false,
6798c2ecf20Sopenharmony_ci		.continuity_counter = args->continuity_counter,
6808c2ecf20Sopenharmony_ci		.is_crc             = false,
6818c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->dest_buf_sz,
6828c2ecf20Sopenharmony_ci		.crc                = args->crc,
6838c2ecf20Sopenharmony_ci		.len		    = sizeof_field(struct vidtv_psi_desc, type) +
6848c2ecf20Sopenharmony_ci				      sizeof_field(struct vidtv_psi_desc, length),
6858c2ecf20Sopenharmony_ci	};
6868c2ecf20Sopenharmony_ci	struct vidtv_psi_desc_service_list_entry *serv_list_entry = NULL;
6878c2ecf20Sopenharmony_ci	u32 nbytes = 0;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	psi_args.dest_offset        = args->dest_offset + nbytes;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	switch (args->desc->type) {
6948c2ecf20Sopenharmony_ci	case SERVICE_DESCRIPTOR:
6958c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
6968c2ecf20Sopenharmony_ci		psi_args.len = sizeof_field(struct vidtv_psi_desc_service, service_type) +
6978c2ecf20Sopenharmony_ci			       sizeof_field(struct vidtv_psi_desc_service, provider_name_len);
6988c2ecf20Sopenharmony_ci		psi_args.from = &((struct vidtv_psi_desc_service *)args->desc)->service_type;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7038c2ecf20Sopenharmony_ci		psi_args.len = ((struct vidtv_psi_desc_service *)args->desc)->provider_name_len;
7048c2ecf20Sopenharmony_ci		psi_args.from = ((struct vidtv_psi_desc_service *)args->desc)->provider_name;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7098c2ecf20Sopenharmony_ci		psi_args.len = sizeof_field(struct vidtv_psi_desc_service, service_name_len);
7108c2ecf20Sopenharmony_ci		psi_args.from = &((struct vidtv_psi_desc_service *)args->desc)->service_name_len;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7158c2ecf20Sopenharmony_ci		psi_args.len = ((struct vidtv_psi_desc_service *)args->desc)->service_name_len;
7168c2ecf20Sopenharmony_ci		psi_args.from = ((struct vidtv_psi_desc_service *)args->desc)->service_name;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7198c2ecf20Sopenharmony_ci		break;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	case NETWORK_NAME_DESCRIPTOR:
7228c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7238c2ecf20Sopenharmony_ci		psi_args.len = args->desc->length;
7248c2ecf20Sopenharmony_ci		psi_args.from = ((struct vidtv_psi_desc_network_name *)args->desc)->network_name;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7278c2ecf20Sopenharmony_ci		break;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	case SERVICE_LIST_DESCRIPTOR:
7308c2ecf20Sopenharmony_ci		serv_list_entry = ((struct vidtv_psi_desc_service_list *)args->desc)->service_list;
7318c2ecf20Sopenharmony_ci		while (serv_list_entry) {
7328c2ecf20Sopenharmony_ci			psi_args.dest_offset = args->dest_offset + nbytes;
7338c2ecf20Sopenharmony_ci			psi_args.len = sizeof(struct vidtv_psi_desc_service_list_entry) -
7348c2ecf20Sopenharmony_ci				       sizeof(struct vidtv_psi_desc_service_list_entry *);
7358c2ecf20Sopenharmony_ci			psi_args.from = serv_list_entry;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci			nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci			serv_list_entry = serv_list_entry->next;
7408c2ecf20Sopenharmony_ci		}
7418c2ecf20Sopenharmony_ci		break;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	case SHORT_EVENT_DESCRIPTOR:
7448c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7458c2ecf20Sopenharmony_ci		psi_args.len = ISO_LANGUAGE_CODE_LEN;
7468c2ecf20Sopenharmony_ci		psi_args.from = ((struct vidtv_psi_desc_short_event *)
7478c2ecf20Sopenharmony_ci				  args->desc)->iso_language_code;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7528c2ecf20Sopenharmony_ci		psi_args.len = sizeof_field(struct vidtv_psi_desc_short_event, event_name_len);
7538c2ecf20Sopenharmony_ci		psi_args.from = &((struct vidtv_psi_desc_short_event *)
7548c2ecf20Sopenharmony_ci				  args->desc)->event_name_len;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7598c2ecf20Sopenharmony_ci		psi_args.len = ((struct vidtv_psi_desc_short_event *)args->desc)->event_name_len;
7608c2ecf20Sopenharmony_ci		psi_args.from = ((struct vidtv_psi_desc_short_event *)args->desc)->event_name;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7658c2ecf20Sopenharmony_ci		psi_args.len = sizeof_field(struct vidtv_psi_desc_short_event, text_len);
7668c2ecf20Sopenharmony_ci		psi_args.from = &((struct vidtv_psi_desc_short_event *)args->desc)->text_len;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7718c2ecf20Sopenharmony_ci		psi_args.len = ((struct vidtv_psi_desc_short_event *)args->desc)->text_len;
7728c2ecf20Sopenharmony_ci		psi_args.from = ((struct vidtv_psi_desc_short_event *)args->desc)->text;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci		break;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	case REGISTRATION_DESCRIPTOR:
7798c2ecf20Sopenharmony_ci	default:
7808c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->dest_offset + nbytes;
7818c2ecf20Sopenharmony_ci		psi_args.len = args->desc->length;
7828c2ecf20Sopenharmony_ci		psi_args.from = &args->desc->data;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
7858c2ecf20Sopenharmony_ci		break;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	return nbytes;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic u32
7928c2ecf20Sopenharmony_cividtv_psi_table_header_write_into(struct header_write_args *args)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct psi_write_args psi_args = {
7958c2ecf20Sopenharmony_ci		.dest_buf           = args->dest_buf,
7968c2ecf20Sopenharmony_ci		.from               = args->h,
7978c2ecf20Sopenharmony_ci		.len                = sizeof(struct vidtv_psi_table_header),
7988c2ecf20Sopenharmony_ci		.dest_offset        = args->dest_offset,
7998c2ecf20Sopenharmony_ci		.pid                = args->pid,
8008c2ecf20Sopenharmony_ci		.new_psi_section    = true,
8018c2ecf20Sopenharmony_ci		.continuity_counter = args->continuity_counter,
8028c2ecf20Sopenharmony_ci		.is_crc             = false,
8038c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->dest_buf_sz,
8048c2ecf20Sopenharmony_ci		.crc                = args->crc,
8058c2ecf20Sopenharmony_ci	};
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	return vidtv_psi_ts_psi_write_into(&psi_args);
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_civoid
8118c2ecf20Sopenharmony_cividtv_psi_pat_table_update_sec_len(struct vidtv_psi_table_pat *pat)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	u16 length = 0;
8148c2ecf20Sopenharmony_ci	u32 i;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/* see ISO/IEC 13818-1 : 2000 p.43 */
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* from immediately after 'section_length' until 'last_section_number'*/
8198c2ecf20Sopenharmony_ci	length += PAT_LEN_UNTIL_LAST_SECTION_NUMBER;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/* do not count the pointer */
8228c2ecf20Sopenharmony_ci	for (i = 0; i < pat->num_pat; ++i)
8238c2ecf20Sopenharmony_ci		length += sizeof(struct vidtv_psi_table_pat_program) -
8248c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_table_pat_program *);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	length += CRC_SIZE_IN_BYTES;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	vidtv_psi_set_sec_len(&pat->header, length);
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_civoid vidtv_psi_pmt_table_update_sec_len(struct vidtv_psi_table_pmt *pmt)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt_stream *s = pmt->stream;
8348c2ecf20Sopenharmony_ci	u16 desc_loop_len;
8358c2ecf20Sopenharmony_ci	u16 length = 0;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/* see ISO/IEC 13818-1 : 2000 p.46 */
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/* from immediately after 'section_length' until 'program_info_length'*/
8408c2ecf20Sopenharmony_ci	length += PMT_LEN_UNTIL_PROGRAM_INFO_LENGTH;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	desc_loop_len = vidtv_psi_desc_comp_loop_len(pmt->descriptor);
8438c2ecf20Sopenharmony_ci	vidtv_psi_set_desc_loop_len(&pmt->bitfield2, desc_loop_len, 10);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	length += desc_loop_len;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	while (s) {
8488c2ecf20Sopenharmony_ci		/* skip both pointers at the end */
8498c2ecf20Sopenharmony_ci		length += sizeof(struct vidtv_psi_table_pmt_stream) -
8508c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_desc *) -
8518c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_table_pmt_stream *);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		desc_loop_len = vidtv_psi_desc_comp_loop_len(s->descriptor);
8548c2ecf20Sopenharmony_ci		vidtv_psi_set_desc_loop_len(&s->bitfield2, desc_loop_len, 10);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		length += desc_loop_len;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci		s = s->next;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	length += CRC_SIZE_IN_BYTES;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	vidtv_psi_set_sec_len(&pmt->header, length);
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_civoid vidtv_psi_sdt_table_update_sec_len(struct vidtv_psi_table_sdt *sdt)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	struct vidtv_psi_table_sdt_service *s = sdt->service;
8698c2ecf20Sopenharmony_ci	u16 desc_loop_len;
8708c2ecf20Sopenharmony_ci	u16 length = 0;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* see ETSI EN 300 468 V 1.10.1 p.24 */
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/*
8758c2ecf20Sopenharmony_ci	 * from immediately after 'section_length' until
8768c2ecf20Sopenharmony_ci	 * 'reserved_for_future_use'
8778c2ecf20Sopenharmony_ci	 */
8788c2ecf20Sopenharmony_ci	length += SDT_LEN_UNTIL_RESERVED_FOR_FUTURE_USE;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	while (s) {
8818c2ecf20Sopenharmony_ci		/* skip both pointers at the end */
8828c2ecf20Sopenharmony_ci		length += sizeof(struct vidtv_psi_table_sdt_service) -
8838c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_desc *) -
8848c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_table_sdt_service *);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		desc_loop_len = vidtv_psi_desc_comp_loop_len(s->descriptor);
8878c2ecf20Sopenharmony_ci		vidtv_psi_set_desc_loop_len(&s->bitfield, desc_loop_len, 12);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		length += desc_loop_len;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci		s = s->next;
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	length += CRC_SIZE_IN_BYTES;
8958c2ecf20Sopenharmony_ci	vidtv_psi_set_sec_len(&sdt->header, length);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistruct vidtv_psi_table_pat_program*
8998c2ecf20Sopenharmony_cividtv_psi_pat_program_init(struct vidtv_psi_table_pat_program *head,
9008c2ecf20Sopenharmony_ci			   u16 service_id,
9018c2ecf20Sopenharmony_ci			   u16 program_map_pid)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *program;
9048c2ecf20Sopenharmony_ci	const u16 RESERVED = 0x07;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	program = kzalloc(sizeof(*program), GFP_KERNEL);
9078c2ecf20Sopenharmony_ci	if (!program)
9088c2ecf20Sopenharmony_ci		return NULL;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	program->service_id = cpu_to_be16(service_id);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* pid for the PMT section in the TS */
9138c2ecf20Sopenharmony_ci	program->bitfield = cpu_to_be16((RESERVED << 13) | program_map_pid);
9148c2ecf20Sopenharmony_ci	program->next = NULL;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (head) {
9178c2ecf20Sopenharmony_ci		while (head->next)
9188c2ecf20Sopenharmony_ci			head = head->next;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci		head->next = program;
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	return program;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_civoid
9278c2ecf20Sopenharmony_cividtv_psi_pat_program_destroy(struct vidtv_psi_table_pat_program *p)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *tmp  = NULL;
9308c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *curr = p;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	while (curr) {
9338c2ecf20Sopenharmony_ci		tmp  = curr;
9348c2ecf20Sopenharmony_ci		curr = curr->next;
9358c2ecf20Sopenharmony_ci		kfree(tmp);
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci/* This function transfers ownership of p to the table */
9408c2ecf20Sopenharmony_civoid
9418c2ecf20Sopenharmony_cividtv_psi_pat_program_assign(struct vidtv_psi_table_pat *pat,
9428c2ecf20Sopenharmony_ci			     struct vidtv_psi_table_pat_program *p)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *program;
9458c2ecf20Sopenharmony_ci	u16 program_count;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	do {
9488c2ecf20Sopenharmony_ci		program_count = 0;
9498c2ecf20Sopenharmony_ci		program = p;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci		if (p == pat->program)
9528c2ecf20Sopenharmony_ci			return;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci		while (program) {
9558c2ecf20Sopenharmony_ci			++program_count;
9568c2ecf20Sopenharmony_ci			program = program->next;
9578c2ecf20Sopenharmony_ci		}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		pat->num_pat = program_count;
9608c2ecf20Sopenharmony_ci		pat->program  = p;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		/* Recompute section length */
9638c2ecf20Sopenharmony_ci		vidtv_psi_pat_table_update_sec_len(pat);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci		p = NULL;
9668c2ecf20Sopenharmony_ci	} while (vidtv_psi_get_sec_len(&pat->header) > MAX_SECTION_LEN);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	vidtv_psi_update_version_num(&pat->header);
9698c2ecf20Sopenharmony_ci}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_cistruct vidtv_psi_table_pat *vidtv_psi_pat_table_init(u16 transport_stream_id)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat *pat;
9748c2ecf20Sopenharmony_ci	const u16 SYNTAX = 0x1;
9758c2ecf20Sopenharmony_ci	const u16 ZERO = 0x0;
9768c2ecf20Sopenharmony_ci	const u16 ONES = 0x03;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	pat = kzalloc(sizeof(*pat), GFP_KERNEL);
9798c2ecf20Sopenharmony_ci	if (!pat)
9808c2ecf20Sopenharmony_ci		return NULL;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	pat->header.table_id = 0x0;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	pat->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ZERO << 14) | (ONES << 12));
9858c2ecf20Sopenharmony_ci	pat->header.id           = cpu_to_be16(transport_stream_id);
9868c2ecf20Sopenharmony_ci	pat->header.current_next = 0x1;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	pat->header.version = 0x1f;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	pat->header.one2         = 0x03;
9918c2ecf20Sopenharmony_ci	pat->header.section_id   = 0x0;
9928c2ecf20Sopenharmony_ci	pat->header.last_section = 0x0;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	vidtv_psi_pat_table_update_sec_len(pat);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	return pat;
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ciu32 vidtv_psi_pat_write_into(struct vidtv_psi_pat_write_args *args)
10008c2ecf20Sopenharmony_ci{
10018c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *p = args->pat->program;
10028c2ecf20Sopenharmony_ci	struct header_write_args h_args       = {
10038c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
10048c2ecf20Sopenharmony_ci		.dest_offset        = args->offset,
10058c2ecf20Sopenharmony_ci		.pid                = VIDTV_PAT_PID,
10068c2ecf20Sopenharmony_ci		.h                  = &args->pat->header,
10078c2ecf20Sopenharmony_ci		.continuity_counter = args->continuity_counter,
10088c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
10098c2ecf20Sopenharmony_ci	};
10108c2ecf20Sopenharmony_ci	struct psi_write_args psi_args        = {
10118c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
10128c2ecf20Sopenharmony_ci		.pid                = VIDTV_PAT_PID,
10138c2ecf20Sopenharmony_ci		.new_psi_section    = false,
10148c2ecf20Sopenharmony_ci		.continuity_counter = args->continuity_counter,
10158c2ecf20Sopenharmony_ci		.is_crc             = false,
10168c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
10178c2ecf20Sopenharmony_ci	};
10188c2ecf20Sopenharmony_ci	struct crc32_write_args c_args        = {
10198c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
10208c2ecf20Sopenharmony_ci		.pid                = VIDTV_PAT_PID,
10218c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
10228c2ecf20Sopenharmony_ci	};
10238c2ecf20Sopenharmony_ci	u32 crc = INITIAL_CRC;
10248c2ecf20Sopenharmony_ci	u32 nbytes = 0;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	vidtv_psi_pat_table_update_sec_len(args->pat);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	h_args.crc = &crc;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_table_header_write_into(&h_args);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	/* note that the field 'u16 programs' is not really part of the PAT */
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	psi_args.crc = &crc;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	while (p) {
10378c2ecf20Sopenharmony_ci		/* copy the PAT programs */
10388c2ecf20Sopenharmony_ci		psi_args.from = p;
10398c2ecf20Sopenharmony_ci		/* skip the pointer */
10408c2ecf20Sopenharmony_ci		psi_args.len = sizeof(*p) -
10418c2ecf20Sopenharmony_ci			       sizeof(struct vidtv_psi_table_pat_program *);
10428c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->offset + nbytes;
10438c2ecf20Sopenharmony_ci		psi_args.continuity_counter = args->continuity_counter;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci		p = p->next;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	c_args.dest_offset        = args->offset + nbytes;
10518c2ecf20Sopenharmony_ci	c_args.continuity_counter = args->continuity_counter;
10528c2ecf20Sopenharmony_ci	c_args.crc                = cpu_to_be32(crc);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/* Write the CRC32 at the end */
10558c2ecf20Sopenharmony_ci	nbytes += table_section_crc32_write_into(&c_args);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	return nbytes;
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_civoid
10618c2ecf20Sopenharmony_cividtv_psi_pat_table_destroy(struct vidtv_psi_table_pat *p)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	vidtv_psi_pat_program_destroy(p->program);
10648c2ecf20Sopenharmony_ci	kfree(p);
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistruct vidtv_psi_table_pmt_stream*
10688c2ecf20Sopenharmony_cividtv_psi_pmt_stream_init(struct vidtv_psi_table_pmt_stream *head,
10698c2ecf20Sopenharmony_ci			  enum vidtv_psi_stream_types stream_type,
10708c2ecf20Sopenharmony_ci			  u16 es_pid)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt_stream *stream;
10738c2ecf20Sopenharmony_ci	const u16 RESERVED1 = 0x07;
10748c2ecf20Sopenharmony_ci	const u16 RESERVED2 = 0x0f;
10758c2ecf20Sopenharmony_ci	const u16 ZERO = 0x0;
10768c2ecf20Sopenharmony_ci	u16 desc_loop_len;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
10798c2ecf20Sopenharmony_ci	if (!stream)
10808c2ecf20Sopenharmony_ci		return NULL;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	stream->type = stream_type;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	stream->bitfield = cpu_to_be16((RESERVED1 << 13) | es_pid);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	desc_loop_len = vidtv_psi_desc_comp_loop_len(stream->descriptor);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	stream->bitfield2 = cpu_to_be16((RESERVED2 << 12) |
10898c2ecf20Sopenharmony_ci					(ZERO << 10)      |
10908c2ecf20Sopenharmony_ci					desc_loop_len);
10918c2ecf20Sopenharmony_ci	stream->next = NULL;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (head) {
10948c2ecf20Sopenharmony_ci		while (head->next)
10958c2ecf20Sopenharmony_ci			head = head->next;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		head->next = stream;
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	return stream;
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_civoid vidtv_psi_pmt_stream_destroy(struct vidtv_psi_table_pmt_stream *s)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt_stream *tmp_stream  = NULL;
11068c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt_stream *curr_stream = s;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	while (curr_stream) {
11098c2ecf20Sopenharmony_ci		tmp_stream  = curr_stream;
11108c2ecf20Sopenharmony_ci		curr_stream = curr_stream->next;
11118c2ecf20Sopenharmony_ci		vidtv_psi_desc_destroy(tmp_stream->descriptor);
11128c2ecf20Sopenharmony_ci		kfree(tmp_stream);
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_civoid vidtv_psi_pmt_stream_assign(struct vidtv_psi_table_pmt *pmt,
11178c2ecf20Sopenharmony_ci				 struct vidtv_psi_table_pmt_stream *s)
11188c2ecf20Sopenharmony_ci{
11198c2ecf20Sopenharmony_ci	do {
11208c2ecf20Sopenharmony_ci		/* This function transfers ownership of s to the table */
11218c2ecf20Sopenharmony_ci		if (s == pmt->stream)
11228c2ecf20Sopenharmony_ci			return;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		pmt->stream = s;
11258c2ecf20Sopenharmony_ci		vidtv_psi_pmt_table_update_sec_len(pmt);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci		s = NULL;
11288c2ecf20Sopenharmony_ci	} while (vidtv_psi_get_sec_len(&pmt->header) > MAX_SECTION_LEN);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	vidtv_psi_update_version_num(&pmt->header);
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ciu16 vidtv_psi_pmt_get_pid(struct vidtv_psi_table_pmt *section,
11348c2ecf20Sopenharmony_ci			  struct vidtv_psi_table_pat *pat)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *program = pat->program;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	/*
11398c2ecf20Sopenharmony_ci	 * service_id is the same as program_number in the
11408c2ecf20Sopenharmony_ci	 * corresponding program_map_section
11418c2ecf20Sopenharmony_ci	 * see ETSI EN 300 468 v1.15.1 p. 24
11428c2ecf20Sopenharmony_ci	 */
11438c2ecf20Sopenharmony_ci	while (program) {
11448c2ecf20Sopenharmony_ci		if (program->service_id == section->header.id)
11458c2ecf20Sopenharmony_ci			return vidtv_psi_get_pat_program_pid(program);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		program = program->next;
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	return TS_LAST_VALID_PID + 1; /* not found */
11518c2ecf20Sopenharmony_ci}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_cistruct vidtv_psi_table_pmt *vidtv_psi_pmt_table_init(u16 program_number,
11548c2ecf20Sopenharmony_ci						     u16 pcr_pid)
11558c2ecf20Sopenharmony_ci{
11568c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt *pmt;
11578c2ecf20Sopenharmony_ci	const u16 RESERVED1 = 0x07;
11588c2ecf20Sopenharmony_ci	const u16 RESERVED2 = 0x0f;
11598c2ecf20Sopenharmony_ci	const u16 SYNTAX = 0x1;
11608c2ecf20Sopenharmony_ci	const u16 ONES = 0x03;
11618c2ecf20Sopenharmony_ci	const u16 ZERO = 0x0;
11628c2ecf20Sopenharmony_ci	u16 desc_loop_len;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	pmt = kzalloc(sizeof(*pmt), GFP_KERNEL);
11658c2ecf20Sopenharmony_ci	if (!pmt)
11668c2ecf20Sopenharmony_ci		return NULL;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (!pcr_pid)
11698c2ecf20Sopenharmony_ci		pcr_pid = 0x1fff;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	pmt->header.table_id = 0x2;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	pmt->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ZERO << 14) | (ONES << 12));
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	pmt->header.id = cpu_to_be16(program_number);
11768c2ecf20Sopenharmony_ci	pmt->header.current_next = 0x1;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	pmt->header.version = 0x1f;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	pmt->header.one2 = ONES;
11818c2ecf20Sopenharmony_ci	pmt->header.section_id   = 0;
11828c2ecf20Sopenharmony_ci	pmt->header.last_section = 0;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	pmt->bitfield = cpu_to_be16((RESERVED1 << 13) | pcr_pid);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	desc_loop_len = vidtv_psi_desc_comp_loop_len(pmt->descriptor);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	pmt->bitfield2 = cpu_to_be16((RESERVED2 << 12) |
11898c2ecf20Sopenharmony_ci				     (ZERO << 10)      |
11908c2ecf20Sopenharmony_ci				     desc_loop_len);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	vidtv_psi_pmt_table_update_sec_len(pmt);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	return pmt;
11958c2ecf20Sopenharmony_ci}
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ciu32 vidtv_psi_pmt_write_into(struct vidtv_psi_pmt_write_args *args)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *table_descriptor   = args->pmt->descriptor;
12008c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt_stream *stream = args->pmt->stream;
12018c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *stream_descriptor;
12028c2ecf20Sopenharmony_ci	u32 crc = INITIAL_CRC;
12038c2ecf20Sopenharmony_ci	u32 nbytes = 0;
12048c2ecf20Sopenharmony_ci	struct header_write_args h_args = {
12058c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
12068c2ecf20Sopenharmony_ci		.dest_offset        = args->offset,
12078c2ecf20Sopenharmony_ci		.h                  = &args->pmt->header,
12088c2ecf20Sopenharmony_ci		.pid                = args->pid,
12098c2ecf20Sopenharmony_ci		.continuity_counter = args->continuity_counter,
12108c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
12118c2ecf20Sopenharmony_ci	};
12128c2ecf20Sopenharmony_ci	struct psi_write_args psi_args  = {
12138c2ecf20Sopenharmony_ci		.dest_buf = args->buf,
12148c2ecf20Sopenharmony_ci		.from     = &args->pmt->bitfield,
12158c2ecf20Sopenharmony_ci		.len      = sizeof_field(struct vidtv_psi_table_pmt, bitfield) +
12168c2ecf20Sopenharmony_ci			    sizeof_field(struct vidtv_psi_table_pmt, bitfield2),
12178c2ecf20Sopenharmony_ci		.pid                = args->pid,
12188c2ecf20Sopenharmony_ci		.new_psi_section    = false,
12198c2ecf20Sopenharmony_ci		.is_crc             = false,
12208c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
12218c2ecf20Sopenharmony_ci		.crc                = &crc,
12228c2ecf20Sopenharmony_ci	};
12238c2ecf20Sopenharmony_ci	struct desc_write_args d_args   = {
12248c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
12258c2ecf20Sopenharmony_ci		.desc               = table_descriptor,
12268c2ecf20Sopenharmony_ci		.pid                = args->pid,
12278c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
12288c2ecf20Sopenharmony_ci	};
12298c2ecf20Sopenharmony_ci	struct crc32_write_args c_args  = {
12308c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
12318c2ecf20Sopenharmony_ci		.pid                = args->pid,
12328c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
12338c2ecf20Sopenharmony_ci	};
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	vidtv_psi_pmt_table_update_sec_len(args->pmt);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	h_args.crc                = &crc;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_table_header_write_into(&h_args);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/* write the two bitfields */
12428c2ecf20Sopenharmony_ci	psi_args.dest_offset        = args->offset + nbytes;
12438c2ecf20Sopenharmony_ci	psi_args.continuity_counter = args->continuity_counter;
12448c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	while (table_descriptor) {
12478c2ecf20Sopenharmony_ci		/* write the descriptors, if any */
12488c2ecf20Sopenharmony_ci		d_args.dest_offset        = args->offset + nbytes;
12498c2ecf20Sopenharmony_ci		d_args.continuity_counter = args->continuity_counter;
12508c2ecf20Sopenharmony_ci		d_args.crc                = &crc;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_desc_write_into(&d_args);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		table_descriptor = table_descriptor->next;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	psi_args.len += sizeof_field(struct vidtv_psi_table_pmt_stream, type);
12588c2ecf20Sopenharmony_ci	while (stream) {
12598c2ecf20Sopenharmony_ci		/* write the streams, if any */
12608c2ecf20Sopenharmony_ci		psi_args.from = stream;
12618c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->offset + nbytes;
12628c2ecf20Sopenharmony_ci		psi_args.continuity_counter = args->continuity_counter;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		stream_descriptor = stream->descriptor;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci		while (stream_descriptor) {
12698c2ecf20Sopenharmony_ci			/* write the stream descriptors, if any */
12708c2ecf20Sopenharmony_ci			d_args.dest_offset        = args->offset + nbytes;
12718c2ecf20Sopenharmony_ci			d_args.desc               = stream_descriptor;
12728c2ecf20Sopenharmony_ci			d_args.continuity_counter = args->continuity_counter;
12738c2ecf20Sopenharmony_ci			d_args.crc                = &crc;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci			nbytes += vidtv_psi_desc_write_into(&d_args);
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci			stream_descriptor = stream_descriptor->next;
12788c2ecf20Sopenharmony_ci		}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci		stream = stream->next;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	c_args.dest_offset        = args->offset + nbytes;
12848c2ecf20Sopenharmony_ci	c_args.crc                = cpu_to_be32(crc);
12858c2ecf20Sopenharmony_ci	c_args.continuity_counter = args->continuity_counter;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/* Write the CRC32 at the end */
12888c2ecf20Sopenharmony_ci	nbytes += table_section_crc32_write_into(&c_args);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	return nbytes;
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_civoid vidtv_psi_pmt_table_destroy(struct vidtv_psi_table_pmt *pmt)
12948c2ecf20Sopenharmony_ci{
12958c2ecf20Sopenharmony_ci	vidtv_psi_desc_destroy(pmt->descriptor);
12968c2ecf20Sopenharmony_ci	vidtv_psi_pmt_stream_destroy(pmt->stream);
12978c2ecf20Sopenharmony_ci	kfree(pmt);
12988c2ecf20Sopenharmony_ci}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cistruct vidtv_psi_table_sdt *vidtv_psi_sdt_table_init(u16 network_id,
13018c2ecf20Sopenharmony_ci						     u16 transport_stream_id)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	struct vidtv_psi_table_sdt *sdt;
13048c2ecf20Sopenharmony_ci	const u16 RESERVED = 0xff;
13058c2ecf20Sopenharmony_ci	const u16 SYNTAX = 0x1;
13068c2ecf20Sopenharmony_ci	const u16 ONES = 0x03;
13078c2ecf20Sopenharmony_ci	const u16 ONE = 0x1;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	sdt  = kzalloc(sizeof(*sdt), GFP_KERNEL);
13108c2ecf20Sopenharmony_ci	if (!sdt)
13118c2ecf20Sopenharmony_ci		return NULL;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	sdt->header.table_id = 0x42;
13148c2ecf20Sopenharmony_ci	sdt->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ONE << 14) | (ONES << 12));
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	/*
13178c2ecf20Sopenharmony_ci	 * This is a 16-bit field which serves as a label for identification
13188c2ecf20Sopenharmony_ci	 * of the TS, about which the SDT informs, from any other multiplex
13198c2ecf20Sopenharmony_ci	 * within the delivery system.
13208c2ecf20Sopenharmony_ci	 */
13218c2ecf20Sopenharmony_ci	sdt->header.id = cpu_to_be16(transport_stream_id);
13228c2ecf20Sopenharmony_ci	sdt->header.current_next = ONE;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	sdt->header.version = 0x1f;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	sdt->header.one2  = ONES;
13278c2ecf20Sopenharmony_ci	sdt->header.section_id   = 0;
13288c2ecf20Sopenharmony_ci	sdt->header.last_section = 0;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	/*
13318c2ecf20Sopenharmony_ci	 * FIXME: The network_id range from 0xFF01 to 0xFFFF is used to
13328c2ecf20Sopenharmony_ci	 * indicate temporary private use. For now, let's use the first
13338c2ecf20Sopenharmony_ci	 * value.
13348c2ecf20Sopenharmony_ci	 * This can be changed to something more useful, when support for
13358c2ecf20Sopenharmony_ci	 * NIT gets added
13368c2ecf20Sopenharmony_ci	 */
13378c2ecf20Sopenharmony_ci	sdt->network_id = cpu_to_be16(network_id);
13388c2ecf20Sopenharmony_ci	sdt->reserved = RESERVED;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	vidtv_psi_sdt_table_update_sec_len(sdt);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	return sdt;
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ciu32 vidtv_psi_sdt_write_into(struct vidtv_psi_sdt_write_args *args)
13468c2ecf20Sopenharmony_ci{
13478c2ecf20Sopenharmony_ci	struct header_write_args h_args = {
13488c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
13498c2ecf20Sopenharmony_ci		.dest_offset        = args->offset,
13508c2ecf20Sopenharmony_ci		.h                  = &args->sdt->header,
13518c2ecf20Sopenharmony_ci		.pid                = VIDTV_SDT_PID,
13528c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
13538c2ecf20Sopenharmony_ci	};
13548c2ecf20Sopenharmony_ci	struct psi_write_args psi_args  = {
13558c2ecf20Sopenharmony_ci		.dest_buf = args->buf,
13568c2ecf20Sopenharmony_ci		.len = sizeof_field(struct vidtv_psi_table_sdt, network_id) +
13578c2ecf20Sopenharmony_ci		       sizeof_field(struct vidtv_psi_table_sdt, reserved),
13588c2ecf20Sopenharmony_ci		.pid                = VIDTV_SDT_PID,
13598c2ecf20Sopenharmony_ci		.new_psi_section    = false,
13608c2ecf20Sopenharmony_ci		.is_crc             = false,
13618c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
13628c2ecf20Sopenharmony_ci	};
13638c2ecf20Sopenharmony_ci	struct desc_write_args d_args   = {
13648c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
13658c2ecf20Sopenharmony_ci		.pid                = VIDTV_SDT_PID,
13668c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
13678c2ecf20Sopenharmony_ci	};
13688c2ecf20Sopenharmony_ci	struct crc32_write_args c_args  = {
13698c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
13708c2ecf20Sopenharmony_ci		.pid                = VIDTV_SDT_PID,
13718c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
13728c2ecf20Sopenharmony_ci	};
13738c2ecf20Sopenharmony_ci	struct vidtv_psi_table_sdt_service *service = args->sdt->service;
13748c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *service_desc;
13758c2ecf20Sopenharmony_ci	u32 nbytes  = 0;
13768c2ecf20Sopenharmony_ci	u32 crc = INITIAL_CRC;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	/* see ETSI EN 300 468 v1.15.1 p. 11 */
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	vidtv_psi_sdt_table_update_sec_len(args->sdt);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	h_args.continuity_counter = args->continuity_counter;
13838c2ecf20Sopenharmony_ci	h_args.crc                = &crc;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_table_header_write_into(&h_args);
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	psi_args.from               = &args->sdt->network_id;
13888c2ecf20Sopenharmony_ci	psi_args.dest_offset        = args->offset + nbytes;
13898c2ecf20Sopenharmony_ci	psi_args.continuity_counter = args->continuity_counter;
13908c2ecf20Sopenharmony_ci	psi_args.crc                = &crc;
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	/* copy u16 network_id + u8 reserved)*/
13938c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	/* skip both pointers at the end */
13968c2ecf20Sopenharmony_ci	psi_args.len = sizeof(struct vidtv_psi_table_sdt_service) -
13978c2ecf20Sopenharmony_ci		       sizeof(struct vidtv_psi_desc *) -
13988c2ecf20Sopenharmony_ci		       sizeof(struct vidtv_psi_table_sdt_service *);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	while (service) {
14018c2ecf20Sopenharmony_ci		/* copy the services, if any */
14028c2ecf20Sopenharmony_ci		psi_args.from = service;
14038c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->offset + nbytes;
14048c2ecf20Sopenharmony_ci		psi_args.continuity_counter = args->continuity_counter;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci		service_desc = service->descriptor;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		while (service_desc) {
14118c2ecf20Sopenharmony_ci			/* copy the service descriptors, if any */
14128c2ecf20Sopenharmony_ci			d_args.dest_offset        = args->offset + nbytes;
14138c2ecf20Sopenharmony_ci			d_args.desc               = service_desc;
14148c2ecf20Sopenharmony_ci			d_args.continuity_counter = args->continuity_counter;
14158c2ecf20Sopenharmony_ci			d_args.crc                = &crc;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci			nbytes += vidtv_psi_desc_write_into(&d_args);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci			service_desc = service_desc->next;
14208c2ecf20Sopenharmony_ci		}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci		service = service->next;
14238c2ecf20Sopenharmony_ci	}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	c_args.dest_offset        = args->offset + nbytes;
14268c2ecf20Sopenharmony_ci	c_args.crc                = cpu_to_be32(crc);
14278c2ecf20Sopenharmony_ci	c_args.continuity_counter = args->continuity_counter;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	/* Write the CRC at the end */
14308c2ecf20Sopenharmony_ci	nbytes += table_section_crc32_write_into(&c_args);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	return nbytes;
14338c2ecf20Sopenharmony_ci}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_civoid vidtv_psi_sdt_table_destroy(struct vidtv_psi_table_sdt *sdt)
14368c2ecf20Sopenharmony_ci{
14378c2ecf20Sopenharmony_ci	vidtv_psi_sdt_service_destroy(sdt->service);
14388c2ecf20Sopenharmony_ci	kfree(sdt);
14398c2ecf20Sopenharmony_ci}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_cistruct vidtv_psi_table_sdt_service
14428c2ecf20Sopenharmony_ci*vidtv_psi_sdt_service_init(struct vidtv_psi_table_sdt_service *head,
14438c2ecf20Sopenharmony_ci			    u16 service_id,
14448c2ecf20Sopenharmony_ci			    bool eit_schedule,
14458c2ecf20Sopenharmony_ci			    bool eit_present_following)
14468c2ecf20Sopenharmony_ci{
14478c2ecf20Sopenharmony_ci	struct vidtv_psi_table_sdt_service *service;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	service = kzalloc(sizeof(*service), GFP_KERNEL);
14508c2ecf20Sopenharmony_ci	if (!service)
14518c2ecf20Sopenharmony_ci		return NULL;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	/*
14548c2ecf20Sopenharmony_ci	 * ETSI 300 468: this is a 16bit field which serves as a label to
14558c2ecf20Sopenharmony_ci	 * identify this service from any other service within the TS.
14568c2ecf20Sopenharmony_ci	 * The service id is the same as the program number in the
14578c2ecf20Sopenharmony_ci	 * corresponding program_map_section
14588c2ecf20Sopenharmony_ci	 */
14598c2ecf20Sopenharmony_ci	service->service_id            = cpu_to_be16(service_id);
14608c2ecf20Sopenharmony_ci	service->EIT_schedule          = eit_schedule;
14618c2ecf20Sopenharmony_ci	service->EIT_present_following = eit_present_following;
14628c2ecf20Sopenharmony_ci	service->reserved              = 0x3f;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	service->bitfield = cpu_to_be16(RUNNING << 13);
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (head) {
14678c2ecf20Sopenharmony_ci		while (head->next)
14688c2ecf20Sopenharmony_ci			head = head->next;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci		head->next = service;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	return service;
14748c2ecf20Sopenharmony_ci}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_civoid
14778c2ecf20Sopenharmony_cividtv_psi_sdt_service_destroy(struct vidtv_psi_table_sdt_service *service)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	struct vidtv_psi_table_sdt_service *curr = service;
14808c2ecf20Sopenharmony_ci	struct vidtv_psi_table_sdt_service *tmp  = NULL;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	while (curr) {
14838c2ecf20Sopenharmony_ci		tmp  = curr;
14848c2ecf20Sopenharmony_ci		curr = curr->next;
14858c2ecf20Sopenharmony_ci		vidtv_psi_desc_destroy(tmp->descriptor);
14868c2ecf20Sopenharmony_ci		kfree(tmp);
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_civoid
14918c2ecf20Sopenharmony_cividtv_psi_sdt_service_assign(struct vidtv_psi_table_sdt *sdt,
14928c2ecf20Sopenharmony_ci			     struct vidtv_psi_table_sdt_service *service)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	do {
14958c2ecf20Sopenharmony_ci		if (service == sdt->service)
14968c2ecf20Sopenharmony_ci			return;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci		sdt->service = service;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci		/* recompute section length */
15018c2ecf20Sopenharmony_ci		vidtv_psi_sdt_table_update_sec_len(sdt);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci		service = NULL;
15048c2ecf20Sopenharmony_ci	} while (vidtv_psi_get_sec_len(&sdt->header) > MAX_SECTION_LEN);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	vidtv_psi_update_version_num(&sdt->header);
15078c2ecf20Sopenharmony_ci}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci/*
15108c2ecf20Sopenharmony_ci * PMTs contain information about programs. For each program,
15118c2ecf20Sopenharmony_ci * there is one PMT section. This function will create a section
15128c2ecf20Sopenharmony_ci * for each program found in the PAT
15138c2ecf20Sopenharmony_ci */
15148c2ecf20Sopenharmony_cistruct vidtv_psi_table_pmt**
15158c2ecf20Sopenharmony_cividtv_psi_pmt_create_sec_for_each_pat_entry(struct vidtv_psi_table_pat *pat,
15168c2ecf20Sopenharmony_ci					    u16 pcr_pid)
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pat_program *program;
15208c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt **pmt_secs;
15218c2ecf20Sopenharmony_ci	u32 i = 0, num_pmt = 0;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	/*
15248c2ecf20Sopenharmony_ci	 * The number of PMT entries is the number of PAT entries
15258c2ecf20Sopenharmony_ci	 * that contain service_id. That exclude special tables, like NIT
15268c2ecf20Sopenharmony_ci	 */
15278c2ecf20Sopenharmony_ci	program = pat->program;
15288c2ecf20Sopenharmony_ci	while (program) {
15298c2ecf20Sopenharmony_ci		if (program->service_id)
15308c2ecf20Sopenharmony_ci			num_pmt++;
15318c2ecf20Sopenharmony_ci		program = program->next;
15328c2ecf20Sopenharmony_ci	}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	pmt_secs = kcalloc(num_pmt,
15358c2ecf20Sopenharmony_ci			   sizeof(struct vidtv_psi_table_pmt *),
15368c2ecf20Sopenharmony_ci			   GFP_KERNEL);
15378c2ecf20Sopenharmony_ci	if (!pmt_secs)
15388c2ecf20Sopenharmony_ci		return NULL;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	for (program = pat->program; program; program = program->next) {
15418c2ecf20Sopenharmony_ci		if (!program->service_id)
15428c2ecf20Sopenharmony_ci			continue;
15438c2ecf20Sopenharmony_ci		pmt_secs[i] = vidtv_psi_pmt_table_init(be16_to_cpu(program->service_id),
15448c2ecf20Sopenharmony_ci						       pcr_pid);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci		if (!pmt_secs[i]) {
15478c2ecf20Sopenharmony_ci			while (i > 0) {
15488c2ecf20Sopenharmony_ci				i--;
15498c2ecf20Sopenharmony_ci				vidtv_psi_pmt_table_destroy(pmt_secs[i]);
15508c2ecf20Sopenharmony_ci			}
15518c2ecf20Sopenharmony_ci			return NULL;
15528c2ecf20Sopenharmony_ci		}
15538c2ecf20Sopenharmony_ci		i++;
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci	pat->num_pmt = num_pmt;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	return pmt_secs;
15588c2ecf20Sopenharmony_ci}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci/* find the PMT section associated with 'program_num' */
15618c2ecf20Sopenharmony_cistruct vidtv_psi_table_pmt
15628c2ecf20Sopenharmony_ci*vidtv_psi_find_pmt_sec(struct vidtv_psi_table_pmt **pmt_sections,
15638c2ecf20Sopenharmony_ci			u16 nsections,
15648c2ecf20Sopenharmony_ci			u16 program_num)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	struct vidtv_psi_table_pmt *sec = NULL;
15678c2ecf20Sopenharmony_ci	u32 i;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	for (i = 0; i < nsections; ++i) {
15708c2ecf20Sopenharmony_ci		sec = pmt_sections[i];
15718c2ecf20Sopenharmony_ci		if (be16_to_cpu(sec->header.id) == program_num)
15728c2ecf20Sopenharmony_ci			return sec;
15738c2ecf20Sopenharmony_ci	}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	return NULL; /* not found */
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_cistatic void vidtv_psi_nit_table_update_sec_len(struct vidtv_psi_table_nit *nit)
15798c2ecf20Sopenharmony_ci{
15808c2ecf20Sopenharmony_ci	u16 length = 0;
15818c2ecf20Sopenharmony_ci	struct vidtv_psi_table_transport *t = nit->transport;
15828c2ecf20Sopenharmony_ci	u16 desc_loop_len;
15838c2ecf20Sopenharmony_ci	u16 transport_loop_len = 0;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	/*
15868c2ecf20Sopenharmony_ci	 * from immediately after 'section_length' until
15878c2ecf20Sopenharmony_ci	 * 'network_descriptor_length'
15888c2ecf20Sopenharmony_ci	 */
15898c2ecf20Sopenharmony_ci	length += NIT_LEN_UNTIL_NETWORK_DESCRIPTOR_LEN;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	desc_loop_len = vidtv_psi_desc_comp_loop_len(nit->descriptor);
15928c2ecf20Sopenharmony_ci	vidtv_psi_set_desc_loop_len(&nit->bitfield, desc_loop_len, 12);
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	length += desc_loop_len;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	length += sizeof_field(struct vidtv_psi_table_nit, bitfield2);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	while (t) {
15998c2ecf20Sopenharmony_ci		/* skip both pointers at the end */
16008c2ecf20Sopenharmony_ci		transport_loop_len += sizeof(struct vidtv_psi_table_transport) -
16018c2ecf20Sopenharmony_ci				      sizeof(struct vidtv_psi_desc *) -
16028c2ecf20Sopenharmony_ci				      sizeof(struct vidtv_psi_table_transport *);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci		length += transport_loop_len;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci		desc_loop_len = vidtv_psi_desc_comp_loop_len(t->descriptor);
16078c2ecf20Sopenharmony_ci		vidtv_psi_set_desc_loop_len(&t->bitfield, desc_loop_len, 12);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci		length += desc_loop_len;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci		t = t->next;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	// Actually sets the transport stream loop len, maybe rename this function later
16158c2ecf20Sopenharmony_ci	vidtv_psi_set_desc_loop_len(&nit->bitfield2, transport_loop_len, 12);
16168c2ecf20Sopenharmony_ci	length += CRC_SIZE_IN_BYTES;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	vidtv_psi_set_sec_len(&nit->header, length);
16198c2ecf20Sopenharmony_ci}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistruct vidtv_psi_table_nit
16228c2ecf20Sopenharmony_ci*vidtv_psi_nit_table_init(u16 network_id,
16238c2ecf20Sopenharmony_ci			  u16 transport_stream_id,
16248c2ecf20Sopenharmony_ci			  char *network_name,
16258c2ecf20Sopenharmony_ci			  struct vidtv_psi_desc_service_list_entry *service_list)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	struct vidtv_psi_table_transport *transport;
16288c2ecf20Sopenharmony_ci	struct vidtv_psi_table_nit *nit;
16298c2ecf20Sopenharmony_ci	const u16 SYNTAX = 0x1;
16308c2ecf20Sopenharmony_ci	const u16 ONES = 0x03;
16318c2ecf20Sopenharmony_ci	const u16 ONE = 0x1;
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	nit = kzalloc(sizeof(*nit), GFP_KERNEL);
16348c2ecf20Sopenharmony_ci	if (!nit)
16358c2ecf20Sopenharmony_ci		return NULL;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	transport = kzalloc(sizeof(*transport), GFP_KERNEL);
16388c2ecf20Sopenharmony_ci	if (!transport)
16398c2ecf20Sopenharmony_ci		goto free_nit;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	nit->header.table_id = 0x40; // ACTUAL_NETWORK
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	nit->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ONE << 14) | (ONES << 12));
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	nit->header.id = cpu_to_be16(network_id);
16468c2ecf20Sopenharmony_ci	nit->header.current_next = ONE;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	nit->header.version = 0x1f;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	nit->header.one2  = ONES;
16518c2ecf20Sopenharmony_ci	nit->header.section_id   = 0;
16528c2ecf20Sopenharmony_ci	nit->header.last_section = 0;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	nit->bitfield = cpu_to_be16(0xf);
16558c2ecf20Sopenharmony_ci	nit->bitfield2 = cpu_to_be16(0xf);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	nit->descriptor = (struct vidtv_psi_desc *)
16588c2ecf20Sopenharmony_ci			  vidtv_psi_network_name_desc_init(NULL, network_name);
16598c2ecf20Sopenharmony_ci	if (!nit->descriptor)
16608c2ecf20Sopenharmony_ci		goto free_transport;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	transport->transport_id = cpu_to_be16(transport_stream_id);
16638c2ecf20Sopenharmony_ci	transport->network_id = cpu_to_be16(network_id);
16648c2ecf20Sopenharmony_ci	transport->bitfield = cpu_to_be16(0xf);
16658c2ecf20Sopenharmony_ci	transport->descriptor = (struct vidtv_psi_desc *)
16668c2ecf20Sopenharmony_ci				vidtv_psi_service_list_desc_init(NULL, service_list);
16678c2ecf20Sopenharmony_ci	if (!transport->descriptor)
16688c2ecf20Sopenharmony_ci		goto free_nit_desc;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	nit->transport = transport;
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	vidtv_psi_nit_table_update_sec_len(nit);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	return nit;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_cifree_nit_desc:
16778c2ecf20Sopenharmony_ci	vidtv_psi_desc_destroy((struct vidtv_psi_desc *)nit->descriptor);
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_cifree_transport:
16808c2ecf20Sopenharmony_ci	kfree(transport);
16818c2ecf20Sopenharmony_cifree_nit:
16828c2ecf20Sopenharmony_ci	kfree(nit);
16838c2ecf20Sopenharmony_ci	return NULL;
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ciu32 vidtv_psi_nit_write_into(struct vidtv_psi_nit_write_args *args)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	struct header_write_args h_args = {
16898c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
16908c2ecf20Sopenharmony_ci		.dest_offset        = args->offset,
16918c2ecf20Sopenharmony_ci		.h                  = &args->nit->header,
16928c2ecf20Sopenharmony_ci		.pid                = VIDTV_NIT_PID,
16938c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
16948c2ecf20Sopenharmony_ci	};
16958c2ecf20Sopenharmony_ci	struct psi_write_args psi_args  = {
16968c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
16978c2ecf20Sopenharmony_ci		.from               = &args->nit->bitfield,
16988c2ecf20Sopenharmony_ci		.len                = sizeof_field(struct vidtv_psi_table_nit, bitfield),
16998c2ecf20Sopenharmony_ci		.pid                = VIDTV_NIT_PID,
17008c2ecf20Sopenharmony_ci		.new_psi_section    = false,
17018c2ecf20Sopenharmony_ci		.is_crc             = false,
17028c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
17038c2ecf20Sopenharmony_ci	};
17048c2ecf20Sopenharmony_ci	struct desc_write_args d_args   = {
17058c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
17068c2ecf20Sopenharmony_ci		.pid                = VIDTV_NIT_PID,
17078c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
17088c2ecf20Sopenharmony_ci	};
17098c2ecf20Sopenharmony_ci	struct crc32_write_args c_args  = {
17108c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
17118c2ecf20Sopenharmony_ci		.pid                = VIDTV_NIT_PID,
17128c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
17138c2ecf20Sopenharmony_ci	};
17148c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *table_descriptor     = args->nit->descriptor;
17158c2ecf20Sopenharmony_ci	struct vidtv_psi_table_transport *transport = args->nit->transport;
17168c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *transport_descriptor;
17178c2ecf20Sopenharmony_ci	u32 crc = INITIAL_CRC;
17188c2ecf20Sopenharmony_ci	u32 nbytes = 0;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	vidtv_psi_nit_table_update_sec_len(args->nit);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	h_args.continuity_counter = args->continuity_counter;
17238c2ecf20Sopenharmony_ci	h_args.crc                = &crc;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_table_header_write_into(&h_args);
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	/* write the bitfield */
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	psi_args.dest_offset        = args->offset + nbytes;
17308c2ecf20Sopenharmony_ci	psi_args.continuity_counter = args->continuity_counter;
17318c2ecf20Sopenharmony_ci	psi_args.crc                = &crc;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	while (table_descriptor) {
17368c2ecf20Sopenharmony_ci		/* write the descriptors, if any */
17378c2ecf20Sopenharmony_ci		d_args.dest_offset        = args->offset + nbytes;
17388c2ecf20Sopenharmony_ci		d_args.desc               = table_descriptor;
17398c2ecf20Sopenharmony_ci		d_args.continuity_counter = args->continuity_counter;
17408c2ecf20Sopenharmony_ci		d_args.crc                = &crc;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_desc_write_into(&d_args);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci		table_descriptor = table_descriptor->next;
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	/* write the second bitfield */
17488c2ecf20Sopenharmony_ci	psi_args.from = &args->nit->bitfield2;
17498c2ecf20Sopenharmony_ci	psi_args.len = sizeof_field(struct vidtv_psi_table_nit, bitfield2);
17508c2ecf20Sopenharmony_ci	psi_args.dest_offset = args->offset + nbytes;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	psi_args.len  = sizeof_field(struct vidtv_psi_table_transport, transport_id) +
17558c2ecf20Sopenharmony_ci			sizeof_field(struct vidtv_psi_table_transport, network_id)   +
17568c2ecf20Sopenharmony_ci			sizeof_field(struct vidtv_psi_table_transport, bitfield);
17578c2ecf20Sopenharmony_ci	while (transport) {
17588c2ecf20Sopenharmony_ci		/* write the transport sections, if any */
17598c2ecf20Sopenharmony_ci		psi_args.from = transport;
17608c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->offset + nbytes;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci		transport_descriptor = transport->descriptor;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci		while (transport_descriptor) {
17678c2ecf20Sopenharmony_ci			/* write the transport descriptors, if any */
17688c2ecf20Sopenharmony_ci			d_args.dest_offset        = args->offset + nbytes;
17698c2ecf20Sopenharmony_ci			d_args.desc               = transport_descriptor;
17708c2ecf20Sopenharmony_ci			d_args.continuity_counter = args->continuity_counter;
17718c2ecf20Sopenharmony_ci			d_args.crc                = &crc;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci			nbytes += vidtv_psi_desc_write_into(&d_args);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci			transport_descriptor = transport_descriptor->next;
17768c2ecf20Sopenharmony_ci		}
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci		transport = transport->next;
17798c2ecf20Sopenharmony_ci	}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	c_args.dest_offset        = args->offset + nbytes;
17828c2ecf20Sopenharmony_ci	c_args.crc                = cpu_to_be32(crc);
17838c2ecf20Sopenharmony_ci	c_args.continuity_counter = args->continuity_counter;
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	/* Write the CRC32 at the end */
17868c2ecf20Sopenharmony_ci	nbytes += table_section_crc32_write_into(&c_args);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	return nbytes;
17898c2ecf20Sopenharmony_ci}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_cistatic void vidtv_psi_transport_destroy(struct vidtv_psi_table_transport *t)
17928c2ecf20Sopenharmony_ci{
17938c2ecf20Sopenharmony_ci	struct vidtv_psi_table_transport *tmp_t  = NULL;
17948c2ecf20Sopenharmony_ci	struct vidtv_psi_table_transport *curr_t = t;
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	while (curr_t) {
17978c2ecf20Sopenharmony_ci		tmp_t  = curr_t;
17988c2ecf20Sopenharmony_ci		curr_t = curr_t->next;
17998c2ecf20Sopenharmony_ci		vidtv_psi_desc_destroy(tmp_t->descriptor);
18008c2ecf20Sopenharmony_ci		kfree(tmp_t);
18018c2ecf20Sopenharmony_ci	}
18028c2ecf20Sopenharmony_ci}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_civoid vidtv_psi_nit_table_destroy(struct vidtv_psi_table_nit *nit)
18058c2ecf20Sopenharmony_ci{
18068c2ecf20Sopenharmony_ci	vidtv_psi_desc_destroy(nit->descriptor);
18078c2ecf20Sopenharmony_ci	vidtv_psi_transport_destroy(nit->transport);
18088c2ecf20Sopenharmony_ci	kfree(nit);
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_civoid vidtv_psi_eit_table_update_sec_len(struct vidtv_psi_table_eit *eit)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	struct vidtv_psi_table_eit_event *e = eit->event;
18148c2ecf20Sopenharmony_ci	u16 desc_loop_len;
18158c2ecf20Sopenharmony_ci	u16 length = 0;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	/*
18188c2ecf20Sopenharmony_ci	 * from immediately after 'section_length' until
18198c2ecf20Sopenharmony_ci	 * 'last_table_id'
18208c2ecf20Sopenharmony_ci	 */
18218c2ecf20Sopenharmony_ci	length += EIT_LEN_UNTIL_LAST_TABLE_ID;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	while (e) {
18248c2ecf20Sopenharmony_ci		/* skip both pointers at the end */
18258c2ecf20Sopenharmony_ci		length += sizeof(struct vidtv_psi_table_eit_event) -
18268c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_desc *) -
18278c2ecf20Sopenharmony_ci			  sizeof(struct vidtv_psi_table_eit_event *);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci		desc_loop_len = vidtv_psi_desc_comp_loop_len(e->descriptor);
18308c2ecf20Sopenharmony_ci		vidtv_psi_set_desc_loop_len(&e->bitfield, desc_loop_len, 12);
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci		length += desc_loop_len;
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci		e = e->next;
18358c2ecf20Sopenharmony_ci	}
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	length += CRC_SIZE_IN_BYTES;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	vidtv_psi_set_sec_len(&eit->header, length);
18408c2ecf20Sopenharmony_ci}
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_civoid vidtv_psi_eit_event_assign(struct vidtv_psi_table_eit *eit,
18438c2ecf20Sopenharmony_ci				struct vidtv_psi_table_eit_event *e)
18448c2ecf20Sopenharmony_ci{
18458c2ecf20Sopenharmony_ci	do {
18468c2ecf20Sopenharmony_ci		if (e == eit->event)
18478c2ecf20Sopenharmony_ci			return;
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci		eit->event = e;
18508c2ecf20Sopenharmony_ci		vidtv_psi_eit_table_update_sec_len(eit);
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci		e = NULL;
18538c2ecf20Sopenharmony_ci	} while (vidtv_psi_get_sec_len(&eit->header) > EIT_MAX_SECTION_LEN);
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	vidtv_psi_update_version_num(&eit->header);
18568c2ecf20Sopenharmony_ci}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_cistruct vidtv_psi_table_eit
18598c2ecf20Sopenharmony_ci*vidtv_psi_eit_table_init(u16 network_id,
18608c2ecf20Sopenharmony_ci			  u16 transport_stream_id,
18618c2ecf20Sopenharmony_ci			  __be16 service_id)
18628c2ecf20Sopenharmony_ci{
18638c2ecf20Sopenharmony_ci	struct vidtv_psi_table_eit *eit;
18648c2ecf20Sopenharmony_ci	const u16 SYNTAX = 0x1;
18658c2ecf20Sopenharmony_ci	const u16 ONE = 0x1;
18668c2ecf20Sopenharmony_ci	const u16 ONES = 0x03;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	eit = kzalloc(sizeof(*eit), GFP_KERNEL);
18698c2ecf20Sopenharmony_ci	if (!eit)
18708c2ecf20Sopenharmony_ci		return NULL;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	eit->header.table_id = 0x4e; //actual_transport_stream: present/following
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	eit->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ONE << 14) | (ONES << 12));
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	eit->header.id = service_id;
18778c2ecf20Sopenharmony_ci	eit->header.current_next = ONE;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	eit->header.version = 0x1f;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	eit->header.one2  = ONES;
18828c2ecf20Sopenharmony_ci	eit->header.section_id   = 0;
18838c2ecf20Sopenharmony_ci	eit->header.last_section = 0;
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	eit->transport_id = cpu_to_be16(transport_stream_id);
18868c2ecf20Sopenharmony_ci	eit->network_id = cpu_to_be16(network_id);
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci	eit->last_segment = eit->header.last_section; /* not implemented */
18898c2ecf20Sopenharmony_ci	eit->last_table_id = eit->header.table_id; /* not implemented */
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	vidtv_psi_eit_table_update_sec_len(eit);
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	return eit;
18948c2ecf20Sopenharmony_ci}
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_ciu32 vidtv_psi_eit_write_into(struct vidtv_psi_eit_write_args *args)
18978c2ecf20Sopenharmony_ci{
18988c2ecf20Sopenharmony_ci	struct header_write_args h_args = {
18998c2ecf20Sopenharmony_ci		.dest_buf        = args->buf,
19008c2ecf20Sopenharmony_ci		.dest_offset     = args->offset,
19018c2ecf20Sopenharmony_ci		.h               = &args->eit->header,
19028c2ecf20Sopenharmony_ci		.pid             = VIDTV_EIT_PID,
19038c2ecf20Sopenharmony_ci		.dest_buf_sz     = args->buf_sz,
19048c2ecf20Sopenharmony_ci	};
19058c2ecf20Sopenharmony_ci	struct psi_write_args psi_args  = {
19068c2ecf20Sopenharmony_ci		.dest_buf        = args->buf,
19078c2ecf20Sopenharmony_ci		.len             = sizeof_field(struct vidtv_psi_table_eit, transport_id) +
19088c2ecf20Sopenharmony_ci				   sizeof_field(struct vidtv_psi_table_eit, network_id)   +
19098c2ecf20Sopenharmony_ci				   sizeof_field(struct vidtv_psi_table_eit, last_segment) +
19108c2ecf20Sopenharmony_ci				   sizeof_field(struct vidtv_psi_table_eit, last_table_id),
19118c2ecf20Sopenharmony_ci		.pid             = VIDTV_EIT_PID,
19128c2ecf20Sopenharmony_ci		.new_psi_section = false,
19138c2ecf20Sopenharmony_ci		.is_crc          = false,
19148c2ecf20Sopenharmony_ci		.dest_buf_sz     = args->buf_sz,
19158c2ecf20Sopenharmony_ci	};
19168c2ecf20Sopenharmony_ci	struct desc_write_args d_args   = {
19178c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
19188c2ecf20Sopenharmony_ci		.pid                = VIDTV_EIT_PID,
19198c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
19208c2ecf20Sopenharmony_ci	};
19218c2ecf20Sopenharmony_ci	struct crc32_write_args c_args  = {
19228c2ecf20Sopenharmony_ci		.dest_buf           = args->buf,
19238c2ecf20Sopenharmony_ci		.pid                = VIDTV_EIT_PID,
19248c2ecf20Sopenharmony_ci		.dest_buf_sz        = args->buf_sz,
19258c2ecf20Sopenharmony_ci	};
19268c2ecf20Sopenharmony_ci	struct vidtv_psi_table_eit_event *event = args->eit->event;
19278c2ecf20Sopenharmony_ci	struct vidtv_psi_desc *event_descriptor;
19288c2ecf20Sopenharmony_ci	u32 crc = INITIAL_CRC;
19298c2ecf20Sopenharmony_ci	u32 nbytes  = 0;
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	vidtv_psi_eit_table_update_sec_len(args->eit);
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	h_args.continuity_counter = args->continuity_counter;
19348c2ecf20Sopenharmony_ci	h_args.crc                = &crc;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_table_header_write_into(&h_args);
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	psi_args.from               = &args->eit->transport_id;
19398c2ecf20Sopenharmony_ci	psi_args.dest_offset        = args->offset + nbytes;
19408c2ecf20Sopenharmony_ci	psi_args.continuity_counter = args->continuity_counter;
19418c2ecf20Sopenharmony_ci	psi_args.crc                = &crc;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	/* skip both pointers at the end */
19468c2ecf20Sopenharmony_ci	psi_args.len = sizeof(struct vidtv_psi_table_eit_event) -
19478c2ecf20Sopenharmony_ci		       sizeof(struct vidtv_psi_desc *) -
19488c2ecf20Sopenharmony_ci		       sizeof(struct vidtv_psi_table_eit_event *);
19498c2ecf20Sopenharmony_ci	while (event) {
19508c2ecf20Sopenharmony_ci		/* copy the events, if any */
19518c2ecf20Sopenharmony_ci		psi_args.from = event;
19528c2ecf20Sopenharmony_ci		psi_args.dest_offset = args->offset + nbytes;
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci		nbytes += vidtv_psi_ts_psi_write_into(&psi_args);
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci		event_descriptor = event->descriptor;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci		while (event_descriptor) {
19598c2ecf20Sopenharmony_ci			/* copy the event descriptors, if any */
19608c2ecf20Sopenharmony_ci			d_args.dest_offset        = args->offset + nbytes;
19618c2ecf20Sopenharmony_ci			d_args.desc               = event_descriptor;
19628c2ecf20Sopenharmony_ci			d_args.continuity_counter = args->continuity_counter;
19638c2ecf20Sopenharmony_ci			d_args.crc                = &crc;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci			nbytes += vidtv_psi_desc_write_into(&d_args);
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci			event_descriptor = event_descriptor->next;
19688c2ecf20Sopenharmony_ci		}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci		event = event->next;
19718c2ecf20Sopenharmony_ci	}
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	c_args.dest_offset        = args->offset + nbytes;
19748c2ecf20Sopenharmony_ci	c_args.crc                = cpu_to_be32(crc);
19758c2ecf20Sopenharmony_ci	c_args.continuity_counter = args->continuity_counter;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	/* Write the CRC at the end */
19788c2ecf20Sopenharmony_ci	nbytes += table_section_crc32_write_into(&c_args);
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	return nbytes;
19818c2ecf20Sopenharmony_ci}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_cistruct vidtv_psi_table_eit_event
19848c2ecf20Sopenharmony_ci*vidtv_psi_eit_event_init(struct vidtv_psi_table_eit_event *head, u16 event_id)
19858c2ecf20Sopenharmony_ci{
19868c2ecf20Sopenharmony_ci	const u8 DURATION[] = {0x23, 0x59, 0x59}; /* BCD encoded */
19878c2ecf20Sopenharmony_ci	struct vidtv_psi_table_eit_event *e;
19888c2ecf20Sopenharmony_ci	struct timespec64 ts;
19898c2ecf20Sopenharmony_ci	struct tm time;
19908c2ecf20Sopenharmony_ci	int mjd, l;
19918c2ecf20Sopenharmony_ci	__be16 mjd_be;
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
19948c2ecf20Sopenharmony_ci	if (!e)
19958c2ecf20Sopenharmony_ci		return NULL;
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	e->event_id = cpu_to_be16(event_id);
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	ts = ktime_to_timespec64(ktime_get_real());
20008c2ecf20Sopenharmony_ci	time64_to_tm(ts.tv_sec, 0, &time);
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci	/* Convert date to Modified Julian Date - per EN 300 468 Annex C */
20038c2ecf20Sopenharmony_ci	if (time.tm_mon < 2)
20048c2ecf20Sopenharmony_ci		l = 1;
20058c2ecf20Sopenharmony_ci	else
20068c2ecf20Sopenharmony_ci		l = 0;
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	mjd = 14956 + time.tm_mday;
20098c2ecf20Sopenharmony_ci	mjd += (time.tm_year - l) * 36525 / 100;
20108c2ecf20Sopenharmony_ci	mjd += (time.tm_mon + 2 + l * 12) * 306001 / 10000;
20118c2ecf20Sopenharmony_ci	mjd_be = cpu_to_be16(mjd);
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	/*
20148c2ecf20Sopenharmony_ci	 * Store MJD and hour/min/sec to the event.
20158c2ecf20Sopenharmony_ci	 *
20168c2ecf20Sopenharmony_ci	 * Let's make the event to start on a full hour
20178c2ecf20Sopenharmony_ci	 */
20188c2ecf20Sopenharmony_ci	memcpy(e->start_time, &mjd_be, sizeof(mjd_be));
20198c2ecf20Sopenharmony_ci	e->start_time[2] = bin2bcd(time.tm_hour);
20208c2ecf20Sopenharmony_ci	e->start_time[3] = 0;
20218c2ecf20Sopenharmony_ci	e->start_time[4] = 0;
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci	/*
20248c2ecf20Sopenharmony_ci	 * TODO: for now, the event will last for a day. Should be
20258c2ecf20Sopenharmony_ci	 * enough for testing purposes, but if one runs the driver
20268c2ecf20Sopenharmony_ci	 * for more than that, the current event will become invalid.
20278c2ecf20Sopenharmony_ci	 * So, we need a better code here in order to change the start
20288c2ecf20Sopenharmony_ci	 * time once the event expires.
20298c2ecf20Sopenharmony_ci	 */
20308c2ecf20Sopenharmony_ci	memcpy(e->duration, DURATION, sizeof(e->duration));
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	e->bitfield = cpu_to_be16(RUNNING << 13);
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci	if (head) {
20358c2ecf20Sopenharmony_ci		while (head->next)
20368c2ecf20Sopenharmony_ci			head = head->next;
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci		head->next = e;
20398c2ecf20Sopenharmony_ci	}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci	return e;
20428c2ecf20Sopenharmony_ci}
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_civoid vidtv_psi_eit_event_destroy(struct vidtv_psi_table_eit_event *e)
20458c2ecf20Sopenharmony_ci{
20468c2ecf20Sopenharmony_ci	struct vidtv_psi_table_eit_event *tmp_e  = NULL;
20478c2ecf20Sopenharmony_ci	struct vidtv_psi_table_eit_event *curr_e = e;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	while (curr_e) {
20508c2ecf20Sopenharmony_ci		tmp_e  = curr_e;
20518c2ecf20Sopenharmony_ci		curr_e = curr_e->next;
20528c2ecf20Sopenharmony_ci		vidtv_psi_desc_destroy(tmp_e->descriptor);
20538c2ecf20Sopenharmony_ci		kfree(tmp_e);
20548c2ecf20Sopenharmony_ci	}
20558c2ecf20Sopenharmony_ci}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_civoid vidtv_psi_eit_table_destroy(struct vidtv_psi_table_eit *eit)
20588c2ecf20Sopenharmony_ci{
20598c2ecf20Sopenharmony_ci	vidtv_psi_eit_event_destroy(eit->event);
20608c2ecf20Sopenharmony_ci	kfree(eit);
20618c2ecf20Sopenharmony_ci}
2062